POC: Cleaning up orphaned files using undo logs

Started by Thomas Munroabout 7 years ago360 messages
#1Thomas Munro
thomas.munro@enterprisedb.com
1 attachment(s)

Hello hackers,

The following sequence creates an orphaned file:

BEGIN;
CREATE TABLE t ();
<kill -9 this backend>

Occasionally there are reports of systems that have managed to produce
a lot of them, perhaps through ENOSPC-induced panics, OOM signals or
buggy/crashing extensions etc. The most recent example I found in the
archives involved 1.7TB of unexpected files and some careful cleanup
work.

Relation files are created eagerly, and rollback is handled by pushing
PendingRelDelete objects onto the pendingDeletes list, to be discarded
on commit or processed on abort. That's effectively a kind of
specialised undo log, but it's in memory only, so it's less persistent
than the effects it is supposed to undo.

Here's a proof-of-concept patch that plugs the gap using the undo log
technology we're developing as part of the zHeap project. Note that
zHeap is not involved here: the SMGR module is acting as a direct
client of the undo machinery. Example:

postgres=# begin;
BEGIN
postgres=# create table t1 ();
CREATE TABLE
postgres=# create table t2 ();
CREATE TABLE

... now we can see that this transaction has some undo data (discard < insert):

postgres=# select logno, discard, insert, xid, pid from pg_stat_undo_logs;
logno | discard | insert | xid | pid
-------+------------------+------------------+-----+-------
0 | 00000000000021EF | 0000000000002241 | 581 | 18454
(1 row)

... and, if the test_undorecord module is installed, we can inspect
the records it holds:

postgres=# call dump_undo_records(0);
NOTICE: 0000000000002224: Storage: CREATE dbid=12655, tsid=1663, relfile=24594
NOTICE: 00000000000021EF: Storage: CREATE dbid=12655, tsid=1663, relfile=24591
CALL

If we COMMIT, the undo data is discarded by advancing the discard
pointer (tail) to match the insert pointer (head). If we ROLLBACK,
either explicitly or automatically by crashing and recovering, then
the files will be unlinked and the insert pointer will be rewound;
either way the undo log eventually finishes up "empty" again (discard
== insert). This is done with a system of per-rmgr-ID record types
and callbacks, similar to redo. The rollback action are either
executed immediately or offloaded to an undo worker process, depending
on simple heuristics.

Of course this isn't free, and the current patch makes table creation
slower. The goal is to make sure that there is no scenario (kill -9,
power cut etc) in which there can be a new relation file on disk, but
not a corresponding undo record that would unlink that file if the
transaction later has to roll back. Currently, that means that we
need to flush the WAL record that will create the undo record that
will unlink the file *before* we create the relation file. I suspect
that could be mitigated quite easily, by deferring file creation in a
backend-local queue until forced by access or commit. I didn't try to
do that in this basic version.

There are probably other ways to solve the specific problem of
orphaned files, but this approach is built on a general reusable
facility and I think it is a nice way to show the undo concepts, and
how they are separate from zheap. Specifically, it demonstrates the
more traditional of the two uses for undo logs: a reliable way to
track actions that must be performed on rollback. (The other use is:
seeing past versions of data, for vacuumless MVCC; that's a topic for
later).

Patches 0001-0006 are development snapshots of material posted on
other threads already[1]/messages/by-id/CAEepm=2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ@mail.gmail.com[2]/messages/by-id/CAFiTN-sYQ8r8ANjWFYkXVfNxgXyLRfvbX9Ee4SxO9ns-OBBgVA@mail.gmail.com, hacked around by me to make this possible
(see those threads for further developments in those patches including
some major strengthening work, coming soon). The subject of this
thread is 0007, the core of which is just a couple of hundred lines
written by me, based on an idea from Robert Haas.

Personally I think it'd be a good feature to get into PostgreSQL 12,
and I will add it to the CF that is about to start to seek feedback.
It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

Thanks for reading,

[1]: /messages/by-id/CAEepm=2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ@mail.gmail.com
[2]: /messages/by-id/CAFiTN-sYQ8r8ANjWFYkXVfNxgXyLRfvbX9Ee4SxO9ns-OBBgVA@mail.gmail.com

--
Thomas Munro
http://www.enterprisedb.com

Attachments:

undo-smgr-v1.tgzapplication/x-gzip; name=undo-smgr-v1.tgzDownload
�Bo�[��y{�F�7:��������E���E����pZ�Hr�t:�%�I�
���������6,�8����~�#	K��S���;�$LW��U��yc���������>U�s��on�O��6��vv�w�m��������g����A���M�"��+�i��3������e���K�M���Zy�G+t��Q����Q?|Z)���(
���de��|%MV�~����\7����nZ�����M���S�o�<}��oj�O������,������"[���������a��t������/�����/���4Q�Dm<S��/�js}}����T���8��1N�����Vib�3J�(�d1�Tu��_��"�W���N�����s���r����
����������'/��g{��?�gk�Q@��R����T���D�*�.Sh��/����W*N�"
B���$J�8�:�FQ���R�a�It#M�A��@-�~�Bz0T����g�<1|{�+��������	�:��]:U���H����Z�|WC��~�wy�.�����P�h�f��?=>�^v�;i6��.w��G-4\�as[�,��U
��Qv��~d���q����`�*��G������~<:}��8~w�;;?�.a;Y����((��,Q7:*Oyvn"�)Io�4�����7��wI?��W����B�KS���Q��������0H���"����+ ������ak�yp���~����<��4��y����@��O���8L���ekee���l�&k���Z3���Z.������R����_��L*cj������Pzml-/�FSz�_��xJ4�t��d0�����Q���5�@��r����{8���8��h����?*o�=Z\�5^����O1���"�4��
E�[]]�<[�{eecu}5�����=����U���wk�����y���D
A�����X��;��|��ne�@���p�
�1tae���.��At���R���5�_�v�
|��V_�^�V+��C��r*X�s���|�'at���Fn=�?
6VW_��?{��_6��#����- �������V6�;�������gK�'k-��B5p��yqG���bK����zzZt�������/om���6;��i���a��E|��S8��8s��s8������}��D��L�h�o�omz����W��d�EQ���[ky!�y�L;������lK�������Q��7��zr;����j��[����[8[����
O����M�l��V^g	jG�/�Rkk����T�8�>�L���elk��.���CU��zy��d�F�`��������~����s�.����f-�N�xx���I�\���p������#�>���'oO{�{������V�(/��BB���E�B��x�m��2����}��EgK-?����y�H���f��Ch�7\��i���%����>������D����q��V�_O�0�w�������;C�e�`��1������]����f��b�&�)���J�l���4��,�������$���.���;�[�7�����hu5z�������{�f��F�Yzics������:y����t�'nA	h��/��%��J��y$�A������u�c9�Q2��~�2V������|O��6,`�Y�lu�������v��U[�.]�:�^t@]����ps�p>�V�YpV������|_$�Z���q?���8�|	E5�����?-�Q>����:��A�
�q��^�W��x��F
����n�P����nL|xW�h�#���Z{����Zl�*�PWB�2�ZHG�
�@�����h���	�����4���,w�����#y|
U���Pglp�w�����A���[ \x�	8�N��8�D]�c'p�p�Q��3��K�A����N.c�n����ri
�P��!rz�)�b�%#�4Fb���s��}z����,,�%5����A0R����f�WiJ��k^~��g���/:��{�
M�	�>���,�&�����kke��3(�\_e���D#���y[7\.��i:ZXH�x�M0b�Xgu��u�h~g���g	��0=�Q@��h!��A�{X��X�7l���"$w�����������F�F���kb5���@G����=���U���X���@��\%�1{�|%A�����D�?�N�~���vq[a�<.�u��d�jAbW��h#4*\/a�pZ�D��������5�o��|$��.*.����K$r5��G��[@���GR ���*�E
��W���:�����������qG
�(�AxeUC�D3�	��G���k�}j���6�=C*ZA"�C�����
kn�SF�"Y�pq�Vd;��R�kK���q4N��}��{�Q:h�tzW�z�D���N���n������i���2���� g�8����
G.�)�h�u��Q��H`�ep�8dp�h��L`D�����',4�hw3�(�z�)\�N����.X!�Y��5��h-��������
R0�_�c���HA��w~�:T��U�V�*�V���L������z~x�w�==�^�u�.j���4$xm����@i�";!��<�:�}.�7<����'�	�q�Mk���m\wo�6���Bm#�&����AC���a[|��~��<����<�����'�����a�.p�d� ����$(��7:Lg���{'�'�--���2�g���O��@����X�mf|�f��E���L^�y?���x������� _���v�=@6������Y��Q��@��.2��������a�����t�L�n�
�����v���'K��d�0�%\����N�n�N�O�f�
�/����y2\=g���Vgc�X|`���,�#r~���y�����VH�P��;%;_G���IJYZ8Gh�edbil?�1�>��n���96u��w�O�|��?�����Y�H;�}�w����B��|	��-�N@��F�[���q���>"�a4����G��)|�7)��:���9J�����]G9LKGKRy?�z�c����8����{�E�&�����~��m4I��B����^�/�ZUY�PJ������v�������Oh�rD��;K
N�,/��Y�D�+���21
y"Y�_��F�_uj^��A����	�S7��<��n�����>�|��G���'����1J��= ����#��
b|
��������!2=s�;�t9��'�p��"�j���pC��x����a��l�5�&�m�-�������zUk8n�U�g��cE��o������/��G���������%kql~����A�E{#Lq~7���L�1���@��<���S;V<
4^d�t������T��w�s q<��������-�a�;�E�r�lV6������K~�{�������3����*��_�����sx/�u>a8o�:;�'��O,�1�0���J��s�o�f��f`�f`�f��ks�f���?��|$�����?���w.�]���^�SElN�%����y����"K�s<��1Yn�0'�tu�ym������ka�y-������>��}����7:�h�EA~�k�C����G���,-Rd�9�B��S�t2A�^��7�fY�H9�D����,��.T{��6^��Y�x�<A���>R�Fi������X��������;
�|�����I�-��^��z�����S/�~��^C�0A;���������?�u�'Q���^��(�"'-���x|�k���#���|�,��u��HG&�|�G�\x������+���*�r/95��h���A$�l�����������gO_<�Y]�w�g/�/6�f�������<H����_#;���`��j��k�Zj[�c�N�#4RvXX��X����/x`s��X�z	Ol���\rNi��Ctl���N��Fgc���U���t�@L�Qp��Uy
��
�e���:Y����dwi�d���BZ���9�o�~-�U�P��i��������p�,��O7w�����t����a��?:��c���l-��������T�G�C�dk����d��������}�)=�V8L����"{�����T����]�U���Ex�������x3"��Q�����I�OO�v����
�f2������r��_�j�m������w}N���8jV��i?�QS�o�a'��^�B~auuM��m�}j�(A/������z�B&����+����t��SQ��<���b�n�h+��G������z�l��@<M
��z��&K:���5�� &�y��b+y����n�^[��"
@h���]��/�b/5���������O.*��M�B=��}�w������cu�w��=HOj��p���������C�����S�
M&)���{�]4f��q</U
����`}'|���P��7	9a�WV@�R�2*!��Yz��w�d������_5����[�c<�[��F��'����}2�q?��9����:7k�����5)���U#���*� JP�r9�	9}]*���T ��7����w����;�i����7�e��z�s���r�APL�pZ���������Fg�[�9�z�>?ob\���_.���9���
��_��#z���,���y�����:��Xd�|�������3v��c��y�6��������{��x�6I�EC�Y���t�;��������l�{��k��
�!�C]�]����~������?�/j�l��M�5�b���
���[�J�
B��%�N��D=�d0��k��O��{��3x���f���[���q���������O���?������B8��-���-���z���o����a��W��������2�>��h4�H�D*t<��T��RG=
nt�z�;���&�|�;��;i�x�����r�oo�}�U�3�:�<�n5�)�kS�`��fQ<Rh�$!DOq{��^��������L���	��d���u�H�A��&M[���p[h{)�HG!���}�"�%3�	|/�e%O�;9F��*y�,3u�g-SL�:���(F��1��a�T���Y��������~����Xe7���#��{J
m?������zl�9����a����|���>D
�T���UX�<	&�uZhK`�)n3$Qt�@�^QrU\sK��0��M��ruOa�K=[���)�'�"X��������������� �rmz�g\'CP��������u�Je�g~��;j�.A%������7�&}"c�N�����"��Nt��,&;��,=��5���d�C�6@��z�����X��`���8��ig���y|�8-h���K�H��������{L�mg�_�V���A�p����`7a'�&�0B/d�H��%������"��F����|�q����]/�:��z��E��m�83�-~��D}lN
�	��_/��^n��m����,�T������N�\�g(�@�G�St0:S��V��Gt�{IU��<g����f�������w�@3M��-��s	d!Y2I@��DT���j��~x��+���:��������i�@��~�-��(��v;�F��m���~��4j���WS��2fE����_V4]~�1��0���9�R�o���
�~�2�f���b4����k�)@�hX��N�6W7^�n�Z.�����X�C�A�	R�}/�����c}�i	�ec}�����3�����h��5F���e�i��l
/���;��������?
�e�y����`K(#k
m��4yK>��wp��1\��!���*����gx�)>��
�� �n��@B�?E������C����V�v�&��E�-����yi0P�F��qb�?Tp���iB�>D���b��i��H���t>|_�s�-V���/�o��+�y��e��8{�
����I��^@lY)L��B8��%�2�X�1�8i��"���%'�3!*h�x���N� �"6�I>������e�������NzMFb������2���1D[�)��Cj�M z���8��u4�4Ic���	Sk���sL��s�Fi��u9KmD�\#����H�������hh��I�^���C�L�R��������E��a��-�\����a�Q��|��Q��r
����|�"#�Q�#��zMK�+��$]�-`�iV�����l��p�����@B�A<�'�/�q��|������F�����%���#�i��� o�!�+o�^���:P������Q�E1�_����v������a4�u�y�������w��?�??�4���������s=?�<���nM'����v{}s�zE��7�yH5>���������=�KbSh|���2��l�!�Pw��J������zG����l�lo� �T��L�����8�����w������-�*�qs��NMF��x��L�	E?|�~^��<�����D��4�3��_��x^���9�A^���E�;l�G����w�%y����tP���u���j=���4�J��`�7���nk���Vj6n�?��;��R�������Z��S�6���t
��<@���g�w��2T����~����
�U~���,B7U��N@x���Q;Z���YW�:�4���2��|hTs1�y��X��^2��^��2�<��u/J)���������V�<7���gC�R���}bo�@O��
D{���
�#-����R��t�/	3c3��j;
���5b�!����&�"�"��E�z�o���0?0Qp�dG��C��>��|`��<�4?��U�<���nl���0vVW_l�����V8?����Z}����>h%%2�vmJ���WE�\��t��*J�,�_k)��+�����d�b�_��W~c�@�K@0�h>c�*��i�����M#+���FK���o�
�;��������f���xxS.lj��"[_D�M?>���N�3nm���a�h�����a���?,���&��c����)��@������e� ��`�j�}S4�z�Q���?$�ff����e�����
��� �q�jc�a���r �s�@��*�w6.������Y���#���-�����f�8viS~�d���WhH����h����a�4h������=���E��>���*Yh:��f4p2�X�./���KP���~,��C��x�7ih��{|����;��~19����|H��K��Wi��Z<|���/X����z"����������8���@��0�u�`�!��)@�<�Z�
jzb9X-4%3*�y�#�+�=��eN����u����2kp_�+s��;�}�����l	�]�4��LH��|�����L�� ��������;���~���O��m�?
^�����IMx�{9�������f(B�k@�<�9`�[5��Y;}�Qb������;[�asd���q��8�+�d��i=�[t�w3%X�/K�����w�b�K25����1&aIK�)�}E��d:o����K����pggs8��x
�/��`h����3����4%����_F��x�����"�}Y}!l�G��
���TW����<�)z��t���>�n/jN�4g��*dW >'�s�����
R$
�G�@�J�d�fJ������=c77�`u^��_�>��� ��!
�����k���[}�G�����@[��Uwr�sd�gj\h�<L���� }+��m4��Jw'W^���r�J
xp9w���5��~�����	�,�k��_��2i�{ll�k���v7P�u��rSr�D�]~���2�v/O���E]w0�t(���`x`�-�A������bH���X�J(�#i>D� `�\c�7���00i���BS�9�C�	����7��&����V�9
&�������f�k$=�����8<Jo�`���3�)mP�>f�7��<BM�
6�1,a��{��7�K�R��Z�b9*�{����1>��������W��:�� #�a�h�Pm����|��7_��!����
��w5}�y�XZr��8 /G�2��4��]12D98��OGge�v�QHHR���a|�+���F1J�c\Sj,�@/������cL��fJ����u]�(Y�H�3�8�!������
�U�L��W/�).�
������+)��$�y���g�L���)b������i�C�MF������s�9~~{t�������w~��S�������/Q��W>��������;���	�L����b�HQt��ab6�������u�(4�[T�k�����l.�������6���?�����Gw��.#B���O9�������#3��D������-��M�$&O�&�i�:#��h�$W���KPB����G����7v�Yw�4@
5+<��8���N3�:�cB��:>����i���|��1y(��C1�OE�qc�t�R����p�1�:~��%I�OAbDYt5���R�H^�HS.�L��35��I������<��%��(Z���	N�,��W���e�#c;�n	6�dh;5M�)����������{p��
I	Wy���]�n�|��m��>t�����4I`\,q�����!���l<(�wd�XQ�/�N���d7v�~�c=�1�WL��yS��=�}J����8D#2>�16����I�����h�1�p�b-�x���C��0&��N�We?:f�.o��HR��c��	DqJrD�;r+K��-9���[�\������\�S~�����Y�2z�w����F����
x8����W������z�'q[�M��@g/���6F5(q�2_�0Ie��,Y_�M�k��@����d�U{X}�� ��IC��N�
��%rk����aq�ya���Q�e^
��#����\zt�~�"�=c����@2_�Ada�NXd��&`
c����m<�%p�"��:Tu"&�'~��k�+���9A�2�^��-El���Q<��^�7"�8��R|�`���t��x����c�4�R�
�8V�P�(K[W �I���S31��p:�pz�:��Jy�����WV�;�5c�H�-���=�'j����~.������)[a�C������5�x����G�Mi�zn%A����c���xC���x�fD&u�$�-)��N����+�������P)�D[#�9�[e��d?�"=�p��m�C�9MDB���U�;��.I��iZ� ����=��Q�	D���My��mv�q�Ul�i��
���f;w����qf���k�q�q��d!��'��7n�YL�ty���pk�U����'�5\�������MY��AY���Q��k�f��%�g���a��&2bI��?V^[m��X
�Gqk�cBLQ�b��"��R��1����U6����zga��}��s��m�O�:���tkICi��WG��9���{���U,0G�:�py�w�����F/��������1��:��!����C^����+\��c����B1���������EF_�T��"�DbI�t��5���U�S� D�Y��d$�������Z�0N��:&����1�� ���T�V.>�������,�t�@7��L)�����<��m������?����g{��������?q��'�HOi.�����b���	����u��I��d~M�/���"Z����}��
������������=?��<=��<���x2��Y�$O�������o��Vj�g�N�8��-�������u�`~6�`���	�'Rw:���)��4�.!A���tO���<:kF =�Q�8K��h��{sA��F��dpV ���������n�0��`$�|�\!NC	�BCgT�DQ">K������1����I���������o����"
�#�F��f����(#T$��K�Y:��v3E�AL�r��9%_"�A���x�yn4
���P���7�BG4���F&SXa
����)M���J�Q������N4���K<��r�)��x�~jFNd�mz�)�������*��X$g�@�2@�<vT��C��+#rQ-81�p&0U2�yfI9!N�������;�K��w�X}��<R���S������0Q����	��������3mG)���J���d�?���&bI��v%�/ZF����	��B�M��T	X+��8�bO��?�TQ6�#���I�+�C�F�#Z���<>��&�s�v�`�,P���x��I.}���7U�4mk����A�c'0�v�,s���,tOtX������,���7�4a���(\�G���]������:?������o�m~�����}-p�^9h_�mT��eL�Xh���	�V��D��z�1�4��K�.���u;�/�R��3 ���O�0���!aK�K/����!�oa�L���Z`���8��T�~�k�2�9@�t��@9���("-�#i����H��(HD�B�&T	�qf��_�}	=���tes��v"c��y�O	����l��j�q�(�:'T	���
��5��cB	�p]��W"�*@�hm]�s�{iK��\��]`28��5�{�QA+��j����1�/���
Y�H��f.<L�A� ;G�@>B��\9���|`��>{�~�?z������m����%L
&�3A��F{@����j�"�W����3�"1�/��|��w���Y�w��Q�u=2�����V�iY�9:.�2�e��a�Cx �_�J�W�������F&�=c�-#h��f�,�K���@���Fw��>�D��Nm750�A����m���y%�Z {�t����P������5�/��
���;�pso�wQ1{
����;p���m���`����X�G����L�<�Fh���)�9����X��#F������������H���*��vSMd�jFO���v���Y^�����u�U]���4
�v����8�>S��!�	��yx~~z���2��j����'������_�?,�%q��M\/������p���?��&s��P'j���oC�����x��������{D�=N�sI����lc��������1�u�������`2��ISFVP�w�x��4P�o6���vw"�\���8�\�q�8����v��^:�C�9q�x�qmR�1�N8����u�X�.�NU�.�.�_����c��3�mM �0�l����
a����{�;Gj�l��?+<�i�O��Ti�j�T�5���+���*X�;��1�)	G��3}���������
Q���D}�������y�~�v�:��1�M6��m���4����!{�����pj�'�E�j�q�P�A)|\;I&�������v�lXr������A8<���]��k���=�����)VQ\���
`Q��?�,�
���X��/6_�%�����!C@
y��P�������Jz�}�hw��&KC~�mOS���$�Ql�Da��I��1%	#�Y�S@���s��`i8	^�%���U�r! ��	������j'���K�p�)��R�5�}���V6���N>@���P2�T��������������U�&H
�D8�"{+ �|��+5'7#I[H�qa�4Y�?����k�?A<��^���������f�h"�7z���f�(
e�x"7r���GF�"�u�(a�8���t%tR )������w~��\�~�$q���]�M�d��||���^���G�!�X�I��w�}Y�b��$��E�����A��6��f�*�����'h�mnt�E�{����%�4�#�26b~�^\:8������d��}�Kp����G�fF�Z�����L���p�z�&r���q~��z$;(����-~��
1����c�N���X[,���	v�#����������C�`��h��6?p�B��+������P���)v�>K��7r�p)�m��(w4�8'�Z�h��i��S�i*/��������*O����=)n�W���:�K����������v�Po��zfDm��CB[r�+ ���Z�����v=9���l����������u��{3RZ.�j���W��.Du!����o`��;�������P����w{i�9nEQh��9B��}�gZ��?�5P{"����'0�$<b��,y ���
�h`�H��G/'�zu���f������`-V�,Ek�o�8��'i�f�����8�����V(jp��
�1=���k���c�!5;�<����(��0i�*u!�an�=�[��49����7����z��`�'|l����<��}��N���U�o���/0H�$��D�G���M���qD�hT��:�9��~��=l�u~�z��Its������&Xk]���J�n���99���
m���Wh����I�|X�,����F�Q}��tZ�]�t@�|>��a����R�v "Y����,�-T������s^�q��p��dTW��L��@��^��ay��<��X<w�W��hi�#�%y}�p3	�>Em������7+����+�#.E���D����u c�.��
#�:e�.�P��F�V�D�N^����wa1?�c���q�����5�g����o���r���]��P6�:<fh)��N�i�&9����`��M:����h����i���uo������%uC��H�*�Vw#U�`A��?|����&�P1�����|�Ao�0K�)��)d���P@���W���8y��Za[�
)����@���"���i����IZ�a:��J��C���&���=�H"����{���tN2e?�����5L�RIj����D��do�m�5G��c��&�|IWA��=�u����U2&���bp!m�+=+���v�/�m��`��|������������
�
#�3�m�9�zI=�fL��%�WH�W���c��=��������l����yt�4S��&���^�N
N<Ba�L��T�Y�-�"jb�V�7��n���$$h���f����ts�i��Ml61>5^[~��t���������!3����l���t�%7=p���L�8����P��UQFq�e��u�<@���h��$
��!&F<'���y�}�~k��;1��,�p:$����1?���4�SU�%:�b���"��&VpD+F���H����aP�p�(���X��dZn���em�AD0	��`�7�)KH�b�$�dM�O ����Y����B���b�S��������7X��#Ee��&����\N�q@b���K������e��S�r���G(%�"���H���D�=�~���}<@GwK�]^L�q��p�H��x�i�-��d��1���!������Ay��������(R�8�1�=@f��p���;��S��5H�'�Dh+�C��c�
5�t�DH�FB~��@�H��zco��Yts��W��2`�D)�vA����4(Ct����`�I3j��ASlR��%>2�����#�CXYaV�j����=%�b"�T�q3��������E�F�P��=��~�<}� c>`���=�,*J�0�(��l�(�L ��)�|OG���a�DwO�@���i����� /�/�gP�D����	P�'�	���x�Gp������yJ�Gf��"��^�ByQ*�#�6v�1���%4B���7*����F��C't�P�H���m��Ka��9�N��#��~�kS~���#[�����c1��o�r����;=3������*Jv��=���s��{��o"*1�(�����A5"����V^ym��FE�/0V�����&��[Uot�o�I��'��+v��N�#�2	�>���E�PZ�7�JD7���.
[���cm��:EF��VdZn��]��0�����@�M�oQJ	b��8}'�7Z�$����3'�U��x���5!���G��\��H�����A���Cs3B�Kq[n���^�)��$�]�t����7�v�K����:_��x/����H^0'*�@A��gP�x��.:��n	u��VO�WU�uC��.B/�A��v�Z�mPw�L�]�S��"f��B����_�|�hRAC���Ms�C��'�+��	�����~C���
�����$X[�=\j��|���("BE��P7�@i:�?G;�=q����V>��zs����������7�\�E�V����,�����{���]��|e[���^2G�u��r�Q����4��/�i)MN)bW((k��[ �%U�}3��f�Q��?��z����g�e�$g�����q�'���������o}��8&��t���+H5{	5�	�yLz�a"-Iu6
�&��d6Y
�����Q��
�$����6��s�5����4�����.�L�Ew�if:�X	���R��S;��7����h
���<1.71�0>d����R�Z��@gw-�$F���'�����e��l��-�zyk�s��T9�w%������L�����@�������#G%Nx�
��
��0x���3QQ���51W�T:�U��,��^M�x��rZ<,It�aW�
B=��J3a�aI#9��rm�FKl :?r��=���i���W�][����=2��EP?���
�at�<�	�	��:��E�������qM'K�����X�|�y�a�=��x��S����0�7
���bAT�9.�c�U���t�.E&W��/���)HU!p�&�H���P��V����?2J�/�4�k���1�j:��T�%$�K�%�X�C����E����#�j������rZtQ�u`��M�U�s{�x��)k�du�}�������/M�I%I��C_%�r����p�����2�!�p�w��n{�� �m���$ �<���:��C���(�����Y8K�8U�L#��!G�����6<��((���%D'������o�1oT\��u��DnDGg!��I�=��x	��Q���N���L�<�����q���Z��m��%��#}������!������F�$*u]����"M��{��/Y}��B��K2��~�`W�������`������^��~`�1����I+�YTf4�����w�H_������.Vz��%�I��L�3*���[KX�/���^+���o��Kh3����`�I�ydt�b��!�Y!��0|!�$�����:c����WYw!�b�	K{~��\�&k�x�7�''�%|�S1H��L�Az��x<�yFGhRd�������G�������I��l@�V�2�����\G�+F�d^!�a�ECr���)�K���CN<
�L�3�$@\��-�YG!&�"�?:�IV�bN�U�&/J�����N����(sS���n����`'
�������f����J���a��%rsj|�DuO1�?E�</��!��lt������b�N����$�����f��T0��m]/$
6����5	0�9Sx#,$1�2�����k�[����R���5�9Wu��q�@�?��e�f��k-	�t-��;�	��F1�?}��\�^���#������e_�)�B
�����@���O��K\SD{����w|b������6���y��f���@(��.�Qf�F�������MO���iI�����<���=�9�A�����M�
9F��p�$9��
k�)zQ�y�}����|ET`\7p!���0l���$����B�/=������ CKL�F���t
#Q�#�=������Ze����R�]�S=��i�Fg�d�m��9�0�=d�<�:'��pN� �����^�����Ft���B��y`��L�6�_����CS��e6�*2Qf��"�������!�a(�H�Mb'���0�z�1#sR����o���J�E����`R���JE�p�s	qV'�#����j�:���8F]�*Q��&1=C���	�&�d9����lwSw�cz7��Zg��F��^����SW�E��[zE�_�tWNq�6�4rM�}t���LZ#�L*�B0��:K�t�k��7���I�r�:
������,�~�sBt��8 ���MI��?��T�D�Bq��&�Az����Fl�zN�&�c!i:�%"-�
X�1�m�,���N(������Iqp7�����d�I>S���k'M��w\	=��b�k.�1r$�pJ�B�`�||����b� ��S[H�����0�,A��=��`�mH
v{(1�T�L�|$����8��80��'DJ����RN�����i�a-�&�
�J��e!��oj�&g~hxM��� �'E
����-NG��U�u�R�H�MX�.Z���T��<��J�����4�B���� M0�����g�����sE���YtOO���O�	��F#��,���o[��L&N���	���]�c����Q3*�\������2A�1c�{��`T<����)�*tB
��lj6�U����E��R8��J*ySg���A��'#'���#G�coH�Az�,^��=7P"1���y�*j�&W��V�V���/Q�������
���R�����������^
]{�b�_����D���3�j�]I=�o8GM}.nk�}1�f-�����$WX������=��Fg~���Q�,h`�|LP�*�X� �U�!���
�SRw>���E5�u�x�jH�t'����w�;@�:C?�Z'�K����lZ;7��0]*g;�]���uj@h����;�x�F�|3E7|���/2PS��+
5Oz#�]��w�C�|d%��.H�a�)l&�
U�gk3���k�0�M���Y���oe��u4��F~���r�H�a]�PBfv��������j���o�������F<���=;	M�����(oO3,����C�Y�� Ig��"L������<V5o�U�KM��i� �9������5����m��SLK=�i�9�V�w�;���1&��%��������U�L��2_R'�a�B�HG��R�KCH���'N�O���S���q���"�A��]�#@�,.�b�in���*����6L�C�
�O��?b��G��$X�OJ`��!�*yP�����7���uP��?������VRl�F���f�#���Y�0���XuI%��L{�H�#�K� ���)U�����.���6u�'Q�s�L�BH\~.�?Q�)"2��p�mp�k9�z}��%ID�|�4Y�"���fc�jr��*������n���%1��{���S������[��>fE�����Nh���Vp���{�r0���@�Q����v����w��l�&jq�
��d+=��PD�r����0��J�*���TCLQ�.��i�D�����n5��7�&��~��-����g�^�"y������X�2��HyFe���t����.�h������I��e�a��0	)X0����l�\�E*"g�`����b�Hf�d�QO���m!��42"�E�ozi�������;
��5��9���]���\����^b4P�"#{Q���\��Hd���cD�%������������*DN�	n�������V�����aE��M.-*���y�Q���yjT���9a23��kG`���p&4;]g��<�d�T��L��$K��I�^����t������ub'���`J��a:D�>4v�<�*�|p����b{�@���$�3O��s'Q@Z�����nr&#i����T��-J.��@��������mZ�\-�u��T'v}cgQuO.w�{������s��QGm�(���{u��A����9B,�����Q�#�f�[rK�"�|:��0��h�8^6�L���*��-Fz�,c BVXQ�&�S����U8g�\Q}q�U���P3Ez��cJ��C�I���Jfh��*��]��c�R�! 
�5%�qN��'�7F�c�b��-������]y��d�dt����rv
�{�=:<�;>���w�?H'�����b��]�m���
T�|���Yrl�3
��}��9����Y�5��p�X+�l3��T���6�"���|���(���L�D����sP�&MJ��,8�n(%�ot(��f����At,-��MXb�;##��������X����1��#"*�`�����8���m���?�0��J�I�I/���^OP����A'40�����(2���04���k\��`ks����`8�d�K��$��.���9KAnQ���$!��h�c7J�O%��U�0�X����CTu�����O��Q0*����F8������6PPsp���N������9Q��
q4��V9ZG��� k�LNzi���P��n���NqV��l:�w�����G��=��@�qT8D�����
g�	�+���a%��J�"P��~��>A9��E�"���o�����K_�R�f�>;����5'�8��"W|����@�vd�K���c�w��aTV���[��;�G�D
a�.6	^�A�����Gy/'�m�����#�4�[V�����&��Y����-/c�*y/b~��������{]��e9�qsT?�B+�{I��E�G��e����st,L������A<)�+���<�_�(���T1���As��c���~:��Z	���v2����������6�3��~z|�\�����'6a!�;K�4A��������m^�������X��	W���2����m�<l�(2��������>�������~`��9�9�M����8�����W�m1x?u�Kk���`9�YHV����L[Z,}��)���9��4����Y~��Sk������9e��S��L��b��.�Bu�r��c����b��v�KS��Q������������$��x8LP����_pJ]�t�/G���A���`�oL�OM7@�n;SnW�f��8��6�:���9b������me��8�tr���{����[\��%Z�+km��0tT9�!b�t�'�80+����Ij��P��]T8�S7��|Zd��,K�V��c ���D
j�5]1�	� �/���>������{��Q5����i����km<<��:��
Z��iW��5���losI��)`4|�N���H|�*]���}qN��]&��%8/�mme]~���wsH��M+�\���p��/��mC��7������/�w}�4��k���L��5�Z^m����5������"u��V����#q���d8}�H��~��.�k��la������c����(<�p�=�U6d��/�X��	7c�Y��ET$S�t�)2��^g�x��+�,>��tk{!����G��@��=��$b<if�����+`bR8�$��O`2+H�R�15�3s�?������5Dc��o��{�a���$��<��8�P���j'�%�
ah�(����h ��c�T�-Z1Wy"�5��^��D)]��K�xs�h+��SG�Ix
��������X6lb1W�<��\�)u93Udfq�k�/l��$�/-���:LV���a*��%�0^G`p����'�Oy��:�le:!����wS�P�+�e��J���/���&:�D6�`d��xh{����E��������L"/���n�*}�P�+�������~�
wj�dMG�FF9��ixW>�9�an���������������M����u�������$RK�h�fu�CM�_&	�X���>������85@���J��Z~�H�\l-WO����$~��L1����7w�Q7!��Y���|�c��?�����T������3�e�7r�*{h*Z���6��^z�XQ10�ZB,2�N��CE%�E����A��$�
\>d���#?��f����<�>C����C!!!`�����m�P�$	#���H?=�H&�WIuR�EpZ��`�Y�V02�|���pD�}�Q]j���N�&rz�����y���R����d�Z0��J�A�h�J\Do�������	����J��c\�(|Lp�RZ���������O�=u��s�u\p)B�#�L>��\"� z���_?9|�����	H�|dN^y	:)�J��"����Lu	��)�,���!
^X��T�������"mi3e�^��7X�/3������;`��B�Lf�(e�Bks��q(����*Mq	�l�H+/��	�&��Gg]�V	V��8H-"\:4�l���-p����y����B���������\.E�,���]�Z���u�����H�����e��t(Z�1�%(���+0����&��VK/H�*����c���#��q�F�y�ZI��t�t��%��I��2]5�2a<>�'cu7i?b��z��c��J�:G�k�1Zn�`�����O���RB�p�����x��dJKc��Fbr�J�Tn�=*�	9V��)c��:��x",q��|��;�fAIu ���a13�t
\ItU�=���*�kP�\�^���w�^r�vY�cl4jD[�e�tm���g_pk�|�����8��@Gqb�;�`c���:��51|cVp���%��9X�W�G�b����@r�?M�H�e�X��e18h�����<NN��})P���6@X������M�i8"�yN�xTQ�����z���y_��O����:�����i^���:�LH����H�@���*�o�Z���9�%f�}h&fP�i��Tx!\���I��u�~c��*���V`�����a?����3�|b�&4��0'~#.�pZi���"��z�U@�%�<���%t�F[�"!�PH(������� ���<�4�A'P��L2ea��:6{����O��;��AQ�������*z!�>��� A������)'�� ��$E���=�t#��(�ES���X�������HZ6��l���_*�'.����.��:;d���[��nK����!7�@�^���~�r���W|�����Vl�&����:�k���8�E�Y�3���)N�.���$+�q����$�U)��mc�����0�M���\ZV,l����a_��+��X�c��&@�]A]�!D�����)p�z��EQ�F�������h���L��~rhg��'�F��������>���5]��N�ZH<�[�h�������%�c�a��tyTa	��(��.U.��3�P��N�f��&����zKr��d�����������A��c�u���m��VB|3U+T�����!H�Vp���N���;[���1�\e08��#1.8D
;)%�:����;<�.��YQ�V ��c2�c'�$�qN}���x���$�b2H:��H��@��X419v]"������O���������.���`�...Zk��lx���z(��3��
6#+{Y*�[���Q4��Q���&�|
�6�8����4+�U:��C&�7��\)��H��V����h�l5��
�����=[��������h��Y\�:�@K:|�H+���1
���`�/�
����e&��$��ng�.
y������O�����Z		U������YO~��U�"��[!����
dHs�@E�P��2�cj�,(��3z�����&?��qE�]��;[2�n��D�1��Do	�#����X�G�������!,���}��5��`���Dmm��4��D��f������EC�n��B�������^�����#f������q�����C������W�L�#���N��N��pP)��`�v"��,�6���m|�;�������Ag��k�	]T���&;Z��$v��!4���q6fwR��<7�t\V=�E/8�K��/	�6O��LKr0���c���AN~����E�>*�p��*��p~
����j���8u���
�c`%�dGS�rFf��IxM�l������$��=�����B��Qp��F��H�~�7�wN�Q}D��T����_���q�o�K�y�o��3�@Q�9QzN�4��X+�q*��1���r,x�q�9^^�A%��4ER�s���[D7�l�3�����4���
o����}��������WQ�z�%�g6�[���$�U��0,�k(�|Z�n��$]'���(:2R��C5V���>�S���	6�?l�U�t�W�j4b
�\�jm`���kf��t1cc��8t+��=�*E�F��	�#���x
<t�z%K���� N�I54Hk�OLa����8u>^�
u�{�?�`r����U�,�L]AU?���V�d���8Er<�i��
�M�tbN�g?�����M�r`�H=���T#�c2|Q�/r}S+;�8�����d1r�}T�����ZMUq"�vK�<!���������O��G{�.z�o��]�.)Osmmi�z��|�2��*�����^��P]N�}�G��<�������s
�$����wrYm,�oU{C}g�k27o�"�������	Uo�&��� fA��#�]�X)�����1�B%���r��0���g1$��!��Q..��:hE�Vn.�e�����@��K&�g�_q����
n0�\�(�b��G�G����5��E��Q<����\|	$����5V�����Z��L��_���z�T�������8�IX	�SV��,0>���i_��������k����t�������]�����)�(��M��l��-"���Y���m����x��$���g�L]n�,�6.�����=������On������������������=���#�b�w���^Z�^-w�\3&~��e~����O�����"��'0YY�Q����t���9���L��$�7��K,#]�ho2a)'l��a<���qb���'D�B�.��I[�T2��C��������>S`Wa�IT/���h4Z��H	�!������ Z�YT<�;��q; x��~���F������-�������'��m��.)�[	n���|��x7�{���Ut�
8��MG�
���q1	�dk�N��R�����M1����K@!".��g�S*�J(P����=��q��\mT�X��N�����0��I�(h�@p(�K/X�!k������g�	_H�%w�%�����7I/���N�����e�����=��������E�����C��o�M��+ye���yY��'�wD����K�2>BQ�4W����HL@ie'�u!;�&oT�
��y!�Z|�Bm������)Nh��*�dn����������jk�W��%��2������t��P�&jB4����+��j�Q-�<���Z-u�����:�%�E#_�}L���1�9V����/_/,`B�����U������@:y��
]��@��#z���6������c^��'���+�A�#��+uA�I��R,�(J��k�gl���v=6�Z�M�Vy�l|�t]�wt�����jv��������K��8�u�_�9AnyjW��|y{iC��lsZ�me��u�m��{��Lo�D����%���-:�%��z+��T���kMSm���; uiq�����a��/��^���g�����[NUT7���o����h��4ST�L:�:\��V���d|�O�������%��M8�u*~uvh���F��g�(4�k�C*KHH>��;J��~0
�����L�+�
3:��<,m�$�n]� ��v�	F	�Y=�$�����R�rx��5�T�R����`K��Ve�E
X�q�F��I�|�H^R^n����%�u����������\9}�b#���4F�*~�uj��
�J�����s;������ L6���4b�!����_�r���G9s:�
��:�n�K,���v"{p��3b�|��DO��\��~/�OnQ0*|I�������D��_�����L�fZp�<zQ��PL4F7�� ,�u��#d��eA��"I(h�,F
�Q��gy����JI5� ��r��)�U���E�����P������P�����NVZ��;D��W�+i���I3��-�Ce��^0h��<Eq��R���]rywt�f��g�:H�vN*S�=�q��{�;�;�;>�<<�������+���Em���Z��U�j�stFn�a�<!��&�����8���.{Q�C��p��x{�U��J%�r8ig���q����^ iNX4�y8���('2Ki���m.�/��M��"���Q_se>#�e[8��G���3.���<D:b�K�
Ab���\��-�BS�Oa�Z����������P{���pOl7?R�X^D������+�>�e�K�K�y7�Gb�6$��{nj4��G�_���bW�zHm�~�I���:������2��e�?��&������r����VM�x	n2 �6��*g���S:��
�)�>����
�@6+e�0���^79����PG��*����$����eW�h��Q$�d��
`��9��5�t������At�U��:U4tKK~���l��w��Mg%9n{�]c}X��?uO0��K��*'-�R��
�!P��4�:D��������#��rm�v��y��y�D�9;@������	�|���zf�E^�������:t6-J���c��
��aiy����&Q .Jp��zD_��[��(�F1�!�����R�OZK:�X�������,�D���&�]
���3��)'��������<�U-�	��(�����)��;D�9Ck��~?\�=YT^���.������hJg��|�-��G�8�D�dEL6���]b�V�(��\�8�����[���L�1#dk].�>!Qx3�pMN�
��7���OA�\3�1@?���w]4tr�{�m�6��9�8&�0N2�ij$�~��nP*���TH�Qtt����
�u��;&��{���n�b�l&���.�Z{�>���N���
%u(f��i�@��2�/�2�d�/"
#"&^�>^�3E��cA���BLL�c)�"�'� �zwb-~��(N��nn
�����$DA�5�����W&X�.%:���eY��9-��f:��:
g�$��Ej�d5i���=b���\����[�~�+��@�^�]�f$!�j/�	���L�FW]�7�^W����:l.��e���<}������kA������\����Z\]\r�����C�)1����v�b�:��jZN����wx�*^y�Xy��<��Q�k�z�5b%�R�+KP�V��`'qDs/+c��-$<��A���?�Ho��n&J��YfVA�/�l�qZ�t�?���������q$
7�n�\#_@�F�f��Z�c�>�F���������&�Yi�����,:�W�sJ�)��SrP�A���}�E�����)T�>��mN���3��Tu)�9�p�t%K�6��5 �UvF=�24�L,m}���b����@k��JW$,5�na{�	��Ci��3�Jn������c()1���N�W��O:����O���l��K�l1��DW������4O�� )�IID��BW���^h	�?F�i!�+A�z �Jm�B}�T����[`#�Adh����B(���S\nD^e$2\j����x�~���T���i86
}���I����19�s�;�w�����N�������v��.����(�Yf��)�Jp���6��KF4�=��������{�1-:�p��G���t��8i����U��|]) o����}��==��������A����3>�{��|�Eo���Bm��'"���S��=��U���%5������c/��Qt���b:	����G�8��	��?��'��K��S�g{���z;�������Pw���[��
tI�����"!�Y�D-	�_�!�4T�t��q���(v]f>�i�i%��?�7�|O.w�7��W+2���� ��R$�������K�����@Z����E4����� ~XD��d�+���+�r�W^#O��B�8��#u����wK!������m��L��XI5:�ZT��gaQ:i�S��[G��=�USH�Y<)P���c���Ywg=qS���n��y�t',=�-B'��Og��o{Z�����2-���6�����#��-�H�� �Y�>��n��v{�rw��M\�/����|Qogt���1��w�6����Ah�O�M��94R��A��s
����>��+��F�`�x�r��j�t��U��@�J�����9
���(A������&4;bTlx�y�_#�/��n���X/^2�f>
����F�F��>Y_)h�a=	�d��KJxZ�4z��a7��.������q�W,��#�-LpHb�C��4)7���i� ��Ih�x~��W���B�e����UEY���x]���S���&;]Ih��!%�U���
��B:k�{~��V�+��>G�b�8l"~�4/\rvx~�wrxr��W���8H`��9�}����_�X���(������3|���p�����������m�������C���.q>��{���:����l���7�[M3.�|�����Y�[���n������<}2�r��3��_\?�1T�'�����G���v�}����c�f~��v�bk�nV���m�����xWd���,�����=x~��?(T@o�jh�x�k�t?��i��a����������s����-�3��3_/���78A����?I���)����yi��}��� �=J��<H8E��=gGp�~���o�����I�)�tA(t�7�A�T��ZKT	���}���S�������k� g�Y�|PB*�\	���j�m�~u�c��:�;�4A/�"V�Nr���J��i�okI�����HL��I�`�g��"���7�v���M�m�A����@"����KxV����#N#�[���F%[|��	!��������J�����4�X���/�J�85�S��B1P��X��W�)�S*~��sKf����z0����D�Xn~���'5"2I�%`��X����k�r���:s]��58r��#�v�S":u�v9~wega��Pv}$�{weD���w%?_�+��������x7 �c
h�!�� ����5L���@/X"wg��	"b4��s`=p����A�0Cs��`�?o�U;o����Y�Z<!DdB;��*��b���R�~�f��
���/�FK�a�ST!�J�����	�Tm��REZ+iWH�a������'oA�;�;��#��
CU��>]dh�`�{�U�=���5��U���ea�;�I��.��0�6��3�����
p:��>�������C�gC�e6�0*	�b8�Tn�����*���-�s �L��[gn�@�}�up~z�l��[�F�}���P8��c%������c�FI�f�	RCG������p��P��j����3A�9nfZv+�~1�`s�����{rn��2�'i-dI����^�G���cmz�S������,i�*�=LCr�hM�9����_�)�W�T����a#$ai<�����;fz�--�Z=�n��3�q�py��1��J^���7�e����\X�_����/�*���F������8Q��L���#8��G�R�h�]�M;�0x�&��Yg�
�O����MKWG8�� ������W�`��I��#WOG�L��q3�'Lr�V�Q��L��^���<;��c��s�[D0U���k�q�$O�x��i
G1����_g��b�y6NL�p]�!���#�-� �]FF���c
����`����M`��\=z�Jy(?���.���j��sN������� 
]$�$�N�������^��Xz�%����>�A��.��1u8��@�Fgt�����H%��Qo01=��������\��T��u��{GH�WR���N�������~�����w~�oS��KoG:r��#B>�`&��p@����
-<�8��1�� #��"��ne�.��,8,���p�R�2	I6�&��30rg��a8�a�n�s�gR��b��/���7=��]X��P�	A/Z�0Z�M�;�\�	[1�}������c�q;��i�t�����mw���uqS5f*|�btd(�������c��r�P�B����
/�)��i�#�4'�7z�]2 ��\3x�i!�T}����y�)�p��
j��nu�j&^[s�;g0%���9��j=P����x2>	o�3�PDT��Z��������U�����SZi�$�w�����5���k���v_z�	�����|�dm�Zymc	g|`Fd!�����=�s)����?�x/�V���
9����K���lM'��#7�`��K�����u�Y/��L�����g���Y�N�iF�8�=����We�$��Qa�u����XYI*��I�/��z��t���tdF�v4��Ka�X�-6G����:�b�,�7�-�y�[x�L��D�Z����5�?���X��_����N��TR����y����jpE�����&D�+%����B~i~U>R���{_d(|��WcL�G���)��#���W^V�C��3�_�1�(<��#�P�#�>��y�x��/<H���w���UM����9���tac�
~�<<9�iH��s7�wy���CMCbh��!��iI[K�n���C�vpB�

y��JmM"��&�*N)H�j�
���!K��pa����do�'8��l�v�F�����4�#��+�+z�w�`�%$���X��6zE����\>
4R��fh�������^���lO6��N����O��:u�������Oj�Z�'�G�LrJp� /�wp}-����f(w0���]�R�7������s��o��br�w���i����P�5Lm.��,�I���_�Ez�@fdt����{	h�d|0��J�v����j��5(iF1�oV�����oy$pr�l�V�8(�/o �-�F@:����&��6���L����.�k���sEO�G��0y~���)��J��';�b�#���P�a�G�s������7������6G���A��V����`����W�����V�R�������M����*��r����5>y{�9�yW���a�+�����j �M���:[
@���X
���"��`��6a�����3�R���d�;����-���"���q����1t$����M�?2�g�����S/;t���2�O�	��4U}r(�#d�/~�6�R�����%���X�-9�Q�I�O Fw8�g&�K�u
�DSLg�Xn@���e����v�o�����`d�
j����l-�`2�!����X��= �-�_��p�a�����{���w�<���f����;�)��~���RdbvM|���&�|�~�3l�7p���f�A�Mj��qE [��W���{���-�X��5���%#�R8 ����������Mrz���K ���Xq/1��[�m[��Q�������^)�L�#�� �s�>K�y��X��%y=����2�L����sc*����	8c��"�I�*��;���������p���k��H���/�z>��AqT�6,0oTL;�o+z�M�>�(9�q�����Q������n�c���>GU#��P?����z�`�\o�Z&�|��M����G���8������������yb�����$*�BH��mu^�o��]O)��*�+B����U��I+_O$
��5d�1�0�,�0�W�>�O.�c�/�V�c����$�(���#4�v�|��tIo|����&5~D��&Mc��E++(�@�;��=%��Y��@���^��V��Z0���Y����H^[�N����f��a���\
���v���R�>.��^��=���)F�������1\V�/.�'���o�L����u/���m���,H���R������wP����P$`�5f,�B��1a��F_�-���	W(S�g�W�Ex=_��.�T�q��v���S�. dQ�������d��o������0�,2�JCF!$�F)��Q�f��"r����Dw��������i%�G[h��)��i8K|>�FM^�b�}��AjB�T.�Z��12X�)�}���J��"�0�����T�28]�3�N���p��Y[�Mk�����a~�F��7������=���94�3]S~�Q����/���f�%�a��-��?Q�e�*{x��4�D�yBn4dmX�0Ut;��v��a��p��7,~��5�9nX�w]�)p�3��1�m��;r��i7m���e_3c���M�3B��n�%�N�u?v��;�������t0��j>
����L�h|�f9�)�xR�\�p���
_p��f>v(�||��C�Z�|N�j3�n����>�i��n�����s���|>�w�/1���<�j��*��\���(�IrK"�����K���z�3��(�<A��Q*X{<����`�����6���Qm���u[��n92�����q�T�eV��=�0���5i.$_O> ����{�O����`��������r�=�w..��AU��L�I���G�l�]��]������������3
gO�����D�SOL/�i=yHQu���D�^^
�������9i�>�B�N\?��x�����G�"���.AI~�6tQ!��]k��
@	l����p�==�0M���)NdZ+7�Jk�]$���H���ip��q�Pn5�\Q��(��[�Fq�?���k��@�G���RZL�]m�����m��m�����Dq��J�����~���Z��x�����)��YF��5��~m����V��xxp��w���qd�����r�#�mb���_�rY�*��5����<;��
5�r�`�i������?�~���\��9Kg��Uk4�{�� h�j��1�����
��E������rU�k�Ck����=��G����y���
�l.6�1^���v@�[Q
�T/��������U}���@��5^M����(�
��"�!�`��n�
���MV)��z�N��A2D�>(z��~�.���W�L+�Wp����QY�IL���P�K�h���K*	��"�=����^^u{��w�z����3�J���l����\w��,���'�8$�Sc���F)d��������~���'�Wh	�x��~��|���_��)9`��	�>����%IAK�MM+;�/OF��f�a�\�dY�������{��������0E�c�2�V��Ek�>�����h ��c��F)L���`o\�A.��������,����ux3�%��vP��A�������g|e�_?��	>���Bk�|"6
��2K����q�[
��G��';�f��MD������v����t�v��������&�$g��k���V���<��;����l��nc�?�[���Z�V�w\kgoavK�Ff�����Jm�u)��
��1<��U�\�R���G?mJ����8kN�}��
�!���<��s������"�`zp1�lU,��������~J���>��	�@�g���5~5/'�?��~���0k���2.)��`i���LR�1��N�_��%l�Q����^���b����#�����"sY��(i��^����:�{C����=�o��4���d�<���������Z����(�H?��1=p�������-�E�ZM^���"��+W��!#�E�>�����&����tl����[t�
�W�kV8���o���-��-�����;'�Wo�7�D�r�@������k}�][������yV�.E~RVqo���B�^�o��x���!<����[�.M����(T,�n�T���`�[k�Fp7B"��w�$��M'H��r
4�r��7?���|�?*��3�O.��s���xb^����{��X�����#�P�A�g������j�$�@���Bxi@�W"�4��q�oH-)`�
���#F���S$Vg�jMK�!��������A�m�p;�'��s���9M��"����ZNzM^��~���8\R��C�'Z�B_k-
H����;(��3���s��'�����vAo�	��$2��	le�k�PB��Jr�K%��1�b�o��\����pV
1�Qslk����L���R����O���c��	%�
kc�+��^dv��=�L�y^�t[��j��85
S�"��D�`� �x�Kb��?�������2�L�7o�N�����E?���^X��z�6,���I;�{�,�,�
%���/FT^n���������O�[u�k�-VA�M-�MSDE��X�#VL���>!��'�����SE��js��@3���A_zu�!�)��j�.d�|ey�zMki��g! _���������+�k��$�S�6��	��F5�����������������E���/�'�WpU��v���Z=��^NU��]������ ��s�,�IN��$�:�Rq���I�x�)���Ux.S�+Z�[���T���K�V���s���YK�[�-)�m>�,�"��k����Bu*�#P�&��aoC��X%�I����yJF�q����$�fV��4�H��O���F@�MWS���41MF���;>��[�����7��K���'��H�.���J�{��9j�I
7e��)�;B���#s�7�����N�w�6~��],#�������3Lwg��
���E��RR�|��Y��m8Z�${�����!��F�t�[����{S(81^����������w��a��Q�3��S��ifT�~��t%����S�5�V��O�5
G���K4�"�{%cj%������!�!{A���5�9���8�Q�O�d�3��(�����F�hLI�d��G�x$ �[�J���������z:NrjzM��d����oH!e�aIR��L4Z����	#����)T��H_���J�'|���D��X08�@P��P)@T��1qR���p���}o]-���3woJ�*�3�O�����[�,���(�i����`I���-S�-�?`�.���5�e���h8s�<����u��YH�>����U�q��R��{�5\��F��$A��/�WJ+�U�W��g`w/��Q�r��[���p1I���]���R�RS��,�N�z8����^��i9�[��zt���.s��a�����bgZ"������"��GH|����"�GI�||�E�m��������1����w�����j�aG^Q$d%gP���q�E%w~|��S��D�I
���yjFl0xH��BRk������h�����{�9������w�z;q�c�$��P�-����G!.��,t2���Dqe�6�#|\-y.�s������
)�Q�����r�(��8g��n`�!�o2���>i6c��B���H���N;�g�%�����4M��%�%�A���3�Q�1�����<����J��F����sK�u����������������`ye�?AD	P���a�:9�C�y�5����@?^�Q�
�2�k�x�����F����>H�dw�H/y
��7��!����0m�p���se+�i&��J:��i�T���
�F���\����;��*��55�%����R����f�����?j���&�|��3�	�J����e�O�����������������AI"����A��u&{1Y�V�2��7+Y���46X<o�+���]D����a<*qs�%�s<+{�YE�B��~I}R�j�j<�1H����,l��]e4�j��E��s�d��x���@Q�}��)�J@8��E�P���V�O��8}����Q���2j�jD%��������)������
�K4��Hi�0L�~1�#w���'wl�b���a
��CU4'U�t���l�}nB�i�:,��A�9W7i�J�0"�n�'xgc^��T����8���fJ�(��7�����U[��H,h?�H��YU)�z1��:H��P���U��AX�`jd��I���������D�&��E��]Na"�L�bK�/S2>��3��N�M���:$
=}K��Af��	���s��d�j�I~�S��
�$&��$�L���;H��R�������B�L���**���E�����Ocy��"��B���@�.���G:'
2�Z�D��\�a�@��1zr��bz&%�k�������x,Tv5���4�
��+�:
��`M���H'�jK��aQ���;|k�j������6\!
��H
����xi��E0�C��HS�� _\!v(���M@��J+P-��Hp������"�(Z�j�����uf#v!�82���"�����d���8��� vS�^���N�}\�]���s[��1'��`T����~<uD��"�g����E�F���Qm�#~=��x�q�^��Ds��.��Pz�F
�79����i�������7��O�#L
�������7t������jW��?b.h8��
E^8P@Yb�E�M�/}V^<��s'\����x��8�2NP���}G��J���TW��,y���X�LP%;0\�	��[����M�Y�@p�'b�	�������y8�/���&�,��/Up���;�e!1u����&��VPK�����)�y�db;7k�����GV`e�Mp#C���2�S��N%��s�l���<�����|;R��K��������-�����3(\�K�F������L1S���z��P����
JO2!IP|F2�gh�v��.�0��(��j�AS����+�m~e#�|�y*{Z�7nH�PI�0!����`+��G�($dM�,C)$��K����T�_�8��R���RK��I	�LL�J�[`j<y��P��"���c����1L
���Qe�A��Zss��;:�.u�������U��bo�,��B��5P
��)�p�����^�������Jf�!"�-9����S��

���%��M����@d�(���_!�3-����E-Y�S%'^<L�����B��q���BdW��\��S��7�w��������4X���	���1t��[E����S��!YDN��1�L�w�������W������}��
�p�0���GqPr^��$H�w����{x���&>{s|��}�&�`'F4�7D���^�=:99>�����Q�'G5�������F�=��A����=`O�kF������3�YB����U��"g�<
�e������0��c9�{�h���g�>`ZI}���a�����Ju�1k��q�pz�~`����b_����4�����af�^s�bz��,�Mz��S0�/R#�a$���I"E��
W�����L�D(�x0�v��`1'���8���O�mz^@�P+�e��.#|���H��=ZL	��K�������	��bLE1�G����Em��q�[EL��@vN�\s��c4�`����\���bu�kP|��M���R�
T�&	4�-;�ro����W����+cmG��z*��!�
�7#�M"*�nh�h���b'���������/��Y��������%��l��^�W�<U�j��I3ZL]9�.
����PXg%��@�jU�����eu��'���!�c/=���Uya�n5�<u��� ��KK���"y�5jE)�8�����k�����m��BI�q����C�e0�rff`0(@��g.���$
�-e5��d����W�I*�G|��J�����
����2���^�6Y�Y1A�
G�:t���0��n�����{��[��H��}FPi��,�'|���6�!sZ��y�}�P���J�,��)�����	��J52�}�E6���<�
����G��N��<�[{�s��7�T�h�^>�(�fr}P��*ZB,�I>s[��)�Hs��C��W��`�}Fs��B8xU�4������M��������H�����`C����>*��"�z=�'7u�2p:����S�������m��ga����0i�o�g��&����Q#`�d�J�%���,�:��q�V�6NO�l8����nk8|lD���
�k��Q��S�����)�z�����'���&�����u�1?�N=hd�����5��0U�X0/n�����dd�(jw�ZN���E�yE8Si���3(��+��5��V^��n��vkJu�LG�rc�2�zV��S�u�x�+��RX~�h������a�r|(�33=4p����o��\�K�
56���.��E�wsT�"����^p�.x���9��T#V�J���xbx�=������SrV8=+����	�}���Pj[wt�Pq$!g{��~��b�dB2F���t:oSr���d�)I�d.�j��no�C+�)n�0&3�<j���w�T�)5\�'��EM��Gh�)�Qj�u�P-�8are���u�����"VG�2!�3J(�Z�
2q����Ak��G��F������CV)������h�C�N@)�����&�9��+��/�(Z%��2����3�$�'�?.�!��������*v��#d�@����b��/F�J�p��n��x�&����tQh~��'�ui?�2Z�y�bAe�������|,�����n>�hn�T<��� �9��K#0���/0�2BBe���" �VY����FmQ5QN�������X�l�TjW>^e�[���������$�v-cU=F��k>�'��t��Z�~�
{���^�Zm5k��������>�P��SEl�Y��`>�ho��*�,��@L�b��T��W�:���~���I��w~�-;_#���A��
����r���������+�����`j�/����0���%���Fe�i��?vZ�j��7����Q���m�7����y�����[H����a�T����<�_K	�=.�F,2COX���T����W�
0b�'��$��W�i����>l�J-mE{����i�����!z��!�$9��5=�8��B�{��S��z��"��4;���v���	�MJ	��5wG���?����������d�P{�<T���� c����8p|����2f��@��WU����+�k����?��[�b$�V*��)��}x���z�bF[1�!>����:g���������2CH�����C��&�{G������J����@��{��/��%TD�IH���b��qWU�=��qK�"t]9�H�_�dU�/#by]�'����=���C�E`]1V���Dm9����^c�f�s���x<o�2���0%9�&���~-���L:W������:��D]���5ux�J��z����F�~P�om��������rA�+��;5�+U:�Qe�f�`QE�
��U��U��\�Z��.�h��&�����k������?�����z��n��{��5�����.��0��j�����b����y2������y�v��n�v�{��=��[��Ak�7�1��p<l��V�;�f�U0�@��j���^�V�oa3���M4�c������~����k���E��M_m�����^�;�>{h3����������������)�{����y����R`��P��P�jd�^L�[�����V����IU����p��<���G�e��i�Jg�Y�s�BK����=�n��y�|�P��� ���v��|�c��������������Z�w?��A��������E����/��������?�jS?�T��oX+T��<��2��\v;G�]���n��~������ET~i�W�k4���!!����(����0nqq�T��a�&�^)m����H�����q�3&:x����T�ncx�n�k���Tk����p���������I
@�x����=�d�Z������@���/���om��$���-�~vUv���P��Zf�M��^C�)����_[��p�8���+{�x5��.1�g�DGDZ%g1c���&fD���M&Cb����(�N��������6y���Bx���Co�Z�	(KgdQMIQ����R�a2<q���Zy����N\������&&��R�,#~���+�N�N�B�T�C#�������;\�NW�C��$!Z�!^Q��z��mH^�[�P-\{����^���M����3O%d}?�8���kQ��Q�V8#P�Q�T8�\:+�$$��D� 2���T��^!b�xz��
�2��Er�sX���k��%5
!�aP�\��{�����d��<�E��N����
&!�R��5aOg��`�?D����,{����#�����=-�^ �L`����m~M����T���� �
Az\'��tK��0hX��I#��a�����M+����G�g~���RM�OD"���%6���R�w���D'�TA�%I�&lt���h�Q���W�
�#9���b)sP��Q&M��CG�����0#82��	4q,�x��7���p��\(
f\��g�)�y��vd����_G���j����V�a�0�RL1�2�"D$�o�����[�F�<��������>I�!y_S�[���������9�4��mB���+:����f\����N�������"
��V��m���|�>�;��nb�q�����:��;����ym�
M\���H��@���"4�)��t��1�6�t�.fO����#n�����_8f�G�0X��t����!y���t2h�����@�(`����S�!1�4xM*�=��@�oPtR����\��Q�K������Oc+��T
�s���5a������0x�b.�B��NJ����K����}�(j:�L��__��n���n�9�,P3
O�"�ht5���:���O��M�_Z�R�!0/Pw��8iX��8?L���0C��[(��*��!����(���p&�����Hy�+�w@����^bgfucV��������0o��v�'{W���(�q�6�~�b�����2���+y,�����f]�g"N�`z���U�"n���<W$r~��|Q7�:�p��
F:�P�)�h��=m�Ta<e�L��"/T�����S�=F|o�*��Bt8�����D/�"5���D����)q�6`B���@F���2�i�@��B_�Tp
U&Y�������G
L���b���A�AC���n'%����SN�dD*��1���fIa�$���}M��>H�Y��Vv����d������,��&��t��{���(b�F��'�0Ib�H��^Y���5��pg��H-F�Tk1� Uc2���8K=�cl�3H�KK�gb8��`(o�:���d?�~���y�T��~ruVB��A��1���1��eukP��ri%�F>���=��f`���4R9��YKN��!YH�6�q����/��Wa����Q�������B�4��lN���@	s�{��"o������PKg�.��e�=��Ih0��YD��A*O���R��'���D�vY�Br6W��C�?��������U�Zj5��La��_���ZDkzUi������c�&�6�����H��.��������0e���������j�(|�D�F)��;:��o�nm�3��X�X cla��Y�K��Z��s����+a�
����f$�q���=�m��P��e�x:
F���s�$vb�6�"���D��s5#[�	�$��6�`�8[$G��3B�����X�
r5o����x�0��K������t*�g���V�d�]��2bX37c���f�!5����l�<��M�"�M��JGii��� ��&F���� ��� ���j9�ng������[h)�X]5(Wu[
��;<��=�^�;.�lG5���������f��b����<���b�O:�c�A�^s1h9.@N�N$��z�r�T�\��C�����!�������*����k���b�E�����v���0�~;��
�gR�E���~���T9��,s�p!�;{D)��Fp��V�z�gzK�3���8���^e��0�!q���uG�=���,q�^09p��+����B�S:�EK��)��)�T~S�o��p���5�EL�X�-�bU�t(K�sy����CMO� �E��M#j�0i��D�_G�{�-���
?��$����^?��R3(]�s��Y�lk[��T�ERVv+�S���x�"`i�e(��L�@�:J����p����=�b������(L�8����)E!������<��]�,���r���J�eTr���@�QI@)�/���m���9��i�A��,�_�&�����R�T�N���J8�1f���� dV������D�����rOJ�#��&hc��0��l�i��B���vb����eY]�I�5zr�p[�)��T�d�����{8�EH�"�Zf(�t���( �e�/W��3��Cd4PZ�A��E��|ILI1-o���8��(z����h#�r3�0�����F�/���g���`�	�#�e@]��Z!-�me���VE�� -��by�5&r��)&��T����Y:6U^%%\QI��,��\�'�'�]�<��	z��QcJ��zvoXE�-We�P�����Q�.������QB��X�e��>�J��D����<�G�����FF�GAL�x������*��7#8���(=�1�����H2��"�$����6� ��bG�!������$�Rt� ��mD*D��F���.��*�NWEe��sL'[�E�~��e@�pE���h��e �H��q*�h�]S�-��dj�$�*(<UY2�.-�5�����K�$�&����d[�y���$�&3%�$�(��W{���L��<X�w#K�����T�2���1��)�����pz����:����`��]�5sC�gryS��2��5���.g�LK���+����<�KNO~��|��3A���`MY�W���Q��������dU<TBV�{��U�[��j���4�����AF��5m��Y�Jq�T������f�����������r>^���,���^���d��B��h K�:UR���&H�Z�����Y�PYho|b�����d�t���<
c���$�x>6�C���O!�e����X����
�-�	�J���<�h�����/���
����'��S����y��#��h��Y���"�'f���\VV�OQr�1e�b a��o��[�A2s�h�A��Ua���'e;�^L�$��K� ��Ez�@�hM7xov��+�i�"��,'�\�nD:91l����
���1��@��##^��"+ �
�Y�\���
+y�U�P�	1Y������b��4�Yy[���:X|�Ss�������8��')O��,�r��+V�x���m���fs=o.k��i_X�#��
^�6E92c!++�[!�EP�Q?���YL�tI�n����a�/@�/l�aD11.���9F�?R-N~�*��B9�u8�
������&������d^#y�Q�7�]�&*����nHlUl�v �w'�G��Y����r10��%��S,dq��F�U�=T����'�E[^��[�UJ����qyhI�E�<�]X�p�s;W{�X6���a�������{7����](8�4&v#	�j��~:I
Uih��\Y�����Z#	L>K���D�?�$�?��a��_%������G�rnr���xZ�q�����id�g�f�/���<#�98/����y1��	���K$�>�3����A5��y
\*���EK1�Y������_:�f�,�(v�#z��I��	d�/Yf��vm���8�y�8�-k����2��
K�a0�q:�[:?-��?����6���6�����W;z���I@*����|}���Y��n�5Z�D�G����?��?��;4k��� ����FP{A��v[��h���n��zN�G[��
-��:�H��M��A��+?F[�u�QU��V
��7��p�#�V�a�A�%I`��[���}=}��"G�$�{XM}���8��+���S��V3��f����fF����V���<�F�������v��d$���c�L�5d��=�	{g����_�w�����?����������������n���;?���������I������p;����9��_�����@=$��������dVc���CY�u���������7����X��~��g�����&�yj���}���;i�I��:%I���6��9j	*�IC���n�[����5p�T��}����8�2����F>g���Y�����O��h7���3��`��
����,^�Vr����V��Yy��7�ff�&d���d�u�Z}����gU4?�N���pE��`�[�.�d�D�^�;������HYi��|�����0��]m�&6�j!
���[�j�9:����s��d����z�09L5;�������-q������;��l�I�fr���jn,q�f@Y[��n��h5�k��SOw��?$�~����]F=iJ�%���WOxlF���MVS�#�;�����^o���9��j
������S�&�]s]w�J������u'�3]�Q����&	��m�hb16��;�V�2�>U;H���\&�z�YO�r�K�N3�cLXU
SD���)��Cc��(sY��U����.��'Q4w��j���M���7n��A�;���+{Q���T=��y�x1[��P��V�����k��C�j����������\��������J�ztE�N=X1_IyLU[�����1?��B�}DE��SI1���Q��:���1U
yl-�����f&@��<���Q=���>��F��qs�o�?��6��>�.JO��J��E���e4�V�d4
%jNU���������+���A�n"o4�~���X~���&��p(�������u#��3����~���(��4�v������f��"�LuUT��Eh:���u#���D�n�:�'�����~����w�7kw�S��u
[�H�l�����I�?���%x���������'��8�[��7��`�������ZH��[��;�2<�A[���������~&�R��'~��'�s�u�z�o�;���3�����7��W����(��������b�;��B�]|���������+~
\R�YH����Z&h�}|�GN9�H��~������������i�����q�����y���e8
i��x�_����6?+y:�w�������4�'��'�������������{b�(���`������Y�s|v��]��N�{%�����S�����S�s^�a��nC��(�������N�������,�UA��e��O����6P�_�*����q����y�K��,w�+��?_.�'+�2�q����}���A��AU^�je���N}^�/��L8�x�\v��=�.�,��1m����k���l@h���g�;��D�P�4�A� ��y�1�ckUA�m����	�_|zx��R��U���9M�Z{�z������O/2*��HU��<�*��Ze��DJ���E�tu�����v�{���Jk���h��f3xPiM��+��H]e8�}RW��?''!6�<���B���_�Z�e�&�"���fkJ�-az�|x���f����JlAe�v�)MoX��}��
�gO�KXE�^n��`���	�gE��x��<}{Y����I�O{bY����
�������p��T"���o�)Be��TV!�M�=�Ih��ce���x�T��\�������^X��b��ry
L�c��A��pp��qc ~��!�^	�SN��{�V�������[�������a�Z|��Ze����xj��z�F������c��w�P��Ne��G���3��O�@�*x��B�#��X�L���]0)�
|��O�g��t�8��K�Kyh��zb���������R}D	HTF������F�J5��	� �Z��a��L|=�����-������W)�f��qpg��M�*�E��+Q���
~�����p�����}��nKgb�?��
�F�G�a�
��()���.8U�>��Fi������>���Ad�+�J���/�B4b��f`��P�����E�`Y	i�kd� �7)V�CE��k�z�S�^P���������C�W�O�K��������'�5�uWy5'��Y�u?v{\����}�y3��{�F�����(�f��nF���s}I1e�W����)�������(���\t;���%�W��(F��<SD��`:������x��Y�O%�n������f���1]x��x5����
������<�	'�E0+�4��Jjaflw[ ��K�9a��`r�(�?.�i���)ij��h��2�`���dIh��� �h�w���
Ff�m�,�pr'\���R �1O��J�<�`G8H�58$3��+R9�}�?"�v�.F������:���T3tY��l��������y����C��p�h��?�S�L<�a����F�
�vIB��>%j���v������^"���Y�Z<`���������sr��	��jf{�(<��%�����<�4�������'��<���j���^�� �8�;J���6�u��.�DJ�;-J@�-7H���c�j���'�������X�H��<qh�W�j/���]2E68|���o�hHpE�W������ur~����P���PV6'}�9��Z����*�9�	>G&��``Wta�uO/Jl,H�H���<��_�6���^�U�c��4M��(������V������z����J��H��\%c�����J�]0\�6D�!��Z'�o�!�x���.
�~$��G��3E�:Pr�udS�������(�U�y��je�]_������*%W�!�5v�F{�z����V������W���]�rj`��6��`e)X����$����s0��;^Df�v��|��z�f�Ly�������o?,��o����0#�"w	_�#	<�,�:1������3��3f`�=3�^�i�C�
� �b���$���k
����P�7j��h��o4\&��pCt��1��HE��6�p���0�=��Qs��P��T�s��0�4���hU�:�w���~.�c9�l��4��i��j������.o��i��2����R�K�XJ��� O�Y�V���T�V�����'��M������o ���\�1{2F�z�A �!D}�@���PEGM}<>B���W0�Mhm�K�{-1>|�S�g����?����!Br���t�0i�I����	����`��$N���n�|b`ER�(X��4���vz��8TM��=Dd�p�E0F�6,�#���Xp���)=$�%
bEH�J9��U�sP��sq,�Z �f��*�1K"�o�
4W+������3�	�J�������P��������c��B�,���X�F���>$�We�7��������P��6�M�Q�V>pe?������u0�V����^P�7�K���RI�Q~Ar�h�S��I8���3���������t>�_�a&a0���q�e�R?\b��0��bMI��)�y}���}��:���[��$G0���%�w�AP��3��W��G���yp,�T�5I��;���rP�QHa�|�	g���vU(���,����X��
��������*L�S���S�]�9�t������G���LZ)�Y�����q���B�����f#P��A;�EG�h��.����c��u��
��G�Rg� D+��O�(k��T�h���9�Q3��~�=����l�i�|�p���r��qZ�xk}��������a���3��?��T��z�T�DmJhA�xU�vm�Q��s��t2m����h��5���"w'�����K��ZN�I�m7)�Ee��8��Zp����L����*���iwg��IE����G����=5b
b[1s�����<	��`}����8�4�i�:���Q�����)�f�N�x���r�^"6�
������!��h����G���|q"i��s��hQ�����H���&���+��Bi���7	�;?��[�;������Q�����q��!��Y��?���5�h��*cl~��g�����"/�����J]�9�@�"����W>[[EF��K�������K��{�! c�J�hI3<h7�
Z���xf���Z���*�����XGC�x�C�9
vv�3oc�~\L��?C��R���.XU�7��,*�&l;X�mk�~y����
�����!b�L;�������Z�~��3�a�������o��< J��-�q�bd�dC4�W��Lh>����&���
K���U�m#�D�i�%�C{=d��xJ#Awj�P�����^��I?��L��q�dr�"��p���^����g�������M/�����x�9����u�e�Gy�l	�Z�0��3�������'
���L���\�se��k�"����6�H"�-L�	/�pr��K��j���Zr�����,HWd=��+�����JL-��>R�v���ej����}�G�����>ZI������7��-��Q�TS�E?�����lU�e/��v�7k�r-x����0�e3]���T�f�N+���������e��s�=z�L��C���T�>��w�7d�%�XY�n�����|M�vO�����n�?�+'���z�89�KR`�������b<Wu�g������I9�+���{�n������{����?>�u//�_���m��6Imw�|�����o�B���+|R6>~������?gQ>9d�Q�b�^�P����� hi��^��@<��y71���f2��7�y�������l�A�IJ�W|_����z	�1����C���dE�S<~������	���(
-���z���3>�{	.{y3O�������fI�����,�\���z�&�C~|�E
F����V���������1�	D��%�d�X�i�x����F}j�~���]v���,^\����y�e�������LG~���2���d�a$�W��>�����o��7?(�������'�����&#~��������/��������(�&��j��I6&'r��T���MfR)>_|�+���P�.B���#n�������O�A_�R����{��\��^�w��RM���K5�Bf��:b���?_,�M��6#m����!)@X�����r�}�f%%Y�RQ�x�A}���������\&�<��~�,a�S
9�Ie�����.)�,*B���'H��m�:�Zr�
���O��r��X+���������n2thmW�NHmH��r�Zi�X}�����L��>!�F��X�<��wl�f�tC=P�A5�}�%A���wa���r�w���Gb����yF N�y�jU
 �q�Q�������d?ENNZ������C�n��tfr��}u�t�����D/y2�
�i�Z�r������\��te$�u�H�N���/��h��HK�����kG�E����c�m8�1���cN	�������
HhnD�0+�
�	�D�&X�z���w B�����Mh*��$3(�&��u�<79Y��\g���"� <�z���tU��T����1s%\�JZ�&�_f�`�3�;H�����N��k��������8@�]�}x~zz���Q��
��z����2��EW�h,�)+������Q�z��]���$Nm�fK{yF������y�r��Rc�(���%?���S�aU_��6bV_�����V#Q��>(����$Y�f7�"*�q�r�K<����r��7C��$/4�-�7ZJb#����LdrA�D`����[*�����B�5�J/E��	�#Kb��L��a�oIE��<��qK*�]0&[��Q�����q�
���$P��CY8���}�\'�bX����U����zSj(%4����$�T��Ce���!�g�'���)S�j���)9��#�����F��w��;%�C(n�fU7c�U�[M�x�k%
�N��a�U����K|����F����d5�u�G�bQ 4��dj�8"�����7~�)v$g�km�W@����=�_�vm�76\�Xv^�_�P�=��_�zM�@]��x�,�;�	Ns^����X�#��9��k����_�:�f���`������� <��zr��N1&��+|�q����F���[<�xi5�y�7|�.�����*p���u. ����0s�&��h�43���X�)��h�:���yRw7�^��Y5�	�1��mP��������(�_����B~��V�=o�J'1���k�eDN�q2mt���U������/�|�M��]�M��y�&��f}�����+�k1�u�C�Y�X�%*��x]Q:rX���K*\N�z��dXT�w�6��g>�#�r� ���Q��@��R�-�t7����fT\�
�%����Q�2�m���J�0����0������}9��e���m�w��8oQ��o�&,�������9��yl=�"��95���,���T�)`�Qg�yC�Z���H��b���q��:��%��K�:q��8Ah�|Q.,f�;�M����"f���m��E��>�;��b��pu���q69e^�}{��{9U��`��^�/�w�o|��1����J��;q72�����a\'��.����b�+�ud���<m�~�L2����J�c������~J��W�x5���������5�2k&���b�X������[�`&�+���'���)R���u�-8�+��Hv/���5�����9�-h�IS���?��#�(�g�n�"�X�>��Dy�`�Jfq\��Ap�0�v��.���T�|����|�Me�{��M��3������/�S���Z��G�j.���e��+��)�2��3v���N���������k���.{
��!
�B�������������3�]�����U�[���������k�w����j51��Y#������N�sR��d~��b�/-������.x��0:1�i&g?y��Yw���~�Z$LP�j@H0�f��K��52�|�������8�&V�ti7���_���~��<hy�}S���K�����U�{v�B��!;�Uv�p��}���&��6�c%[�zS���o�2��8�w���&�|�g���G����7�>������~4��� K�[�\b���sB�x'q�f:�5	�F���9U5���g�����(�����`8\)3�����������K��-�����uR�+��l��w����Wvh�����pO��U(mZB'�w&��9\��nN�j��Xm���s�����i�C�.f�X�7�?��fr�L�<����$��`�����SE����E<=.��d�����k���]4�hn;U�u4Me/����w@��������v����p3�����C���A���,�O��I^�2��<�c��'�N@pHS������M�f[�#(�����N�T��y��<�|��Y�I%�+L���x�V�~��O��#\"3���x��F����E�vs������l�=����,wL��=�w�+�k�"�p���m"����78����������O�������oJv��w	~�?��D��k����� ^�b���F���^�6S��at�
@��?����x��Dr4_���r���I�)X�=����#��4A�����`��7��E�No��$�������W��G���D<��.���[I2����D'�=����-<����R�
�U�g6L*������(,��Z��f�k�"m����x���@��������c��oN�H��o�`������7�C��g������<��F�)�����5cn:f'W�����j$CqP:ju��P
#K5a\Q,Sk�KC�g���g����������zI<�|���y�g��"�W�z%Y��3���.U�4^�����$���v�`������������� ��)����!���\���-{O2�"���<M=)�������h I���T��E��$_��D�������d+��+�1�<j�����G��[o��-+�&g�/��{i��ND����BT���7����bB�	�"c�+}g<��?�(����gD����-;����#,u�\��B?�a�����hx����^���������)h���q��nve��	h��[,����PFI^�,���}	@����Y���s��(�Z���1p6%<~���"3pv�1�����a������Z}��lI�� p6��L��F��v�d�v\���� �s�W��rSO~����E8�������D�}E�+��S�j�&�&z����[8��3�\�g���h����kJ��_�(�^QB|�����G�s����t��T	�C���R�H�P�������s�U��T���6����+N@�,�����Y|"n4:��Qg'��y����!{u����X��],��%��I����AJ��;A����J�y�=�����������N��q��V��>�]Wm��$�g%��,.:g���:u@��a
G�8>zB�����������;�y(�`����=�!����U�H��e���������?�./(*��#�!xM�E��k��#��2����t�^8P��6J����0�s\V!���>%7�##�o!�-���F������>��}8sy�V��"T�J��^����[M�b�r�I��B�y��@�����,���K��S��~��;���U�S+��Yr��&��:����D1v�cfvhc7'�'�����E����<���~o�Q�v�m�U%s1����+��U1��SU�6\�T]8��h�y?��F
�(.�?�.�v�����N����r���#UdU��_/��9d'��}��=������;��l3������l �&����p�U�c�/��f�_��s�D��=��������rsW�����wf��j�QrI���X,.>��;h��4�5}�&�$6z�$�0���q4}z�D�����
��{$|k�����4�$��'�rJ�����<$L����t�'M�x��O({�v!�S3y�U.���3��'�31N@+��
�Sr"(�D���Y�/(�L��U=�������S*��=��*?SK�
���V�`����������]O��9]E��$m%4v�;W����j���

VK{T�wJ��*L"�W�W
��;7Y@��<��~���l����g���|��7�Ry!�8O5��D�7����@���/l����B�iV)�qM`y����dor� �,(b��KT�B��7��O��F�������:��&���QH��9�p�����7�N`��}��7u��7���L��MY�Xv��&�o�L�pI�
Y��O�)S���?�ct�$0�ke�]��1��v���&/�%�.)#���������^����'|Z.����<Y��
3[�En�|��S2E�����G����Z�$G���`���T��(����,���`t�~��)7��\e
��/~b�/~���(�)1X���[�����&:]Rq��%���m����OG@Og�u�s��b������/T�������>R�e��b"#����\�&�����5��@1��/�
�\?���������j6��r����%Q]�o,>;����x_�
�������!B n�����a���
O?����u�l_��_�6i&������Qd ;O0�4��)���%�.�W6X���x�6��xg�-�Y��jl���^?�)N����e�9��3��F���z|�k�z�K<u#�������?�����O>�j�^��X]r����k�*u�qw���%l�z�W�o`�x�8�g��B��%�����(���s��|������-�
��,����4q�H
�����e7��eM-��Z���c���8R���M+��v+i/�
nV�`v_��1�BM����N�(]�O@�5�=���+�s�
����i�w���B�~0l����~����;��G�z��w���g��z��?�}EV�Q�F���[�-��j�\�Z�Q��R"��]�K�h ��*���v*����
����B��m}"a��!O�)L?	?�_��������C����Y�9�O?m��V�9G�b[�b���}R��7�f�T����`Tj��.���(��3[M&l��nn�\��-����m.���G�
9}b7O?p�~�<����'�e�-K^�J�V�+C�xy���x�1����I9�S\��E��?�R��]�G��*���,��O�KRkA��<}=���T
�E$�7u-L>�w���8��/��N����;y�[��������?�p''��S�]D=f|��f�<'ZEfp�����0N�~D�`��d��!����V��o������e8�wAr��s�8���9	�3�����i��2Y�����'�NY�\
��T\Z���r7�����V���2�_F��A��.���,O����j����_��G_�V2������y��(�k�B]�����:y5���#�f�����fX�uMb���Z#����w��E�����g��������oM����u���w�����w���[���^��K�(��Jx�&+T��tZn�1(�h�����2��9��lE~_�����}y�Q��p��ZLFgp�'��
U4���Ff��YXM��.����vf��G�!J
�p��N�D��)
o���u�)7
��^��������0����^�T�|���b0V%;7����V�VETs����aC���4���
�"��'�:��E�q����61�����R��:Y$J�{��&{��S��C��xS[|E�T��'�g�����e� ���F�:��=c��S@�,�_��x���e`���������|��M;��7���n�j���@���iE/�B����M�i4C|��
T�?�	�,�0;9Y���7���y�,;C�Y�����X�A���EU&A��/��F�3����T����H�1�$��{�����J���P�A���&H�>X0�yhT���P��?f����?g6���,�����O���j�Y!�%7����M�kq�O3���D�U���������)�#��a0s����,��%�5�BAs����/D��m{�GN�F"�?[������m�m�OzZ*$=�a��w ��a���3.U�lR}K�2���2���X��\���ui�{,���Gd1N����pR��I/��0P�
14%�����*��RxFK�}����:����T������:�]��%3���4�H�N��^Z�C=:P���#�t���J3?�f��	
�����3��>�7A�t#h��������n�C�2S��x�E�"N!:�����^k�d���E�����@�-A�(v��Z/�vJyZ��L�4j���g��-����?}R����^z�Y�5�9f�Iu��#V7/,pU�
z�|���7�rx���hNy�D�G2����/�d����UF����%M��P���3(Sy�%gr��)e��o�M�k���(���v�R^����CzBLT���@	��!�25�H}�S7U=	;`�--)��Fg�4QIT� ����X!�����$��L,@���-`��5r�0Q{WsC��?��]���pN���x�<�E)��B����s���En���%I�!��z� b
��+:}��~��n&��9��E��%���Ng%�+�Zr"��q�2��ASg*��mt�d�	�h������#�*�Me����M���V�O%��P�����{�=���9�ST�
�u~��IuUs�ndG�T�2�_����
K��0�uLC�����V��$.��������\��`:bX�`A#A\����9h3�t�9��p:
F!�_���Z��pWT��VX�(&
U������,�����"5������r�z
�4�:����, h��^p�L$W!e�p�����x����R
.�t�>�����q���=����7��2��2�X�L�)���I��W����u��Eo8"��U�[���A�UY(���d����X��HTQ�5�H,�D�&j��������4�GNm�2M�QFe};�3<c���clhW��w�V&P���R%��e�\
��awb��3��5��n�v/����8n���y&�����{.k�*��G���
}�X����:��3����7%P����"k���n�(%�Y�(@��O)$������j�.�u��U������p���(��Y��S����I���0x����D�e�����5�^-��n�@�EG�T�D<g��Z�rk��D!��;�
�	9N��B�0{+e5��I@�<]��?�0����������E�BjO	m4��@a��Y�f��!asD����wU_���h{�?��w����g�������T��_%KB��1�_uz�~������4�"�B�Y�-yA����Rt��b�3<f������zxTfi)��H]\zTX��\��C�R����1�f���I�@�u�c�sMw�����#G
6��������Hrd�M�?���0f��^��}FJ6{X�u���TT]Gk��>��T4�~��������MG��:�4RA���Q3!,%�7�YBy�M�&W�o���J�/�E��U�
5�llC�+�;z�%N�.d�Sv�)�A	&?�|V�����6����c�����J�L���8�-u�4)L�x����WS�u��yc?b��x���m����V<��|�T�FynD����|�?7A�dMN:%hY��Mn����G:�����F�^m��Nl8�����~�O��S�kY���������V����z��L��y������X�����k�:����m`2�*%�hl�S��f~;�����K��J!e	�u�>�[\�\�������7��A���l��5�"�tP�������xU��n ��Y��q��V|/��7\�W�^n%���ta�y������E
|�09��v��	R��������"$���\cU�P	�KM�@3����6�<�&��8�%�m�ly��}t�7�������3D&15�^��>}+Pj��tC��(��|�u��H-�x3a��N����c�j����Q����!�g�W2��.@�<N���JA����%d�������mm��8���Y����[�,��<[H&���N���n8���)^H7���0	*�xy��_uI�)�6�����y�l����%���n�C�D8��<�Z��[����7�����ijK,�V�3�v	]��W����\�?b�J�w\���w5�����RO����+	k��\|p�]�K����#�d|�L�c�x<�R��(%�'������J�R�!�� M^��Q�
����!�H������?�-x����Bw���dC{o�9#!h?g�����x�g���_,?VQ�����\uN{�*������VZd8������F�����D��B�4�n�y�>r\aD�P��F<��2�4d�!8v\g�n�FBQ�|��E����&#X��b^�)G{�Jhs������U�������)���p���o�J-u��6a�\�w����U�Q�6��(D�p���-|�����9<Tt����G��t:1�?���9e�T����E�<��.Q1����.����n�����eu�B|�d�4 
��[0�!=�e>����v��w���4��H������QR�����k��2�wr���0L��(�f~T6�pu����F��z�M��E~0�[��
P�A'F�Hs2
���d5��mG�{�j�	.}�F�����S� �b��J��jF��"?�n�nQ_/���������t1��{��3����Z)}_��6j���O��!g0���Fi��fh�i���M6P�#�`�����a0a|�p�
p�����H5�M_�6�}�
oS�M������|V��)H��7{Sw����yIX�!�ha���vep�GxY"CLW�W�Q?�����y,-FPdQTJ��g��gIK��_1,?�<����B�1�l���u�jK�f��L;�ff07��&I���S�m�E������`�l�&���`������nI.!�o�L2
y�����8T�R������C�M���~�R�%@��B����7��>'��xx����O]Yr���D����>* �C��xF�����Z����#*M
\��(�����-���)E��)FA�
iH];	_�>����f�L��%Y��Z���e$HM�CI�
���^�U>s�l�`H(w�/d�������K5`��b��xl�*(������UT� ��+p�8=��q�,�!�������R&���S�}z�r)>����M�
���\�	97%�X �f�����f:;�\������c9�����N"�K8.E����N$����W��|���^u{F~�\��2�B:F!�-,�����@����;&}A/����,���GkM��u3Py�����f��|��k"+{�'4v�mnY8��"?zX|?mS��1�"��F��a�������!�.�,E,��b-V��)��q�_9z��nK�Ng�L@%<Mj�y��k9�xa����Fi	�����:E����G���7��AE�wJ_�
��������vk[@q���l�(Z�x��G8\m��dF������q���������TO�F"h&������x�$��P���\�g�e��A�Vf�Q����c�u����5\5c���a���sr|����8��{���oT��Lin��4�}���e��������5��c�S�7Fa{��n(�qQ?��L:{e��W�Xk /;$A�\��b���+���*��)���'���L;j.�_-�	,�d��)�]g�/��s����{������o/��e�#�q���'���H���$�Z��d�%���nz��������t��NWh	C����VS����&�\��_��������v�����~w�������b�1��r���3�A	BLtm��Q]�~�b�"	||e`���
��ss����}��DB�O�b�o?B'l�^�g�
a�6����
�����^b�D$�:W�{'����SPx�?����se�����8�~
��Y�N�������2�K�#�K`��}��;3���g���O����$�>�����zd�eO���3%p
�������G?�yF�|�~b��W��{o��F����->E��������\=�������k���H)���VJ���g�q�����p������LeF�'��;D����T��D��x�2�"�0�+���'�]@H�u)(V�27�!Xc�v|q�v!j{j�.�y�����#�f4m��v/�C2�C�e�)�q�p��Y��0U >��(��c@}�
���w�>��v~G��Bi�����!.��FT�mc��EIh����:���u/����,�YW���S�, �K�FL�e�a��������g�V�U�u>)���R��"sq��W���t�L*����.����R5��@/J4��up�&S�}<�D|�������&��.MF��)�X�:��N)��%����T3I/�]Z���[���U�Bn����VH��e�t����:~5�Srt�<0���G�s2�Es���BQ���\�9��vw��b�
�f8����[O�����>�����_>����Vv�.�h��{D��	��U&���"�5�Y�W��k���	}c
�k��[&��~	y� �.�}�L��D<������������
�e��:�}�	��������=�I����������l�����B}���TbE�02A�{X��}�xC1{X��o��r t������VVW�Q�7�~���JVQ4��|���|�I�
kg�q-�D5�9}�������X6�\[����C�b�����hD��/
%�B"As6Yk�H��	��%�ui�!�&%��<��;sG�D����������}W�M:�w�����;;8:�����h����-�^�=������4(~������Z�r+��i��d�����FP���������Te^������l��{$��;�%+��#@��&�3p��D,;CFD+U�X5%�����J>�3����<�3[����p��h���>6�a��u�
,sNV���g�5(��G(�C �z���
O!|V{��`�X�Zr�����\%R^�b|\�O���Fdw��U�g;K�R/�C\������^��{�dk�Q��i�J��������ls[�������d�oM�h2���u�PL������v���)h �Yod2�&)��
�M>�:l^�?Hq��UBS\0��C$E(o7Z
N�p��M��b-����i	D����`�m������%� ���)���-�����,��(�_�������A�������n��"/Q~xp���
3a�:z����k�4+�����vY�]4���-/js?����0��[���6�r����c��yy��E�N��M;i�dk;}����*S;����~��nH&yL��Dd���x|����d�}Cv�o�{�]����
|�,_=G���2a,�f��rx�	'�[2���\�&t��/���
bz;�L!H�<�L����,��a�t����;e��N�,���������T�}GXb~�����	����S���&n'�D�&��;n��r,X>`��1	=S����:w\�![��Xq8=�
�(�
N0����4jxO*�~1D�x�����{f!h����L�S����U��)����A%�9�����7��f�f�V��wl���g�O�2���rQ�oF���D+�}�8|�|�*y}����������O��f��1����1u���pR�~�zF�$��u \�y��
��RlQf���0���CP��l��C#��� �@`��/rbs����v�#q�|�����2�L2L	wJ�a`u�J����z��h$f�P�=|q$0	�Z4[\`u%t^�Y��/���{J�f�@\�p��m%o�P�����|��{�tT�
���="b^���#<����7$�>��RW�j�#����C�Y���������v`EZ^�_e��W�+t��
��G����p��3}�H��;	KtB��������i:��G�\�tX\�R7���V��%�9������@l��['�B��=��������pX����9	�h�����5�����s�
>*��\��mMd�E��M'�g�����jZ���`��/�>���KOA�
F|�C	t�E-��DGC�R|�m�q��,,���
�.|7'v���5T����[����;��7����y�a9fo5;��_��Cm>�a�����0�u*qL\��m�V�|��;����w���>����W��5�� t(���g��4�������_=2&�&�K�Z��|�1r�S,��Aj�c������o� ��19B�%Z��G1T5W?zv��H���2��N��b�����{�U{�!�f(~u�}^�4:����x#qi��M~���-���os��=�=�f"as����V+e��*->���b�y��Ks�p���*R�l�M�;!2�@�Pm�'#pm��l'��j7[��j_&�m�:����������`}Vw�wAL�����@R4�r,�G���5�r�UId(YZx�����I����U��ue�N0���S��-nJzE�:��@n�r�d�U�&�YN~z5'��7���U���H�\?:��P�9�SU�b�V3hlU$��q���>Kua����,J����d�����X���:������Op���!����=
���sE��eb��
��5�N�|��l�s%At�z'R|����j>�1���(nV����Z��/�[�Q,�����<t1�c7a��0�|�<��\�h}7x��K����~<<#t����?�l�9X����X��R �?�n������.5��]�����z��[���n�Kg�)}�����qs������hv7�g

�a�9������F�q�`
�l��La���v����no��V��������`��8��k��D��L���������od��j������0���[�
t6�A�w��A��;&L��i���`�w�u_���0d!���Oo�BeB?�C���S�����(��^r�F��rat������<4K@� _��|�J�b������p���w
������G@d��e��E�
`p80�?��,?��-�L�F`���z$(I�$�r���1��SI��c�2��f.���A
W}�U~�M��=������zxk'��dz��!�.���
�S��5�`�A� R���rO7���[����`j�|�9H�3�7�w�[��&��,����>�/��8s1/D�h����z����L2�
�61Y5+���X]v
�W�jz�
����c�>\y;�w��03{������
����v�J���;�)���l�k��GXN'+2Y��h-"D�@���eJU|����K���Hq��B�<�5u��L{C2����H��o�`Mz�EWs4�b�[�TKX�����zYA�U�4Z���E��XM���
+����#���CCod/�#� �]Pof��~�w�����>���hvEg���U	'���8�,�����B�Z@�����HQ�d��.3;��PTShV2Y�H*�2 �!��7�4 c��G*��Q�(�2�)������-�({z+��"�����?�Pgc

�_UY�U�)�<��/��,V���h,�e����l?�R0E]�C��p^a#<#��W��d�}������L�%I�\���7��X~�A��9!F��l�YI@�@��;$��aF��3\Gg�O��M[������'��
��jSJ�m��B*��m����7�&U,_}$%��_H��6�-��8g���3����G�������_����RD*��`.��!,+R`������Y��.�'�$$�4I�3��Ae,h��:W������2��h���-T���_U�-���j	K5��W���k�k�����|V:l��u���{�S�)�������������p����\v"��v��r���/�r�b8�����&���0����o���3��z�e��z%uu�X�\�����2�lo*�3���0�_��L,�@�W�/�w��%m.N&���m�E�C���U� %�s�e�������r�����lj�2s�5��RP��;�� ��G���6�Xg��~an]���4}D��b@����]��>���,E�jSj����Q2s7�?�S9AVOH5��N�S���z�����9
�s�L��u��O(hG��R�X�R8�>���
>�����'?��I�&�
�VV���%F�m�r���9]�~���QM�s��H��t\�#����g�>j"�S������md���|������ZS_�]�l!�uw���DCjV�^���<����	{���dG:��
�����kQ9���K(������s��~U,r@$i���8#6���$��.h�	�zZ>9��z�!�~�zX8�� �oe��W��n2;����x|��Z�UZO��[GQt��Y���r��c+�`������J��e�i���l0�(�v�(	�����(i���{�t���/�h�_}'��8�1�{�Ct]�}�^�f=]9"��D�S�Q�J����1��!%��N'���������]{W��PF�koJ�7����(kC�J��>�~�
5�*T��$p#r1j��Q���PL���aD�RW��K�\,��}��G��\>��C�Mtu~�jE��mE�WW�:zn�W�+W�d��Gs�fo*�.���2S��Y���^/��F�4_���0`l.4�(���?3
�0<$�G�Au`S�zRW6p.a|�4���d�9��a>��IN����8ft�%�K%�@0���)�
��q=�9����)�'/�����I&�e:}`#�%G�T��$=/<|S>x_�V7�G�T�B+H�!3�����4��+��:8bw���u�|	����b��E2$�Rzc�y�������P�ku���#DQ=�1������B�E6�P1�A�U*=�����1/s��SX�` >d?�pj��2�	%�q<����:0�g���N;U�ve������_d.�����q��#�=�j���N�.%C��+�Vy�}n�S�	[��d���%r�F�i\���$��CG���M�:@���W��$q�GZ"��6�sWSL�r|��YDa�&�]�|�O�������L�Gf��V���
p7�f�*�<�]�2D���O5u�w�:
��{�m�5�8)W�r�-}0�"�����F����>~����z�������
�{G0�IE�G�.�����������uJ.�� 
e�3�������v:
�N�z������}��:%[.>\{�T�;�MN;]���u,1�����4����w�W���fI����L	aO1G�hKb���	O�f��Q���� V\:�^���0����V�m�6���N����5��	�B�U��>SC9��Be
�y���*o;�/j��&s������U�VT=�[X�e�o��7���9�+}�M��*� �Q �e]����%*7� @����$[�(��e�g�c�c�"��`I����;�!+3B^&�R�!����	�0O�WTi��;���.��A0�����3���4�}a�����d�� _��E7d�,%���|�l�(x8I�DpF�"�z���P��<����D�_$�[~)����.l%v�+�1�����g�wy�/$�F�d�N��w�({���W��cD:���H^��%�,���A#Fn=a�'����E ��+����ou�!����U���4'�b"��	Y(�).%�+Q\A��+�@�8o�
G�];�Q9Hh��dH�l
������W�m�S���X8�^��?@Pi���%�|j�O����<K������\�,����e��F'�����EQs)<u�N�U{jE�4�iW�I�cA����'�o�H��T���T.L������\��5���y��A6�1m�#��!����H��cq��t#}������cqlc*gsk��7����R()��0�^���N�? :G��� ������;�t8W8[f�ah�xd(,�{����/�(9���8��0P�S�P�����e1�0r�v
�"��-�x�;������)�@�`��a��ua����p9�0O�e�D��i9>���v������SEbD��4�?!>(�Y1�d���Q���b.a�%��	7�'0��O��/�`������s��?A��)
��\r,�hVp��|��1�Ug�����V%*�!�>LBgT��1gC?/Y��Sm� N��9td�k�
\����*b�m��a2��493[�e����H"���~ku�)z���h������?�~"���I�'h���<b��/��hvy���4��Z���������>���*��$ �U�B�P��z���.{	7��f�[�+33|�1P��~n�����9�H(����S�.~O-t�����bJ2Jy��4kxZQ����Zh��
o���&�=L���|��a��	��M8lG�J�My�jO)����QKR9��e��4^���+/�����h�bv���Go.DAe����)�t�h�en0���A7�m������c��~��}7?��$�����9�G��t���)o������%T�/�QVMx��6�T��H���T�Az#�� q�~ls��S����N;�R0�J�����S�3@XZ��:�)�3p���;$��M@BCk�f�s(gP��_���������(�\5MEF���G�M>�
l�^/[�\~�����|����4O��K�;�'����h����Y�TiB�]�Y��1<�A����Y��)���I�$G�%�����0���1H+���������Mm��^�9��5[�/h�qR�!��$��-�H~���A{��KDSB���1�O`kd.|�@%�lMRw
y�T�P@��I��������*��L�����{��B���5a���9��3�H7`9�X�a���4�������������p@'�@��X;����-yn��!!���F����l�`�ti��S��(��<
]��������F�u�V b33w���+��np�D���;	���B�7��i�KC�S^z����4�R:����|���E1XjAsJu�D��5��69��<�U��[��l3��-��^�&�b��%65��`�$7d����K����N���s�:��\�!@�!�rK���B3��u�.@�3�k�������a:����;��	n�D`}���|X�f�7�������$��I_F��y�K��sT�����$Gf��_4����H��XN]��E��T��������N�=�����HK���@������V��:�Q����/
^ ��\����	[���������Y��L���~���G���?-
��4)��^*�*�����(�	D��E2q���y���<�Be�WU�"�
J����tB��(�j��a���zp
��5���-i07���JQ�{N�'���*��pL���5&�:)r��#j�����)�w��(e����G�i��w�s(��Z�zW�/���6�����B�!#��)g��o�Vl�='��R������?�N����10������(:O����,"����J��u&�)�O���C���g����w_<A�a:���
mC�]�e�i�R�yZ�+����m���&�C��Hy(;��a��n,��I��p�f�mQX�H�6��Z���$�|pEh�L�����3�����%t8Bz���l���0�D�YK7���
�y�Hvm"P�se<����+�������T>�nhz]	���r�/n��32���7��df�����y�@F�i��8�<�h'�X����s�;������tcEs���~ B�@]�P��;%��2���K@,���Qp�sv�"�1Rpt��1g�Re���mj
����V��vA��/R�Uue�w�n�UX��fq�S�l:����,�j��T�R]�������SH��E�����rP��%3�2��RU4��M�����������V��<�����5l`�Q'����
U��b���S�uA�z����C3���N���	C6�C�������]��5�=�1:����w�+�KSzw�W$%Y�	���������RNA��E3���b�`6�"DTX���8Q���:�������$�r��3	��4n�b��Zg���a=��.��2��'���}q���F�m���6!���R�
���;��hB\�S��7G�J�.�L�K]g�e(Sn����
��OI��|���q�iS~9T�l�%���!�rx���]/����wQ�N�/W�^�t��9��w�q��s���}|rv�{�?��K�
=v=.g�-bw�;G�������`���Z1����t�������"�
��{����8����������K�����D����d��"��������a�[�!�*���������5�e��P�c�2�VU����e��g����,A��<,"g����L}��$�	HS%	R���������M����J�4N�a�9�
I���h�F�+��hw���c��"}��-�^h<�:��U7���U������Y���^���q��E,���a���V��
r'��cN�O"��o���T���D�@/C;u��C�_�8�y���F�I@�
()qT�e#xw��#�~��&�K�J�=��nn��@���>����B�WP#P7!b�������.��.�/��}�U���bX��B���+}�+�L���X_k�S�����t24�]�7���������,hXI~�M�S2h q��@�[��}�{`� ��F��%��!;�{Jne��!�����g
Qp�(���n��
�Tr�5
�`����7��^�������KS�i��I���bd�����:�i������/���O�?t�M�y(Wd���vpvb}�P2��M=��7��|lzb��!D�����'ZKjAkH������+T�I�l� �D���J��#JG���c#L�5�Q1��y3+����2WepM����<c+�
���������H���kM�������d��,c5[$t�br��9��In��r[J�G��v�&3�R��
s��L�JV��s���U��~W9g������(�z�.(������t�1F����T���#�q�>�G��r5���l�_'��H��l�p=x��+�2�����!���,(:j8���\*��%��f������i� ,��wY����(kaOr
�Ya���t��hf��Z	e5�#���&!�����R�`	�A���:{�M����Y)q�������x���R_+Z������^��1���%�Z9+ID{���c�G>��A���=z��%�U��p����oY����e�v�L�����bT��Hq@���'���Ke�i[�8<��s�2`�x�i���l`eA�o�
���J��������63�Bn=�G�����y����������g��.n�!������y�b����V�e���3.�*oG�����6�
�|��\��CL��5��~��>m�d[A�5�@���&���]�I���iu�L�e����/��(Vg@x=��K�(_���$`��Mz���3; �Y�t /�@1F86�S�N�)Sm��#�[p��J�et���������k��$�]5��,v��#�~=��;����T��-m��"a�p,���
��j��+
�E�`�B)jJ�R���.>�~��5*��������6��OA��-;��\8�2D�)���������'��bB������D� �m�;;HhT�����t9���BS�|������%QcqC����>��*8n�ib�'*��Z���j���umh��t�*]5F-s����z)k?�?ijO#�y[�:��~�?ra�x�
+�%�[�X����=o1#��2���F��I�������JE�����:l��*w���B%���qv�r
G9�{�:� W\��yt�`���k0R�H�y�(cf��H�r���k�S8%fs�4��@�[�����o�n]q���JsW���a���]6������W�K���,�9=@F�J�<!#�P���k����j`O�
��}B*+��?\�hy������Nn1�PQ��������2��y�c�f���PHEmQ�	��3��J8�1��3�����3�����|V����\��:����r�0�ZP�=�p�N3*��\�n�(��������T���S.����%��`�c�INC(5OM���5�����v�2"�F�]#q����������K�o"e�,>��5��"�C���:���O���)��K�V��E�L���h�{H1�j��C���I'���p|X�4��|9B��v��f99�lPdc�B�bD�"�t�9��/�����V�����+��H��������w�ZY��l4"�����l�Q`�������zY54������}�*��v%'�Q{r!R�G��9|y
�h�ia~L]d�]eN��,�j��6�.a���I9d�cB�0?*R�����s���K�=��k��p����A�m�\���Dj1%����RG�;0Fpt�
�Q�?	���{C:'h�w}���UsA2�J6�>5c�T�O	SO�K��]���i3�l%�����[����i��U��`T���s�#��L�G�6�v��sh=9�;9:j�p���������MX���Tg�V�B��b�=���X,%qrn+U�C���8��:@�[k��d�����xc�� �i���r@����[��NR	������o������@�V�+m����K	%#��le)+6�B@R/���a����P�D�8����mj����ua����*�=���jV����vn)��J��)�����������}0K�#���vW�c!��n��*z��u���BjWI��gX>cY��2������0#�,5$��iM#a��Q��(��F��g����wv��������v;����,s��-�7~?
���"��!�,�d���0����W�, oa��./������U\�)��!�.�;#|��ZT�E#y`v�i��'"�D`B�������
P4Y!N3f��|$l��k�b�����yl5�v��jEpi]�*`��|�hii����pKjn�w�����H�-T)�|D�+���0�f��*?6�OW�9�P�<_�\,��e!q�QxO��ED?�T�&���S�*��(���v�N�����(�c%�i��L�z�H�%�~9E���8st-��7 j��0�� P9(�;�9�
�D����T�����K)I3���9��@t�@v�KW����Un�r�^@z��l������"*��|�b5���rH�6U�DY��C'���"E�o=���)	��,b�U�U�O�������g�����pu*E�}Wb����zTV ���Kx�+�T���ExH�cK��P��MJ��g/0[_7�:6��������|�t�Rk��<<����
���N�w
�@7�����nK�/\]�P��%����'l�G�|��'�O��;'~��{U"������'������t
�z������X|W�'J%��N5�D�_�����b����5�@n���P%Es/���e=�.G�v���,<�Nw %\5�/���oAJ��!L&���\�>��������w��}i��'KM�y;OQ�����B�	�D
��~Qfx/k����Wd�wT���"�YQ6g�G����R������^Hq�*!9
���5���6�|�r��O���^?�,���#%����*�S�n�/u��������9C����v���J~��MO�����w����F�f�w#�v�zPs��(e����2	/M�]��(\��y���������@c;������xq�������j����s����������4H'��s���u��$��hA�9SB��K��&7�����K��(��k��������O�0�� /.(.NA	����������3���]��96���sT�|����\�MR��)��X������~6Wh��U%��J�4|bd���U%�I�
�
��|s�4E������"�Y��2�����?F�$��rf������6������d�P��_X���.��lx9�"��%N|$��X$o�Ay��S���1�;�j@���r�Y���(f�����$Nx���%�u��4[�Z)%O��
1�D&� �U�� ��?����N.��M	vn?��Gs��p���QN6�����9���MX)@�F#�lu%N���]��2Ao:��E�sA7���|�	r�;a\H���|XN2u�e��B�$�����\w�E�RLhpe[�5v��*��*)f�N&�	�{q����>��,�GX��K��6����f�d�^�-�rd��s?�C� _`V$�3������h�;b�qdX�"��'�l����=�r���[��d/i��:���=���w��H*�+���x����)]��l���*F��E����
K<���%���z�ZVL��b���+Ke��G��tQ����=?A�^v��R��$=D~�r��A��6I �cV�k�Y������*ei��uH���7�`�`U��$Z�hJ,K@@T&��L��l���uO�o+��M�<Y
=[�(J�M�6���H��	�G|�]b�M�	�g��^k`��,�"�aWx��k��)>����*�P�k%x][�<<F����D�������^�c�-����IAD"��-��H�X?�E[LN�mk���e	'c��v��9�m��L�m�f��g�H!��$<�$]���^�K��1��� �J���~.�4�`��1\\�X6�s�p�a�VW��%$��/8��y��{)&@�����k.[~I�B�0c�c�������o���%"���8n�:��@?&�%>4I-���+�U<y��DY�9G�YgB�(��|�E
�,c9[��9~��7y2�aV%��2��2,� 1��0]��K������1<&u�L_/x4\v/���J\������$v�W�����!
��=8m�NGNr��0g4\�Y�A
� 	��w��$��:�,�lE��a�pS���|y��Giy����g�E
���+e������MG�){nH�* �[����By='�:��JP��*����<b����SW��kZg4�9���X��zr2|8+P��\a$��]VO��-8�������l`p��#]��\��� ��!�c}o�z���p�����g<��i�A���%�)
C��$�~�)c���H�2:K�A�Ixa\4�B�hNu�����r��z�
m�u)����1�(�1���}�I��Eo�(X1�Z���M�����j��`&�nl��[p�K�S���{R6��_���,���7�S�0=6QP����������7�?P�!�4w���W`r�Rsr<�n��)u�}��eM��p�%!�y9|�M�� �s�<,v�[��
���i���Z�/��u�X�9��
f�0.8f����c\�]������e�%��B����>�j{{�:?Z�&��K0B������+7��dXq����L���$�b4�t�BI��]%:9�V��m#���L�Q>;U�}��s�*
D�	^���`��j��e
%2�C�!E��
$����~�EW����=���/��6,z��K0�(�w��B<7�DA����m���
��M(q�Hh���o�gx�y}��rp��n���>8Wv�[e-d~d�4���8���T�,t�*�!�>�����e�b6�4���r=��nDCdo��xgkQ�@,]3;���lr�>H����;���E#���z����T�Q��i�A?�A��	�>+��3W������/�8PI�J�
��2�X���0�d��B��	��=��N���KZ�K��I=WiW3����?2��vn.���4����+��c]���/^d'R6;�U 0�:5,���Q����qP�T��T���^[_��-%������?<���=�N��8����/s����,q�A��=�����2��0W�U��'A�8�*�,�V`6f�14�s�aD����l��>�f7���Z��
��D,�~�����c<'�jK���e��\����.!t���-����[�&����<���*#�;�'��M*9�#i���r^4K0�KV��%Dc���~^�J�;������.���s��1�J��#���}�s�4��TbGi�dyjvR�!;���`�eF.���
|QY�gW��J�+6��(�;)�����a�nN0�n�����xZ�F���t��<��Op#8��R�3{V�}�8�^�;���[�	����M�E�qA�h-������3F��g��#:�$@��g*J�b�TA������������@��(�Mw���5�N��WPs^�������+wH�
/+��$p*/XZ&
��������=5�xkh�����<�0{]��x{�C��w��C���K��O|?Q�Y�|��9���w��i>�~�R"������\:�N]�
	��I��VI�|��=,����T��Gl��+n��+���J
�7����%��0�p��X��/�,�`��g�������eP
)�
����QQ��4��n6������hmf�B�����ho�j�Ty��ct��xlJ?�,�XA[�d(�e��bs���	 ub��8C�"H��.� ��������=���aR��|���K[��� p��������
��p�[_�e����O�+kkk��n������+�=Z����dm���<�ll?�J��_a������.T�i���L0�/�,���:����t���{��������d���gk[���D1��d��v����.�~�o6j4x���d4/�i<).3����~��>����=���&�<��ZjrM��:����/�P}oL/����/�C���KoUO�������=_������2��^f�I�G�����h�����~�����|=��k�@B��.��JE�2"$��f����tcE����WR*��&(7������e����O'��hz;�����@�����j��)�?���3�8��vf)4�s�����c��~�K���T�!��c!z`v��v�=��B����1:��o�~��=����O |�%������GI�w	�F�C���Q����}�1��Z�D�~��\1`R����W-�*�c��7�,�1i�4�W��L�u��WY:D)�G���0�3�|Q�����
���^��Vl>t����z���'������7'�����V������m������%�9�;8�K4�����{��Zg����{��%jU$a��Ow�zx�����m^���r�2.���l��F�1p��y���0!�Z����1��I��x��KGK(�%rNb���B�G| ��,�2'�3�h���ac����
��;%���C��z�j����d�4'twy@>��G���:���������n`.!L�m*�Q�`40W3�I/
 �7-�� ����������8�'Bj��"������|��G1!�P��?��Eg������p
L�o
Y� 0L���|�Q)2���&����EF7�����F)�/
�%l�!�n����^��X���J��Q�A��,��CY���5_��S��2�_@�C�9�-��?��-�����%l��;K�-�L\�C��@lt?QYS/����d��al hR���
�,��w��A���|G�'�JB>�0���.�Y��,���Z@�|BYT:d�49vc���"�����;�b07���c0_o�#�\$f)l�i�$���:�W�p���E
��:~��.d�<�R����R=U���+ex{*�m]R)�K�A8��2;�J9[�E��Dh���zZ
��H1�����o
�3��-
�]1a�f���J�Z�?D�"�,d8�hY� 3�*�/2�!����/���R���\
xY��4|�2�
f�2JV=v���E���ep0������9��I-�9_yG6~f�yL?��U������t'V�?����.N�;8�����^>�s���9_8#Fp���4  s�;b�s�#���r�j��jPt"F(<7����s�F='�Ok�
�UTj7,u_6b�Bp�����Usx
xU�o��N�����H&,�Br�=���VRVT
��B5�!X4-�nyV��{M�/�;�u�G����l����\TA}~�)i4��C<����xv`T��eK�(<�b����W��*��,��X4�����r�.xh��.?������*<���%��Z^��6�7��Z���s��K
��C�k�����FF�������Q��<:(>�B�!w���?�	������Xe����NEGtk��������X�^���X#��+����"����X��;0�����K�x	�5qA];2�a3._>}R�[���J^GJ���0?�~�uS$Q�>�1�$��-�����dc�aNc��]��&6�x���S
/�0(���j�h6D�Y�5�2e-������73(@
�}:fe�u7UZ�q�jX�
�P�A�9����+��#�gP�b��#�WF�)�ypr9O>u��I7*�f)br
���"��Q8��D)�?�����Jr��/-/�K����VT�����g����� ��`�g�����N{�P4T��7��d'�c�	@*s3��P�H����X�{��'��^. }���e�q��l�H����E�b�0��(m�
!�����\f|��A�B�E�GJ=H~~���r�bk��N��q���-KV���.WO�*O����zjY=%��u��\m��l������Q���p�>���.�����l����+�6���m]mf���G��`�v}'�%8Q�>eY6�(��0���S�?z�x�<v"��"�
��3��t3�����{��j�Y����82�r�\�J��*����.�� �d8����A��Pi;�ot������>��*S��B���d-��GNb�0D)��)}��j��r��^�`����~��O���������<i,tX�?�smK�����b�a|-rB��y2"��������l�\�k �B��^��p�8����&%	dH]�v���Y��1�1?���6�|1��Ve3�gd�����g��AY��)�j�K�,m�'�%?�l����Us��2wWr%Ir�z��"	�
���w<,�P0���f&��v����
v����ju���pE��~�<W�&�T�MdP���5��ra�N�L'(�������C���F��o�%��]4���-h��m��}�j ��M;^)��,
~d��G�-7_K(��49�g�L�&�g�6��v��Md���:_-9�K�+��<�y�l��'N%^�������-C~�y4E-�2zV����f]����R������s�j�R��9�4��\y�p,��F(��!��e?S���
U����p�w*	�����x�-��C^����G�����:��mi<I���Z�1��g�Pvbq��;�y�Hp+�"����Q�h�Gj��o���S�������]�2����>���wd�st�<�(�!�f�
���u�uT��Tt�7/q����~]���)���zPn��U ��PL<A�T��=	�������7��>xFm��*�7�8[A��T��_��G��r�V��h��i���X�q�������!x�=s�����Q,sP�b,,�`��0AE��<QasX�nf�+=#5'|�VU�W0`���08�U�����^`7�g�.p1������n���f�w�"aHA:�~zi��A1���W����
$�!l�������M��Mp��b�t��@
�%P��X�[p�?��NF	Xb���#6�!%��d2�y�a`���>������AP�v1jbF���Yh��6�.T��|Z�����SJ����F��J�d��B&��"��B��<%PI���K���{y����!3����=�	��=�)r������Vk��������A����j�:���o����������W�_����v�����Jp�9u�v�T!�~�`.�N)TJ�j����+����r�w�F��!��c���E)�=V���7�>I����'Yo}���Vws�����K	y_[[[��H�{��o>�z���,yDln$���$�CP��h�&x�6'f��"	���	;������6	�(�~��;�]�Y�w�c����d��N��N����v������<��=����a��2�$8�@!������� �T�qa�'Y_|�ug~7���G���[�����4C{����Ff�\~�?����1�� f������7;[O�67/����l{���������������R�����������}1���n6�����+6Y��S�EV�	�o�QV������=7�����s�����j�
����O�������#$��@�1s���������Sk~�/�^�v,�#������ma6�%>�N���Y[���Q��v�}l-��$�"31v���3w���R[��WH:�ex�t�.��|�mn?�Z_�~��=�4���Xj����n U�����<��_LF�phv��yG���t:������D7��08��*��ne�j�+�R���cD�~N���I�����(�Q�"����Y�zI�q����#��X>eY
S���7 S�v��������1�I�8��0��������G����~a��^��3"��*��!��v�I
.
���������>y�,fNs
.f���7��	�%���@	H&�'���5k;
-��a���O'�����4��[�7�=d=y�K��������
�b�]����OZ����t��0��-e�y�nEl��?��t���X�O�4��S��Y!��][���K��+^WF����X�A5�/^8V{,I�+��N&�m�~j����S lb6�����Y8Vz�n�Cn���q�G��/Mm���g��y����a{����@�@����C�p>����D��
�Jy��@\_�/]�l
����n�������}������CWf�m=��r��9T�U����V�c�\��m3������F�K\�p����������ujd
��W�6e��Qw�z<pm��e������l���?TX��\3�����!9����!���}"K���#�������-�* ���PN�^���2��1��C�E+�	!_Jzi����A�c`F�9�<�F�^~9��t��ttH��y�|�H6�\g~Rb�����6���_��pr�g��������{{��	�)����{>8�o���v_����N���\/U"�.���%
�\�Ai�	o�!��C�R1OS�}��������6[�������g���>������� ^X��t�U�f	�!�A=����i��m�s�#N��"��N@MY�O������d���������o�����h4����m=��:�H7yf�a6<C���-)���d2(.��*H��{�*��AB�Y�`+S�GW�����y�mc��g���Z�XKQw-��
��0X��N��[@t��n��1O�	bt���c+$��0����k���f�g�7�zx�v��������e�1���}n
�
��{��0#�j���[0�i#�t/�����]��"5��anqr
I9��ZB�����V��2O�X���������;|�US}������:?��";��8��Z�O�r���
_�H��c�}�~6�D��R�,���]�<g���������_�ow���Y�*v�H�T�y�f2�����NK;�l�@�Z����v�>�	1��9n�C�6���k���v�5VV[R~���"�����K
-�l$�d5qC];�$��g����T����EC5�}<��N��}�I����=����s
���[�L`f�A��wC��R�9�_������c.	`�������h�Q�/]����N:��E���w �a<F9'##q����NSx1��#�PQ��$�K��j��T^[�c(}+"y�p�Nx~M��H�R��0{���"=�&����-������#�����}���{g�`+�/�	�����'����sC���m�S_����v����_���)�E��ffl%��0�IU�I�����\v3��cX�	3�pP>�xw�����BbE v������!��Y�A�p�`f�b�`P)�}�jt5hS�S�O7{�H YO��M�U'�`�>�e���/ ��H ��*�<�I;�=f���~�~��I���O�~�T���d]t�r���*�������;%�r�)13�4Kf���6b'�"
P�}y^�����f��A:�o*��.��E]r��=|�=0K	���������r��k��7��������w�6P�b���/z���������������Y/�x��f��o�b�+�J��M0�=��B
9(���Q\��j����|O��g�����1eI�{����|/��5�	�B-���V~k�h���_4^�R�2ogC�����o�)��C~�<�yN��d������IZ\YEBBQg50Wa��8+c�3��Es� ������c���8v�
J.������q[���S�9��@�l�����h��a�/��]�������
�A��}�,�!���_~�06ZCs����aK^�� }�a��f�%T>+�y�`��VU�����H\Y��$p�'��49�XM:�\\���d<��� ��T�l\|��e����s��.�����^��bs��q�����z<}�����&��y������#4��}#���:����+����U8�}D���U6�+0�W�i3�X���a�Km��:��cQ�^/�������57�Jc�[����� �rP�w���A���&�9��*�~_1�e#L��rEy����A6�?����Q{�x���P�����_�|���<"����a�.�xWA`���M�xS){�$�1%��.�3���9��h�9|�R#�"�g�GTd�.#
�t��?�3Ec�"�6��HJ�U�B�MR���JO��;�yB�L�e���:}z4d��/�NS�!�8dB
�Y`����kf�#�~�	�	�%�n���KZ=�{��sr��Y?������������R�jr�j^���>��Q>
;�oysx������M������g����}��j'�������^�s ���T����N�W������^�ez��7	��14@��S1�{�l��K�CirN �Vc��/~�t	w0��i"��j�{�FOa��p$�"���y
����))j4�"6}�����t����f4q�n�VM�
��y�x�7�i��-��d�s��5=7�<��_OJq��^���*s��v�������������������p�A�M7��r�M���h)Mi-F��`��x��)�prbH���i��~&j��Nde>N��^�L�rY���
��h�0�A���a{�����4�S-
���=��uZ�Fr���=��|�F�w��������p�sV��������_D��6��ou��1�y��������u�n�B�dY�S	1�PG4�t�4�6�!s����"��3J%L]�}�e@�m��t���L�\%gS���$�gcKuP���h
t;�\���5��R���_E���q���kgWo������}�����GvDjk*���z��!7�p��\��r�!�S����]G+�7g�i�%v.ng0��dS%�������� `?��z�g���C�����WB�,�^|RDAk����7
�h����{'������`���'-3�������{�<o�`�V%q�(w���������l0��6��E�|�^^8��J����'$�qp�����a������C
��k<Rxy�`@+b�
���W�
�F�N^��k8��{K�k%,����?�����F�gu�^8��!�J"�USmp,Kl�H�$�������|'�<?�V��KL
��]����=*
���q �qr"M����>u/�D��]p�<�)	e:���#/����U��WEL�-�s�~rM;.X���4�k����:)n��an�J�d���r[f�|W9���HS��8��JR��h��.��k�S%���B�Qfw�
��?*w��I�b�5,�UD��
3����g}���a��1������������i�P��tI����I�����^eVyW���{����Q�Hh9���jd��H
�"{jn�t��8=G�La�:�&��9�c&�B����G��G���(th�����F��4�����K����W�_����AK�P�)z���.�?�t���>>6�Y���J�ObN�(;~6��'[F�D�����'e���������X��h����E�C���?cwI����������k��f�i��-|�w_���+�L���c�"�,���ST����a���RIOKk���__j�-��L�d0=(^�)j���H�H��2}��s��(�v�,w��I�����;d^LZ3v[}%'�
��0��z��&z�v����������]�����~���]<%��K���i�_^�.G\0�f������G�������_?c���Q���vKI�MN�t�J.���,ECx����9���9yX�	
A�(��oe���R�R��4a�g/�3����_��7��K�qO%I�0��4g������9]339eS]�T�\���f����~�v�m�_4���O?Cv��x��-l�I"qY�i�c�*�*��l�Wz#6�"��T��	e��8������M/�zI~wUr/���r�����,*���0����LI�ws	o��_����mAo|�����4M2`x1k*n}[=��r�]K�D���z����};�9tz��x���1��3�����p*��Tt���p�����L������~��6h���?x���f�3�5a�W��3`u��Of
�1:���|fH^+w�=3
���6\�R�������6u=b�	��%C���J1��~��Y K"�0F�JAF;w��=i�3�g���(����\�L�6G6���������9��Z4��iw�e@:A�tP	���[�������N�upR�������'{�-��2���0b�2-4�{�g��=�.���� \kt_�g��n�,e��F����j���O�����un8���N�&��������]T���E�`����Z\�O2�k �#��,��^��jb�$��|�� �VQG:��<�����t���94<t�^���N���5�4,������DGT�����
�P|�� *���F��W��a���U-0�!�1}uT�b�w���������F4~B@^�_�B{9[�I�I~��d��x����3�v�����J�����c��9��w	G����J�91i��_��7�K�N�WuF���sT���7��1�>�V���tz�9A�Zq[f�\q��?��y`���8}�U����xx��������e7��6gNgc�+� �?�j,���5��k���K�OW�z�E���!f�*���-f�*�\���u�^!��]�.�����v��Eb��x)^d^Q��=��:����������/��^ln�x�V�����G2�8�= �G��y����c���4L����a$����f�O�\�G���gG��g��vs�G�LP'��g��'g�����{���������E'�h8����������1�Nw3s�1��
�&`�%�2A�k����cyrV���^�Tt~�N������JE�����T.�h������������z�6���l�y�zE�*o�H!�����-�*:��A�|�.
&��h|����3���\
�J}mVv-x���Da���m:Dq�Q�A����{�
����V�:"*
�O��$�lJ7���Wq����w���@�,��Y��kZ�33
�A������#�����)��+����=�$����;��f[�-�{�z��5V���k%v*)���?�?�k)x��6����4���H�b�zL�'�7Y|����	���j�X�"?���|��l���u;O�u����n��M����9��s+�&���dO7��#�_����!�y�So?Y)���d�g^����Q���+��F���y�l����������1����O������B�TRL �r56�*�rX�({���������C���6��>O_>�\r�t�G���~����3�j�Q%f�g���Ga���
���$�z��>��P�0�E�(sY'��<� w
�Z��\�����>�Gs'h�3`z���`pj��y13d}BV�5�dg.�&�!ln���77�Q@�@�p�x�c�i��O��!���[�goZ�����)���aUo�����"2�	e�L�o�8��d(������v�`D��1�du���l�����p/?2�n�
�bSU���AXB|�������h!��X��a:.�F��d���|�Aj���������
�$��{�����������C	����L���V/���?�|��bc��E�t�#�u-<�X
/���G� ��A����?�2�$��!�?��!f{D��Y���Q�|j����d������/�!87��WxQ���d#�@my7 -T���B5�i�Rpi��>���,��G��">�l�L7{O6���w;���[���y�"�k+/c�.���K��/����t�	����9l����qv��/����m}R���xS$����]���f[=5?���,���o��A< F��^����dR���*�w�"��x�W����{�!�`�`<&P��5���u��|�����/_��one��ol���^&ZY�
��{��-&�d��|k����4 �w4�
i�
�!iC��+���m��L�l�;Y[�y���.t�6�U����L�����D�7a��
��g:�f����� ���6���bnj%\(L /���_��j=���@r&�u����n@8�KZ���s{cc�������9r�����f�nE�*kR�YY���s<j$�B*6CU�Pa ��y�X
��	��@,Q�?'�I\�eF�V�\bc��$�Yw~��}v���^����cfx�04��|�'7h�O]c��V!�������e�Z�c�2Y���������������Q�I��j#\���l0~<1C;�F(���mlv6.zf#<�z�����Xf���J!R��"<��������5��PW�M1����� �����UsJ�F�
|�a$JK�_<��}'4���S#=��8����_�����kq��T���K�u���B�Oi�����L���<s�a�\r
��XK�H!�;���Fl�P������X��PN��S����&��������@����I���{�/;�z�t�Y�1�n�e�_<y��d������n.���ic���G�c��3��V�nZ�Q6���LpB-�oC��4�S\`Y��>�f������5<ET�I����j_
2H{u1��T	Tm����O��\��s�/U}NrD1���j�O��\��=;{477����>�b��r���\3�MU�����z}x~��s����;�T?�����?���lG�[M��Vx]P��ZK��Zr��m����h��Z2��Q:���?H�������{��{�����m�a�����Kv������m�����5�.K�[�z@�����o����W��H=K����nU���n�n��}=�d��.�_�������x��.�o��~����
���~����W����e�}�y|.�!�
Q����~!	���>�����:oI[�+���bj�+�^{�g��<R-�G�&����j�����f��.����<���o"������i8�X����5��_�P�;x�^g�N�l��])�����[+ogM���-�@�1�_��M���o��)���<mR�U�O�L|�nBH��}����>�e,��3�������s���r!���w!��7!o��i�������9_�l�8"��4���A!�E=����e�����>���rnH~Q+�k^��:w3�����T7�����&�T]��������Quk��K�n�ksLs��������������WU��W������q[��|S�k^����s�G6��o�����_�S�s�|S�m��n���-���f�gW��o���o��1r����)�k�������/R5���q��}���O�������/���?�xi~�.����beE�zFW�������T_Iju�G;J;���K�,�AC�����U@{�z����@
-��?�ZM���ZB/��!X7����������d4H��s���`<�-��d`	G�R(��GrVaip|��j��^�IG���~���
�o�3(��b>O���4���Is5��l�y��j������X�^p���O��i��b{}���������������j����plR���l-��d��A���k�3�./;�:�-�I��r,	��.����uv,�
�_��pD._����^��
���jO����������e����/�r�j����r-�S��N�-D�aTA#A�1��ke��C�gp�3@T���KXp.��7��z�uN����/����`�]��]�XF(W��{�-<F�.W$]b�<O��>�z�������<��Q9&�^|��I�b�W��Gp&�eSJ��oJ)�h:��W�AL\���P���-�?]*���~���0�U��V:�z7g�iJ94:���d�SS��������C��}N>]���=��&X��|�!�����u���_��9�w8�l�_t��������y���b+�����Jc��>��'W�-_�L���>��]ya����w�Q�?����G�@����~nv������z��J���L��6��&�X�<(��h��m��G���P�����Kl�ll���J���I�lF������}zv���>7��6T�o�m���gI�Z�����z��~N�>��.��=�prZ����v�t�{U�o����������<o�n6[m��R����W���70f,�������f�Ny��tY0\�����U�4]3�z7�g�,F��S!�Y�m�4��x�O��@���3�5�'gJ�O�L�����C��I�	���@�n�f�MG�z��F����<�$���;L��R6�	\���Y��m)8w�H���o,�����f�Lp��lx�4l��\�0�ZyR0C-�V�y��;��|�
�z<w���Cq�^�j��S�����������T��u5p���zA��e�@
T���vC�*P���&�����zu5Z^�h�����8�$E�^����\�6��5�E������lw6:O�_��s
)�������!(�|�Kr����]nF��%i�vK=�V��&Ic4(�`���R&;�c��Z&d�Kk1$���kju}5*2T*B���L(�
�'���QF��k����.-@j"�u�ZfS��&
2��l��;�o�1?��
zV�$&�I������l��=�f�6�k�����~��5�m8~	�Sg��HP�+r}i	���)T��/8f�����#��(��}���\o��Gq�<���A�Gz���xf�	��9�6��������X������c�=��B�mO	e�\�t��&���-K��e��n�����Ix��(�����{nC-���!�	O���)l�R:������,�Z!wO����yK�[��b"����R
�I���|z^r6�,���fR��Qn���@ZMb��	��T���?�uJ�=��* s�6��Mc
�����g���f�}�.�'v�/��+�g�IX��h�������<(��q{*�����fX�uI����$���!gVip�X�Tn<("Ig������A�@1��nx�t&f���`dHH�$��Y|��,!DV�c�y�+H�1J�Y!1}��y��'�LO�7��D���`����fE�vM=�"����7�{�3�5���
���:#�e�F����5]����`��A+�� �`��N����7�TFG�y�s��tJ�l���K�!�_�T@]����@^�z�8����K�J�?0������=a�`���O�i���yI.�?���c��p/���[�����6��o����,���y�!�v�mi3������+���	�f��������x�`(�����X����6< �M����b�-���D
W)f$��%����l� v���w��T\a��5r��"���������}!Sa�Z����'��d�����\$�|RZT`�g��y�G���L`�Gx�8�@&V%�f6eDh�E��D���Bpo��2�(�������[�)����,Z��Y��\�h�hXA^�����S��#"��2QJ��6�
A��QbX1��>#��*%��|���9�m?�/[(C�a��P1�4^�W�k���W�`6p�J�-��a�k�{���W���7R��,*�����a=E}
|y7"�@^�Q��=z��S1G�p1�����Fd�����s����2��������_2��kSS�1�������n�����b��M��6'C�����������ve�5�T�U����2����#W"u�����E���-���0�$*~�|�4�<s`�����r����_�P�eY��%�r��XZ~�l^��O��w��m;�cd��*>m�"?�E�0�k�?Al�c��b����IY���.-6/eF��8��w�����.k�>p�J�����!y��
�����A��F�#Q��(L_l�S iw������i������N_�%R!+���-�����<����t��N��&{<�iZd���������OJO��L�X�,�2SL�t
�+8�
}E�������SHf6*���m2k�Q<z{������['�'?o�A+�g�6�w��_=[��S]<T���^*Me�X�&J[2�/H������fg�3jo]+��|S��3m+���}@:�����,��3��$�T8�8��L�])`JP��=>����e���`�&� �U1�9h�=��PX�T�\�@�R+��9������n�L����	sEg.l�;V����'sx>��G%�'���K0� +e�n=�3�NGc�����z��npc�TPNM��f�c��-�����!<0b]�|���8|�H�Me����f��7$��<~~4��hq�?h�6��Rd�+�2�����y�p4K��i'�^�b�ZJ�h+[P���_Ov��U����
oNe`��SVV��x?D�4�]���0$�t����B$���-J�"!�����_�4<E�-�E|�!�.j_�1�������4�T�~(t��^F������ �������2��2�K�z.�bNU��#��5����|��[��w���}LY�����
����d���04�,���zjci���>����O��$�F�1w��$Z�y~��w��J0����/����>~d���Hu�c�wTA��F
��u�����X�V�O�T0$boLOI�����|��A�t,ht.A�xe�1��^�`����	�.z{P*��R���lt�E�N�YI\�$���E��D��F���m�<_�?A������.���~e�R�3��<��bj�|�f�Yn�b����K��j9_�jT-���VS[��5%e�����t(eQ)�G��|:����v���pM,\[���>a3�{�}�2�Gi����cO������.�������iX���O�{�p�;�f��Nl�� ��� �����B%u������6����xY�MY]��U����[��0�+�/��U�+[�=1���Z�.k/�!�����N0�'0�t) ��?��`��H�&d�c>r��vs#-�o��!
dyR&��q�Fu�����ENl��P��J2��������w�i����_��(���
;��$���k�����4J���������mG��J�����	� � u�R$$1����u��rV�D1���[W��\ HIN��q`��������:��`� �q*�����k&gjn����3�
�fDaLaj�e�QW�Pa��I3-�&yc4!��ZB��7����N�:�����@\B��RF��4?�0�xk�����ES���	b�J��J��Y����)�#3�UX���U�1��}�2x2J���k�'D��'�0�t�c���}b	��u
�����o�j�,P��rN&z��w��������������r8�w�[�c;tx�Y����u��'t70����8*-(j-�XU!#K�\�VS��Z�V����I���OF2�8���,-ni��@M���,�c�|�<�*��*���eJ(�T�\c����K������]���
�b��,KGkI���e����4=@��.S�"�H	��WO����6?���EU� ������ b�3��P�u-��iW_�.�\��������N
�9��1&����q�>�x	�R��2��K�q�,��0Q�yC_�����\���0k��9 �1cv����,v�
��K��6#���/��i
�����
3��mJ����_��WQ�;��'�h��d���k�E���R��Yg��61�I�[��+3�b�3��c�8�,Z���"TQ��O$�_�k�M/%���@���3��������=�W��l����E�1������p^9��uI����]�mO��V�e��y6z�h�S��>���6p-I!\�%�h1�����6<�#�
�+�M1@g*�4��,��D�i�����z�I�U@:�����{5'����Z�eg}�k\��r�hey���BD��3Ac:�H�1�����<$�%C-��W k���lRs����	h15�',c��x�Wa�	c\3-�����vm%(*�
���DlB�r"9��������g��`�1��{\����(��*�&�@A)��H�z�K{�e)�����}'�$��N���1���y����6�Z���{PA����i�.���(�.!7N(���i���i���`ah�@�����&�#d��.��aj�����c
i������	
P��(S����W�=�&4�\a��m���t������4v��R�]�4[�a�V�H��J�����XE�b�����.���4��o�����V~�W����\�z�'�]8~f($���S"��j�%'�����%a��/��c	�#%����V��������V�b�����������h����}���7�����C�����&�]^��4b�Ti���tX<F���>���LW�E�*�	��T�oo�0�J�x������}�29gO	�<��^id�*k���+7{.9�S��?���H(_�]���-`�3NH�G$IhZ�P.�z���e��cP]�������P;����{�M$�<��Z��]�����]��������[@d�����N��T���%
&�Hu|OOr��=�X��9Mn�\l`��flr��D������=j�&����yJ����2/��K���x�������b�/<�f���l~�����=q��_)=&�cb�b�
��e%��$������O���Ru�-�-����F��{wnl'�}F��*���?v-,���x��W��<��(�<=8~w��q�T6��I�l�O	&�������vN�/���;=:9?���?�$�
K%�o���m��3\N��A(%�A�]
�JW�Y��dc�����iQ��ZU�-�LBN�n<Td���;������
CF��
�vSr��'8�9YX8��aQ	�qT�o�R���lQ��v9������Nn�x���Ex�J�c'�����@{�
����15��C����xJ6���zJse�*y�P]8��4gn������������]�j���x�=7Nhrc�u�U���,�X�t�r�R��\g��n{Tm�����J;�nn�V��T�m@����#�j���D��w��s�	�2�xv���1��<�-Ds:�:�m�� ����6�ZC���4��@��� ��V�f����f��xJ��*�}�(+:���z8�P��C=/��f�(�	
C�m�?�%^a�K�Y���=�+�"�M�&�8�3FAm����k)%�I)�����������
����9sx|�|bi�������M������*�C��9]�U�J<������O����YE�N�Z����t��[*��U������N��X�Jd� ������2vK3����4I�^���A���%f���/����L���2�CAD<E����H��9-�j�Z(-��F�{r7I��C4�F�Y��E�zl�Z�_�4�jY�e��h��8:#�)����zH���:Q"I�Y�Ss+kx���	A���m��)T)�+]u�D�������R�iT|���|e��M��>l������]�7� �
��m��;f�P�nM���>������������a�0-�o0p�!�}�.����� s ��)�P/ ��[7hd�]-�J����n�W�l:,T�E���K[�P����*�����r�!�C���n�E@�
=�k� e�.�Hl75wQ��hQ5sW6bw|2` ; ��ti����������oC!)u���jR�^��LL�x�_������/Ea,���ZvG0��������E���1
`�T��ei�S)������~��7�_*�Ox'6h�q����z#dv�����r�<��J�n�����f;���m������s�A�>���n�&b�Y9�#��.��L'��/��{Y����s�*�."U�\����D�3j�B9��,�?��.��?[����[�\<�1�&�-�
����v��k�Z}�T�8�zu��k�j>N�[JcF��T(W/���i=%�=�r^�	��9;��'�gg�EQ"����T��<���>�X6��������N���}�H-D��K�/��r�9�\f����LkU�8;����P�<�r�U
^vN�{+QZG�C"*A��
5��KL�s���8 ���HaU~��=�G���9�7�m�N��%���������]�Y�Y^��q]�����L�W��X��������7�~������VD��K"FG��uI�e�sL0��F�^hG��T=A����x-��s��|N���&�U3�C4�D�`~A(!�zr���&��*�7��q;��e{��s�n[����0+���|O�vl�"4PZ�x='2B�3�q�s�7�����������r��%h��73�I�����t�<�����h�}�b�dQ����*V��	�)�5h�2����~N|uv��������q�{��r���@��O
����l�D��4`��^���Y�����b���7%��������A��<?���JI�]A�Sf(��z���r\�0�;�d�g�)#�6S^��Z"�D��I5����&�����vbCN��\C�?��G}�1���2�d�*�'�����R��D]�D�,��������*>%�5�
����^�] ��l�g=��6�D�Rc}���W����!�Y�����tIw���@�)�*�K����(]�g�tY�(-��x�����z-�h9��q���
���
4�$Ho���
������/�)*^IYosu�WO���t��a��������W��[L5���#F{���%P�����v$��#5��`���?��m�!��7������L�v�7�-`�6dE����K�ev	bk�Pa<�2����<u�VuR:�M���B�{rB@	�������[�V���K���n2�1��� o�IB<4�x�Z����%��!(MP�0���u]&E��h���	�E+������s*O�h�/��k�*��E�_!m���Q�.zky�tp�����0�E��!NdvD��eoir��`��Q��r?��'n���P��I+*f����l��V����.s������1���
K"�x(��+��2��H�Z�����t"Cf��.)��"\�e�jNL"��(-q����)Yc��,�q�z��
T������|����6���(�H	��eW�����,�Q�D�w�*C{\��A,��<�Y��B��;y]h��`�dB^����z	�{E.0p� ��H�o�a#��.?
_�x��^������tz=�b�G�8������B��cW��p#VF)
�	�����6�����|��l�o/�~�����W�i4�o���+�s-�P�C��!o��l0���:���-vYw�A_f�e���:vef��gtB����n��]j��g��r��F�����3K�U���G��YV�?�*+9��~��Pb�����s��&t ���;#b����0k(��8YeE�!���;$��*�\/� ���<��"�A�C1��(�!H��5|��������.��a\;��Ll���>�Y/��03FLp/���z8�g ��@���w{!8fm�C5�F�Jh��)��������F�Xb�Nw>�3����5�<d}m\]� >k�P�p��nC�����!c
��t��MUp7U�v�N�^�>IO}v-���E��l�(�:�u�^4�`�fS�$K��X�X.��6���Q<�i=�M����x��k��F��Z����7km`�}������/��Yh�������}�R��([\:W�,��J0�Z��#y�=a��7���r�M�����w��V��"N�G/����.�Js_nS�f�����W)(��w��	�V�p���}��5n���e�LJ��s���}��]�}�9-bo(Wc�"�#����`����������K��R_�N�����`��}NUO������-tu��&l����nW�s��^GTH7�����x(�A�Z�^�����Q�q�6����d9�������d&�)H��b�3tgx BM��6[vT�MFl����p�\m��E�P/��������9q����������N:�o�7�_MG�����n�-QI(N7�'�'X�h	d�^�b�Q�*�����A���"��E����9�V����� 6�������y���IK��4f�p4��l���-Q�0�>�����#\���'��a�x��Q-�}y�c�j�l�GknJq�_�x�l8��;�X�tT�NQ/<rE~)�������r���J�fw��.�Z��Ei&_����F�����(�g���0	��V����sT��]7F}Y�9��]���z2�e�ajE8U���
��X'���7}U���q�����{&�H2�?��U����m�;�NO�WOq*X5��D�WK��Re}�O�_�w;�\�+;�r���?n��W�
��
X�Me�T���������W��LM�g����r����j����J��l��-�T�j����I������ne����rPl4Z�����|�A�P%����nk���:n�\w�fsw�������G4�6u���JK��{����	6��.��	�'8����~�hb����/t������\����U���
U����+����ph�����I�{������������D��yd�a�5����s�C<� �����X�@���`��J�T���b5����?��i��!�&����|���b�*(���d�`���&����_.�~�K����l|�~�����/���������O���}���7��3�]��07���Iy���0��:���;��W�EmeT�tY��T���Z�T���J��pb�
�0����y��b�.��?V����Osk��yf�_U������t��/�\��T3���+�r	Z�j��j����6Zu�U������`���G(�J�R�F����B��#[�"���}�e �}��F�����,�m&�-���F��P|FU������MC���Dn���e�Qn��R�^�UkNsTu2T���Dbs�{R�B�%�38N]t�0���5E����x�{4{���!�=
�'��/�/��/H��������w�Bw��� ����U+_S��J�������w�e��s^������Z�:9;|w����+�wSd}��]�I	���y}t�9�t���h��\6�-�qg����i����/��'�v�:�7�n7^c}�\�������;������������%����=
���u%��
'��
����e��vz�o~���a���Go`��7����]���1��<��U07'gW�B8�kkY|�C������%K
��g����(p]����]j�PN�H���d��P������s�K�&H��Xywe�K�?�
���*�~�?j[��}�\V������Nf�����#.-#���:��3j��f}t�	���"A(��</�S��<~M%����=�������l4U��QIO(:��Wg�t��q����b`h?b��~���I����{�<(l�R~r�������>z:��������UU�o\��9�0��
�&�b$�7X
%PL�b�����rJ��%����Zv
^���E����H�'���V�g5��f
�)�
��TJ{:5�Y=������{�;���L��#J�T��������t>�k1���hdqO� 1M���p1�6�����J������tL����H��5����������=?`y*���uO�F���:�c`������o��<��,P5�@���a�-��`���!�7�dKp��������rr��[�_~��/�d��M�$u�eZY�a%������$�?��g���K*U7b�Rt |Q��^Xy�/��~�c���@R�"lIPc���/�����������KW�K=q���-��5,�����R��EO�hD8*&*��M?��~���9����J`Tn,4�<�6�:���n�eTD��:�c���Y`��Q�Q�q��B���ywp�f���O�jxo�������aF������g���������lGnep��t^���A�}�Q�� m�1��?D��]t��NR1�U���8�\�t��[����K:���b��J�j=��Js�>�+��b��Q8�w:m��W�����d|+���~��~Y��W��%h���G�(�Q�WA[��Hi��V��=2_d��yt��Y�r��_S��zT;�=�����<6�_^������P��
J6�4S\8�,$L����v_�������P��Io�����F_����P�**�~���9���=��fE5]�����1��d/�4�DX��o��WW.��o��W�[����������}��w��r:��;�4��G����k>�C��B�?��`�*2��#~���;�he��
5qf�Np��@���6��q��V&N<R������"o��d�����_$��������M�^�#����U�����n���������=�&���2�U�e���-��
������w�J;)�h���5��"BT<!_��xXGYaH�����(_�^`�z8����]����l���_�"V&����<n�n���}��6�`Y����^X�@�?(�&����K��	�|��������h�w=�����z��?A�#y�]���W�N�~�qJ�SK��"S*��G����'F����b�{m��W���������_h�F���_`��(f?���}���F��#�f�_�6�_Q�6}�D���I�#i���d?��~��~,<O=�D�z��	l<\�_��u��������	�#A�$Q?+�8U>�����Kn��Gw�?s&�����xw..�.�lJ��~������O��|{tz	��n�:v?�*�P��Q����a�)5��J��nBN2�������f6��Vu���,�$V�1�w���<�?����ZAmU>����j�>v+_�������
n��������?�',��Qb�]�5����jq���n�j�4�D"���3��gF��������������Vv+��f�v�1g�j����s��z������eGEpMOPt����	S�U��_��G��~wJI_�Y��\��� ce�M�{����K?����jC����_�=�?�l�o���y��������)�3LXm�J��0�Y����������	�����{��/����L�W��0�T���w^j����t,RC��/���.��N0���L?����+�H���!})`B��g���Ow��{#L��f��&J�)ui�2c(�[������f���Zu��:������k
��p��i�)��^�^b B$^Op����Z����U�|�Y^g^��qh8�����+��x�b��/��8����p�L
�y8��~����p1��C��.8s����q�i����>�}_)��"������-/C�x�"D>W)��+Kk��yiA$��0=XZ�&SK����D4�C�J>���F��!�o(G��;8��o�[�c���OE�Q;���[A*���r9��=�����uBo|[R��w4OQ�fT�M7��c6�r*f���PC!��P�,Ft�D%1U9�'pH�{s��������������	$��O���4��p:�Q�_0a�����5�}8wY,�o�s!�A8�f�RJ�FGX<j+<�l�"�m. g���F���OGT�
s��7�����5��?�P�ML��E*���qFk���� t�%/������O��Z�$Gw13���r�I+������X#�M�`7��'���U��	����w��8�?��^"���A��0'��6.;'����9������7o:�����/�����W�������oBX�p!�l������[���p����l:�t?�]�{kA
�>oTg���s�;Q�w�J����r�S�c�����h�!)o�,�9��g��MW7>I=/�x���w@�.k�B��+�?��x�� S��%�z�����i1s���'����3\L(��NX~�N
���l����3�#�(�&I^��"Gs��|���v����UW�8_b�HV�E�]�@X��m�|
���J_�B\�n��g���j1�+	��!�*Qo[�px7��R�T	�����?��o��9���@y��
�]afi�6�v����i����{={����8���%���t����Q�nf"��sp6�4*�Iw
���K0���1��)���Z��g���?;:,,C`�@�k�Mm��g}����
�0q�,��)���2��8��5(b(_{���G	�)��2t��3���*�LsB:��Ya.�}y��H�c��~@��)�� ����g�X�%o��[��!O?����t_��3����T{���#�K�CY���Y��������b����<F�#�`���7���	�Xl��z^�~�������|���R���n�r��`��	$�����������0������/����.��:�������e�HH
��:a.������7�8����K^D��w�^���>������V������+jPc�_{tE������nG���������3W�4O=�h����|m
��k�j��_�s�d1��6#�z9<�����V��lU�8�k�1����x�����3zSH4K���'T?���dUm��K�m?�viP^|��o�����G<7���x��
���$��t��\6l����V	����pQ�����z�����}�yA0��A0��E��zk����:7��KD���MF�6��e��.$����o��%���hD�Z������E�F8�#	!
��_��%�����\7�Y����>�
�}7�P
:y�������N�t��f����������X#�6�Hn��Bc1�8�[D�D���`��$`A�],LG0�9�\hc�'j������
����I�����	�Hj#��\�:������8����q�I����C�Q7�z��u��w|v<����w���;\���D/��R,0~e��]M�a+GSo^��5��,tm�N�[z������x�2�C/���@�c�xP>����J�N��Jo�����g�9���	��'6�%����L1���Bw�����
"� �9lL5�M	��a�������nU>������8���Pb��F.��p�;�a����Pz8����p�� �$���1T����<t�A���>M�(���S�����MaC�������W
/���&w%*�`�tm�c||p��|����#���_2�����@����>�,�>@���L�_�_��Q�u�%o���e�(�?����>�pr�5���H���#�/�0�~��t4����}2qP	N��I�'_���"v$V��l��������/.Wki'p�P!%>"�e�5XY��g	<����}����#"	��I�H��������y��Cw)�V?�����yEtV�J���?��%6������?� �%A�,:>X/��"-h���hD��@�Oy�X�G1y��5,'}Y���2E���`6�;k������r	�5.���T�a�������������@����m����_QIh�����G�-�*�<�Gu�Zr�x��vzR�[�{TZ=*���J+��pFgJ��4>�w�u��/��������e���q�~����So�8e�������*��s=	k�����������K�	�t����e�b�=��J������#Y1�.���,�{��~��c7:e���8��]>�5#��.�lx)���+�NNE�%`�M�f.���MpBM>
p8
�(��!`�{T�������T�x��2A���"��6�0�QI�������3Dm�C�1�����Z=�4�xW�dD�GjMc��v�hh�����7�fy�2�.�6�9p%����3o:���JS���I��v�
��i��QU:v�ob����9�y+o���h����o)�	-s�h5��V��BW��\��5��
R�DV[�i��H�LAHC���,��2�����q`��w��E`��;�zV(YC#�1'�F~�|�[�b���<����d��(*�S�x��"������v���YYhB��"�5�@�Y
LR13
-����%W��4�n�-]Z 3x�}IA.��jjt�~�qF�7f�NE_��n�+��dr������{��}�C�&��ko##��
��'3��6�oG_�������	n�meWa�<q��aN�(a%_��@w0���� g������
����os���B�}�6�;P��G����10���e~�����;��o1��o1��]�<�����c�B��62���Y����dO=I��_���>S�z����<>�����]Ui���P�������R�~�iz?�� _N�6`������H�����
�����vQ�T���5�)/�n!~B�j]�$��%6�V��PU�_M�����/"l�"��v�/
;��9���6�6'1����w�7\|��}X���5f	��A�4��q��p��]{�O�m�|o�����  �V6	N&2���NOC�e����^�,�E}t�-��G�=�8O��mc<5��71��\� xuvv\����qO����*8Y��Q������V��M��D�PRN��(i�������E�,�N�rs�T��y
3���Mu����W_+�������#U\�����nF]�*��[��>���"t`d^;L%T<�Vc��v���kf�M2-�l��%��9*���������A�
�U�����GYi�6F�j����3�tj���C=j�P]��@J���-�����{�G{��J�/����O�	����{����������X�pv�^-UvK���?�;��P���U���������#l�m�2�=�����m$��=g��6�y�g�\�H���G��B�g����r����j�/+�Z�Yo��S���r��'U�]&e����rPl4Z��1]��?��k<�VZ���J�\�����j�Z�v�qF�j�q��j���*�vQ��J����������������J�|N�J4���"�;t�}�����@�\��(�
��������^��W���]+������������|�������V���1T��
$����S a(*���P�1��T�r�pl�U�g��7�.�b@���J5HG]
�).��\�����\{��u
�h2���"��}t���1���p2�z����\��Es��530��%5�wR'�2��f��������8���S@;�E�7 �^UGgp��e�.\(�lx��q����8,�����rT���`3?t������:w������@7�a���"i���g���_6��DI@�e\����jy�����hd�
� (l{�u��j�����p�C>���<z�"�8�����sXZ?�#x���s;�(tw5�(�3�z'x�\(�j�D �$p���(���=
3�	q���=	�Jc##R����/`�M�guB8�^�s9qo��4qG&���/�>^E�u�p��E�<pO�E��/����w�����a�������}���������/�r�;���<w����v��������^�S��tE���"c_W����
&z'�f�`,�$�U����(,,�yc���)�^uY	L3����Z�+R
L�X3�
m�a+�
�����K�	��*V�(����4�W
@R��pi
k�R�F������v$����k��CPw
��B2�O�B=�B�,�+���`FumWh�
�R%�3Dm��
���<����f[�����J�aZ��*��[/��������XP$��;vA~�����V�2hW[ZGA{��-9|��(����bKm�<�'����;�Y��I��
�s���|\\Wkt^��"���k|j#]��8"���|������wtztY�&�k�����/�1o��=bLh����,���Y�)Q�������@qqU_�� ���v.�z�����T���2_w�O����*tYs������\�]�n���
���7��4; |�^}w����f�((OBu�i��o��5��OD�s�ztPTOx!����<����}�[b.Df� ��#u"N�YE������]��|Q�^���K>LQE������<e
"�Ch���RXd]�%W��np���/�X�zZ�"����;�UQ]t��;���e�������
�Oayp��v��>�_b:�r���7���b����w�y_����"�d����������/�h`�9��Dx���Q�
@@*��P���O��2��~d%t�HC�����b����Z��_��!t���n��|�/��W+'����k��nm���J�F8j��5����-��_~YD�J�J�O>��~?���k�/�`E�HN�������������AAR����sB{��x1�L?��*��6H4��'Q��Y7��R���6a�����u�Gw	�=�D����e��!-f����F��]
]B�@�����3��J��:�C���������oJ����TH���J
�����8��q�y����m�=�����<Q7�Z������|�����������mq����v��{gL�����M����3��@��������������Ht<�q���a$���q�G����������<[)xP� W��Q�������x�����;�>vS�t����L��6���c����������j;kQ=?Z������|v�v����>:���f-���?����5hU��7q0i��������a��E���jkb5-P�����~�w<=���.�����y^68��O��l���c�����w���|=^���hp�,b{��Cv�@�a'^�t��N;�����.~�)� CP������hI������L�����>O��_���Y��N�m��M���a��v�5�Y�'�y��	^I���&�	� �/���������_L���<�UOO������2����I���)|��=�������H��U��\XH��*����a
��vreE��7���-;O���F�����s��`h�c5�\����wI�;��'��vu���n�R���e��2F�����X�j��IP���������'G��
[�s���qPF)�	��wt�@���3'!GSg�����p�a	[Z�����E���A�1�����@���(���=��{`+4D`�ZW�����%���]�PR�o��B?�j����N����A�zv��`g(����:�9#���3h���E�^���:b�5�mXH"�`1��fI!]�'�*9��B`�C��"3�� ���	��p�nb3b h�<	�y�"U����3l��N�����a4R�bN�C�r�mT��O��sl�R�&��9�+��F����Z�b�[��(V��-g����8,��e�v,B�{�����7�W�"������L@kkk��]�dl�
����v���=�?���S��T?������6���}�2��I����7�����wQl���p�j���;����7H��3?I�E�x�8r�RS7:��.�~�GW�U��@	zO��0D/_8cj��&�\�
H�<��K�w�BN�.5	��l��
����%P��r�;����[t��/�ZUqG�bT��6aRb.�
���U���1u����'W��]<*�!9|P@�0�W,M���6To��)�V|t�[:o��`�yI�H�����[��[��8�^h8*9Es���YZ@H�u��h�Ft����c�lCi����S��B �x�CZ�n�z�
��n���>S�[�Z�\J�w{�6�{{�W���|\Z��C�w:�V���g(q�MuG��sI�� ��g�h���+B�e���w��t�N���`��a0���}����m)�E�]��*$H�j�b3��������&��h�D��%c� �D,<E��G�9	B�9���&�X-
�F�I��A�"SN��yc�|����i�<��:�Y����&�P�@l	naz&V�t�A��p�$��:���+7�JF(7>����b
�������<��#0��D|>���"�2��l_�-_Q���LB��(�#h��N�����D����](f�/�������K|�.����s��t�G% �zz.������� �]K�*�I q��(��Q
�X��X6>y��G��(�>��DE��
B�='����������Cr�@���Fc���V2�>������W��o
P����_��=��RtU�&���EG�y������P��c*D�1�h��~�����Ql���	lX@�s�M� 	]7u;-;AO=������Y��WT�����|�rX��t�E�AH\#�Z�&������7'���Hh��G�s���X�������oF���-���MtMi��p���+p�D��K�lO(��
[���8#4��!��%/f�1@�Km�S:�s��:����w���
���B3+�>m�i�L24�p	��
��}L��2������O(7dX7�����p���h�,���eGK�����/%}Y����o�!�������.&�K�W���JG�#-Y8��N�PE��yG�b�3��E��y���k�)Z"�<WO����\�+%��d�$��|wgL�RpK�������.�+���@��bpO���	���=�Y��R�������'7�����h�M�����~�x�9��;��R*5mO�Xu��c��0^|�D]��v��^f������
�����2����w�~y8�4K�~��r��Vc��~7��|-o��F�$S�5�t
@��Sy�l���vC.X*�����Ng�_��&��{-]��>��,v��/]hb2,�l��D��������*�x����W���H��������"��u�k����j��R�\�m�v�������F-��fT��4d���}����D.ywz|t�]����W���Wkk��(�Q��������]cc6"�Y7�$p�T{2���Yi��m�Y�=�'3g0�������pL�fM��w��C5?;&m1�k��Mf��C��'���!��M�`�����:�F�$�x�G��7�XU=#���Oi��<28���� [B�M�:#��YH���m���
FSJ��*r)������������4G�$p�����g�=�V�MduU��r�����i\�������A8��0I����������w'�y����E�x���.:}��^f�W^kK����2#^{��3����3�������L��6�Q�'��n��D��>��������k�������.��HY�Od���~�0�z�<�������
-@���/�y���>���	H�oo\��EA�En���"
1��
��:��MQ%�y};s�V�����y#�o��j����� ���j�����+��f����ZM�`+����'�g����QC>�p��SC�	^�a�:��]���m�u�t.��m
r7�
��N�,��wh>pn��6��������V�y=w������?��q�?u�Df�����{E�@wL�=�Xm:W��e�V	ml��������tJ�a�_�������P�epa�B|h�Qo��m",I�^��@�)1�)[�o��D�U$Gkk�t1��+��Y$n�4$���,$�T�[Ac"����L��4�����<d-��*)�n�h���X�0�8��T������
�z�����y&��	����-Q���&������������z��^�4���rP�����Y*c~Y%��Q������n�l_���,�������$e�?"��D����5��z1���UD?��QL{�=����.AO�"�wa�'V*�"�$�
�����O�"2�E�J��~[S'���^�m���$��1�m�Q�3��������h�n��V>F��VZ�3������O�XH��q���od���H�N�|s2v�W�kT�MN��9f��G�\��t���2\�^�0(�1v����k����q��i�Mfc��l���FnP�JI�#/n���������-gKJ,�=�uor�����n������J�*p~����|�bq�6���
�@?J���\����V��:A���F�8l)�G	>I����B��|h0�>9���	h7�����VZ�
1as����"K�n���4�y����Xi��g���pb�������v�8+������!D p�^\)�:�E���[��3:�]g:R������F�cYoL�!��9��������n�|kK���x��L�=x��VQ{�$E�I�0�}o4�Av���E���&�'�����7'L����u��e<�p�1<����P=O��lUI�j��P���i����,i�1#�F?��u��5�E��mW�r�!Ag�
8'���"�p4gD�o��a�b�d{�?�r~�r��9�wR';�'�nf�q�jx��%��r���L��@�������vP�(�������jP����[Cg��
JtW_V,"t��hD��,gpmH"����c��D����H�		�@���F�������n���Y��E1�,���GI��4$��1��M���gP7q������w:���|�/!U��"1aLA�;,
�{��
$$j7�@,�K�/4�p�*��Gb:\T:�2c�B����NSC�%:��<������
pb���6�1�L�^�7t#�1T�?���	������h�x��2��M=�=�V��`[M=�����Y[�z����y�������_����d|�z�)�f��G��7M<�A!�$;���	�A���h��"���T`g�N8�nG��#�VA��JW\�7K��brkL�"�I�&(�U�s��P_�dI�I)NT6�����9A�z���G6�4��Lb���7V��O���c�����)S~������	��x����|��Q�<�>���VC������%��8P34M�lh��eE���Z���3��1h�Xt���?��a']�:4�_�@$� ���4��������NQ*��������������pY��d��I,`<�Ph�w��M�_����	N7��U#�9��?�=������>qhy�9'$Ph
r �1u.���8���0KE��J6A������Q����d�<$��bJ��*��gysm*��������YP�D�T�iD~�A�$�����:�&����k�B���� gh]$8CZV���<
��0��Qbf���	�`� �����M�Xd���a�ktm?lx��g��vzN����Z.B�)�F���������:�3Q%*��E�i ���3 �<o�����Z����d��Oi��d?����������r$�z���+�������m}��6��)��������$���;�); �T����f2� ����$�j�����������w��=L �����9�sG���I����:Dbzj���W&(�������c���UK@�������d��'�:�����A�'�B�C%��h���������u]�v)��,RxLQW���������g~��������R�����D
��3w_����f3�����{���<�����
<�o8`$b�G�=���u�;'�W�����UW�88;���:L�o;A�n���U�s�����o�G��A���b�'����V�l{~1��8������+��3���u\��������fA4�+�J�Uo��Rf� ;j��0$����D�;����+������h��d�V	O��O��M��H�����P�c��@��g��~J9V�N���������! 
��%�������85�1�#�6u:���`�}�
�|o��hus�����F�I|�\�������|%H&O9�|��"�j3�� @��O���a���n�o�$�*<a!���$q����,LL��=�&��NG��s�E�sg~}�L\�Y�����4��2"�I�C��0S"-���g��gK^j�����{>�&���V��U�c�:�����Y0g��xw~�9\�Z�����������r������5�i�1^k�-����c���,L%v���Nf���6���	C��6�|� ���(��X�u��D�2}d����$ZH�8V1���"#��037N0��}&��)7����&�����Q�]����/��\�h�<��z��b�d�FX�tV�`��bA�
�l�)����k���0-�_c3���7w��H�����p�2�s�}PL
���?�YR���pK����f�l"������?���YY�K*�qf�D$^�|�!��:5p)��m�����l�n�����w��2O��������JCN(pm�9��t&O�����"���C�1���,�jf^R�������NMHT�O&�S�
.���Pd�z\������3%M'����������`N�i����I���X�j;FhS�U���Y�+�����g��������������Rl�Pf�#���b�c�n����HZ��

S�!X�C(�����X��|[���|��D6r�QZ�I�*�/`�����!�'c������~\��y��4���+���<�bT��ASUK����4����@L��c�u�����d�qU4iL��nl��������(j1O���_�_���?	{Cw�c3�B���s��9L���EFZ�h����[:�������z�d~&�(���9�����(��������\+�n�
�����#�����S���p}F9Z`�)`z�e��!I���6�T���3���l_����\��-�����G�������eO�2/�N���y�^�+�3���"���PA�����8�>N't��#�7��,;0i#F��F���������|�U���*T�������8�I������bb������W@���8*QM���
�m�����42��������W�Ey��?0�	�le1JaAJH�/|#�-MZ1	-�Y������M���M:i�}��}�S`k�Y�eQ�*��5��x�L�`[�����ZTc&@y,CY�.U�x��zNx�h&��Nj����	��[^o��M3�����hrKU6��k����Nq�'������a�1�!V����_��Ju�cd/����K��lbb���c�%z�=��Rg����������2��"QF����
-�����wQ��8b+JnUW��lA��V\-	%VR�=�x�I��F�8
&��9�2E	"���;��*�Br�4jEb���~A�Q)���%�������C�����NRS�ADI=�R0�5�q0/��q�>�S��J7c��n�P �y(6��
����J�b�F�mO���lL��3������ynM�C0*�����d���7���o����<���F�V��-��\��w(�������4��D���,e��J1�7G��?��)�M�	B,��<�*����tPH��D\�������1�5�k�5=���\��:{n�{D����Y�L����7��gk$W�I�G��]n�N�k�a�(���I�)2,Z��0��4��Q�,��\3N,A���i�F���)�|���<l.���t���ko����x�{�*e������������bdAE�2Z��B���I*�l*�HL��M�Q��|�!��6$�0Q�x�����V��F��������+!D)��"B���H$@a����)@�icY�Z�u$�z.F����h��67Z%��2�$Y��%+l����"�J���k.� "oo'��7XF�V�tl��1F6+�&3K������.����~�nV�Y����1Ywj�gPu���6`_jz��;,`�wq��B�����^��_�h��4��{���-���_A`��A���a�7���$[R�����u������2
������!�iU7�#sN�QEr�t�������qS]��E]�B>�9�/B���y���;q06���P��~`�}E��jJ���qZ7p�70���	7l�)@5�DPbD1f����2n��=��F��R��h��.��o������D����RYN�����LY��hx�X���z������EH�E<���#'����0����k�8��pP�0��8I�n�1��R���hJ`�C�\�p�����,�o1x��6�E�9�/=	�x��1d��G��C���ib������-!��f192iv�lATh`"�<
 z�X��I���(g�*�,�C)0x�1���s���W%7�'.��@f�.����K������b��
����[@�e{��F�'��0~4q��t���z�yV1� {�����k���'�����R���R������h�OJ4C�)����4��|�CGV��������Rk����eDJ5���^Qj�j����R�)t~�t���\:}���5yz b�|1��q�iw�,��t����\�V/s��uC:r�����Z�%�,^�(�;�"�m9��%��Mgc��v��G�4����������=�������_��z�z"�����bO�>v&p'H]t<:? <.*��_�]�&.;{w)�vs�����u��d���7�����i��=>����rK_���?�����>0���<������>�m+����Ii,Z�:����&�:w��������KL"y�9�}��^������[+%`B��b�m^���b�����7�J��,=8I�B��jU�n��R<	#�]����eh�Y�I�c���*�0,�"�Hr^��Y������;Rf������.������+'��$��R���������"yt�1j���N�R���=�cB�c	��*I�0~��f��%���*�@d�����W��N�!�^���P���8C!����
��;��5O��#�q)�������@"����N�-��J��h6�V>[O5�f��"������)b���
�'�	���7�g'���5�������+��Le�)r�C�S�A9����s�� l�p������N��Cr��}h��d+��X{I��){����NPj-7;P1�T��o������'���L�'�%���i��)0:�p�"�:xLos�AJ[�KNF�������*���'�c$���2���]��������TjQ��U��XY��r��	���k��`-AD����y��{DP�9�2�Z9O�����N���dn@3�Ta��j�B�Vk	|��������t���,��.�psq6v��,D�c7�/�E�,}��p��/Y�P)���`��z���#U���M*�:��������H��#	O.�k�j�l�������^h��A�^�����j��l����U���y^x�#~��Lj��n�����be�L�R�8pV�8����H������e�g-dA��m"0^(����R���:D��{
WT�%��������.^�l"g��)Y��XsH�1�[�o�J�ARi��B��S8����7Xn�,���g�����#��8��g�8g�z_���&b���#��g:� ��E�X�$��
%�9i"?l\QD�{,��
4o�K�����#]�~9�U�^�bdV��$#���,�D<�F^�����m�>Q5�o���Z���+RFx�zq����bO���i^���GA�g;m�vq����|�s�������y���uk�}��T��Kz��Cz���P^��'�����9��"�9�$��V�|w�y���9�l��j�#g���0�c�g�k�l��D%�:��?3 x��3]?�cX���aj~�I����(�f�A��Q�j��V�^���F���
���;^n������.���j����� �Kf4�;���5���][���"���R^=���GI�q$��~����HwJ�"����E+�[K������+�iE�����Ee����D����K<���T�K�R���"�64x&�/���u����z������j��[����������e�����x����p�,�9�6�������:(L�f��;�(�0����l�g����[��T�Z���7[����F����*����3(��4����b����<e>� ������Cg��(7�z�������n�:�T��r�Vk��nK�m��3Ui��`��SU(�����D�O�P��T��s�U�������W�4�'/����b�EQU���Q���*��r{���J�ry����D{�����������Y�*CUCcY�����������[D@������7Ez��&�}l��zN'�;�>yN��E{���6�O���!FQ�S���,����%7����w�����a�������}���������/�r�;���<w����v��������^�8��tE��D���L�����xg�OG�U	�+��U�ZCmY�
�u��r�_����U��Kv������c���z��&�4�w�Wx�S�m)6�A6W��D� ��5���?������J��F�>�;��n�Tj4��J���J�Z.�>���&K�f��������T:}���������w��cg���s�e�7�����
�=[W��|��@�����|t�1]���������c�
_<�Z�jG�?|�>�@>v���E���/_J[���`��D������Q��xf|������M��>����@&@�J�<��2<sx
�=�����v�]Y��N�n�6��.�3���d��>��f����R4N�6������$]RhIea��L����q�1�o�i�-�s����d�*����V=!��'�����D�w�"0w�%����sG�;Y\ VgD@'t��h@�'8�05�^�XW�������p1���G
\��9���CLnb��}o�K�-G�`�.�G��`��)�1���C< l��-S}���p��+�!&���{c���i@���-���:��W�����u��"�D�J�w����Zo(��2x�����bMW���Z&3t>�Z�`~���	���BL1��&��u�� g&F�pns�
���4t��~/`���Y��z�<������F�v���b����@o/�87@@5j���e�P�LR�$��R��gjr���j���?���J�t���xi8E�dg�N�mT1���n��E��YpR��/}-��[�z��V�Wv���Sqk�V�l(�'��Uh��	�'��Xm�wkJ�Ke����&>oT�k��b�Je�V�O�{u{pz"&�y����1�/�'\�9���R$�SX%�m��ax���X��1=�%V�2m}+op��"��7j2mV�%��,��u]����x���'��m8:Cn��d�%1t����Rn���u�5\���W�~�_���KN$^�Es�F�����&T�LY7�9��`������E)��6����5-��A���{^��.!��9Pi��1$R�C�0?T!3�f��|�k���?,�c
9g�����]���'Q=�����n�j����bLbd��<��5'��$P����d~���D?�(���f@���22��kv��pn}81��}�d�t�&��q(n,���?��g��|�W�s%@a�&�'��U����+�����_a:K�ra{�����h$<���Te<��7sj�@9ow�}L��,�
d=^imnm�v�;�j��i�=�Pi�C��~�*�$3P��v.:v1o]���
N�c>i����V&�J��,fe��',�V�2O�]mI#���(7_<�iU�s`��P������b2����<���o��N�<��u�$�&)h�b�����0�������dwR�t����]�h�1>�����p$��]�Blwj�p�)F�X
�y���=E�D~��������2=r;zG|g
�F���]L�.N���RmX�������L_l�.#���`��������\������c8��tZ�C��q������Y��5��!��Q��x�Z���@��������S�5`��)AY�(�D����W�K��'���t%���Vpg>G?�!�P���cB�-i7>c$�����W��KN#�������������������5�:K���{0w�x�P�!?RGPk1�S:6�*)J��}��&���E�P�S�j�/X����Q�&��)��,�f�4�|���4���u�����>��N�<��?�h�PX@�U+�
! %��������n[�����,�R��k�n�D�BR�b0�  3Q��u��K���������Iht���]G�(#I��;�5e�1�%g����9l���h�1)G;����+_��
��#�
/S��,�3_����=h�U�A�\)7K%��[����Vu�4�J�4���ur�s��n�Jw��Hf87��Q��H���bf0a�d��/:E�NM5}�&��#�<�1O,t4�L���my[��P���q�Y��9x�+�U��8��6 �
p�I���b�=�M����2M�M�����+#�)��;�,��/�?�+<�����9}=!2,8Nd��mtf�����U6�C��p�o���EaS��)CL*O2�����(]��|p@�/#�\TJdt���,DM_^rX�H���LgnG|�<8>��&��5X.�J�W}�b%��Z���F�3#;i�1��f#�C���i����|�s�$�[4�(Vu���fUEV��F���W<
Y���G����&��������&�"�4Gw�0M�S����p=(x)_��8�E��E	r�d���J��8�8V�"P
ZL�HW�$�l\�������=@��-����;�{X����A�[V�Y�pY�����-�e��P���[�h8�/����,�[Cq� P0��[��>��h8�����a"�%i��n ���g��50w~�o0et����s'A��f�J�`���VQ��@g6| $�0��0A��j�&���V�b�f}k!���G��x�Q6BsQ��,����������p�)�!�H�kP�%�edW�>,`����!��"JRZ*&�*M`��)EG$�����Z��y���@\KW�������z+�-
�F���y^���wuV����"{h��]kj8�*����7_BqT�����67C�����L���q}���72F�f�/��#��p��;��aK���)���zf>�[�C� l�H�z{��}����(�D�ZH�6v����(��k�T���s���
�h������������������������������������������'
#2Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#1)
Re: POC: Cleaning up orphaned files using undo logs

While I've been involved in the design discussions for this patch set,
I haven't looked at any of the code personally in a very long time. I
certainly don't claim to be an independent reviewer, and I encourage
others to review this work also. That said, here are some review
comments.

I decided to start with 0005, as that has the user-facing
documentation for this feature. There is a spurious whitespace-only
hunk in monitoring.sgml.

+     <entry>Process ID of the backend currently attached to this undo log
+      for writing.</entry>

or NULL/0/something if none?

+   each undo log that exists.  Undo logs are extents within a contiguous
+   addressing space that have their own head and tail pointers.

This sentence seems to me to have so little detail that it's not going
to help anyone, and it also seems somewhat out-of-place here. I think
it would be better to link to the longer explanation in the new
storage section instead.

+ Each backend that has written undo data is associated with one or more undo

extra space

+<para>
+Undo logs hold data that is used for rolling back and for implementing
+MVCC in access managers that are undo-aware (currently "zheap").  The storage
+format of undo logs is optimized for reusing existing files.
+</para>

I think the mention of zheap should be removed here since the hope is
that the undo stuff can be committed independently of and prior to
zheap.

I think you mean access methods, not access managers. I suggest
making that an xref.

Maybe add a little more detail, e.g.

Undo logs provide a place for access methods to store data that can be
used to perform necessary cleanup operations after a transaction
abort. The data will be retained after a transaction abort until the
access method successfully performs the required cleanup operations.
After a transaction commit, undo data will be retained until the
transaction is all-visible. This makes it possible for access
managers to use undo data to implement MVCC. Since it most cases undo
data is discarded very quickly, the undo system has been optimized to
minimize writes to disk and to reuse existing files efficiently.

+<para>
+Undo data exists in a 64 bit address space broken up into numbered undo logs
+that represent 1TB extents, for efficient management.  The space is further
+broken up into 1MB segment files, for physical storage.  The name of each file
+is the address of of the first byte in the file, with a period inserted after
+the part that indicates the undo log number.
+</para>

I cannot read this section and know what an undo filename is going to
look like. Also, the remarks about efficient management seems like it
might be unclear to someone not already familiar with how this works.
Maybe something like:

Undo data exists in a 64-bit address space divided into 2^34 undo
logs, each with a theoretical capacity of 1TB. The first time a
backend writes undo, it attaches to an existing undo log whose
capacity is not yet exhausted and which is not currently being used by
any other backend; or if no suitable undo log already exists, it
creates a new one. To avoid wasting space, each undo log is further
divided into 1MB segment files, so that segments which are no longer
needed can be removed (possibly recycling the underlying file by
renaming it) and segments which are not yet needed do not need to be
physically created on disk. An undo segment file has a name like
<example>, where <thing> is the undo log number and <thang> is the
segment number.

I think it's good to spell out the part about attaching to undo logs
here, because when people look at pg_undo, the number of files will be
roughly proportional to the number of backends, and we should try to
help them understand - at least in general terms - why that happens.

+<para>
+Just as relations can have one of the three persistence levels permanent,
+unlogged or temporary, the undo data that is generated by modifying them must
+be stored in an undo log of the same persistence level.  This enables the
+undo data to be discarded at appropriate times along with the relations that
+reference it.
+</para>

This is not quite general, because we're not necessarily talking about
modifications to the files. In fact, in this POC, we're explicitly
talking about the cleanup of the files themselves. Also, it's not
technically correct to say that the persistence level has to match.
You could put everything in permanent undo logs. It would just suck.

Moving on to 0003, the developer documentation:

+The undo log subsystem provides a way to store data that is needed for
+a limited time.  Undo data is generated whenever zheap relations are
+modified, but it is only useful until (1) the generating transaction
+is committed or rolled back and (2) there is no snapshot that might
+need it for MVCC purposes.  See src/backend/access/zheap/README for
+more information on zheap.  The undo log subsystem is concerned with

Again, I think this should be rewritten to make it independent of
zheap. We hope that this facility is not only usable by but will
actually be used by other AMs.

+their location within a 64 bit address space.  Unlike redo data, the
+addressing space is internally divided up unto multiple numbered logs.

Except it's not totally unlike; cf. the log and seg arguments to
XLogFileNameById. The xlog division is largely a historical accident
of having to support systems with 32-bit arithmetic and has minimal
consequences in practice, and it's a lot less noticeable now than it
used to be, but it does still kinda exist. I would try to sharpen
this wording a bit to de-emphasize the contrast over whether a log/seg
distinction exists and instead just contrast multiple insertion points
vs. a single one.

+level code (zheap) is largely oblivious to this internal structure and

Another zheap reference.

+eviction provoked by memory pressure, then no disk IO is generated.

I/O?

+Keeping the undo data physically separate from redo data and accessing
+it though the existing shared buffers mechanism allows it to be
+accessed efficiently for MVCC purposes.

And also non-MVCC purposes. I mean, it's not very feasible to do
post-abort cleanup driven solely off the WAL, because the WAL segments
might've been archived or recycled and there's no easy way to access
the bits we want. Saying this is for MVCC purposes specifically seems
misleading.

+shared memory and can be inspected in the pg_stat_undo_logs view. For

Replace "in" with "via" or "through" or something?

+shared memory and can be inspected in the pg_stat_undo_logs view.  For
+each undo log, a set of properties called the undo log's meta-data are
+tracked:

"called the undo log's meta-data" seems a bit awkward.

+* the "discard" pointer; data before this point has been discarded
+* the "insert" pointer: new data will be written here
+* the "end" pointer: a new undo segment file will be needed at this point

why ; for the first and : for the others?

+The three pointers discard, insert and end move strictly forwards
+until the whole undo log has been exhausted.  At all times discard <=
+insert <= end.  When discard == insert, the undo log is empty

I think you should either remove "discard, insert and end" from this
sentence, relying on people to remember the list they just read, or
else punctuate it like this: The three pointers -- discard, insert,
and end -- move...

+logs are held in a fixed-sized pool in shared memory.  The size of
+the array is a multiple of max_connections, and limits the total size of
+transactions.

I think you should elaborate on "limits the total size of transactions."

+The meta-data for all undo logs is written to disk at every
+checkpoint.  It is stored in files under PGDATA/pg_undo/, using the

Even unlogged and temporary undo logs?

+level of the relation being modified and the current value of the GUC

Suggest: the corresponding relation

+suitable undo log must be either found or created.  The system should
+stabilize on one undo log per active writing backend (or more if
+different tablespaces are persistence levels are used).

Won't edge effects drive the number up considerably?

+and they cannot be accessed by other backend including undo workers.

Grammar. Also, begs the question "so how does this work if the undo
workers are frozen out?"

+Responsibility for WAL-logging the contents of the undo log lies with
+client code (ie zheap).  While undolog.c WAL-logs all meta-data

Another zheap reference.

+hard coded to use md.c unconditionally, PostgreSQL 12 routes IO for the undo

Suggest I/O rather than IO.

I'll see if I can find time to actually review some of this code at
some point. Regarding 0006, I can't help but notice that it is
completely devoid of documentation and README updates, which will not
do. Regarding 0007, that's an impressively small patch.

...Robert

#3Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Thomas Munro (#1)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Fix-shared-memory-size-for-rollback-hash-table.patchapplication/octet-stream; name=0001-Fix-shared-memory-size-for-rollback-hash-table.patchDownload
From a7251157d42a81f5e92a4594bcd43081888356a1 Mon Sep 17 00:00:00 2001
From: Kuntal Ghosh <kuntal.ghosh@enterprisedb.com>
Date: Mon, 5 Nov 2018 16:31:38 +0530
Subject: [PATCH] Fix shared memory size for rollback hash table

While creating shared memory segment for rollback hash table, we should
set the size of the segment correctly.
---
 src/backend/access/undo/undoaction.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
index bdf9d1e013..3cb0b8216f 100644
--- a/src/backend/access/undo/undoaction.c
+++ b/src/backend/access/undo/undoaction.c
@@ -625,7 +625,7 @@ execute_undo_actions_page(List *luinfo, UndoRecPtr urec_ptr, Oid reloid,
 int
 RollbackHTSize(void)
 {
-	return ROLLBACK_HT_SIZE * sizeof(RollbackHashEntry);
+	return hash_estimate_size(ROLLBACK_HT_SIZE, sizeof(RollbackHashEntry));
 }
 
 /*
@@ -635,7 +635,6 @@ RollbackHTSize(void)
 void
 InitRollbackHashTable(void)
 {
-	int ht_size = RollbackHTSize();
 	HASHCTL info;
 	MemSet(&info, 0, sizeof(info));
 
@@ -644,8 +643,8 @@ InitRollbackHashTable(void)
 	info.hash = tag_hash;
 
 	RollbackHT = ShmemInitHash("Undo actions Lookup Table",
-								ht_size, ht_size, &info,
-								HASH_ELEM | HASH_FUNCTION);
+								ROLLBACK_HT_SIZE, ROLLBACK_HT_SIZE, &info,
+								HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
 }
 
 /*
-- 
2.17.1

#4Dilip Kumar
dilipbalaut@gmail.com
In reply to: Kuntal Ghosh (#3)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Nov 5, 2018 at 5:13 PM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

Hello Thomas,

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

I have included your fix in the latest version of the undo-worker patch[1]/messages/by-id/CAFiTN-sYQ8r8ANjWFYkXVfNxgXyLRfvbX9Ee4SxO9ns-OBBgVA@mail.gmail.com

[1]: /messages/by-id/CAFiTN-sYQ8r8ANjWFYkXVfNxgXyLRfvbX9Ee4SxO9ns-OBBgVA@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#5Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Kuntal Ghosh (#3)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Nov 6, 2018 at 12:42 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

Thanks Kuntal.

--
Thomas Munro
http://www.enterprisedb.com

#6Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Thomas Munro (#5)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Nov 8, 2018 at 4:03 AM Thomas Munro <thomas.munro@enterprisedb.com> wrote:

On Tue, Nov 6, 2018 at 12:42 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

Thanks Kuntal.

Thanks for the patch,

Unfortunately, cfbot complains about these patches and can't apply them for
some reason, so I did this manually to check it out. All of them (including the
fix from Kuntal) were applied without conflicts, but compilation stopped here

undoinsert.c: In function ‘UndoRecordAllocateMulti’:
undoinsert.c:547:18: error: ‘urec’ may be used uninitialized in this
function [-Werror=maybe-uninitialized]
urec->uur_info = 0; /* force recomputation of info bits */
~~~~~~~~~~~~~~~^~~

Could you please post a fixed version of the patch?

#7Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Dmitry Dolgov (#6)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Dec 1, 2018 at 5:12 AM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Thu, Nov 8, 2018 at 4:03 AM Thomas Munro <thomas.munro@enterprisedb.com> wrote:
On Tue, Nov 6, 2018 at 12:42 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

Thanks Kuntal.

Thanks for the patch,

Unfortunately, cfbot complains about these patches and can't apply them for
some reason, so I did this manually to check it out. All of them (including the
fix from Kuntal) were applied without conflicts, but compilation stopped here

undoinsert.c: In function ‘UndoRecordAllocateMulti’:
undoinsert.c:547:18: error: ‘urec’ may be used uninitialized in this
function [-Werror=maybe-uninitialized]
urec->uur_info = 0; /* force recomputation of info bits */
~~~~~~~~~~~~~~~^~~

Could you please post a fixed version of the patch?

Sorry for my silence... I got stuck on a design problem with the lower
level undo log management code that I'm now close to having figured
out. I'll have a new patch soon.

--
Thomas Munro
http://www.enterprisedb.com

#8Andres Freund
andres@anarazel.de
In reply to: Thomas Munro (#7)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2018-12-03 18:43:04 +1300, Thomas Munro wrote:

On Sat, Dec 1, 2018 at 5:12 AM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Thu, Nov 8, 2018 at 4:03 AM Thomas Munro <thomas.munro@enterprisedb.com> wrote:
On Tue, Nov 6, 2018 at 12:42 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

On Thu, Nov 1, 2018 at 8:53 AM Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It passes make check on Unix and Windows, though currently it's
failing some of the TAP tests for reasons I'm looking into (possibly
due to bugs in the lower level patches, not sure).

I looked into the regression failures when the tap-tests are enabled.
It seems that we're not estimating and allocating the shared memory
for rollback-hash tables correctly. I've added a patch to fix the
same.

Thanks Kuntal.

Thanks for the patch,

Unfortunately, cfbot complains about these patches and can't apply them for
some reason, so I did this manually to check it out. All of them (including the
fix from Kuntal) were applied without conflicts, but compilation stopped here

undoinsert.c: In function ‘UndoRecordAllocateMulti’:
undoinsert.c:547:18: error: ‘urec’ may be used uninitialized in this
function [-Werror=maybe-uninitialized]
urec->uur_info = 0; /* force recomputation of info bits */
~~~~~~~~~~~~~~~^~~

Could you please post a fixed version of the patch?

Sorry for my silence... I got stuck on a design problem with the lower
level undo log management code that I'm now close to having figured
out. I'll have a new patch soon.

Given this patch has been in waiting for author for ~two months, I'm
unfortunately going to have to mark it as returned with feedback. Please
resubmit once refreshed.

Greetings,

Andres Freund

#9Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#8)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, Feb 3, 2019 at 11:09 PM Andres Freund <andres@anarazel.de> wrote:

On 2018-12-03 18:43:04 +1300, Thomas Munro wrote:

Sorry for my silence... I got stuck on a design problem with the lower
level undo log management code that I'm now close to having figured
out. I'll have a new patch soon.

Hello all,

Here's a new WIP version of this patch set. It builds on a fairly
deep stack of patches being developed by several people. As mentioned
before, it's a useful crash-test dummy for a whole stack of technology
we're working on, but it's also aiming to solve a real problem.

It currently fails in one regression test for a well understood
reason, fix on the way (see end), and there are some other stability
problems being worked on.

Here's a quick tour of the observable behaviour, having installed the
pg_buffercache and test_undorecord extensions:

==================

postgres=# begin;
BEGIN
postgres=# create table foo ();
CREATE TABLE

Check if our transaction has generated undo data:

postgres=# select logno, discard, insert, xid, pid from pg_stat_undo_logs ;
logno | discard | insert | xid | pid
-------+------------------+------------------+-----+-------
0 | 0000000000002CD9 | 0000000000002D1A | 476 | 39169
(1 row)

Here, we see that undo log number 0 has some undo data because discard
< insert. We can find out what it says:

postgres=# call dump_undo_records(0);
NOTICE: 0000000000002CD9: Storage: CREATE dbid=12916, tsid=1663,
relfile=16386; xid=476, next xact=0
CALL

The undo record shown there lives in shared buffers, and we can see
that it's in there with pg_buffercache (the new column smgrid 1 means
undo data; 0 is regular relation data):

postgres=# select bufferid, smgrid, relfilenode, relblocknumber,
isdirty, usagecount from pg_buffercache where smgrid = 1;
bufferid | smgrid | relfilenode | relblocknumber | isdirty | usagecount
----------+--------+-------------+----------------+---------+------------
3 | 1 | 0 | 1 | t | 5
(1 row)

Even though that's just a dirty page in shared buffers, if we crash
now and recover, it'll be recreated by a new WAL record that was
flushed *before* creating the relation file. We can see that with
pg_waldump:

rmgr: Storage ... PRECREATE base/12916/16384, blkref #0: smgr 1 rel
1663/0/0 blk 1 FPW
rmgr: Storage ... CREATE base/12916/16384

The PRECREATE record dirtied block 1 of undo log 0. In this case it
happened to include a FPW of the undo log page too, following the
usual rules. FPWs are rare for undo pages because of the
REGBUF_WILL_INIT optimisation that applies to the zeroed out pages
(which is most undo pages, due to the append-mostly access pattern).

Finally, we if commit we see the undo data is discarded by a
background worker, and if we roll back explicitly or crash and run
recovery, the file is unlinked. Here's an example of the crash case:

postgres=# begin;
BEGIN
postgres=# create table foo ();
CREATE TABLE
postgres=# select relfilenode from pg_class where relname = 'foo';
relfilenode
-------------
16395
(1 row)

postgres=# select pg_backend_pid();
pg_backend_pid
----------------
39169
(1 row)

$ kill -9 39169

... server restarts, recovers ...

$ ls pgdata/base/12916/16395
pgdata/base/12916/16395

It's still there, though it's been truncated by an undo worker (see
end of email). And finally, after the next checkpoint:

$ ls pgdata/base/12916/16395
ls: pgdata/base/12916/16395: No such file or directory

That's the end of the quick tour.

Most of these patches should probably be discussed in other threads,
but I'm posting a snapshot of the full stack here anyway. Here's a
patch-by-patch summary:

=== 0001 "Refactor the fsync mechanism to support future SMGR
implementations." ===

The 0001 patch has its own CF thread
https://commitfest.postgresql.org/22/1829/ and is from Shawn Debnath
(based on earlier work by me), but I'm including a copy here for
convenience/cfbot.

=== 0002 "Add SmgrId to smgropen() and BufferTag." ===

This is new, and is based on the discussion from another recent
thread[1]/messages/by-id/CA+hUKG+DE0mmiBZMtZyvwWtgv1sZCniSVhXYsXkvJ_Wo+83vvw@mail.gmail.com about how we should identify buffers belonging to different
storage managers. In earlier versions of the patch-set I had used a
special reserved DB OID for undo data. Tom Lane didn't like that idea
much, and Anton Shyrabokau (via Shawn Debnath) suggested making
ForkNumber narrower so we can add a new field to BufferTag, and Andres
Freund +1'd my proposal to add the extra value as a parameter to
smgropen(). So, here is a patch that tries those ideas.

Another way to do this would be to widen RelFileNode instead, to avoid
having to pass around the SMGR ID separately in various places.
Looking at the number of places that have to chance, you can probably
see why we wanted to use a magic DB OID instead, and I'm not entirely
convinced that it wasn't better that way, or that I've found all the
places that need to carry an smgrid alongside a RelFileNode.

Archeological note: smgropen() was like that ~15 years ago before
commit 87bd9563, but buffer tags didn't include the SMGR ID.

I decided to call md.c's ID "SMGR_RELATION", describing what it really
holds -- regular relations -- rather than perpetuating the doubly
anachronistic "magnetic disk" name.

While here, I resurrected the ancient notion of a per-SMGR 'open'
routine, so that a small amount of md.c-specific stuff could be kicked
out of smgr.c and future implementations can do their own thing here
too.

While doing that work I realised that at least pg_rewind needs to
learn about how different storage managers map blocks to files, so
that's a new TODO item requiring more thought. I wonder what other
places know how to map { RelFileNode, ForkNumber, BlockNumber } to a
path + offset, and I wonder what to think about the fact that some of
them may be non-backend code...

=== 0003 "Add undo log manager." ===

This and the next couple of patches live in CF thread
https://commitfest.postgresql.org/22/1649/ but here's a much newer
snapshot that hasn't been posted there yet.

Manages a set of undo logs in shared memory, manages undo segment
files, tracks discard, insert, end pointers visible in
pg_stat_undo_logs. With this patch you can allocate and discard space
in undo logs using the UndoRecPtr type to refer to addresses, but
there is no access to the data yet. Improvements since the last
version are not requiring DSM segments, proper FPW support and reduced
WAL traffic. Previously there were extra per-xact and per-checkpoint
records requiring retry-loops in code that inserted undo data.

=== 0004 "Provide access to undo log data via the buffer manager." ===

Provide SMGR_UNDO. While the 0003 patch deals with allocating and
discarding undo address space and makes sure that backing files exist,
this patch lets you read and write buffered data in them.

=== 0005 "Allow WAL record data on first modification after a checkpoint." ===

Provide a way for data to be attached to a WAL-registered block that
is only included if this turns out to be the first WAL record that
touches the block after a checkpoint. This is a bit like FPW images,
except that it's arbitrary extra data and happens even if FPW is off.
This is used to capture a copy of the (tiny) undo log meta-data
(primary the insertion pointer) to fix a consistency problem when
recovering from an online checkpoint.

=== 0006 + 0007 "Provide interfaces to store and fetch undo records." ===

This is a snapshot of work by my colleagues Dilip, Rafia and others
based on earlier prototyping by Robert. While the earlier patches
give you buffered binary undo data, this patch introduces the concept
of high level undo records that can be inserted, and read back given
an UndoRecPtr. This is a version presented on another thread already;
here it's lightly changed due to rebasing by me.

Undo-aware modules should design a set of undo record types, and
insert exactly the same ones at do and undo time.

The 0007 patch is fixups from me to bring that code into line with
changes to the lower level patches. Future versions will be squashed
and tidied up; still working on that.

=== 0008 + 0009 "Undo worker and transaction rollback" ===

This has a CF thread at https://commitfest.postgresql.org/22/1828/ and
again this is a snapshot of work from Dilip, Rafia and others, with a
fixup from me. Still working on coordinating that for the next
version.

This provides a way for RMGR modules to register a callback function
that will receive all the undo records they inserted during a given
[sub]transaction if it rolls back. It also provides a system of
background workers that can execute those undo records in case the
rollback happens after crash recovery, or in case the work can be
usefully pushed into the background during a regular online rollback.
This is a complex topic and I'm not attempting to explain it here.

There are a few known problems with this and Dilip is working on a
more sophisticated worker management system, but I'll let him write
about that, over in that other thread.

I think it'd probably be a good idea to split this patch into two or
three; the RMGR undo support, the xact.c integration and the worker
machinery. But maybe that's just me.

Archeological note: XXXX_undo() callback functions registered via
rmgrlist.h a bit like this originally appeared in the work by Vadim
Mikheev (author of WAL) in commit b58c0411bad4, but that was
apparently never completed once people figured out that you can make a
force, steal, redo, no-undo database work (curiously I saw a slide
from a university lecture somewhere saying that would be impossible).
The stub functions were removed from the tree in 4c8495a1. Our new
work differs from Vadim's original vision by putting undo data in a
separate place from the WAL, and accessing it via shared buffers. I
guess that might be because Vadim planned to use undo for rollback
only, not for MVCC (but I might be wrong about that). That difference
might explains why eg Vadim's function heap_undo() took an XLogRecord,
whereas our proposal takes a different type. Our proposal also passes
more than one records at a time to the undo handler; in future this
will allow us to collect up all undo records relating to a page of
(eg) zheap, and process them together for mechanical sympathy.

=== 0010 "Add developer documentation for the undo log storage subsystem." ===

Updated based on Robert's review up-thread. No coverage of background
workers yet -- that is under development.

=== 0011 "Add user-facing documentation for undo logs." ===

Updated based on Robert's review up-thread.

=== 0012 "Add test_undorecord test module." ===

Provides quick and dirty dump_undo_records() procedure for testing.

=== 0013 "Use undo-based rollback to clean up files on abort." ===

Finally, this is the actual feature that this CF item is about. The
main improvement here is that the previous version unlinked files
immediately when executing undo actions, which broke the protocol
established by commit 6cc4451b, namely that you can't reuse a
relfilenode until after the next checkpoint, and the existence of an
(empty) first relation segment in the filesystem is the only thing
preventing that. That is fixed in this version (but see problem 2
below).

Known problems:

1. A couple of tests fail with "ERROR: buffer is pinned in
InvalidateBuffer". That's because ROLLBACK TO SAVEPOINT is executing
the undo actions that drop the buffers for a newly created table
before the subtransaction has been cleaned up. Amit is working on a
solution to that. More soon.

2. The are two levels of deferment of file unlinking in current
PostgreSQL. First, when you create a new relation, it is pushed on
pendingDeletes; this patch-set replaces that in-memory list with
persistent undo records as discussed. There is a second level of
deferment: we unlink all the segments of the file except the first
one, which we truncate, and then finally the zero-length file is
unlinked after the next checkpoint; this is an important part of
PostgreSQL's protocol for not reusing relfilenodes too soon. That
means that there is still a very narrow window after the checkpoint is
logged but before we've unlinked that file where you could still crash
and leak a zero-length file. I've thought about a couple of solutions
to close that window, including a file renaming scheme where .zombie
files get cleaned up on crash, but that seemed like something that
could be improved later.

There is something else that goes wrong under parallel make check,
which I must have introduced recently but haven't tracked down yet. I
wanted to post a snapshot version for discussion anyway. More soon.

This code is available at https://github.com/EnterpriseDB/zheap/tree/undo.

[1]: /messages/by-id/CA+hUKG+DE0mmiBZMtZyvwWtgv1sZCniSVhXYsXkvJ_Wo+83vvw@mail.gmail.com

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-smgr-v2.tgzapplication/x-gzip; name=undo-smgr-v2.tgzDownload
�T��\��iWY�(�_�_q�}�)YH���Uc'�h������R� ��B!��~����L1�0��}^��D�8�>{��0�d���������������-E?���z�����������66�zoc{c����4��2[)Le����=���x|�{Y�������=�x�s���"I;���3��f��4��8�v�x9�'��3^.�i��|�������M�����f���7�z��>�����Z�����P��%����M�����[p���`�w���{���8�����8�z����z���e4W������S}�:v�R]��3�&�����c��L��Lf�Q2���&XD/��(l���z
����������Tw�~��{4Z�T;?�:���Zo�W��RX*Ke�R-%`�,�e]���$�F�E���Y����g*�-�Q<�G*�y���,
�p��>�	�#5
f�#�T0If�Y���;���E}��p9�{O��'1�'Q���"�������q���2�~a�E�����z�;�9�%ew�"��i��([��k�k��EY����a��'����3\<�y
_�_0��� ���,���x��&��9��7��9q	�^��Y���L��[�N6����$��9����Fu�r��e�/F7���<������pzSXhH�"��l��q<*n9,���>���N�jL&�v���n�AC�p�%-x�wf�X`B]�Bn:��`B��l	�����atM3��38�����o��F}��h-�|�B\|2��]81��h�W@�
��O-�0 ��Q),�N����
��!j����2�x2��������ip�<a�8�K��/�Fi����#��������6�@�������Vm��u��?�7q������`���$�}j��Kng�_�7	@!�q���~A��`.�M��L����zfxm���-��Q4�`����"�����J28X�LY/�l/9NY�������&H	�nx~�pD���p>#Z ��s�����x�X
���\$�8������x�<i�����	w�����o�����.3�g�c��W��4:���85���i�����$���6j2�z�T�~}���(��^����h	�=����,����^E@}Q�6];<8���W�������h��b�OW�f�7�7�����������?��������������{Me�w:�����^g8��`�f
n�,�k��d�-���R��Z�6�������O�k���j�fS��fk�P�����;�����,��R���f������O�����+���f��C�n�����w7�U��������L�)��r\�J���=��{���oo��4����x��!��#�8����i"����6���nL�]��<����=�^�{�|Z�|���� #�#h�������&�=�V�b�L�s�w	���	������]@�Y��h5�jwgP�$�'�f]�����T�����q_18T~S��+76�����:�v���1 �����p�v��������v����q��v{;����xD�29D���\�^y�?�Yu�v���������5[�,���xDr�6� �����m�����|�oZ�7��O�q���}2��U����_������g�FN+���o��v���nwkcws���\��iIo����S�Z�S������	��x�`T��B,����I���Em���w����������p���3�}����>"n�G���GWQ�*��L����<�U�c(�+���e<�����^�4���|I+����jZ�s�V�. -�����k|N��B��U�d��st�@�K�D���su�}@�qn�0}�w��v�N��F���x,��0O��_�@��h��m��o3Oz�������0G��� A`E=�B;���zL��/����.���rt����������H3'�r<^�^������W�������=:$�r��9r�
b���:�u���_�9S�aT�4!� X��AR
�" ����
����P��I���@�B��,Rp�(�PR�%���=��Q����`Cq���b�[�[w����+�6h��B���A.bN��(N�N@X%i�����h
��x>2[�V�m���%��}��!h��wi���V������H�v	����4�D�l9W���-\J����fn+���^������?�C-��i�N#��J�p$��(�9����� �Q�5/��_�(2|������Q��Jm"�[�	��0�������_y;"Q�D�6O�&���������i����|{��vx���>��Ga���ac�0j��T�A��Fm5L�����*$����&i/�hI7B0	�M7}��������N��Pp���P�GZM������4o������3���������|�Z!�6�����U����R�H��(�L��"�����ex���kW���G���6kC1*c���`?�+tC��f8y����i��kd��B� ~|��y4��j���xt������(@mL:
���$m0�7�Lx�=��Z����,���iJ��$�Q�h���D�����&�?k|�@��O�A!N>������g=�Td��i|}��f����I5@�s/�Fn�[_��XO��L��%��������E
��]{��J4Y����N��8��m�6���]����E��p���eu�E�Y�����
��yD��-��
�����-���p^�5�(���.�g$���4�5�t��S|������d�F'�Eb�0��`�>8F�	h�@��T�4�0��9�����Lx�}bR��Pw@���"��W	|.�L��LR��' ����c��8�t�����;r^�/&�$@�����'��$��u�X�K_�����aH-��.�h����t-��> V#���������;�mNY�1��,At�)S������F�4it8�@����V��.S4 M������x�3`�3�~�x��I&Pr�����J��:�������F�� H�Y�"dbp�����IA���sF�M4�Z�!k�*v��q?��3����88�4���<���y�7�aw�~��r����I����7i�G�9�y��g�;ORn�%����P?���N����x\V���, ����G]h�LF��<C�
\'x&�	�d"P���$��OP�A�E�������s����3�����������5��h��]'��G2(����������G���7������������g
��7M�h�_�#L�7��>�����|8���>�rp����>���so�����^�k���O?�M�����<�Z���x��(����j�#��}�1kj
����~cG�|N��xfecb��D�%Bm�5�`D
��8���>fI:,�|��e���b�u������s��I���������������h�Y���d��8�k�6$��j,]#��	�&�q����0�o�x���!_����������9��������>�����i\FC$��������+�360e�(eW���[�&Q4o��_/�>�p����{��2/�'���[�G�U8�m�S�0D�������n��������<w&�l���\�A�_;��%|�\^��N���65u�#�J��h2ab@������ua�M���=���a�����q����EU��	��C��@5gd�F]xW�4�����]jx�����N��
|������)�H�@z�.0M�����)U�5	�l2�+�e(�Q��pm��������}�7��J>�5�2a@��
�U��e*�It���
d2�8���Xo���m���/.��'��(����-�/�$��_^���>]�+��Ic�����5M�Z�_q'5��M�4��y#S�wo����s�p�M9c	m���)�������.�q�%\O��|?�V�ex]��KM��;T���j���������������s@�hQ����4����G�/'��}0��E�7t$w
�[P��|HX����%���6O�w������)����0��H������y��Xpb�Q,��&��^���t�}�����F����;�t�Ca%b�	�)�Q����S^��O��]7�>�z���3���3Jk6�L���{�i�����+�����%�������!���O��{��W�SF�y�V	���B����''�"s����&m��&"VxT=)U;�x(�k�
�w��)�>�����zj�j�~R���A�H��i��������z����m������tuW.�|���YSHb�<�*-��F�B���!�j�������S��+�[�����E�5�!@�h;
������Q8��jeV������7������l9VIT�+���M�!��v����qB�?
�I���cu�'�2LP��������%���R����1s�x>R���AB�
���P�����C'�
����Y����d��~z����^�G�k�]Hv6��xcwk����p�1�[�z������$e��.a���-��Z��Y��!��?��j��.�|����EQ�����t�������������������^x����������n�Z2��)O;f��!��	��<yRo�N�������z���y�!�p��vF����������W��l:������^���3������OA�����5���a�y�D0�'��n
��E:X����,s�{�����#-���v����=��"���,B���8����9o[�S���:v�3*���w�6���
G����4�6sRch��[�n�"�V����NO�O����B<��o���,�:Ff��{�������x��S��.��"/~�@d�0�0�*��
�H��1*�c������
f�tf������]^`D�v��_����*X9�=23��ww�.�]rr����v��8��v��#�6��9�:x}pyT�M����I�U�]���k�ux��y
"������%�X$�	���L�	��t������U��cme@��"���r���s-:��	C.�p��I�@I�:#������d����R�o�o�XF(���0B�,�?	��~�
�.�Z�IVZPH����S�O�YQ��9^�x�*��ti��������2���8
f��v�n���,��u��O7������<~}���������7�(M���P�^����G�,����o�)������%�"P�E� ����(� �8+D^�OQ��4��Sc|�����#�Pk~;s�Q�$��C�����x!*������;��u�Q|s����_"���Xbt����B��,�I5�G��P��O��dD%{���)�ll�ZmP���dF��Uc��c�6���{���E�(��-�y1
q����I
��Y�~9�a�5NR�7�h]�HT��.�B�V��E'TG��A�C���1V�g��%�i���c��V9�n�
���-U�W�s|���(:���qf?S��O�r,����e@H4_^1��#GrN�9�o�)�����
t���g���+�<G�v<4?����Tc�����`T��d&����4�+ ��,����%�������03�J���3}D��������b$(d9����K�FC���'M�X�4���$�n~q{��#!������{������>rx\@��gS`��
�p�?G�y�[������U����1�k>�����$u?J�l�,����)���8���F���`LZ���.:�/P�{����X?��,����.�j}��:�Y?Vm�I���r��Y��3���>F5�"��
e�ehB���s�&�����u@�y��8qjBT#sy���LS���&`��%���,O�7�L!�������#����7��$�`\�_`�qd�s�h���mux7�D��T8����;���#3�������@����Z|c�pC+��q�-~h��W��6�FzF��
�S��&�c�0�Y��4���.�1�A������	l�)v�=�6��V�����@\����UK��r��El(g��.�U���U
Gy����pl74��U�3|�i�8�-�����BL����
#�c����T<������6�����0s�@W�NTh�� j�s�����B��u����D��c������U9^����(�
�Jz�E({������6��������k4(s���r9����*�q�����bB��������q�����%I��'�W5z�W�������[xOo�������d���J,�����+��
.���H&�I�cR�9��w��;E��]9�����;~��%�����A��S�9w�������b�
z
>f�%|s��|[^�}�-��Qly�����wa�;�����!F�5���=^����<�?#����<���b�%E��n#�{b������{@X�e������U2�0q�<����/���3�������%����|8�"r��D�@����5Z�8�r�L8T=F>�q���W��
���r��9�n��9�zh?��q9��� %�r�%��Az���]j�����UCS?�N����X�
��H�k?\�|x�����������/��$�mo�����N�����B���<��tp�<��(�P����j��z���y82&g��/3��_�-F���@*)�>�/�����x!�T��
~�01�M��'�R2�p3�b���d	2���=;W-N�v���:?8=>�J'pgKLd3��:s��M)M�U���?E��w��@!�z�����grx����;G7Rs������Y�Q�����k����D/�(�������5��8��F���#�_� �L�J7tH_�)n�q)W���fD�����Nk���6�	�5��R����b�=�K;8:9z�~��>9{}�96
���;�<�[�
l�cF��]��K��RLO���;��"�d��Q�jMX(���Ca���;��kzc��]B�GjF����^F�����B�O?���@ruX�S0e&��H�,���El`����h�m$-�C{��1��^O3�1f�c��~-���#�E��������F��Na������x	����P���x����_���tP��4�
�ed����j���	����d����+,[�Y�H�����6�@�5w!w�a��}���1���I����o�6��q��V(k|
�d��-U�����L=�.D"EH�?.��%[�ntO�0���W{��Ls���E~}H��[��z��0��r�������	�P��l�=`_��;�6������+R�Ro�Di���<6�xP������M�\j��/,5����y�!����#��Cj�9c�Z_>A-��I�����
����l�G��K�9X����3����~x�����zp�p�-R��E-��I��l9����(	(�k�#BsE�u(��h?*&�P0L������V�1!h��V�W����'u��������U��p�m�R����S.N�O�����&y�n��	��@���D��u����`��f���E���?d�������Y��������B��h������D���!��Z3�*�2��U��Bb8���mD���6�|����&�X�����gq���`��,��
�z��hskHC�#@|e7�L���w�
����}�K9x�Z�d���yS�:������lo�n�a~��%�(��!r��:���7L�������d�q��;34nH%|��L� 22����������=�j����*���H�V^b����`��C�Q+'��p�S�[��J�����b�sQ��7�
tD��P�V��_��E^}�T7�1�d���z��[���3��an��T��k%]':�IX9���C�*��3_��L|�H�����%�;��loF��#t���|�����dL��(�
w��G����'8�\8����ExS�L�'��2EW)�Xp�����+Q�b�R��������;`�(0wY{:}F8��m/����`g9�����fltB�<?Q���Io����6�,�Hlj�<1E�F��Y�r�j����f 0,:-"��l�!�-o;o�hgnN}!&81�sWx|4�Cs���A��5�����gl��
���l�g�d#v|����R�W���F2��#9_0m��]'2�E��)R�c���JO���E�(�L���;g�N1s;Fo�
�9o�
nE�h�?!+�t�Vfm��� ��d&��BH7���j���2�b���ZS���@�'�"�px���y��G�A�}Cs��z�����S����d�@����^C�
��Y�/`%j�yxs:������3sMRl��f'�F��\�`#�����r�;��b�����Booh��-)���$�!�m��=��F�d��F��(G�YJ�M����V��,�^���/��vk&ZG8$�,�0+�
AMU��3�d5C7�)t3���r��I�pzz�<(g$��p6�;+�#f`���*B�)�
�p��4�=�����9�Eph���dJ5�~/��e^{��&I��1�`R�� v���v1�M���z8!�Q���zp

aN��i��qb�-��9[|_oS�;�tz����������������\f�����M	�H�����x���`R�S�|7w��XxR�a�<_S��$A���p�8q�k�p���p�)�A�o�+1.m9s�J�L�	������1���|O�UB�O�'���E'����U���S �rF�bF������	5Asl�&X�3�����z���s�Rj;>X�:L�dO�k����`v�,�#&wq�\.�C��Od$@��CS�V����fc�3*��(����P7[?��>��hE<M�2t$2��n��Y]r�?�dC.vp�&��3��c@v���R���F�8�h���@�
���L��!�6����_H��Qg����8�+QC���j�e��(��K�s���
��\�s�	GI�A���F�	���t��,CGR��;����s�����2G��e���@Dp�N�INw)%�b��FW����8�������!m����S�T'��r��\�<�������E'YL �]��A�q:�[1����BU��S�7�z��u���r7�c�x!�bW�*M8�V��J7���x�����\�N���H��oL���%� 2��][$"���)^�h5=��u�Z��L:���aL�U��9^����>H��Q[��,:��hS��I�V��TB����(4�,\��2%J�_"�/�gb�'��$�G��<?VwX�)��%���g�7m�6������ �{����$�;]�t�i�����c�-��	g�]�}��k�a�n�4��RfN�v��������4��L�A����8"E�M�����S����6���9�
�.���^D��'�+gD�����������
��i9*"�vz��F<�9
��������:5�L��f.�@��0>EM�x�H��q�"��*��P0p:*�x���\���!�����x�JH�+=P���/�?
��	dh�"�������PuQ*j��>�7�+�g9-�#2�(��0�#Ku�kH�P����S�{NG�������V�w�eJM���Y��9�0t��#Gu�=�OpD���q������CjBN��RvK6�����?p��V�Q���Q��L�c�2;���5M�\n��������
�����d*#��|M��"�3�l<d�I��4u�^0��\&3���h6������Y��U�L�#�?����+�R��(�@��g�o4}�C�g����4�"��8K��i������^/�������C4���	��/�)&�D[-�����\��+;����6�l���U",���2�ZE!���N��*yg���p�(�F*�����j�#"���
a����(�P4�k�K�t����X?Z�: ��Ad�J��#s���*�T�"�1fY�gN�-�5)�QR������h@eO�N��D�c���^.���-GH�BE�L�l�A��%�6` i�8�z�~����Z���7"Kt�Cxbfs���}E(f[5��,y!���!9Y�JL����;t�[���X?<C�We0�l��H[�>}A.�?y/��Z�����i�F���,!g�&w���n'X��h�d8yT���baG�9�+�4X�0��I��f�R������:AS�Q�u��^'	" ��<�����v�1����)�#��C�4y���F�zE�#�{�p�J��M����*�i�	��%B�H�7��������Bs��4O`x�$�6�E�������;���}M�_��
��8��pq:@N�o�[u������W����Gt�<�pq��a�~N[���r�T���zD4}��^w~�m�<�����jp���SNu0�8v��.j�"�����'��K�����d��u�����+b��~Y/^c��?���vW�k�t>����/�.�m�#p�����F������1���\�*60fR���~���nU��7�Ir=pl�vzd�|s�����<-b����z����Q������W�n��4�F�?�5{��t]E��n�'�m�MPk���@l��/�����9J&���E��d��h�k����f�,"0�K�0k��xI��s����;������v���(�M�j���������}�c�rOm��� ��H�D�
G�!Q��KBR �&&���|>A���$�*5_y����NF��O8��Ll�;�|O�/�n_ 7g���X�8��/���Vl����l�*rm��i7���O��7��8�����4Ab�[��c~��@K;��0�.���G�mej�%��o�"�����j:@�4�>��d@N��Z,�f��9���w��
{�m��'(S�hTg���
�w���6�v���`D��'�p�6�
3���J����L��$���?a�q����+zy
�w��a���_��{�C�k���`6��D�Gv����id�U���=F����dZl���I*"-l#�U�ro�S����@>v2�<A�knm����X�[���
!#I�|�#�>�@k(�QR�~��
�,��l��l3���%����rE������l��2�h�0�c�(���G-),���X���9�8�� ���w�+2e�y�t$��C%$bG#���+����+v��i\��{�tt�G�8�_����8�Shc�+�8�X=hD��*g�j���e�.�'^�?��Q�D�ve�+\>�d����?3���m����L(E3�\�5��IH�5�O1O��;����X�Cp���,������)��j-:}������k���j�W�c��9�-�LE�/'����$�_�iT�	���ih�	���
)���|�`S�$����#�N|�;�er�?;���|I���{y�\Ll����%����P�'�.�z�"Z��T'������l.����`��5���#�*������"�%H�{j-4#�W�K�h%�l+$I��:n�Iv��a4'7M��P���IQdD��%xM;��~��/<�N^"��(�Q�J;���G�Iq�>����9�\&����b
��;�k��|�u[���u�u������&��S�U�N���z,�=����!�s�By����#d?y��a��V�~����n�����T�U��_���E\%)�������[:��h�^Z�"���@W�(�`!����X,�Gp<(	�a���B5l�Ho����ZMTlZ���	��S1�WR1Af�||tqpq��_u�K�-�e�����F.|����E1����;~��&����zfJ����_�j������[-~n����b��S`�\���V��!������yF�fTo���q|N}O�f/�G���J��bV��$�a4Z.0��u�GY�-�H���";���s')�q t�9�RP�x�pL/�4��1S��h�"�!��!P���l#����L��>CY���c/f�4W���>c�:F�\
�� 9]�������8�>�H?�F���~�m�Z��S��D�D��Xm����L�7��9f�����82�N|������h�$L�&��	��t7l�I���TsT[��[l�d%����L��B"O)nuf:;)�i��W��.�v\	��vV�K�o�������FQ�FI�F��y|���65�����R��cE:/������m����+�z&U$���d��+��%^`4�(�)%��V\1=����]&��qJ��.S�O��}���I\��?n��mfv3���>r�v���y�"bEr�B�����E��6�v��=�����py
��1���TZ^*?�������F�{|z|E����w
�X���Oh����g)F74�/���D�>�:��C�����L�)<�}I�Oy��P���)�csen���J+�]"gkw_�2�����pQ2�L������c���D6H�t6;B��b�i�q�����=�B��}���(Y����
��E��W^*�7�:I)x���*���)���<�i����vkn��N~���
�:�V!G�"��D�9S$��
5'�X��;n�T��������N�%f/���(�Pty�Z9�@����%l�,��g*��n�CA�:��%��%�����4����r��z�wO]�:���
.���a%J ����<4�PH�
�r��r�VmXU��G`fFVz��wG���k�����w�-���R�B�^���~q:�*�K�f�x:7B��!�
�<�7�<bTu+N��r��?}���B�`(�&Q���;I�7��\�)��O� �_������P��<�Z���[��K�����O�dX���U�����y4gd@ Lf��iN
�)��)�T���D5�&=�"��d����h5[,��<;�C>��89��Q���|+��R�'�3{���q�*�,� ��`���m�A�|��;��lp���"�l&k�������'�I���l���1 r�����g���� ���ic�C��n;1��5v����)�1�!B��_7 K�����j+:O_"��|.-�fdQJ
M��������|�o�cc��b�F~-��ft{�L�\-�UUy!g:'aK%�Y�y���T��I���T��lY����s�	�)��d�?��I/����]�
��P�� pB�Q����"����S�3��F��z��SW#����bqG]�F���H�d2x�����&��LP�D�6^H��,�>�TG��E��c���S��"�'�#vY��w-�QS����g`s��r���=$0�U��uYG�^�a�I!�F7���sY�B H�B��U��J�����z�����d/}{|������F?�Z�2��G����\x^�N�G$�I��t������{���:�����[����^�sW�[+�+Hdp��$I
�d��|���q�Av���%"�>��)6C��I%���D��i���4����5�}�����}��`���sF���u�[%T��`1���V����W���$�3�_y��M~����+�T*�I�cQ	&L�6��2	��Y�jX:�!�>�Mgc%�O}v~E�l5�2�c�?#~��k��������Y_����������@�����E���7�C�q��[�������`?R[A6��*0k����VP����}���M{��hJ}�9KC�������T��j����KZ�?���@�'�I&!���_:��J�$�M���"��&:&�������
&@�3��������.��A�
?!�31<����_�o��@���lx���
���P=�t�i���d�w_���Aq�	�N�\M[�TU,�o`b�N��.J����
�h���hbm���q=$d����:�{����s��������%�W�h��$_����jJ5L�E�#����.����nt�C�%�%
�/�	�$�`/�:���9�n�������Xs������<T������j'je�K��'�������mT�����1<��p6km�#��\9e%E �]�$yL�L68�����[(���{���@��P��#��,���"�����-��|{P�������W������������+��l�����^����J���1����W�|	%M���:���U��D:��d�����maD."SB\��|�������t��0`G����4���W�.s
��[�L���#��?U#j?hgxR�bQ��O������Z�����b����A@h�)��h�����v&1���
�O7o(�5���������1���b`uqSi���8T��|L�f�����QUR����r}�oK�P�W������nlX 3�
����"A����}$r��X��C����d�_�pY�k�9�����*��>��Qxx�s
�$X��������&_C?��;R��Z�c8�/j}�>��pc����o����
1fy{s��9a��j�V���QE�u*?PW��B?���p+�5�g�`uo��-t����+�G��U|3
+>���-��ek7,����6�
.jk������z{��`�x�#�@�V�5^�?��qn�U+�9%�\�����D��&7��CB���>@�z �;�g���:%[�{�}"��}�B}�M$�j�o8`��P����o(:���W�z����v����j��IU���ui���$������u���/�{s ���FX���75�����6���D�N�$���W"j��e��	Qk��A�7x�M�a��G�O�(4�������*�E��W��d���S�aoX�+����� ����R�����*��5��,�����9�l��[p�v67�z+���!jr��� ����N����1��/��<E���������~�D9q@K�,�ec�E���|v�OV<HYF��KL2+^6J�_n����a|�
��krjS|��,y%/*�X����(e�r^r&���w;��JOE��V�/���������0G;��>�q���m��+��Sdh�	V���vG����(��#������D��xo$����u���{�������@_���c�L^f���������7G�W�o�I(��^
}�'_oe�a#~���Z$��pOBn������-�������I��g���$�`������KI<�M���=|e��sAv
��������dKm��pk�����`�q(�8����K����;��T��0�SQl#=�I��.��Im������<I���0��q�ZX����6����6a��4�����S�����z{�&��=m���e����s�fVOA��2�=-�0Ohu��|�]�x�����	��?.g�3����p<�a=T�Yv������n��8d7�g9�b~��U�!�G�}�$�$,T�������%������l9�B#-��
���UZ!YQ���C�T�y��nM��"'L���o����b.)�k�`n�f��p��f���K[�w\[���1�I=u���F�*p)���s�W���~i�qs�����M�"t.�\�W�E������KN`=&QYH��4�SN��y6]����>���/� ���q~�s�r[�j��������uH1�q:ia��(��������9��A��N�0���gSo[��w\��6���*���l�IB����sq|G�b^�]����}S�����"UM�[�%�2�c)����c�J?�b��UbS��4��X��PD�0O��9�AG$��Cc�{��6�4@2�x��@�\�oD>Xu��b��y"��Z9n���`w�K��l�m�����JRR��:��\���F�|�L��(���������;���7�E��&gp�~�g�����nFlqJ$Ie�����``kK&
�_?����'��>g�;|+f�}�	�\�4��}�DLBv����U{g�Z��
������	�m��S��m��mR��&g�h�L����L�69�4#����Ch~�:x�^Xc��t���)�E���*�S������2*��7E��nPU6��l�%���CILC!oX��[�������PY"����]�\nu&�LG���,�-�:�Zce�����?��XLK�a�S
������a��r��$�I}wx
l��59�\LR	��f��B|3
/�K�eAW2�D�i�+"~�Z���m�:����J(q�h%2�B�.�>z:�N�,��r�X�I���-{R�t��=J�+�,2��qA�,i��AJu�L*����H��L�g!p
�Q�I�-	���; s�@���	X%Tnu��+@S��.��2�9do�D��.�)_��o��q�T�UN�
�t���3�_YSL�2My�}�2<Y�}2Y�������R�t���v��FW������V�4�
���x`���������x�k	�i9�>���..�j��k������j�ou�L��!�C���s�+��X��e�1�\��|����D��� ��T��S����/ AaaI1�r�5�^0�b���%�y��4�������j��DG���q�-&��7���/����!n�[�����I�6Uz"X�����&yG����@��T��I�tz�'���2���C���h4�U��H���Cr���@����K��%������$����U����(���N����[������.a��\!�������O�4�7�
SO�CWF��L��=j�=P/��p|+W��clC�g���
��,��������9z{������_r�5�w�����P��O���l�����7�s
���~�����]����~�
��5#�=�a���}r���>#�o6�����f��4vwO�&��b��=f������7��������������_���l�L!"^@��E�y�FW������OO|��n�[�#���Ii�C+	����oJ|��������.�-�K	��^	[OM��ig�HBI7k�U��|(9
�����Yxgk+����R���'<�w���*f�������k��c�Z�1��c��}r��n��cOsX����AU���B�u�=9��<{-�gO$	@T&��RC����C�����k�.h��vO:yZ�=���i�������N�v����+O�G\G!���"����&��v�%e}��{��M�W����v���������L���� (/���r�i�a2�}K
>��O�w
>wT��3����9:�>�J��:��,�r~���������<����>+�����\� ��ZAt0i��oLgeK6%3S�)������H������r�&���r5
���������b{��ar�2J��c�����n��M�&q�
+k��BQEs'��!���<��Z]�#S!�r��VN9b_Dz���� ��E����������������=�o��x��S�� ���{�w^`�c�Z����wWb�xM1�w��=7�M6P�_j��Y��0��n�+S����$�����@x���)��V�D+�SK�5M�tsM��J���1 �H���+��u�|�l)�D�y��PR�a��t5����n��A��j������D����	��m�M�^���	�o-������V��w(������a����c������ _����H��2y��3��,�_���WV�nq
IT�c���d;����,B�����+d�,�c}j���({I�<����?�����\q�2b�N�-�|Zrygb/P�8-����e��#X&����|d�\������K@��7S��P
S�rH��M�GO>?oGDp�|���?��&��K9OYH�	��$�:	��Y������b�e�yB	��3�	��9]Ds��R0���$��P�f�����!��e �"f�Y
�8
 e��c�bs[�-�xqb��O����K��;[W,�tK�'�5���:�m�q+�S���a��6��"�>aJ2N��������V��Ie\89�0.�6�5`��RZ
�@jA ![IL"'m:e��@$�%����K W@����
w�ga�pk5*�
B�d�*l���WY� �s7<^��
��h$W����R�^3��s|"7ZM��L�"\�Rqv�&�3y��80�g#�K����`H�$<y�����b.��3��V��qy�U����$�?�o�g�*��tF5*Y�u����v8*]���U��!��p�X +����@�"�
��!	H��q�3&�����9q��r
����e�]���5���U
��V;��w�t���kD�;�u�L���c��f;u��d+���	����p��P\��B�.��#�5PW�pZ�������A&�;��<f�|���t�C4��y��-6WD����X���:y��c!����m�x�;�*���d�'�u�����@�nH�H��V�3��gz���W�������hH�]9l�0���(��[T�9��]J+���	m�:�7"�g���Y2fK��%�l�/�8��~�s���T�����+@E����R����_��E�c�mRVS��	�7�������x
)�t��2r����%�	R.S��7���j�B�~G�G).�mO��l�Bj�!���|�-���y�kW;�6�����Fl/���<�,�_G���p��Hi~�p.���K6]�D[�?��0�����A�!Yt	QU�s� ~�6��u�i���t���l�:�W� ���LbakLZ�^o����82��A
�A�d��U�6'��hv�����=��l�����U+8����%A��^�P�#xV�&���?�B�M��������� ��o���f��Y��6&�YX����s�7Sy��]u�$�:H�M}d<#��3��q�;-zPh��,D�F8�P7s-����:t
:|��9*�t	�3�]�t�mu6�x��B����
^�\�U�����k%=�5���|��L-��J���+��o�O�=\5�W8z5�=<T1����O�TT�g�DI�xzQ])�^{u�[v�B�w��%��%�)�ni�;�����L���y��-�m,��������'l�F�jY�Re�4�/`!�v�eX\�m�O������J�6�=�F�kX?�B�|�����9�N]�]�}�2;�����*��s~CMv}���y��C���_��3KV�
����fxK�a��J]K�V,��}'QV!�dFL�
�S�^}[mp
�TwO�I�pUI��}�����-6����LgY��;����l�:s
���9�j����:�-��j��E������Go�h]U������ee�o.��V���m�u��Y���4�eU���z&����g�������^�e���.Q(&��`��C�#ws�D��@��#�����N���nY���TB�t��u�U�uJ���n��z��C)C"����i��y+��-�	�q�E������E3�A�������*�<�q>7�������V[�����c��Ve���N��(^@�(�����[�D���[�mz|m��O�t�Q��f(�Q�ACW�~����O������������\R��S����xzRv��,���v?1I�#��4}P��%io�O�������Q��[OL���7?{�����Ko���y�$�}��v_i���t�ie�2�G�So������s��O��J9��r����
��*��1>�,F/!����w&�zk��-?�z+gn-����%hWs�EM��S���G������["��
�>o^�P�4��2�����I����bm�(�� }�?" F_�|��#1 I�"9�2����������f����I�L�]�>^�������pq������<q�W���&���9vd��a��	�[6��R^s����{ ��gL����j�J#��]:zj"t��,zQ��\�������9�l�������!8w���^Rrgos
�
��/�v�H�hQ"�r��G#��G���� ��E�%�����v�?�?W?x�Ot�n�LInh��E�������HeL%�8{Ak*x*��=V!3Vp���!����zA� z
C-��!�����+��������=�t���q�0N'��X9
ja%(h^|���h������������Y�����������R;W�Rlu�<&�Z��H�}#p"�M������^Y����-F������ '��M�Cy�KuX����G~�qv�����iY�a��s�L�����5����,�����;[���z�w���������F��������.���t�=��s���r�U��)W\.H�]����o�W�Q2_��`�|�<�`4)}�y��3�|k���M�R�A��dr/��~���o=����t�4Q$���Y�>��9�I�A9�)	2�L��;��?�~�J`J��V�?PN��6S[����8�
������V�����)����Y����o���T��4o<�����xT��zV,N���}8T�����pz���jml���+p��!�G��C������Ee��������X���L��X�4��������m��b��2���\: �����j����C�\����Xg�9�����<��gM�=�o;�3�bX%W���(���yy<:>5m��ZZt�_}75�����t_i��9�������&�7���;�����uR���k�{���6��]����999~~vq�Gf�����K�i������c��0�]�\��L��y�m���^qAf�0���O)
YRPg������83i:p�:@�;w��sM�%�k>	���M^�3����5�Y��o�SU����r���I�����0���YD�)���)�3�t�eJ����8�x�����d��S�j��F!WFI�B��&��\�?�����x|��w��U�4k
����	j�G-��c���#:V��4v

!�N�LF�&��&������V_|�r����ml������8���z�[�i�s3������������1>��h���^����I�U�N�nI����3���:�V�hY�%����Y�a�l��(e��%B�0v�N�y@�\:&�1�eZ��
	���~���['"����ZTn�K�|�G�m�Z���.�C�@����z�l���w�Q��8Z�n3�*��7**V=����(��!~����Fc�D��q+.����s���@�
�������8A}�Y��#n�T����&M��T�l���a4�L�(���o�+h�26��(sC���Y��G.9�t� Q��
e�O��ik@"��~�K�c%�b��^�e'�mn��wwV�W�(���f���s����0����A�n���?�V�{2)�G��c����8���l���^��S0��������>��VG$�����.y�����m�5�����9��~>30fK��+�$#�2YXY�F�h�5������YvE���vO�;��>�R.�f��>����h��pk��5����=,�K7�\Sn ��R�mW�W�s��I�n|��8J�J�z8��n:-+%���2��K}�8�!-V�d�r���h1�4�� Y������������y]
��e��c�����K��dcU��|�����|^!����{�K��Yn�^�1���Rk,7����T��6�l�q�������TI�88>��F:O�F�?A�<I�|wi��i����H�i�H�>i�����N�4�y�4��Fi�l��H#�GJ#��"�t�&���8�o�F�`X���Tz��<���
.#m�.5GxmKkq?�2��2����z��
U����s�|9����������OT�@�rea������p����0�C8�����[:P�A�_(X.�U��"�]w���w��j���#X���M�AN�� 'T�q�e	+��%{��%-�GT������.)��K���f�L ����+d���������P6)���������;�r8����}K�����K��+{$y\�Y�*�}N&�*�NR����~��OQ�%�b��w�.��&
�.9�����>�gNR��:��7������,��l^W��YL����dr�H��}�$���A�����M�0�5��v�\R^�����qm�@7S>!��3>�\��(�|w��7�\p�n�[��#"L�m�~s�@������|�d�4�p���|�p~/����C:�>g�5��������?.�i�/k������a���86y��t�����W�a���,=1�3��xL�4H�{�)�0��sz6��,#X�0^H�<��3*B��]�8*
d|h�ei���|p����b��
�!�`�2�Tp+�i�[w�$���	�dN��dy�%�$������r�^L����:��;J�,��w��$��m�5�X���w��Z�X7t���!���=V��Dt
�ji_vz|E�I��5���/f����2�����j�_�F��R;�(��4���|*o5ci�S�����=G�K<�k`.%�"S9c�(��Q����C�Ye��\�������Gx=�k��d���>�Z7�B�f����n������������r&D����5��v:���r�9gt�1~"Yg
���:&Ru�k��s����X'�L����-��������fokS�667{;�����Z���7��A���l���T��Y����������(���N6����w���p{{wk}���n����[[���{����hk}G�:y�Uo�����������
��K��i���T\�o]��?_O�x�%���o,�6��jW��R��o���x�������^�\�a���������j}g����"��A	tZ/�L*F��	�k&u��HC�����~
O���<|���,��l�����98?���u���aFt��"KAy��^�q�E���r���Y0���,����8$:bS��;�8	�,:=`�sJ8
�6�^���m�u�yi4
�
$�09I��Tj���/�a�4d
\�����q��.)+���&m��2cuM�tW�A��xJf���x(G����S"�P�O��Gf4.�������X2J���I���bS�<qJ�o��eD6�G��p�pB���s%�&�V�x�b��)px$Q=�8�g�b�n�i����R�h>H
�C\���zY=��p��
��.yeG���k�>��h�
��'.�����r�#.B/8S
-eR��@�c���l3��c���.:���2��^I��@���T�z�C���d�h��H��d�3��8d�� �q��[����|c�
���)�/l����u*c.��2���'��}w�����Z�.L�`	�*���	��
r�<Sk��5���6��UK�)QP����TO�����i-�(���wT:���h���
����-8K �6�e���W�U�s�R3��������hc{��`�� �
���{��*;,�U6Em��::���G>@D���C�>9���e�4����K��^��Hi��1W���[��"�Z��*yS�\�������+���`4�l1����Z���X����%W�gX��4�FA8�g�3����h�P�7���Y�,1�Z,1�5U.5��l?DD�O���_�a��#�
q���|���������o�]\�}U
|~~qt~pq���������?8>}{����S�����;���R:��74#(]:��,I*����D\��/�vPonR�
p�<�z�9B^t�+g��l�0�/�����$�����|��1O���#t��&�t����qh��r4�h| %Vt�����m��g'�z���@H`t�)B)���l�;������|��=�7��9�/��9oV�_^�E��T
��F����L��,1�����)���l��-�����%4��E1������\Kz&(�n9/>���q��o%���H�uGN-n6��#v����������!�!
������W�]��l�(�[\�uq+^�_���s\����/�x�~��|c"F�.d�m7��]�8�E��Qu&�z�����*!
 -ly�����wL[�����C�$;��
�!���7\4�/Rm�F��V�x�3'U�o;�	��X�K�`������z����S�$����w�J6VjHM�R���9�Io��I�����p�&����s��_�(RgR��_���!rfaI�	Z=P����nRz[WT�
=p]�<�dJ������|h��x�j5A
���%?��cw�G�t��X`c�B��{;�6������ 
���&��)D� 
8wV��!�g@��G�NX/����	�S�H�Fy�t��H��Q*��9���#�p�;������f�����aEx�-�8l�6E��(��S����#��3ot����#��������[+5&D�^")6�yX�P� ����^�����h�����=�]qu^"��S�������4T�28�D�W���"L�g0�����C�-#,
f��|N��_:25W\��){4u�R�� �^��tfNlezup.�:1l���~��i��".��6aC���*��UM*q���|$
����o�3�rWE�9��j�?q�35*�������J�eb�Uv��I�����Bx50�G�0u����9���QYi���ykB��.��s��d�(�J�#0j+��l�
>w��q��),]V���;|\���)�Z�����c����������%��{���n��}�x�-�����Ns���z��u�	S�
N�Fj�Q�47W\G������D���'�n�� p�&cLLScp�����F5��^������3{�4�8�z&�R)�8����q<��w��s�������m �41<�������\�����w�Q~�9�L?�^BB�T�WE�Lc..�? ����H��Y�s@�b9�w�w�4�������l��g���H^�f�Jc5�{�*tnk�J�N����N6�����]�����0Mb�~GX��r���,�l�)�R��c�9aV�������Gp�,�h=��w�p�1�3�i�qt~Z+'9`K�����.o+y��G�r�:��1�������"��8 !�h���u��.�����
�r�0���@�Mb>��J��R���^0bt��5��d|���%��0�*5�]x��"`fl�6�/�����3�������|��<�E�����0���l������4����,Hr�&���x�D�@�
u,o�8�z�,�j�g���v�H�yg�������l���;���=Vd�Vx��fmc����|m���o0���CRg�.N/���NiV_�|���.�S������&�*�~����������k���[#�4��v�R3)�05�%�'k�U��ds�@�+�/�.�~#�[�����3
u�}:����r��hA�(��a�zm�r���/��R����Pq�Fy��~<��k����'C�����[����=���cM��fx'��e\����+������u|��)I~���9V��G��JG2��-u�;��;2U���
]A�����gMj��F�{��y�e���xq�]����1�*F�~��q��
���>��ii%FG]�CQ]��~sD�6q?����[��bT������s�pv�����p�,}$�������twlv�&��e�wJ'�2��d��	�����HWWp_yt�����<��;��E���%G(�r}UR��p%	w��d^��������	��g�Z{}�>���
K|TdOKSl!���M�'���1r\���#U�r��?Rw�H�[o���������F�R>�{B���$g��-���atk��K}f*���0��������C�J��mq���7V��tE�s��m��u4#�=������q����;�(���^U����T!+x#������t�\�T��+��=C
������i�T��cV�6����c�"R~��{<�O� �8�N��XW.�-�D��%��W
w�
����	�����un��E����h0��.�����J��1+��M
��4Y�������O�����x�������[�?�*H7+O���@H��b��Ad�,H��{��?&�0�!,S�����zi���.-����W��"������%~yN�����i-*F�=�r���iJ@J<�p�r9����%�)G��g��B���a3_���C���"�4�/�]�?8�T�r��$�0�	t�]dC{����hp,xS=����������K��8rr�np���~�����v6]LV������a�D���I-K�b.�
H�&A����z���fQ�T��i	����la0�Vq&WN�
���f��G��&{����d���x�R��

o�gZ����;����C������^ol����gWj5ry��\%s���>g���~P�4�2��^��@�8}��.ay�����x��lPp5Gp�{�
�����l��O��d�,��;=k�|"|�����he1>��W��QUu1��\���:j+X�K�+���B��GI�7w79��W�]�����2��p9f/�Z��b_��x���8s_�w-��^�H����~ |-�MH||�����U������	���	���I����8�No�������^{w��l�w�T���L�J5��d���j�7�������B���K��}Bw�x�Y$��2_��Y�FIIc��hHa����s4�;�����h���������z����X�Jt�����C�y��8�LL30����7*�%����]*/I�4z�w�3��2��2E�GTg���oXE��V(�V���)�~��[���
TX'�lZ�,
P����A�+���Q�n���}���>a5-���7%6�;r���+a��"S���S5��~��E�N��
 �������d��#�N��V�:�.��:Y��FWo�l�N���I���C�)�Q4	�<���G���������~��.�B�tQxg��*�uTr���O�1�M�������J��p�]@2X��ln1��d�$w�U��Y<-�O��c`~�'��C����x��N�u����qJ�PW�9(B����Z(t��Sf<3�A��pZ4{���9����P��k��������0�H4������Qs���|�����9"���}A��d��^��4.��Y�e	4���F���X4f���T���i"��VB��-ff��1�i����Z�I?E�k��C�(7�M����y���Zy�-��Wgo�^�KSr
,g�����������$�����������J��,���j��%���-������u�$�ND������W�+3W������lY���nh:�#��_������������q�WQ����7��(b���LM.�b�v
�}t`����mt��K>1��/�������C��o��9�R��F�^\�Mw����V�`�b�]�R����������<���U���}�O������,���v��W���*�!����:������seD 
����{�]�6�1aS���8���zY�����W��d���J���������KCa��i+7N����V�e����V�G1@�[��^�$K��r�e����~8�}��\�������W��dy�Ee��9��O�@��v{}Z�N�U	��
5X�������t�-�.�+i�kuk"s�j�$5+�NMx��3��Z�!*��V��j��`��;��Ck&��~��I4�&���d�N"i8'��u�2u~�J�u�X��s�c�\H(��u��U[w�	-������}
�3��5������Fb�DO��Y���L
3Q��t}�ex�\��3%�D���f�����B���&_�W�:������q��d���r����Wn�Ws,l���c!��f��t8��+�9d5���{mu�����J(�c�4�.P��h9�������A:�/=eb���T����FVJ�d���B3�f)����o��9����Qa�R~?��nT�Q����+��b`���e����H�|�F�"K���,����nW$a[S�Z��{j�pH�|f+,���>�������lWO�L�L[RfDd������"
j#�?:��|������|����s���,�v��6m}�M������d?�d��V��h"B�R/J8e+����M^:l~��LP
]���6�L
�3yT!����t=�=����X&�T�"K�D69�I�\��4*��'M��*��T����6> �������^p,PH���n����'(�h�V�;B���1)�#�����;C��u�������E���}���J����b��Y?����l��d�r)����/=�%���5�P�Q���Q���:d��<7|N�_~�X�������������E���!D����XA����ysM����y��EmT����!
^�H��i)�"�-<T��kE���E�V�U#�Q���3�De��D�~F�!v�{�eZ�:�|fS��i9-����J��d�b��	�����Vky������b��8�a��f��{��zCo6��`X"VhF��L��}<�lW�V��Z�FT@��(��z�!325D�za��$bQ����
�#]�r�_7l�mb�S)E�a�lda�L�c.LO@"��sK�J�CWp-��y�8�2�\9��������ui	���>mB���v��~�Y����=�`����$I��Y��`���Z�WY@�s�b��#��/�*e���3���	�%�� ec�yO�c'��u	�����k�i�9$��7i'0~���M����{����{�<�����H�U������i����^u�r�#�0Vy�]����F3����)��
�^��*����ec�6�A���.m
-��M�b�C�U���zTkD�R�(�9�k�A-�ZL5�ZLE��f�0-*����%�P�Z���u����&w�FI�$L	.�E2��fY_9���Sfe�u6|`���)����1�X&����p�>o�A�w�����;{��)�a�������o*��?�V��R)���� :,��Z���N��k+���W�V�b�$�J�'I�?��kR2z�~���{�& %����V�y��l�t�O������W6�o�������d���I:iPjG�x��l�
a�+]
�s�������br�j~�U'���0�oq5r�gz@��������e�}��2�������
��DD�64p2=����Z��Z��T=r��k�j�_�������4�k�R�,�K�J�vT=:j���A�h8�7jG��Z��F�?�w�����@�o��mN���/�9%���������ha��a6F�s����p�0a���G���P���m�A�����;*�0D}����p9u�Cj�3���G���{3*jYFM�X0|�2�E:X���������CX�?�&��I�.s��M8������B�����p��Bp!
������%��i5���w��.3y!�X��3�Bb���W�x]= 
��W�}=�Ld
��Z�����*����z����N\t���^mc���"Y��/�ze�Q
A�n
:���7|��S���2��������d
S]�x�z�F����o�v���47�����g�Z�Z�(���
�����������j
��c8X�&���}�Z�p)���3�'c����D?��!�
�j<�n�Q
<�iT����)5D�i}����y8��j����'D�
��.b2K>{���
#����L�]M�X���/N��w���X�g�{LN�����\��V=�������[-�q�,.���/7�����jI���J���U�g��t�v�Qm~��� �T��fjN�z�3�����(����&�:���\��i���bo�{����"��a�
y�����h}�����������XP.{���<�����T�(��B������@�T����i��&����DT����jGZ������.����y��eVc���V>��J���Y�tg��V]SM������*��	��y�R Y��	��c���e��Z�L�wgT�����i�O�j
�a�Wt?F��dWS��3d�N�n5d Y�hRkn��j�\u3���v���H��(��RB�V$����J��X3[�zLUS�����D>����TTrc��C�����=����FcV�n�5(���<9�pX�F��:,����z4������t����tQ��bZ�2��$����/g�^���x����jE��v����/��"�C	0n|��q��e���~G��x��'D��j,����w��(���,|O�[��	�5~4����x���?[��cI��j�I�����CL���k�'s�}�\����t���/k��x3L�*��������b��:��Lq�'���)����"�����8|J��b������F�tX��/�Y�MW��w��N}vy� X���5������f��D�����b��[<Y��%j�ed��I�������hp2�?,01w~�����A�T>*�t����<x�G38�d8�9f�{->9��R
l@���*����#Xy���$����8����������������6��7]�gf+y���V��X)F���f�x��w��<�	k�g"ee�'�zp 8�O)��
�6o��q�HM�Cc.<	����2�_�:?Qt����K�"��n�n��Mj���IaS����0�&���b���qt����c\����{�x�||?vu�<7�Dt�-~�wru�m�]vz���^�����9���C�<���dl��f�����v���������6���%��=c�1����1.��~�Wf/�`P��}����� 
��V1���W����Yp�{�@�A.�NA0;U+����[��8�DG������>�[�����M��Q�H�z>?��a99A>������i7s����'�0~J�M�a�a�*�G�.��Q}`(��ql���bB5;pk�������b�7|����|����E �������a�X�%uA���Q8�4j���h5���`T�^5���������hP������\v�	v��Xl��g�?F���������h��� �C��]��]�X���3O��c*M�eu�M��3!�@Y��e<�L]"����:p>�XQ�O����Hd�M��7w!�(��P������Q�"�����y��"��1/
��[B�b������u�N�e���,�xVU�A��e�+���!����n����R�h�6��Z��|���~f!��
N<�����������O�Wg��^�������K`e���j).��]��vg����]�]�A�/���Wg�{��c���T��F�tX=���� �j�R+��j��H8�e��^PxAm���dE�(��_��`vFVq�N��
7�dM��������i��_��p��A��y�PyMU8fA������7bA�0%i,q \�,8h�����{7�o����M�:����t]]��>?'a�a���X'�Jx�)(@�B ��b5���}�.`=�${xi����3�9C���o���/]@y������g�B�+cR��k�������&X{dt9>���!8���7�Rc����Avs��Q����7��Ck%�5�C����r~����T{V5�TK��5���������!H&i�����J�S�:�`���k�
���(`p�f ��x���)��|�~-���;���������4�d"
�7N���-~�v4�_��T^��BW�������H0�+���@��(��6H���K��:B � �:��$�h���
c	y�8u�B�0���;�	�S�*d��Jh'
i)#w�I):��F����/�h`q�@��/~��r~�z�����<��~
�}�Z��AA��)��9��V�Z�L-�W*�'�a�M�0�h8	)a<���Icd�������f�b�k�S{�I4�
�S������E�%`l �k���w�c8�����9q���R�L��Y��.�����������:����\���nr���������(��S:��6�\�_���xx���"����~��"T�QT����	��$��y���A�o�O�����l�\�B`�e�b-.�{�IUJ
3u+�	+zBU�p�V'��(v�+���70�/o���=������V��?FcAW
�;5�t��uFA�spW��
0In�T��M�|E
��H���J�_�k�d.��p ��"RN��B�������g��������O��J�P�}�<�)������"09�
`6���:?{}	O\�tcO���l�����x�G	;�q���-�$a#���>X�_��>3_,�/@�`�/��$^����g��Z-�9��%D��SO;���6_%w���PM�����-g���f�.*�w�30���
l���O��'�wz�)}/�V/�D\!ujPx�^��1�tF�}�Cc ��u����p{w����M���/�,�q$DBz�T�����������n�_�rY>�)�"M���w�(!�
V�*�����V�Ct�SD��|�L�e�����dF�������B�Sfu��v��9� XS���|D���m�a�9{�5H��+�(j�F�pT�5�����X�K���W0����*����?*$�DK{z5=o��
X�h1�{4���x�:����r6�&_���[�Yo��{
C���R�gc$��Yr	R�y�4�'��Y��e9	G%�H)1t`�	[-�)���ON��y��}��"L
����dC��"�����'sq���0�D545�}�e ��b'��6�U���fO�=���9a�* �����S�*|��"��e`R�c�O&<e`F������*>[f��tw�x������TbCV��gK�AC�'�m�R���T,d��p4���-�����������J�^�U��hT,6�j�qxn�hwZ�H+�^0��VI)j�q�����%J����8_��a���%��j:�cE�&�E
�oT�(:J���WE�}&d 9��y<���A(ei;��$8�0f�N�@��1�gR���F3%/�SK���_[X�.��m�V�����
�2ij�2�Qv��j�sFZ��=�����9��a���?��Og&$���U�%������2�[��QR�#���:_��3��x-�;5H��k�2�"sY����F���+�e�����1R�o6��f���5^��7aT��r��W��h�CB�&���j��`K�W\N�����Y��g�Z.��������
�����$�59�q�g�$)���[��P���iu��Z�XyaI�{"�	2�L@a�o�)��b�h2&�v&�T���,W��!�1#��TjtQ�1��D��
����p���.����g�r0����f@��jJ��w��4����	�m}u��f�r��1��������+�Rb�t
J��3Z=B��J�T�P�}���:��9 �`�����R����3g���#	�;LGj�3��$B=aHJFc'
�PR��p������}�~�V-Z��r������j���s����C1S��+�B�p����;o/����n����u��m#X����[��]���Xn��������\e�lMSCN5������I]���pS`&yy����2%�F�[q��A}B�8�GZ���I�P��(����U0Y	,U�F�;XL4D+3;�d�����R[���@��Q�W��T	HB`���ZR�"}�����-�������9�-6p3�|nYw:Tfx�{��P��!�(550ur�I4t�h��H�!��k������0\3N�
�.cjqS�����p�)h�y�&�eZ��Vy�fPP��J���Pb`=���$�������0`������i�:*�S*I~IXr����C�yP�B\�q��vBm,!�T0��nhy��j�.��������l��y�u;j�����7D�~�6s�>�k��k�4�j�������b��TJ��)	�$N<���:����ke�����B����Xn��W�J��$!�-f�(�L����`�F1z��Y�7����I$�]��'\�Y1D$�ry��f�}�w�s��W�#?d�dK\��}.�y�O0:�/�t���-e�F�N��f��:(smJf=!t��W�����lz_���G�72W��lk�����[�(��E5r�)!�@�+�^��q�p��h�Ks��C-r�s/������O2?�s����|R=o6	�9�����e��y�~�z{��2����1�Z��
����&T�����N�������
����l.�H����}��+e�G	b���d�U��?4�]yD�����
���oD�-�H�&N2�*Bf��`%�"���<bi{8�h��Q�	P5o�h4���t�Z�����+�J��5.f���4���0�X��Wu
�/='�0J�20�}"���O��F���{Ue�p��:?���'Wg]��
��.��l�N.}R��	���o�$�W��,g3�$�d|�}y�����Y:��zck��3�d4��:�2����@u�KB����	�R��H�g5 �P."�������S"'N��a����-x��/U`��WJd����QR-�I\������������qm���e�������A+��F���h�C�*�-.��}7L}�S�$�(%-�QK2q�
 
M����sq���k���`���N�7��7��j����j��(��J���9p2\i�7�	9m{�l��D����LH	[1���.h%����l��bg��_B�@^PY�3�27����p�c�xI�^��D6����Yw7��iO�xY
_�_�B�$%��%Q!T9�����
^��q
��j�9�;���c�[���Q�4�i��[N�)���n��������C������h
O�5��M�X%���VC�D�������	��B�h��
������M������O���e�r�
��j�P>z��R��s�:{����]���iw����sg��"�<!��c��1\qX�LU1j��r���n��Y���Y6�JfPF[zc����,XhmdV���n�����\�������%CW���xUq�.�d�c���L����v����2k���JHO5���r�\���sb��g�:���8]��;�,td� 	��"ie�F
�
��V�P��,��L���=	��+����24^�G��G�~�%y�MH������#����5�M>oc��N�8������@��Y���u��xgX�i�z�4Yj�gT6�'�
���y�LO>��O`�F��
������L��\�u��6�&��W��Q����T?�]�������V�8k�� ��s���_Lf�H������mf���:Zvg&�>��Z�����Co�V�cM�;y�D������|}0������9�+�__m�2��f���N�C:����<��u��{��J�����3-��d�q)�}�%N~���&f|\���'�������2�����s�X.�H,#E����R'����t}(��T�NoR�(5�|b��LJ��Ihn��f�E�Txz_��E?$����?�D5�)I`���K"A�$qT�r"D�1���ju&T@�s
��,�)2���H���5���]��g^�og�V��k�����L�6wQQ�������6@�]����g:3f�$Z��x�Z��.�l�t����
��R���:�3V�C��s��r�u@�e���/w^|C/�mT��CR�6�5V���#�k����Z]�:��)�\���<�����Ft���J����i�T�3uc�����$5;�I��{{�7��uV���)��3N��2,Q-Vv���������6�y�����{���|9uS���$f�u�3�/O��Z?�C��59k�v}�*��[$K��uD�&�h��K��r��(�M��$��xR@�s*���L�i��6��O[����)��3�h���<M=V�b�xC.�n���*a4���q����~�#J`N���J+�0������/���������/t�25��a�[8X�Q2��� ����N�"�V��FG�Q��:
!���Q�_�p�k��Z���B���K�K^4���+c�hB�������^
���(��G|�K>��*RI����tI2���P~�N������������hz�,�/�����f���l8�lwmQ����6Ey����\��N\�Pu���xN���:/E���X"�7�&���~��z�G�Cs�g\r2xz������J!��go�
@�S��`
\�^��$��a
�t��P|�C��F����\�W���I���d{�Q{he�<@��h2[<��������-�;��	�R�o�H�����pz��5��M&�u�G8�|~�Y�.I�%������nj:_�*�/�:�z����t�{������ ��#�����|?�k��|A	�7_f������8����hx%��t&�4bq�\e)o�:�����e����[e�]������f��w��u�$�;j�%���������������m�4��Y�*��l��G�����B��%�"����?��cC��������c��WN�����.^��f��79�k�!m�Y��c8rX��a��>�fBM'���zQ�]<��x)S�Tg��R�(�	��b(Y�lY[�kaI�B����ac�XVRf�(�e6m
��
�}F+&���k�:-�+8�����.	���]k����@F�������v�wR*a�A$�-~���<�|]v=]��$6��l���d5y�5��H�i�,`q��p9aL �t�G�h1%"*��zTq��+�B������Y�}��� j;A����NX�UE��d[h9��Lk���1��7	}� ��	�30�����	|�Z��86�}=kOc�>�`���a���!�\|X	�q�����z�����U�PQX%���8Xl`)�������o�S�f�G���B@���K������gqJ����5c���R���[����X/�F�Q�rX	��f�\�F�z�_����Cny�r�����1�:cz�f��(9��Aj����T6��������#^yH�!~�-�0�D��X��N&����+���=��vA��.bp�<	EK���C[��/�������:���^F|I��j�X�s����g��<��Tb��
u�s�q�0���R��K]������� ��������t�Iw��N����p�R�I�4�Mm�c	��^�=�������� �2�����=�Nw���B�&���d��X9Y���0����3!��6�y�����H<��eAFv6�f���(�'�@H�A�����|Q����Z�
+���<3��j�%��HE��t�������B��F��!�����qTGO����^����t���a*0�&Y�� ��[��'�������k�����h
O�������}!~2� Mp0@��]���L�f��'�����W������:)�����T�� V~]�<����#��'���CA��c��c�������U����?S�Gpv�<�	������x<~n�N��}��`���� �yO�\��3��2�$1luu�����pTE����a�Hl�q���b��HQQ����B���^�a����bOp5�[�i�"`�I�6�d������O�6�����<M�	?&hx
0S�Zm�R13�"0�Og��o
�,��(*���)�c�L��l|�i��������;��i���u��\`'��2*�b�'#�P�"BT��k�s�����%Y����B^�x.��m`��.�&��
�V���V����p�(G����hU��0��e
�v1D+F!�=��?������4j�����������������R{���"IbS\�{G4�RJ��L&��hsg;�FY����?>�*C�z�:[���]U]��qW|p�a�U7��q�J����)����M[�����a�4,����g�����j<�����`�K��X�B��i;��}����j}S<d"�D����������n�{A���TJ���(�,oQ�/�Q��T0�9z��^�V�
-�
B�G{���o��:�H�%���������VS����$�*�NK��1[�'�+��7���:w�����|���?�I���b�'��_Df���]4���VfZ�$�L�	�#��Y��'2y����w�Q��l�T�r��u���g�Jn��^���E���'���:??n���{��u�����hF(��D�Oi���o���\O�+�L,Ls�\pZ���� ��u�0�
z�]�Pk��*��z ��`O{�.q�����c��Yj6����`Xh�H�������`��;���9A��@���h�G�1f��b"������c��������:R��.��Vp�������pm>�Q�Eds9s��J��*��N
Mg���E;��k����dT���B4��0'�*�?$M��F)�;���E����"��j*:��D�5,�k�� N���[��nBg6U�8O�����l��'�F�llv�� �d<��BC���k�@�����:	����F��Ty:
��D�5a������4�l�A
���H ����8$2���`6��92n������`��-FE�?%zT��p !0t#�1��E	�!��'��m�.U#d�c3�����)w�~$�b�'�+��h}Y���9�����(%�#:��h9����@.�1*�I�\$bb���%	����[��%��d`$*A��������#2�����)��fl�4��h�d3Ei���K�nUF�99i��]�t�Z��H��]�J!�\q��`�w���&����+R;�SF<���5����P�`�T���Ti�=F�	������%NK.7�
'�DG�UJ<�O^c��Y8�=�)�o���}v-��K}I��TX?}-�g��3�>�~x��}��q�xz�����<�E�C�7��:uF���l�@��?��hX��a�S���EY�
����_�����yp�����31)����+w�Q2�j�X�����C����'���6��^�VJ����<Ii�R�]k�P�f~"�v���A�DVh��c�{Y�:f������8@�*�~�A������;:�y}��'J�p�n�jk��d��$0.{T��-w�@B4Q���(s�������r�LI��xKmZT�#���a?�AB���8��S���
�83X��H8����;�pr�}��-5Rxo��BC?B�J��&H�)��H�3����o-@�
dgf����.���~&W����t���%��
���c�N{	y�Zl"���A����YJnX�$LQ����)���0��F�0�n��g�uk���x����8�[����hx�R�%�1�1� ���JG�/8��z�V������!�d@e;�807�0�,�99{t�1�s^gl�I���}�;�|u�COm��|G�����>��Z�'���l�j�����&�v2�5wX��R������&+$�X��U8vr,�$'�l�c�h�e�d
�� 1;�[��<�5�������D�Q������<-�P��)�j����y��P�$�L�����;[jB@�a��//�+�TtX�K���hEy�K�IAW�H��=,O�s���6�k��!�!.(�g���xl�4�S���K����!��-�cPR�!^H;�����u���zB��nB<7h��-'�)���.sbYN���I�L����>[��ENy����������l!�����M���e���aA�u��i211\�����o,`d n��R����e���+8M�5N�A����:�U%�N��kH
��$��r���!tk&�\:����,z��������6��A�����[o���M&s�pX-����)r����AG��
#�!�0+�}%��fz,����7H`z����:����e��>�*�p��)C���u��g�L�o?��]o0`yc��[|��<lr~����8�XG`�eP����DP�h�L�3����p/ :	'rcE�m)B	$�
�4�n�������8_�Lr�u��\�S0�Z�p�1�q��V��#�[�����[�:�_|N���\~A6���0@Q�o��FK�����L�R�P��J��SY<�~,�h.�%F��M����fQ�����*=����{{�*�j����u>lb?�>n	�,����|�;=����U6��|��1�3��f����*]�-I���F�8e^4L�t�NA'��5�VD#WPd�Q�:|�Y~��9��eh|��z�.-��f^N�\��R���s[Js.����'���$#Q�w&��&�Es�����>D[ �����=k�h�\���X�f�����(F�?r�%������1��V$�?l�����+i�7����r����kn�����<o���u�P<���@U��<��g��;y��N���-V�/�3����B�}t�_�U������j�dD���T��)'�������NEmX��)��>o5�Q��#���9=�#��(��s'A�
F��4�?���~���S(���p��Hj$eLB�u�EY���+P���q��0�a58�U���J��!YF�`��-5;�J��H��$�zn����AJ�B�B &5������Hc�#�m�I.`�B�/� �����r��=#�C�����kz��?cW��
�����u��<��H3��khU��-=VHu;�����t��
���BJ��:;�	`���
]��z����4aP����o�^����LLDW3��/�AJ�C#J��s����N���������f�^!p��pb��Y�-s���c<�N2���h��5��3e�M�����u{��I���2oj�z����J��[��#�f�/���k�3)���a���X��i���To>��:P
^��hH3z�/������*���!���6��nL`A]���#QA���K)�LT+�`��8�{u��Zwn�Ev� D~9C��qPh����y������D�����@}n���[Z�����B������>�[�����i�v��4�op��:v�1�-o]�2m��X}��)P��g[��f{������|K�:�#BzgTC�=�#K��`c�����XX�mIiXL(
��cSS��<�����B��I��3D���M���Y��*�D��*���Px��jI����R�/�4�g�O�$r��o�YJX�f}T_@�������<�0UE�6�s���)tW�i��h��������fhJ��&��6Gw1(����/I��-"}7!�]��3}�x�c���~=������M;����l��\��g�m���������_uI���)3�d�&}�����B��l�2G�o���
�������80�����Wz.����m��q����4�93���-*a���^��]I�.�0:+h�����w��6�������Q�8���po0��{Fj�~���Zxx�G�C���������9�����L��/����k��E��<�-I����1�����������X�Ln]��?cu�N���$�R\Lp����5���yW�|3�q�������3�fIS
�,N�E��.:�V��1x�{LGTex\bnZ�E�2������03%c��D��yzk��%���(��"�x��"|�K�������:oN�����������r��Pb�������!z�����\z�c!�P���s�J�M�^���W��^���F`LP;���$������<��8������pu��^��}�#��_��DQ^�}��}��=+��H�"�8oH���W�H�
o_�b$s�_�0mz;W��lHV���E����Yfn*7�`_r�_�E��hz6%��C�9B%�a�N������<;p��d��0{cO8�+~|iu�@�p���~����Xs�g��Z$e��@�t���"��p �d������n�
��$+T44�2�%��q��)��F���l��<~>�A�����c����$,��9J��z��'�o;gmK
�2MN��#�J��|pg�$��/����D�.���)����%���;5O6���]�g�����Q<),*�wiL��R9��[0i��[�+�;I5����,�����*���~���{����P+���(�#����4'Z�0�$M������{V����Y�'�����0��V������1~\4�6�Q����A���@&����#�^W�s���j^����Y^b����,#'��(���w����~v��5k�/���H*���h����]%$��h�:`$-�F�x�;oZ7
���-��h���&=��8J��{��^��<�L~����B=/�X�������7���7S�_%������N��I��>i/�2J}6�b���6������S�����g��SKO��n8G������zv�H�/���)�tgm�io���R!��,0]
��)���V��g5E���k���'�B,`�G3�m� ������_�V$��=��o��R{j������-$�
�������9�bN*�P�����r:��0�`_�M��AtR�YM
�M���������%���L���%84n�O�z�.��)Y����n�I��h	De#}��CIX����������im!�C��V��9!=�9_���cx�7n�~.���9f\?�t��n����O�a�LN��l��N��X��'�^�����<�z��{�{qC��B�!3c�X���l�B�R�8�{��i���xb�^*�����n�d�[�\`u��!N�Z%��:���z��-�[��N����=�g�6��EY 47�h�����M�wy�����:;�������O��*vn�#����]�x��,���T�Vl^���������7Z���0��=���4���[tk���au���A�
`wF�5�	��w��+�l���O��L�G�"<;m_v�^���`{���^��	�p{�B��
 �������l� �wQ	��3�G��1����������P6�.R��Q�):���{#=%�!��D�9��E���j�kv
|�������@�S�=c�����q��������=6�%�'��{~C
��O4	&�
��o���
����/��K�';g���Q(dW��i��?�u��\�����4���eElGHA(o������q�W�����(:lTk�a��hJ��f�y�����y�Q���t+�����&��/���fb���r�T�'��+^���HD�o���dU��K�k2��B[�*d�{�z�x��T<z5[��4���c�������']
2��<K �PIt����tyw�f���S��

���B
I6�a��GPE�M��;�7?�>�����s��c�)�w%���g���c�������&��5�'��3W����28pb��t����P��.\(#~N��$�7D�
����_R^K�[�1��0��	15�X�

C�(�����	������>JV�t����_t�T��m����^n���(����d���AF����X!��/�u���WE��|��HjV��}���K����u����1t�\����K'lD�A��k�D���EgV�r�����>�9�������Sjx
�Ki���xi�����.OeE�2s����q�9��������i��J��hY�yc�E�����tA!��<)C�^PKy)�?WS��j*�^�0����Q����&������!W������2wC�
�n���@s��GHA!������yJ�F��4����I������81�J6(�<�HD��$E>�la��;�����&
�7�G*W�3��h���)#'�����Q/gn��^�W}0bR�gF��_����gR�u��_�
@�7l�3���t��SHo4���ZE[��9��*[@��k�j2��m�*����i#����Oq|�~��	�����FQ��E��tJ�{���`5�:S�ta#��h�P(���4�1D)�l����z���8�[h������s���M��������NK�u�!S!C�5�A&)x��[d2�DQiL��\k�d`�d2�!-F�v����g�YL���g!b��{	�!�A]���9ky(����"��S��X�
0\�(��=�t����{��T��	���������y�y����*��	�E��`���[8�����TQT�� .�D�1�.���8�=�	K4./ ���6o	���{ ����Q[\)za&�����1�Q&"$c�a��=B�3e,���a���m��j�p��"	@q?!0Y����:g!T����Z�Q4\�����1������6���p�
���b���g���:v[G��Z>X�1��n�z
�_(uey��TB�$8���,)�����K�������J��<7.�1����~t�� ��.�z��Y��Z���,�
�H���am�D���!��m��`-�p&�����tv�H�uA0��/�L4d��}�AZ����8�$��Cc'�H����iy����\��H�kV��f���u���������H�����������6J[H65����H�"�o;���!-�;�����A�T�qu#���S������qo ��l��`�1�!MRT�TRi�	c���������)F�������':3;�ud��Xe
x� �\)��WW^I�^�v�3��R�������!0�����4!�i"L��B�	����G�M��Ynu�h��t(�I+
T�ToV��B���4}RL���.���f�����SKO�$��=9���/��S����.���tVl9V��2Bj�UcR��E���4�B*�0	��j2�jX�����"���gM�����q���f�Q_>;!�t����c�tu�Q�Z�+�7��%����G_�O&3��cOX�d�c�:�p��M���9Vgf/(Hz�`
�2���IM��|�Y�[��M�[C�����:+~ec������Pt�:u�c�R]���%����iq���m��7����TV;
r+�����^�^=�-�	��q����A������+��AL�<`:v8�v�q*�'{t+r���Z0��2`�g#�V4��a����&X<��QL�Y]O Y�~g�[�_4����IS����9X��%@����
���5�;���_�M��)���pD$��*[�����[?(��N�Y��'��N�m�� Zam������Qy��B'�&;�m��>������F-�`S|gQKC��g�6�UP�h�M�$� �;�^�Tlu=z�~��(���"c:��Q�x=rB���dm9�uA"tu��_��M
c��������`���&R��T��<����!H%�'�8H$nZz����a��z��
��Z���=�]�I
G�s+���>"�jtQul��I���O��&U���R'_nQ��H]��Bx����}����I��g��.o��L��bT����2Z��^����+-��Md8�A1���A�b>3C�	���-�����R�hu�`�4����r6���E�I�J�	�0�"�}c��?�����4�D'�M�GJ�C������l�X�u)I$��r���)��[n�����0�c�R�����G�S���)-�h\��W�A�����C���4�ev���a/���TW�
�I�M��! ��GSC-�7f���~��a�v,l�9�)k��i�k#wD&�8�����M������U/
��������D�r�;�`��|���� �b����H�a��y��":��������@d�_hYl-�oZV�[�:�RBXw6���
J%����Y�M�V[�=�����^�jD��!v�Q�k�7O�Yr*�?�^�nb��d��:dO1��l�VR�Lu�f�A)��PB���O�;a�UZnFw��,������z�G-u��M��dF%[����2�Ei_2�:������,Nl�g��xm�T�>�V��M���g3���.�6��)O_7���5i�����X~���zX`��on�f�PE��,a�/9�q��3]�4��<3���O�<��DZ�`�L�����ZR^���p�*�A'�8�,c�$�P������pK[�]��EmR����$�DA��&�v���WJ�i
�z?yC�u��R�b��@�@�y����@�d������}[i����������#uk�
����H�M�>�����pX��+�bq8
�Q��+2����`��n���B�O�^/��������i���{-{��Aw�~��k:��s�	$}4�% E����:��A`*����7�

��O���4<��Zug<[��%D�K��ZL��)XM�[�Y*����O�+��������f�P����Q�^��x��e����@���=��C�Ne�c�`�Y����%L��H$�ES&���z{zD�����r��ccir�4�Ba3�����#���:������j��XKv{�B�2U�D����QG�Ll5�7�z���
�~��LL��-n|;5�,�����\w���[sf���3G�Y'CC��l��q�_�
$�9��]^�\�����i��?
�R���gOt���������g\��}�`�+���Q�gz�KQ�$�
����������@Q���?����^G���Zh�8���P�$��[L�q�����N���N���Mg��I��d�vPT�#�T�~A�A�@iB/�h
�I�fNbE�oCF��k5������B����6M����+_��6����dw�*&��3��tFi1�X<
�����t��=�Y���rf��X���2��o�9w��{���:�m�
q���X��I���X�����a�V��b1������Y�n/a�7KX�(�u������b��8�*�B��N��dX�)�Sy�aq���E���()�Bzw�%?�jS����.�|�Gf1��S�c�U1�QN[��%�J&�\�~����U�k�g��d��B���Q"��|	�%	��*��������P����H��j�J#��:���S�-O��L���4����)�f>=�R�l��WG4�)��������@��'�|�h���@$���u�������R�������L������T)�}��JT�7�D
S0y�1�V�mJ���>q�;,
F�rS��0lT�����M��-��Q��j���~����=!
L!!Io ���4Z��M����K��\�W�kO'�F�37��f	�#/�jt�[�	���e�Ga�n8QGNf������Sq����
ie�]��Qn	�2���p����B���������c������M$�$V�T(&�-�M(o,.��Nm���2�������`�����hzO#����2��>1��s���[
'�kh�����m�p��2u+$�5��5���bk��*d�������.���!��'\���q&�O@�9�K. L4	G-�
����)�0��u}NeK ��h/�b���B����L5���J�R.�+B�i��hT*��T��V�n�J�F
Q��F��cD��V�"�\\���������w	���f�=���`�w�8YW������r��l�o���~�~��,)��c���t����s������c~�.��z\������}A��[|z��'v���7�U%S������P��]
���������0���Uu��o����6���/��*�F��?Z�����v��P�)�V�"^]` �����Zg��3�kI�U�=\�i�Xd���bg6i,�Z o;���3��1@�	�5�n���%��0A@:�M��-��8�k�*�R~.��C��]�������n!p���� 5>��������G�
V���0����g����3��m���kN�Spk8��A?�jQyT����s�W�����y��%D���5H<v�C^�<��]���d�BBL�3���a�6"9�Y�&�z���4N�����dz��I$�}��i�����$EK���xbY�]>����.��|3�l_���@�y����_�1��cK���+�q�/��3�Gp?A�c��-��,�KC<�%�-L����p-�������^��uG"�������j�-�A�2x��$��L�$N�%^\-�h���6������e��{v��]�oz';9o�;�eN�����Oe9�V*oE���{��U�${K����o.�H��/`��� �i.w����K�7��w�c ���prk��1�B
M��t�5#��=6
���h�a�`�55:<]t��@��W���������������Bo#&6�����HwCe� �1u1b	u��L��(�]-(����8�`uz"j�I�����%~)���`���p�����Ob9;o.����I��	0~]�}�t|����[�v������MR��-��4�n��a��W� �w��'��B��7�?
'`]~��'��y����3�K�7��8�C�WW��_x$,D:��iQ��%v	��:4�Mg�x�����*��/_/\�U3.W�=i���.6���}}�=�?bQ��?n�A}�T ��0����W������Ir�wVAz�����	8���
���f��!��J��|hkH4��;Co�����V���2��!�'�x��FB�Y-�i�d7�Q?�Ss�)%����B��BR�x8f��y~T{�
���Ozo;��N�+�1�G���F����dm>�Z����3�h�4"�C��D--8�(�%b�)���D�f�U- ��i��Un��#�����Z}b�������6�N�4�3(�������
��~�����j=�.�K��N8��<�Y^�\��nnZ��:o���N��Zo��������2~�Vo��8�a�rA%.g,���>�~no9@�1� �V����7��;`�6QMN0��7��t�3��q��rl�S�$1�5���\9�d�Y)�
�h���*��>G��),���2R:K�v�UW������bw �C
��%���q����.�$!@��i�*
������aM�5��<��fX4l�mD2@��(�4�(a�d8�0��5I?O.`�#b�I�����y�{�h��?�O���[������hu ��i��}c�T�5[k�u{���;%����u�)>���q�:��Tp�$��`:&g:X�G�3���g(%�q4��x���y8(���r����:�&
 �
��|�w��-&���R�$Y�T���
x��i_�����nQW*���ac�q_
���w�bp	f[�q�%O��hQ�O��Y0Y
�(�-$R>��D.������O���$��{�/����R��?+����[�������� �N���sJ��,n��=���>U|M�p�4>�s��w�S`����/�?�a:�j�a����Y}���gw�%�����ZD��)mp�5�t�_���%X�Wk4�f����` �E��_��6��Z��S�@�W=�UAQ�?��5J�13�y��@����l�������i���:b�_3z�����(��n�!9G;��!��Lp��"p���j���������~���������-������7�����}�>����x���.
bzB��f��n����%:l6����:,�����8��������-g�D�@�\(��~V\[�U�l����l#�aJ�g�^,�[���Lr[Zp�e����e��
�	fxFCi�i�3H�!�F������,sq;��p<����=����y��2g�\"��f�c���F�x���}w'���9���R�����Pj��"�B�O�4
v�!msL��ZsL�R[�~�hP���A}s���o��z��wM��X}���+Ni�:��F���c�q'��N���^@���m-2���1}����F���1!�Mo-�8E������r�RG7
�/� !�eg���HSM"1���T��^o6[�a%�R��U��-8��g� >���� ln�jj�\vF�5V�,���>cE�������������R�z�M���.�����V2V�ma)�r7�����������>Q��Th=V������h�4��^��~J�!�J8�����	ee,(����O{�D����A���)s�jg�&���\Xl�`r��Dv����Iu�<�P��,7��h��b2#�k�~�>�)TP
��\�1���bL����zW�0Ys	$��&l�y<�x�'&�^���,?u�MF�zg5��d�Y"��ao	���@������<���jv������������#��E���}��=N�7{��1�|�g/|�e�z=q?�;5����"�
U^d��%�����,��=����+D(A ��z)M�o�^[�?l8R*�0��;�`Vs�U��U3�|Dau�O�[��k����W)��`���xJ&PI�g@�)b"��7a%��l�7]�fM�����������7hl
���Iao��yM�
S�����B:x���W���z�� �c�}s�������M���h��4�(F�eTD�&��@��$@5a��	������D��\>�i��ll7��
p��,�mS%
!�B21����?2m
tGF�g�a�W1M@� �ii�,���u<����'s�~� ����M�����s�~��X0�hq@qZ9d�3R���)�j~�a�grY�SVk���&_�rJ}�N�n�:�~4W�"�O��R���DTv����}�Cr��v�>�6�'=�WK�"mD�����������zb*�;{i|�D�t^0^�������6����!v+�[�0��*�5���Po:&<��T*�	��$�W@��9�W�r>x��S�X�`���?��H���{�qGMTp��'&�>�Q
��_t��wf-I���|b��]���
oP����'W�������O������w}��i���Y������m+�
��7u(7~?T���m���&\���=&=�[Pa;���ZJ}�o��fD����Z�/]wi��y]�`|��$�#N���J\�v��v�)d���?H
<=h<�t���C1��kZ\/h�����gK�HB�0�;���];"8#���j������8z��J	��A�����|�L���:�T��-�s������������H���3���"�}�9����E�]��8�S������}�A�r|���1���_�<	�������"9%���5���\0��%���.��S���`?��8�UN��V����n��y�p���:{�~R�AH�@� ���ylBF�����5���������E�Wi*t�L���<$N(�x&~�=@������_^(������L����8$x{QkG:�qkgSq&�@s�_�z���I��Z�D���q�7�{y��n$>�����i�/\���#��I\M�4���Y��yo6��?%�����Cu���$��%f%/�^?����0/�-(u���3���p��B��v0m����x�4��;��w���c��3�!�/p��CW���2�+�1,���,
�k��@�M�8J�~�n0{��g���%��.�F�r
��|H�(
,ZS�8�������b�>���^�������$��j�L:�0H���'8Ca��K�.2��@z&���a������pQP�����t���5�Z�[Zt��>-�A���[���k�?�|wo�r��H�*�|^�8�����*(Sacd	��Hjc�7(�>��I]��[�V�h��{[��yp�c��YfW��FD���T$x�/��"�vj8���i8G��g�77����'����XU�@M�5w���
v�"H�_u'3�*_������zsV�����i�y����jE�]������+�	�r�JB)%+P~I�����w�t��\QJ���}�^��I���V�A����;wDy��o�~����~p*"	j1�E���
��<r>,� �\l�a(Rs�
�MH���E0�x�of��O��*��M*�XR�!�|��]Z����D ��=���%�8�����x>�e�$]������>	���_�k�X_��j�}	Y�@KL	�b�+P!>Y�#9��i(0������dV��&���s�!����a�4��yx�<�U��U����������o��Oo�<��������Q58����o��mhSA���y���n�&�.����9�X���J�PWvT�c;�Q"����'��@�5�
�P�)�K��8!T���[#�gM�����5����.|�������������Q������{���wb����|�W�^�X �#�di����(����lX\���m`8K�%]���� h�<��=x��l�����n�@���%�3�l�K�b�]�F�m��	��H����Q�	���?D�t��W\��i�n�9�aM��IX��}MH��BB�o��
�����p��_�ER�����v��$��e�x�Y� %Z#�=�V��I:"�G:���)����A�J;�!���������jjE�ly��]��Vu����&��)2�j���1���tyb���`��ou/!J�{�s�s�N�(
�$0<��n}�=��rK�!�MTVk���.�t�O�#���;z��x���c���;�8�T����Y��3���?!W�`9����y>���l�������N���k\���#��M�F>�,A�%^/����GC2�\�?�o���P���I�a�O�
��I��y�������������e^Q���=9Sg�D}+�S3���z�2��������5��������N�K,�	B��FY!����]g3j:���I�n��e[��W'�vW��?��V�4���(��zaa���I4����-�����x��H1c�t"ee4���Lt�^w��$��`sg�1�o��d<�����q9rb(�`��0���3_�������Np��L�9�����X/����S��`Y`B�Y<P'�]�eKi�rf���M��DA���{!�v��!0�\e2^�i�U�vT������.��g6����P>0�WGu��Bi��l��@	�v��P���}k:���@ns���������[���[�;fa��fH���{H����mz]��s�'� H"�G��n�]�R[���7���������:H��:���	��&��*���1����43�����%M��/���"Bp��Tls����f�J=jpx����C�8i?F��w6@9bk�]IVN�|P##�5�yz�5�	����N���Z�&o����lZ�=��'���GH�+�����a8U��z�X�6���hTf��m4p��8�Z��Q�Y�:�B���Y��Kyy:��S*�nT��Dmq����&!��.z)���8)�����ME���v�]qs�t��J���)I!�q��!��� f<D�?FlC1�p�*��o:%&���@�����D�	HI@���7�P��.,&��^<||�������9���I��D����L#���c��D�k��a����J��Y���r={z�J�=O!J��yL��F
|bxDfY�O�j�����~5������X00��>��;�5�:&�dq#��5v�I8�bY�G��I�!�^�EIN�7�L�A.C?{�]D����,�y��3r��-�v�*Ye��is�l����x���O��s�p��1m�h��v��xD��R��[-QR��[�����M����i8�*[��~c"*�%����
�;L�BxlCH?`D&Aw/h4u��R�7��h����(n� v����n?��S��"��l�K�R>�v6D�f�	Q�Q
�����O�|E%�T�V	��Q��G�A�V��Fk��m4��z������Z�_kRnX�����d6��<0�Zy�;�p����]q����me'e!R��;��x�n3C�u����`��j���J����&�u�z�m��y��"��e����rs$$����V�K����mmXG.E\������Tk�R|��=��I�����8��<F����!��S�eNo�=P��C`�mM��HzG4��(�
IZEx��a+DR��P�[q�3��0���t	�DIj��e$��h(V�4:l4����_���k�K �o-�z�r�������w(��1��0s����0�.���J!���x�\��O�pd��D�+p��(S�YzL�w�a��+���G�!�+"r��.f��0��x�(�O�$�Ex����aiP��� x��|���J�q������)FyN()Z�s���I�^������&�/f�D�B�������{��z��e4�DT=�L��0}��C'��=�!zT^ ��"l&_=���D{,��k/x�CP�S'��F�(�V		
�/��$C�#�x�M�
�B�������<1�j5<,�����v�z88,�-���FCY+i���GU�kX-���|5��@0pN��N��t	��9�4�rzi4�m��������o����7'�@o�V�6Z�
�7W���dCIp�Dw�x��+J��q��x����H������Y�wq�Z7|���=|'�3P�Og�W���;�"5f��#����+n�$����9Z�)^�~�
���k�,w��m��s7��pA�����6�t��x��"u�M(���2uc�5��-)����j���R�7�e��,5"!�F[m�T�kwi�4l���`���/"v�"��N��u���KR@���3w��:����o;9���a���r���!�8���G�N�J�s�r�(�adD�A<dT��-�<�,��"�������Ad7=�G%u���N� '�A����H�[��N�|k�\P��^Pn�]�t��5R��]��m]\w�����#>�� Y�'Xp@�{�|6�������;���!����ip���D�A~Z]i0�&%��,��P�6J���D�T��h��r�&�TOL���� .��7��Gl
q����H���v���G����l��
�X�nV�I�J��
3��2��3{��zQ�:O����v���%�:��������*n+��NK������I��5T�[���`0�����P�I�~�R�U*�5���-��a�:H�je�"��	�-8y��^]��G�4`� �~3�D�I����|6�o
y?���m�w���J�0��=�l
�����&��]{�S {�"��*v�iE�t�
D�x�������h7LT������8��-��&�#�3�9�lTt���%��;s�������5�!��ZC�GPl�`��4�������x�0�L����Lv��a�Rn��b�T�G
�oC�8�E��~��4�#�9�u'�qn�*o&Akv$s����������d\���<�~���]2Ol�^�.9&����L�/�o9B���c5l������F����(�DF���E7�
��w.[�RDy��	����g�����-�������c�ex�V�Q��\����bq4��5�Vj�2�jFk�]�Qum5����h��	�{c��Y��)x�>�@��Vv��[5��2���o������]=+u�,X'�S�cI�<��	�\�%��G,��Z�����+�b���9���m�
�@S�X[�y�b�I��CA
]��6���"^��aq`]#��]���;�L��2����y��{z���}��e��ai%��5�Z��v�\J�K
=t�?���,���w��[,�3�������Wn�p��(2P��N�O-���b"��OB�!�p�1d[���I�@yUVI4Z�/^��G�q�K�,K}�����@�B=zN�tv*&����g��~������OY��%c1�p5m�WH�=Q;(���{�1����$�$�f����U�������r_����J�r�o4}y$2[���T6������!4�]�6���:�H9��1@�X#�|qf�?Ylo��
�>$�� ��$�2��.^����	Fx�?�h8+�-P��+p���4&a�a���
 ��?xy��B����[Y���������I��c?(��?k#�b������|��������(���f��X�F�RX/5K^�cfK���)���Q!�!?�/q���
�r�8��7��VV�g�%���n��8n������7P��WG;N�|����J������_����"O���}A��[:�����+�:��yw��=j�hm�j��=��o;�;�;i����6�}T����y4��V����G�&R�2{CH�p�Nj���id�{��NVb^F1&��r�=����h�2������E������&A
��N�zz���`g����B~ao�����-?��-Po��j�<,����2U����n.�4��P"'~Hv���u�x_��tN�^�
�_8���)rJ�S\`�b�>���x~�-H��-��$
�ZuM@{v���.�i�8�i��l~����m�����t�� �]�C#5��R�	�9n��������������cf�#]�Q	G�A7��k��S����mw��;�'<�@���H4��������?����_�x{�=���xv1�zk���/�����M���uM_����|�3�/��A�?��3��������A���'~�;�����m��7��u�>*��h�n�~���w�hk@��j]7�mM-?����M����r���On�~���w�����V�
�;L��N����_���kF��U���g������m<����i{���(|�J���tSE������%��D�����tr��(��$�������U������I&~�E��N�:��V�7���6��0��u�����nnU_aD����-�twa���x��#�Gx���f�VQ�?�szQ�
�H�[�����v����w�N�Px�I�����T�=|�^�j���R�}�Z&�*Y��M9�<??����I
�m-�����7�MWg�����cS�Kr�#T�����FW��BY����\�O����K���b��W�
�W~�mv=��;=>:;��KP�.��G�N��|*`1p���M'������gU�zZ:R��������N����5)��n��S����������Pq?��2*��SI�m�����T������{�JTUq'_��*������;���WY��Vr�EV�����J���Qj%W�7���p�oF��@��������(��|*��;��+=)��������ky�i��D�lV6+�4�����y-���Z��z���n4;�ZAc�P��1.�b����_r���v*�k|���A%a,p�	G�w{�B�����W%	-�4�=��U,~i���|���#��J���M%�������u�?�%����6z���nSi(���������Je��_Y�Y�����=�v���V����Z�'V���d����P�[p��|��?�{�)\�%������F����N��n�;����~���� .C�C����74�[�%w�������xr�~~�
��jB�x�yH��VY�����c�w�D�sLP@��E�&�-`�������R@IP���h��o[�]x���UO��Q��
����d�"�r��H#nvi��*�PE e��8�|��n��~�=���/���#o��iy���F^v�=�
7M��/J�n9��W]A���d��2��|�������'����s�$'�6��P@8T?�� s�#��<�Rqtc>�;�������n�l��OR�;+|�����Ev��VKj�iFu��ws�r*N��U�
Xw�����v4wZ������Z��qr������~��et��O'm�C���x��i�F'�����Y������X'�~�tEH
P8��Ke�L&��s��3u0�m���R�`!)B5i�M���]� -Q�\���gG����[,��-stuu������;(}���1�������GJW�?=�������S�9�2��@#����G�7����N����a�>]<�9}szvz�����o��'��|��L����*}�^�j��HsO���X���#qb����A�3]o�E^��.U���C���g�?;����=��Q��b��r6�zQ02�����q��C�\Nb6�

�T4C/
&�l����}��W1�al{[,���>��U��CU�t�����T����f�WB
��F������V��rT��zO:/|r����x����-��v�vv(`��,�����m��R��7T�%OoW�w��iR�J~ 57�Z��&��	�<��zE���fL~1G�����)�eY��g�5\�Mh1��R����.�1
����V�g���

wd�_��7���9�w���������W�7 �NNo���O�����)��2�5�Jto�N�:a���Cq�����l��I9���L���{L���5�&1��"_����������I�t�%$����(�%X�@���n$����*0=�������Qe����8qd�aa^4��q��gL����$t���$WLt�������8}
�����;F"7f���/E"��eJz�F�D����|G^>����)�S'��h ��^�>�Z#�W��#�9qlw��	�S�V)���H��%wEq�K/���v/���e!a_9�\�b�"�E��'��+D*��u���R;C��_��p>�S��+F��rQ�8�-�6?M�yB~���LZ�H�8�`z����_e>,\�,���L��):�ey�IZ!<��*���9|�^����r\�=����703h��Nz��=�h��+u9��G����*�c��'�Q�9��pC��Q���&n�GJY[�`�K%�RE�N5�������:%Kv��	9��*�������I�'��'�����W�:;:?\��S?>=�]�axyq���S�#���vp�?\|�� Yk���-5�/A����'����{���RU�x�#�VY�b�/,�maH���
�a2~9C��b�����~����_�^�\��hJc���.e����������	9Om�\�-��Z`��lY$f�,�C��B���F%�Q��,PA��Va,2��G)~��.)rR�#�kbD�4���#��ShK/N��@cN�&�R��t�;����U�����?������g����k)�K�����d_�Q����)Mz���.$o
�$� ��}�6�,����	���3�6S�Z�~!��L����V�
�
mt� ����4�� ����pZ������zs��7�v}=��V
Q��g�&�Fh��M�s�w������'r��*K��zL\���}�]M�J����q���%�X�g���XpS6���eb"!<�\!9�S�{a�������xKF~-$�(<D2���6zNc4����F�f����z��D�^	�z����?�$
N"J]���M����������m�H����npf3���lD��h����z������������<��)Q"g��M����Sd���]�8�g9V�u����9z�*DO��L�i����P����%�$��tFG=��C��+�%~�*�)S���F���a���<D�	��w#`1�_���(��el�Q������"G��d���^c#�1�+�#� TC��_1�)���q�]�X�����JW�PGSK(�� ��,��
�a����"u��9���e���]��x���~��h�������a� �{��)�=Kc.���3�e��
cY��BU9s2G��F��s���Q
�.4��Y���
�,��)�/��`n\D�����x�L�Fq�8�p���|f��i@�89.��-��m���6<b3h;�9�����^~���p�V���X�����zwrv�x���A�F�&��W�A���1�h�X'@��S��12�"��
��7�N{��S�!��V�����4��eEn1��#��6�9��X��`��0�2��)"SS�~��w8�HB�e��8�$��5�T�i��T5*]����\Za������o0���T����;����MQ�qvV ������(���|�*�nJzHdru��%uip1���:������F�V�;�Q���v��b���-���"�aR���"d�N:&���+*ftq���e��CM��������|	h?:��jv��c�������>j2wwq�Ifk�ceW���M����v�6�x
����x_u�M��(TX�����3�9�q$���1]x�nH	�N��w�|��
��B��N����^���T��t2,���(��������&/�����u�9F���o���9a% aK6.Nq#���&���7V�M��qg�j�z��t%��f��o��K���:�KC�0�8��#��J��k���\���"/�F����@%��*����P��"��-3��s2I�Yg�2o����V"�����;��������S�0���a0�$�����(0\�4*X�%ecI���@������,.fz��B ��K/\$9���O�B�:��s���8��E�9`��U�
�����e�v���3����������|\fc�b7�~c�����6�����*��75���.�-pI
H����~vK��J|�aS���	���m��������n�-z����,I�*�6��7�D�wF��ql'��0&e;l��61��A�)6��7��k@��r4�),9����a�x�~��k?�gp��t����1?W'1��S��
|2��aQ�#`<A�J����])O���+��D�5��LCm&p������z8����������6.�QX>��U��%�����1�%I�TR���?1;U�9���%3*`d%1`-)��M
��}���7
RG;�X�vE�Sn3�������eSm��/������c,���{`���$}?����`�72�,���2,@7U�wR�������lTW���M��b�V|F��v�z)���K5a�2��77�X�<��R�g����8�'d�\��u>���92A���j9����3~cr���A�.9|1�����A�.��M#��������>�`�p�0.8���D�G(�$��RYq�;C�Q���J�}?��aL4`��K�@����]�h��6X�y�U1�P3Ea���GF(�@s*�����K|�a�AL
[����J�&/o2��>�4�����4UR�r�8�B���8���b:�B�:#�lf��(i\�1\����l�S8�����e`q#����Z"��H��vR�@ty��$���<:+&Vhr(uU��a�z^"nQ}���H2G�qc7C����x]j��x�$���d	���4�[�����R�}��O����J�p#������Z�f���Q��v�tt56��+�T�1HXdG��b�@p��p�h����h������yc������1����;!	���m��a,M7S�
�4|������@�+�]�:��Y1��8���4�z���R)�pBmaN7����v����jS����,�f?��<J%�%rz�Z��
����X0�����_��O���N")k��3��m�2�c�Q<&�����N#s�kkP���2��'�UXU�����`�c&g�y�d�,�1�<��]��hw�my�<�\��r�
��������t�93�#�K>f+���_�&���������K�/= w��D\�������������0yzZ��l��������5FI$�������Z��������L<"�����eVo����p,��9'x�*��kD��T�JpN�E�aS&*%�|��S�)���N8+��a�lE.�Y7��c/5[������)�Q���-o�/"�-����1��Fg!pbw��6GDkU�=��N���S�^%)����K�o���v��V=#�-*�_O���?[2A��7�������MUz�jO2�I]m��*�Yg����Q����n�sm�;��D�I5��BS���MV����^��w���=z7��������Y�X�����I�(�(�c�(sz�*|F=y5�y,�#�J�3�����������������U���Y��Z�,�g�f���`�)W ��H�L�����a�`���=B+��b���{����H�R\�����mI|hi��J����X��;���z�c����(����w�@~�|x�6�l����[�|K/"��T�"�n�&_���zm�(������3/�\JQu2x{��������/O(��v�����R=T�Ad%�����n�Sa�*��1Vy���"��"��+���,�~lc`.!�^2w�&q���_��#\�x5�����
�Z�gE�Ae�"Z�"Z~��������w'��=�7{���JLCR�#Z)��$9�n�N"Z9(W�����'����<���
Z��|qplEX����g������d��VS,�_h:�z�`�������h�?8=��v6d��"f�x���K���2m��{�9��C/a+�]�f�k����������0���'��^o��\��e��[��x�d?�t�p"��rk'��Q�#n�g��������[��h[�f�����z�����v��U�M�'e��/��rPl2)y�����?�?�&Z@���M���0��Vk������h�'}��u{����n��s�M7��4�V�����l�~c3/��fNh��RZ�"�U��������$��v���^X?yn��`�ChSdw_��/�6���z}�fE���������pM6����8�pj����p�ZpK�����K�U��'�eWDT�|������7�*G+��29��k�z�����u�Y�'�E+�U�I�_X�Q�_J�������b����=�����c���m}j�,[��_�gv����������\-~
o���[uZ@��xa�ZfZp����zptr>��_�����5�����g�TC�Z���v�W���!�[��p�~���} u�	C�T��!�\3�����9S�)��������=qZ�F�%��R\z�����.���HC�Z����	hV@l-����?Bi��JA�����
���3��f�~b~�<���Fd1���*�SH��;����7�[U��z$E��&��LN-�G���gpQ���5���>I�b
�G)�H��$�c���
^l��������a�sg����DS@y����}�L4�
=�shDr�]����RN)]� �qL�d<a��>S�F�bx]����w��(a�Kg����^U���C��
y��xG����n�����el_��8!�������R�+��4�o�ErRg@%�����`�\����]�1�����sIXc�d������i��*�PsCU�7���DR��K�*�
i�h�2����C�1V�� �I���>	�EP!�	
q����@�C��U�di$�r,��������G�^�
����~"��������{��;R��UbL8��1���D|D��{zwt�V��E5�����[�YsA��`�FMn��`uwo�@� �B���-�U�G�����;������;!$���\��ST1X��VU��]-�D6��a��=a�C���b
@igy�Y�"3�����V����sFg���;t��I�L����<�<5b5Z�LT^�����lJg�c������>*hM��0�Sw�| ?qVI��&�K���������1z@H1��g.�;=��)<���;�:�"����'qa�������EZ�O�����L���y�GF1IQ=�G����2��%�Y��2�F: �-�dd������a�p�s���9�p*�[U\�a�_����>Ce����Q�D0u������"5jU����{����;<�p<t:v��{��3��gJ��!n�M��c���08B����#�	�n�e�`��P F�j�b���M�^zP�*A� O5��,>�}�������l�8��z0�G|����|Pb:�X��^�:h����}�L��t/
��&o��E�(!��{������+���sF��e���i�H�nNlC��THEh��:3��Ul���T[����d��<�{���\����Ht[��r
�5�1�y��d�=���	�Q6P���d�:��ty������,���9<��3%��0>]ruh38���<$��^����n@Q[���1�J�`�Z�pLxl�H��b���I�w���i���r�$�M�&[��4���{o��$,�g`�B"�F�X�(
�U�L���&�1�������sO���Ce�F�$�=�V� ��V:P��cM����4��������|b0;�4�1�kp�
7T^�8]<h�0��O$�� c�Q��}�GK������C�@,4�7;�����1D)�
��:��.i�.���.����h ���hP�U��SVc��I��	3JL��d����4��|h8P�����f������:B:f5����s��]�d
|�\�����*����g��aC��$���G�������`�W�N�n��)�8���`�Z7�}��&_k�O��\�!aJ�sm�gz�t�l��U�{.�0�'v�Y��$��WVZ�������12��������`�,�BpG������
 �H����CQ�rb�X~ tQ�]+�<�(r��{��D�$f~�H]�&���{����yJ�Ic$���tV�2|�M��=���3C>�v
2�V����#� \�`fS���M�c�9�`0������q��j6(�r�efS$��PL�T���@P/6
��Gx���'-���<"�hM/�l-���0e�)�UM��p��(����4+�<��
����v���@�@[�gD*�$l���	a��L�4f&�t"�<���E*\QDh�"V5\���>R��aH��ox#��2p2O�?�y.n���%�������f:�;���D�
��V�x}��@���qN&��9����D�����~%X���?���8�%�g�~>Y83&�$��~��~��d35K&��������c^�e-8z�xA�����"���J��W
	Hx25�P��
�(1�;����
�V�v@�na5&���jP��"#�;D	[��
R���������c�n #u��XL��k��48�]���3'`J�P�s}�-6n������5A;�E�y>f9F���D���T/�7��7I&��VL�Fw+�0jC��/\��z�������BJ����S%�
$��:������)tF=�����L}_���1b���a,-�#���������R� �7�&>a��0�a�\"^��K�����5gu�b�KR�{
���7�`3\�`�>�W�V�{�d��V�������^%�b�����Gz��M	v�a�\M+�]H������.>�%>)}��#��:�Ge�����QeH���SV(��^�	!k�W5o$KDv�S|��-x���� �������H��r�E�{]�Bdd\��(+>XJC����%�m$�t�bK@iY��Q�q��H5��e�G04��~S��B8^�!������g!�<�e�een��%�<&�F���3A���Ae��j}���77i5�ZQf���xG�N���WP�T��A�i#�q�W���"�����h-�[|��dDE���i�#�&�	z.���0��'�IC8�3�"^�~fQ��c�e���uu���$+ds�&�I����=a9L(H,�(��\d"�t��A6T��#��������	���B�a$'����.b"��!��>�25Sn$�Qz>�Q�1��:�tO�"^B�;���^��O��@�S�o��
@�@�aGz�m�B/c"K�.���pJ�������!�v�����ts'��7�!��Q��r1
�	�Q|�0X�����#�2V��A��i�=��d���%kN$-V�=�t�`y�_��y��VB�+H�>4P�V�����k
T#�[�W#J���J!���:�4�=P(
 !�����`���[j��{�Zy2�	����K]�m��Jj��
F�J���k�'����;�Z�=����v����m�������"�z���u��e��
d�,,;�a	��A���(�w����C�*G$��?�@XR�O��Rm�Y�g��s���$�y�nD1^���]���lt��H���G�����W(s�!���GL�=��Yb�a�OD�-��6?��(���/���^�y$�P�	�4o��{��H���b�C�v����q��^�G;���01d�hC$=HL�#c���?`�#����2�"�k�pg���.EW��B��)������5������| w�yE"k�Z��w��1�p��!�.E��Y`_�]�L��	�����2���`�E��(*"F ��d5e����@��E��n��X�L)���VO������:2�����At"����D�de�q`������H�h���fq��2"H�~>�p���(��XT��F,=��xW�r�������C�b)�D��
�p:�cx������(����}�9F*�U-�\ Wx��G-_���F#�ff�p%1O�V�B�(��#G�c	��l��xek �W����u�y���qDX�/_����b{pJ��ow�
J���vvD������w0�����z����^${yIBl �����$��������-u��=��������C���8 �"~GO����LH������~_@��{��u���1e;(�:�;;tC{!���9�Ob�0I��d�
z����1 �����|c��">���������S���aN���P�K��)�����GhwT��������0�^��It��Y��!��	�r��~8qI|m.�I��%_e9�a�����
��_�����@����%7�8�j�������_�������
��G�����e�����k����v#e��n��e����#��n�>n�]o�qz����������Z��c�{���u[���Q��\,}��#�
����Z-�?��@�E����)rhh7Fs*r�DY�����y�����5��u�����&�|����>�|�$]zos|A\��*wX&�v�����5��j�/i��o���_@��,�����\�W`��k���(�!�3t\���k5��v�N���
�8*|���4{M���
��W�&�f�s�7)S(l��	��)�u�&�D�la"R������B����������EM��F�!<gB �������{�������CL5T�*��R�$[>������|l���gw�1��������L��7}�U.� m�����I4��.Hc�7V���?p��W��cO7C�Om�G�+�3F�I���+Q���j��q�U#���lWF�������W����V��W������?�]��	��2��9V*M'H-��h_E��~Z�7����z{����HJ��Y:�!R�!���g$:�2��0fl�����9�j`]e���Uq��'�hcc>�!~� B�7��������P�x����
�
.��an)�=n&�E5Vt�8�����d�X:�)*�xP��5be�O�
0�9eU�H�����ejY�
^�������5�$�	�;�������A6"��j��
9�C�����F����)�^�<��-�?� ��x#j���q���>1x����1���������N,yl������x�r�~��x�[
E��b�(���(p�>�CUb(%~�wA)���bf���9s��UcN+�d�t�������f}�6;=�VkL���7�I���M7�s���P��V�\�`���m-tj����U������>o���+�������f>��UM�@�{|
��V~.����_
N��+�K@[��{xuh����rj��������}�������*�����)�*Z�V5�gT���=>7�g�B�E�U|������(���~v�'dC�4n!5nY��]�,�Z�4_�/����I(��L0�N�Ym�����:�Pc���[ID��xB|���_�`������4����E�1:%���mG������$j��|r��0g����Q�,J)X	�R:�Z��/���|��8���l�0E����%Z��*_{�E��f�����87(���-��J��c�J���������o��,gj�����tt�&"�f�E��f*��#����S�#�>���C&��mJ�_��]R���0����0fH�
�d>`Y]��@�~z?����]�8�;��L����6��_I����$�2��������[g��������	����1`T���gM�j���O���#���j6/��1�\��//}
�Yp��w���o��h�D
����z����<?Ow'U��|W��R��A~�M�������������4�t�WGQ�����xI�1��v������!��o�)w����Rm�����3�$_m�b��wq��c�{����/����|�9���P����{���W8A�����/���Q��e���l��������i�U�&6�N���26�	�:����b���`B#\�a�464���Di0��f��f&�@��%P�,%�^l���;�67
s���3�Q,X8Y�N�a'�
v�.?C����-n{
,E������������K�D��-����a#�1��HEw��:����8�+�������/���6!������r�[��@������q�jK�-�YFQ��J�b;�d_�T^t��;��f�����&�B�B4r���F!��P�F�:�IS������mT�N�r�����qs2j�j����u������#�K�'���d?|4Hv
������j�m��x���C�m.�!	A��E�|K,}SxL�#������:�O��f49��4���X^����<8������^�)�#$��m/5��������]�P~l����T�<��%>�������cQ�0bd�f��+j���W:�[zFXR`\���&����iI�Q6��%5j�[�d�0�b8���,s2�YNl4FI���u\E��{Xq�e�p�LC]�Hd��0���� ��5mN5>�hT�%���'�cv��P����:���c�����H������A�B�����w�X�Uhbdt:��z�:�����!�hhj����[D]^q�$�����?����b����8x*��;�Wa�`.p�I��8��} �S�8��:�Pl�Pl:�������2��H�	���	aT�>�T�D"��7\�g�����f�6K��B�)�f����O�!���(�#a���ki#�9��/)'��H\���2.���p8`	a�P��Yq{p�64��t��j�B��!�G���Jg�i�����/����slm~��H�#!X$�UH4�zKN�A�y=�'~�'��r:����`6�$�����R!e��������2)�DE07h�
���j������eSU^��FE��}��7Q/���'�wVH���]L��R��H3��
o�;��.��#��8��*����H,������@2������L#OtI�����=�,5A�R��p=��*�7����M�q��;V��k;�'�G�1
H8tK�B�����PjK�p���qz$����~�K���9����{���
M�?c@�����j(A����m���P�5�^�H�$/yjI>r��*�~��A��3���\�t�.����)�ku$:��}?J�IgHq����="�z�aD^dW�"'*w���SM�������_���n���
<8u"~t��@���w�:w~��R�o��ju[)��f������o����'v�n�
����Gu�7�u�����:]�����^������d��3�����^/0��������n
�
�[1x���22ZM�!�B$2���������
N������AU,��4�
7��X��s����y��2��x\���&���J<�*W�N.6��O�j<�=����!E�_�U��XD!p1���\�d��`�5!�t�Y\NXo
�����
��a��B����;.��g��o
����n�n[���<���2��$��3R������{I>�1��3B��]1��

�A�G%����D��0#���=���j�O�a�����������W�?�f�������2����������	�������7���>Nu�����y����~��>5��<��7?���������c�S�z�O�
�����0�����$��jY-k��j5*xp���5����������;V�Y�%�%�$����6��9����j���/@��2�C��3��>��R���	}��?�\�;�����3u�Z��Z��l�_�yP%s���'o�v�RKT^�����:P����,��3�Nsm�Xy�����J;������Gf��[WQ����-����+z(��pA�����Lpm��.����G���s��tI�z���j5�S�N��6��m��0��*B���1[�J$��x�k�A
������Yb�s�YW7��a��lg��P���
J]]��X��.������(z+�#sq����|����M�K�KvOR�)L|OB`/���`�H�����]��j�,�KJgI!T��9��v�2���~�^!����o��e�N�q�q���h�>��z��]����;:����N��8b����U^SV��y�s���JW�����������,���������.=�j9U�Z����t��BU������3c5[��Oa���_����x�+"g��%���V�n��i��uD�h]	�p�7�:�n�m�j5��q�v��6��*w][��]W
5��*]�Jv���'���g�N�~���d�X�����E���`zQk����\f:@�h�q��9��O�^�iw_���*�mP��f\`MVN�F��T���jh�#��K����u������dps�y�����%�H�-1*��?��!�_z�0-y!�,��d�!eA-�]9h��n7U5
6����;��v{�f{��j-���5��Fg�hL�G�h<d(Qma1�Y�=8|��ZP��mj�T-8v0O
�G��j�J�������Y(^�����HU�|�:���s�k���������_�N�3��i��,� ������������O������G���uut}tv68�n��^������d��1�
oT�.5�O?��G7p�����\��gpVe�ZW�;~��&���<p!�^��b�^|`(|��8�4<���^��(�h��r$,����%!����}FDq��6���h12�YI�M������9q���i���������� z����3W1�8����"�-��c�[��/�0�P,��.(us���v_}xsvz��	(QKA6<���������;^�Mj5�g;^{���O�C��-����XSr��B����v��O�k�{�3*�%OT���~�@��C�*���=&��y��~�����A�=�xx���>ZG�F�=�jd1I0����+�	{�ER��*�������'���^���v���Z�q�	{�[�bu��vhf�K�(T��,M�?�O*#������o�������W�oo���C@��;�u%�T?N���F}]QU�B�g(d���;<����=�o/�l!���i�w���a�G�T��\[� �g�>�10c�a�_J�P�j�%^o�'z�����G���wH���P�%T8�mv�����]w*��s����s'�BD�;;��)��4
���U�rE���{V�j=S�|V�����������I�U�v�jp�3c��!����(�j<*;1��ZmfFO����J�1 l=�VN_kt�����[;S�k�LZ�������i�*���'�$�����5�`���9���z�����\^�}���Y"��D�m=�~�~�v���S)F����%����vti��Y�Z)���M�Q��a��Wh2�>������H_��=uc6�M��o��������q�v���3��7��{w�||���8"�/��k��x-:l��,�y?����iA������:w����V0Y�d�%>��r��V6�����b\Z]�Gy���%�����l��w�v���$��/�?���x��{��"�>�Vs�S��Q��"�~�0�F����&K<I����n�������;���!��
��~i`�K��8u$N�h5������Y�t5:�uV����Z������XbI�f�������G���Qy�1m\=1�xT��Q�6jVn���6���P<�V�x���n����=+���ZN�����R�0;":�o�`Z��\��qt�4��������������[L$�q�q|�cL�'�i���J���I��\����
��<"aJ��1�V������f��JS�2��k�(���9v3�u%D�1q&m��h��q������k��V�o`N[9�9��.l���m4Ij��T�����^��j�j�X�ENro���������(�yN:=�3f^�����?~C�����'5����a�8~�Q��a��h�C�r&���0�K�	�h�R*R��1iu����(6�������vq�����`�Jy�����?��~?\z�f�bH�P���YpwMJ�����y'����)��s]�<R����=���g8�����N�������U�������]�����G�)�W��n�������z�w�U%
BQ�O��6�$�����Kk
�r�%QDy-�:���i�:n���G�I���;����l���X�F��}�������dE��s2:S����g�3�Z���YKLu>�~�U�|�/]�VZ��8r]t���:����,�}nXb�n��z|�W��*����{u:����^XM��9��ko����A���s��s�!}��9%�
=����aN��ws���������Ux`�u���*����k`�'��������q
4�����C�O$���U-�G��,Ro������($p���Fn���;�Z�9�{���n���7h�����Q[�}��0B�>�,��v�=7�k����/Cz;�sxF�6�cb@����&��O�e&�Vix�%LQ���NU�(:�M�����U�H)���g7<�n�Q;����P�d8|H'��`�(�^[j�����[���G�������p^���_�9^\^���C�u�u�@��������zpv����U2.%W��"c��q�������x�B���0V�
u|u�Y7<�<��p�������{"i�)��j5�C���@|%�]2�~#)'8��9�)F#��$���UBjP��CU{�8��!_��������~��}z��1�������'�0�y'��D�(����	#�a��r���{/��l�q��-�)��M.��$"����fS�����u����q<�y��?�R��������2����G?�����Y��ATIO[N��Ld�g�]��M�3R7��m����=���	�9��p�b��~�J����5S�8���������//o�o��pqi@���>��<�������u�����j�X9�f�vjz�lY?��;��
��\8YyD[��@�(:p�2�#[���#z2~+��G��"�;n=A��>�=M�7l��p��=��
��LI8������!:�
��!��:G%��<o*P*��_>�9�u�+��1��9����0�P�f�Y�QmJ�hjYc�M�Tf�@�cqqz��:
o����r��?n�u��q��ov�G~��������+(�{�%��m�M�O��B�p��\�:
��t�����'(N@D���(Y�m"�U]��%`,7l�� �*�H�p����d�v��s��A����������	cxts3���^^���D����{���o������[,���H�m�9���Qq�H���F��t������x$��*I�J{{�X�E;����V~r���lx�E�4��/������R��a�M�|f��a�����l��
�7�,���q�������N���U��[n�������sZ���IZN�s!aF]��������1h`��^���6g���|�d��Xh:�C�a$�*:����+48R����������{y��"�NW����l���>��w��h�0>���RG&UI��G�l(����9^U��
�g�m��(�V��Q�Z���od�P�����s��=���F��C���!�s����$����I��UW���?��O�h���m����Z��:jF���\��I
���������f9
�q�i�N}4��j�F���[N�[�M6V|�(we���L>?�k�t��+�����I\T����-����`�%SZ?y�c����P�d��t
n{�}Y7$n��~)�r������y�:�6�&VM���F��x��5�X7��(��0$��N��a�Y��������y��y�(#,&
��u�5��_{�2���������d���b�{)������J�kP�'��_G�^F���zR��H��&E��C2?���O��3�LD����oth��3��;�l�K�0�?�*�����|&Vy��F�k�l�ifCm������7�(�*^�=�g����G#J0�Q��;f�B��$����k�H�!���\�yN���
  )H�����
)n�k�K�9��^[�'��aJ�$��(�k�=m�u��[����b���s�����k�����EK�4c��e��E�0�"�����,6��zDk�gb�pl[����b2�PF7��.�����^{�6	M�s�����W��L?�jJ�pX6>$|��H��%j"4�i��*�����gf�d��Qw��Y���d`�b�!QNq�cx�V�c�k�I�?y�����eI�X��$������A�j�� E���O�ri��~'�=��d}��'�s�q��?��/kB��\��1r���a�������c�<�b���]�����1 �� ���.�I�N�*Y��Vl�%�h���d�vH�����#��k���CS���~}<����O�n�9����G%�\�9I���,����3�F�f@��c���_4}��6\�N�����U����`0<�����Zp���fh3�Y5�yKW�L�2�w�H��}�7Q	�I6~@��C��c���
����mc����ros���k6�K	��q����3�j�I�m��z[@���II�ls�����"8C����on����� ^'-�P��A�(z0PGL~�DI�bz�b�����+�}�c��PnT����(��Y*R�6kX�V^����l2e�7�R���w:���4k�v������h����&SR��
�c*�t[��W�H�9���=���w����������@�L?G3���8hl[����v~WR9mF#���)�4�����tj�H�R��������b�Q!�|��9~���EY����O����[���0��:_Uu���R=�_ej����5�S�lj_���|C0�����:��uDHN��\b [N9����
��B��������k	��I�������3�����=#����+V@�����r[��D�����)X9�u������}�AQI�l������?����O����?����lO0:������O3��������'���?����gl\��K	&�6z���������F�f��yL��%���S��x���"R��.zP�;��,���t>��Z(9��G�6��s�`u]q���%3`y�Xtg�����������Z���il+,[�"5svMN��q�#T|�i�������
������D�#��y��c�5;ud�#,Hp=��-���{��b��M������mN�=���d�k	5#,����cR�i�Ac=x�C���b�3�4�@��������43b�B�GZ��w�
8d�����N���'�����u��/�H���6����dC�(�j) 3o��a��]>����o�����.M�6x��n�f�;;q\���G*]R���,#7]����.0^�V����yn�����j���K��[��mP�E���#��&Il��=?G.V����Q��G9A������C��;(��j!�
���S�G���j�<����SV��	���o�s�{Jf ��M����Xe�e�e�II���'_�r�	.����s*�H@��m��K�
����>��s���L�.���]Q�(v��#u>d��'� ��+��3��j��`8|����Ji��J�<�")�	�1#=TZ�������R��"	�������z#b�ai5���c�bH��cxv��PE�bT��c�mpQ�$$������nu������3r�*���K��!�����&���������9��^�����c��i@po������vv&S�.����wo>�>��!�EV`&
s�U�%����x|&0�I����yw~]-l!Ob���A������s����(V�f�����28#7�-E�I�e �TT)~M��fK����wKi(���j�����,q��
�R�y��������
���X1�����k8$]���5���b���P�MC��E��=c|T
���4�C�v��l���P
U���W�w<�a0uQW���K(��9e:h��/+g��j���sb���#Zhi�,/q���Jn���Q����%�����f���<�N��a���v�I��:� ~�Na��D���+~��Y�p�>+�$�\�j��D{5�8�q�v�!C��`L��\�2�DF���N6�Z��"1����Kfmu^s�� gLk�j&��o|6�x
��;��xS{uJ����_o�C��;�c���c�% �<�wF
�_[��d�,I�<j���m��.�/�����B@�:E��&7
ii�������-g�e���|"d��e�p���V�S|sIo��Y,�OC�������G/�Q^��flRm���u��<��;���u���=��M��G#��=��W���p������P����kS��n���u��&�e20�������p�t��}]���I�����N����:��gV�����1�tOjf6��o�qI�v/F�7pI�@�a
r����s1�rMK�\�;��ws�%,4���(�
�r���`�������h��q����G �5��z�:���y�������u��b�G;.����=�89�>yY�4!��&�7�T1�H�<;���I��Q��"]s��Xl�3^D���T����h#>�������)��yzqk���w�4�ObO�D�D�'Q�9��T�����Z��_���!#.��)�7�O;�3W�r��T�mp_�M�vG� Gx��~B���`����S�c��S��Rh ��l��6v]���^_iQ�����y;�2,3S`l@�E,������l���1���\��P'���7�&�KPf.���\�Om^,�(�����m"�(�9�lJ�Z�+���
g�����-#���Y��L���:�x�G���Z�G� E�yoo��1��A2'
C$ya������5Y�����A)T��xI�H�vv.m�"��
�L�����@��J����q�ky]xZ��v���N���w��ms#)�.-��:|���:?��z1�����..��25��[.��,�����E.���cx���C�� r��C�\1��Q�>���������
����d�*�����l��z�Q��UA�j�D�wY}OH%&�������������������'�����j�%�w�
�%�j�H,���17��
F����	�08�7�!�wEe�'�)�>�J����AM�h�3���[��k����l�M�z!�"�]��^k�
��S�
���\@��k����������=� ���3+���T��p��f'��?i.�9���h���%�$���T�*$.��&��	�X��f�L�
.����h��f_E�4��KA����t:���u�Zm�������m�����Fh]f�^Jm���W�%�VmfR]�Z�i&��O|L��G����=������24��?��X�K���n�2@vA�WG���S�k0�X�|zb��}�K�G�M��f�gR�O�#<��)��=Z,���%��+_l~W��E���'����m�������i����wRP����r����lpt���'X/����x�T>���m��Z��5aZQ4�=����n%^Hr����KN1�XN�%+���%Ij��ku��4���Q��Yv���4�kO���_kz�BZ�5��D���2e#������<}�E
k4
�L8+n�;9�Vf|��-�I��z�����Z����d5_�>W�#���O��������"Zk�}��l�@��"���ZqK{�7�)3n��)��cF�����b)Y��
c�
�l��)2>����#�e�CnZ����+/d���x��'_x��y�V�1������;�����`��Th�r;�u�7F��3m����_��������~�5����!��q>\�x���f���)}�n��D�?}������SM�"�!�����@\e.<JHB�~|��W[p��_���_�r_&�����l��6�t�������o%&�c�Y�J����y/LC�Y������L0c�����qs\�4d[��
�2"M���"�VH�Ko�l{x�-�NR�����Y���Cd�8�-|$���f�l�
��� �!U$�q:���O���R���o-'�{�C���n��2�cjq($Q�bk'�4���8YRX�\���l�G��67�wK����+���D�$&����W=1��S3���b�����7�����6LB���V��$��U���U��#h��=��&(���(���<%���$"ynF&L�(�I�Zx#����'.�e4H�N�w	I�c!��JZ�lF.��"�����3�������pq|t;����w�Y���I�l��\�]�����?�>��4��L�pD'Y�1��V�����l�%�N�3w�C��>��YiN^A�m�mw���=i�j�z��u�v�_����XV�+��[}[r���
@���oP��oP?[��]��^�=����C��j�M{~y2@�Ga#0I����a�^�T&OJu�`�>N�L�\(��o7�R�K���NM\�-����,�ghA�2
w�������v��CcdXTIa�q�J<q�[�a����T�Z�j����H��*}gm����M��+�-��q�gj�-���V�������%�r�l��3�y��c���������U���J,�K�������l:�������>��n�c�U�Y���gS�!�Z���bo�2����b�l�ZRv����o
#�f�Z�8�@�x/)}�����M�4J��F��Y�qI��������|�8��4������}���P�2e��������]�_�=u���c��/�[��j��(g�B�������-n�������Yxc�����'.nq��n���r^z�p�R�7��S5T�����4�}gWu���:��n�DB����n�t-b��%�`�d����x����U=Y-��g����=z#�y��;�d4C�4C��8��:�y�hs7|�g-�ZR��=�9�ZM�6U��#�;Ko��J�������W\�]�a��s
%�]�v*��<@��O��G������sFv,�|&���C,��o���{�i��i\K������d%�p�A<���}��b��M9��NH��b�$����0���*(%'�������j�~�|fs����������$E�h��?��B_�h|/R���`3T�n��W�2�����{�Ya��1pk�A����D�A�~Xd|{��B��1~��0A��lQ��A"g������`�d��D]���p��;�4y�p�@�p��O�J���u�;�~��D����t���O���B����5������6�.nak���u-�����F�:�����M���{Dl��g�d���'��o��,����R���\��%u	N��tHej(��d�����2��SJ6xmnwN[A��c��A�9���E�2���$I��c9�!��<�Z��x��
�'f���x`�6�<��Xf��Y/7�j�����puyzq;�N'JB�G�M��?.$u���B��a���g�aI��zx������L#H.�H��
����4]?!�3����lqy'���]m��rl���^���3a��'����Lb`��z��k$}u|E���c~���`,� ����eqS�tX��O���6�6���H�c�]���	'e���Y'��m;���O���1okK���x��x2���i�))|v������<A?�N�����GHG��j��(���F��u�N_��[�=��e�P$s�|����L���NT���{�+D�0U@!0u��D��:`/�]c���eO���R�S����*��*���sa�I��&�{yz�zb��n����m�����x0����}w=��?grU��}�y�nN��q�ib��nrY9�Z�'T�g��h�>�-��8D�5MJ�)��k=_8U+�{���� ��	�����cg����������������y����s��^Y�8z�7����Yb?�7���/O�Fw�hb�@
.���%~3��c�b�Wg����������:e�@�%�������0k�~��Q�6���?w���E������r�M��S TzF���D�"a4�`�5\� ��w���8E�
�pk%�T��M���(����}3�S~H���9=����Z��v�i��������Z��(��V�0�{�/S\i��O�����aR-�(z��	����X��QL|g�����%����WS��A(�S������a�T{���<�jX(����_�u�[-���>`]*��n��^�aN��V�=���kz�%�����>�i���	�������U�G�&��OC�����
Ib�j�?��pi%��(o�L
�']�%��U�
�M�_���B��[V�	[�N�=i;�n�f�^�1��������#���l�c�����bpQ��j�YJ����1T��RVx�����M��%6�5������o�mW�t��k�13�t�#�>	g:~�e����F:*����J��
Eg��/�C�^q\�P,rVr6?O�W��|U����6�G1V �]���//�����k������"����1�+e���D�||���Y�������y�%%6�N��I�v�Z�u�#��;����67:��43�=d�����5�/u���]��*��?K���r��!"�tM����D�)]S�7����$�.*���+?��GA��;��w2x��]�������%@�b��+#r��W���3�����%�k�'+��/��mv��$gn!����N���Q��h�j5�YwF�Qk�������b���������9Rq��:�f��&N��=��de�<BRC����Gol����#�
���Z���K�hi�X����O��G�K�
"_�����}��A
Z��|��������W���5���O~@�D�[��>����U����/��a��������X��G
v�����x��J�hQC�hx�\4H�`7����st8�fp;<�=�pv;�9����
����d��/{pVL��A������\}<I�h���E{����7�����GT~������!���_K��/��W�S�Y�[�����(�0s�(��&q)�_���9o���w��Pf($��$�����g�1*��X�������6���(v����i*�v�:��"��(R�?�.n�2�[����en�CSmI�l������-,�c7>��'����G,k�A�Cuq��6M��>�	0�1�����@.��C5Y1���r��n�1���,��.R����&6�I���	�GS8s�B�Uc����$��v����d���5�7��$�UQ�Nt�����6�6�"0���g����x�M��o�Fi�|��9����y{t�'.�b���WNZ>�������3�@J=��T�g���,i���������T��2�58�]�)�O�T+�wY����zSt}bP��_&���h��	bT�cS�������|����[��K��w��8��������fp���������!�@�j�DZ���!�1L
	�����3?q��phn8q���e(X
]��P|zL��,�wXL�LbJ|2�#9��'���]����&7��2+4��p���5�\����h5���5�n�kS^[k��0�<cb�%�@2a�5!�+��H���TH;D�p�b��%�ad�/O�xx����a^Ve?Y��gI������+�������f���*3�����e����Xy�+I';|����))[M�(�0~�����I�5:�5�B�������o���^�W��;x�)Q����2��	����/q�D@X;�;
[��CaP�E��aF���U��5���_N�sGBY&�n��0b��M�=�x����k-�r����0N#-�q����6�
�d�mVm�^[<����4Z���)u�7��5q�"����H��*Y���q��
���������-�q�����(�s�������Q����Z^�WQ|9�K�TH���ZW� Q�]P���I+"��� ����ea9u�T�.�0v�8���<������h<RTg������W�?�������#����~Dd������n;����z5��p
�@U�^z��pq7\zp�.�e�%���W~��������Q�V�xN�?j��F�*��%��+,C"�~�"��G�E?GK@I(V>���q;_DZ !��/�3� �>�*�h�^�\���*�-�{g�I~��9=	�]�3����a�NT���'9��!�	���UH,6�&l�!)��"H}p��j�0��a5��� �����7jz����t�z�i���.i+����1��Y�-#��	�>����y4y�S���##��Z%��d	�@c5�*������)������,N�g��
���|4Y������������mw[�.K�]m����$����w�M����d��|�Q�[��r�ClY�U5/uG�.�V�QQK��I��#~\�o�_�@�F+M������*������~��i@K�S�V�yG�_]B6����C��_��^?je���������;����dF��!J�;��H��i����[����,�G�����P��@e�F�V��������T$\#=��\x%%4A���]��9�Zm������j�=��_�e��i�P�l�k�����i�H\�����w�I��SK�gK�+k��[$o����F	%
�Uv��Q*C��aC�c������v��Q �'��o���dB�[����,��dC�<����`���6�!���),�����RT�?=�q��kUVGLF������12�� %�J^�����8��TMi�!%��x�q�(�j�hj�e;@���zC��u1��Q�_���]��hpo(�s�%�.�,LO����F+�uJlc*�G����H�y	��*����Y�lX9G|�e���F���(�@&K����A���a�����I-���V��vm��mt9��J/]�
��UJ]�7A�E	w�9
�#���{�!����J����R��;q)�l:�q2��6/�����8JR;2q�����|��e�Tf�33�d�����I���K���U�Acad�~�"�	n�k���{�y'jd��r�l���m�<a�'6�G��{%�:M�6��y�rE69��3*�`)���:������v���T������K�*���� mIS�E!b`SDQ
j8X�7�������Q���X}��W��tPD
���
���|rD}7y1x"��f�7F�!���_p�`'���D�	����j�LU~����-�����o��@�s8��<Z>f�'
1h��[M�G���";�^���i����Rr�GNc�nu��q�V���~����N�9.j/{��JR��.I�����6��^�r����-g������c��J���q�J:6Y����R���r:N����3�`�&;�c�G�Bb������:��	�����
/D�JN�y,���J�g"�/��Zh�Q����a@A����Aa�����A���W%���j��p&)��6r�2�U!&�6���V�='��MJQHq�������
��������7oo�������@kn�I�Z�]v�Y�m%�"���t�������iII|?����H��d��x&-	K��S���s_��D(���(I-���OrJ8��3W����z~��/��q����D�b��2��z����������X����9����P�3x���<�e�g�l�#���p�������y@l�b��5��G�	�k2������@�z���<�W��s3�ef�4����n�^��I,����������l�
��p+*��<��������������L
�[1�������K�����z�
|��'��#����3z�����|�X�Y.��|�������aS����^t���v��������!�B����UZ��L���v?seHxOX��<	�Z�#�%��:K#F.(Uy����_z�	�W�<�T��`P����������;�D]�tc����z��������E�� #(����C�AG�[��\M�Q�+��o^���
�Z�sF� eN?�z.5ts7�F��<��a��6�ZL/�NP��PZ�����&O �g1S�\9�W�G�%��^s;��	\_��n������6R��6�N�]M��b��F������}�����	E��Sdg����</�MPR�����������n��T?!)�:2w��Ygan���������`w����������v�}q��T�wnN�}����p(�N��Rh�}���Vp���h4���zQ�Gz����@T\��F g�&��.[(���
�(�C-�����J�_�Ur���E���J���k��7��0+�=-P����U���	���-<�k>|*O`����H�^��Ni#Xw�������ns7��)y������v�Z��Fy�w1�����\�2�!�H_�z������[c;g�RTH}j���LQ�tH�k��i��U���?�Q����s���x�?�� �$���6�����G��Q��)�kI�>���}\��0O?���%����y��!��l����_0d�����{E=�;����Qo��Y���{Qve�%*_~��b����H(5����m���'��K_�Y�)��js,o�om�7������M�~��k��6677�����5�:Lo�xK���I��k����%���{��ss��V����m�������^ss'��jn����`�[����TWF�$Kg<�f���l���C��D������A8��w��;[�����Z���fst����s�6o�q��67_��[���eh�E��KGa��T?N��u������p���^/+n�>H�H����a��{���B��js{ss�f�@/�_�������������"N�n�Z��������2f����?�'��3=�_����������5d^7
�����h0�{?|l��S$/���G�^��_��y/�����6��/����U��`�6��Ug����	5��&X���iuH���������L�5�V� ����b��K����*��'�t���}����}�6p�j�DG�����6�?�C�n���'��A�)#��K��,J���+��4��0>�q������DnJ�����/�G�p
'����H��h�O3
V��B�m��
�l��I�,����z����;�����M�I���VA�Y�YI
����N�Jb���������e�?x
��f����i���<��(�8
���~q����X���q�1~���s�g#���
9�]2U[2s�i�<���)�T&�$���O�QHH�q�
����A������#�i����|�:������}|�|�&f��o/���d������D�MQ�1�8>jE����'[���/�����_���y�� ��n�v�9���w\�[����t�1����;��T4k�u9�34�A|~�P�r������_�������|	�_-?���(xi�0X��3Q{;G��z��]s�;����S�t�3����3���8���=�V}��vtMVd���|=�m8��n�@A�����]|�I��.j�Bh���4�3��U��(��������;���;C���{}-����C��'����&
9N�2����������;�
��8���]�Q|�77G�G�M�o��x���n��;B��+����N`���Z��}>��U�������=(_T��~���B��w���hR �))v5?*��^�s������|tv���n3���a���Hm)�W�9m}ik�����woA����b	^�k�J��!�^S:�WcQn�O,�u}���OzI3�������/0A.W�#O�����up�m��nw}�;��o�f�?��N��"��A�F�t��N�a�C�������������H���o�i7�*~������
x�Z>SJ?�8��P��h�Iw�EQ ��c|�!3�wY��T+��>�z�'w��z��������Y�>��\zL�|���~����{����������J��
V�q�Q���s���I�z���7����S�U��_Q���g���6J�,��_`�QDq��U�_�$�WE�+�����mb���ME<����F�U!�,6���t#k�5$���u$��#�5���t�����"��h���U
�����g����A�G��X�0,�o}}�po���������D���t8\�b�/�n6�8�l�C����}��A`�7+-@^�uA�h���I#�]�tl���QQ��$���A��po
*
�(X�����w����N����S��ii<,���s���$���a�8���IJ������E����1��x��/7�MW`�!�S�)Kb�(��+�7�D�XQn�(j7�P.��W+�7�)����zj�H*=
ay�)��y��������|N�������?�"W�{�0F�����~_���-%��,=;2Q��U]�������hf��<��ORL�����x��.����E���'������'t�s�
��^y4y�f�p�������Hu��c99�9>���MG����O��������#{����������S������S��WM�8�>�$���2g�����_��v�*�?P~���]|�YY�����1����`d�}<����7������l�T7
�\�++���U���K�����W���2]����^&j���+�Wq�\R�Vg���Mk^��}�%�������^o���t7w��w6w�EK��U�"��f�O�+�x
R\!��o+	U���M%/>,|�y2l*���$���G���l��EW��<���m�v�(��E�J��k����-Oh�������������o~:"���Q*]�>�R�H��{]W�����+��d���(��D`��qc�s�!}��]^��,�8�����<
r�zF�Y��[�0��U�3�d�W@3���{���]�H���d��������V��������������Vp��y7��5+�����i)J	���cX������b3���6����V�~��M�����x�6��jk�f�^�Z�����������������`Q=����>!���0�Pv����|/��GODAQ����3w ��7���j>���������00��>�x�>���^R��-u��/��2���(
F��h���y
�/YD�����5x�S�Fm{�-5]+���t��07S5E�3<>��L���[S�v�{�
#��
�W�H������	k{�s*�����$���������
���R4�FA�r8�*����Gy�����b�G��D�[�n�X���/2S<�P���L�nA+F����S����������m�g���m�r�����' �>�������8)\�HApkE=��pW�x�T$Nr���l#��A�E<�U����&�$�,�2� ��������K��E�C8��(���U�\W�����fJ����c��o���/O0��E���$t
� ������k�!#;)�Q0K�,�"=��������k�3���0F����=b[jSc1�\���z�[7��Fc_���,�C6�n\/�'Q����7�X:�}e��w���rP���2���
Hc�^��c�*��w���|��������{�����T+�p���?8��sp����������V�����/������5�Ky�^Z4��g��y@��|��C�V�S���*X����Cj�K2��J�/�E5��SSd��/1�Y����r:�����"N�����P8�P�9D�O�H�qB;T�P�����.�gThX�t�~�u���h�q�*	�!��&-&��>s���L�l��j������pQ� �&}{�8@�+<�95,P
M�	�g	����w��yd�Bz�d�n��PR
�O��&I��Y�0��%x��NZl���?�$Z�D�H����K�C�d���e���Nh�BiR-C#���u�|M}�Vd#h�T8~��U������iLr��:[�����i������U���Z��w��~�����,!�v��0g��3�e���vK�D���~��T�s8��c�~1�V�F}N�^�����<�5��1��HL���~A���q;$�D�5���$����u�	���2�-�����6�s|b^�����u�_�RuN���b�@��dS%����0XIzh��V��������w���@��wyNQ:�������r�9Fn������:�W'���7���6NG�����"��l�8�7�H�
� x�+�������`� ��O���������s�
��m�&�C�$�I<RG4�����j�&����Pi(�p���{�	1U�0��X?���-C����	���QQ��(�@�����i%���	*�P�+~~��@]�>�K�_�1QgI�=������~f��)�0�Q�iO����6�H�v��@�0��f�j�o������>��q>���>v�8�,�5=���)��L�cJ!P��q�U��t���y���;��O%���3�8,(����<�^�U��������T��\Y�����=�SS^���p��h��������������[�AvR[q\i�	�#P�4a^��P�PG*��\��%>�.Y�@�����)\��#�R�T#��}kX�$fW*����1�g U}��0�P�@�
K0�������N{T�(d��Gh��	
c!i�����.=P��G���6Bc<�:a��k�����D��h�&�W^}���T�A��t��W����u��}3��r� �'Q���gf��[<����u��ftn��#e�
)��������(<�����-���m�c���A��X�Q
�n��
��/V��E��mzG���q�A�'��R%��
\�O�����EBRh�c�6��Ig�1��K=�/&��_)1>�%�H ��'f6�d������`(�Xn���W�Io��.����j���!o���3����4Uset�g�}`|V���WW�m�9k�\�:��'�?�	�>i0�pk�	���� 4^��:j�o+:�m�D���0Eb�%���9UBH��X���u�U�NN�[�����'�8��N�j��;��@�C�n.k�m;����T;~��S,]Y��R����O[R�����������*�>Bql�4�.A��7��sE)k03,-�t���Ey�fF���)Kl�c2�Tu5� O���.z�QO	�C��������Fj|�hrq1��Z�4�
�br���rIKQ���{?�������+8�/E�S������ r�+%#�HQY:U���;�
Cr��!~����H���h�����zR����Bs��ni�sg����)-Yiv(`F}�%T��d5��< �{����S<V�������4nz�Ek�������X�fHT�5:��z(yK�p���������D(�|�%88u��C����X��y6��`z��H����KO������W����=97�'���[]�O���%�_��_�]�r��lkr�����^TA<���;�X��0p'�C����O�����xQ��^s�����/�(5��!e2��DS.2���?����8��0�6��?���o��js<C1�6(�������Y\sm[X�����f����q��E�R��4!���(� !"?���,���WZRX���M�Nk��ee�w�K��R�h=�n$Z�Pj� 8Ecj��38�n��NL�O���a�Y$�q��K�v���#���	�������	$�k��
A�-^��R��&���9@���Tr�d���
6���U�~�k�2�H���'u#���l���t�
Y��
�8��D��������l;�+���+�O[6����_��s9%0��#�������$,S�R�h��I(���kX�����	���#�����E��t����g9+�8�lJEW�Z�c�%a?�]
�b��1��'�����l�6���������I�3=$�w��5,�LCx�>��3D��,w���������^.�����m��.0 m���[2��6������sXT�(c%��;!�)-�Dp���,J�+��[��v ����w����K�����{W�|��g������8g�}D�%F�5���$r����x��JbG�1����X�z,��xTk����,W��p�z���
���g}��9�?�u'�a6��~>>�ps�S�o�1	@J)�Gc�C*�cm�\��
3��-Yo��L�
v�d����n:oYH�Gu �Ns�?+-�Eu/}4�)��j[Cgl[�tO��Z�;G4��O�
<���bzm��3��K��
��G{����1��e
v�b�q�JT-���B~O,�N&!�l�4��?D��Qp��&sT!�6ml���DPA�<�������J����7���'BM�gJz��$���f�;����S��h�������01�]BO!aO�(����.��Z�A`�p�AK����Pl��K��ZJ��Z�����KY�����	;G�1W����	d�������K�f�@T�]M*�KG��$�2L����;b�6��� ��cp���]��:+)�9L)��+e ����d���	�fJ~&e<����P�Mr�o}T2p
����P�;������0��r;��>L&���I�4�$��Q�(����6[���_����[H�d�����J.� ��j]\�\��A����#�6a��{�w����E}^#��Dk��;����@i��ep�a��xR�$u����s}��:������oW�:oN/��HK�I_��M������������/���u�6����F01S�@E��l���*���'�lM�h7����?�c�aQ���j�|z��::t�D5�C���{��5[��;M8{����RDk]k]__^k��S��X�Y�,�tk���V��=�56�����������X~3	u�_�*�;0�AZ
:����a������W�nW�}�B��!�C�d��2��5���g1�^0���v:T�dF;I���
�T{O�@=F���-���b[�8�I��������/���^�*\�<�����F��=Mjo������kJ\��������OyL!�w��4�D/����rhm���!~���-�lF.^�1�m�����sqF�����/Dy!����O`�K�\�;��/�Q���@b���n��0��Z���@U-Bp#���}��h�cA���Z�a��T�#�]E�|X,���|����1���iW|bk�w���f��`���������X��-�Lq��j�A�!j����|��i9X�����1�����	L�	V���=�f&�f}��5
�9� A��p������NRT�,�y|�@�u]�`�N�c�h��b��>�H�����7�.����sx"�#����8�pQA�*��n�ARp����S�=l�D+����_D�7O�M(k�l��d:��Y
(7l7����R�`��pc�������6�%E��WY�1��6T���!D�]�$�pQ�cE��T��H�7�Y����������l��b��$���	MH��t�����Z�Z���
��"�,�*EW�r�lK��m
�0l��?G���G�pNP|f]1�`,@T���� P5��0���u&�V���'���!���
��������a?_`��%;���W����*
��V��@�=�|g")��;z�}V�$5��->�R���L��Yrg�8X�?�O�Yt�X��}����t.�Q@*�i"�NR�U��u1�������g��1J���A���P���q:t>3�F��5T� �;���B��(�|��i���������s�pM S�vB�t�b
�}p��$}�;�0���z
�t�G��*)�:�b�R����h�A�CH��o^��0N$��Yd+���e�.D[B�7XKcq�-�m�jl�t���������!8���"2���F�o��lF���_,3�5�	�u}�������L
�dM5Q/oW�����Cr
PR8"�"hV�"����&�����0%K��PK���M�}�b��<nF��	�V�����md�������+J�!k�M��>��Zf�fZG�$:z�lu=("�I�lK�~�,�-L ��xz!h��r�1=0�<n���,#	i���3�Rs�S�R���Gu�j~�KG0����
����&��)[�B"e�t�E��&$��0-Ea�� PlQ<�x�,�pHzr�������d�����G|���V�1�e�n�t�f������2gnR��j�$h$����$� ��OB�,e'l!�����eB#f
���Z]2�`Y�,����'��,�?F
}��;A�yf���P;�Y�Cf�&�{�$fy�[L�:�lF���L4��b��Z����<t����4T'��9�Z'<7��X�6;��8��{~��l62f���E|0g��s#�
A��dC2�x�	���0���'A��0P+�h8��d@�f�[F�:������O�S���2�����\njb`a80�E�e�z���B|X>��cL��P/���
��@���#<��y�M��j�B����c#�����&2�
M7N��9R}��E�����tL9mj���3�B<� ������'^2uP�b�r����RTOCG�)�� x��.[�<�`eE�-�M�(��T��
t(��.^���2����T�a�_����h�j�gX�	��������b/���1Q�1�^�6�+�� �E�)Quz�8���n��hf���>ST<fz�e
Jt��yX����8tup�>L(��RM�e����b�����'0�9�`<����^����@N�OZ$M�u��5�U;2�fqd�#d�>���($��bdWFR$%\�N������J����%S���	�'�qs|��\���0h�%�0�~J����&��q��X����W[�
�Y:[��"@�T��d���"Ne�IC�����SGQ(��+�B�%jM5��U�j<���}b%���Z&w�=`��Y~� ~p���`
N�[�a���d�v���;S�%--��|1e�F����/�+2�����o���w�BV���^�H�N����y"�?�\�;,BxYO���F�d�:-9�K���������&:�J�T�$�I�2��ZI���Y<���������Zc��@;,�
W��#y�PnS���F���h)�������=;��;�r/��y��Lm�7_V�X�������=�����O[G�1�������4�R+FD�-�����\C���i"�VB.(�,�{mN����
lX)��h<�&D��l<3>GL=���	�0�������?��W�Z�j���{Ou�B��dRHl"���69�����5�hg��:!�����d�!�g�V���4_F�"���1]��O�����i��Ir�f��]5@^�>�c�O���d�S������� �)��yi{��*��������,����`BL}D��	�����Q���l����t,�!��H�~j
�4rM�Z��F���xN�����y� Nr������Ir�)E��d@������"'��U>$1el�m���+��X	�R�h4%��M2�M����!�[��	����}�>(~�Cc�V�����x��<M�]�����M��r���j
��6V'`mSO�J��OKGoUP�+�CH=�0F_�>�5�l�/�IN��	\&��j�c���2�R�����-l3����L�����V�1�}.[�Z�z���;7�D��M�X~�o��pR4=�y����a��v�WC�r���J\Ws�'�XL�XJ�i�_�!��3��xH�]>���B$aE� ��/�MQq�5�{S1lH�'�b��-��#\���l0_
�{��WW]��D�2
�1�1��^��th=c�/d���s6.*������V��F!wy]�P!2�G9YOz���u��iN�h�q0�.���
�:8�dI"�~���������h������?���1�-H'��[SLJ������+����������C�v�������iD0�������<����'%D�&QD$C�������N*����F��}���in`��ic��Lr������a<y���c�	*�C\�����4��H���bFm!j�>1N��$Y��k��\�����AP6����f_xb�a��DqOYO�*�aE���?�l�"�����.��H$�5m��`�D�;�=^k��Zwv[>��)�f���/h�K�#\��[=I��u"��R��c:��
�4AJ)1+�-�A��>N!�A�&h���E����.�G�U$�F�Y�6���k�Z��C�XHA�x:�_b�9�v���8L�q�XJ�YW��K�w�<���(y������r,���d��n�D�:kX�3�z��������%LrP�����qH����4KDY�7��G�0�%�@�&�`��&��Tu2�\���I�R,"@!���-�Y�������PZ�"���O��4��E�c7�
���'��o�)]��T��3�?sX?lBM�t�GM/{�{VEV�G�V,��y���~�09�P]�O��B�b��(�L�M�*�!�0q3��~,2s^p����E��I�'|�7�YrA�@���1���]�����g��`/+����od�@
7�������s� ���M�/3�����xe�5h&+����k�~�w"?�A9��~��M^i���������h�o|7�y��?�|M��J�%������s�	]�rdx�5n��29u�$�
WSK��Q/���1�v�
Z�d
��M�V���0�F���I�'�X�/�4R��Y���*������b�(���V���]c!�!�`���	��	2"���Dr�c��,"���	t�k%�"S"7�A�7f�����,"`M����m�R���zh���.b���]���Y�4
�BX��0�sOr�.!�no�(���r���X�����I1�����|�����E� (N�mlV�cT�~���`d]r�@k�t���Y����&E�1�8;�=6����U{U���$m��l�w�V|������G]��:��CU���a�m�"��b��-��o����maGR�e<I�:����})�2<Ed%
��\��IO
��e�=���]�3W����u�� ���e	m(����d��s�U����F}uQ�QQ%J��s-�rdW���������--��u���v��FQ�����g��f����X��N����Q�>X����������F��m�CM�&����!gnCPmIl�No����ee
�	��H��X��v0i"�#k�h�c�K����Fo~(-�LdD�C#|��b��U7���\������E��9����H�K���W@{�(���DNX�Ue�0�$y�"t��&���^����B������(��������H��������
N2!f�C���	r�|��������M�|���������y2���K��vO=?*�7��b�p^��]�L;��@�!�g������J�6�&E�$������:"
�_�L_��!lJ����5�P5���t�����OQ4nHvb+^����C
��G�aL���	��q��"6��x�G�&n����O��g�8t=�$J;�����d��-��h \+Ku�b��O����=�V��L���8���O8�W��(������C��B��G�p����W��K��Au��R�z�BR@����D������I��d
�i���XG��@;'��%����$���e�GL ����) _�G32�x)�>��b)i���o�UO�4]�3�A:+T��B"-��)D������Y9�>�>E�K�d]�
Z�&&N�5���x�=`9-�>`�o%��S����lmvkG��4����E�B=vor��yE��%y0B���De%RzY��{E�����(�������d�9��E��(����������@�h�7+�]����8�~�1���pvLZ}=G�����t�zv��X4�h>�����&�'|�j��������r8��������������e�Q��_�%�?8x�I��S���c�p}(G}tk�"��	]��1��p��:���~p,+����4F.��^��-@PN�oc��,1��
K>��{�����7+����(_�z�]0g�J��1ZV7X���,M�)�E,�-]�5^"��6A��La�q���=TD��� �p�0b^���L����f�����1������8����C5N���uS��X���7K6��S]�tp>�I1[P�f�|��xqa��	SZ �!B��U4C�C��uE�B�>�-�
!�8{�����p�'��4�	I��@4z�6P�#�]tZ��/`�9���������
��8��&���pQK�mS�����AL;�����'.����f�i��'����_����x<I�4&aD�}K�'���y���7���"W}AB�(��Dm��d���$���,10�����0��$�k�OB12q���;:��s�Q��>�@4K�l1{���h�FS���mB\��JvyL�xh���(�\��\��!�������=4��f��d�r,���Hs��c������oS�C��h	���b� ��&G�����)�JhpN���E
��`9�f��^�>1/r��A�c,-������v	��/�p�8r����v=��X���%m�&GD�W3`��]v�����}��"�U�2^��������Ix[v���05�@E�"._���W��44~�4B�5]���Zb�������y{z�j����^�����u�,�'
6�L�������>��L�����		f�Q��&<������/������Z��u��Z7m�������Eq��)[�\����f��z�0��ab�$�Z���@��c����HR|�e�q���s�P$�-�&Z����#s��������9����uX��VOz%�������C��4��f����tc<�V�?�E��^����B��o����kk�$+�S�$�^�Q`���.�u�������]��^T���3����	U�l%n��f�������l������l����`vR�����l�i���D��a��b���2xp�t;P�r������|��/f�UY$���D�U�(|��&XV���9��@�h����?��L�d��
���Ba� P�;E0�o��FyUq���mI,z|���D��g���& �m�� *�6���]��-�4?A����*�
Q�%�%���������y��y�3�Q���7���Ct,?�mg�����}�&
�������A����2��fXL��X�=1�n��3r�oR���r�rl"b?�]����g��'��q��a���I�������K�:&�I�Y�z���sQT*��4���y�u���y���T
��7p�~o
A�`��,�~-���)D�b5���J���G�#���%'w9u	�����j.@V@3�G�����F_��
^�\��R���R6���l�B^�3}K���6�(����-�8u������s���a��l������������}�Vv�8�^�:.(�A��O7��?_����������������S�t�R�<
3E�Y��!�1�c�E0��������������B����;��{&����S�-�XD}�`��E8V+�|@�-��aa���5K+��[�Q_�ON��$�j���:��?�2 ^'��o��BC���KH?� A����g�=��c=.f"���A�k���l3��<��?8���`����'�"�9�f@S+� ������7x�4����l�7���G���&w�2z��j���*K	�zL��7�A���F�'��|���[	N/�{;���������`�gq�(��
GIX�I��V���}�v��5�
� ��y�nW�,���|:b�� ~����?�S�8��s=@���e�\g#8���A�]=��:���(����R��O��Vo�X.qT��#��,1R��J��^��a')c�<l�4�0N��E�b
�:�9>=-��}WS=TLN����~,Yu���o���R- ��/��[��]�=g��o�h7�������QQ���F`�M���k�*����9X�L���d��8��1���^�(����*����(v|D�Q��+��s�$�~��)��!�����6���d���%���*3���fA���K����Mp��32)�\��� ����C�n1�|�%����,N�c� f���?-	R��(�"R}+�����{�+F�/<���Q
�:e0��D`���A_��,��e���^�~��DA0X��  ��T���Z
 G�X.%���hu/���i<�`�����)eC�%%��Q�'���(R������1��
+��:��?�N�;x�+(QS6����
q��������n>���x�%�X<&�V/b�sh+���B?a�����9��L�|�Gk5
:��z�VY����K����&���K��&+�J�L0����=��m�����Y��Q�t��a:�IDu�B�-T�r\�KF
���lr�!PD��I4w�!W�*Q+H���Kd���h��u�x��b_?H��!��6���O�X�aC���9$��*O�w��S��~���U�c	���������)�f��	[]�Z��^D���.Y�Z���i5O�d T�t[�\�n��d�n|���e�>�9�������i��RrFQ��x}����V��)|�S
sn-��J��C���q:��b�D�%��tc�������qM�����+�Z�&��������4Pm�[U���W������0V�)����yiyWj����������?�k}������.`��Y�Y�M����0�����7�m��?����5r�����k��W�!��������|�C�y��h����_[����Jh������������_�$K�-������CS��TM��)UR��m��T��@��V	X|S�P&��!Q~�)�P|�����<�
�:
���B��tC	�5k��
��RY�"=9����^:H�70��x�v�{����V�S����j�u-��~P[����D�)���L�T�����hbC���T	��,_e)��z�\�j��m%���M�,;�EX�����_P=r1�����/s��a9k��q�,[����F��k�t4�Hm���_!E��l!I�����O��	���$�R�+B�� �erh������N���'���}i��iI����W�-7����i����w���"����]n����w�1�$AF���i-�������U65��2���a5�{�d=��`��'����.��h�e[�b��E--�F������J��0	�[������9�U����6��'���x���`���6H�0Sh�*��v�����n
'�K�R�)1����o��J1S�S��D�������y�YjbRu"qr����W����Ft-?�����;���h�����s���2|��/���K���h�h�4����{j`���+�����Sk��[�d_rD�;�^m����(8�;v�xEk:���pG�q��m�S�U����\�}���d�2M�����q�\�F��P���T�n����j`��J�{]f.������(��'�Oq�M���� l
����o����Gz�BTZ�p>�,&����(t��V|�	��OV�'M�����rd�Kx0`�^rX`p(����=��~�����VU���B>��i��x�S���B���)��f��6Cs�[w��?�w%����j��Z���W���������G������4A�����y{�9�N����a�����F���l��Y�����i]a����j�{w��d��o�;}�i��#9����Jy����I��+^�-�7�&&�)I��]�"S�������)��i�1g���q�4T����|.� R(���1�b�dT'#� �W\tHnSjK|�>O�"hP+0�8-������l���`jTb2;�QUR�)�>t&������F�1*��	�L�K/.�3����fH������#bT�=�5���4���7�D��+m�;�+N�"aF��-|t��>���@���Ih����BP�4(Uyr(N��&T\�����`Z%U�IR���8H;f�T�lH�6bDP�Yd�V�9�>��r]��	�jEb����_�������!�5q���u�[}x��2�b�e���`���T�2����AJ:�J�	���E�2�&t�.6��K
 3����(-�\]��21#��B+�Ii0`&e����h�9��A_�^7�r��}k�mwP
��`��#�
(X		L�n� 
�\��c=��a�!�i?��8�)>�����V7tlZR���I��$����&BC3 k<V{�v#J��A������T0O*�P�Z��ce"���L�LW+�?9�F}� �cl�U������M�D+bp�:�����m[����+�!�Uz�#��7@�r?�L0z��!�3!�h63A��{@��`'T?9<�:/2��k��Ux�DAB��F����kk�{4#��]����dG ��^(����,N����fjc�*�hc+���\*F�T-=A�f=@�,-��v�*�������mer�UA��!fD{����|��zJ�C '*:���HG���'D�
���&�f����� �}�RZ��$w��:���_�B.��d1iA��� ����k��Z���9�3�
T�R�q�Ir7.�'�|}���JI��9u��wK��������u�~�D�a��1�H�l��X.$�����N�m���c$q3�0��h�����i�����f~e�y�(E( ��z�2�a"��?L4�Qb������%I����N���i� E��v�i8)�T�d� t]<�,���0����)��H�x4N{,]��F������F+���kc��LY�qFO��S88a���,�v��o�Cfl���0�^}[bSk�U�Br�po�.Y�-VT���������Fl9M��(Qb�����vS����.�L2�o%���8f��[`�V����E=��N������R�g
Wf��*u���]��I]��YB����EMk�U���u��-����R�a�lO�(X#������<6�w��WW����`g~�I?}}���N���)�N�������z�fW����~���^0K�K�HlCC�qm	*���6�h�o�"zp��+c��r�P����ol7���7\���|<��������r�x���}>Fl���~T2�Y���^Y���%A�)�5M����0���v6k����jS�m��Z��'��LM��9�@���t
g ��)���3�7T9��������!���
y��M$l#�R���1�#'"0�;j��q�>�kR��',W�	dj+������4%��=�]Y�]������*��}IcQ4�	7Q
�O�i>U�6�8LP���4���X1���<�����gK�����d��H��Jc�rED���}��(0&j�E�+vf��!��cF�����I'��-��"V,�uo��� 
KgL�"WP��4l�5t�\C����e����X	������3>��g�^@X=�����-��!����}���<eqL��@�}F/N_����;�H�����M���.��I7F����>��5����Pp�{��
����r%��D�a
a%�LTbj�;�9���i�K�����_��w�Y����up>T������~���S5p�7���3?�r"��0B�?$��+��=P�r�^LA�@�9�j��K��\}���L	Mq�p�dBp#�N6�X�Z4���n�6V���/zwb��"�/��d����e<�����!MG��	Crj����t}�Q�F�0�+`>,DS��2�y8��qP��������Sb���:QH���������}IE���$�F�1�UQ�yW
!��\�S���!��pL�U~(QX��?X'�v�y�0x�� �{��-�e�5����q�g��9Jp�4�����QxN�\*�\��� 1�S�V�
!Z2Ca���)�juu�h|pTQ@��+�J���}e�T"��
g�|�j?�N�wz��~���CY:��Z��w���bU�!"!9�IQ�J*eC�i@�uqc���i���TF�/�=y�B8�!�j1��������I�s���^�5���-��8As_^�w��r>W/���}��=��i�&�f�zr��
+�B@z�Cj�Qu����g����Q����2���aAk�M�nW���q��T����+��"$��^��1���G�������p���8�������J�V��`�80v���]���"����FC��xONu_n��5^#x�x��<Iu���'�����p>pt�>�}8��*Z'.������u*(��?���z���^g��.H����g�\#^��t��8UXmM\��m��Rj���I�����I�/�8D�X��>r��������`R��lk�����h�N�.���(y��-E��=73._��:�o��:nw..������u0�OoN�-<�Z:cL�\���A��H�:Iv'�����w����kKW��b����@�u��FhJ��Qf*XC�X��Y��B����o��iC�cf,��W�\,��RAM�`��������w�(��	k�L��4UQ#��A���P3v���n�����n���1%�k�4�~N�FT��|���L�:�'��V��\���VJ�.�iOK2�K�x{�t����x���T�#|��������E�d4}^��
J�*A` ��I����vrW7�r���Sk���*N�	Qx����k�w%e��%r�$Fm.���0\�^�������r�	����W��$o�KM��1qS��i���&���u��}�-k?.m�DP2p��.����!(�f�X�E�f\A:6|��LH68�mh[��;4|�z{����>zs���::V���M���zn [�Qz|�H�0<����J�J��l�P�i���E[�AC{���Y��A�/��|L�S6-�N��qz�qx2�<.���'$���0U�t�a��xF�6��j`/5����1C��J���-�f�q�,H�y�M����R;��2(r�$�T���hH��Ee�����-���!:847/6gI
LiL�����3������g#6���^�~St(����Ll���Pu%+V�>��m)����K[��VEL>/_��K���8���b
Z71������LdG]�����q2L���*C�>��������0��abk���j��j���d����N(`���P��X�$O
,2�L�h<�D\I��0�����b�\�!���Hm<�@��Z���I�A�m�NL��U`�]M�g����J�n�=�h��PZ�Lo"|ZT��)P$
�h6^i I^e���������IY��@���ytdh��q$!?6��;�|st��d���IiJN/~::;=�\]��������C�/��r��gqm�������0U��������6���������F�6;�~���X�w�^yY>�RN	+NRo�S�^�lQ��X��I���V�Od���)V�l`8���j��V�����2��
|��s��y1����Y$h�k<�� �y�Ss��X��f��yM����������*Y�?�=��)�����)�s�����9!V�FG��~�K���:����41d��qrz��~�0������&�y����a1��*�BuC*~���:#����8)�
�}���M'>E45
	��B�O�s
7*�)1B���k��g������nRzVY�-$��sUp�� �����S-�%��0���*��{������H��Z��s
V��
�Ns���b��z��������vUg�G/Y���,<��H��K�����$��5@���}{1�^O$�t#��|m�tD+���Ej�A=�2������c���b� ��3�,r"�9��h�u�����@��}V����D��L����Im���tE
�
��Da6���(�S���M
n+�=���W�M�;���	�e�0��P��Z^$lN]��q�a=�I4�������"EZ��i��"��������D";u��7X��V[/pJg�� --��%�H	���d�m>����6mb�V|�w.n4��cU��vg���d��Xa���60,��E���	w�V�%����NI�(�0�,������ca_*<��,�)~,%|�|�b�>�5���5�q=�������,�+�q%�NP��8�d=I$GK	�}
a�Dvd��/����O:��Q���/��V%;��
V����H���2����	����eY>l��
�'�8��b_!�����zPLGi�TA������[�*�+���w��dX����_Y_���j�i�!y��3@h�;�`��X��9���%���zN+��.���[8*k8��P���9��i�J��9�Ug�aO��q�+4��3#�Y-]8��H������xR�`����d��k�������I]��5S��y|�y*���L��k�Q��=+3�:���Aq�&�p�v��xE�T��tW��,��Gt��Nn���e�!]�|2�}d���$I�u����2;��^F������J���4D�Q�^K�{���'����g�>���F�������x����<uN9��	�xEf�l�����s�9��G�"J
"��o���D�����.:�0!�`w���8�\�]�Z"�D�F���1��w��
^�gy����'j5����1��#�l��n[��X�)�r��K�T��Y�&!����������(�2sz�����	�{��,�B`��B�'�gM��u-��=�!'�#�/����Y���:Rbu���;QG�hyu|�A�5[�kW�:o?\�O//:G��n�$���B����w--V�9�<�	�����4��s������V�Q=����~�w�b{:F'Q�[�L�}�S_�k�(�������s���K4���Q���M�����_���M�c��Nd1���\�n����';���/��}HZ�t��:�r�#a^z0��a��Ls�?�|w���F�L�|,x�S|�R��uC�n>\]]^��@V�WB��e��
�H�q��p����MO�-Ra4���k`�Q@��������y�\-��1�L�$������+�_�ZZ�Nj5���bWix���p���,O@�Q��@F.�<����M	S����[�=��%n?��#������@�^BHf9|J�Z���
2��.I����?�$���oJ�6�����^����wsOzw�����_���lM��.�w��p\A�����	��4#�����F��b?���Z`9l6b��Cw�����Yn�c�5�0� �}I}=�]K�&)�^�e�#�����*�dTT����>]C��%����U2g>�>�Z�����]��R�uD���r4[H�	�=Ld��J6�$^]������[E����H{7��+E���b��D2H���6Z�~�2���h�#�e������-"u�1����m��2��w�`R���!8N���:����^����&�rL!��6�U�����j]�]�.�����(L�4�/x�����w�xU����l�����I4R�N�=�����h�x�	
��39�*�P������UF�-��O��*����m�%��3��U3��|���WA�u�����?%E�}�]�O��c�4�?��{��n\m���]�]��*��n��N'��>�?��x�4�lo���~o�����1O������+3�r0��T|�i������>�x��\���qXE+��b�	u��
�6�	��-����5q���-�{���D���S8cLWAL
��Q��
�#����aF�'5$X. R�Su���`�I���Pu�1YE	�bc������$�p_Aic�
�����WV�
A�XH�� {�K�_!��K���K���c��:�&���
zKY�0���e��tB����a���V��B��R5����������7��o�����(c6�"�a�X�X+��H2��:m��l����T��m��4���#�
���T^&v��1��Pt}e�&Dn�GM}[���#f>���!6�>.DRSM�������nxr�1!�Tnsn>���i�����h!e���vw�,�^���g��~��9�
0Gj��@�@�Gh��w��[2��cwC���E-��=#�i,�=�y���{3�'Q=9R��(y�}�v`��rr���t�.�`�����PFD����	�9�U�~��#~|�I�����o~=� U�[*����t���o�?�G�I�E�PA��v	Y����E���l+���e_��d��k�����@~vs�����l�I5�^l����&��t�EL��G�H68�y05�~�g���Z�A6M�LL��R��\Dh���������E�wa�`���t�V���d0��,�rn�j�����8�E��2�nU7��QL���W�������$\��<V�L�d�\��CP��	T��j[����u��7A	<��~Tr�
�C,����(������Yg�B0��1��#�Ahh�x�!3�dc�$g8�
�K��/�sJ�����6�asz�
�ip�\��?���2Z��s����@
��P�%��Ki00��4`��mtR����P"� 5S 1��K�&s@���z��H�0�P��������zU��K>�����
^��g�2G%������$���1Rb�����5�����7���%�h��H�BE[�vx�!a����
-RJn���5@9>�<��\\[��_c���*���!�����o��u�!�#��|��z��s����[��-f)�X����Y�}�nK)5�:Ao@f^�q���	���t�[{�f�.�#s�<���#]PP�n�X���I��su�;o/�����6_|tqr��F��#�~�J�K��\�k&�bZ{�-��q����l�^O%�� �``��h�\F�F��V��]����9�������hs�����"9i$v�	?�
s	�^������|o�U��|ma(�� �1��)vh��������'����q���]<:�%���1���!����GS�D�������h1�0^���l>M��\�}��2j���y��<��$/s����'�H��g���wcr>����Il�]���,�g���P���H�R4u��u�"!bq�A
A���)���������~:�PW�z�������V�S����s���+����z)������k�O��fYl����a�?D���^:��fT�t��L��}PLe�<2������|v�9�x{�9?��
[����@��@�c�g�f���/7�����8�4�V��b��%�u,��u����OL�
9�d�WG��D��B��c�+��8����������*`&���}4:b<�v���@iH���F�����i([A5����������S$�%��#�n��t]��@>3����a$p�"��V�!�FP�:���'jF���}v����_9��ey@�G�=p6�Bp�=8r��r~�|N��a$�G;Z�%L|;�j�v��a ���1H�����0�-6'�3�hb�Y��I��,����]Zc��3���O����`
.����@�/��Q�����c#�zl��`@�
�Zt>��}���6�=�L3��[����������a;�9hv7�Asssoggymmm��.���.�����`�pg������f����>�h7���q;���.��"�i�c��]�F0�a�UBr�v��F?2�v��������x
�
�l��=�r9P��J�=����1��_���EV��c��������?>��d�?�%r�����1�8%��� ��u|�hB��[�������6�R�+a?��+��Ps3(',n�a9�{���=�4����;��A��67���m��5�f��y��P��.����"���?��7���d�����d:�Bj�~��yB�b���&~/����M>�o����N�;��Gp��s���98hl�f��c�A�����h<�������k����<)eD{3K9�"q ��Rw`��lB.���w�+^fd/�>M58�����Z`���$z��N�
��|��"�z	���c����1	 �����82��*.����g��t�)������f������
����5��T�����!�bI����(%�a��-��`��F1��B����[	���xz��ceG%6f!%��S?��&o����V��{h�����!P�~���8�����`�nVkX�p�P���2	��S0�~��E�fS�8���Ju
��"/�|=�����{Q��j�l�Y��Sro	-
�[ 0���:(G�a?�V&�Fv{�d���s�#Tyr}y��}r3E���q��6@)d�[�J��	>v��vs��������~s�0��;����>v}m�]�S����w`G���.�(����}O:qZ��~�G��2���D�����v��S���i��zt�B�@���)h��!�&ac+����{�1�'��K|���Z
c��������.O/�R�*��2��c��.����g4�������i��ca~���p���������I���>=o��^�:�Oo�����)�;�Zl���3���^���d-A%U��d�dD��7�p�y�z��|k������J���[���^5��l�L�3��dw���7�3z��QR
@�*	�yK�pS���&������>�l�s�|���)-���n]�]^��S��p�]��u}z��W�eSt��YAX��CX�1�n�	�F���QD������k���5�7�a���k`��;��Y|;��E��T����h#��q�@�0Y�������6������`gwk/��&OSeb�<$��
P���7��$��
Mc�E��B�`>�]C�E!�e��#���8)\gf�Fs�iz��h��Q������������*;8��2D����s��s���cV
���|�!2�
�
�oQ����0B.��q����Gu��b
���'�|��o>�i_]�,��:��`���������~���v��y��X�'X:.^��h��y0�������������s��pt�m�10�j�;���?h����n������nn�{;�����m���������8��xM!��w���,���X�!�];���N�N��y#X�tG+8�������Y���J��U���a�u�OlF�`�;T�����M�+NkUc|��}E�}���.��8����b6E��]'�.�/�!g%8�#W�WW�O"E,��>Q��\���2a��;�;�[[�������V��
6����4�}���vt��V�Q�����Mr`����i.������w����et����4��(]�m/+�����\RC�7\���[�<�I<�7�:��	�R��gxA�����Pq����z��7���������V^	�sd�<$�$�o��,Qx�<��\-e�9)�c�Kq�#�>����W��96n���I3�y�z��^���R��������n�?7����P�TY���$�<�wf	�b|��D��Q��t�&�B)���#�	��u�t�	HE�����:��9��}<n���o0�3��:�Zx
���,M*�~S�-��H���G�K�<l4q�
�8T��#C���/�	�o9e5��k�
��VQC�8�o@(�z�KmR���G�D����w>���oZm��NA�<��� C����l=�P4�A��i:���D����	����I��������Ai�3��vN/�>�����?\�[��O�P�0^]!k�������
��UlZL�X�?*��:��Y�s��r��4)����kR}*&��	��1g
�����4'��'�a�]�a�q���Aw3<�<D����Vx�����>��
k�z�����(��$U��mQ�])�~�]��� ����p��y`��
�� :.*\���w1Y�,�+����I1��t�q�m�I���O�i���gh�5=W_�X6u�>����F�<d:���/���V���t���:�>h�n���M��U�����gh9Q�����V����
�w|+�U���>��~�bV%�������$�~i���
k�e}�SVy_|���~w������wv���pws��@�-��~�}��I��z��>w]+���-
�bU=��3���a�MI���x�n�12�&��i�2�o�^����������u���=
��o����a������X�2
��{�!���"�?!wGa����RXN����o��
��T~��
��(���Z����Y\��j��]aAn�!�K�!�M�_��f}bak������e��E�=�rb*&Z�4���{��v����� �o�����\M��#@���M%�5���!�f�[����1�+WJ>����?��(L�zg���c�J��c�0��N�a<��+l�^��g��<A���Rg,)�#���)K���[d��d����O������8��'kXw��3����A��pO���A�8���J�8�wY:�ii��n10��o>$�g��`1��P�O�%q��5���o��p���g�
�u�s��Q�v����D����Z��m�^���!�w����w��1������o�// ���[��9E��K��$��ap�U2Wp�Bt�)���&�D)x�>EH����Z`g�)�O��m5��-��N��L(+4N�Yx7JS��b���%e��m���K��;oY���nZ^�������V������>��B���u�V�Dk�t
k^���>>�77�����@@���P�Fo!�T�A�]F�D
	�B��k��\A�8��
Fc���K�e�4$({z5�����/"|��`Kf��P3^�/_,f�mM��b?C08FMf��l��<��&��HSh[��@*f��+z�6����u������$��	a���357J�l]����\��W��W-�,w>_$�z��zO��|�S�;H5�BK�)
�=�����*g��~�h����*�/N�;���K�gG�5�`�!1����dMMb]{$q�R
���:�+�lO{�ua��&�����]�q�$-_�$�s�
�QL1��p"���M��Fm�v<wso%8�h��t�/ON�������W��!�����-�����KE-m���)���
o���t�_��n�S6 �b����<�o
�Uw�Rl���Z�����h>0��wVUP�J{]����f�:�����������2��AY����{��#�����7Pqdk���(|�G�:��s[:�������?��P�����5[���t��~�#��W�����}��YA����� i0"5��.�I;��N�g2�,�4����?�����o%��t��,������8���=�B�;�^H�������G��t�Sl_��C��~a���?���[3����|x�����;P��������G�EE-L���)1����%fG�h3�Hhg:���&Y����{�d��N�rZ��y�'��_W���<����J-��Z_�B��
�//n��f�?����;���~[w���3�����������w�)#�U��gP�7m��hF���>��;���>`�����������Rt�"D���k��b�<T����xM�������}��S��u�Tfg�j���l'K�R��t���0�/��
�9��c������G�������~��W�^�"��.-��)`�������{z���DmjqO"+�o���:��%u��o*�
r0�f{��PIj�W�l�
�`-)f��Q�>�Is����_Q����N/@�����sn�8��\�IRG��P��2��3H��c.��bE��-uO��a�q����4a�5��}����\>8�s�{:���>��?b�0r��� ���'���7����R��:����l�{[u�!W�r�p��G+����_�3�[!(��p�?���7�GQb\wt���`P0����,��V�kU�T�3�s���3���W��H�)ZC#��	�G`���co����i$C�+�,��1��$/iBgF�����%��#D��-T���$H�������}��3���4����lI�Z�������A-��v���C��l�����#t�[���A����O��
D����%x����{��6�$Mx�?E���������4%Q6�u-I�����W
$V �F�8m�g3����,�i���hzL�*+�����|�K�qP�&P���d�S_�W�QP��(}X�����_9p�j����~�]��$y;�
��3X��d�BDr��������=���� ��}B�4/ot�K�Q������h-�+Y�k�����A�J���r�����[C�7S���k+AL>Ec���~���
�����TGr8R�2���#w�����o�ql9E���b��A^$��B�Hc���d�!Ht�-�~��J�����o�EJ|���c�SwD���/������^��������9f�K;�*���E>�y.m���U��
)p�
I�^���"A=9�<E:��2��a�X���G8A��4��2/�`����3�JH�#������|��*@l��-[�#_��x9��Y?L��i����;/������h��E�u�`��aCz)���f!Z�G!u���*�����@lcH���`s �xd�	����I�����>�%y4���|�9X��`���#,���$
#nZ� �f�h~v3�W��u�Nzq&,���$�[��X������	�2B��]]+��A!��XI�q��T���m�z;�h��t:�I��d��}EaF��b�� �T���Z�������11��=a�� �����b"=�s���E����pS��	��P����m�����!R�D&F�M��P=����!���%�LT�F������\����%2��(� P��Y������\*��
>S;�k oG�T0�X�l4�\T��lr�m��o;W�%�C�V���y�t�>�r�<�^�CqITz
�!�A���������iW���l���i`�+��i�	��.S���r9��;�������
��g�����g�7{�W�c���������w/�\^N1�xuM�}6|7&�0�1�)��� K������@�
#F��Q<���/��*���o�e�l���r�5/I�
��U������������M���!f0�\*x�5I�������A5IdVv�;"��9�m��9��>�/��Ne�J.u�t-�J��s#�s��\�i[��z6��am�\'k@X��B�,,�p{=��5��Q��H�(#�l>5-���*b�9�^}G��wb�u�q�t�*�����\�r+@
i��U�������4hO�m2"qH�_�8����^��E��fA�3~��^�g��vd�f���?�>hf��a<��j�~=�P^��(w/v�{���/!wal�U�����+F����=��0���������vR������_c��P��#P�m��_�3}`�pZ	�������N�g�N��h�����$l������������o I�7q�F���Y^����|D�a[���$�|~����Fb(���h,�R������Zb1�b�E�H�]<��1'�(Q�����T�<�����+8V5��u@~�O`�c**U��<f��A��@ B������q��E32o�)�R���5��]����l|W�J�!��I��D��9�`a�B����9�)e3��s��$9KG9s���-Z������lIqX`#��nS��N�@S�X#T��V5ty:�$F)��$���f���It�^�_`#��F����jN��#�$�������g�
�3��A�	�$�� )�5F�t&����A��$�5s&3��C��~�O���T�����'�Nr��}�
�b��x�"��r?&��� &�:� ������$�-�d��D�����<f��*{���7��6��$_���P�}�x�� ��?R��-k�q���h��Z�������P+����V8Q�����c�0E�UZ#K<��N�k��M/Q7Fi"/������j�(�TGaC��k:�_
5��a-"�$�����E�d,/�s����b�/��om�g�`Z22�a����B������*�ZR��Q�t�y���y�=�|��lt��Y�g;+4j��~����u��7����Hc~wm��}x�r�JB5�:���O��<��������n�j����oo�U)s�N�/
{j����g���m����~�������<�L$+ci7I�Ph�U�W�G���a@kL��U��2���!�u6Y3k�9[�>��w�c������c�,��M�+�dw\q�1+����bz@p"c��j]���z�)����wjb���B���&�u����!
v	�9�2���
C�8fse(��8hm�e��Ak�3��3hMZR�k{��;pM�D�_
�>0x�p���p�`j�DL��%�a?�G��$�\:��
t�\{�W@6X:q0���Hf�J����,)B9�H��=�"��"���t�r{rn/l#��+dp�(��
	Z�B��T�Z#`�H�	��:����S���/�%��Q�7��|Cp<�������O�(5ee���3�z��=��.��w��}����`������(T�8��`��(�M	��u��MLR�	CDb�[��g�l@����D�.�D�)���:�>�p�*a�@~���V\�8�Z�I�:Y&����P�:�O��A���H	AR�k�w����A��he����{���c��*���R�����vk]s��/��N��O	����|�(��������g�P�"��
�r���t~/��-�2�*�6!4�v\��B	�.��mv��X�9>��[�4�S�w1��o�^,���@X
�����}	��f�����7X[:��7�$M�?��0��[�R������*j����58�B�1*J|�"�5�.�������
�����cG��kd����l�YM����S!�����������?�5{������Ao/��G��7�}�9-��A?�\��&�RPd����*�+ @1Hik����vV������.��|�������������`��������y����;m��{�F�
;��^���*������PDp>�K����]�<_�����1O9b:
p����g'///>�}���CJzB����z����������}-Bp��CAn�^/�?+6`z�k�R)A�<^���w[2��Rp�Zis�l
�ms�����N����Gp�J�)_N.�H�]������JB�9aP�ssIpeY�k�*���V�w�F^j�7����.7����b�H���B�xg���r���Jz�=uE����:��a���|�^��WM���4	�/I;����doY������5Sr��h �-���W��_!v�LYMW�S��$�(��v.�U[�v-(�<����B)��^u�P��;���R����������G� *2��H�iX�bZ�HV�x/���y�4�R��#@0�gH����V;�����F��u�Y���FRA��(��a��r�
�G���'i(P���1�� .R�"�c���@aL�Z���>]��6��ZC�/�S�����Wo�VT���k�yGB���#O�����V]��t��Fh�{����AJ}b8~H���<n���9�*�/Vo�R������N������nkx��E���������(v�0����]�����W���W@��J������r1M'������>��o��������O�N�j5RWn���1���_������_F�����O)�#b2D.��������;K�4��G���i�0���`���l�������D���%�����g�+/&���2,�*�5@{�,A���Z��I_���d�%Ve�+y�&=��-Ef��2���M9*f��M��c�����z����br|�N|�xL�|����q{MOC$z����s��n������?������`���)�j?i6[���<��S��
���x.����g�����1z�>����t�h�+�]�F��~�����f��6�:�nw/iu���~{���&���n��OI���/1]�YN���L��p�{Lb����0����7����?�o�v���������;��Ag�l7y3�$��,1�u���������j�&���4O��T&Z�_
��?_���q�?��v�e��L��!��v��0B`�K������m#[�:�������&?�?�x��� OZ����������'<���I�����;���#���*���wglr�PxF�jc�m��@����������~Y�5z���m�����
��@u�F�v��g/_�?A�N����ak�HAV&J�s��2/�kg�A���	�����A�hM���H�
�S;�O�d~4�w���a��Q���CP��Y����.n^�-"#	:����u3���f�|������74D��)�a9�8'h�����f�����V�%��|�\/���Lap5od���'/�O�����������v�*{7����|��������������v~���_�v����������6y+�)�1����������h���t��F���V���,�7�UZ	���T6|���y������tW�-���������A�T
;��2�5e���c�~�����1kI�u�������!5]:������N���b��n���X���
J��� m����~���������L?+�,f�XQ����L�eptfUJ-r���^1�X�6�*O$Q&���k�6B��~E
�%9�0�H�2�/��*~������$���_��y����
�DvD�*��c���K��L���+5|�
t�2!Bh�����hJ3�.�K�RT���&;�o2��`D;�F��i0���\�}x�W����N/(YOl<���*���:_,�Oc���vd�^��7���������W�.�����"�	#;��]��5��>�'�t_�����~����n�^��L�nM����Q30�9w���������G��~��s����~�])�K��] `s������Yb4"?+�|v�kT���Qy��I�+��w���1�&P'��O�UU���j`�y�o'��{_<t xy�Ao4x�/�Z�������.�t�m��%�@�1[
�Y�/T���7\��D/��k{��A��L;���������`��"�R���+-(���6e,K�������tF�^��]Y,s���E��3����%�[�le�V��o4�����Ag0�����XSq��eH5yvv��!.�F<��
����XR��X���t�����(�F����&�}��*���������v���,����2'�H�}^������;&�{x��|�6��'���]��a�p<i��0,�,o�5�9��5k".��68m5 ��]���F���	����E2�9�0X
�S�����J�Xg�w9�hIxZ9���b��U{���f]����������g�_�89�v3����P�FZ����9Q��A�i
<$�L�7�!���5k�x���9��o�pE"g�d6N��4yfj W�_U4�s���J�lA�Vg�M5������fN�6Z��J�J���g.��NI}d5@���D�s���l��GS��?�d#|�#<�-G�*�9��<�r�sJ��@�x���a�D��2@O���������"��3��U�9�
�������_Ud�N^_��{[�E��d3Vr3�q�CGua���#jw�8���orD����������x���xdY�����H��\��Eg��@�	�U����x��M�
%[���Z5�=�	�V��m�If���V�j�YbO�z�.(��y�����< 3�����d��o�o��� �!��o�@��%�=K|:�J8�g��(�u�`�7����@��������H�y��d�U"N���3����+�#���e~�����N_���U�"%I�O>��f9w�/O.�O_�'���������q�E�\����� 3�&�����X����|@�!\����+zALy��e�%o��b����l�
^rY��0�M�3>,O DW)	��p�HjE9����#s;������I��5�����V�g"C�}��F�������,�k~�z��t?��y0X��gm
!�M�q�����$�+;��[��8�8>�0'�������u��0�XVm��
|����O7�F���
�q
)7!B�H�v�g�����E���AT���o������[��`0��y�Io�}*��U���N����-�����4y�f��U-���M�V9.u�'o_�	�����3C����q����F���D�0�=�
[�j7�<Q�Lg��,����J��'cv�=����<���L����@��_������+n��4?��=�Y��\�����o���Jf�av��]`�+ii��j�
���S\�d�����|�hAV��Am��!�����2(����������@*�%��/2"��F�N�Wt��w0�<���p;�p�)��t�T��?������a������*}���u��n�,t$3�[�������k��S������Eg���kG:x����%��
�4Z��%��������M���"������!��<�4�}4�y8n�j4��w��BW^�'`i-8���'������ii;�_W��ZB#����f��84&�U��v2Fk���M�a��D�jP���v��k�i��Su��R����m��R�l6
������RSG�&AE����<��)&!��eU�9�wB
}��Y�2��R��|wRY�� ��@p�,���0��G�g�H�������oY�- zGqO�Ho�{6��������a�a|/;��w{����5��G����^�
'�(���@^h�wfo��)A����R�����R����A�<	�t���H�|�Mb�np�?A@7L���nt5�R���&�F#�x�������y��*��K��g��p/�g��A�q�l����~/b��i���F���<+�������AM�L�/�/Ny�]>��p�j�-h�aX[���G��~����P���MT�y�2Kyy���_\P����f����_��u�f��Ke�f��i��5�ZS�M�����}��TM����s����Z��[��[�������L�
6�id?����������������]`LS��8���-���?6mm-hB��G1\�6�J��Azp�?<���*iJa�7���Q�����)�	`�X ���(���
�6U�����d�g`"�>����50Q�|��}5��{	L]��#_{i�Co���;AA���Cq��2����TT@e�r��{!�N��O2�\�����v�Z�,�4�{�~{w?K��pMu�)�B/k���!�"��hTW�jg�c^������U#F�mA�S����!N��NK�pT�&��X?J�q����]k�E� B}(�&�����
P~-af��9�(^��n�_�UI� �mR�Cc����!5f"s����rd�-�5���o��_��������3�GB�0m=���l���1�����v��k�_�������#�a���ak�����V��=<���f����Po���!�{����:���h<�%Y����O��#��g8_����
��kE���E-�M����1��|��<m$;��N1���q`�K�-�x��"��s?�� v���3����5�Q*���-�fB��`H����G3�B�Fl<���%3�8��T��-��$������n1y6�DU�$(a��O����@��:.�%��2�^�������N�N���@�@�E�D���Q�{���`��$�i�)��0����@3�wb$�1��f�@�n��y
����'9g�w*����&0[3�F�D�S�.3���O�<�Y�#�"�G�"7:m.�������_a��QoI\="��M{`��>M�Zr.0Ig�qZ��x!�0��t8J��������O��6�{wO�w[a���f���uj�5w{{����������m����O^�]��/�����l���?����y����
z'�{����������i������������t����6���%x��F�)�	X�Q��*�,V^��B�]��m���F��zX���f~A}�]������o�&�-�3q�������:]�
�0����~!�������[���P�}m�^d����L��@�`��H�2>�9���=��4���^����d�����
��r�2G����$������G��nN����8l�[;:����aa��O�uw����\�`�����|/mB����xd0��/n��#�}�tn�h���CZz�����4(�`BVqhF�u6p�;�$9��4�.iq��W�\�����u~����y|�03�������$>�(��l���!����O��zr�y�x��'���Yv��4��9���T_��nZLI7���d9�%��]���'x������G�7������b% �|Lq��<@.��6h��Y������\[h�:l�:gx��N�����+��b��ok�|��|��$����/�\�h���.�
�[�%]F(d�T��K1q��R���/f���G}|g?���Xu��d���`��8i���T���u	�c��{��7N��i~!  s+����<H��X������L����������g:���//Y<��\g\����?Y�C�Le�A���E,���j;���Dy)g��l��Y��R{cnS~�TC��������OsL	Z-=9�����2�}����1[��mi!)ZQ	f��W;� �z
X�(�`�>�<���p���O�`�w����.�~;yIY.�O�k2DUm�z�SfD_�f����@M�U��Q~wR�=b_�3��P�'{9�/(����AgQ�[jD��^����B�kIN�F.DZ)#1�����t�_O�������c�����Z�W�=Q��+"x������/���Fc�me��l�����++e4�b��`��\����<�J��2������T��x"���$�����6vDm\��|\�=�o�.!]c����0:u�U��)����Flc��g�!:77[/O�,�?O,���z8���~go�.���mLYYQ&�=s��F��J�������LI�,���</k�Y
�}wqr������oz7].��b��������T��b�xK��)��V�M%�6��V�8[,�N�F��6�����@����T��BJ�%~:P	�(#GP	�J,����'�]��L���N���w��
ix��s�Oo�7�"����2��js�Jj�l����w������Cc5ApB�3�a����T{�9�p5QN�^�������p���~����g�zO��\�}bjit���7�s��m���x��s�lb��z�����rT��B�T�N��������C��(�*[(��n8
�5u�f��)�&�i}1�q����v�4�(6z�&T	����r+�D���
��HXJ������#���K���?�g�����=T8�h�a �$d�������z��������=p`b3%�TM?�n�(��f�G���h
'U�_���H4�8p"
���]��iH0���W��D�Wr/�:�N&����0��������9sYU�5��	@������l��:�� ��Z��wl��[}`�R+A��av�;�$h��3��u�B4~H�b�:�e�Q/��B���!����F���Z��C�{��F��s�_�,������H2Hc>��������T���4N�O�O�^�m��zg������5�yt�zhg
k���e)D^��Q�H������J�UN���g��h�/1�,�� ]��>	��E�&`#C��yV�0o�&6�]��S�6��	��|���tO�����'���P<�Q_etG��&5j�V��A��G
�|X�6���@������>B����l<N���0]����4M!R�0.�z���Fx���6	��f]��27���Ke*��5��u+l�*�L1��[2����wOc����D���D��x��M�{��G�EE�t�����e��G�q�����7n*�0U��t�zt�7
;���2����L^"$�m:A�'����H`��>f����DJ����9��N
������	|���!O�'�I?��T���x����W'g�Mi^���:�w7��eBNa��z	����#��������W��d5��t����q��Mq�����N�c#��n�o5����9��3	������M�_��Y0��f��S8L(Y���2 b������K	�U��i!��an��C��������^�m�4��k�n�<_.j(��le�@4�HdJ �3�����nc���#N�f���X����
B��$��N������@�{�	�����D�(��^� �C+r `]�ta�>>����Td��`/�X��w@���v"���}�I5������?C�"����������ta�������[��&&��,�,�8���f\��,
��|������w����X��V�������}6�y���R,�gE����\?O��#����A���J��H�P�@8����F�$������,$�.���/9���z08/<��1��K>?�����	����h��Hm�s�7�4L���x ���?�
����8D�S��p���g��r>�A��]l���C�j�������nd?EN����KY��<�s�s��z�.��F�W���A&���V��R��F��`�9�������Q��~m�+���������}d�|�$�L�D�A�N�D�(�����A=�����
Az��,?2_��BaD��A"Y`D5j.���
w���v\ ��qE5������;%�������p�Q+5����� S`�����n�T��z�JQ��H$��)6�����5_fy@���q���>����z[<��g#����m�EB�:kXP7;[�qbYLe�-�w,�Z���.3�����/!u`�t
B�"�.����������M�
�T���)X	b���(j���;��X�-��Z�����M@����r�t=���$M
�=c�~�.[��d��Zv="��)2V]�
����>��M][|�������1��x�z�S4+���w\�������7�	O����t�r�=�%z��{7��f��awj��[I������������d�f�j�\�z��1���PE�Al�C1tA %��(�����/��B)I�`�����{��	�|��hC���g��
C]�|-��uf�CX,+.ft{����6ie��9�l��*�b���?�6}��t�����t�>w�f�������a93}��6�=�U`�w�8�_�rR����|��H��xI]���L�A���H���,����TQ�A�#HH��xE�}J^��K�j"����@3�Q��]�4����&��]���\�"z!n������A
�I�2�m20�(�A���O'|������c���D6�����^;�_J�U�]������������f�9��'B'�����Dh���)	7�����/�~8��Q�^(�[)��j�	�j)f��������3��h&_
����{:ChY���s���IJ������X~Zl2<R!k��$��^z�A?�l�.qn<btg}-Xzh���~��.�;%��*�J��Q ��	?��$07�y���U��W��e�x�^2@&8�Qx)J����B}c:I|���(����������]�kd~0�;�0�����F9�>�=+fXY|���"C��Y������vE���m��k�V��z.�E0��vu�HE�'��"|e��h���"(H�����������������t�}��S��a�3�����}��%��L����Ca���#k�E;[9�t@��xOB�|;��fy7������oh�m!sU���&>�����T�Y��A������.U���?:������Z�4G_������S]�y���`s�'(��DW/\�cl��T��k�7�i�1����>�n�h�W�R.{��<;��$�h��<���7�o���9~�?��|b��(��(��H�wv@�����T�����
�6C!�bL/x��
��h��U���������n����j�7������I�-�w{1]����d�e�sp���Tdf�4���p���U�'��sg�qm�
1�	_�7~�*�g�4?����M�O�Y��dDH\B�6��7jH���b���!�7���#�U�rsVqY��I������c��l_���7j/o���~
�X�T)5�d�c<d�5�.c�J�:960��C�g&O���+t�%|h��S�t���^1�?�1���E�e��n��J9:wzj�*_��^U���^��_��������E���Y�W��/��W�L������dM���.��AR.�X"�]q�wW���
�+�@�����������N��K'I�������Fx+l�<��,���V$�l}y������Q[p]���f.3a�,��C
M��*�E�Sx�Y:��^j����c^<��2���d���eI�!��)n���A1 }�,uP%�R��T�+�U'��[�:$��(�7���&<�^��}~!L����%�������	�H��g��+'��Es@�&�\ef��qr���Z�9�H����nB����h�-�Y�<'�F)�XUm����
�@W��bi��92�l������M�Kr	�YFG�"0]����8�������v!V�6apFT�d�
� !��K�CVrM���$(2R�AB/�X��J�y22�Y9^$����p�`Ce����fO0�)_��qddT�U$��f���r}�g����$��q�*w�)�Kz�8^p��e\�t���d������z�]�F��d�q;��\�j��95�f������?y,��w9CO�E�/������a��7��-�DM'|'d��sg�)���4l���U'O:��$��}i<�'������03'?�x�����N�cW�����C��(�����6��PB����~�r�M��%#X ���������Q�J���vY�����P�>�W��J�8knd�G��
|�h�}���3���B���.U5]R��k�[�yJ�b���0S�����������'���,�����}b�wi)������i���P�|�)������e�./�q�@��X�	�>��}t��x#�P���z������&c�0����+�([����	�2i\���*~Qg/z�Kl���+s�vp�����R_Hb�P����bZ�6r%��T����)��1�+n@�BU�H�9�k(�07�5L�i���M�Y����Z3���_UH�\b4�=��9�z
�2_������O�^fTlh����l�����8YK�������D(�:h���lI���,l�r�������"�gKE�����r4"�I�SL 8���j]�e\	�*n�.�,�Z\�6��}�(��������_#��B�D�����������,-�����=�"'gg��j���=��"�Ru�_��x�9Y+V*�����?U)*Ns�7^AU�mv��p:+|��}����%�|N7[��v�g6�SP3������Y�y�YU�}j��ndo0�����Bi��?q\AhSI�f�qQw��B��sRj_3,�%4�\
��)��s�o�QBt
(Cu�;��2���-*@�Y��g���k�>�d����h�&�l���f���j*������:zdu��|
�g�[���3��#>���J���y���G�+
�A�������*X������rNijk K����Weu&"�)�*v��P�Z[B'�uQ�j��9����>i������<k��um�����0U���m8�N��K^B�55P<���boQ����DI�Z:�W�aO8-Ob?q�7h�������}s������M��b���h[���z����������1&������H��#������Z?����Z0��K>!9���G��>�F�+G�)a��m�!��VVI��c��9>r:E�������4�r��\���$����B�;��U���,�.x W���KP1Z����f�4�|w�G����18cB&��!��q�h��8i��Q\�=�o���b���������/��1�VnQ��{�G�\#A~R�z��F6��B���|dh0_��
�����,��o��P5� '���gL
�a��f�Ms�8j�5�������l�y�m�
�>�����fY��P���A��VR!3wjP��� Q��`E���$���z�LXo��Q
�1�M"�W�N
,�<�'�t��������	����-YB1��E��*��p���g�����t�=���'��#���0$0��	K�T�5U"���	��	yrt����/��x�	t4���Jw=`��&�.G�@�	�K���.DS�$��C�Ic1S=���S.Z������LW�0���\��B���2H�sT��	�*����5����)�W�t��Y�L(D�
�=+�|�D�o/�Y�����!}�����R��U�B�:�|n��6����rX�]�(�QT��E�p��{:�W� ��%2>"�9_9o$��x
VbA��x2s1�z�f����J�(b)��c�
�@�dQ�[�Um]����92����n����YA���cJN2BR@�����X
�,��(��:U����"�)`H��j=�Bj�J��}��xuL/pK���!wt_�!�^���]��~��2~�*:�zb��\"q�Bu	k�A����T���sj�+�������yT�?9{s�����=����+��l��M��>w���e���	�����/�/fvZ��`0�����
������B��C���<@+AX+���|@��,�&{ �@	����,��C1��K�8^|��}uN����o$�Y�r����io��R��^�����o��/j��R�;^�"����>S�bf`����f^��2�� ��������*;��7�u��)=����5D��
�H����N&E�a
���/l���]�>���t�<��^|���o��Nf[�&.v���P��1������;I�F&y,��hZ�!���1	���P(�#�������3f��>��Q�p�Y
58w�%�n�e�=!f�3�Z���9r�[M:��Z&��8&
:1���]���9@z�rd��.���� �o4v��N���������0-'���#6�g�9���3.����#�<�5E����
^I�S��4�}M�Vj+faU��#4Q��F��{�K��T
����Te�yQ���[G�����e���������6�Y��;�J�W/@t'�h�L�&Pi�����Xb:~Q�Wi������KV����v$��u��
Bz%�8nJR5�����������x5���URh�n���<i��G^V!�x�r>j�����������rZ>5P��G{�����le����CV���x�U*E�=-[�k	����5�J��������+Q�������M��d�]s�����M� ;bwN�V
E�����c����/��.�s'�
\�<�/���
�'!
�����^�j��(bG��K��9�zR�5�9��Q��#v��!�J���;N��y����42���K���M����'�������7�
����~n(�a�b����Mtj8N�)
��{d��P��3}q�i���}K�L���B!��}�J��]�?Y"���y�)+��:��+Be���Ti������D��#�j�����8ZWr�,��Z�RCq*��.�Cf��]��@AL�$�+�8{�'���z
�?5��}��?����l��|�t$C6���v��@P3�AM�Pp�9L��V�����rZ%�x�<�/�;�@bD�������]���Q��I��_j=�}����qp+�d�|��{B[�4��m�,����E�m:���Uf)��{a*:�:0+��y��|'e�Q<0��m��Zp���d�����iaI�g�������LkY�I)��f�DQ��E�>G��W�gq�Vw���-�H����d�[����G�>f�4�pP� �B:�:J/���`1�%����F���]d�V2F�6��T���^a�v�ouX�!a���a=P�=r#?0:�}��b�wV�;�z�$
����"L%|@����Fv�X@WI������0���d�$�U��y�_�86��m&��a0?���� �/�	2!��8�s��(��Df����`�^L��`U
���Y�G{1��������&d��e����|/�������**��|�`�{�����f'G����@a~%^p+>������������������BC������#>���U����h�~�6��<0:����`�M�o@g�Fc��lgVJ)���X4�rE�Nl���fci
y1cn�<
%�q�!�+T8�e����6N�J�un���'a�@V��U�����������r������<y��m��D7�	NF�/g�5�s���&X��8�'cGzh�3������F���g+P�R�a|F�Up���p8/)��%�I'��K��f���>����3�����g@80��T��aY�����;�$-1E���I%�{�#&�{L�	���r�
���a�~K������\+�m���,�qP��('D8`��"��6�M:�/�d�b��~Q*@�@L�t�)���DL�2�o6
��Y|n��
��.7\o/X��k]lMx.���2zL�&(�i$��t���
@�Mb����cJ(�Zt����:��h�K]�����0�:�)F�q5�eq�4������96�MM��#G���q���G����F��Z�9�i�M0�w�t�[�_Cz�I��]V� �x������M)�@���i>��^�!���$B8����gc��0�q��+
g{��[������-��.(\m�M}��Y}V�����C��5
����7�c}�z/�����|�FR��>f�����
��)��(��Z�`��6�����X�T�b>��ok��XxR��������� ��*x�/5������2L)W�b��DO�:<��J)������|g/�LDF# &Uo��2����Ot��R
����	�[)C~��k[�i4)pi��n2 ��B����&&���Q������`8�-"�|�F54T�\��UY��b
�����{O�;�$�vz���f�����Y�>��VF��H��J��4f��Q*w5�L��T���w��()(.�6��Q�^-�I7Ss�Fd�s���.�_��!`u����M���:``�,����;*P1H[�2,nqE��i�/�J\�)�<���1����^�D�W
��9�N���z�qLD�L��� ��+e���)�/�`�b%�.��%(�G��k67�mi��k�YS�	�]�]>!s
�P7b�D�<�4�as3`A������r�lnG��N#�Q!y��s�P�$��s{��!-yL_�pT�f":��F�9@,]�gE�}AJ`V��*��s�+��P
o�K
�I�o%�%��7|�g4��X���'�����,��#�e��w%�p��1���ze�."y�:]�u�#�u�
{�����-�b����;�G�/�$�(��.5�VVD�Z�a�$Xv��0�T��CNiS&A�%�<t��=�#�&"��3<���(�M�+�� ��2gm�B��R!��r�}�>O�H(�4�	1������G��MD��mI����l��z��)���B�!�w�7c�$[/���ZI�x�s�@_A�G�	�1>U�r�M`Pe��*�YE$��Q����c��\��By4�SK,l�������:)���j�
.�^�kld����=�yQ]��s��)p��Z(��e��[��/��g��Ub8%�Y�"����D�vHrX%Id�<_Y�!�#��IA������V�+�9��GKV���)Awa�������X[Bt��c��|m��X4�N ��x����������<e�Z
������2F�������s�Z�DCh���x%R����J���$���7�Sc����=�,�	�����&�S��<��
:��P=	Zdj�0����}��z�
n�P��B�w�BTn�\�7[����[���+��
������b��>���!�1�Wy���c��������C��������^]�p���������)����]^M���27�\+�l~7l+�;@�4@��[3�`�����v�.%��	��R'�����8���24&r��M(6����S&���O{�X!M�c�tb1��L1�;uy��Tp���(@�T�9�2;�lC��e*�Y�qS�loF������"�mA��k��Ua����D��x$���N�
���Ry��9�'�~�o)���bT(��A�U�JZHf��d�g-�u~Fj?�O���6��;�i�[[���c?�.�I���R�^W6��^�8���#�/E��)�u�n\����3��D#ZoQydc9��Q]��B��Y�����������U R���^�\�'����s�=�<2O��'N�h���F���O�P0wD�j6���-��i��F��
:�����������A���d	?;;�6��?'�f����j��f��?���?�?���m��$1�/���=����)��x1���15@�������f��fJ���yv��_'��U�2����3TT7�.gkj����
�jX�a2��hqO^�Jx:��R���&�|��|���!��
<o\���	��/�U=����n����r�2:�e�e�\�O��I��[R;(����:tH��cr��$K��t)r���K���|+��RXC�L*�P�c*��<��{��?�a.��G������M��	L�} �'�����B�����|����Qr�0������}��|.�^A2b�'P2�%������GI�w	�F�C���i���#����]�RV	�q����L'A��xp����&\���!�N�8���{������.@��o@����c@g�|yrq|��l��)A�a��f��������f<��Pe7�U��
�~�����_�z��������2.��Yu��������x�5_ta��MU#Se�}pK�h�/@��I�
���A�CtY���y�
��G1�����?��<�3�w���
�~D~c�����z5�H�5�-���8���j'bw�r��l1u�%����*����d�A�p���G{��*���a>�7V.PB�A�����L�EVL0m���@�fj����\/!��R����i�/oDoj�kj2��Q�0��a5��9Bs�E9
LK����e�=H/��&����Y6�@�����	w��R�=�m����0j�r��-j��d���������iH7
(Le!�6Q� ;��/��-����D�am;����TKK�u��pj��[
&�c��;n���U(�]�
u�l�t��n�xIb����/�w�o*�y�������Hr���"L���n��'K�Tm!ka>	�h5	�F��R�aF��U��~\�g����d+zi-`/��u�����I��A-l�u��<W����1�0f���^���^f)����imp��Q~�$��Db��v	!���$V3���Z2C���������K�����60�����s�L���h�r���;��}T,����E����0�2Rxn$7���e�II�����(R8(/���u!C�����u!���2�c��.�
�aa�O��������t���[dv,��z)�@��(��U���9gWPX2RKI��N���?.�}ps���?y������@eT/�&ri�[���������l���
�-�>��d.h�`9��7�Yp��0��i���������������>��_�_�.�J�c��3�b��a-���_��
���/\P}p~��D�q� ���tp�W��'�x�W|EG<8����g=~����}�������GUsR�,�l�N�m�d���Z��o#JLQ����2����	�e�}@�����Y�(�9��`?AhZ5�{+%�}��O�Fe�GM�J
�0���)N
�zE�-�\���z�)h�6�C<���F�����|���r�xv
�����/��=27Y�_7�����r�.�m��n>������*t�c�B�%Jax��#��1���]�P��\<�F�rn6��5�;����O��{�
�?
ZJ�l��-c4�@�
�3��^F1L&� ���""�*���`��S;=���G��f���EB3	?�f�G�`������E�k��������!]0�|.
���5
�%���j��1A���9(�u#@N@I�R?��`�y6���d��$�b�f��|������(�n��� d�.
�R����ip�8���NA����7�(�������'�N�u�1�hsF�+����'�@5��L�h�.��X��E��
��F�0�P���.��V���eYx�P���E�O������C��%v�L��8���)�9�T�>��h-
��Q�'�/���F?��s�PAZ~3����R )za��1�w&1^�iJ$`��g����t�$\�����_��C9� ���n�*��J>-�[T�A��3��G(��bGE���z��
��!O�*O��iB�,8�&U��\X��l[��aP�e����3��c��}!�����d7��]Umg�����F�M��3^�9S?fY6C������"���w�%�.���b��������2���)�{�^O��o?�jx��?�v�B7M��fl#�!p�J�1�����D��=I^M��Ti}�����B��P%��`������>�n����!`M��L�����T��v/�gxr�b�����Qum���Cs���d��o�tq��@������b�!��7�bam�z8d%���uE��2t���I]hj����
{f#X�����b�R�7��C��lsPHa�-��@B�,[-|�e���nyP���!���w���
�L�7���8�U�u'p��r�$��x��(���t)63��^)ge��������}j�)�MZ�](��G��%��
��`�m��>��N�|�|���	��*_�P���s?���o6�(���Ip���!�((L�����/h��1����u�p�7W
�qs��q��?�lH-c��Z��5�����Z4�����V�s��J�������\x f��_.�J��xe��[;
@W��
j�CSB��q� �>�"��+��,�1~�3�A	
�aS6?'�?��M
����S���R��*���)���d��d��M�Q:r��{�������=F���b��w:�#5�:&�L��`�w��(7~�2����&;����cMot��DT��'�jc��� ����y*z�����r�W�.LI�.,�:�F__l��U���P�5!�����=	������7��>xfd��2�7Q8�|�V��/�b12�Y��"�gg��}{����[ ��R<���F��y��r��z�6����1���<QasX�~�L��$$|�vU���R�L��(f2�W�����I�4��,n���`<���U��N��l;Aq0���8��~��)X���m
���~g��A�A��\�O�_[���vC���[)6B�=��B�(5P�|������=����EoO������=��A��Ja/��F�z�Q�S�_�����.GET��<m=�����2����C�������J��������,��6��0��$������:�s@��k���0}��l<���������61���9�}C��W�~����������f���p�<[��:
+%��W��X+�F]P�B���@��N�>
�j��_b^�k*g��(|���:�X_s(�����;�����:�Vi���vT����ln��`s@����{^����u�j~���N�����0L<����������)f����%���	}�0t~��|i
��U�zl8_�N,��,��J!Yc	Y�8?�38�V6h�#P������X.��MT�B�n'���V�"���(�F�E
 �t7��|��6��~����7I$)Az��`���Cml�T�j��?_�)	vV2G���62S���s���s���M�)�'T��<��������%[,��
)s������>8�Q�qcpR������!K�K
����.T�������A���:���}�w����[���������dV��p�6�����^wO7���#�O�T,�Z�����[J���14����`n]}��D�X(�g���f���2�U2�a,.�?@[im��b���;�v��.4��x�}K�,�*���S(��f��Q�S��,���h�gc������u�B$p7FU��E�1C���M\��)C���^�B���v&pdr�@2��C�<���A�E�,�����+�Y��O��*Z���I`�,U�n4�����A=�`�w�/`�e}�s9��C<%[�e
kLI�:N}:����7������������Of�aRk��#�U�%�dY�Ep��4�Dy����)�/4y�#�H��j�;N�]
�/���R�iS�<C�'��'�b:��,�^����c��
LX��i����A���\.=�r��7��L#�nT��i���xY%��6���3��H\���
E��k>!a�,�Qt,��<uH�Z������H)B���%�OY	�T�V��8RUA�����4p�8�������N8J���Y������e�J�#�o�|��n6��� ��r���h.eKH-�$���t��0��������&��i���LK�n��Zf.v�6���#��I���,u��� q`f�v@��������*DR����0|[C�kT���@�\�������
o�V
�^s���}���L���F���ZHQ��^��%�e4&t�Kl��+��4pM�*q����W.��)X�������N:��j'xU<�7�l�T�
Fz�v��@��2����E�rN���s�����U����%�Z���e�U+��u�����������1�+�*@P!�
)����A�������e�,g����kW�B�����L�}�)�	��������I���EWop?h�C����j@���%��#�l�c�!��0��5�H��i�Q]h���\#8HF1$q:'qs&�:,�
8Nb�m�Z�Q��U��}	�`��$��=�W��#���L2J���4�m*��}(p�2$��@��A����(H`�Y$���h��lq"ELO3���������xjCB�\+�W�KIy���aN0J��f+<�>�a���*�B.3�5(m��"������y���C�.FQ���o�pd.��c.a>���s
�iD�-kE��c�f'�q�D	���p��J������f����K��	[����(����yu&���j�c�e�v�l��56�;\�������S��{!u�y���}J����X��U�4c���L���+��a��|�&Pv���c!��1O�
h�'L��S�)��H�6�w��M���P��`�f;�7�6�{L��~_)|D�_�'b"i/k;<D�~�dYuHpVE��0��'��=��)�Hm�i���?+r[�����0��OI0#���w�II1l����	`q=��a2��p�d8X@��*��=��s���#�kG�RJv��\	���+��[��R�w��VZ[���*QmU��PxP~Q|��z�Xq�'aD�����/*�xW���M��E���yw�yq��B��~����m�^���p��H�q
�Q�1Jd$8��nK�s.�2pK�F���Q;m
���zb��������DuSgd|�7�$<G�P<(M��^��+8�����#�ox���^Pj�\����}pu�7�w_`��p����}����
�P�[j��&Q�5Sf
�wl�������������8�5[i�p���U�^��j������������}0�&bf�����$�����MkI�%fN&�}���^B.��e��|�����am7��v�����>��y�C*%��qA����%���TK�?�U���3�t`^ny5�52��4~��"���<Z�*D�%?��
y'����'�A�xy��}�����d��h����U�1�o��a3�K[��V�1h���������R���=,j���?���*����������K�/��M
�N��))��;�.�)
2�����d�F����<FG�%���PK��M-���<LM��cS�{c@B�����.����$�4��+#�7fM�JKA�B[�F��hmm���@2����������l�����Yv{��y�plu����d�.����h�&M����.�l��o�owo��K������{�f7i�w�{���4���	���t�f9�OW�3����i0�������{��a��v;�n����[��n;�4�����vw�w�=l:�����g����4�O�I��lmA5O��>�1R����O��������5�������������$o�y.4I����y�7`���:_���a��&?�?�x�}�<|�������
y��v'�x�(�sk����wOI� �%�6�>��2���u����|��4�@��7s����4�X�����L'�0�a� ���lv#�a���4�P���'� ��%i6��/��Y�I�&��%Iv�~�g�9�3�u[��������2[{���N0T�	�AKg�2�$�����+���Y�6������{]�o';����DY��i�>������	�%��}��Ww�����Y�qFO��[k�������a�`�?����i��<�:��N��_dV�V��-(�,A{��;��<������8K'��9�����.��0k�Lg�"[u]�xaZC���
FZG��R������3N7��v��WTj��Q�6�S{�����e~i�T������E�����=s���7�[I����������'gg�_�C*���`v���
@�uX��V]�o���\
�r�w�W�0����?�����1�>�md~1�>��C|�S>*f���-Bz}�J�7|0����\�7P/�q}�%y���3�f������?l���s�M�>9c����������T!p����;t��b�s�_Z�m���5*�qE����:Z�d��?�X�j�x-y�,>R��d���1�����l8��B��`%?��?^0J�ja&`�d���XS���S�E8���E���'>�a�*98+y��+�,�?{��9���|U���F�	��W�l3<_�����"#ht�'o���J��� %xCF��TH�E/<�j�FW�0����������p{����{�����<���������Du��%�&�]*���43��,k�Kn���43��N��R��hq=�.�����_�tA����9���z�~����o�@8��M\�6�����������C?d�*v�����e��J��;�1U�����5Y��5�_�]S�Dk��k�/~��������-�����w�<�M�����$y\	/x������C`h�{�����|g&��I��_�GWW��L���P��(P���n#�uX���}>��\'�pI�]n�����/_�>���4�jz;�����z���|�G9��P�uI���n��B%���a����;�_�2!7M�����&"���0�}b�����xfNj�[4�`'a��N�j���t�{��.L��)A2��Vv��Y!h��=sm}�_�D��<���P����~�S��������(�;?���p���F��r����!��~xhM1�c�"%�����\$:�4��X�c��:����N9���/	T�a�[�o�?l=v������������.Y�c��W�A�M����7���������eh)�W�(���wI�,y�n�[k��.�����*��*��3�8�o�����_�>]��rM������,�����0y����S�Jo����b?<vy��M��hN���
�����1��pV����1k��:�u��Yk�V	P��U������Z�u�����d������%����!�����84���2��o�X$_�Y8
r���P�������P�(\6|�q":a�Er��*��������V�U��t���%����1��9����L�4�"fo������C]_E
��M5�1��������p�K���)�>��j��5��R\O��!�U�����{R��(�v�4~>uu
���^�����R�c��`���+�J�j~��r�^����(R�f7F�sR2�J���+�|�u��lR�m��!����>	Q�s��w���������r��{`���[k�.VXj�.E��Z���Z�Q};5���Z}m�h�x�$w�����xb$����chX1�.6<`��K#N�&��hHM��	. ���Y�x��C���]�C�	 �i�vp�����y���F@�`%��b��XS�s���	��.���iZ�=�n�[�"��/���Y5�p���.�X���aU1�-�)���5��4=��
{�l��g�wbw�"�k�{e���B^y���$�$����h��:!��g�~����v��G�T`S*fPL��wR�I�L'xdH6wsh�6�D�k��|��
�&�u�; �%"�ng!���N)��g�����f�&�Y�d���a��0ww���a���qL7qE���Q�n�1`EL����~���r��}�����~�X�D���!�fy��N�������p�e�p�������K,O#aCYk���j�%1����.	�R�y�����W��g���u1��b|�m�{���8�U}\1��/���	���6��v�5�N��H���J[�Y	L��.���\I�^�f���yT$%��+�n`!q��k-8���Z�a�]�5�q�4�]b��y�%�o�u�Kye��>�{�\�C@:�*20�h��xY�X"lzl�Z-���56��9�v7����$0��=@K�
�_[�w���N���Mv��� [��l���/�7�.�WY��j���&,�}���#�'a��)������A��������.��M���%o��|U\o��X6n=�}[K��37(�9�C�(Vxs�[�v�K�J�yW1Ua��n���7�$���[����S��C��?d�����/�+�
���,�$�]sE jF���g�qz�
D��4w��l�T��[���M��5������/�����}I�P<��*�/��(��������
|x:��������d3�6��>rjw�w"���e*1����Oh���Z�d����Z�����1bJ�0��Xb���`�sZY��J�Gq5hi�DaP�ss�f��b)�c��$��M{S����y/
gP<��#�)5��<����V��U�z"����5��AA��:e����0g*_�u���,~Y`	�����l�t�Z������Z��4��Q������Q��l�
?fOZ��K�(��.����v9e��91�rn���������i�����
~n�o��r���4t�Y6����D�=T8B8�W#VpkTo��'�����mg1��7�:���N�Y�kN��#�A�T��<��Q=�<�_���T�sL����_=�}�y��������ihV�o#��x1s����1�����G����E�����rl�I���A��4{��^g�w���v���M8���6W�5ZM��@_w�k1#�~��D�28^��P%o��"��+���]����z��n�JU��T0�^P�Z�F�^���y<���_
�=��z�n��M�{��|a$<s9�4�*X�L[L_�zs��HK�h��+�3{{�=L7�ot
P���Ed����^�e����&�c����wg��k���xH

�~c�}��������T�a�h$�^a�v��A�{ek8���H�q��!��:Dt
�Fv�h:?�Ka�Bn�8,G$�2���Ot|��K�[�oP�a�Ur	�%�����1�	5�w�����d�i7�R?����Y�-+�(!�����8@��Qz*2N36lX@I�p5EFq:�M-������C�/���|���J������<rl�Y���T�
�J�:{<1MM��C"�h���[!�����'[B�W.���^X�g�9@#�c�;� ��{(���M&w���d*_|�M�B6�XH��X_C�f����
�5z�@��Y��	�Hf��`^��_8^;��}f*���y�h����\�;����4��<�1��������dt��N��F�������w���\�T�>��������	���yq)(B������M��b"sq&W��p��hL1�}��s��$��<�U�VT=�[�rbH��}�D��E3v���G�FnZU%M������x�E;��[��T�G1�-���rs ��qv�|M�S�Y��=���8��b�vE����N7WF��n6/���5X\���4�S���|[L������#q��K�#9��B
Cs?G�V����%%*���
9%yL~[A��kCr���J��A�<
�8C���;��E��������H���V�a�nI���o����dK/H���q��o��U��N�n�]sHj�f��{\a�7q��~�mh}��I��Y1��k�W`��f6)������"��R�-��AS��~��.���D,`���*�Z^Q	'�x[�^K��� �w18nE}�~���x$��k�w���j�Y�$�z6��ncm��[���b�k���a� k����~��7l������l��UM�(
���Do����H��^*��jf����6S��%������Py��������!\4\���)���	h�){R>�W
��l@B6����"��`���@��-D��7[������$.g�����;��^�'*��A�������Pe�A�����U����i����B��z2{
�!�R�`�
�1�L�=�OP6c],��X�|�m�+r��n� ~�{�\R�5Zf���}���%��B�J[���ds���M���4����pwr��4586�i)����'����$J%������ x��-j�vco��Le���l���ZCCe���n�l�bX�TY��+
�����Jv����[�D��_�d7��/*�����^�2xX6Sl�����S#%xM�~��~k��M�v����:�^w�����V.�-������Zm#���2��������h���O��]����'s�����E0s'?�^\��������__�pvzqrv	����|x	1���~�C:�a>2�yc���=�^�Y����/��^^����/��z�f��>�P_����o_|�����rb��H���4dK��@#��r�Lof�������	)/�E�$��ED-��-%����;��=�uw��n������p=��W�����Y��0���P���]���U����������?�|��.�k/"������h:?��������
TQ�fU,X�KU�\c�
/|��$���-�W�$
D�������h��p/3'����n% �.�W��A���m0HI������i$Y��������,@<�����5-!
����� 
�����P`Y���������
!�3}gC����|������;h��qL�q�vv�����pw���-����#��9	�_'���^n���=��$/+�!�8�|;{�0}������]b�:�vf<��$���C�0��f���3��g���U�� �r��T��\7���f������]��mA���f��R�R*���+�Z�	��_�!��Q���=��8��f���8�r���������W/�%��	P:,�(�)P��Q'�VB���?����+8�;�����\G�^R�x���+<\���Duf�����)��A���i�g�����f��6��5��d�\�@��Ba-q|��K�b!������Jr�U�M(���L@��� 7t��b��5h|�O?�#BN0������r���7)
�=����@���`��TaR&�����O�0���Z��q0���y��-6e��[)���.WF!(\�0�{�����g���G	P,af����{�����������q�w����G��p� KgKn<���?_�;���NR��������� �M0.I�3{���v������t�ywz�"(�����k|�sO�Q�����?eM�] ����8�b|�3"u��e��>���G<�������?v9),��.�N�K��'�p=�
�q<1$��D��@��s���4�d=��S7�-�2u��]���6��Z���"��2�Ur�m�����{q�>��m�1�\��(����V��S/e������:o�Tg�����I[%\��K=�{�Cn?�e�X���MFV�R2��*�����7���z�{��qu�����1<r#u�w�PCs4��-���%��v����K6H���L���Q���4�Sd���l57�O�����w�G-4U�&�7f�T��M���|&�U���(���$sf|/��/�7�����9�dw��������-�I���zer��-���urz�Z����!�a�"���][���w5���/n��V��'�/��r���$'�+x��`*�l������e1�G��l�")������c�7���w���F?D�i�x"�"���C���Z�h�6�����R�>g��V��Z�)d$_�}n�>�{�[�������U(�7���*�:��f�������C�����p�\���@;�FKk��8Y-�/�����%k��5��R:���Ez~ {�>t�I�"��cM��Il�r��T���Zc�Egv��'%��t�����B ����'�
�O!T{~Gj)7��)�H9(�)f
�`<-���@�1�c7�8����������������Z%d��������}���;�[����7����S%����v��X�b�q��q9��
���������_�0&S��,��/.u����}Rc"������g�O���hG�VS�����j�]�FW�{+{�R�7�Wl=D��<���g�(*�6�>J~��~���������-C}G����z2���U����{�O:)�����"�k4�!�J�,u���Zd��o,d5��v4fL|��Yt�#����i�����|�5����;������>pd�C��������8=����+Yk9q����`[6�V���w���li��l3����Q/cy^��z��g�[fh��.B���������J_�i��%6^3$F]:�!| ���R�������K�����/gyJ�KQ�����Q����O��s�Y)�Q�_*���zL�_/V���������jsX����R�8�V��f�qP4\T5�`	�����D��A(�/�J�@)��$��TL���������a��!�D���zFc@��lN�����w��	�"��a!�"����IW�^�=�q�u�$�*�2���+��
Rt
9�'!�J�p���2���/1]g=�9�&��>�����amYV����f�����|7/�F?�?j����y����{���?�����������:(����������^���/���c����j���L��1���t��X�uQ{�T�_��������	1��E����e��i�
i�=���w#���2cl�G�>�h���*����8^$���TE+��R��tH���,���M�95a�1=���m���+h�f����q��H����$��V�80)��\A5�3��.W2��Z\#v�(��E�+�X�i��{�95_z���Z���D�	���F��hP�J�B����Q�����T[E���J�62����9��g������.U]��e�So�Q0^�2�:5��c'�,n���A�G��R$4��d�7A�c��k�3bMEH�i�BAt���(�tVP��)?��>	��|
�`�Qj*hJ	��;�pMp�"�m�O�;{8 �*��M ����O]�i��k�yC�Ml���"j|C=�7<	��,�������8w�p�|����aY�Fft��8�}�i.��5����$N{��������/^P�!�o��M^[C!��A����n�0,M�:7Y(�{M��f��I�,��DGf4����C�0�e���
���low��M�eo���ZR���54�]��j�^�V��$�bc�k�_j�e����wl�s����L&cIM/���^��6�M�o)Y7����&	<Q�������}����@��1�Os�D_�z���J�L'Ls�����.���e	��g
� 7Y;j��$&��G�}��,�D�X0�
{��0%����m�X=_��?�OX	�Ko�W,b��(�a'\�w`���:�Cw���05� ����0�g8�c�L�9���<KV`nQ]A-�<��}X�� �[X	c@���:�n����@��KU���s��3������'7O�����������P#K�SK�=R��`$>�0(r
%~����R�������7������v������.�~��.���Y���pI�H�����R�
��m�Q;h��=�zbg���_��s�����o1g���S\\��0B�{����@�����:�Ft��>�!�,��l��z��~�3���a��(���n�	�b�V(@���������I7!�^1r�����7h%T2D��l�&���]�6����
z�>�lw{���v�������N�s����MTF�
��(0�j3i�}�(����=H~�8E��a.����\�@%&��Sl?n�(���'��:u���u9��{�*���e.���!#��nD�Rxr:��Q�K0��8���&��A��5_�5�LVN����]�K�C�C�����R�&������3��h��f��4��^k�F���j�P�T*�[i����ksi�;ks:���������9�%�=+(��e����"�]B`�x�$��B�����Lr5����\��cn�9m�a�n��'^�.�?�F����e�Dd���8~���#[F��i���k������,B[o�zw1�����Y�����C��p�����z}r��W��b�@K�������|�����:@A�]s��cb�����S��S�JFO��(1��v'��D��D�s�^��2��l��=�����a�B��@�?M|7T(m0�x����n��0	p�Bf8��/v@�����m�OEH���i����H�A�A��UDH������5��+�_���88�W^������)�gf�	��mv�F���h<h�'dh'��[���[D��tiU�$Q����h]�/P�7����y ;z�����r��Y.�5iO(�q��xq� <@!8&Z�l%�6
�l&���� 5�0����4�T*"6<CB�N�W�2�mf/4		_lz4����~�t���S��.�AHx���."�����Kr7'��&Y�K"��7B�.�8����
:�L1q�4YK)��,���a���z��{���6���fn�pjN�8)��PP�aU�#�>��*��(�@k�j�)&p������dY��x.��&�E�J �O�%o"�r\�0EMQ^ ��8��i�_~������}2���,�J��c���1�3%��p�E�k;��G��53W��%��e�����Ws���;� ����������ym��h��]�a���v2�����Tj4����������l��}>-���^+�4�e��=��K^&lu�
��#L@f�k��t@�N~�g�4,ND,�c�� �����h���!l��^
DG��]m4�7b����gG?�����VA*���T����������G�Qv���}6��w4=r�@�4������\ir���]����j�1���i�������4�9�D�8=�c�������W\�=$��������~���O�
$<E���V�����0kO�B:o�����8�k��T�n���`bc��.Q�T�lq:�	0��>Y����o��4�8)�� !Q�sG
��$��O�6qK��W=h�j�B��hz���S�oP,~5�	Gg��C���6yF��CQ���c�cD��� sD���c�(�b��bb\����D����M���0��,��������A��~��8���_:GI��=$n�n�=`��S�6t�����[�i�������G���<�����C�����W��
�QL/O��*.y����c ������i�������"��� �W���-����w2��|��livE�mR�Wj6Jn��2�5�z�X�c��T+�;\Zy��\ZJ��H2R�0T��].��p�v���H�����&�A�
��K!���J�|���g�U�?�IB/�JY���f1��X{r[�(.];�V����+11\P{�������Z,�GBH�
��>I-��m������t�����\����8<�����N��.��:����%AtE>����X�n)���cPL(w��B1:6���4?��Rn��R"�B;�_��,��0*��4A5��kwI�QBl�\sH`�q�!��%_���Y�b9�Xk?q�{�01ke�~)r��7Z����#a��Xt� * .��N;��t��c�:�� �����M[M���Qf��p�6��y�l�0���\����H�0�	�B���0��P�E��C�s%��b�A���l����(T���1�Y������8���&) ��:r�\��fx�]2���%�sP�F�j	�����!�B�L>-�z%�6��E���%g�I��S�})i9�,��-�8n�����x��g"G�s�!����L�Dt�(����C�B�-/�P�MC���HB@���E�l�-&��*]Z�chp*�_��3b
)."w��U�����#�,C����n_T��3�8z���y�9�f�#r�f��H;P���0�I�||���<�}��_�X�bJ����{���cT��`K��d ����H��[�n�]c��b�����2"�/#���s0����d�������2�X�}�l�M��Hms�L�;#�� ��N��E�$x@���q;��Dpl+���;�u��D����g�!�&3nH��@.\�������h�������	T/`b�������k4Dq�^��8��&?�de#��w��2
9��r:�����5*�O])����E�'�A�IE�C�x������6�	6?D ���lQ���7�y�����bg��{z1'�=�����lFR�T��gc��b�d���|��	BS����X��[K4f6H���_!3QD2�$'D�x��00��"���m`I��4s�������N�O�i����CM�!�qL���h�D��XQM�	��3���fv��X���X]9�.�*!</�9�-T���#
���(?u�K�mMd��H���f��fXK9�4�JK���@[��`>*�}�Q���rY Y�W
{;��+�zg�\�fA����syG7
� �X�Z��z�Qr���_�B�y���e Ls
N���'�1����l���a�``�dc6���]����������.�xq�p�A�K�1��XEtDTZ���Z��u��<�!.y`�]y�
MC���yB+���L�6�,
�� o���17���������� /�)4W�1������ /�7Y,(lI�yk&��Zl~�F��8�O���3VjmN�&�$�����)r����(Q�Z�b7xR���{���)g�Zm�c|�R!�2�@�O��<l��B�6�#Z��bS���=qre�3�.��{�l�b�%G|�p���
����,�~��9pS���x0���o����ZC.v� �E$C����J���0x�����
?pFy�8��uq��G@w���F��V|����@/�$��^_���c�0�d���o�;;kR!=�v�s�����T4��z!�5b�������������>��P�~ <���3���sQ�Z�iy?n�1��D��h�5��6G�B���x
�Qz[�7���pZiI�Xg��S����L��p��0:/x0�F���Q���0-p��?v(�����"�]��@[~ ;��	mr�aa��o
�K~@��X��@��bYZ��*K��@��f���(��Z�>1F�=�[p`x'*�Gs����MD����D�u���D����|R����}�Ua(�/��&�A[0BV��k3�����x�~��[���#��H��"����G�e�s�/����\�T�j���w�f�7�M���
[��=q%�9���#��
���f-Gf=C��oW�~�9��Mdr��WK�r��z������O�����,�w������#vb�12VG,nFJ�� ,
^����[���3��;����Q�_U���D���g�x�F��,��j�3����P�����{�3�������>m%	�l8g�a`�~����$_U�����k��QT���,�F��W��������hT�j���`*�\Z��MGr�lG��r���M��Q�e�C�eU&/P��0�u� �����q���-��n/K��v��
6)s�2KN�T��^��V
�"p8�wP�����>%������r95�?O��4�2�B#$ba������y�Z�;�Z8��21�^����P��n=�����������
�M�d����eGD �s�����`&��a��'�E;jJ`~!+�{����&�vA���������~�����j��n�v��J����[��c�����;�v
zb%w;+	?���P���N�O"�,���*�XI�aX^�H?�����J� �a���<�==�' !`<>���a��K��*$��$R	�<��6�]�o6Q2_*I$,�`��X�`��8���������������u��Ll����������)�C�rv7m)���]��ag���@����Q��jI<Ju�x7�bY����y�P�#����������5����c�??,9-4��kf�����7�$�`~��f��A��^>"�C�v~��-^����������0�6������Ry�V��Rs�� x_]X`��������w^�`s/�Wb�7!>�Z9�F�0���'�������f���h/	7��4L@����Qq_olR���-����I���hl�;SO��hK��c9x�A�tG���_@��;� ����.�b����3kT\5BK<����c����U	�*#�PE1p�R�y�/ak��|CIU\��:�
����'XH��B����wL����(�q�����_��LPY�N�i�y�q/1�����4>j�/���"|����5x��7��g��Gr.� �?G�
$��M��
i�ms�	J����1R7�k^cb2����0�c����a����h�� *=	���zI�m��CvT�^�����>yaJG���G�;����
DHE�j��n�0�T
�2��q���:�R�+��������}��!�T�O=����1ts���X7c���Q����3Y��:��B0=���+k4���38�`q���Y��}Y���Q�5��$+Q��ht����`t8�����b�	+(����>Y���Z�;R���K��E!,�p��.�-��#���=o)�rD�s�j6��U�K>4A���,��uvvq�k_����Z�������{Z�o�a���%%c&i�m��F�_,���D:B�����e�a@���[���2����$���'I�����e��lW�P� ����lUQa����fs6����r�2�
�i9�Sr>@m���3:'�C���Y�(�Q� ����s8R=�Ym���V�	��"a �#���#���Mg&�V6b�	���'���{��.�[7[�\����6ol��.o�$d���9b�Hi"�#;r�`4�>�4��}&�������^�;���Yhu���w�������b��l�����yj_�H�}B;'!M@�95jW��`QfM��*������P9�Z]�����U������M��3.	�3���\�������"�{����H�z�-
v��Az���&�-(1������!y�^H1	�����)�$.��x�2�K���y�?U`bc�j�^_:���3�����.#�a�]�������i���n���U�X������d1�������
�6yS�M"[c��z���|��5�-�$������K�{��(>��a���r�Q��k��UH6FUe��**���|P������Y4o��_�p�|)Y��&��,d�
����h��U�U�bo�Z@����]������Y[�
E�G��������i}<^�����t�~���d�S�P����iJ�0�ImX���+2�CE��]������l�W�V�y������c�}�pz��w�jYrG��b�
�=�x�+�C��Dl���VQ����sc��A���:8�I��>9X���;�B�?��f�9��zfG'��`����,��B��<���M�k�X�i�W�;�N{?���b=���gPn���O<���l�"E���,�N<�x��K��Q���^�(\����8��#�Zb�9]�����j�$��=����Y�0_-mY��d,I��&Kb�M12�q��B�O��
���"5�|d�Z��t�?�9�
�0��a���c�J���
��j=�7�wH�j�~��*k~-"��tC����+;oh��
1����G2x��Gx:x|�%sa�4��^:��������
6l�3�M��O�/��/}a��$-�b|�u.�>�/�F�����(3���"���W��Xduvv0����[?_�`��_C���w��i��`����w�{�t+�b�@�{���~���oO;��/�MY�����g��NOpMgx��;�I�{d�����,
"(;T����K>�M[C���cK!p�-�#7�~a��

Ou��:*F-��#����L�����7]��_���Zh���)����CF#i���T���y��Z��Hu��"y<�p:���p�I�>�����	#o�jb�QR	����X!.�\c�2�o�Lk&b?
?����VZ#��k�������h}nX��7[��]��i�wY�1�S�@�,6%��H�fU�@�'�J.�=��b_��dL9lv�����:�8O=������i��;���d�5i�`t	�i�1��������6Wo[��g�{[����B��N������[�o��S}�[_�����������)c��M�/���O6`�R�Wh���:|�����3$8@�������������1�w5A���Xn
�b��������^�)
��{P�
+�JiH��
�%V*2�d��F�����~�u��4���������j�TJ��DF���#�FV�:$G2r�?r��@@: �N>���CFx��md�����E�DjE�)��n�7��@hqP2k���P(�+�F��+��������7$"��N�_&o�r������[a��1��u��MA\p&�PG���@�$���I��!���`P;���hO��G�%�?�.�`J�������N4����Z�CI����tUV� 3M���f��w�'O#�0�Z7�^$3ch-��l\�^����qE������z��<8I<8P*����V�r�Y,��^S���5�p��%4��?���p<�4XQ�@��K����D�s����m�OJ/xt�6kB����E���8&%� 1�V��+�fJ���R���Y) k�(�!Q����.�$��'u��YS_n�J����	-�W0a��^\\�������������s�M*��Xl6+���t
��pT*�EoP��/����WVZJ�����M	29�k1��,x-�L1��O�eo�X�.�	"��Y��>�^��sg�Z�����6�#n	6���ES�N�����(U�E0�Z�+��n��n������I���k�=�mK�����:F�Ao:���Y���:������Pz��sr��x��c�����4��M���[A�4~\`��D���2������J"���~F�j�2�sR�vR��rkE��P���B������9��M��u]t�,��#{�e������yF%$D���5jd�R���R]o�Q��Q`v�uRY�9�U�`��Qm{�|���U�������{��aa-T��O�{�F�zK��2*�2����+������^�Q#�Z^.f����N���e�K�S_y���b����<��-������T _r��h����}Z�n�3� �Kd�-�2���1����������>��m�j�[~#�E�����6���}F���ry0T�O��k��Z5j������2���A��#��0����Y:���W&n�F#�P`��U��*��|��?wG�OL����#71=KS�omw1w1Tg���b���39��R�b��)�@��x�SD�����q�j
�a�q�G��t���q��1G�9xu0OA$N;������~�m���N/:������P��a�	>�,����R��^`�e���PC!^
Y�i����6��a�^vR]�6Tcy�U�$��V����Rc�!7G����j0&�E�C�z��c"LIa�x�5�@��
�-�������x4���$L���� H��,I�L�A���$6������n	3�SRD=W�R�8��%��Hjq	��,���QY��<9�����{Z��������>�*��U�l
�Z��B���.���P��o8��2����.��~�H���><��)�"����h�d�xZ�U�R���e��d%���6o���U�\(�L�?��?8 y��\�F��G������Y^B�#��!,l0��O��|a�z��:��4)�[?���b���Z��R�Z-���bU��R�?T�_2V�2YM�
� �h��=�E���&�w(P+���JU�^�Y���s0�Vj�����a���W��V[���R��C����b)�������	�9��~I�
4���h���;(g�2p��U��z����L2�0����a��p��b���o�<T�l]�W��~��+y���a�9��T`�l"�T��
M<�B&s���Q�J�&=����V��M�@�q83��~��K��,�Q)X9�V�e���4��Uf��\�m*�^P�+
�H���6����wy����2��&�C�jD���'�F�8�NX!p���
&o��L���{����f02�����������|I��'�*��h�7��T������JQ�K��?�)��b���C��F$��	tqo7so����%�OJ�C7����fTw����F�^*���0O,35�ybj�3�������0�=�ZBM`��][-&(�b��?�X��v����&C*�`����.�k`f�?���u�^�n����w���K���E����za3��.��z�J��K]�1��n].;(����)��^{P�������\��'k2~�F!c�����|,���Y�uk�@�c;Nix����2�{��^�V�E_V�Q%��p���V���Gp��N�6~��W��-������6�a��N����yM*���lg�,��	��j����Y���3:�V����zsP��F�B�4(�����6���V��"�����I�bY��2�,��s��%������u���T$��\�i�(H���������H���g�C� �8�jL���U�R.�xU�h�I�?��@���[$w��`9CA�N���%�S{�9�E�t� �����.������y�<}�>O��������y�<}�>O��������y�<}�>O�������[qv�`	
#10Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#9)
2 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Mar 12, 2019 at 6:51 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Sun, Feb 3, 2019 at 11:09 PM Andres Freund <andres@anarazel.de> wrote:

On 2018-12-03 18:43:04 +1300, Thomas Munro wrote:

Sorry for my silence... I got stuck on a design problem with the lower
level undo log management code that I'm now close to having figured
out. I'll have a new patch soon.

Hello all,

Here's a new WIP version of this patch set. It builds on a fairly
deep stack of patches being developed by several people. As mentioned
before, it's a useful crash-test dummy for a whole stack of technology
we're working on, but it's also aiming to solve a real problem.

It currently fails in one regression test for a well understood
reason, fix on the way (see end), and there are some other stability
problems being worked on.

Here's a quick tour of the observable behaviour, having installed the
pg_buffercache and test_undorecord extensions:

==================

postgres=# begin;
BEGIN
postgres=# create table foo ();
CREATE TABLE

Check if our transaction has generated undo data:

postgres=# select logno, discard, insert, xid, pid from pg_stat_undo_logs ;
logno | discard | insert | xid | pid
-------+------------------+------------------+-----+-------
0 | 0000000000002CD9 | 0000000000002D1A | 476 | 39169
(1 row)

Here, we see that undo log number 0 has some undo data because discard
< insert. We can find out what it says:

postgres=# call dump_undo_records(0);
NOTICE: 0000000000002CD9: Storage: CREATE dbid=12916, tsid=1663,
relfile=16386; xid=476, next xact=0
CALL

The undo record shown there lives in shared buffers, and we can see
that it's in there with pg_buffercache (the new column smgrid 1 means
undo data; 0 is regular relation data):

postgres=# select bufferid, smgrid, relfilenode, relblocknumber,
isdirty, usagecount from pg_buffercache where smgrid = 1;
bufferid | smgrid | relfilenode | relblocknumber | isdirty | usagecount
----------+--------+-------------+----------------+---------+------------
3 | 1 | 0 | 1 | t | 5
(1 row)

Even though that's just a dirty page in shared buffers, if we crash
now and recover, it'll be recreated by a new WAL record that was
flushed *before* creating the relation file. We can see that with
pg_waldump:

rmgr: Storage ... PRECREATE base/12916/16384, blkref #0: smgr 1 rel
1663/0/0 blk 1 FPW
rmgr: Storage ... CREATE base/12916/16384

The PRECREATE record dirtied block 1 of undo log 0. In this case it
happened to include a FPW of the undo log page too, following the
usual rules. FPWs are rare for undo pages because of the
REGBUF_WILL_INIT optimisation that applies to the zeroed out pages
(which is most undo pages, due to the append-mostly access pattern).

Finally, we if commit we see the undo data is discarded by a
background worker, and if we roll back explicitly or crash and run
recovery, the file is unlinked. Here's an example of the crash case:

postgres=# begin;
BEGIN
postgres=# create table foo ();
CREATE TABLE
postgres=# select relfilenode from pg_class where relname = 'foo';
relfilenode
-------------
16395
(1 row)

postgres=# select pg_backend_pid();
pg_backend_pid
----------------
39169
(1 row)

$ kill -9 39169

... server restarts, recovers ...

$ ls pgdata/base/12916/16395
pgdata/base/12916/16395

It's still there, though it's been truncated by an undo worker (see
end of email). And finally, after the next checkpoint:

$ ls pgdata/base/12916/16395
ls: pgdata/base/12916/16395: No such file or directory

That's the end of the quick tour.

Most of these patches should probably be discussed in other threads,
but I'm posting a snapshot of the full stack here anyway. Here's a
patch-by-patch summary:

=== 0001 "Refactor the fsync mechanism to support future SMGR
implementations." ===

The 0001 patch has its own CF thread
https://commitfest.postgresql.org/22/1829/ and is from Shawn Debnath
(based on earlier work by me), but I'm including a copy here for
convenience/cfbot.

=== 0002 "Add SmgrId to smgropen() and BufferTag." ===

This is new, and is based on the discussion from another recent
thread[1] about how we should identify buffers belonging to different
storage managers. In earlier versions of the patch-set I had used a
special reserved DB OID for undo data. Tom Lane didn't like that idea
much, and Anton Shyrabokau (via Shawn Debnath) suggested making
ForkNumber narrower so we can add a new field to BufferTag, and Andres
Freund +1'd my proposal to add the extra value as a parameter to
smgropen(). So, here is a patch that tries those ideas.

Another way to do this would be to widen RelFileNode instead, to avoid
having to pass around the SMGR ID separately in various places.
Looking at the number of places that have to chance, you can probably
see why we wanted to use a magic DB OID instead, and I'm not entirely
convinced that it wasn't better that way, or that I've found all the
places that need to carry an smgrid alongside a RelFileNode.

Archeological note: smgropen() was like that ~15 years ago before
commit 87bd9563, but buffer tags didn't include the SMGR ID.

I decided to call md.c's ID "SMGR_RELATION", describing what it really
holds -- regular relations -- rather than perpetuating the doubly
anachronistic "magnetic disk" name.

While here, I resurrected the ancient notion of a per-SMGR 'open'
routine, so that a small amount of md.c-specific stuff could be kicked
out of smgr.c and future implementations can do their own thing here
too.

While doing that work I realised that at least pg_rewind needs to
learn about how different storage managers map blocks to files, so
that's a new TODO item requiring more thought. I wonder what other
places know how to map { RelFileNode, ForkNumber, BlockNumber } to a
path + offset, and I wonder what to think about the fact that some of
them may be non-backend code...

=== 0003 "Add undo log manager." ===

This and the next couple of patches live in CF thread
https://commitfest.postgresql.org/22/1649/ but here's a much newer
snapshot that hasn't been posted there yet.

Manages a set of undo logs in shared memory, manages undo segment
files, tracks discard, insert, end pointers visible in
pg_stat_undo_logs. With this patch you can allocate and discard space
in undo logs using the UndoRecPtr type to refer to addresses, but
there is no access to the data yet. Improvements since the last
version are not requiring DSM segments, proper FPW support and reduced
WAL traffic. Previously there were extra per-xact and per-checkpoint
records requiring retry-loops in code that inserted undo data.

=== 0004 "Provide access to undo log data via the buffer manager." ===

Provide SMGR_UNDO. While the 0003 patch deals with allocating and
discarding undo address space and makes sure that backing files exist,
this patch lets you read and write buffered data in them.

=== 0005 "Allow WAL record data on first modification after a checkpoint." ===

Provide a way for data to be attached to a WAL-registered block that
is only included if this turns out to be the first WAL record that
touches the block after a checkpoint. This is a bit like FPW images,
except that it's arbitrary extra data and happens even if FPW is off.
This is used to capture a copy of the (tiny) undo log meta-data
(primary the insertion pointer) to fix a consistency problem when
recovering from an online checkpoint.

=== 0006 + 0007 "Provide interfaces to store and fetch undo records." ===

This is a snapshot of work by my colleagues Dilip, Rafia and others
based on earlier prototyping by Robert. While the earlier patches
give you buffered binary undo data, this patch introduces the concept
of high level undo records that can be inserted, and read back given
an UndoRecPtr. This is a version presented on another thread already;
here it's lightly changed due to rebasing by me.

Undo-aware modules should design a set of undo record types, and
insert exactly the same ones at do and undo time.

The 0007 patch is fixups from me to bring that code into line with
changes to the lower level patches. Future versions will be squashed
and tidied up; still working on that.

Currently, undo branch[1]https://github.com/EnterpriseDB/zheap/tree/undo contain an older version of the (undo
interface + some fixup). Now, I have merged the latest changes from
the zheap branch[2]https://github.com/EnterpriseDB/zheap to the undo branch[1]https://github.com/EnterpriseDB/zheap/tree/undo
which can be applied on top of the undo storage commit[3]https://github.com/EnterpriseDB/zheap/tree/undo (b397d96176879ed5b09cf7322b8d6f2edd8043a5). For
merging those changes, I need to add some changes to the undo log
storage patch as well for handling the multi log transaction. So I
have attached two patches, 1) improvement to undo log storage 2)
complete undo interface patch which include 0006+0007 from undo
branch[1]https://github.com/EnterpriseDB/zheap/tree/undo + new changes on the zheap branch.

[1]: https://github.com/EnterpriseDB/zheap/tree/undo
[2]: https://github.com/EnterpriseDB/zheap
[3]: https://github.com/EnterpriseDB/zheap/tree/undo (b397d96176879ed5b09cf7322b8d6f2edd8043a5)
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Enhance-multi-log-handling-in-undo-log.patchapplication/octet-stream; name=0001-Enhance-multi-log-handling-in-undo-log.patchDownload
From 567ffe31ea21f7c58c3fe415941f955b96f548b9 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Tue, 16 Apr 2019 16:46:01 +0530
Subject: [PATCH 1/2] Enhance multi-log handling in undo log

As part of this patch UndoLogAllocate will return the current transaction
start header in previous log if there is a log switch during transaction.
This will allow discard worker to fetch the last undo record pointer for
an aborted transaction.  WAL log the undo log switch so that during
recovery we can detect the log switch.

Dilip Kumar
---
 src/backend/access/undo/undolog.c | 76 ++++++++++++++++++++++++++++++++++++++-
 src/include/access/undolog.h      | 19 +++++++++-
 src/include/access/undolog_xlog.h |  9 +++++
 3 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 38f8a4f..028b282 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -650,7 +650,9 @@ UndoLogAllocate(uint16 size,
 				UndoRecPtr try_location,
 				UndoPersistence persistence,
 				bool *need_xact_header,
-				UndoRecPtr *last_xact_start)
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
 {
 	UndoLogControl *log = MyUndoLogState.logs[persistence];
 	UndoLogOffset new_insert;
@@ -755,6 +757,11 @@ UndoLogAllocate(uint16 size,
 				 * comments in undorecord.c file header.
 				 */
 				prevlogno = log->logno;
+				*prevlog_xact_start =
+					MakeUndoRecPtr(log->logno,
+								   log->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(log->logno, log->meta.unlogged.insert);
 			}
 			elog(LOG, "undo log %u is full, switching to a new one", log->logno);
 			log = NULL;
@@ -831,6 +838,8 @@ UndoLogAllocateInRecovery(TransactionId xid,
 						  UndoRecPtr try_location,
 						  bool *need_xact_header,
 						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp,
 						  XLogReaderState *xlog_record)
 {
 	UndoLogControl *log;
@@ -959,6 +968,13 @@ UndoLogAllocateInRecovery(TransactionId xid,
 			*last_xact_start = log->meta.unlogged.last_xact_start;
 			allocate_in_recovery_logno = log->logno;
 
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = log->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = log->meta.unlogged.prevlog_last_urp;
+
+			log->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			log->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
 			return MakeUndoRecPtr(log->logno, log->meta.unlogged.insert);
 		}
 		++allocate_in_recovery_block_id;
@@ -1221,6 +1237,43 @@ UndoLogGetFirstValidRecord(UndoLogControl *log, bool *full)
 }
 
 /*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogControl *log;
+
+	log = get_undo_log(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLog(log));
+
+	LWLockAcquire(&log->mutex, LW_EXCLUSIVE);
+	log->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	log->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&log->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
  * Return the Next insert location.  This will also validate the input xid
  * if latest insert point is not for the same transaction id then this will
  * return Invalid Undo pointer.
@@ -2552,6 +2605,25 @@ undolog_xlog_rewind(XLogReaderState *record)
 	log->meta.unlogged.prevlen = xlrec->prevlen;
 }
 
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogControl *log;
+
+	log = get_undo_log(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	log->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	log->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
 void
 undolog_redo(XLogReaderState *record)
 {
@@ -2571,6 +2643,8 @@ undolog_redo(XLogReaderState *record)
 		case XLOG_UNDOLOG_REWIND:
 			undolog_xlog_rewind(record);
 			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
 		default:
 			elog(PANIC, "undo_redo: unknown op code %u", info);
 	}
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index c1b3a75..2cfce8c 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -205,6 +205,16 @@ typedef struct UndoLogUnloggedMetaData
 	uint16		prevlen;		   	/* size of the last record in the log */
 	UndoLogNumber prevlogno;		/* Previous undo log number */
 	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of undo do switch wal will
+	 * restore these two undo record pointers which will be reset on next
+	 * allocation during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -343,12 +353,16 @@ extern UndoRecPtr UndoLogAllocate(uint16 size,
 								  UndoRecPtr try_location,
 								  UndoPersistence level,
 								  bool *need_xact_header,
-								  UndoRecPtr *last_xact_start);
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
 extern UndoRecPtr UndoLogAllocateInRecovery(TransactionId xid,
 											uint16 size,
 											UndoRecPtr try_location,
 											bool *need_xact_header,
 											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp,
 											XLogReaderState *xlog_record);
 extern void UndoLogAdvance(UndoRecPtr insertion_point, size_t size);
 extern void UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
@@ -378,6 +392,9 @@ extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno,
 										  TransactionId xid);
 extern UndoRecPtr UndoLogGetLastRecordPtr(UndoLogNumber,
 										  TransactionId xid);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
 extern void UndoLogRewind(UndoRecPtr insert_urp, uint16 prevlen);
 extern uint16 UndoLogGetPrevLen(UndoLogNumber logno);
 extern void UndoLogSetLSN(XLogRecPtr lsn);
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
index feebf81..802a727 100644
--- a/src/include/access/undolog_xlog.h
+++ b/src/include/access/undolog_xlog.h
@@ -22,6 +22,7 @@
 #define XLOG_UNDOLOG_EXTEND		0x10
 #define XLOG_UNDOLOG_DISCARD	0x20
 #define XLOG_UNDOLOG_REWIND		0x30
+#define XLOG_UNDOLOG_SWITCH		0x40
 
 /* Create a new undo log. */
 typedef struct xl_undolog_create
@@ -56,6 +57,14 @@ typedef struct xl_undolog_rewind
 	uint16		  prevlen;
 } xl_undolog_rewind;
 
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
 extern void undolog_desc(StringInfo buf,XLogReaderState *record);
 extern const char *undolog_identify(uint8 info);
 
-- 
1.8.3.1

0002-Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=0002-Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From 1d26203d07f50984ddae4a8928c9006c1da01f8b Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 18 Apr 2019 16:09:25 +0530
Subject: [PATCH 2/2] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/transam/xact.c    |   37 +
 src/backend/access/undo/Makefile     |    2 +-
 src/backend/access/undo/undoinsert.c | 1257 ++++++++++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c |  494 +++++++++++++
 src/include/access/transam.h         |    1 +
 src/include/access/undoinsert.h      |   54 ++
 src/include/access/undorecord.h      |  201 ++++++
 src/include/access/xact.h            |    2 +
 8 files changed, 2047 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/undoinsert.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoinsert.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index bd5024e..6747095 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -30,6 +30,7 @@
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
+#include "access/undoinsert.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_enum.h"
 #include "catalog/storage.h"
@@ -68,6 +69,7 @@
 #include "utils/timestamp.h"
 #include "pg_trace.h"
 
+#define	AtAbort_ResetUndoBuffers() ResetUndoBuffers()
 
 /*
  *	User-tweakable parameters
@@ -192,6 +194,10 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	 /* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -998,6 +1004,35 @@ IsInParallelMode(void)
 }
 
 /*
+ * SetCurrentUndoLocation
+ *
+ * Update the start and the latest undo record pointer for the transaction.
+ *
+ * start_urec_ptr is set only for the first undo for the transaction i.e.
+ * start_urec_ptr is invalid.  Update the latest_urec_ptr whenever a new
+ * undo is inserted for the transaction.
+ *
+ * start and latest undo record pointer are tracked separately for each
+ * persistent level.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr)
+{
+	UndoLogControl *log = UndoLogGet(UndoRecPtrGetLogNo(urec_ptr), false);
+	UndoPersistence upersistence = log->meta.persistence;
+
+	Assert(AmAttachedToUndoLog(log) || InRecovery);
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
+
+/*
  *	CommandCounterIncrement
  */
 void
@@ -2736,6 +2771,7 @@ AbortTransaction(void)
 		AtEOXact_HashTables(false);
 		AtEOXact_PgStat(false);
 		AtEOXact_ApplyLauncher(false);
+		AtAbort_ResetUndoBuffers();
 		pgstat_report_xact_timestamp(0);
 	}
 
@@ -4993,6 +5029,7 @@ AbortSubTransaction(void)
 		AtEOSubXact_PgStat(false, s->nestingLevel);
 		AtSubAbort_Snapshot(s->nestingLevel);
 		AtEOSubXact_ApplyLauncher(false, s->nestingLevel);
+		AtAbort_ResetUndoBuffers();
 	}
 
 	/*
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
new file mode 100644
index 0000000..27cb5a7
--- /dev/null
+++ b/src/backend/access/undo/undoinsert.c
@@ -0,0 +1,1257 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.c
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoinsert.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans across
+ * multiple undo logs.  We need some special handling while inserting them to
+ * ensure that discard and rollbacks can work sanely.
+ *
+ * When the undorecord for a transaction gets inserted in the next log then we
+ * insert a transaction header for the first record in the new log and update
+ * the transaction header with this new logs location.  We will also keep
+ * a back pointer to the last undo record of previous log in the first record
+ * of new log, so that we can traverse the previous record during rollback.
+ * Incase, this is not the first record in new log (aka new log already
+ * contains some other transactions data), we also update that transactions
+ * next start header with this new undo records location.  This will allow us
+ * to connect transaction's undo records across logs when the same transaction
+ * span across log.
+ *
+ * There is some difference in the way the rollbacks work when the undo for
+ * same transaction spans across multiple logs depending on which log is
+ * processed first by the discard worker.  If it processes the first log which
+ * contains the transactions first record, then it can get the last record
+ * of that transaction even if it is in different log and then processes all
+ * the undo records from last to first.  OTOH, if the next log get processed
+ * first, we don't need to trace back the actual start pointer of the
+ * transaction, rather we only execute the undo actions from the current log
+ * and avoid re-executing them next time.  There is a possibility that after
+ * executing the undo actions, the undo got discarded, now in later stage while
+ * processing the previous log, it might try to fetch the undo record in the
+ * discarded log while chasing the transaction header chain which can cause
+ * trouble.  We avoid this situation by first checking if the next_urec of
+ * the transaction is already discarded and if so, we start executing from
+ * the last undo record in the current log.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoinsert.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * This defines the number of undo records that can be prepared before
+ * calling insert by default.  If you need to prepare more than
+ * MAX_PREPARED_UNDO undo records, then you must call UndoSetPrepareSize
+ * first.
+ */
+#define MAX_PREPARED_UNDO 2
+
+/*
+ * This defines the max number of previous xact infos we need to update.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous
+ * transaction in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+/*
+ * Consider buffers needed for updating previous transaction's
+ * starting undo record as well.
+ */
+#define MAX_UNDO_BUFFERS       (MAX_PREPARED_UNDO + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO
+
+/* Undo block number to buffer mapping. */
+typedef struct UndoBuffers
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+} UndoBuffers;
+
+static UndoBuffers def_buffers[MAX_UNDO_BUFFERS];
+static int	buffer_idx;
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+typedef struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+} PreparedUndoSpace;
+
+static PreparedUndoSpace def_prepared[MAX_PREPARED_UNDO];
+static int	prepare_idx;
+static int	max_prepared_undo = MAX_PREPARED_UNDO;
+static UndoRecPtr prepared_urec_ptr = InvalidUndoRecPtr;
+
+/*
+ * By default prepared_undo and undo_buffer points to the static memory.
+ * In case caller wants to support more than default max_prepared undo records
+ * then the limit can be increased by calling UndoSetPrepareSize function.
+ * Therein, dynamic memory will be allocated and prepared_undo and undo_buffer
+ * will start pointing to newly allocated memory, which will be released by
+ * UnlockReleaseUndoBuffers and these variables will again set back to their
+ * default values.
+ */
+static PreparedUndoSpace *prepared_undo = def_prepared;
+static UndoBuffers *undo_buffer = def_buffers;
+
+/*
+ * Structure to hold the previous transaction's undo update information.  This
+ * is populated while current transaction is updating its undo record pointer
+ * in previous transactions first undo record.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+static XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];
+static int	xact_urec_info_idx;
+
+/* Prototypes for static functions. */
+UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence);
+static void UndoRecordPrepareTransInfo(UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp,
+						   XLogReaderState *xlog_record);
+static void UndoRecordUpdateTransInfo(int idx);
+static int UndoGetBufferSlot(RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm,
+				  UndoPersistence persistence, XLogReaderState *xlog_record);
+static bool UndoRecordIsValid(UndoLogControl * log,
+				  UndoRecPtr urp);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp,
+								   UndoPersistence upersistence);
+/*
+ * Check whether the undo record is discarded or not.  If it's already discarded
+ * return false otherwise return true.
+ *
+ * Caller must hold lock on log->discard_lock.  This function will release the
+ * lock if return false otherwise lock will be held on return and the caller
+ * need to release it.
+ */
+static bool
+UndoRecordIsValid(UndoLogControl * log, UndoRecPtr urp)
+{
+	Assert(LWLockHeldByMeInMode(&log->discard_lock, LW_SHARED));
+
+	if (log->oldest_data == InvalidUndoRecPtr)
+	{
+		/*
+		 * oldest_data is only initialized when the DiscardWorker first time
+		 * attempts to discard undo logs so we can not rely on this value to
+		 * identify whether the undo record pointer is already discarded or
+		 * not so we can check it by calling undo log routine.  If its not yet
+		 * discarded then we have to reacquire the log->discard_lock so that
+		 * the doesn't get discarded concurrently.
+		 */
+		LWLockRelease(&log->discard_lock);
+		if (UndoLogIsDiscarded(urp))
+			return false;
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+	}
+
+	/* Check again if it's already discarded. */
+	if (urp < log->oldest_data)
+	{
+		LWLockRelease(&log->discard_lock);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * Prepare to update the previous transaction's next undo pointer to maintain
+ * the transaction chain in the undo.  This will read the header of the first
+ * undo record of the previous transaction and lock the necessary buffers.
+ * The actual update will be done by UndoRecordUpdateTransInfo under the
+ * critical section.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
+						   XLogReaderState *xlog_record)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	Page		page;
+	int			already_decoded = 0;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before accessing the undo record so that
+	 * discard worker doesn't remove the record while we are in process of
+	 * reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 * UndoRecordIsValid will release the lock if it returns false.
+	 */
+	if (!UndoRecordIsValid(log, xact_urp))
+		return;
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/*
+	 * Read undo record header in by calling UnpackUndoRecord, if the undo
+	 * record header is split across buffers then we need to read the complete
+	 * header by invoking UnpackUndoRecord multiple times.
+	 */
+	while (true)
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk,
+								   RBM_NORMAL,
+								   log->meta.persistence, xlog_record);
+		xact_urec_info[xact_urec_info_idx].idx_undo_buffers[index++] = bufidx;
+		buffer = undo_buffer[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		if (UnpackUndoRecord(&xact_urec_info[xact_urec_info_idx].uur, page,
+							 starting_byte, &already_decoded, true))
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	xact_urec_info[xact_urec_info_idx].uur.uur_next = urecptr;
+	xact_urec_info[xact_urec_info_idx].urecptr = xact_urp;
+	xact_urec_info_idx++;
+	LWLockRelease(&log->discard_lock);
+}
+
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(int idx)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(xact_urec_info[idx].urecptr);
+	Page		page = NULL;
+	int			starting_byte;
+	int			already_written = 0;
+	int			i = 0;
+	uint16		remaining_bytes;
+	UndoRecPtr	urec_ptr = InvalidUndoRecPtr;
+	UndoLogControl *log;
+
+	log = UndoLogGet(logno, false);
+	urec_ptr = xact_urec_info[idx].urecptr;
+
+	/*
+	 * Acquire the discard lock before accessing the undo record so that
+	 * discard worker can't remove the record while we are in process of
+	 * reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	if (!UndoRecordIsValid(log, urec_ptr))
+		return;
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(urec_ptr);
+	remaining_bytes = sizeof(UndoRecPtr);
+	do
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_urec_info[idx].idx_undo_buffers[i];
+		buffer = undo_buffer[buf_idx].buf;
+
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+			/* Overwrite the previously written undo. */
+			if (InsertUndoRecord(&xact_urec_info[idx].uur, page, starting_byte,
+				&already_written, 0, 0, true))
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+		else
+		{
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * removed by discard process, so we can just skip inserting into
+			 * those blocks.
+			 */
+			Assert(InRecovery);
+
+			/*
+			 * Block is not valid so we can not write to the current block
+			 * but we might need to insert remaining partial record to the
+			 * next block so set proper value for already_written variable
+			 * to jump to the undo record offset from which we want to
+			 * insert into next block.
+			 */
+			if (InsertUndoRecord(&xact_urec_info[idx].uur, page, starting_byte,
+				&already_written, remaining_bytes, 0, true))
+				break;
+			else
+				remaining_bytes -= (BLCKSZ - starting_byte);
+		}
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	} while (true);
+
+	LWLockRelease(&log->discard_lock);
+}
+
+/*
+ * Find the block number in undo buffer array, if it's present then just return
+ * its index otherwise search the buffer and insert an entry and lock the buffer
+ * in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm,
+				  UndoPersistence persistence,
+				  XLogReaderState *xlog_record)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction 	action = BLK_NEEDS_REDO;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < buffer_idx; i++)
+	{
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == undo_buffer[i].blk) &&
+			(undo_buffer[i].logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(undo_buffer[i].buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(undo_buffer[i].buf - 1)),
+										LW_EXCLUSIVE));
+			break;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array
+	 */
+	if (i == buffer_idx)
+	{
+		/*
+		 * Fetch the buffer in which we want to insert the undo record.
+		 */
+		if (InRecovery)
+			action = XLogReadBufferForRedoBlock(xlog_record,
+									   SMGR_UNDO,
+									   rnode,
+									   UndoLogForkNum,
+									   blk,
+									   rbm,
+									   false,
+									   &buffer);
+		else
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode,
+											   UndoLogForkNum,
+											   blk,
+											   rbm,
+											   NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/* Lock the buffer */
+			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+		}
+
+		if (action == BLK_NOTFOUND)
+		{
+			undo_buffer[buffer_idx].buf = InvalidBuffer;
+			undo_buffer[buffer_idx].blk = InvalidBlockNumber;
+		}
+		else
+		{
+			undo_buffer[buffer_idx].buf = buffer;
+			undo_buffer[buffer_idx].blk = blk;
+			undo_buffer[buffer_idx].logno = rnode.relNode;
+			undo_buffer[buffer_idx].zero = rbm == RBM_ZERO;
+		}
+		buffer_idx++;
+	}
+
+	return i;
+}
+
+/*
+ * Call UndoSetPrepareSize to set the value of how many undo records can be
+ * prepared before we can insert them.  If the size is greater than
+ * MAX_PREPARED_UNDO then it will allocate extra memory to hold the extra
+ * prepared undo.
+ *
+ * This is normally used when more than one undo record needs to be prepared.
+ */
+void
+UndoSetPrepareSize(int nrecords)
+{
+	if (nrecords <= MAX_PREPARED_UNDO)
+		return;
+
+	prepared_undo = palloc0(nrecords * sizeof(PreparedUndoSpace));
+
+	/*
+	 * Consider buffers needed for updating previous transaction's starting
+	 * undo record. Hence increased by 1.
+	 */
+	undo_buffer = palloc0((nrecords + 1) * MAX_BUFFER_PER_UNDO *
+						  sizeof(UndoBuffers));
+	max_prepared_undo = nrecords;
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'xid' refers to the transaction id stored in WAL, otherwise,
+ * it refers to the top transaction id because undo log only stores mapping
+ * for the top most transactions.
+ */
+UndoRecPtr
+PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,
+				  UndoPersistence upersistence,
+				  XLogReaderState *xlog_record)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	try_location;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+
+	/* Already reached maximum prepared limit. */
+	if (prepare_idx == max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	urec->uur_xidepoch = EpochFromFullTransactionId(fxid);
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/*
+	 * Since we don't actually advance the insert pointer until later in
+	 * InsertPreparedUndo(), but we may need to allocate space for several
+	 * undo records, we need to keep track of the insert pointer as we go.
+	 */
+	if (prepare_idx == 0)
+	{
+		/* Nothing allocated already; just ask for some space anywhere. */
+		try_location = InvalidUndoRecPtr;
+	}
+	else
+	{
+		/*
+		 * Ask to extend the space immediately after the last record, if
+		 * possible.  A new undo log will be chosen otherwise.
+		 */
+		PreparedUndoSpace *space = &prepared_undo[prepare_idx - 1];
+
+		try_location = UndoLogOffsetPlusUsableBytes(space->urp, space->size);
+	}
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(upersistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(XidFromFullTransactionId(txid),
+											size, try_location,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp,
+											xlog_record);
+	}
+	else
+	{
+		urecptr = UndoLogAllocate(size, try_location, upersistence,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16	prevlen;
+
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, upersistence);
+			/* Compute the last record's undo record pointer. */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might use
+			 * callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(urecptr, last_xact_start, xlog_record);
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(urecptr, prevlog_xact_start, xlog_record);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk, rbm, upersistence,
+								   xlog_record);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo[prepare_idx].undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * Save the undo record information to be later used by InsertPreparedUndo
+	 * to insert the prepared record.
+	 */
+	prepared_undo[prepare_idx].urec = urec;
+	prepared_undo[prepare_idx].urp = urecptr;
+	prepared_undo[prepare_idx].size = size;
+	prepare_idx++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo record.  This will write the actual undo
+ * record into the buffers already pinned and locked in PreparedUndoInsert,
+ * and mark them dirty.  This step should be performed after entering a
+ * criticalsection; it should never fail.
+ */
+void
+InsertPreparedUndo(void)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			already_written;
+	int			bufidx = 0;
+	int			idx;
+	uint16		undo_len = 0;
+	uint16		remaining_bytes;
+	UndoRecPtr	urp;
+	UnpackedUndoRecord *uur;
+	uint16		size;
+
+	/* There must be atleast one prepared undo record. */
+	Assert(prepare_idx > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < prepare_idx; idx++)
+	{
+		uur = prepared_undo[idx].urec;
+		urp = prepared_undo[idx].urp;
+		size = prepared_undo[idx].size;
+
+		Assert(size == UndoRecordExpectedSize(uur));
+
+		already_written = 0;
+		bufidx = 0;
+		starting_byte = UndoRecPtrGetPageOffset(urp);
+		undo_len = remaining_bytes = UndoRecordExpectedSize(uur);
+
+		do
+		{
+			PreparedUndoSpace undospace = prepared_undo[idx];
+			Buffer		buffer;
+
+			buffer = undo_buffer[undospace.undo_buffer_idx[bufidx]].buf;
+			if (BufferIsValid(buffer))
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first record
+				 * in page.  We start writting immediately after the block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it doesn't
+				 * succeed then recall the routine with the next page.
+				 */
+				if (InsertUndoRecord(uur, page, starting_byte, &already_written, 0,
+									 undo_len, false))
+				{
+					undo_len += already_written;
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+			else
+			{
+				/*
+				 * During recovery, there might be some blocks which are already
+				 * deleted due to some discard command so we can just skip
+				 * inserting into those blocks.
+				 */
+				Assert(InRecovery);
+
+				/*
+				 * Block is not valid so we can not write to the current block
+				 * but we might need to insert remaining partial record to the
+				 * next block so set proper value for already_written variable
+				 * to jump to the undo record offset from which we want to
+				 * insert into next block.  InsertUndoRecord will not write
+				 * anything if the input page is NULL, it will just update the
+				 * already_written count and local work header.
+				 */
+				if (InsertUndoRecord(uur, page, starting_byte, &already_written,
+					remaining_bytes, undo_len, false))
+					break;
+				else
+					remaining_bytes -= (BLCKSZ - starting_byte);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvance(urp, size);
+
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(urp);
+	}
+
+	/* Update previously prepared transaction headers. */
+	if (xact_urec_info_idx > 0)
+	{
+		int			i = 0;
+
+		for (i = 0; i < xact_urec_info_idx; i++)
+			UndoRecordUpdateTransInfo(i);
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord.  It will fetch the undo record pointed
+ * by urp and unpack the record into urec.  This function will not release the
+ * pin on the buffer if complete record is fetched from one buffer, so caller
+ * can reuse the same urec to fetch the another undo record which is on the
+ * same block.  Caller will be responsible to release the buffer inside urec
+ * and set it to invalid if it wishes to fetch the record from another block.
+ */
+UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence)
+{
+	Buffer		buffer = urec->uur_buffer;
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	int			already_decoded = 0;
+	BlockNumber cur_blk;
+	bool		is_undo_rec_split = false;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* If we already have a buffer pin then no need to allocate a new one. */
+	if (!BufferIsValid(buffer))
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode, UndoLogForkNum, cur_blk,
+										   RBM_NORMAL, NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		urec->uur_buffer = buffer;
+	}
+
+	while (true)
+	{
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		page = BufferGetPage(buffer);
+
+		/*
+		 * XXX This can be optimized to just fetch header first and only if
+		 * matches with block number and offset then fetch the complete
+		 * record.
+		 */
+		if (UnpackUndoRecord(urec, page, starting_byte, &already_decoded, false))
+			break;
+
+		starting_byte = UndoLogBlockHeaderSize;
+		is_undo_rec_split = true;
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		urec->uur_buffer = InvalidBuffer;
+		UnlockReleaseBuffer(buffer);
+
+		/* Go to next block. */
+		cur_blk++;
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode, UndoLogForkNum, cur_blk,
+										   RBM_NORMAL, NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+	}
+
+	/*
+	 * If we have copied the data then release the buffer, otherwise, just
+	 * unlock it.
+	 */
+	if (is_undo_rec_split)
+		UnlockReleaseBuffer(buffer);
+	else
+		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+	return urec;
+}
+
+/*
+ * ResetUndoRecord - Helper function for UndoFetchRecord to reset the current
+ * record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(urec->uur_buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(urec->uur_buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(urec->uur_buffer);
+			urec->uur_buffer = InvalidBuffer;
+		}
+	}
+	else
+	{
+		/*
+		 * If there is not a valid buffer in urec->uur_buffer that means we
+		 * had copied the payload data and tuple data so free them.
+		 */
+		if (urec->uur_payload.data)
+			pfree(urec->uur_payload.data);
+		if (urec->uur_tuple.data)
+			pfree(urec->uur_tuple.data);
+	}
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(urec->uur_buffer))
+				ReleaseBuffer(urec->uur_buffer);
+			return NULL;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 */
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (!UndoRecordIsValid(log, urp))
+		{
+			if (BufferIsValid(urec->uur_buffer))
+				ReleaseBuffer(urec->uur_buffer);
+			return NULL;
+		}
+
+		/* Fetch the current undo record. */
+		urec = UndoGetOneRecord(urec, urp, rnode, log->meta.persistence);
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undorecord satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undorecord before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode);
+	}
+
+	if (urec_ptr_out)
+		*urec_ptr_out = urp;
+	return urec;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	/*
+	 * If the undo record has a valid buffer then just release the buffer
+	 * otherwise free the tuple and payload data.
+	 */
+	if (BufferIsValid(urec->uur_buffer))
+	{
+		ReleaseBuffer(urec->uur_buffer);
+	}
+	else
+	{
+		if (urec->uur_payload.data)
+			pfree(urec->uur_payload.data);
+		if (urec->uur_tuple.data)
+			pfree(urec->uur_tuple.data);
+	}
+
+	pfree(urec);
+}
+
+/*
+ * RegisterUndoLogBuffers - Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < buffer_idx; idx++)
+	{
+		flags = undo_buffer[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx, undo_buffer[idx].buf, flags);
+		UndoLogRegister(first_block_id + idx, undo_buffer[idx].logno);
+	}
+}
+
+/*
+ * UndoLogBuffersSetLSN - Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < buffer_idx; idx++)
+		PageSetLSN(BufferGetPage(undo_buffer[idx].buf), recptr);
+}
+
+/*
+ * Reset the global variables related to undo buffers. This is required at the
+ * transaction abort and while releasing the undo buffers.
+ */
+void
+ResetUndoBuffers(void)
+{
+	int			i;
+
+	for (i = 0; i < buffer_idx; i++)
+	{
+		undo_buffer[i].blk = InvalidBlockNumber;
+		undo_buffer[i].buf = InvalidBuffer;
+	}
+
+	for (i = 0; i < xact_urec_info_idx; i++)
+		xact_urec_info[i].urecptr = InvalidUndoRecPtr;
+
+	/* Reset the prepared index. */
+	prepare_idx = 0;
+	buffer_idx = 0;
+	xact_urec_info_idx = 0;
+	prepared_urec_ptr = InvalidUndoRecPtr;
+
+	/*
+	 * max_prepared_undo limit is changed so free the allocated memory and
+	 * reset all the variable back to their default value.
+	 */
+	if (max_prepared_undo > MAX_PREPARED_UNDO)
+	{
+		pfree(undo_buffer);
+		pfree(prepared_undo);
+		undo_buffer = def_buffers;
+		prepared_undo = def_prepared;
+		max_prepared_undo = MAX_PREPARED_UNDO;
+	}
+}
+
+/*
+ * Unlock and release the undo buffers.  This step must be performed after
+ * exiting any critical section where we have perfomed undo actions.
+ */
+void
+UnlockReleaseUndoBuffers(void)
+{
+	int			i;
+
+	for (i = 0; i < buffer_idx; i++)
+	{
+		if (BufferIsValid(undo_buffer[i].buf))
+			UnlockReleaseBuffer(undo_buffer[i].buf);
+	}
+	ResetUndoBuffers();
+}
+
+/*
+ * UndoGetPrevRecordLen - read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the previous undo record is split then this will add the
+ * undo block header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber	  cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer	buffer;
+	char   *page;
+	char	prevlen[2];
+	RelFileNode rnode;
+	int		byte_to_read = sizeof(uint16);
+	char	persistence;
+	uint16	prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+									   cur_blk, RBM_NORMAL, NULL, persistence);
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = (char *)BufferGetPage(buffer);
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while(byte_to_read > 0)
+	{
+		page_offset -= 1;
+
+		/*
+		 * Read first prevlen byte from current page if page_offset hasn't
+		 * reach to undo block header.  Otherwise move to the previous page.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = page[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/* Release the previous buffer. */
+			UnlockReleaseBuffer(buffer);
+			cur_blk -= 1;
+			persistence = RelPersistenceForUndoPersistence(upersistence);
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+			page_offset = BLCKSZ;
+			page = (char *)BufferGetPage(buffer);
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+ }
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..f11a20c
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,494 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Workspace for InsertUndoRecord and UnpackUndoRecord. */
+static UndoRecordHeader work_hdr;
+static UndoRecordRelationDetails work_rd;
+static UndoRecordBlock work_blk;
+static UndoRecordTransaction work_txn;
+static UndoRecordPayload work_payload;
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *my_bytes_written, int *total_bytes_written);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *my_bytes_read, int *total_bytes_read, bool nocopy);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+		size += SizeOfUndoRecordRelationDetails;
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * To insert an undo record, call InsertUndoRecord() repeatedly until it
+ * returns true.
+ *
+ * Insert as much of an undo record as will fit in the given page.
+ * starting_byte is the byte within the give page at which to begin writing,
+ * while *already_written is the number of bytes written to previous pages.
+ *
+ * Returns true if the remainder of the record was written and false if more
+ * bytes remain to be written; in either case, *already_written is set to the
+ * number of bytes written thus far.
+ *
+ * This function assumes that if *already_written is non-zero on entry, the
+ * same UnpackedUndoRecord is passed each time.  It also assumes that
+ * UnpackUndoRecord is not called between successive calls to InsertUndoRecord
+ * for the same UnpackedUndoRecord.
+ *
+ * If this function is called again to continue writing the record, the
+ * previous value for *already_written should be passed again, and
+ * starting_byte should be passed as sizeof(PageHeaderData) (since the record
+ * will continue immediately following the page header).
+ *
+ * remaining_bytes number of bytes to be written yet.  This value is only
+ * considered when page is NULL and that is required when caller just wanted
+ * the local work_hdr and the already_written variable to get updated but
+ * don't want to insert actual data in current block and work_hdr should be
+ * updated so that we can insert the remaining partial record in the next
+ * valid block.
+ */
+bool
+InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written, int remaining_bytes,
+				 uint16 undo_len, bool header_only)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+	int			my_bytes_written = *already_written;
+
+	/* The undo record must contain a valid information. */
+	Assert(uur->uur_info != 0);
+
+	/* If caller is not updating only header then must provide valid length. */
+	Assert(header_only || undo_len > 0);
+
+	/*
+	 * If this is the first call, copy the UnpackedUndoRecord into the
+	 * temporary variables of the types that will actually be stored in the
+	 * undo pages.  We just initialize everything here, on the assumption that
+	 * it's not worth adding branches to save a handful of assignments.
+	 */
+	if (*already_written == 0)
+	{
+		work_hdr.urec_rmid = uur->uur_rmid;
+		work_hdr.urec_type = uur->uur_type;
+		work_hdr.urec_info = uur->uur_info;
+		work_hdr.urec_reloid = uur->uur_reloid;
+		work_hdr.urec_prevxid = uur->uur_prevxid;
+		work_hdr.urec_xid = uur->uur_xid;
+		work_hdr.urec_cid = uur->uur_cid;
+		work_rd.urec_fork = uur->uur_fork;
+		work_blk.urec_blkprev = uur->uur_blkprev;
+		work_blk.urec_block = uur->uur_block;
+		work_blk.urec_offset = uur->uur_offset;
+		work_txn.urec_progress = uur->uur_progress;
+		work_txn.urec_xidepoch = uur->uur_xidepoch;
+		work_txn.urec_dbid = uur->uur_dbid;
+		work_txn.urec_prevurp = uur->uur_prevurp;
+		work_txn.urec_next = uur->uur_next;
+		work_payload.urec_payload_len = uur->uur_payload.len;
+		work_payload.urec_tuple_len = uur->uur_tuple.len;
+	}
+	else
+	{
+		/*
+		 * We should have been passed the same record descriptor as before, or
+		 * caller has messed up.
+		 */
+		Assert(work_hdr.urec_rmid == uur->uur_rmid);
+		Assert(work_hdr.urec_type == uur->uur_type);
+		Assert(work_hdr.urec_info == uur->uur_info);
+		Assert(work_hdr.urec_reloid == uur->uur_reloid);
+		Assert(work_hdr.urec_prevxid == uur->uur_prevxid);
+		Assert(work_hdr.urec_xid == uur->uur_xid);
+		Assert(work_hdr.urec_cid == uur->uur_cid);
+		Assert(work_rd.urec_fork == uur->uur_fork);
+		Assert(work_blk.urec_blkprev == uur->uur_blkprev);
+		Assert(work_blk.urec_block == uur->uur_block);
+		Assert(work_blk.urec_offset == uur->uur_offset);
+		Assert(work_txn.urec_progress == uur->uur_progress);
+		Assert(work_txn.urec_xidepoch == uur->uur_xidepoch);
+		Assert(work_txn.urec_dbid == uur->uur_dbid);
+		Assert(work_txn.urec_prevurp == uur->uur_prevurp);
+		Assert(work_txn.urec_next == uur->uur_next);
+		Assert(work_payload.urec_payload_len == uur->uur_payload.len);
+		Assert(work_payload.urec_tuple_len == uur->uur_tuple.len);
+	}
+
+	/*
+	 * Update already_written variable and return, see detailed comment in
+	 * function header.
+	 */
+	if (page == NULL)
+	{
+		*already_written += (BLCKSZ - starting_byte);
+		if (remaining_bytes <= (BLCKSZ - starting_byte))
+			return true;
+		else
+			return false;
+	}
+
+	/* Write header (if not already done). */
+	if (!InsertUndoBytes((char *) &work_hdr, SizeOfUndoRecordHeader,
+						 &writeptr, endptr,
+						 &my_bytes_written, already_written))
+		return false;
+
+	/* Write relation details (if needed and not already done). */
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0 &&
+		!InsertUndoBytes((char *) &work_rd, SizeOfUndoRecordRelationDetails,
+						 &writeptr, endptr,
+						 &my_bytes_written, already_written))
+		return false;
+
+	/* Write block information (if needed and not already done). */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0 &&
+		!InsertUndoBytes((char *) &work_blk, SizeOfUndoRecordBlock,
+						 &writeptr, endptr,
+						 &my_bytes_written, already_written))
+		return false;
+
+	/* Write transaction information (if needed and not already done). */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0 &&
+		!InsertUndoBytes((char *) &work_txn, SizeOfUndoRecordTransaction,
+						 &writeptr, endptr,
+						 &my_bytes_written, already_written))
+		return false;
+
+	if (header_only)
+		return true;
+
+	/* Write payload information (if needed and not already done). */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		/* Payload header. */
+		if (!InsertUndoBytes((char *) &work_payload, SizeOfUndoRecordPayload,
+							 &writeptr, endptr,
+							 &my_bytes_written, already_written))
+			return false;
+
+		/* Payload bytes. */
+		if (uur->uur_payload.len > 0 &&
+			!InsertUndoBytes(uur->uur_payload.data, uur->uur_payload.len,
+							 &writeptr, endptr,
+							 &my_bytes_written, already_written))
+			return false;
+
+		/* Tuple bytes. */
+		if (uur->uur_tuple.len > 0 &&
+			!InsertUndoBytes(uur->uur_tuple.data, uur->uur_tuple.len,
+							 &writeptr, endptr,
+							 &my_bytes_written, already_written))
+			return false;
+	}
+
+	/* Insert undo record length at the end of the record. */
+	if (!InsertUndoBytes((char *) &undo_len, sizeof(uint16),
+						 &writeptr, endptr,
+						 &my_bytes_written, already_written))
+		return false;
+
+	/* Hooray! */
+	return true;
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *my_bytes_written, int *total_bytes_written)
+{
+	int			can_write;
+	int			remaining;
+
+	/*
+	 * If we've previously written all of these bytes, there's nothing to do
+	 * except update *my_bytes_written, which we must do to ensure that the
+	 * next call to this function gets the right starting value.
+	 */
+	if (*my_bytes_written >= sourcelen)
+	{
+		*my_bytes_written -= sourcelen;
+		return true;
+	}
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *my_bytes_written;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *my_bytes_written, can_write);
+
+	/* Update bookkeeeping infrormation. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+	*my_bytes_written = 0;
+
+	/* Return true only if we wrote the whole thing. */
+	return (can_write == remaining);
+}
+
+/*
+ * Call UnpackUndoRecord() one or more times to unpack an undo record.  For
+ * the first call, starting_byte should be set to the beginning of the undo
+ * record within the specified page, and *already_decoded should be set to 0;
+ * the function will update it based on the number of bytes decoded.  The
+ * return value is true if the entire record was unpacked and false if the
+ * record continues on the next page.  In the latter case, the function
+ * should be called again with the next page, passing starting_byte as the
+ * sizeof(PageHeaderData).
+ */
+bool
+UnpackUndoRecord(UnpackedUndoRecord *uur, Page page, int starting_byte,
+				 int *already_decoded, bool header_only)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+	int			my_bytes_decoded = *already_decoded;
+	bool		is_undo_splited = (my_bytes_decoded > 0) ? true : false;
+
+	/* Decode header (if not already done). */
+	if (!ReadUndoBytes((char *) &work_hdr, SizeOfUndoRecordHeader,
+					   &readptr, endptr,
+					   &my_bytes_decoded, already_decoded, false))
+		return false;
+
+	uur->uur_rmid = work_hdr.urec_rmid;
+	uur->uur_type = work_hdr.urec_type;
+	uur->uur_info = work_hdr.urec_info;
+	uur->uur_reloid = work_hdr.urec_reloid;
+	uur->uur_prevxid = work_hdr.urec_prevxid;
+	uur->uur_xid = work_hdr.urec_xid;
+	uur->uur_cid = work_hdr.urec_cid;
+
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+	{
+		/* Decode header (if not already done). */
+		if (!ReadUndoBytes((char *) &work_rd, SizeOfUndoRecordRelationDetails,
+						   &readptr, endptr,
+						   &my_bytes_decoded, already_decoded, false))
+			return false;
+
+		uur->uur_fork = work_rd.urec_fork;
+	}
+
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		if (!ReadUndoBytes((char *) &work_blk, SizeOfUndoRecordBlock,
+						   &readptr, endptr,
+						   &my_bytes_decoded, already_decoded, false))
+			return false;
+
+		uur->uur_blkprev = work_blk.urec_blkprev;
+		uur->uur_block = work_blk.urec_block;
+		uur->uur_offset = work_blk.urec_offset;
+	}
+
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		if (!ReadUndoBytes((char *) &work_txn, SizeOfUndoRecordTransaction,
+						   &readptr, endptr,
+						   &my_bytes_decoded, already_decoded, false))
+			return false;
+
+		uur->uur_progress = work_txn.urec_progress;
+		uur->uur_xidepoch = work_txn.urec_xidepoch;
+		uur->uur_dbid = work_txn.urec_dbid;
+		uur->uur_prevurp = work_txn.urec_prevurp;
+		uur->uur_next = work_txn.urec_next;
+
+	}
+
+	if (header_only)
+		return true;
+
+	/* Read payload information (if needed and not already done). */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		if (!ReadUndoBytes((char *) &work_payload, SizeOfUndoRecordPayload,
+						   &readptr, endptr,
+						   &my_bytes_decoded, already_decoded, false))
+			return false;
+
+		uur->uur_payload.len = work_payload.urec_payload_len;
+		uur->uur_tuple.len = work_payload.urec_tuple_len;
+
+		/*
+		 * If we can read the complete record from a single page then just
+		 * point payload data and tuple data into the page otherwise allocate
+		 * the memory.
+		 *
+		 * XXX There is possibility of optimization that instead of always
+		 * allocating the memory whenever tuple is split we can check if any
+		 * of the payload or tuple data falling into the same page then don't
+		 * allocate the memory for that.
+		 */
+		if (!is_undo_splited &&
+			uur->uur_payload.len + uur->uur_tuple.len <= (endptr - readptr))
+		{
+			uur->uur_payload.data = readptr;
+			readptr += uur->uur_payload.len;
+
+			uur->uur_tuple.data = readptr;
+		}
+		else
+		{
+			if (uur->uur_payload.len > 0 && uur->uur_payload.data == NULL)
+				uur->uur_payload.data = (char *) palloc0(uur->uur_payload.len);
+
+			if (uur->uur_tuple.len > 0 && uur->uur_tuple.data == NULL)
+				uur->uur_tuple.data = (char *) palloc0(uur->uur_tuple.len);
+
+			if (!ReadUndoBytes((char *) uur->uur_payload.data,
+							   uur->uur_payload.len, &readptr, endptr,
+							   &my_bytes_decoded, already_decoded, false))
+				return false;
+
+			if (!ReadUndoBytes((char *) uur->uur_tuple.data,
+							   uur->uur_tuple.len, &readptr, endptr,
+							   &my_bytes_decoded, already_decoded, false))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'my_bytes_read' is a pointer to the count of previous-read bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *my_bytes_read, int *total_bytes_read, bool nocopy)
+{
+	int			can_read;
+	int			remaining;
+
+	if (*my_bytes_read >= readlen)
+	{
+		*my_bytes_read -= readlen;
+		return true;
+	}
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *my_bytes_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	if (!nocopy)
+		memcpy(destptr + *my_bytes_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+	*my_bytes_read = 0;
+
+	/* Return true only if we wrote the whole thing. */
+	return (can_read == remaining);
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_RELATION_DETAILS;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
new file mode 100644
index 0000000..5693827
--- /dev/null
+++ b/src/include/access/undoinsert.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoinsert.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOINSERT_H
+#define UNDOINSERT_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+extern UndoRecPtr PrepareUndoInsert(UnpackedUndoRecord *, FullTransactionId fxid,
+									UndoPersistence upersistence,
+									XLogReaderState *xlog_record);
+extern void InsertPreparedUndo(void);
+
+extern void RegisterUndoLogBuffers(uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(XLogRecPtr recptr);
+extern void UnlockReleaseUndoBuffers(void);
+
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern void UndoRecordSetPrevUndoLen(uint16 len);
+extern void UndoSetPrepareSize(int nrecords);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, uint16 prevlen, UndoRecPtr prevurp);
+extern void ResetUndoBuffers(void);
+
+extern UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+											UndoRecPtr urp, RelFileNode rnode,
+											UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..bbe7861
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,201 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	uint16		urec_prevlen;	/* length of previous record in bytes */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple present in this undo record.
+	 * If this is older than oldestXidWithEpochHavingUndo, then we can
+	 * consider the tuple in this undo record as visible.
+	 */
+	TransactionId urec_prevxid;
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_RELATION_DETAILS			0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_PAYLOAD					0x04
+#define UREC_INFO_TRANSACTION				0x08
+
+/*
+ * Additional information about a relation to which this record pertains,
+ * namely the fork number.  If the fork number is MAIN_FORKNUM, this structure
+ * can (and should) be omitted.
+ */
+typedef struct UndoRecordRelationDetails
+{
+	ForkNumber	urec_fork;		/* fork number */
+} UndoRecordRelationDetails;
+
+#define SizeOfUndoRecordRelationDetails \
+	(offsetof(UndoRecordRelationDetails, urec_fork) + sizeof(uint8))
+
+/*
+ * Identifying information for a block to which this record pertains, and
+ * a pointer to the previous record for the same block.
+ */
+typedef struct UndoRecordBlock
+{
+	UndoRecPtr	urec_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Identifying information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUrecNext (sizeof(UndoRecPtr))
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + SizeOfUrecNext)
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	uint16		uur_prevlen;	/* length of previous record */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_prevxid;	/* transaction id */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	Buffer		uur_buffer;		/* buffer in which undo record data points */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern bool UnpackUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_decoded, bool header_only);
+
+#endif							/* UNDORECORD_H */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index d787f92..0d1b846 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -15,6 +15,7 @@
 #define XACT_H
 
 #include "access/transam.h"
+#include "access/undolog.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
@@ -439,5 +440,6 @@ extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_ab
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
 extern bool IsInParallelMode(void);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr);
 
 #endif							/* XACT_H */
-- 
1.8.3.1

#11Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#10)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Apr 19, 2019 at 6:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Currently, undo branch[1] contain an older version of the (undo
interface + some fixup). Now, I have merged the latest changes from
the zheap branch[2] to the undo branch[1]
which can be applied on top of the undo storage commit[3]. For
merging those changes, I need to add some changes to the undo log
storage patch as well for handling the multi log transaction. So I
have attached two patches, 1) improvement to undo log storage 2)
complete undo interface patch which include 0006+0007 from undo
branch[1] + new changes on the zheap branch.

Some review comments:

+#define AtAbort_ResetUndoBuffers() ResetUndoBuffers()

I don't think this really belongs in xact.c. Seems like we ought to
declare it in the appropriate header file. Perhaps we also ought to
consider using a static inline function rather than a macro, although
I guess it doesn't really matter.

+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr)
+{
+ UndoLogControl *log = UndoLogGet(UndoRecPtrGetLogNo(urec_ptr), false);
+ UndoPersistence upersistence = log->meta.persistence;

Could we arrange things so that the caller passes the persistence
level, instead of having to recalculate it here? This doesn't seem to
be a particularly cheap operation. UndoLogGet() will call
get_undo_log() which I guess will normally just do a hash table lookup
but which in the worst case will take an LWLock and perform a linear
search of a shared memory array. But at PrepareUndoInsert time we
knew the persistence level already, so I don't see why
InsertPreparedUndo should have to force it to be looked up all over
again -- and while in a critical section, no less.

Another thing that is strange about this patch is that it adds these
start_urec_ptr and latest_urec_ptr arrays and then uses them for
absolutely nothing. I think that's a pretty clear sign that the
division of this work into multiple patches is not correct. We
shouldn't have one patch that tracks some information that is used
nowhere for anything and then another patch that adds a user of that
information -- the two should go together.

Incidentally, wouldn't StartTransaction() need to reinitialize these fields?

+ * When the undorecord for a transaction gets inserted in the next log then we

undo record

+ * insert a transaction header for the first record in the new log and update
+ * the transaction header with this new logs location.  We will also keep

This appears to be nonsensical. You're saying that you add a
transaction header to the new log and then update it with its own
location. That can't be what you mean.

+ * Incase, this is not the first record in new log (aka new log already

"Incase," -> "If"
"aka" -> "i.e."

Overall this whole paragraph is a bit hard to understand.

+ * same transaction spans across multiple logs depending on which log is

delete "across"

+ * processed first by the discard worker.  If it processes the first log which
+ * contains the transactions first record, then it can get the last record
+ * of that transaction even if it is in different log and then processes all
+ * the undo records from last to first.  OTOH, if the next log get processed

Not sure how that would work if the number of logs is >2.

This whole paragraph is also hard to understand.

+static UndoBuffers def_buffers[MAX_UNDO_BUFFERS];
+static int buffer_idx;

This is a file-global variable with a very generic name that is very
similar to a local variable name used by multiple functions within the
file (bufidx) and no comments. Ugh.

+UndoRecordIsValid(UndoLogControl * log, UndoRecPtr urp)

The locking regime for this function is really confusing. It requires
that the caller hold discard_lock on entry, and on exit the lock will
still be held if the return value is true but will no longer be held
if the return value is false. Yikes! Anybody who reads code that
uses this function is not going to guess that it has such strange
behavior. I'm not exactly sure how to redesign this, but I think it's
not OK the way you have it. One option might be to inline the logic
into each call site.

+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(int idx)

It bugs me that this function goes back in to reacquire the discard
lock for the purpose of preventing a concurrent undo discard.
Apparently, if the other transaction's undo has been discarded between
the prepare phase and where we are now, we're OK with that and just
exit without doing anything; otherwise, we update the previous
transaction header. But that seems wrong. When we enter a critical
section, I think we should aim to know exactly what modifications we
are going to make within that critical section.

I also wonder how the concurrent discard could really happen. We must
surely be holding exclusive locks on the relevant buffers -- can undo
discard really discard undo when the relevant buffers are x-locked?

It seems to me that remaining_bytes is a crock that should be ripped
out entirely, both here and in InsertUndoRecord. It seems to me that
UndoRecordUpdateTransInfo can just contrive to set remaining_bytes
correctly. e.g.

do
{
// stuff
if (!BufferIsValid(buffer))
{
Assert(InRecovery);
already_written += (BLCKSZ - starting_byte);
done = (already_written >= undo_len);
}
else
{
page = BufferGetPage(buffer);
done = InsertUndoRecord(...);
MarkBufferDirty(buffer);
}
} while (!done);

InsertPreparedUndo needs similar treatment.

To make this work, I guess the long string of assignments in
InsertUndoRecord will need to be made unconditional, but that's
probably pretty cheap. As a fringe benefit, all of those work_blah
variables that are currently referencing file-level globals can be
replaced with something local to this function, which will surely
please the coding style police.

+ * In recovery, 'xid' refers to the transaction id stored in WAL, otherwise,
+ * it refers to the top transaction id because undo log only stores mapping
+ * for the top most transactions.
+ */
+UndoRecPtr
+PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,

xid vs fxid

+ urec->uur_xidepoch = EpochFromFullTransactionId(fxid);

We need to make some decisions about how we're going to handle 64-bit
XIDs vs. 32-bit XIDs in undo. This doesn't look like a well-considered
scheme. In general, PrepareUndoInsert expects the caller to have
populated the UnpackedUndoRecord, but here, uur_xidepoch is getting
overwritten with the high bits of the caller-specified XID. The
low-order bits aren't stored anywhere by this function, but the caller
is presumably expected to have placed them inside urec->uur_xid. And
it looks like the low-order bits (urec->uur_xid) get stored for every
undo record, but the high-order bits (urec->xidepoch) only get stored
when we emit a transaction header. This all seems very confusing.

I would really like to see us replace uur_xid and uur_xidepoch with a
FullTransactionId; now that we have that concept, it seems like bad
practice to break what is really a FullTransactionId into two halves
and store them separately. However, it would be a bit unfortunate to
store an additional 4 bytes of mostly-static data in every undo
record. What if we went the other way? That is, remove urec_xid
from UndoRecordHeader and from UnpackedUndoRecord. Make it the
responsibility of whoever is looking up an undo record to know which
transaction's undo they are searching. zheap, at least, generally
does know this: if it's starting from a page, then it has the XID +
epoch available from the transaction slot, and if it's starting from
an undo record, you need to know the XID for which you are searching,
I guess from uur_prevxid.

I also think that we need to remove uur_prevxid. That field does not
seem to be properly a general-purpose part of the undo machinery, but
a zheap-specific consideration. I think it's job is to tell you which
transaction last modified the current tuple, but zheap can put that
data in the payload if it likes. It is a waste to store it in every
undo record, because it's not needed if the older undo has already
been discarded or if the operation is an insert.

+ * Insert a previously-prepared undo record. This will write the actual undo

Looks like this now inserts all previously-prepared undo records
(rather than just a single one).

+ * in page. We start writting immediately after the block header.

Spelling.

+ * Helper function for UndoFetchRecord.  It will fetch the undo record pointed
+ * by urp and unpack the record into urec.  This function will not release the
+ * pin on the buffer if complete record is fetched from one buffer, so caller
+ * can reuse the same urec to fetch the another undo record which is on the
+ * same block.  Caller will be responsible to release the buffer inside urec
+ * and set it to invalid if it wishes to fetch the record from another block.
+ */
+UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+ UndoPersistence persistence)

I don't really understand why uur_buffer is part of an
UnpackedUndoRecord. It doesn't look like the other fields, which tell
you about the contents of an undo record that you have created or that
you have parsed. Instead, it's some kind of scratch space for keeping
track of a buffer that we're using in the process of reading an undo
record. It looks like it should be an argument to UndoGetOneRecord()
and ResetUndoRecord().

I also wonder whether it's really a good design to make the caller
responsible for invalidating the buffer before accessing another
block. Maybe it would be simpler to have this function just check
whether the buffer it's been given is the right one; if not, unpin it
and pin the new one instead. But I'm not really sure...

+ /* If we already have a buffer pin then no need to allocate a new one. */
+ if (!BufferIsValid(buffer))
+ {
+ buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+    rnode, UndoLogForkNum, cur_blk,
+    RBM_NORMAL, NULL,
+    RelPersistenceForUndoPersistence(persistence));
+
+ urec->uur_buffer = buffer;
+ }

I think you should move this code inside the loop that follows. Then
at the bottom of that loop, instead of making a similar call, just set
buffer = InvalidBuffer. Then when you loop around it'll do the right
thing and you'll need less code.

Notice that having both the local variable buffer and the structure
member urec->uur_buffer is actually making this code more complex. You
are already setting urec->uur_buffer = InvalidBuffer when you do
UnlockReleaseBuffer(). If you didn't have a separate 'buffer'
variable you wouldn't need to clear them both here. In fact I think
what you should have is an argument Buffer *curbuf, or something like
that, and no uur_buffer at all.

+ /*
+ * If we have copied the data then release the buffer, otherwise, just
+ * unlock it.
+ */
+ if (is_undo_rec_split)
+ UnlockReleaseBuffer(buffer);
+ else
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);

Ugh. I think what's going on here is: UnpackUndoRecord copies the
data if it switches buffers, but not otherwise. So if the record is
split, we can release the lock and pin, but otherwise we have to keep
the pin to avoid having the data get clobbered. But having this code
know about what UnpackUndoRecord does internally seems like an
abstraction violation. It's also probably not right if we ever want
to fetch undo records in bulk, as I see that the latest code in zheap
master does. I think we should switch UnpackUndoRecord over to always
copying the data and just avoid all this.

(To make that cheaper, we may want to teach UnpackUndoRecord to store
data into scratch space provided by the caller rather than using
palloc to get its own space, but I'm not actually sure that's (a)
worth it or (b) actually better.)

[ Skipping over some things ]

+bool
+UnpackUndoRecord(UnpackedUndoRecord *uur, Page page, int starting_byte,
+ int *already_decoded, bool header_only)

I think we should split this function into three functions that use a
context object, call it say UnpackUndoContext. The caller will do:

BeginUnpackUndo(&ucontext); // just once
UnpackUndoData(&ucontext, page, starting_byte); // any number of times
FinishUnpackUndo(&uur, &ucontext); // just once

The undo context will store an enum value that tells us the "stage" of decoding:

- UNDO_DECODE_STAGE_HEADER: We have not yet decoded even the record
header; we need to do that next.
- UNDO_DECODE_STAGE_RELATION_DETAILS: The next thing to be decoded is
the relation details, if present.
- UNDO_DECODE_STAGE_BLOCK: The next thing to be decoded is the block
details, if present.
- UNDO_DECODE_STAGE_TRANSACTION: The next thing to be decoded is the
transaction details, if present.
- UNDO_DECODE_STAGE_PAYLOAD: The next thing to be decoded is the
payload details, if present.
- UNDO_DECODE_STAGE_DONE: Decoding is complete.

It will also store the number of bytes that have been already been
copied as part of whichever stage is current. A caller who wants only
part of the record can stop when ucontext.stage > desired_stage; e.g.
the current header_only flag corresponds to stopping when
ucontext.stage > UNDO_DECODE_STAGE_HEADER, and the potential
optimization mentioned in UndoGetOneRecord could be done by stopping
when ucontext.stage > UNDO_DECODE_STAGE_BLOCK (although I don't know
if that's worth doing).

In this scheme, BeginUnpackUndo just needs to set the stage to
UNDO_DECODE_STAGE_HEADER and the number of bytes copied to 0. The
context object contains all the work_blah things (no more global
variables!), but BeginUnpackUndo does not need to initialize them,
since they will be overwritten before they are examined. And
FinishUnpackUndo just needs to copy all of the fields from the
work_blah things into the UnpackedUndoRecord. The tricky part is
UnpackUndoData itself, which I propose should look like a big switch
where all branches fall through. Roughly:

switch (ucontext->stage)
{
case UNDO_DECODE_STAGE_HEADER:
if (!ReadUndoBytes(...))
return;
stage = UNDO_DECODE_STAGE_RELATION_DETAILS;
/* FALLTHROUGH */
case UNDO_DECODE_STAGE_RELATION_DETAILS:
if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
{
if (!ReadUndoBytes(...))
return;
}
stage = UNDO_DECODE_STAGE_BLOCK;
/* FALLTHROUGH */
etc.

ReadUndoBytes would need some adjustments in this scheme; it wouldn't
need my_bytes_read any more since it would only get called for
structures that are not yet completely read. (Regardless of whether
we adopt this idea, the nocopy flag to ReadUndoBytes appears to be
unused and can be removed.)

We could use a similar context object for InsertUndoRecord.
BeginInsertUndoRecord(&ucontext, &uur) would initialize all of the
work_blah structures within the context object. InsertUndoData will
be a big switch. Maybe no need for a "finish" function here. There
can also be a SkipInsertingUndoData function that can be called
instead of InsertUndoData if the page is discarded. I think this
would be more elegant than what we've got now.

This is not a complete review, but I'm out of time and energy for the moment...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#12Shawn Debnath
sdn@amazon.com
In reply to: Thomas Munro (#9)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Mar 13, 2019 at 02:20:29AM +1300, Thomas Munro wrote:

=== 0002 "Add SmgrId to smgropen() and BufferTag." ===

This is new, and is based on the discussion from another recent
thread[1] about how we should identify buffers belonging to different
storage managers. In earlier versions of the patch-set I had used a
special reserved DB OID for undo data. Tom Lane didn't like that idea
much, and Anton Shyrabokau (via Shawn Debnath) suggested making
ForkNumber narrower so we can add a new field to BufferTag, and Andres
Freund +1'd my proposal to add the extra value as a parameter to
smgropen(). So, here is a patch that tries those ideas.

Another way to do this would be to widen RelFileNode instead, to avoid
having to pass around the SMGR ID separately in various places.
Looking at the number of places that have to chance, you can probably
see why we wanted to use a magic DB OID instead, and I'm not entirely
convinced that it wasn't better that way, or that I've found all the
places that need to carry an smgrid alongside a RelFileNode.

Archeological note: smgropen() was like that ~15 years ago before
commit 87bd9563, but buffer tags didn't include the SMGR ID.

I decided to call md.c's ID "SMGR_RELATION", describing what it really
holds -- regular relations -- rather than perpetuating the doubly
anachronistic "magnetic disk" name.

While here, I resurrected the ancient notion of a per-SMGR 'open'
routine, so that a small amount of md.c-specific stuff could be kicked
out of smgr.c and future implementations can do their own thing here
too.

While doing that work I realised that at least pg_rewind needs to
learn about how different storage managers map blocks to files, so
that's a new TODO item requiring more thought. I wonder what other
places know how to map { RelFileNode, ForkNumber, BlockNumber } to a
path + offset, and I wonder what to think about the fact that some of
them may be non-backend code...

Given the scope of this patch, it might be prudent to start a separate
thread for it. So far, this discussion has been burried within other
discussions and I want to ensure folks don't miss this.

Thanks.

--
Shawn Debnath
Amazon Web Services (AWS)

#13Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#11)
3 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Apr 19, 2019 at 10:13 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Apr 19, 2019 at 6:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Currently, undo branch[1] contain an older version of the (undo
interface + some fixup). Now, I have merged the latest changes from
the zheap branch[2] to the undo branch[1]
which can be applied on top of the undo storage commit[3]. For
merging those changes, I need to add some changes to the undo log
storage patch as well for handling the multi log transaction. So I
have attached two patches, 1) improvement to undo log storage 2)
complete undo interface patch which include 0006+0007 from undo
branch[1] + new changes on the zheap branch.

Thanks for the review Robert. Please find my reply inline.

Some review comments:

+#define AtAbort_ResetUndoBuffers() ResetUndoBuffers()

I don't think this really belongs in xact.c. Seems like we ought to
declare it in the appropriate header file. Perhaps we also ought to
consider using a static inline function rather than a macro, although
I guess it doesn't really matter.

Moved to undoinsert.h

+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr)
+{
+ UndoLogControl *log = UndoLogGet(UndoRecPtrGetLogNo(urec_ptr), false);
+ UndoPersistence upersistence = log->meta.persistence;

Right. This should not be part of this patch so removed.

+ * When the undorecord for a transaction gets inserted in the next log then we

undo record

Changed

+ * insert a transaction header for the first record in the new log and update
+ * the transaction header with this new logs location.  We will also keep

This appears to be nonsensical. You're saying that you add a
transaction header to the new log and then update it with its own
location. That can't be what you mean.

Actually, what I meant is update the transaction's header which is in
the old log. I have changed the comments

+ * Incase, this is not the first record in new log (aka new log already

"Incase," -> "If"
"aka" -> "i.e."

Done

Overall this whole paragraph is a bit hard to understand.

I tired to improve it in newer version.

+ * same transaction spans across multiple logs depending on which log is

delete "across"

Fixed

+ * processed first by the discard worker.  If it processes the first log which
+ * contains the transactions first record, then it can get the last record
+ * of that transaction even if it is in different log and then processes all
+ * the undo records from last to first.  OTOH, if the next log get processed

Not sure how that would work if the number of logs is >2.
This whole paragraph is also hard to understand.

Actually, what I meant is that if it spread in multiple logs for
example 3 logs(1,2,3) and the discard worker check the log 1 first
then for aborted transaction it will follow the chain of undo headers
and register complete request for rollback and it will apply all undo
action in log1,2and 3 together. Whereas if it encounters log2 first
it will register request for undo actions in log2 and 3 and similarly
if it encounter log 3 first then it will only process that log. We
have done that so that we can maintain the order of undo apply.
However, there is possibility that we always collect all undos and
apply together but for that we need to add one more pointer in the
transaction header (transaction's undo header in previous log). May
be the next log pointer we can keep in separate header instead if
keeping in transaction header so that it will only occupy space on
log switch.

I think this comment don't belong here it's more related to undo
discard processing so I have removed.

+static UndoBuffers def_buffers[MAX_UNDO_BUFFERS];
+static int buffer_idx;

This is a file-global variable with a very generic name that is very
similar to a local variable name used by multiple functions within the
file (bufidx) and no comments. Ugh.

Comment added.

+UndoRecordIsValid(UndoLogControl * log, UndoRecPtr urp)

The locking regime for this function is really confusing. It requires
that the caller hold discard_lock on entry, and on exit the lock will
still be held if the return value is true but will no longer be held
if the return value is false. Yikes! Anybody who reads code that
uses this function is not going to guess that it has such strange
behavior. I'm not exactly sure how to redesign this, but I think it's
not OK the way you have it. One option might be to inline the logic
into each call site.

I think the simple solution will be that inside UndoRecordIsValid
function we can directly check UndoLogIsDiscarded if oldest_data is
not yet initialized, I think we don't need to release the discard lock
for that. So I have changed it like that.

+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(int idx)

It bugs me that this function goes back in to reacquire the discard
lock for the purpose of preventing a concurrent undo discard.
Apparently, if the other transaction's undo has been discarded between
the prepare phase and where we are now, we're OK with that and just
exit without doing anything; otherwise, we update the previous
transaction header. But that seems wrong. When we enter a critical
section, I think we should aim to know exactly what modifications we
are going to make within that critical section.

I also wonder how the concurrent discard could really happen. We must
surely be holding exclusive locks on the relevant buffers -- can undo
discard really discard undo when the relevant buffers are x-locked?

It seems to me that remaining_bytes is a crock that should be ripped
out entirely, both here and in InsertUndoRecord. It seems to me that
UndoRecordUpdateTransInfo can just contrive to set remaining_bytes
correctly. e.g.

do
{
// stuff
if (!BufferIsValid(buffer))
{
Assert(InRecovery);
already_written += (BLCKSZ - starting_byte);
done = (already_written >= undo_len);
}
else
{
page = BufferGetPage(buffer);
done = InsertUndoRecord(...);
MarkBufferDirty(buffer);
}
} while (!done);

InsertPreparedUndo needs similar treatment.

To make this work, I guess the long string of assignments in
InsertUndoRecord will need to be made unconditional, but that's
probably pretty cheap. As a fringe benefit, all of those work_blah
variables that are currently referencing file-level globals can be
replaced with something local to this function, which will surely
please the coding style police.

Got fixed as part of last comment fix where we introduced
SkipInsertingUndoData and globals moved to the context.

+ * In recovery, 'xid' refers to the transaction id stored in WAL, otherwise,
+ * it refers to the top transaction id because undo log only stores mapping
+ * for the top most transactions.
+ */
+UndoRecPtr
+PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,

xid vs fxid

+ urec->uur_xidepoch = EpochFromFullTransactionId(fxid);

We need to make some decisions about how we're going to handle 64-bit
XIDs vs. 32-bit XIDs in undo. This doesn't look like a well-considered
scheme. In general, PrepareUndoInsert expects the caller to have
populated the UnpackedUndoRecord, but here, uur_xidepoch is getting
overwritten with the high bits of the caller-specified XID. The
low-order bits aren't stored anywhere by this function, but the caller
is presumably expected to have placed them inside urec->uur_xid. And
it looks like the low-order bits (urec->uur_xid) get stored for every
undo record, but the high-order bits (urec->xidepoch) only get stored
when we emit a transaction header. This all seems very confusing.

Yeah it seems bit confusing. Actually, discard worker process the
transaction chain from one transaction header to the next transaction
header so we need epoch only when it's first record of the transaction
and currently we have set all header information inside
PrepareUndoInsert. Xid is stored by caller as caller needs it for the
MVCC purpose. I think caller can always set it and if transaction
header get added then it will be stored otherwise not. So I think we
can remove setting it here.

I would really like to see us replace uur_xid and uur_xidepoch with a
FullTransactionId; now that we have that concept, it seems like bad
practice to break what is really a FullTransactionId into two halves
and store them separately. However, it would be a bit unfortunate to
store an additional 4 bytes of mostly-static data in every undo
record. What if we went the other way? That is, remove urec_xid
from UndoRecordHeader and from UnpackedUndoRecord. Make it the
responsibility of whoever is looking up an undo record to know which
transaction's undo they are searching. zheap, at least, generally
does know this: if it's starting from a page, then it has the XID +
epoch available from the transaction slot, and if it's starting from
an undo record, you need to know the XID for which you are searching,
I guess from uur_prevxid.

Right, from uur_prevxid we would know that for which xid's undo we are
looking for but without having uur_xid in undo record it self how we
would know which undo record is inserted by the xid we are looking
for? Because in zheap while following the undo chain and if slot got
switch, then there is possibility (because of slot reuse) that we
might get some other transaction's undo record for the same zheap
tuple, but we want to traverse back as we want to find the record
inserted by uur_prevxid. So we need uur_xid as well to tell who is
inserter of this undo record?

I also think that we need to remove uur_prevxid. That field does not
seem to be properly a general-purpose part of the undo machinery, but
a zheap-specific consideration. I think it's job is to tell you which
transaction last modified the current tuple, but zheap can put that
data in the payload if it likes. It is a waste to store it in every
undo record, because it's not needed if the older undo has already
been discarded or if the operation is an insert.

Done

+ * Insert a previously-prepared undo record. This will write the actual undo

Looks like this now inserts all previously-prepared undo records
(rather than just a single one).

Fixed

+ * in page. We start writting immediately after the block header.

Spelling.

Done

+ * Helper function for UndoFetchRecord.  It will fetch the undo record pointed
+ * by urp and unpack the record into urec.  This function will not release the
+ * pin on the buffer if complete record is fetched from one buffer, so caller
+ * can reuse the same urec to fetch the another undo record which is on the
+ * same block.  Caller will be responsible to release the buffer inside urec
+ * and set it to invalid if it wishes to fetch the record from another block.
+ */
+UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+ UndoPersistence persistence)

I don't really understand why uur_buffer is part of an
UnpackedUndoRecord. It doesn't look like the other fields, which tell
you about the contents of an undo record that you have created or that
you have parsed. Instead, it's some kind of scratch space for keeping
track of a buffer that we're using in the process of reading an undo
record. It looks like it should be an argument to UndoGetOneRecord()
and ResetUndoRecord().

I also wonder whether it's really a good design to make the caller
responsible for invalidating the buffer before accessing another
block. Maybe it would be simpler to have this function just check
whether the buffer it's been given is the right one; if not, unpin it
and pin the new one instead. But I'm not really sure...

I am not sure what will be better here, But I thought caller anyway
has to release the last buffer so why not to make it responsible to
keeping the track of the first buffer of the undo record and caller
understands it better that it needs to hold the first buffer of the
undo record because it hope that the previous undo record in the chain
might fall on the same buffer?
May be we can make caller completely responsible for reading the
buffer for the first block of the undo record and it will always pass
the valid buffer and UndoGetOneRecord only need to read buffer if the
undo record is spilt and it can release them right there. So the
caller will always keep track of the first buffer where undo record
start and whenever the undo record pointer change it will be
responsible for changing the buffer.

+ /* If we already have a buffer pin then no need to allocate a new one. */
+ if (!BufferIsValid(buffer))
+ {
+ buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+    rnode, UndoLogForkNum, cur_blk,
+    RBM_NORMAL, NULL,
+    RelPersistenceForUndoPersistence(persistence));
+
+ urec->uur_buffer = buffer;
+ }

I think you should move this code inside the loop that follows. Then
at the bottom of that loop, instead of making a similar call, just set
buffer = InvalidBuffer. Then when you loop around it'll do the right
thing and you'll need less code.

Done

Notice that having both the local variable buffer and the structure
member urec->uur_buffer is actually making this code more complex. You
are already setting urec->uur_buffer = InvalidBuffer when you do
UnlockReleaseBuffer(). If you didn't have a separate 'buffer'
variable you wouldn't need to clear them both here. In fact I think
what you should have is an argument Buffer *curbuf, or something like
that, and no uur_buffer at all.

Done

+ /*
+ * If we have copied the data then release the buffer, otherwise, just
+ * unlock it.
+ */
+ if (is_undo_rec_split)
+ UnlockReleaseBuffer(buffer);
+ else
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);

Ugh. I think what's going on here is: UnpackUndoRecord copies the
data if it switches buffers, but not otherwise. So if the record is
split, we can release the lock and pin, but otherwise we have to keep
the pin to avoid having the data get clobbered. But having this code
know about what UnpackUndoRecord does internally seems like an
abstraction violation. It's also probably not right if we ever want
to fetch undo records in bulk, as I see that the latest code in zheap
master does. I think we should switch UnpackUndoRecord over to always
copying the data and just avoid all this.

Done

(To make that cheaper, we may want to teach UnpackUndoRecord to store
data into scratch space provided by the caller rather than using
palloc to get its own space, but I'm not actually sure that's (a)
worth it or (b) actually better.)

[ Skipping over some things ]

+bool
+UnpackUndoRecord(UnpackedUndoRecord *uur, Page page, int starting_byte,
+ int *already_decoded, bool header_only)

I think we should split this function into three functions that use a
context object, call it say UnpackUndoContext. The caller will do:

BeginUnpackUndo(&ucontext); // just once
UnpackUndoData(&ucontext, page, starting_byte); // any number of times
FinishUnpackUndo(&uur, &ucontext); // just once

The undo context will store an enum value that tells us the "stage" of decoding:

- UNDO_DECODE_STAGE_HEADER: We have not yet decoded even the record
header; we need to do that next.
- UNDO_DECODE_STAGE_RELATION_DETAILS: The next thing to be decoded is
the relation details, if present.
- UNDO_DECODE_STAGE_BLOCK: The next thing to be decoded is the block
details, if present.
- UNDO_DECODE_STAGE_TRANSACTION: The next thing to be decoded is the
transaction details, if present.
- UNDO_DECODE_STAGE_PAYLOAD: The next thing to be decoded is the
payload details, if present.
- UNDO_DECODE_STAGE_DONE: Decoding is complete.

It will also store the number of bytes that have been already been
copied as part of whichever stage is current. A caller who wants only
part of the record can stop when ucontext.stage > desired_stage; e.g.
the current header_only flag corresponds to stopping when
ucontext.stage > UNDO_DECODE_STAGE_HEADER, and the potential
optimization mentioned in UndoGetOneRecord could be done by stopping
when ucontext.stage > UNDO_DECODE_STAGE_BLOCK (although I don't know
if that's worth doing).

In this scheme, BeginUnpackUndo just needs to set the stage to
UNDO_DECODE_STAGE_HEADER and the number of bytes copied to 0. The
context object contains all the work_blah things (no more global
variables!), but BeginUnpackUndo does not need to initialize them,
since they will be overwritten before they are examined. And
FinishUnpackUndo just needs to copy all of the fields from the
work_blah things into the UnpackedUndoRecord. The tricky part is
UnpackUndoData itself, which I propose should look like a big switch
where all branches fall through. Roughly:

switch (ucontext->stage)
{
case UNDO_DECODE_STAGE_HEADER:
if (!ReadUndoBytes(...))
return;
stage = UNDO_DECODE_STAGE_RELATION_DETAILS;
/* FALLTHROUGH */
case UNDO_DECODE_STAGE_RELATION_DETAILS:
if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
{
if (!ReadUndoBytes(...))
return;
}
stage = UNDO_DECODE_STAGE_BLOCK;
/* FALLTHROUGH */
etc.

ReadUndoBytes would need some adjustments in this scheme; it wouldn't
need my_bytes_read any more since it would only get called for
structures that are not yet completely read.

Yeah so we can directly jump to the header which is not yet completely
read but if any of the header is partially read then we need to
maintain some kind of partial read variable otherwise from 'already
read' we wouldn't be able to know how many bytes of the header got
read in last call unless we calculate that from uur_info or maintain
the partial_read in context like I have done in the new version.

(Regardless of whether

we adopt this idea, the nocopy flag to ReadUndoBytes appears to be
unused and can be removed.)

Yup.

We could use a similar context object for InsertUndoRecord.
BeginInsertUndoRecord(&ucontext, &uur) would initialize all of the
work_blah structures within the context object. InsertUndoData will
be a big switch. Maybe no need for a "finish" function here. There
can also be a SkipInsertingUndoData function that can be called
instead of InsertUndoData if the page is discarded. I think this
would be more elegant than what we've got now.

Done.

Note:
- I think the ucontext->stage are same for the insert and DECODE can
we just declare only
one enum and give some generic name e.g. UNDO_PROCESS_STAGE_HEADER ?
- In SkipInsertingUndoData also I have to go through all the stages so
that if we find some valid
block then stage is right for inserting the partial record? Do you
think I could have avoided that?

Apart from these changes I have also included UndoRecordBulkFetch in
the undoinsert.c.

I have tested this patch with my local test modules which basically
insert, fetch and bulk fetch multiple records and compare the
contents. My test patch is still not in good shape so I will post the
test module later.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Enhance-multi-log-handling-in-undo-log.patchapplication/octet-stream; name=0001-Enhance-multi-log-handling-in-undo-log.patchDownload
From dfde6ce004c8bfe165a0a7283887f0715927e8c2 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Tue, 16 Apr 2019 16:46:01 +0530
Subject: [PATCH 1/3] Enhance multi-log handling in undo log

As part of this patch UndoLogAllocate will return the current transaction
start header in previous log if there is a log switch during transaction.
This will allow discard worker to fetch the last undo record pointer for
an aborted transaction.  WAL log the undo log switch so that during
recovery we can detect the log switch.

Dilip Kumar
---
 src/backend/access/undo/undolog.c | 76 ++++++++++++++++++++++++++++++++++++++-
 src/include/access/undolog.h      | 19 +++++++++-
 src/include/access/undolog_xlog.h |  9 +++++
 3 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 38f8a4f..028b282 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -650,7 +650,9 @@ UndoLogAllocate(uint16 size,
 				UndoRecPtr try_location,
 				UndoPersistence persistence,
 				bool *need_xact_header,
-				UndoRecPtr *last_xact_start)
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
 {
 	UndoLogControl *log = MyUndoLogState.logs[persistence];
 	UndoLogOffset new_insert;
@@ -755,6 +757,11 @@ UndoLogAllocate(uint16 size,
 				 * comments in undorecord.c file header.
 				 */
 				prevlogno = log->logno;
+				*prevlog_xact_start =
+					MakeUndoRecPtr(log->logno,
+								   log->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(log->logno, log->meta.unlogged.insert);
 			}
 			elog(LOG, "undo log %u is full, switching to a new one", log->logno);
 			log = NULL;
@@ -831,6 +838,8 @@ UndoLogAllocateInRecovery(TransactionId xid,
 						  UndoRecPtr try_location,
 						  bool *need_xact_header,
 						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp,
 						  XLogReaderState *xlog_record)
 {
 	UndoLogControl *log;
@@ -959,6 +968,13 @@ UndoLogAllocateInRecovery(TransactionId xid,
 			*last_xact_start = log->meta.unlogged.last_xact_start;
 			allocate_in_recovery_logno = log->logno;
 
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = log->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = log->meta.unlogged.prevlog_last_urp;
+
+			log->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			log->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
 			return MakeUndoRecPtr(log->logno, log->meta.unlogged.insert);
 		}
 		++allocate_in_recovery_block_id;
@@ -1221,6 +1237,43 @@ UndoLogGetFirstValidRecord(UndoLogControl *log, bool *full)
 }
 
 /*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogControl *log;
+
+	log = get_undo_log(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLog(log));
+
+	LWLockAcquire(&log->mutex, LW_EXCLUSIVE);
+	log->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	log->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&log->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
  * Return the Next insert location.  This will also validate the input xid
  * if latest insert point is not for the same transaction id then this will
  * return Invalid Undo pointer.
@@ -2552,6 +2605,25 @@ undolog_xlog_rewind(XLogReaderState *record)
 	log->meta.unlogged.prevlen = xlrec->prevlen;
 }
 
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogControl *log;
+
+	log = get_undo_log(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	log->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	log->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
 void
 undolog_redo(XLogReaderState *record)
 {
@@ -2571,6 +2643,8 @@ undolog_redo(XLogReaderState *record)
 		case XLOG_UNDOLOG_REWIND:
 			undolog_xlog_rewind(record);
 			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
 		default:
 			elog(PANIC, "undo_redo: unknown op code %u", info);
 	}
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index c1b3a75..2cfce8c 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -205,6 +205,16 @@ typedef struct UndoLogUnloggedMetaData
 	uint16		prevlen;		   	/* size of the last record in the log */
 	UndoLogNumber prevlogno;		/* Previous undo log number */
 	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of undo do switch wal will
+	 * restore these two undo record pointers which will be reset on next
+	 * allocation during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -343,12 +353,16 @@ extern UndoRecPtr UndoLogAllocate(uint16 size,
 								  UndoRecPtr try_location,
 								  UndoPersistence level,
 								  bool *need_xact_header,
-								  UndoRecPtr *last_xact_start);
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
 extern UndoRecPtr UndoLogAllocateInRecovery(TransactionId xid,
 											uint16 size,
 											UndoRecPtr try_location,
 											bool *need_xact_header,
 											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp,
 											XLogReaderState *xlog_record);
 extern void UndoLogAdvance(UndoRecPtr insertion_point, size_t size);
 extern void UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
@@ -378,6 +392,9 @@ extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno,
 										  TransactionId xid);
 extern UndoRecPtr UndoLogGetLastRecordPtr(UndoLogNumber,
 										  TransactionId xid);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
 extern void UndoLogRewind(UndoRecPtr insert_urp, uint16 prevlen);
 extern uint16 UndoLogGetPrevLen(UndoLogNumber logno);
 extern void UndoLogSetLSN(XLogRecPtr lsn);
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
index feebf81..802a727 100644
--- a/src/include/access/undolog_xlog.h
+++ b/src/include/access/undolog_xlog.h
@@ -22,6 +22,7 @@
 #define XLOG_UNDOLOG_EXTEND		0x10
 #define XLOG_UNDOLOG_DISCARD	0x20
 #define XLOG_UNDOLOG_REWIND		0x30
+#define XLOG_UNDOLOG_SWITCH		0x40
 
 /* Create a new undo log. */
 typedef struct xl_undolog_create
@@ -56,6 +57,14 @@ typedef struct xl_undolog_rewind
 	uint16		  prevlen;
 } xl_undolog_rewind;
 
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
 extern void undolog_desc(StringInfo buf,XLogReaderState *record);
 extern const char *undolog_identify(uint8 info);
 
-- 
1.8.3.1

0002-Add-prefetch-support-for-the-undo-log.patchapplication/octet-stream; name=0002-Add-prefetch-support-for-the-undo-log.patchDownload
From 645ccacb372fa43b8c4b13c0ff69dbf23fee3fbb Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 2/3] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 103 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 88 insertions(+), 36 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 733a7f9..6b4e8ec 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3995,6 +3995,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 7101bef..08894a8 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -518,14 +518,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, smgr->smgr_which, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -548,43 +591,33 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_which,
-					   reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+							  ForkNumber forkNum, BlockNumber blockNum,
+							  char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index ac8e117..6f0db9e 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -116,7 +116,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 434a537..7cd71bd 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -933,6 +933,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 755e0f0..0a87d95 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -166,6 +166,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 			   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 				   BlockNumber blockNum, ReadBufferMode mode,
-- 
1.8.3.1

0003-Provide-interfaces-to-store-and-fetch-undo-records_v2.patchapplication/octet-stream; name=0003-Provide-interfaces-to-store-and-fetch-undo-records_v2.patchDownload
From 3438f926d607738f4a8a941fa594e214b3b9b764 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 18 Apr 2019 16:09:25 +0530
Subject: [PATCH 3/3] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/transam/xact.c    |    4 +-
 src/backend/access/undo/Makefile     |    2 +-
 src/backend/access/undo/undoinsert.c | 1523 ++++++++++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c |  677 +++++++++++++++
 src/include/access/transam.h         |    1 +
 src/include/access/undoinsert.h      |   64 ++
 src/include/access/undorecord.h      |  276 ++++++
 src/include/access/xact.h            |    1 +
 8 files changed, 2546 insertions(+), 2 deletions(-)
 create mode 100644 src/backend/access/undo/undoinsert.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoinsert.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index bd5024e..47a8f0d 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -30,6 +30,7 @@
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
+#include "access/undoinsert.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_enum.h"
 #include "catalog/storage.h"
@@ -68,7 +69,6 @@
 #include "utils/timestamp.h"
 #include "pg_trace.h"
 
-
 /*
  *	User-tweakable parameters
  */
@@ -2736,6 +2736,7 @@ AbortTransaction(void)
 		AtEOXact_HashTables(false);
 		AtEOXact_PgStat(false);
 		AtEOXact_ApplyLauncher(false);
+		AtAbort_ResetUndoBuffers();
 		pgstat_report_xact_timestamp(0);
 	}
 
@@ -4993,6 +4994,7 @@ AbortSubTransaction(void)
 		AtEOSubXact_PgStat(false, s->nestingLevel);
 		AtSubAbort_Snapshot(s->nestingLevel);
 		AtEOSubXact_ApplyLauncher(false, s->nestingLevel);
+		AtAbort_ResetUndoBuffers();
 	}
 
 	/*
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
new file mode 100644
index 0000000..5161ca2
--- /dev/null
+++ b/src/backend/access/undo/undoinsert.c
@@ -0,0 +1,1523 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.c
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoinsert.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the previous log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the last undo record of
+ * the transaction in the previous log, so that we can find the previous undo
+ * record pointer during rollback.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoinsert.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * This defines the number of undo records that can be prepared before
+ * calling insert by default.  If you need to prepare more than
+ * MAX_PREPARED_UNDO undo records, then you must call UndoSetPrepareSize
+ * first.
+ */
+#define MAX_PREPARED_UNDO 2
+
+/*
+ * This defines the max number of previous xact infos we need to update.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous
+ * transaction in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+/*
+ * Consider buffers needed for updating previous transaction's
+ * starting undo record as well.
+ */
+#define MAX_UNDO_BUFFERS       (MAX_PREPARED_UNDO + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO
+
+/* Undo block number to buffer mapping. */
+typedef struct UndoBuffers
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+} UndoBuffers;
+
+/*
+ * This array holds undo buffers required for preparing undo records during
+ * prepare undo time these buffers will be pinned and locked and insert will
+ * insert the actual undo record inside the critical section.
+ */
+static UndoBuffers def_buffers[MAX_UNDO_BUFFERS];
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+typedef struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+} PreparedUndoSpace;
+
+static PreparedUndoSpace def_prepared[MAX_PREPARED_UNDO];
+static int	max_prepared_undo = MAX_PREPARED_UNDO;
+
+/*
+ * By default prepared_undo and undo_buffer points to the static memory.
+ * In case caller wants to support more than default max_prepared undo records
+ * then the limit can be increased by calling UndoSetPrepareSize function.
+ * Therein, dynamic memory will be allocated and prepared_undo and undo_buffer
+ * will start pointing to newly allocated memory, which will be released by
+ * UnlockReleaseUndoBuffers and these variables will again set back to their
+ * default values.
+ */
+static PreparedUndoSpace *prepared_undo = def_prepared;
+static UndoBuffers *undo_buffer = def_buffers;
+
+/* Index into undo_buffer.  */
+static int	buffer_idx;
+
+/* Index into  prepared_undo. */
+static int	prepare_idx;
+
+/*
+ * Structure to hold the previous transaction's undo update information.  This
+ * is populated while current transaction is updating its undo record pointer
+ * in previous transactions first undo record.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+static XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];
+static int	xact_urec_info_idx;
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static void UndoRecordPrepareTransInfo(UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp,
+						   XLogReaderState *xlog_record);
+static void UndoRecordUpdateTransInfo(int idx);
+static int UndoGetBufferSlot(RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm,
+				  UndoPersistence persistence, XLogReaderState *xlog_record);
+static bool UndoRecordIsValid(UndoLogControl *log,
+				  UndoRecPtr urp);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Check whether the undo record is discarded or not.  If it's already discarded
+ * return false otherwise return true.
+ *
+ * Caller must hold lock on log->discard_lock.
+ */
+static bool
+UndoRecordIsValid(UndoLogControl *log, UndoRecPtr urp)
+{
+	Assert(LWLockHeldByMeInMode(&log->discard_lock, LW_SHARED));
+
+	/*
+	 * oldest_data is only initialized when the DiscardWorker first time
+	 * attempts to discard undo logs so we can not rely on this value to
+	 * identify whether the undo record pointer is already discarded or not so
+	 * we can check it by calling undo log routine.
+	 */
+	if (log->oldest_data == InvalidUndoRecPtr)
+	{
+		if (UndoLogIsDiscarded(urp))
+			return false;
+	}
+	else if (urp < log->oldest_data)
+		return false;
+
+	return true;
+}
+
+/*
+ * Prepare to update the previous transaction's next undo pointer to maintain
+ * the transaction chain in the undo.  This will read the header of the first
+ * undo record of the previous transaction and lock the necessary buffers.
+ * The actual update will be done by UndoRecordUpdateTransInfo under the
+ * critical section.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
+						   XLogReaderState *xlog_record)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	UnpackUndoContext ucontext = {{0}};
+	Page		page;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before accessing the undo record so that
+	 * discard worker doesn't remove the record while we are in process of
+	 * reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 * UndoRecordIsValid will release the lock if it returns false.
+	 */
+	if (!UndoRecordIsValid(log, xact_urp))
+	{
+		LWLockRelease(&log->discard_lock);
+		return;
+	}
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (true)
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk,
+								   RBM_NORMAL,
+								   log->meta.persistence, xlog_record);
+		xact_urec_info[xact_urec_info_idx].idx_undo_buffers[index++] = bufidx;
+		buffer = undo_buffer[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_DECODE_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_urec_info[xact_urec_info_idx].uur);
+
+	xact_urec_info[xact_urec_info_idx].uur.uur_next = urecptr;
+	xact_urec_info[xact_urec_info_idx].urecptr = xact_urp;
+	xact_urec_info_idx++;
+
+	LWLockRelease(&log->discard_lock);
+}
+
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(int idx)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			i = 0;
+	UndoRecPtr	urec_ptr = InvalidUndoRecPtr;
+	InsertUndoContext ucontext = {{0}};
+
+	urec_ptr = xact_urec_info[idx].urecptr;
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(urec_ptr);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, &xact_urec_info[idx].uur);
+
+	/* Main loop for writing the undo record. */
+	do
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_urec_info[idx].idx_undo_buffers[i];
+		buffer = undo_buffer[buf_idx].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * removed by discard process, so we can just skip inserting into
+		 * those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * Skip actual writing just update the context so that we have
+			 * write offset for inserting into next blocks.
+			 */
+			SkipInsertingUndoData(&ucontext, starting_byte);
+			if (ucontext.stage > UNDO_INSERT_STAGE_TRANSACTION)
+				break;
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/* Overwrite the previously written undo record. */
+			InsertUndoData(&ucontext, page, starting_byte);
+			if (ucontext.stage > UNDO_INSERT_STAGE_TRANSACTION)
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	} while (true);
+}
+
+/*
+ * Find the block number in undo buffer array, if it's present then just return
+ * its index otherwise search the buffer and insert an entry and lock the buffer
+ * in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm,
+				  UndoPersistence persistence,
+				  XLogReaderState *xlog_record)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < buffer_idx; i++)
+	{
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == undo_buffer[i].blk) &&
+			(undo_buffer[i].logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(undo_buffer[i].buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(undo_buffer[i].buf - 1)),
+										LW_EXCLUSIVE));
+			break;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array
+	 */
+	if (i == buffer_idx)
+	{
+		/*
+		 * Fetch the buffer in which we want to insert the undo record.
+		 */
+		if (InRecovery)
+			action = XLogReadBufferForRedoBlock(xlog_record,
+												SMGR_UNDO,
+												rnode,
+												UndoLogForkNum,
+												blk,
+												rbm,
+												false,
+												&buffer);
+		else
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode,
+											   UndoLogForkNum,
+											   blk,
+											   rbm,
+											   NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/* Lock the buffer */
+			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+		}
+
+		if (action == BLK_NOTFOUND)
+		{
+			undo_buffer[buffer_idx].buf = InvalidBuffer;
+			undo_buffer[buffer_idx].blk = InvalidBlockNumber;
+		}
+		else
+		{
+			undo_buffer[buffer_idx].buf = buffer;
+			undo_buffer[buffer_idx].blk = blk;
+			undo_buffer[buffer_idx].logno = rnode.relNode;
+			undo_buffer[buffer_idx].zero = rbm == RBM_ZERO;
+		}
+		buffer_idx++;
+	}
+
+	return i;
+}
+
+/*
+ * Call UndoSetPrepareSize to set the value of how many undo records can be
+ * prepared before we can insert them.  If the size is greater than
+ * MAX_PREPARED_UNDO then it will allocate extra memory to hold the extra
+ * prepared undo.
+ *
+ * This is normally used when more than one undo record needs to be prepared.
+ */
+void
+UndoSetPrepareSize(int nrecords)
+{
+	if (nrecords <= MAX_PREPARED_UNDO)
+		return;
+
+	prepared_undo = palloc0(nrecords * sizeof(PreparedUndoSpace));
+
+	/*
+	 * Consider buffers needed for updating previous transaction's starting
+	 * undo record. Hence increased by 1.
+	 */
+	undo_buffer = palloc0((nrecords + 1) * MAX_BUFFER_PER_UNDO *
+						  sizeof(UndoBuffers));
+	max_prepared_undo = nrecords;
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,
+				  UndoPersistence upersistence,
+				  XLogReaderState *xlog_record)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	try_location;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+
+	/* Already reached maximum prepared limit. */
+	if (prepare_idx == max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/*
+	 * Since we don't actually advance the insert pointer until later in
+	 * InsertPreparedUndo(), but we may need to allocate space for several
+	 * undo records, we need to keep track of the insert pointer as we go.
+	 */
+	if (prepare_idx == 0)
+	{
+		/* Nothing allocated already; just ask for some space anywhere. */
+		try_location = InvalidUndoRecPtr;
+	}
+	else
+	{
+		/*
+		 * Ask to extend the space immediately after the last record, if
+		 * possible.  A new undo log will be chosen otherwise.
+		 */
+		PreparedUndoSpace *space = &prepared_undo[prepare_idx - 1];
+
+		try_location = UndoLogOffsetPlusUsableBytes(space->urp, space->size);
+	}
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(upersistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(XidFromFullTransactionId(txid),
+											size, try_location,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp,
+											xlog_record);
+	}
+	else
+	{
+		urecptr = UndoLogAllocate(size, try_location, upersistence,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   upersistence);
+			/* Compute the last record's undo record pointer. */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(urecptr, last_xact_start, xlog_record);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(urecptr, prevlog_xact_start, xlog_record);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk, rbm, upersistence,
+								   xlog_record);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo[prepare_idx].undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * Save the undo record information to be later used by InsertPreparedUndo
+	 * to insert the prepared record.
+	 */
+	prepared_undo[prepare_idx].urec = urec;
+	prepared_undo[prepare_idx].urp = urecptr;
+	prepared_undo[prepare_idx].size = size;
+	prepare_idx++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.  This will write the actual undo
+ * record into the buffers already pinned and locked in PreparedUndoInsert,
+ * and mark them dirty.  This step should be performed after entering a
+ * criticalsection; it should never fail.
+ */
+void
+InsertPreparedUndo(void)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	UndoRecPtr	urp;
+	UnpackedUndoRecord *uur;
+	InsertUndoContext ucontext = {{0}};
+	uint16		size;
+
+	/* There must be atleast one prepared undo record. */
+	Assert(prepare_idx > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < prepare_idx; idx++)
+	{
+		uur = prepared_undo[idx].urec;
+		urp = prepared_undo[idx].urp;
+		size = prepared_undo[idx].size;
+
+		Assert(size == UndoRecordExpectedSize(uur));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, uur);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			PreparedUndoSpace undospace = prepared_undo[idx];
+			Buffer		buffer;
+
+			buffer = undo_buffer[undospace.undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, starting_byte);
+				if (ucontext.stage == UNDO_INSERT_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_INSERT_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvance(urp, size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	if (xact_urec_info_idx > 0)
+	{
+		int			i = 0;
+
+		for (i = 0; i < xact_urec_info_idx; i++)
+			UndoRecordUpdateTransInfo(i);
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord.  It will fetch the undo record pointed
+ * by urp and unpack the record into urec.  This function will not release the
+ * pin on the buffer if complete record is fetched from one buffer, so caller
+ * can reuse the same urec to fetch the another undo record which is on the
+ * same block.  Caller will be responsible to release the buffer inside urec
+ * and set it to invalid if it wishes to fetch the record from another block.
+ *
+ * prevbuf :  Remember the first buffer of the undo record in a hope that
+ * while traversing the undo chain in backward we might get previous record
+ * on the same buffer.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UnpackUndoContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_DECODE_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * ResetUndoRecord - Helper function for UndoFetchRecord to reset the current
+ * record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 */
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (!UndoRecordIsValid(log, urp))
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			LWLockRelease(&log->discard_lock);
+			return NULL;
+		}
+
+		/* Fetch the current undo record. */
+		urec = UndoGetOneRecord(urec, urp, rnode, log->meta.persistence,
+								&buffer);
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	if (urec_ptr_out)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+	return urec;
+}
+
+/*
+ * PrefetchUndoPages - Prefetch undo pages
+ *
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * UndoRecordBulkFetch  - Read undo records in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoRecordBulkFetch(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	do
+	{
+		BlockNumber from_blkno;
+		UndoLogControl *log;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = log->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+			if (!UndoRecordIsValid(log, urecptr))
+			{
+				LWLockRelease(&log->discard_lock);
+				break;
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+			LWLockRelease(&log->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (prev_urec_ptr == to_urecptr || uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, uur->uur_prevurp,
+											buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit then
+		 * stop processing more records.  Remember to set the from_urecptr so
+		 * that on next call we can resume fetching undo records where we left
+		 * it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	} while (true);
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * RegisterUndoLogBuffers - Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < buffer_idx; idx++)
+	{
+		flags = undo_buffer[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx, undo_buffer[idx].buf, flags);
+		UndoLogRegister(first_block_id + idx, undo_buffer[idx].logno);
+	}
+}
+
+/*
+ * UndoLogBuffersSetLSN - Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < buffer_idx; idx++)
+		PageSetLSN(BufferGetPage(undo_buffer[idx].buf), recptr);
+}
+
+/*
+ * Reset the global variables related to undo buffers. This is required at the
+ * transaction abort and while releasing the undo buffers.
+ */
+static inline void
+ResetUndoBuffers(void)
+{
+	int			i;
+
+	/* Reset undo buffer array. */
+	for (i = 0; i < buffer_idx; i++)
+	{
+		undo_buffer[i].blk = InvalidBlockNumber;
+		undo_buffer[i].buf = InvalidBuffer;
+	}
+
+	for (i = 0; i < xact_urec_info_idx; i++)
+		xact_urec_info[i].urecptr = InvalidUndoRecPtr;
+
+	/* Reset the prepared index. */
+	prepare_idx = 0;
+	buffer_idx = 0;
+	xact_urec_info_idx = 0;
+
+	/*
+	 * max_prepared_undo limit is changed so free the allocated memory and
+	 * reset all the variable back to their default value.
+	 */
+	if (max_prepared_undo > MAX_PREPARED_UNDO)
+	{
+		pfree(undo_buffer);
+		pfree(prepared_undo);
+		undo_buffer = def_buffers;
+		prepared_undo = def_prepared;
+		max_prepared_undo = MAX_PREPARED_UNDO;
+	}
+}
+
+/*
+ * Reset the undo buffers at abort.
+ */
+void
+AtAbort_ResetUndoBuffers(void)
+{
+	ResetUndoBuffers();
+}
+
+/*
+ * Unlock and release the undo buffers.  This step must be performed after
+ * exiting any critical section where we have perfomed undo actions.
+ */
+void
+UnlockReleaseUndoBuffers(void)
+{
+	int			i;
+
+	for (i = 0; i < buffer_idx; i++)
+	{
+		if (BufferIsValid(undo_buffer[i].buf))
+			UnlockReleaseBuffer(undo_buffer[i].buf);
+	}
+	ResetUndoBuffers();
+}
+
+/*
+ * UndoGetPrevRecordLen - read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the previous undo record is split then this will add the
+ * undo block header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	char	   *page;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	/*
+	 * If caller has passed invalid buffer then read the buffer.
+	 */
+	if (!BufferIsValid(buffer))
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+										   cur_blk, RBM_NORMAL, NULL,
+										   persistence);
+
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+	}
+
+	page = (char *) BufferGetPage(buffer);
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		page_offset -= 1;
+
+		/*
+		 * Read first prevlen byte from current page if page_offset hasn't
+		 * reach to undo block header.  Otherwise move to the previous page.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = page[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/* Release the previous buffer. */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+			cur_blk -= 1;
+			persistence = RelPersistenceForUndoPersistence(upersistence);
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+			page_offset = BLCKSZ;
+			page = (char *) BufferGetPage(buffer);
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Return the previous undo record pointer.
+ *
+ * A valid value of prevurp indicates that the previous undo record
+ * pointer is in some other log and caller can directly use that.
+ * Otherwise this will calculate the previous undo record pointer
+ * by using current urp and the prevlen.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, UndoRecPtr prevurp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	if (UndoRecPtrIsValid(prevurp))
+		return prevurp;
+	else
+	{
+		UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+		UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+		uint16		prevlen;
+
+		/* Read length of the previous undo record. */
+		prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+		/* calculate the previous undo record pointer */
+		return MakeUndoRecPtr(logno, offset - prevlen);
+	}
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..fd16a88
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,677 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+		size += SizeOfUndoRecordRelationDetails;
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * BeginUnpackUndo - Initiate unpacking a single one record.
+ */
+void
+BeginInsertUndo(InsertUndoContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_INSERT_STAGE_HEADER;
+	ucontext->already_written = 0;
+	ucontext->partial_write = 0;
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it presents. */
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+		ucontext->urec_rd.urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it presents. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_blkprev = uur->uur_blkprev;
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record transaction header if it presents. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record payload header and data if it presents. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * InsertUndoData - Insert the undo record into the input page from the unpack
+ * undo context.  Caller can  call this function multiple times until desired
+ * stage is reached.  This will write the undo record into the page.
+ */
+void
+InsertUndoData(InsertUndoContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_INSERT_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_written,
+								 &ucontext->partial_write))
+				return;
+			ucontext->stage = UNDO_INSERT_STAGE_RELATION_DETAILS;
+			/* fall through */
+		case UNDO_INSERT_STAGE_RELATION_DETAILS:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELATION_DETAILS) != 0)
+			{
+				/* Insert undo record relation header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_rd,
+									 SizeOfUndoRecordRelationDetails,
+									 &writeptr, endptr,
+									 &ucontext->already_written,
+									 &ucontext->partial_write))
+					return;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_INSERT_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_written,
+									 &ucontext->partial_write))
+					return;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_INSERT_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_written,
+									 &ucontext->partial_write))
+					return;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_INSERT_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_written,
+									 &ucontext->partial_write))
+					return;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_INSERT_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_written,
+										 &ucontext->partial_write))
+						return;
+				}
+				ucontext->stage = UNDO_INSERT_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_INSERT_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_written,
+										 &ucontext->partial_write))
+						return;
+				}
+				ucontext->stage = UNDO_INSERT_STAGE_UNDO_LENGTH;
+				/* fall through */
+			}
+		case UNDO_INSERT_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_written,
+								 &ucontext->partial_write))
+				return;
+
+			ucontext->stage = UNDO_INSERT_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * SkipInsertingUndoData - Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context right
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(InsertUndoContext *ucontext, int starting_byte)
+{
+	int			remaining = BLCKSZ - starting_byte;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_INSERT_STAGE_HEADER:
+			if (remaining < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_write = remaining;
+				return;
+			}
+			remaining -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_INSERT_STAGE_RELATION_DETAILS;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_RELATION_DETAILS:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELATION_DETAILS) != 0)
+			{
+				if (remaining < SizeOfUndoRecordHeader)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= SizeOfUndoRecordRelationDetails;
+			}
+
+			ucontext->stage = UNDO_INSERT_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (remaining < SizeOfUndoRecordHeader)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (remaining < SizeOfUndoRecordHeader)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_INSERT_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (remaining < SizeOfUndoRecordHeader)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (remaining < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (remaining < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_write = remaining;
+					return;
+				}
+				remaining -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_INSERT_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_INSERT_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_INSERT_STAGE_DONE;
+			return;
+		case UNDO_INSERT_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * BeginUnpackUndo - Initiate unpacking a single one record.
+ */
+void
+BeginUnpackUndo(UnpackUndoContext *ucontext)
+{
+	ucontext->stage = UNDO_DECODE_STAGE_HEADER;
+	ucontext->already_read = 0;
+	ucontext->partial_read = 0;
+}
+
+/*
+ * UnpackUndoData - Read the undo record from the input page to the unpack undo
+ * context.  Caller can  call this function multiple times until desired stage
+ * is reached.  This will read the undo record from the page and store the data
+ * into unpack undo context, which can be later copied to unpacked undo record
+ * by calling FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UnpackUndoContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_DECODE_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_read,
+							   &ucontext->partial_read))
+				return;
+			ucontext->stage = UNDO_DECODE_STAGE_RELATION_DETAILS;
+			/* fall through */
+		case UNDO_DECODE_STAGE_RELATION_DETAILS:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELATION_DETAILS) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_rd,
+								   SizeOfUndoRecordRelationDetails,
+								   &readptr, endptr, &ucontext->already_read,
+								   &ucontext->partial_read))
+					return;
+			}
+			ucontext->stage = UNDO_DECODE_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_DECODE_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_read,
+								   &ucontext->partial_read))
+					return;
+			}
+			ucontext->stage = UNDO_DECODE_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_DECODE_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_read,
+								   &ucontext->partial_read))
+					return;
+			}
+			ucontext->stage = UNDO_DECODE_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_DECODE_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_read,
+								   &ucontext->partial_read))
+					return;
+			}
+			ucontext->stage = UNDO_DECODE_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_DECODE_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc0(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_read,
+									   &ucontext->partial_read))
+						return;
+				}
+				ucontext->stage = UNDO_DECODE_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_DECODE_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc0(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_read,
+									   &ucontext->partial_read))
+						return;
+				}
+
+				ucontext->stage = UNDO_DECODE_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_DECODE_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * FinishUnpackUndo - Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UnpackUndoContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it presents. */
+	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+		uur->uur_fork = ucontext->urec_rd.urec_fork;
+
+	/* Copy undo record block header if it presents. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blk.urec_blkprev;
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record transaction header if it presents. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_prevurp = ucontext->urec_txn.urec_prevurp;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record payload header and data if it presents. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_RELATION_DETAILS;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
new file mode 100644
index 0000000..d50085e
--- /dev/null
+++ b/src/include/access/undoinsert.h
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoinsert.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOINSERT_H
+#define UNDOINSERT_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+extern UndoRecPtr PrepareUndoInsert(UnpackedUndoRecord *, FullTransactionId fxid,
+				  UndoPersistence upersistence,
+				  XLogReaderState *xlog_record);
+extern void InsertPreparedUndo(void);
+
+extern void RegisterUndoLogBuffers(uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(XLogRecPtr recptr);
+extern void UnlockReleaseUndoBuffers(void);
+
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoRecordBulkFetch(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern void UndoRecordSetPrevUndoLen(uint16 len);
+extern void UndoSetPrepareSize(int nrecords);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, UndoRecPtr prevurp,
+					  Buffer buffer,
+					  UndoPersistence upersistence);
+extern void AtAbort_ResetUndoBuffers(void);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..008b02d
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,276 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_RELATION_DETAILS			0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_PAYLOAD					0x04
+#define UREC_INFO_TRANSACTION				0x08
+
+/*
+ * Additional information about a relation to which this record pertains,
+ * namely the fork number.  If the fork number is MAIN_FORKNUM, this structure
+ * can (and should) be omitted.
+ */
+typedef struct UndoRecordRelationDetails
+{
+	ForkNumber	urec_fork;		/* fork number */
+} UndoRecordRelationDetails;
+
+#define SizeOfUndoRecordRelationDetails \
+	(offsetof(UndoRecordRelationDetails, urec_fork) + sizeof(uint8))
+
+/*
+ * Identifying information for a block to which this record pertains, and
+ * a pointer to the previous record for the same block.
+ */
+typedef struct UndoRecordBlock
+{
+	UndoRecPtr	urec_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Identifying information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUrecNext (sizeof(UndoRecPtr))
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + SizeOfUrecNext)
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoDecodeStage
+{
+	UNDO_DECODE_STAGE_HEADER,	/* We have not yet decoded even the record
+								 * header; we need to do that next. */
+	UNDO_DECODE_STAGE_RELATION_DETAILS, /* The next thing to be decoded is the
+										 * relation details, if present. */
+	UNDO_DECODE_STAGE_BLOCK,	/* The next thing to be decoded is the block
+								 * details, if present. */
+	UNDO_DECODE_STAGE_TRANSACTION,	/* The next thing to be decoded is the
+									 * transaction details, if present. */
+	UNDO_DECODE_STAGE_PAYLOAD,	/* The next thing to be decoded is the payload
+								 * details, if present */
+	UNDO_DECODE_STAGE_PAYLOAD_DATA, /* The next thing to be decoded is the
+									 * payload data */
+	UNDO_DECODE_STAGE_TUPLE_DATA,	/* The next thing to be decoded is the
+									 * tuple data */
+	UNDO_DECODE_STAGE_DONE		/* Decoding is complete */
+} UndoDecodeStage;
+
+/*
+ * Undo record context for reading undo record from the buffers.  This will
+ * holds intermediate state of undo record read so far.
+ */
+typedef struct UnpackUndoContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordRelationDetails urec_rd;	/* Relation header */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	int			already_read;	/* Number of bytes read so far */
+	int			partial_read;	/* Number of partial byte read */
+	UndoDecodeStage stage;		/* Decoding stage */
+} UnpackUndoContext;
+
+typedef enum UndoInsertStage
+{
+	UNDO_INSERT_STAGE_HEADER,	/* We have not yet inserted even the record
+								 * header; we need to do that next. */
+	UNDO_INSERT_STAGE_RELATION_DETAILS, /* The next thing to be inserted is
+										 * the relation details, if present. */
+	UNDO_INSERT_STAGE_BLOCK,	/* The next thing to be inserted is the block
+								 * details, if present. */
+	UNDO_INSERT_STAGE_TRANSACTION,	/* The next thing to be inserted is the
+									 * transaction details, if present. */
+	UNDO_INSERT_STAGE_PAYLOAD,	/* The next thing to be inserted is the
+								 * payload details, if present */
+	UNDO_INSERT_STAGE_PAYLOAD_DATA, /* The next thing to be inserted is the
+									 * payload data */
+	UNDO_INSERT_STAGE_TUPLE_DATA,	/* The next thing to be inserted is the
+									 * tuple data */
+	UNDO_INSERT_STAGE_UNDO_LENGTH,	/* The next thing to be inserted is the
+									 * undo length. */
+	UNDO_INSERT_STAGE_DONE		/* inserting is complete */
+} UndoInsertStage;
+
+/*
+ * Undo record context for inserting undo record to the buffers.  This will
+ * holds intermediate state of undo record written so far.
+ */
+typedef struct InsertUndoContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordRelationDetails urec_rd;	/* Relation header */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_written;	/* Number of bytes written so far */
+	int			partial_write;	/* Number of partial bytes write */
+	UndoInsertStage stage;		/* inserting stage */
+} InsertUndoContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UnpackUndoContext *ucontext);
+extern void UnpackUndoData(UnpackUndoContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UnpackUndoContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(InsertUndoContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(InsertUndoContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(InsertUndoContext *ucontext,
+					  int starting_byte);
+
+#endif							/* UNDORECORD_H */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index d787f92..61df235 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -15,6 +15,7 @@
 #define XACT_H
 
 #include "access/transam.h"
+#include "access/undolog.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
-- 
1.8.3.1

#14Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#13)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Apr 25, 2019 at 7:15 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

+static UndoBuffers def_buffers[MAX_UNDO_BUFFERS];
+static int buffer_idx;

This is a file-global variable with a very generic name that is very
similar to a local variable name used by multiple functions within the
file (bufidx) and no comments. Ugh.

Comment added.

The variable name is still bad, and the comment isn't very helpful
either. First, you can't tell by looking at the name that it has
anything to do with the undo_buffers variable, because undo_buffers
and buffer_idx are not obviously related names. Second, it's not an
index; it's a count. A count tells you how many of something you
have; an index tells you which one of those you are presently thinking
about. Third, the undo_buffer array is itself poorly named, because
it's not an array of all the undo buffers in the world or anything
like that, but rather an array of undo buffers for some particular
purpose. "static UndoBuffers *undo_buffer" is about as helpful as
"int integer" and I hope I don't need to explain why that isn't
usually a good thing to write. Maybe prepared_undo_buffer for the
array and nprepared_undo_buffer for the count, or something like that.

I think the simple solution will be that inside UndoRecordIsValid
function we can directly check UndoLogIsDiscarded if oldest_data is
not yet initialized, I think we don't need to release the discard lock
for that. So I have changed it like that.

Can we instead eliminate the special case? It seems like the if
(log->oldest_data == InvalidUndoRecPtr) case will be taken very
rarely, so if it's buggy, we might not notice.

Right, from uur_prevxid we would know that for which xid's undo we are
looking for but without having uur_xid in undo record it self how we
would know which undo record is inserted by the xid we are looking
for? Because in zheap while following the undo chain and if slot got
switch, then there is possibility (because of slot reuse) that we
might get some other transaction's undo record for the same zheap
tuple, but we want to traverse back as we want to find the record
inserted by uur_prevxid. So we need uur_xid as well to tell who is
inserter of this undo record?

It seems desirable to me to make this the caller's problem. When we
are processing undo a transaction at a time, we'll know the XID
because it will be available from the transaction header. If a system
like zheap maintains a pointer to an undo record someplace in the
middle of a transaction, it can also store the XID if it needs it.
The thing is, the zheap code almost works that way already.
Transaction slots within a page store both the undo record pointer and
the XID. The only case where zheap doesn't store the undo record
pointer and the XID is when a slot switch occurs, but that could be
changed.

If we moved urec_xid into UndoRecordTransaction, we'd save 4 bytes per
undo record across the board. When zheap emits undo records for
updates or deletes, they would need store an UndoRecPtr (8 bytes) +
FullTransactionId (8 bytes) in the payload unless the previous change
to that TID is all-visible or the previous change to that TID was made
by the same transaction. Also, zheap would no longer need to store
the slot number in the payload in any case, because this would
substitute for that (and permit more efficient lookups, to boot). So
the overall impact on zheap update and delete records would be
somewhere between -4 bytes (when we save the space used by XID and
incur no other cost) and +12 bytes (when we lose the XID but gain the
UndoRecPtr + FullTransactionId).

That worst case could be further optimized. For example, instead of
storing a FullTransactionId, zheap could store the difference between
the XID to which the current record pertains (which in this model the
caller is required to know) and the XID of whoever last modified the
tuple. That difference certainly can't exceed 4 billion (or even 2
billion) so 4 bytes is enough. That reduces the worst-case difference
to +8 bytes. Probably zheap could use payloads with some kind of
variable-length encoding and squeeze out even more in common cases,
but I'm not sure that's necessary or worth the complexity.

Let's also give uur_blkprev its own UREC_INFO_* constant and omit it
when this is the first time we're touching this block in this
transaction and thus the value is InvalidUndoRecPtr. In the
pretty-common case where a transaction updates one tuple on the page
and never comes back, this - together with the optimization in the
previous paragraph - will cause zheap to come out even on undo,
because it'll save 4 bytes by omitting urec_xid and 8 bytes by
omitting uur_blkrev, and it'll lose 8 bytes storing an UndoRecPtr in
the payload and 4 bytes storing an XID-difference.

Even with those changes, zheap's update and delete could still come
out a little behind on undo volume if hitting many tuples on the same
page, because for every tuple they hit after the first, we'll still
need the UndoRecPtr for the previous change to that page (uur_blkprev)
and we'll also have the UndoRecPtr extracted from the tuple's previous
slot, store in the payload. So we'll end up +8 bytes in this case. I
think that's acceptable, because it often won't happen, it's hardly
catastrophic if it does, and saving 4 bytes on every insert, and on
every update or delete where the old undo is already discarded is
pretty sweet.

Yeah so we can directly jump to the header which is not yet completely
read but if any of the header is partially read then we need to
maintain some kind of partial read variable otherwise from 'already
read' we wouldn't be able to know how many bytes of the header got
read in last call unless we calculate that from uur_info or maintain
the partial_read in context like I have done in the new version.

Right, we need to know the bytes already read for the next header.

Note:
- I think the ucontext->stage are same for the insert and DECODE can
we just declare only
one enum and give some generic name e.g. UNDO_PROCESS_STAGE_HEADER ?

I agree. Maybe UNDO_PACK_STAGE_WHATEVER or, more briefly, UNDO_PACK_WHATEVER.

- In SkipInsertingUndoData also I have to go through all the stages so
that if we find some valid
block then stage is right for inserting the partial record? Do you
think I could have avoided that?

Hmm, I didn't foresee that, but no, I don't think you can avoid that.
That problem wouldn't occur before we added the stage stuff, since
we'd just go through all the stages every time and each one would know
its own size and do nothing if that number of bytes had already been
passed, but with this design there seems to be no way around it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#15Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#1)
Re: POC: Cleaning up orphaned files using undo logs

Replying to myself to resend to the list, since my previous attempt
seems to have been eaten by a grue.

On Tue, Apr 30, 2019 at 11:14 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 30, 2019 at 2:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Like previous version these patch set also applies on:
https://github.com/EnterpriseDB/zheap/tree/undo
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

Some more review of 0003:

There is a whitespace-only hunk in xact.c.

It would be nice if AtAbort_ResetUndoBuffers didn't need to exist at
all. Then, this patch would make no changes whatsoever to xact.c.
We'd still need such changes in other patches, because the whole idea
of undo is tightly bound up with the concept of transactions. Yet,
this particular patch wouldn't touch that file, and that would be
nice. In general, the reason why we need AtCommit/AtAbort/AtEOXact
callbacks is to adjust the values of global variables (or the data
structures to which they point) at commit or abort time. And that is
also the case here. The thing is, I'm not sure why we need these
particular global variables. Is there some way that we can get by
without them? The obvious thing to do seems to be to invent yet
another context object, allocated via a new function, which can then
be passed to PrepareUndoInsert, InsertPreparedUndo,
UndoLogBuffersSetLSN, UnlockReleaseUndoBuffers, etc. This would
obsolete UndoSetPrepareSize, since you'd instead pass the size to the
context allocator function.

UndoRecordUpdateTransInfo should declare a local variable
XactUndoRecordInfo *something = &xact_urec_info[idx] and use that
variable wherever possible.

It should also probably use while (1) { ... } rather than do { ... }
while (true).

In UndoBufferGetSlot you could replace 'break;' with 'return i;' and
then more than half the function would need one less level of
indentation. This function should also declare PreparedUndoBuffer
*something and set that variable equal to &prepared_undo_buffers[i] at
the top of the loop and again after the loop, and that variable should
then be used whenever possible.

UndoRecordRelationDetails seems to need renaming now that it's down to
a single member.

The comment for UndoRecordBlock needs updating, as it still refers to blkprev.

The comment for UndoRecordBlockPrev refers to "Identifying
information" but it's not identifying anything. I think you could
just delete "Identifying information for" from this sentence and not
lose anything. And I think several of the nearby comments that refer
to "Identifying information" could more simply and more correctly just
refer to "Information".

I don't think that SizeOfUrecNext needs to exist.

xact.h should not add an include for undolog.h. There are no other
changes to this header, so presumably the header does not depend on
anything in undolog.h. If .c files that include xact.h need
undolog.h, then the header should be included in those files, not in
the header itself. That way, we avoid making partial recompiles more
painful than necessary.

UndoGetPrevUndoRecptr looks like a strange interface. Why not just
arrange not to call the function in the first place if prevurp is
valid?

Every use of palloc0 in this code should be audited to check whether
it is really necessary to zero the memory before use. If it will be
initialized before it's used for anything anyway, it doesn't need to
be pre-zeroed.

FinishUnpackUndo looks nice! But it is missing a blank line in one
place, and 'if it presents' should be 'if it is present' in a whole
bunch of places.

BeginInsertUndo also looks to be missing a few blank lines.

Still need to do some cleanup of the UnpackedUndoRecord, e.g. unifying
uur_xid and uur_xidepoch into uur_fxid.

InsertUndoData ends in an unnecessary return, as does SkipInsertingUndoData.

I think the argument to SkipInsertingUndoData should be the number of
bytes to skip, rather than the starting byte within the block.

Function header comment formatting is not consistent. Compare
FinishUnpackUndo (function name recapitulated on first line of
comment) to ReadUndoBytes (not recapitulated) to UnpackUndoData
(entire header comment jammed into one paragraph with function name at
start). I prefer the style where the function name is not restated,
but YMMV. Anyway, it has to be consistent.

UndoGetPrevRecordLen should not declare char *page instead of Page
page, I think.

UndoRecInfo looks a bit silly, I think. Isn't index just the index of
this entry in the array? You can always figure that out by ptr -
array_base. Instead of having UndoRecPtr urp in this structure, how
about adding that to UnpackedUndoRecord? When inserting, caller
leaves it unset and InsertPreparedUndo sets it; when retrieving,
UndoFetchRecord or UndoRecordBulkFetch sets it. With those two
changes, I think you can get rid of UndoRecInfo entirely and just
return an array of UnpackedUndoRecords. This might also eliminate the
need for an 'urp' member in PreparedUndoSpace.

I'd probably use UREC_INFO_BLKPREV rather than UREC_INFO_BLOCKPREV for
consistency.

Similarly UndoFetchRecord and UndoRecordBulkFetch seems a bit
inconsistent. Perhaps UndoBulkFetchRecord.

In general, I find the code for updating transaction headers to be
really hard to understand. I'm not sure exactly what can be done
about that. Like, why is UndoRecordPrepareTransInfo unpacking undo?
Why does it take two undo record pointers as arguments and how are
they different?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#16Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#15)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 1, 2019 at 6:02 AM Robert Haas <robertmhaas@gmail.com> wrote:

Replying to myself to resend to the list, since my previous attempt
seems to have been eaten by a grue.

On Tue, Apr 30, 2019 at 11:14 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 30, 2019 at 2:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Like previous version these patch set also applies on:
https://github.com/EnterpriseDB/zheap/tree/undo
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

Some more review of 0003:

Another suggestion:

+/*
+ * Insert a previously-prepared undo records.  This will write the actual undo
+ * record into the buffers already pinned and locked in PreparedUndoInsert,
+ * and mark them dirty.  This step should be performed after entering a
+ * criticalsection; it should never fail.
+ */
+void
+InsertPreparedUndo(void)
+{
..
..
+
+ /* Advance the insert pointer past this record. */
+ UndoLogAdvance(urp, size);
+ }
..
}

UndoLogAdvance internally takes LWLock and we don't recommend doing
that in the critical section which will happen as this function is
supposed to be invoked in the critical section as mentioned in
comments.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#17Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#1)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Thomas told me offlist that this email of mine didn't hit
pgsql-hackers, so trying it again by resending.

On Mon, Apr 29, 2019 at 3:51 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Apr 19, 2019 at 3:46 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Tue, Mar 12, 2019 at 6:51 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Currently, undo branch[1] contain an older version of the (undo
interface + some fixup). Now, I have merged the latest changes from
the zheap branch[2] to the undo branch[1]
which can be applied on top of the undo storage commit[3]. For
merging those changes, I need to add some changes to the undo log
storage patch as well for handling the multi log transaction. So I
have attached two patches, 1) improvement to undo log storage 2)
complete undo interface patch which include 0006+0007 from undo
branch[1] + new changes on the zheap branch.

[1] https://github.com/EnterpriseDB/zheap/tree/undo
[2] https://github.com/EnterpriseDB/zheap
[3] https://github.com/EnterpriseDB/zheap/tree/undo
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

[]

Dilip has posted the patch for "undo record interface", next in series
is a patch that handles transaction rollbacks (the machinery to
perform undo actions) and background workers to manage undo.

Transaction Rollbacks
----------------------------------
We always perform rollback actions after cleaning up the current
(sub)transaction. This will ensure that we perform the actions
immediately after an error (and release the locks) rather than when
the user issues Rollback command at some later point of time. We are
releasing the locks after the undo actions are applied. The reason to
delay lock release is that if we release locks before applying undo
actions, then the parallel session can acquire the lock before us
which can lead to deadlock.

We promote the error to FATAL error if it occurred while applying undo
for a subtransaction. The reason we can't proceed without applying
subtransaction's undo is that the modifications made in that case must
not be visible even if the main transaction commits. Normally, the
backends that receive the request to perform Rollback (To Savepoint)
applies the undo actions, but there are cases where it is preferable
to push the requests to background workers. The main reasons to push
the requests to background workers are (a) The request for a very
large rollback, this will allow us to return control to users quickly.
There is a guc rollback_overflow_size which indicates that rollbacks
greater than the configured size are performed lazily by background
workers. (b) While applying the undo actions, if there is an error, we
push such a request to background workers.

Undo Requests and Undo workers
--------------------------------------------------
To improve the efficiency of the rollbacks, we create three queues and
a hash table for the rollback requests. A Xid based priority queue
which will allow us to process the requests of older transactions and
help us to move oldesdXidHavingUndo (this is a xid-horizon below which
all the transactions are visible) forward. A size-based queue which
will help us to perform the rollbacks of larger aborts in a timely
fashion so that we don't get stuck while processing them during
discard of the logs. An error queue to hold the requests for
transactions that failed to apply its undo. The rollback hash table
is used to avoid duplicate undo requests by backends and discard
worker.

Undo launcher is responsible for launching the workers iff there is
some work available in one of the work queues and there are more
workers available. The worker is launched to handle requests for a
particular database. Each undo worker then start reading from one of
the queues the requests for that particular database. A worker would
peek into each queue for the requests from a particular database if it
needs to switch a database in less than undo_worker_quantum ms (10s as
default) after starting. Also, if there is no work, it lingers for
UNDO_WORKER_LINGER_MS (10s as default). This avoids restarting the
workers too frequently.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.
It also registers the request for aborted transactions in the work
queues. It iterates through all the active logs one-by-one and tries
to discard the transactions that are old enough to matter.

The details of how all of this works are described in
src/backend/access/undo/README.UndoProcessing. The main idea to keep
a readme is to allow reviewers to understand this patch, later we can
decide parts of it to move to comments in code and others to main
README of undo.

Question: Currently, TwoPhaseFileHeader stores just TransactionId, so
for the systems (like zheap) that support FullTransactionId, the
two-phase transaction will be tricky to support as we need
FullTransactionId during rollbacks. Is it a good idea to store
FullTransactionId in TwoPhaseFileHeader?

Credits:
--------------
Designed by: Andres Freund, Amit Kapila, Robert Haas, and Thomas Munro
Author: Amit Kapila, Dilip Kumar, Kuntal Ghosh, and Thomas Munro

This patch is based on the latest Dilip's patch for undo record
interface. The branch can be accessed at
https://github.com/EnterpriseDB/zheap/tree/undoprocessing

Inputs on design/code are welcome.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

undoprocessing_1.patchapplication/octet-stream; name=undoprocessing_1.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..2ff2b86 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest xid with epoch having undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 checkpoint->oldestXidWithEpochHavingUndo,
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..6238240 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index ecc01f7..00dcd59 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -927,6 +927,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations. We need to store these information in file as user might
+	 * rollback the prepared transaction after recovery and for that we need
+	 * it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1011,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1043,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1484,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	int			i;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1524,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1612,6 +1635,83 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	/*
+	 * Perform undo actions, if there are undologs for this transaction. We
+	 * need to perform undo actions while we are still in transaction. Never
+	 * push rollbacks of temp tables to undo worker.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		volatile	UndoRequestInfo urinfo;
+		uint32	epoch;
+		FullTransactionId full_xid;
+
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here.  This assumption is not good, but
+		 * we can fix it by storing full transaction id in TwoPhaseFileHeader.
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		if (end_urec_ptr[i] != InvalidUndoRecPtr && !isCommit)
+		{
+			uint32		save_holdoff;
+
+			save_holdoff = InterruptHoldoffCount;
+			PG_TRY();
+			{
+				bool		result = false;
+
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = MyDatabaseId;
+				urinfo.full_xid = full_xid;
+
+				if (i != UNDO_TEMP)
+					result = RegisterRollbackReq(end_urec_ptr[i],
+												 start_urec_ptr[i],
+												 hdr->database,
+												 full_xid);
+				if (!result)
+					execute_undo_actions(full_xid, end_urec_ptr[i],
+										 start_urec_ptr[i], true);
+			}
+			PG_CATCH();
+			{
+				if (i == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then
+				 * remove the entry from the hash table and continue to
+				 * process the remaining undo requests if any.  This request
+				 * will be later processed by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTRemoveEntry(urinfo.full_xid, urinfo.start_urec_ptr);
+
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+			}
+			PG_END_TRY();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 8c3d84f..9e90b51 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -300,6 +300,30 @@ AdvanceNextFullTransactionIdPastXid(TransactionId xid)
 }
 
 /*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
+
+/*
  * Advance the cluster-wide value for the oldest valid clog entry.
  *
  * We must acquire CLogTruncationLock to advance the oldestClogXid. It's not
@@ -333,10 +357,23 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUndo because this is a oldest xid whose undo is
+	 * not yet discarded so this is still a valid xid in system. The
+	 * oldestXidHavingUndo will be only valid for zheap storage engine, so it
+	 * won't impact any other storage engine.
+	 */
+	oldestXidHavingUndo = GetXidFromEpochXid(
+											 pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo));
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -403,6 +440,13 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	curXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
 	LWLockRelease(XidGenLock);
 
+	/*
+	 * Fixme - The messages in below code need some adjustment for zheap. They
+	 * should reflect that the system needs to discard the undo.  We can add
+	 * it once we have a pluggable storage API which might provide us some way
+	 * to distinguish among differnt storage engines.
+	 */
+
 	/* Log the info */
 	ereport(DEBUG1,
 			(errmsg("transaction ID wrap limit is %u, limited by database with OID %u",
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 47a8f0d..5996997 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,11 +26,13 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "access/undoinsert.h"
+#include "access/undorequest.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_enum.h"
 #include "catalog/storage.h"
@@ -129,7 +131,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -154,6 +157,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -164,7 +168,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -192,6 +197,14 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+	bool		subXactLock;	/* has lock created for subtransaction? */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -363,9 +376,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -724,9 +737,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -906,15 +924,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -998,6 +1016,22 @@ IsInParallelMode(void)
 }
 
 /*
+ * SetCurrentUndoLocation
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
+
+/*
  *	CommandCounterIncrement
  */
 void
@@ -1886,6 +1920,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1969,6 +2004,15 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+	s->subXactLock = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2246,6 +2290,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
@@ -2265,6 +2313,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2281,7 +2331,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2434,7 +2484,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2629,7 +2679,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2788,6 +2840,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2853,6 +2907,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2914,9 +2970,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2995,11 +3059,13 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			SetUndoActionsInfo();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3016,7 +3082,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3060,6 +3126,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3073,7 +3157,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3095,7 +3179,9 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			SetUndoActionsInfo();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3115,7 +3201,9 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				SetUndoActionsInfo();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3168,6 +3256,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	SetUndoActionsInfo();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3183,7 +3279,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3199,6 +3299,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3209,8 +3310,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3224,6 +3329,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3235,6 +3341,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_END:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3264,6 +3371,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_ABORT_PENDING:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3275,6 +3383,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_PREPARE:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3286,6 +3395,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_SUBINPROGRESS:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3300,6 +3410,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3312,7 +3423,210 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
+	}
+}
+
+/*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	uint32		save_holdoff;
+	volatile	UndoRequestInfo urinfo;
+	volatile int per_level;
+	bool		error = false;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (s->latest_urec_ptr[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				bool		result = false;
+
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = MyDatabaseId;
+				urinfo.full_xid = GetTopFullTransactionId();
+
+				/*
+				 * If this request is not for a temp table and not aborting
+				 * subtransaction and the request size is greater than some
+				 * threshold then push it to undo-worker through RollbackHT,
+				 * undo-worker will perform the corresponding undo actions
+				 * later.
+				 *
+				 * We can't push the undo actions for temp table to background
+				 * workers as the the temp tables are only accessible in the
+				 * backend that has created them.  We can't postpone applying
+				 * undo actions for subtransactions as the modifications made
+				 * by aborted subtransaction must not be visible even if the
+				 * main transaction commits.  It is not advisable to apply the
+				 * undo actions of a very large transaction as that can lead
+				 * to a delay in retruning the control back to user after
+				 * abort.
+				 */
+				if (per_level != UNDO_TEMP &&
+					!IsSubTransaction())
+					result = RegisterRollbackReq(s->latest_urec_ptr[per_level],
+												 s->start_urec_ptr[per_level],
+												 MyDatabaseId,
+												 urinfo.full_xid);
+				if (!result)
+				{
+					/* for subtransactions, we do partial rollback. */
+					execute_undo_actions(urinfo.full_xid,
+										 s->latest_urec_ptr[per_level],
+										 s->start_urec_ptr[per_level],
+										 !IsSubTransaction());
+				}
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then
+				 * remove the entry from the hash table and continue to
+				 * process the remaining undo requests if any.  This request
+				 * will be later processed by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTRemoveEntry(urinfo.full_xid, urinfo.start_urec_ptr);
+
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				error = true;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	if (error)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
 	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
 }
 
 /*
@@ -3641,6 +3955,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3833,6 +4149,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3949,6 +4267,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4089,6 +4409,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4107,6 +4429,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4165,6 +4499,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4200,8 +4536,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4274,6 +4639,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4392,6 +4759,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4412,6 +4781,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4430,6 +4800,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4481,17 +4867,29 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	SetUndoActionsInfo();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4518,6 +4916,14 @@ AbortOutOfAnyTransaction(void)
 	AtAbort_Memory();
 
 	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	SetUndoActionsInfo();
+
+	/*
 	 * Get out of any transaction or nested transaction
 	 */
 	do
@@ -4537,7 +4943,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4554,6 +4964,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
 				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
 				break;
@@ -4581,6 +5004,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
 				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
@@ -4674,6 +5110,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4713,6 +5151,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4730,6 +5169,15 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+
+	s->subXactLock = false;
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4924,7 +5372,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5352,6 +5801,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5370,6 +5821,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5395,6 +5848,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5994,3 +6449,52 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * SetUndoActionsInfo - set the start and end undo record pointers before
+ * performing the undo actions.
+ */
+void
+SetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 28fdaaf..007911c 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5162,6 +5162,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestXidWithEpochHavingUndo = InvalidTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6613,6 +6614,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 checkPoint.oldestXidWithEpochHavingUndo)));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6629,6 +6633,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestXidWithEpochHavingUndo,
+						checkPoint.oldestXidWithEpochHavingUndo);
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7317,7 +7325,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8713,6 +8727,10 @@ CreateCheckPoint(int flags)
 	if (!shutdown)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
+ 
+	checkPoint.oldestXidWithEpochHavingUndo =
+		pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo);
+
 
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
@@ -9625,6 +9643,9 @@ xlog_redo(XLogReaderState *record)
 
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
+ 
+		pg_atomic_write_u64(&ProcGlobal->oldestXidWithEpochHavingUndo,
+							checkPoint.oldestXidWithEpochHavingUndo);
 
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
@@ -9683,12 +9704,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestXidWithEpochHavingUndo =
+			checkPoint.oldestXidWithEpochHavingUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestXidWithEpochHavingUndo =
+			checkPoint.oldestXidWithEpochHavingUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9727,6 +9753,9 @@ xlog_redo(XLogReaderState *record)
 		/* Handle multixact */
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
+ 
+		pg_atomic_write_u64(&ProcGlobal->oldestXidWithEpochHavingUndo,
+							checkPoint.oldestXidWithEpochHavingUndo);
 
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index f41e8f7..6e4c890 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoinsert.o undolog.o undorecord.o
+OBJS = discardworker.o undoaction.o undoactionxlog.o undodiscard.o undoinsert.o \
+		undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..a0e0f96
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,96 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The request for
+a very large rollback, this will allow us to return control to users quickly.
+There is a guc rollback_overflow_size which indicates that rollbacks greater
+than the configured size are performed lazily by background workers. (b) While
+applying the undo actions, if there is an error, we push such a request to
+background workers.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue which will allow
+us to process the requests of older transactions and help us to move
+oldesdXidHavingUndo (this is a xid-horizon below which all the transactions are
+visible) forward.  A size-based queue which will help us to perform the rollbacks
+of larger aborts in a timely fashion, so that we don't get stuck while processing
+them during discard of the logs.  An error queue to hold the requests for
+transactions that failed to apply its undo.  The rollback hash table is used to
+avoid duplicate undo requests by backends and discard worker.  The table must be
+able to accommodate all active undo requests.  The undo requests must appear in
+both xid and size requests queues or neither.  As of now we, process the requests
+from these queues in a round-robin fashion to give equal priority to all three
+types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just remove the request from the hash table and
+continue to process other requests if any.  The discard worker will find this
+errored transaction at later point of time and again add it to the request
+queues.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..4476769
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* max sleep time between cycles (100 milliseconds) */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static bool got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	/* TODO: This should be configurable. */
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestXidHavingUndo = GetXidFromEpochXid(
+												 pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo));
+
+		/*
+		 * Call the discard routine if there oldestXidHavingUndo is lagging
+		 * behind OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do. Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		ResetLatch(&MyProc->procLatch);
+
+		/*
+		 * Increase the wait_time based on the length of inactivity. If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time. Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds. Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..31ee8e8
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,303 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+#include "access/undodiscard.h"
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block == ruur->uur_block)
+	{
+		/*
+		 * If records are for the same block then maintain their existing
+		 * order by comparing their index in the array.  Because for single
+		 * block we need to maintain the order for applying undo action.
+		 */
+		if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+			return -1;
+		else
+			return 1;
+	}
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else
+		return 1;
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo action.
+ * to_urecptr	- undo record pointer upto which point apply undo action.
+ * nopartial	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	UndoRecPtr	urec_ptr;
+	ForkNumber	prev_fork = InvalidForkNumber;
+	BlockNumber prev_block = InvalidBlockNumber;
+	int			undo_apply_size = maintenance_work_mem * 1024L;
+	TransactionId	xid = XidFromFullTransactionId(full_xid);
+
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	urec_ptr = from_urecptr;
+
+	if (nopartial)
+	{
+		/*
+		 * It is important here to fetch the latest undo record and validate if
+		 * the actions are already executed.  The reason is that it is possible
+		 * that discard worker or backend might try to execute the rollback
+		 * request which is already executed.  For ex., after discard worker
+		 * fetches the record and found that this transaction need to be
+		 * rolledback, backend might concurrently execute the actions and
+		 * remove the request from rollback hash table. The similar problem
+		 * can happen if the discard worker first pushes the request, the undo
+		 * worker processed it and backend tries to process it some later point.
+		 */
+		uur = UndoFetchRecord(to_urecptr, InvalidBlockNumber, InvalidOffsetNumber,
+							  InvalidTransactionId, NULL, NULL);
+
+		/* already processed. */
+		if (uur == NULL)
+			return;
+
+		/*
+		 * We don't need to execute the undo actions if they are already
+		 * executed.
+		 */
+		if (uur->uur_progress != 0)
+			return;
+
+		Assert(xid == uur->uur_xid);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	/*
+	 * Fetch the multiple undo records which can fit into uur_segment; sort
+	 * them in order of reloid and block number then apply them together
+	 * page-wise. Repeat this until we get invalid undo record pointer.
+	 */
+	do
+	{
+		Oid			prev_reloid = InvalidOid;
+		bool		blk_chain_complete;
+		int			i;
+		int			nrecords;
+		int			last_index = 0;
+		int			prefetch_pages = 0;
+
+		/*
+		 * If urec_ptr is not valid means we have complete all undo actions
+		 * for this transaction, otherwise we need to fetch the next batch of
+		 * the undo records.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+
+		/*
+		 * Fetch multiple undo record in bulk.  This will return the array of
+		 * undo record which will holds undo record pointers and the pointers
+		 * to the actual unpacked undo record.   This will also update the
+		 * number of undo records it has copied in the urp_array.  Also, for
+		 * prefetching the target block ahead of applying undo actions it will
+		 * update undo_blkinfo which will contains the information of the data
+		 * blocks for which undo actions are going to applied for this undo
+		 * record batch.
+		 */
+		urp_array = UndoRecordBulkFetch(&urec_ptr, to_urecptr, undo_apply_size,
+										&nrecords, false);
+		if (nrecords == 0)
+			break;
+
+		Assert(TransactionIdEquals(xid, urp_array[0].uur->uur_xid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urp_array, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		if (nopartial && !UndoRecPtrIsValid(urec_ptr))
+			blk_chain_complete = true;
+		else
+			blk_chain_complete = false;
+
+		/*
+		 * Now we have urp_array which is sorted in the block order so
+		 * traverse this array and apply the undo action block by block.
+		 */
+		for (i = last_index; i < nrecords; i++)
+		{
+			UnpackedUndoRecord *uur = urp_array[i].uur;
+
+			/*
+			 * If this undo is not for the same block then apply all undo
+			 * actions for the previous block.
+			 */
+			if (OidIsValid(prev_reloid) &&
+				(prev_reloid != uur->uur_reloid ||
+				 prev_fork != uur->uur_fork ||
+				 prev_block != uur->uur_block))
+			{
+				execute_undo_actions_page(urp_array, last_index, i - 1,
+										  prev_reloid, full_xid, prev_block,
+										  blk_chain_complete);
+				last_index = i;
+
+				/* We have consumed one prefetched page. */
+				if (prefetch_pages > 0)
+					prefetch_pages--;
+			}
+
+			prev_reloid = uur->uur_reloid;
+			prev_fork = uur->uur_fork;
+			prev_block = uur->uur_block;
+		}
+
+		/* Apply the last set of the actions. */
+		execute_undo_actions_page(urp_array, last_index, i - 1,
+								  prev_reloid, full_xid, prev_block,
+								  blk_chain_complete);
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urp_array[i].uur);
+
+		/*
+		 * Free urp array and undo_blkinfo array for the current batch of undo
+		 * records.
+		 */
+		pfree(urp_array);
+	} while (true);
+
+	/*
+	 * Set undo action apply progress as completed in the transaction header
+	 * if this is a main transaction.
+	 */
+	if (nopartial)
+	{
+		/*
+		 * Prepare and update the progress of the undo action apply in the
+		 * transaction header.
+		 */
+		PrepareUpdateUndoActionProgress(NULL, to_urecptr, 1);
+
+		START_CRIT_SECTION();
+
+		/* Update the progress in the transaction header. */
+		UndoRecordUpdateTransInfo(0);
+
+		/* WAL log the undo apply progress. */
+		{
+			xl_undoapply_progress xlrec;
+
+			xlrec.urec_ptr = to_urecptr;
+			xlrec.progress = 1;
+
+			/*
+				* FIXME : We need to register undo buffers and set LSN for
+				* them that will be required for FPW of the undo buffers.
+				* This is currently not possible as undolog API only allows
+				* register buffer via backends and we can reach here via
+				* backend.
+				*/
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+			/* RegisterUndoLogBuffers(2); */
+			(void) XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+			/* UndoLogBuffersSetLSN(recptr); */
+		}
+
+		END_CRIT_SECTION();
+		UnlockReleaseUndoBuffers();
+
+		/*
+		 * Undo action is applied so delete the hash table entry.
+		 */
+		Assert(TransactionIdIsValid(xid));
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+	}
+}
+
+/*
+ * execute_undo_actions_page - Execute the undo actions for a page
+ *
+ *	urp_array - array of undo records (along with their location) for which undo
+ *				action needs to be applied.
+ *	first_idx - index in the urp_array of the first undo action to be applied
+ *	last_idx  - index in the urp_array of the first undo action to be applied
+ *	reloid	- OID of relation on which undo actions needs to be applied.
+ *	blkno	- block number on which undo actions needs to be applied.
+ *	blk_chain_complete - indicates whether the undo chain for block is
+ *						 complete.
+ *
+ *	returns true, if successfully applied the undo actions, otherwise, false.
+ */
+bool
+execute_undo_actions_page(UndoRecInfo * urp_array, int first_idx, int last_idx,
+						  Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+						  bool blk_chain_complete)
+{
+	/*
+	 * All records passed to us are for the same RMGR, so we just use the
+	 * first record to dispatch.
+	 */
+	Assert(urp_array != NULL);
+
+	return RmgrTable[urp_array[0].uur->uur_rmid].rm_undo(urp_array, first_idx,
+														 last_idx, reloid,
+														 full_xid, blkno,
+														 blk_chain_complete);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..087e737
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoinsert.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+
+	/* Update the progress in the transaction header. */
+	PrepareUpdateUndoActionProgress(record, xlrec->urec_ptr, xlrec->progress);
+	UndoRecordUpdateTransInfo(0);
+	UnlockReleaseUndoBuffers();
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..cf304ba
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,348 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard the undo for the given log
+ *
+ * Search the undo log, get the start record for each transaction until we get
+ * the transaction with xid >= xmin or an invalid xid.  Then call undolog
+ * routine to discard upto that point and update the memory structure for the
+ * log slot.  We set the hibernate flag if we do not have any undo data that
+ * can be discarded, this flag is passed to the discard worker wherein it
+ * determines if system is idle and it should sleep for sometime.
+ *
+ * Return the oldest xid remaining in this undo log (which should be >= xmin,
+ * since we'll discard everything older).  Return InvalidTransactionId if the
+ * undo log is empty.
+ */
+static FullTransactionId
+UndoDiscardOneLog(UndoLogControl *log, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	bool	log_complete = false;
+	TransactionId	undoxid = InvalidTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	uint32	epoch = 0;
+
+	if (UndoRecPtrIsValid(log->oldest_data))
+		undo_recptr = log->oldest_data;
+	else
+		undo_recptr = UndoLogGetFirstValidRecord(log, NULL);
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		bool pending_abort = false;
+
+		next_insert = UndoLogGetNextInsertPtr(log->logno, InvalidTransactionId);
+
+		if (next_insert == undo_recptr)
+		{
+			/*
+			 * The caller of this function must have ensured that there is
+			 * something to discard.
+			 */
+			Assert(undo_recptr != log->oldest_data);
+
+			/* Indicate that we have processed all the log. */
+			log_complete = true;
+		}
+		else
+		{
+			/* Fetch the undo record for given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+
+			if (uur != NULL)
+			{
+				/*
+				 * Add the aborted transaction to the rollback request queues.
+				 *
+				 * If the undo actions for the aborted transaction is already
+				 * applied then continue discarding the undo log, otherwise,
+				 * discard till current point and stop processing this undo
+				 * log.
+				 *
+				 * We can ignore the abort for transactions whose
+				 * corresponding database doesn't exist.
+				 *
+				 * XXX: We've added the transaction-in-progress check
+				 * to avoid xids of in-progress autovacuum.  Note that,
+				 * while calculating xmin, we ignore the vacuum and
+				 * autovacuum xids in DiscardWorkerMain.  But, when a
+				 * backend performs VACUUM, we forcefully clear the
+				 * vacuum flag from MyPgXact in lazy_vacuum_zheap_rel.
+				 * Hence, the problem arises only for autovacuum xids.
+				 * We should fix this behaviour.  Perhaps, discard worker
+				 * should consider vacuum and autovacuum xid to calculate
+				 * the xmin.  But, in that case, a long-running autovacuum
+				 * might block the discard worker for moving ahead.
+				 */
+				if (!TransactionIdDidCommit(uur->uur_xid) &&
+					!TransactionIdIsInProgress(uur->uur_xid) &&
+					TransactionIdPrecedes(uur->uur_xid, xmin) &&
+					uur->uur_progress == 0 &&
+					dbid_exists(uur->uur_dbid))
+				{
+					FullTransactionId full_xid;
+
+					full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+																uur->uur_xid);
+					(void) RegisterRollbackReq(InvalidUndoRecPtr,
+											   undo_recptr,
+											   uur->uur_dbid,
+											   full_xid);
+
+					pending_abort = true;
+				}
+
+				next_urecptr = uur->uur_next;
+				undoxid = uur->uur_xid;
+				epoch = uur->uur_xidepoch;
+
+				UndoRecordRelease(uur);
+				uur = NULL;
+			}
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) the next transaction is not all-visible. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if ((TransactionIdIsValid(undoxid) &&
+			 TransactionIdFollowsOrEquals(undoxid, xmin)) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			log_complete ||
+			UndoRecPtrGetLogNo(next_urecptr) != log->logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If the transaction id is smaller than the xmin, it means this
+			 * must be the last transaction in this undo log, so we need to
+			 * get the last insert point in this undo log and discard till
+			 * that point.
+			 *
+			 * Also, if the transaction has pending abort, stop discarding
+			 * further.
+			 */
+			if (TransactionIdPrecedes(undoxid, xmin) && !pending_abort)
+			{
+				UndoRecPtr	next_insert = InvalidUndoRecPtr;
+
+				/*
+				 * If the more undo has been inserted since last we checked,
+				 * then we can process that as well.
+				 */
+				next_insert = UndoLogGetNextInsertPtr(log->logno, undoxid);
+				if (!UndoRecPtrIsValid(next_insert))
+					continue;
+
+				undo_recptr = next_insert;
+				need_discard = true;
+				epoch = 0;
+				latest_discardxid = undoxid;
+				undoxid = InvalidTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&log->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If no more pending undo logs then set the oldest transaction to
+			 * InvalidTransactionId.
+			 */
+			if (log_complete)
+			{
+				log->oldest_xid = InvalidTransactionId;
+				log->oldest_xidepoch = 0;
+			}
+			else
+			{
+				log->oldest_xid = undoxid;
+				log->oldest_xidepoch = epoch;
+			}
+
+			log->oldest_data = undo_recptr;
+
+			LWLockRelease(&log->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&log->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&log->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = undoxid;
+
+		Assert(uur == NULL);
+
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+
+	return FullTransactionIdFromEpochAndXid(epoch, undoxid);
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo = InvalidFullTransactionId;
+	UndoLogControl *log = NULL;
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((log = UndoLogNext(log)))
+	{
+		FullTransactionId oldest_xid = InvalidFullTransactionId;
+
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&log->mutex, LW_SHARED);
+		if (log->meta.discard == log->meta.unlogged.insert)
+		{
+			LWLockRelease(&log->mutex);
+			continue;
+		}
+		LWLockRelease(&log->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (log->meta.persistence == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin the try
+		 * to discard the undo log.
+		 */
+		if (!TransactionIdIsValid(log->oldest_xid) ||
+			TransactionIdPrecedes(log->oldest_xid, oldestXmin))
+		{
+			/* Process the undo log. */
+			oldest_xid = UndoDiscardOneLog(log, oldestXmin, hibernate);
+		}
+
+		/* If oldestXidHavingUndo is not yet initialized, initialize it. */
+		if (!FullTransactionIdIsValid(oldestXidHavingUndo))
+			oldestXidHavingUndo = oldest_xid;
+		else if (FullTransactionIdIsValid(oldest_xid) &&
+				 FullTransactionIdPrecedes(oldest_xid, oldestXidHavingUndo))
+			oldestXidHavingUndo = oldest_xid;
+	}
+
+	/*
+	 * Update the oldestXidWithEpochHavingUndo in the shared memory.
+	 *
+	 * XXX In future if multiple worker can perform discard then we may need
+	 * to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestXidWithEpochHavingUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * To discard all the logs. Particularly required in single user mode.
+ * At the commit time, discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogControl *log = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	while ((log = UndoLogNext(log)))
+	{
+		/*
+		 * Process the undo log.  No locks are required for discard, since
+		 * this called only in single-user mode. Similarly, no transaction id
+		 * is required here because WAL-logging the xid till where the undo is
+		 * discarded will not be required for single user mode.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogControl *log = UndoLogGet(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (log->meta.persistence == UNDO_TEMP);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
index 69f85e2..aa61372 100644
--- a/src/backend/access/undo/undoinsert.c
+++ b/src/backend/access/undo/undoinsert.c
@@ -173,7 +173,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 static void UndoRecordPrepareTransInfo(UndoRecPtr urecptr,
 						   UndoRecPtr xact_urp,
 						   XLogReaderState *xlog_record);
-static void UndoRecordUpdateTransInfo(int idx);
 static int UndoGetBufferSlot(RelFileNode rnode, BlockNumber blk,
 				  ReadBufferMode rbm,
 				  UndoPersistence persistence, XLogReaderState *xlog_record);
@@ -258,19 +257,18 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
 		return;
 
 	/*
-	 * Acquire the discard lock before accessing the undo record so that
-	 * discard worker doesn't remove the record while we are in process of
+	 * Acquire the discard_update_lock before accessing the undo record so
+	 * that discard worker can't remove the record while we are in process of
 	 * reading it.
 	 */
-	LWLockAcquire(&log->discard_lock, LW_SHARED);
-
-	/*
-	 * The absence of previous transaction's undo indicate that this backend
-	 * is preparing its first undo in which case we have nothing to update.
-	 * UndoRecordIsValid will release the lock if it returns false.
-	 */
-	if (!UndoRecordIsValid(log, xact_urp))
+	LWLockAcquire(&log->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoLogIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&log->discard_update_lock);
 		return;
+	}
 
 	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
 	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
@@ -305,7 +303,63 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
 	xact_urec_info[xact_urec_info_idx].urecptr = xact_urp;
 	xact_urec_info_idx++;
 
-	LWLockRelease(&log->discard_lock);
+	LWLockRelease(&log->discard_update_lock);
+}
+
+/*
+ * Update the progress of the undo record in the transaction header.
+ */
+void
+PrepareUpdateUndoActionProgress(XLogReaderState *xlog_record,
+								UndoRecPtr urecptr, int progress)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber	cur_blk;
+	RelFileNode	rnode;
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urecptr);
+	UndoLogControl *log;
+	Page		page;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	UnpackUndoContext ucontext = {0};
+
+	log = UndoLogGet(logno, false);
+
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (true)
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk,
+								   RBM_NORMAL,
+								   log->meta.persistence,
+								   xlog_record);
+
+		xact_urec_info[xact_urec_info_idx].idx_undo_buffers[index++] = bufidx;
+		buffer = undo_buffer[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_DECODE_STAGE_TRANSACTION)
+			break;
+
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+	FinishUnpackUndo(&ucontext, &xact_urec_info[xact_urec_info_idx].uur);
+
+	xact_urec_info[xact_urec_info_idx].urecptr = urecptr;
+	xact_urec_info[xact_urec_info_idx].uur.uur_progress = progress;
+	xact_urec_info_idx++;
 }
 
 
@@ -315,7 +369,7 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
  * UndoRecordPrepareTransInfo.  This must be called under the critical section.
  * This will just overwrite the undo header not the data.
  */
-static void
+void
 UndoRecordUpdateTransInfo(int idx)
 {
 	Page		page = NULL;
@@ -326,6 +380,12 @@ UndoRecordUpdateTransInfo(int idx)
 	urec_ptr = xact_urec_info[idx].urecptr;
 
 	/*
+	 * We've pinned the undo buffers in UndoRecordPrepareTransInfo, so
+	 * it shouldn't be discarded.
+	 */
+	Assert(!UndoLogIsDiscarded(urec_ptr));
+
+	/*
 	 * Update the next transactions start urecptr in the transaction header.
 	 */
 	starting_byte = UndoRecPtrGetPageOffset(urec_ptr);
@@ -731,7 +791,7 @@ PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,
  * criticalsection; it should never fail.
  */
 void
-InsertPreparedUndo(void)
+InsertPreparedUndo(UndoPersistence upersistence)
 {
 	Page		page = NULL;
 	int			starting_byte;
@@ -825,6 +885,12 @@ InsertPreparedUndo(void)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(urp, upersistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvance(urp, size);
 	}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 028b282..1b58f7d 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -185,6 +185,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&shared->logs[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&shared->logs[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 			LWLockInitialize(&shared->logs[i].rewind_lock,
 							 LWTRANCHE_REWIND);
 		}
@@ -1230,7 +1232,8 @@ UndoLogGetFirstValidRecord(UndoLogControl *log, bool *full)
 		result = InvalidUndoRecPtr;
 	else
 		result = MakeUndoRecPtr(log->logno, log->meta.discard);
-	*full = log->meta.status == UNDO_LOG_STATUS_FULL;
+	if (full)
+		*full = log->meta.status == UNDO_LOG_STATUS_FULL;
 	LWLockRelease(&log->mutex);
 
 	return result;
@@ -1778,6 +1781,8 @@ get_undo_log(UndoLogNumber logno, bool locked)
  * If the calling backend is currently attached to the undo log, that is not
  * possible, because logs can only reach UNDO_LOG_STATUS_DISCARDED after first
  * reaching UNDO_LOG_STATUS_FULL, and that only happens while detaching.
+ *
+ * TODO: analyze missing ok cases.
  */
 UndoLogControl *
 UndoLogGet(UndoLogNumber logno, bool missing_ok)
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..5d27882
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1491 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the other worker found the same request in other
+ * queue, it can just ignore the request (and remove it from that queue) if
+ * the request is not found in the hash table or is marked as in-progress.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/discardworker.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+
+#define ROLLBACK_REQUEST_QUEUE_SIZE 1024
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+}			UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdEquals(xidQueueElem1->full_xid,
+									 xidQueueElem2->full_xid))
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size == sizeQueueElem2->request_size)
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at == errQueueElem2->err_occurred_at)
+		return 0;
+	return -1;
+}
+
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoXidQueue));
+}
+
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoSizeQueue));
+}
+
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoErrorQueue));
+}
+
+static int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * ROLLBACK_REQUEST_QUEUE_SIZE) + ROLLBACK_REQUEST_QUEUE_SIZE +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static inline int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid, urinfo->request_size,
+					 urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo * urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns the hash key corresponding to the nth element of the specified
+ * queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey * hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+		{
+			cur_undo_queue++;
+			return false;
+		}
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+static uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogControl *log = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoPersistence persistence;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+	Assert(!TransactionIdIsInProgress(XidFromFullTransactionId(full_xid)));
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding the current undo
+		 * pointer
+		 */
+		if ((log == NULL) || (UndoRecPtrGetLogNo(urecptr) != log->logno))
+			log = UndoLogGet(UndoRecPtrGetLogNo(urecptr), false);
+		Assert(log != NULL);
+		persistence = log->meta.persistence;
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * If the corresponding undo record got rolled back and rewound, we
+		 * return from here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+		next_urecptr = uur->uur_next;
+
+		/*
+		 * Case 1: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			/*
+			 * While fetching the next insert location if the new transaction
+			 * has already started in this log then lets re-fetch the undo
+			 * record.
+			 */
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			if (!UndoRecPtrIsValid(next_insert))
+			{
+				UndoRecordRelease(uur);
+				uur = NULL;
+				continue;
+			}
+
+			/*
+			 * If next_insert location points to the starting location of a
+			 * new page, we should subtract the page header size from the
+			 * insert location.
+			 */
+			if (UndoRecPtrGetPageOffset(next_insert) == UndoLogBlockHeaderSize)
+				next_insert -= UndoLogBlockHeaderSize;
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+
+		/*
+		 * Case 2: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == log->logno)
+		{
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidUndoRecPtr,
+									  InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: If transaction is overflowed to a different undolog and
+		 * it's already discarded. It means that the undo actions for this
+		 * transaction which are in the next log has already been executed.
+		 */
+		if (UndoLogIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+
+	return sz;
+}
+
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
+	{
+		if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE ||
+			GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE))
+		{
+			Assert(GetSizeQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(ROLLBACK_REQUEST_QUEUE_SIZE)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Size Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as not in_progress so that undo
+	 * launcher or other undo worker don't remove the entry from queues.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->in_progress = false;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid matches
+ * current dbid and remove it from the queue before returning the same to
+ * caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter. If we've not found any work for
+ * current database, but there are work for some other database, we set this
+ * parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo * urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database. If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->in_progress = true;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || rh->in_progress)
+					continue;
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->in_progress = true;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself (a) if the entry is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues, (b) if the entry is present, but the size is small enough that
+ * backend can execute by itself and undo worker hasn't started processing it
+ * yet.
+ */
+bool
+RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	bool		request_registered = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * There must be space to accommodate the new request.  See
+	 * UndoRollbackHashTableSize.
+	 */
+	Assert(!RollbackHTIsFull());
+
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr, full_xid);
+
+	/* The transaction got rolled back and rewound. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	/*
+	 * Backends always register the rollback request in the rollback hash
+	 * table irrespective of whether we push it to undo worker.  This ensures
+	 * that discard worker won't try to process the request on which backend
+	 * is working.  OTOH, discard worker won't add an entry to the hash table
+	 * unless it can push the request to undo worker.  This is because
+	 * otherwise backends might not process the request by themselves even
+	 * though no undo worker is going to process such a request.
+	 */
+	if (can_push ||
+		(!can_push && !IsDiscardProcess()))
+	{
+		RollbackHashKey hkey;
+
+		hkey.full_xid = full_xid;
+		hkey.start_urec_ptr = start_urec_ptr;
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+											   HASH_ENTER_NULL, &found);
+		if (!rh)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+
+		/* We shouldn't try to add the same rollback request again. */
+		if (!found)
+		{
+			rh->start_urec_ptr = start_urec_ptr;
+			rh->end_urec_ptr = end_urec_ptr;
+			rh->dbid = dbid;
+			rh->full_xid = full_xid;
+			rh->in_progress = false;
+
+			if (can_push)
+			{
+				UndoRequestInfo urinfo;
+
+				ResetUndoRequestInfo(&urinfo);
+
+				urinfo.full_xid = rh->full_xid;
+				urinfo.start_urec_ptr = rh->start_urec_ptr;
+				urinfo.end_urec_ptr = rh->end_urec_ptr;
+				urinfo.dbid = rh->dbid;
+				urinfo.request_size = req_size;
+
+				InsertRequestIntoUndoQueues(&urinfo);
+
+				/*
+				 * Indicates that the request will be processed by undo
+				 * worker.
+				 */
+				request_registered = true;
+				pushed = true;
+			}
+			else
+			{
+				/* Indicates that the request can be processed by backend. */
+				request_registered = false;
+			}
+		}
+		else if (!rh->in_progress && !can_push)
+		{
+			/*
+			 * Indicates that the request can be processed by backend. This is
+			 * the case where discard worker would have pushed the request of
+			 * smaller size which backend itself can process. Mark the request
+			 * as in-progress, so that discard worker doesn't try to process
+			 * it.
+			 */
+			rh->in_progress = true;
+			request_registered = false;
+		}
+		else
+		{
+			/* Indicates that the request will be processed by undo worker. */
+			request_registered = true;
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
+	return request_registered;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * To check if the rollback requests in the hash table are all
+ * completed or not. This is required because we don't not want to
+ * expose RollbackHT in xact.c, where it is required to ensure
+ * that we push the resuests only when there is some space in
+ * the hash-table.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
+
+/*
+ * Remove all the entries for the given dbid. This is required in cases when
+ * the database is dropped and there were rollback requests pushed to the
+ * hash-table.
+ */
+void
+RollbackHTCleanup(Oid dbid)
+{
+	RollbackHashEntry *rh;
+	HASH_SEQ_STATUS status;
+
+	/* Fetch the rollback requests */
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	Assert(hash_get_num_entries(RollbackHT) <= UndoRollbackHashTableSize());
+	hash_seq_init(&status, RollbackHT);
+	while (RollbackHT != NULL &&
+		   (rh = (RollbackHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (rh->dbid == dbid)
+		{
+			RollbackHashKey hkey;
+
+			hkey.full_xid = rh->full_xid;
+			hkey.start_urec_ptr = rh->start_urec_ptr;
+
+			hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+}
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..ce1956d
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,788 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 5;
+
+/*
+ * If, a worker would need to switch databases less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.
+ */
+#define UNDO_WORKER_LINGER_MS 10000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+}			UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+}			UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker * worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo * urinfo);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached worker for a given db id. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo * urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		CommitTransactionCommand();
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on logical replication worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	/* Should be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.
+		 */
+		if (InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTRemoveEntry(urinfo->full_xid, urinfo->start_urec_ptr);
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+	}
+	PG_END_TRY();
+	CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	for (;;)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	for (;;)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7855954..4ba173a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14370,6 +14370,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..6a41f2b 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -163,6 +194,79 @@ binaryheap_first(binaryheap *heap)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap. The caller must ensure that this routine is not used on an empty
+ * heap and is not called with n greater than or equal to the heap size. Always
+ * O(1).
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap. O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer
+ * to it in O(1) without preserving the heap property. This is a
+ * convenience to remove elements quickly. To obtain a valid heap,
+ * one must call binaryheap_build() afterwards. The caller must ensure
+ * that this routine is not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * binaryheap_remove_first
  *
  * Removes the first (root, topmost) node in the heap and returns a
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f5db5a8..0550d2c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 6b4e8ec..72fa92c 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3595,6 +3595,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 067487f..cd4c26c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		disable_undo_launcher;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -981,6 +985,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (!disable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 905acf2..4658113 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -159,6 +159,10 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
 			break;
+		case RM_UNDOACTION_ID:
+			/* Logical decoding is not yet implemented for undoactions. */
+			Assert(0);
+			break;
 		case RM_NEXT_ID:
 			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
 	}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 7a4f452..d2fe91a 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -152,6 +154,8 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -226,6 +230,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -264,6 +269,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,5 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0da5b19..a725854 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	pg_atomic_init_u64(&ProcGlobal->oldestXidWithEpochHavingUndo, 0);
 }
 
 /*
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index a5950c1..323e1e3 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -121,6 +121,13 @@ bool		allowSystemTableMods = false;
 int			work_mem = 1024;
 int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
+int			rollback_overflow_size = 64;
+
+/*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
 
 /*
  * Primary determinants of sizes of shared-memory structures.
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 4eba98b..27f6c62 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -453,6 +453,20 @@ InitCommunication(void)
 	}
 }
 
+/*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
 
 /*
  * pg_split_opts -- split a string of options and append it to an argv array
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9ba8ed7..6084b0a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1954,6 +1955,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"disable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		},
+		&disable_undo_launcher,
+		false,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2923,6 +2935,16 @@ static struct config_int ConfigureNamesInt[] =
 		5000, 1, INT_MAX,
 		NULL, NULL, NULL
 	},
+	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
 
 	{
 		{"wal_segment_size", PGC_INTERNAL, PRESET_OPTIONS,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 77bb7c2..9779082 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -749,4 +749,10 @@
 # CUSTOMIZED OPTIONS
 #------------------------------------------------------------------------------
 
+# If often there are large transactions requiring rollbacks, then we can push
+# them to undo-workers for better performance. The size specifeid by the
+# parameter below, determines the minimum size of the rollback requests to be
+# sent to the undo-worker.
+#
+#rollback_overflow_size = 64
 # Add settings for extensions here
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 64aafef..467e4e9 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 7f0a179..f079d69 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -29,7 +29,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..396193b 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..e1fb42a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..ef0f6ac 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,26 +25,27 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 592c338..1477ddc 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
@@ -72,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
@@ -105,6 +116,14 @@ FullTransactionIdAdvance(FullTransactionId *dest)
 	(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
 	(int32) ((id1) - (id2)) > 0)
 
+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)			\
+	((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)			\
+	((uint32) ((epochxid) >> 32))
+
 /* ----------
  *		Object ID (OID) zero is InvalidOid.
  *
@@ -225,6 +244,7 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 /* in transam/varsup.c */
 extern FullTransactionId GetNewTransactionId(bool isSubXact);
 extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid);
+extern uint32 GetEpochForXid(TransactionId xid);
 extern FullTransactionId ReadNextFullTransactionId(void);
 extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 					  Oid oldest_datoid);
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index fcd1913..fdf4470 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,10 +14,12 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undorequest.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
 #include "storage/lock.h"
+#include "access/undolog.h"
 
 /*
  * GlobalTransactionData is defined in twophase.c; other places have no
@@ -41,7 +43,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 				TimestampTz prepared_at,
 				Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..652712c
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
index 0a6df73..25a80bb 100644
--- a/src/include/access/undoinsert.h
+++ b/src/include/access/undoinsert.h
@@ -39,7 +39,7 @@ typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
 extern UndoRecPtr PrepareUndoInsert(UnpackedUndoRecord *, FullTransactionId fxid,
 									UndoPersistence upersistence,
 									XLogReaderState *xlog_record);
-extern void InsertPreparedUndo(void);
+extern void InsertPreparedUndo(UndoPersistence upersistence);
 
 extern void RegisterUndoLogBuffers(uint8 first_block_id);
 extern void UndoLogBuffersSetLSN(XLogRecPtr recptr);
@@ -59,6 +59,9 @@ extern void UndoSetPrepareSize(int nrecords);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, UndoRecPtr prevurp,
 										Buffer buffer,
 										UndoPersistence upersistence);
+extern void PrepareUpdateUndoActionProgress(XLogReaderState *xlog_record,
+								UndoRecPtr urecptr, int progress);
+extern void UndoRecordUpdateTransInfo(int idx);
 extern void AtAbort_ResetUndoBuffers(void);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 2cfce8c..7b08a18 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -242,6 +242,19 @@ typedef struct UndoLogMetaData
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogControl
 {
@@ -265,6 +278,7 @@ typedef struct UndoLogControl
 	uint32		oldest_xidepoch;
 	UndoRecPtr	oldest_data;
 	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
 	LWLock		rewind_lock;		/* prevent rewinding while reading */
 } UndoLogControl;
 
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..b7f68c3
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,135 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoinsert.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+/* Remembers the last seen RecentGlobalXmin */
+TransactionId latestRecentGlobalXmin;
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId		full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	Oid			dbid;
+	bool		in_progress;	/* indicates that undo actions are being processed */
+} RollbackHashEntry;
+
+/* This is the data structure for each hash table key for rollbacks. */
+typedef struct RollbackHashKey
+{
+	FullTransactionId		full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+	uint64			request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+	TimestampTz		err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId				full_xid;
+	UndoRecPtr			start_urec_ptr;
+	UndoRecPtr			end_urec_ptr;
+	Oid					dbid;
+	uint64			request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+						UndoRequestInfo *urinfo, bool *in_other_db);
+/* Exposed functions for rollback hash table. */
+extern bool RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+				Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr);
+extern bool RollbackHTIsFull(void);
+extern void RollbackHTCleanup(Oid dbid);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..e2a61ff
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 61df235..ca7924e 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -22,6 +22,7 @@
 #include "storage/relfilenode.h"
 #include "storage/sinval.h"
 #include "utils/datetime.h"
+#include "utils/resowner.h"
 
 /*
  * Maximum size of Global Transaction ID (including '\0').
@@ -429,6 +430,11 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 				   const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
 
+extern void ApplyUndoActions(void);
+extern void SetUndoActionsInfo(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
 extern const char *xact_identify(uint8 info);
@@ -440,5 +446,7 @@ extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_ab
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
 extern bool IsInParallelMode(void);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+					UndoPersistence upersistence);
 
 #endif							/* XACT_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 8b1348c..04440ed 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -21,8 +21,11 @@
 
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
+#include "access/undorecord.h"
+#include "access/undorequest.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -295,9 +298,13 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	bool		(*rm_undo) (UndoRecInfo *urp_array, int first_idx, int last_idx,
+							Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+							bool blk_chain_complete);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 9375e54..08584fc 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -13,6 +13,7 @@
 
 #include "access/rmgr.h"
 #include "access/xlogdefs.h"
+#include "access/transam.h"
 #include "port/pg_crc32c.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..0fdd2f8 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,13 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest transaction id with epoc which is having undo. Include this
+	 * value in the checkpoint record so that whenever server starts we get
+	 * proper value.
+	 */
+	uint64		oldestXidWithEpochHavingUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 92dab66..c6fa89b 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -40,6 +40,9 @@ typedef struct binaryheap
 extern binaryheap *binaryheap_allocate(int capacity,
 					binaryheap_comparator compare,
 					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Size binaryheap_shmem_size(int capacity);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index b677c7e..acc7323 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -170,6 +170,8 @@ extern PGDLLIMPORT struct Latch *MyLatch;
 extern int32 MyCancelKey;
 extern int	MyPMChildSlot;
 
+extern bool applying_subxact_undo;
+
 extern char OutputFileName[];
 extern PGDLLIMPORT char my_exec_path[];
 extern char pkglib_path[];
@@ -245,6 +247,7 @@ extern PGDLLIMPORT bool allowSystemTableMods;
 extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
+extern PGDLLIMPORT int rollback_overflow_size;
 
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
@@ -422,6 +425,7 @@ extern AuxProcType MyAuxProcType;
  *****************************************************************************/
 
 /* in utils/init/postinit.c */
+extern bool dbid_exists(Oid dboid);
 extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f9b1cf2..3cda35d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 7cd71bd..013c0eb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -781,7 +781,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 8ccd2af..6212a18 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,8 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool disable_undo_launcher;
+
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 048947c..a146bf6 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -152,7 +152,6 @@ typedef enum LockTagType
 	LOCKTAG_SPECULATIVE_TOKEN,	/* speculative insertion Xid and token */
 	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_OBJECT,				/* non-relation database object */
-	/* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
 
 	/*
 	 * Note: object ID has same representation as in pg_depend and
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..118ff6b 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -222,6 +222,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SXACT,
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_REWIND,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1cee7db..a656991 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestXidWithEpochHavingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index bd24850..8aa3249 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7ac37fd..ce302bb 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
#18Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#1)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Apr 30, 2019 at 11:45 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

The attached patch will provide mechanism for masking the necessary
bits in undo pages for supporting consistency checking for the undo
pages. Ideally we can merge this patch with the main interface patch
but currently I have kept it separate for mainly because a) this is
still a WIP patch and b) review of the changes will be easy.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0005-undo-page-consistency-checker_WIP_v3.patchapplication/octet-stream; name=0005-undo-page-consistency-checker_WIP_v3.patchDownload
From 8a153e0b992db14aa602958f2d80794773204178 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Tue, 30 Apr 2019 19:06:02 +0530
Subject: [PATCH] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar based on initial version from Amit Khandekar and Rafia Sabih and design input from Amit Kapila
---
 src/backend/access/undo/undoinsert.c |   4 +-
 src/backend/access/undo/undorecord.c | 135 ++++++++++++++++++++++++++++++++---
 src/backend/storage/page/bufpage.c   |  33 +++++++++
 src/include/access/undoinsert.h      |   2 +
 src/include/access/undolog.h         |   2 +-
 src/include/access/undorecord.h      |   2 +-
 src/include/storage/bufpage.h        |  34 +++++++++
 7 files changed, 201 insertions(+), 11 deletions(-)

diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
index 1f3762f..71a3210 100644
--- a/src/backend/access/undo/undoinsert.c
+++ b/src/backend/access/undo/undoinsert.c
@@ -796,7 +796,9 @@ InsertPreparedUndo(void)
 				 * block header.
 				 */
 				if (starting_byte == UndoLogBlockHeaderSize)
-					PageInit(page, BLCKSZ, 0);
+					UndoPageInit(page, BLCKSZ, uur->uur_info,
+								 ucontext.already_processed,
+								 uur->uur_tuple.len, uur->uur_payload.len);
 
 				/*
 				 * Try to insert the record into the current page. If it
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index 7d7e088..fb0b691 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 
+#include "access/bufmask.h"
 #include "access/subtrans.h"
 #include "access/undorecord.h"
 #include "catalog/pg_tablespace.h"
@@ -26,31 +27,70 @@ static bool ReadUndoBytes(char *destptr, int readlen,
 			  int *total_bytes_read, int *partial_read);
 
 /*
- * Compute and return the expected size of an undo record.
+ * Compute the header size of the undo record.
  */
-Size
-UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+static inline Size
+UndoRecordHeaderSize(uint8 uur_info)
 {
 	Size		size;
 
 	size = SizeOfUndoRecordHeader + sizeof(uint16);
-	if ((uur->uur_info & UREC_INFO_RELATION_DETAILS) != 0)
+	if ((uur_info & UREC_INFO_RELATION_DETAILS) != 0)
 		size += SizeOfUndoRecordRelationDetails;
-	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
 		size += SizeOfUndoRecordBlock;
-	if ((uur->uur_info & UREC_INFO_BLOCKPREV) != 0)
+	if ((uur_info & UREC_INFO_BLOCKPREV) != 0)
 		size += SizeOfUndoRecordBlockPrev;
-	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
 		size += SizeOfUndoRecordTransaction;
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
 	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
 	{
-		size += SizeOfUndoRecordPayload;
 		size += uur->uur_payload.len;
 		size += uur->uur_tuple.len;
 	}
 
 	return size;
 }
+/*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint8		uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload  *payload = (UndoRecordPayload *) page_ptr + size;
+
+		size += payload->urec_payload_len;
+		size +=	payload->urec_tuple_len;
+	}
+
+	return size;
+}
 
 /*
  * Compute size of the Unpacked undo record in memory
@@ -72,6 +112,85 @@ UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
 	return size;
 }
 
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset = SizeOfUndoRecordHeader - sizeof(CommandId);
+	UndoPageHeader	phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page has
+	 * a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size	partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						   phdr->tuple_len + phdr->payload_len -
+						   phdr->record_offset;
+
+		/*
+		 * We just want to mask the cid in the undo record header.  So only if
+		 * the partial record in the current page include the undo record header
+		 * then we need to mask the cid bytes in this page.  Otherwise, directly
+		 * jump to the next record.
+		 */
+		if (phdr->record_offset < SizeOfUndoRecordHeader)
+		{
+			char   *cid_data;
+			Size	mask_size;
+
+			mask_size = Min(SizeOfUndoRecordHeader -
+							phdr->record_offset, sizeof(CommandId));
+
+			cid_data = next_record + cid_offset - phdr->record_offset;
+			memset(&cid_data, MASK_MARKER, mask_size);
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/*
+		 * If this is not complete record then check whether cid is on
+		 * this page or not.  If not then we are done with this page.
+		 */
+		if (page_end - next_record < SizeOfUndoRecordHeader)
+		{
+			int		mask_size = page_end - next_record - cid_offset;
+
+			if (mask_size > 0)
+				memset(&header->urec_cid, MASK_MARKER, mask_size);
+			break;
+		}
+		else
+		{
+			/* Mask cid */
+			memset(&header->urec_cid, MASK_MARKER, sizeof(header->urec_cid));
+		}
+
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
+
 /*
  * BeginUnpackUndo - Initiate unpacking a single one record.
  */
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 14bc61b..8519fd7 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,39 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint8 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	/*
+	 * TODO: We can update the value of the p->pd_lower whenever we insert
+	 * a record into an undo page.  By doing this we can avoid processing
+	 * complete undo page if there are no more records.
+	 */
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
index d50085e..daec742 100644
--- a/src/include/access/undoinsert.h
+++ b/src/include/access/undoinsert.h
@@ -44,6 +44,7 @@ extern void InsertPreparedUndo(void);
 extern void RegisterUndoLogBuffers(uint8 first_block_id);
 extern void UndoLogBuffersSetLSN(XLogRecPtr recptr);
 extern void UnlockReleaseUndoBuffers(void);
+extern void mask_undo_page(char *pagedata);
 
 extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
 				BlockNumber blkno, OffsetNumber offset,
@@ -60,5 +61,6 @@ extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, UndoRecPtr prevurp,
 					  Buffer buffer,
 					  UndoPersistence upersistence);
 extern void AtAbort_ResetUndoBuffers(void);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 2cfce8c..abbaf47 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -128,7 +128,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index c607ba9..adbe3bb 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -20,7 +20,6 @@
 #include "storage/buf.h"
 #include "storage/off.h"
 
-
 /*
  * Every undo record begins with an UndoRecordHeader structure, which is
  * followed by the additional structures indicated by the contents of
@@ -251,5 +250,6 @@ extern void InsertUndoData(UndoPackContext *ucontext, Page page,
 			   int starting_byte);
 extern void SkipInsertingUndoData(UndoPackContext *ucontext,
 					  int starting_byte);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 85cd0ab..5be7d1e 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -212,6 +212,37 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * FIXME:  It should be declared in undolog.h ?
+ *
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+	/* Fields required for undolog consistency checker */
+	uint8		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -415,6 +446,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint8 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 					OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

#19Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#1)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Apr 30, 2019 at 8:44 PM Robert Haas <robertmhaas@gmail.com> wrote:

UndoRecInfo looks a bit silly, I think. Isn't index just the index of
this entry in the array? You can always figure that out by ptr -
array_base. Instead of having UndoRecPtr urp in this structure, how
about adding that to UnpackedUndoRecord? When inserting, caller
leaves it unset and InsertPreparedUndo sets it; when retrieving,
UndoFetchRecord or UndoRecordBulkFetch sets it. With those two
changes, I think you can get rid of UndoRecInfo entirely and just
return an array of UnpackedUndoRecords. This might also eliminate the
need for an 'urp' member in PreparedUndoSpace.

Yeah, at least in this patch it looks silly. Actually, I added that
index to make the qsort stable when execute_undo_action sorts them in
block order. But, as part of this patch we don't have that processing
so either we can remove this structure completely as you suggested but
undo processing patch has to add that structure or we can just add
comment that why we added this index field.

I am ok with other comments and will work on them.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#20Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#19)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, May 2, 2019 at 5:32 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Yeah, at least in this patch it looks silly. Actually, I added that
index to make the qsort stable when execute_undo_action sorts them in
block order. But, as part of this patch we don't have that processing
so either we can remove this structure completely as you suggested but
undo processing patch has to add that structure or we can just add
comment that why we added this index field.

Well, the qsort comparator could compute the index as ptr - array_base
just like any other code, couldn't it?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#21Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#20)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, May 2, 2019 at 7:00 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, May 2, 2019 at 5:32 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Yeah, at least in this patch it looks silly. Actually, I added that
index to make the qsort stable when execute_undo_action sorts them in
block order. But, as part of this patch we don't have that processing
so either we can remove this structure completely as you suggested but
undo processing patch has to add that structure or we can just add
comment that why we added this index field.

Well, the qsort comparator could compute the index as ptr - array_base
just like any other code, couldn't it?

I might be completely missing but (ptr - array_base) is only valid
when first time you get the array, but qsort will swap the element
around and after that you will never be able to make out which element
was at lower index and which one was at higher index. Basically, our
goal is to preserve the order of the undo record for the same block
but their order might get changed due to swap when they are getting
compared with the undo record pointer of the another block and once
the order is swap we will never know what was their initial positions?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#22Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#21)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, May 3, 2019 at 12:46 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I might be completely missing but (ptr - array_base) is only valid
when first time you get the array, but qsort will swap the element
around and after that you will never be able to make out which element
was at lower index and which one was at higher index. Basically, our
goal is to preserve the order of the undo record for the same block
but their order might get changed due to swap when they are getting
compared with the undo record pointer of the another block and once
the order is swap we will never know what was their initial positions?

*facepalm*

Yeah, you're right.

Still, I think we should see if there's some way of getting rid of
that structure, or at least making it an internal detail that is used
by the code that's doing the sorting rather than something that is
exposed as an external interface.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#23Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#17)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 1, 2019 at 10:08 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Thomas told me offlist that this email of mine didn't hit
pgsql-hackers, so trying it again by resending.

Attached is next version of the patch with minor improvements:
a. use FullTransactionId
b. improve comments
c. removed some functions

The branch can be accessed at
https://github.com/EnterpriseDB/zheap/tree/undoprocessing. It is on
top of Thomas and Dilip's patches related to undo logs and undo
records, though still not everything is synced up from both the
branches as they are also actively working on their set of patches.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Allow-undo-actions-to-be-applied-on-rollbacks-and-di.v2.patchapplication/x-patch; name=0001-Allow-undo-actions-to-be-applied-on-rollbacks-and-di.v2.patchDownload
From 5d9e179bd481b5ed574b6e7117bf3eb62b5dc003 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Sat, 4 May 2019 16:52:01 +0530
Subject: [PATCH] Allow undo actions to be applied on rollbacks and discard
 unwanted undo.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

To improve the efficiency of the rollbacks, we create three queues and a
hash table for the rollback requests.  A Xid based priority queue which
will allow us to process the requests of older transactions and help us to
move oldesdXidHavingUndo (this is a xid-horizon below which all the
transactions are visible) forward.  A size-based queue which will help us
to perform the rollbacks of larger aborts in a timely fashion so that we
don't get stuck while processing them during discard of the logs.  An error
queue to hold the requests for transactions that failed to apply its undo.
The rollback hash table is used to avoid duplicate undo requests by
backends and discard worker.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

Designed by: Andres Freund, Amit Kapila, Robert Haas, and Thomas Munro
Author: Amit Kapila, Dilip Kumar, Kuntal Ghosh, and Thomas Munro
---
 src/backend/access/rmgrdesc/Makefile          |    3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c  |   47 +
 src/backend/access/rmgrdesc/xlogdesc.c        |    4 +-
 src/backend/access/transam/rmgr.c             |    5 +-
 src/backend/access/transam/twophase.c         |  104 +-
 src/backend/access/transam/varsup.c           |   44 +
 src/backend/access/transam/xact.c             |  535 ++++++++-
 src/backend/access/transam/xlog.c             |   29 +
 src/backend/access/undo/Makefile              |    3 +-
 src/backend/access/undo/README.UndoProcessing |   96 ++
 src/backend/access/undo/discardworker.c       |  206 ++++
 src/backend/access/undo/undoaction.c          |  311 ++++++
 src/backend/access/undo/undoactionxlog.c      |   49 +
 src/backend/access/undo/undodiscard.c         |  364 ++++++
 src/backend/access/undo/undoinsert.c          |   93 +-
 src/backend/access/undo/undolog.c             |    5 +-
 src/backend/access/undo/undorequest.c         | 1468 +++++++++++++++++++++++++
 src/backend/access/undo/undoworker.c          |  789 +++++++++++++
 src/backend/commands/tablecmds.c              |    5 +
 src/backend/lib/binaryheap.c                  |  118 ++
 src/backend/postmaster/bgworker.c             |   11 +
 src/backend/postmaster/pgstat.c               |    9 +
 src/backend/postmaster/postmaster.c           |   11 +
 src/backend/replication/logical/decode.c      |    4 +
 src/backend/storage/ipc/ipci.c                |    6 +
 src/backend/storage/lmgr/lwlocknames.txt      |    2 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/error/elog.c                |   36 +-
 src/backend/utils/init/globals.c              |    7 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    6 +
 src/backend/utils/resowner/resowner.c         |   11 +-
 src/bin/pg_rewind/parsexlog.c                 |    2 +-
 src/bin/pg_waldump/rmgrdesc.c                 |    3 +-
 src/include/access/discardworker.h            |   20 +
 src/include/access/rmgr.h                     |    2 +-
 src/include/access/rmgrlist.h                 |   47 +-
 src/include/access/transam.h                  |   20 +
 src/include/access/twophase.h                 |    4 +-
 src/include/access/undoaction_xlog.h          |   39 +
 src/include/access/undodiscard.h              |   25 +
 src/include/access/undoinsert.h               |    5 +-
 src/include/access/undolog.h                  |   16 +-
 src/include/access/undorequest.h              |  138 +++
 src/include/access/undoworker.h               |   27 +
 src/include/access/xact.h                     |    8 +
 src/include/access/xlog_internal.h            |    9 +-
 src/include/access/xlogrecord.h               |    1 +
 src/include/catalog/pg_control.h              |    7 +
 src/include/lib/binaryheap.h                  |    8 +
 src/include/miscadmin.h                       |    4 +
 src/include/nodes/primnodes.h                 |    3 +-
 src/include/pgstat.h                          |    5 +-
 src/include/postmaster/postmaster.h           |    2 +
 src/include/storage/lock.h                    |    1 -
 src/include/storage/lwlock.h                  |    1 +
 src/include/storage/proc.h                    |    2 +
 src/include/storage/procarray.h               |    7 +-
 src/include/utils/elog.h                      |    3 +
 60 files changed, 4752 insertions(+), 76 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/README.UndoProcessing
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undoaction_xlog.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undorequest.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..1de1bbc 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest xid with epoch having undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..6238240 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index ecc01f7..b928e24 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -927,6 +927,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1011,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1043,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1484,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	int			i;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1524,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1612,6 +1635,85 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	/*
+	 * Perform undo actions, if there are undologs for this transaction. We
+	 * need to perform undo actions while we are still in transaction. Never
+	 * push rollbacks of temp tables to undo worker.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		volatile	UndoRequestInfo urinfo;
+		uint32	epoch;
+		FullTransactionId full_xid;
+
+		if (end_urec_ptr[i] != InvalidUndoRecPtr && !isCommit)
+		{
+			uint32		save_holdoff;
+
+			/*
+			 * We don't allow XIDs with an age of more than 2 billion in the
+			 * system, so we can infer the epoch here.  This assumption is not
+			 * good, but we can fix it by storing full transaction id in
+			 * TwoPhaseFileHeader.
+			 */
+			epoch = GetEpochForXid(hdr->xid);
+			full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				bool		result = false;
+
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = MyDatabaseId;
+				urinfo.full_xid = full_xid;
+
+				if (i != UNDO_TEMP)
+					result = RegisterRollbackReq(end_urec_ptr[i],
+												 start_urec_ptr[i],
+												 hdr->database,
+												 full_xid);
+				if (!result)
+					execute_undo_actions(full_xid, end_urec_ptr[i],
+										 start_urec_ptr[i], true);
+			}
+			PG_CATCH();
+			{
+				if (i == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then
+				 * remove the entry from the hash table and continue to
+				 * process the remaining undo requests if any.  This request
+				 * will be later processed by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTRemoveEntry(urinfo.full_xid, urinfo.start_urec_ptr);
+
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+			}
+			PG_END_TRY();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 8c3d84f..25db556 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -300,6 +300,30 @@ AdvanceNextFullTransactionIdPastXid(TransactionId xid)
 }
 
 /*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
+
+/*
  * Advance the cluster-wide value for the oldest valid clog entry.
  *
  * We must acquire CLogTruncationLock to advance the oldestClogXid. It's not
@@ -333,10 +357,23 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUndo because this is the oldest xid whose undo
+	 * is not yet discarded so this is still a valid xid in the system.  The
+	 * oldestXidHavingUndo will be only valid for zheap table access method,
+	 * so it won't impact any other table access method.
+	 */
+	oldestXidHavingUndo = GetXidFromEpochXid(
+											 pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -403,6 +440,13 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	curXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
 	LWLockRelease(XidGenLock);
 
+	/*
+	 * Fixme - The messages in below code need some adjustment for zheap. They
+	 * should reflect that the system needs to discard the undo.  We can add
+	 * it once we have a pluggable storage API which might provide us some way
+	 * to distinguish among differnt storage engines.
+	 */
+
 	/* Log the info */
 	ereport(DEBUG1,
 			(errmsg("transaction ID wrap limit is %u, limited by database with OID %u",
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 47a8f0d..077c47e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,11 +26,13 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "access/undoinsert.h"
+#include "access/undorequest.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_enum.h"
 #include "catalog/storage.h"
@@ -129,7 +131,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -154,6 +157,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -164,7 +168,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -192,6 +197,13 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -363,9 +375,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -724,9 +736,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -906,15 +923,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -998,6 +1015,24 @@ IsInParallelMode(void)
 }
 
 /*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
+
+/*
  *	CommandCounterIncrement
  */
 void
@@ -1886,6 +1921,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1969,6 +2005,14 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2246,6 +2290,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
@@ -2265,6 +2313,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2281,7 +2331,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2434,7 +2484,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2629,7 +2679,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2788,6 +2840,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2853,6 +2907,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2914,9 +2970,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2995,11 +3059,13 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			SetUndoActionsInfo();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3016,7 +3082,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3060,6 +3126,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3073,7 +3157,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3095,7 +3179,9 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			SetUndoActionsInfo();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3115,7 +3201,9 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				SetUndoActionsInfo();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3168,6 +3256,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	SetUndoActionsInfo();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3183,7 +3279,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3199,6 +3299,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3209,8 +3310,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3224,6 +3329,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3235,6 +3341,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_END:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3264,6 +3371,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_ABORT_PENDING:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3275,6 +3383,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_PREPARE:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3286,6 +3395,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_SUBINPROGRESS:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3300,6 +3410,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3312,10 +3423,213 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
 /*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	uint32		save_holdoff;
+	volatile	UndoRequestInfo urinfo;
+	volatile int per_level;
+	bool		error = false;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (s->latest_urec_ptr[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				bool		result = false;
+
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = MyDatabaseId;
+				urinfo.full_xid = GetTopFullTransactionId();
+
+				/*
+				 * If this request is not for a temp table and not aborting
+				 * subtransaction and the request size is greater than some
+				 * threshold then push it to undo-worker through RollbackHT,
+				 * undo-worker will perform the corresponding undo actions
+				 * later.
+				 *
+				 * We can't push the undo actions for temp table to background
+				 * workers as the the temp tables are only accessible in the
+				 * backend that has created them.  We can't postpone applying
+				 * undo actions for subtransactions as the modifications made
+				 * by aborted subtransaction must not be visible even if the
+				 * main transaction commits.  It is not advisable to apply the
+				 * undo actions of a very large transaction as that can lead
+				 * to a delay in retruning the control back to user after
+				 * abort.
+				 */
+				if (per_level != UNDO_TEMP &&
+					!IsSubTransaction())
+					result = RegisterRollbackReq(s->latest_urec_ptr[per_level],
+												 s->start_urec_ptr[per_level],
+												 MyDatabaseId,
+												 urinfo.full_xid);
+				if (!result)
+				{
+					/* for subtransactions, we do partial rollback. */
+					execute_undo_actions(urinfo.full_xid,
+										 s->latest_urec_ptr[per_level],
+										 s->start_urec_ptr[per_level],
+										 !IsSubTransaction());
+				}
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then
+				 * remove the entry from the hash table and continue to
+				 * process the remaining undo requests if any.  This request
+				 * will be later processed by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTRemoveEntry(urinfo.full_xid, urinfo.start_urec_ptr);
+
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				error = true;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	if (error)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
+	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
  *	PreventInTransactionBlock
  *
  *	This routine is to be called by statements that must not run inside
@@ -3641,6 +3955,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3833,6 +4149,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3949,6 +4267,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4089,6 +4409,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4107,6 +4429,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4165,6 +4499,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4200,8 +4536,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4274,6 +4639,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4392,6 +4759,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4412,6 +4781,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4430,6 +4800,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4481,17 +4867,29 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	SetUndoActionsInfo();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4518,6 +4916,14 @@ AbortOutOfAnyTransaction(void)
 	AtAbort_Memory();
 
 	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	SetUndoActionsInfo();
+
+	/*
 	 * Get out of any transaction or nested transaction
 	 */
 	do
@@ -4537,7 +4943,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4554,6 +4964,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
 				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
 				break;
@@ -4581,6 +5004,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
 				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
@@ -4674,6 +5110,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4713,6 +5151,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4730,6 +5169,14 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4924,7 +5371,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5352,6 +5800,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5370,6 +5820,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5395,6 +5847,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5994,3 +6448,52 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * SetUndoActionsInfo - set the start and end undo record pointers before
+ * performing the undo actions.
+ */
+void
+SetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 28fdaaf..47c4ad4 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5162,6 +5162,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6613,6 +6614,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6629,6 +6633,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7317,7 +7325,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8713,6 +8727,10 @@ CreateCheckPoint(int flags)
 	if (!shutdown)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
+ 
+	checkPoint.oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+
 
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
@@ -9625,6 +9643,9 @@ xlog_redo(XLogReaderState *record)
 
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
+ 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
 
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
@@ -9683,12 +9704,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUndo =
+			checkPoint.oldestFullXidHavingUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUndo =
+			checkPoint.oldestFullXidHavingUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9727,6 +9753,9 @@ xlog_redo(XLogReaderState *record)
 		/* Handle multixact */
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
+ 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
 
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index f41e8f7..6e4c890 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoinsert.o undolog.o undorecord.o
+OBJS = discardworker.o undoaction.o undoactionxlog.o undodiscard.o undoinsert.o \
+		undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..ba5da5f
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,96 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue which will allow
+us to process the requests of older transactions and help us to move
+oldesdXidHavingUndo (this is a xid-horizon below which all the transactions are
+visible) forward.  A size-based queue which will help us to perform the rollbacks
+of larger aborts in a timely fashion, so that we don't get stuck while processing
+them during discard of the logs.  An error queue to hold the requests for
+transactions that failed to apply its undo.  The rollback hash table is used to
+avoid duplicate undo requests by backends and discard worker.  The table must be
+able to accommodate all active undo requests.  The undo requests must appear in
+both xid and size requests queues or neither.  As of now, we process the requests
+from these queues in a round-robin fashion to give equal priority to all three
+types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just remove the request from the hash table and
+continue to process other requests if any.  The discard worker will find this
+errored transaction at later point of time and again add it to the request
+queues.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..ef48e6c
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,206 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestXidHavingUndo = GetXidFromEpochXid(
+												 pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..d1d961a
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+#include "access/undodiscard.h"
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block == ruur->uur_block)
+	{
+		/*
+		 * If records are for the same block then maintain their existing
+		 * order by comparing their index in the array.  Because for single
+		 * block we need to maintain the order for applying undo action.
+		 */
+		if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+			return -1;
+		else
+			return 1;
+	}
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else
+		return 1;
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * nopartial	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	UndoRecPtr	urec_ptr;
+	ForkNumber	prev_fork = InvalidForkNumber;
+	BlockNumber prev_block = InvalidBlockNumber;
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	int			undo_apply_size = maintenance_work_mem * 1024L;
+	TransactionId	xid = XidFromFullTransactionId(full_xid);
+
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	urec_ptr = from_urecptr;
+
+	if (nopartial)
+	{
+		/*
+		 * It is important here to fetch the latest undo record and validate if
+		 * the actions are already executed.  The reason is that it is possible
+		 * that discard worker or backend might try to execute the rollback
+		 * request which is already executed.  For ex., after discard worker
+		 * fetches the record and found that this transaction need to be
+		 * rolledback, backend might concurrently execute the actions and
+		 * remove the request from rollback hash table. The similar problem
+		 * can happen if the discard worker first pushes the request, the undo
+		 * worker processed it and backend tries to process it some later point.
+		 */
+		uur = UndoFetchRecord(to_urecptr, InvalidBlockNumber, InvalidOffsetNumber,
+							  InvalidTransactionId, NULL, NULL);
+
+		/* already processed. */
+		if (uur == NULL)
+			return;
+
+		/*
+		 * We don't need to execute the undo actions if they are already
+		 * executed.
+		 */
+		if (uur->uur_progress != 0)
+		{
+			UndoRecordRelease(uur);
+			return;
+		}
+
+		Assert(xid == uur->uur_xid);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them in order of reloid and block number and then apply them
+	 * page-at-a-time.  Repeat this until we process all the records for the
+	 * transaction being rolled back.
+	 */
+	do
+	{
+		Oid			prev_reloid = InvalidOid;
+		bool		blk_chain_complete;
+		int			i;
+		int			nrecords;
+		int			last_index = 0;
+		int			prefetch_pages = 0;
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+
+		/*
+		 * Fetch multiple undo records at once.  This will return the array
+		 * of undo records which holds undo record pointers and the pointers
+		 * to the actual unpacked undo record.   This will also update the
+		 * number of undo records it has copied in the urp_array.
+		 */
+		urp_array = UndoRecordBulkFetch(&urec_ptr, to_urecptr, undo_apply_size,
+										&nrecords, false);
+		if (nrecords == 0)
+			break;
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(TransactionIdEquals(xid, urp_array[0].uur->uur_xid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urp_array, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		if (nopartial && !UndoRecPtrIsValid(urec_ptr))
+			blk_chain_complete = true;
+		else
+			blk_chain_complete = false;
+
+		/*
+		 * Now we have urp_array which is sorted in the block order so
+		 * traverse this array and apply the undo action block by block.
+		 */
+		for (i = last_index; i < nrecords; i++)
+		{
+			UnpackedUndoRecord *uur = urp_array[i].uur;
+
+			/*
+			 * If this undo is not for the same block then apply all undo
+			 * actions for the previous block.
+			 */
+			if (OidIsValid(prev_reloid) &&
+				(prev_reloid != uur->uur_reloid ||
+				 prev_fork != uur->uur_fork ||
+				 prev_block != uur->uur_block))
+			{
+				execute_undo_actions_page(urp_array, last_index, i - 1,
+										  prev_reloid, full_xid, prev_block,
+										  blk_chain_complete);
+				last_index = i;
+
+				/* We have consumed one prefetched page. */
+				if (prefetch_pages > 0)
+					prefetch_pages--;
+			}
+
+			prev_reloid = uur->uur_reloid;
+			prev_fork = uur->uur_fork;
+			prev_block = uur->uur_block;
+		}
+
+		/* Apply the last set of the actions. */
+		execute_undo_actions_page(urp_array, last_index, i - 1,
+								  prev_reloid, full_xid, prev_block,
+								  blk_chain_complete);
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urp_array[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urp_array);
+	} while (true);
+
+	/*
+	 * Set undo action apply progress as completed in the transaction header
+	 * if this is a main transaction.
+	 */
+	if (nopartial)
+	{
+		/*
+		 * Prepare and update the progress of the undo action apply in the
+		 * transaction header.
+		 */
+		PrepareUpdateUndoActionProgress(NULL, to_urecptr, 1);
+
+		START_CRIT_SECTION();
+
+		/* Update the progress in the transaction header. */
+		UndoRecordUpdateTransInfo(0);
+
+		/* WAL log the undo apply progress. */
+		{
+			xl_undoapply_progress xlrec;
+
+			xlrec.urec_ptr = to_urecptr;
+			xlrec.progress = 1;
+
+			/*
+				* FIXME : We need to register undo buffers and set LSN for
+				* them that will be required for FPW of the undo buffers.
+				* This is currently not possible as undolog API only allows
+				* register buffer via backends and we can reach here via
+				* backend.
+				*/
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+			/* RegisterUndoLogBuffers(2); */
+			(void) XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+			/* UndoLogBuffersSetLSN(recptr); */
+		}
+
+		END_CRIT_SECTION();
+		UnlockReleaseUndoBuffers();
+
+		/*
+		 * Undo action is applied so delete the hash table entry.
+		 */
+		Assert(TransactionIdIsValid(xid));
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+	}
+}
+
+/*
+ * execute_undo_actions_page - Execute the undo actions for a page
+ *
+ *	urp_array - array of undo records (along with their location) for which undo
+ *				action needs to be applied.
+ *	first_idx - index in the urp_array of the first undo action to be applied
+ *	last_idx  - index in the urp_array of the last undo action to be applied
+ *	reloid	- OID of relation on which undo actions needs to be applied.
+ *	blkno	- block number on which undo actions needs to be applied.
+ *	blk_chain_complete - indicates whether the undo chain for block is
+ *						 complete.
+ *
+ *	returns true, if successfully applied the undo actions, otherwise, false.
+ */
+bool
+execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx, int last_idx,
+						  Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+						  bool blk_chain_complete)
+{
+	/*
+	 * All records passed to us are for the same RMGR, so we just use the
+	 * first record to dispatch.
+	 */
+	Assert(urp_array != NULL);
+
+	return RmgrTable[urp_array[0].uur->uur_rmid].rm_undo(urp_array, first_idx,
+														 last_idx, reloid,
+														 full_xid, blkno,
+														 blk_chain_complete);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..087e737
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoinsert.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+
+	/* Update the progress in the transaction header. */
+	PrepareUpdateUndoActionProgress(record, xlrec->urec_ptr, xlrec->progress);
+	UndoRecordUpdateTransInfo(0);
+	UnlockReleaseUndoBuffers();
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..2f530cd
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,364 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard the undo for the given log
+ *
+ * Search the undo log, get the start record for each transaction until we get
+ * the transaction with xid >= xmin or an invalid xid.  Then call undolog
+ * routine to discard up to that point and update the memory structure for the
+ * log slot.  We set the hibernate flag if we do not have any undo data that
+ * can be discarded, this flag is passed back to the discard worker wherein it
+ * determines if the system is idle and it should sleep for some time.
+ *
+ * Return the oldest full_xid remaining in this undo log (which should be
+ * >= xmin, since we'll discard everything older).  Returns
+ * InvalidTransactionId, if the undo log is empty.
+ */
+static FullTransactionId
+UndoDiscardOneLog(UndoLogControl *log, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	bool	log_complete = false;
+	TransactionId	undoxid = InvalidTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	uint32	epoch = 0;
+
+	if (UndoRecPtrIsValid(log->oldest_data))
+		undo_recptr = log->oldest_data;
+	else
+		undo_recptr = UndoLogGetFirstValidRecord(log, NULL);
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		bool pending_abort = false;
+
+		next_insert = UndoLogGetNextInsertPtr(log->logno, InvalidTransactionId);
+
+		if (next_insert == undo_recptr)
+		{
+			/*
+			 * The caller of this function must have ensured that there is
+			 * something to discard.
+			 */
+			Assert(undo_recptr != log->oldest_data);
+
+			/* Indicate that we have processed all the log. */
+			log_complete = true;
+		}
+		else
+		{
+			/* Fetch the undo record for the given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+
+			if (uur != NULL)
+			{
+				/*
+				 * Add the aborted transaction to the rollback request queues.
+				 *
+				 * If the undo actions for the aborted transaction is already
+				 * applied then continue discarding the undo log, otherwise,
+				 * discard till current point and stop processing this undo
+				 * log.
+				 *
+				 * We can ignore the abort for transactions whose
+				 * corresponding database doesn't exist.
+				 *
+				 * XXX: We've added the transaction-in-progress check to avoid
+				 * xids of in-progress autovacuum as those are not computed
+				 * for oldestxmin calculation.  See DiscardWorkerMain.
+				 */
+				if (!TransactionIdDidCommit(uur->uur_xid) &&
+					!TransactionIdIsInProgress(uur->uur_xid) &&
+					TransactionIdPrecedes(uur->uur_xid, xmin) &&
+					uur->uur_progress == 0 &&
+					dbid_exists(uur->uur_dbid))
+				{
+					FullTransactionId full_xid;
+
+					full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+																uur->uur_xid);
+					(void) RegisterRollbackReq(InvalidUndoRecPtr,
+											   undo_recptr,
+											   uur->uur_dbid,
+											   full_xid);
+
+					pending_abort = true;
+				}
+
+				next_urecptr = uur->uur_next;
+				undoxid = uur->uur_xid;
+				epoch = uur->uur_xidepoch;
+
+				UndoRecordRelease(uur);
+				uur = NULL;
+			}
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) the next transaction is not all-visible. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if ((TransactionIdIsValid(undoxid) &&
+			 TransactionIdFollowsOrEquals(undoxid, xmin)) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			log_complete ||
+			UndoRecPtrGetLogNo(next_urecptr) != log->logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If the transaction id is smaller than the xmin, it means this
+			 * must be the last transaction in this undo log, so we need to
+			 * get the last insert point in this undo log and discard till
+			 * that point.
+			 *
+			 * Also, if the transaction has pending abort, stop discarding
+			 * further.
+			 */
+			if (TransactionIdPrecedes(undoxid, xmin) && !pending_abort)
+			{
+				UndoRecPtr	next_insert = InvalidUndoRecPtr;
+
+				/*
+				 * If more undo has been inserted since we checked last, then
+				 * we can process that as well.
+				 */
+				next_insert = UndoLogGetNextInsertPtr(log->logno, undoxid);
+				if (!UndoRecPtrIsValid(next_insert))
+					continue;
+
+				undo_recptr = next_insert;
+				need_discard = true;
+				epoch = 0;
+				latest_discardxid = undoxid;
+				undoxid = InvalidTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&log->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If no more pending undo logs then set the oldest transaction to
+			 * InvalidTransactionId.
+			 */
+			if (log_complete)
+			{
+				log->oldest_xid = InvalidTransactionId;
+				log->oldest_xidepoch = 0;
+			}
+			else
+			{
+				log->oldest_xid = undoxid;
+				log->oldest_xidepoch = epoch;
+			}
+
+			log->oldest_data = undo_recptr;
+
+			LWLockRelease(&log->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&log->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&log->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = undoxid;
+
+		Assert(uur == NULL);
+
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+
+	return FullTransactionIdFromEpochAndXid(epoch, undoxid);
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogControl *log = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((log = UndoLogNext(log)))
+	{
+		FullTransactionId oldest_xid = InvalidFullTransactionId;
+
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&log->mutex, LW_SHARED);
+		if (log->meta.discard == log->meta.unlogged.insert)
+		{
+			LWLockRelease(&log->mutex);
+			continue;
+		}
+		LWLockRelease(&log->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (log->meta.persistence == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin the try
+		 * to discard the undo log.
+		 */
+		if (!TransactionIdIsValid(log->oldest_xid) ||
+			TransactionIdPrecedes(log->oldest_xid, oldestXmin))
+		{
+			/* Process the undo log. */
+			oldest_xid = UndoDiscardOneLog(log, oldestXmin, hibernate);
+		}
+
+		if (FullTransactionIdIsValid(oldest_xid) &&
+			FullTransactionIdPrecedes(oldest_xid, oldestXidHavingUndo))
+			oldestXidHavingUndo = oldest_xid;
+	}
+
+	/*
+	 * Update the oldestFullXidHavingUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogControl *log = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((log = UndoLogNext(log)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (log->meta.discard == log->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogControl *log = UndoLogGet(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (log->meta.persistence == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&log->mutex, LW_SHARED);
+	if (log->meta.discard == log->meta.unlogged.insert)
+	{
+		LWLockRelease(&log->mutex);
+		return;
+	}
+	LWLockRelease(&log->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
index 1f3762f..0e8e448 100644
--- a/src/backend/access/undo/undoinsert.c
+++ b/src/backend/access/undo/undoinsert.c
@@ -170,7 +170,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 static void UndoRecordPrepareTransInfo(UndoRecPtr urecptr,
 						   UndoRecPtr xact_urp,
 						   XLogReaderState *xlog_record);
-static void UndoRecordUpdateTransInfo(int idx);
 static int UndoGetBufferSlot(RelFileNode rnode, BlockNumber blk,
 				  ReadBufferMode rbm,
 				  UndoPersistence persistence, XLogReaderState *xlog_record);
@@ -245,20 +244,16 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
 		return;
 
 	/*
-	 * Acquire the discard lock before accessing the undo record so that
-	 * discard worker doesn't remove the record while we are in process of
+	 * Acquire the discard_update_lock before accessing the undo record so
+	 * that discard worker can't remove the record while we are in process of
 	 * reading it.
 	 */
-	LWLockAcquire(&log->discard_lock, LW_SHARED);
-
-	/*
-	 * The absence of previous transaction's undo indicate that this backend
-	 * is preparing its first undo in which case we have nothing to update.
-	 * UndoRecordIsValid will release the lock if it returns false.
-	 */
-	if (!UndoRecordIsValid(log, xact_urp))
+	LWLockAcquire(&log->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoLogIsDiscarded(xact_urp))
 	{
-		LWLockRelease(&log->discard_lock);
+		/* Release lock and return. */
+		LWLockRelease(&log->discard_update_lock);
 		return;
 	}
 
@@ -295,7 +290,63 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
 	xact_urec_info[nxact_urec_info].urecptr = xact_urp;
 	nxact_urec_info++;
 
-	LWLockRelease(&log->discard_lock);
+	LWLockRelease(&log->discard_update_lock);
+}
+
+/*
+ * Update the progress of the undo record in the transaction header.
+ */
+void
+PrepareUpdateUndoActionProgress(XLogReaderState *xlog_record,
+								UndoRecPtr urecptr, int progress)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber	cur_blk;
+	RelFileNode	rnode;
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urecptr);
+	UndoLogControl *log;
+	Page		page;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	UndoPackContext ucontext = {{0}};
+
+	log = UndoLogGet(logno, false);
+
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (true)
+	{
+		bufidx = UndoGetBufferSlot(rnode, cur_blk,
+								   RBM_NORMAL,
+								   log->meta.persistence,
+								   xlog_record);
+
+		xact_urec_info[nxact_urec_info].idx_undo_buffers[index++] = bufidx;
+		buffer = prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+	FinishUnpackUndo(&ucontext, &xact_urec_info[nxact_urec_info].uur);
+
+	xact_urec_info[nxact_urec_info].urecptr = urecptr;
+	xact_urec_info[nxact_urec_info].uur.uur_progress = progress;
+	nxact_urec_info++;
 }
 
 
@@ -305,7 +356,7 @@ UndoRecordPrepareTransInfo(UndoRecPtr urecptr, UndoRecPtr xact_urp,
  * UndoRecordPrepareTransInfo.  This must be called under the critical section.
  * This will just overwrite the undo header not the data.
  */
-static void
+void
 UndoRecordUpdateTransInfo(int idx)
 {
 	Page		page = NULL;
@@ -317,6 +368,12 @@ UndoRecordUpdateTransInfo(int idx)
 	urec_ptr = xact_urec_info[idx].urecptr;
 
 	/*
+	 * We've pinned the undo buffers in UndoRecordPrepareTransInfo, so
+	 * it shouldn't be discarded.
+	 */
+	Assert(!UndoLogIsDiscarded(urec_ptr));
+
+	/*
 	 * Update the next transactions start urecptr in the transaction header.
 	 */
 	starting_byte = UndoRecPtrGetPageOffset(urec_ptr);
@@ -722,7 +779,7 @@ PrepareUndoInsert(UnpackedUndoRecord *urec, FullTransactionId fxid,
  * criticalsection; it should never fail.
  */
 void
-InsertPreparedUndo(void)
+InsertPreparedUndo(UndoPersistence upersistence)
 {
 	Page		page = NULL;
 	int			starting_byte;
@@ -819,6 +876,12 @@ InsertPreparedUndo(void)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(urp, upersistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvance(urp, size);
 	}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 028b282..2d5ee45 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -185,6 +185,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&shared->logs[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&shared->logs[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 			LWLockInitialize(&shared->logs[i].rewind_lock,
 							 LWTRANCHE_REWIND);
 		}
@@ -1230,7 +1232,8 @@ UndoLogGetFirstValidRecord(UndoLogControl *log, bool *full)
 		result = InvalidUndoRecPtr;
 	else
 		result = MakeUndoRecPtr(log->logno, log->meta.discard);
-	*full = log->meta.status == UNDO_LOG_STATUS_FULL;
+	if (full)
+		*full = log->meta.status == UNDO_LOG_STATUS_FULL;
 	LWLockRelease(&log->mutex);
 
 	return result;
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..d92bd8e
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1468 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/discardworker.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+
+#define ROLLBACK_REQUEST_QUEUE_SIZE 1024
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdEquals(xidQueueElem1->full_xid,
+									 xidQueueElem2->full_xid))
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size == sizeQueueElem2->request_size)
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at == errQueueElem2->err_occurred_at)
+		return 0;
+	return -1;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+static int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * ROLLBACK_REQUEST_QUEUE_SIZE) + ROLLBACK_REQUEST_QUEUE_SIZE +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo * urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+		{
+			cur_undo_queue++;
+			return false;
+		}
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+static uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogControl *log = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoPersistence persistence;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+	Assert(!TransactionIdIsInProgress(XidFromFullTransactionId(full_xid)));
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding the current undo
+		 * pointer.
+		 */
+		if ((log == NULL) || (UndoRecPtrGetLogNo(urecptr) != log->logno))
+			log = UndoLogGet(UndoRecPtrGetLogNo(urecptr), false);
+		Assert(log != NULL);
+		persistence = log->meta.persistence;
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(log->logno, log->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * If the corresponding undo record got rolled back, we return from
+		 * here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+		next_urecptr = uur->uur_next;
+
+		/*
+		 * Case 1: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			/*
+			 * While fetching the next insert location if the new transaction
+			 * has already started in this log then lets re-fetch the undo
+			 * record.
+			 */
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			if (!UndoRecPtrIsValid(next_insert))
+			{
+				UndoRecordRelease(uur);
+				uur = NULL;
+				continue;
+			}
+
+			/*
+			 * If next_insert location points to the starting location of a
+			 * new page, we should subtract the page header size from the
+			 * insert location.
+			 */
+			if (UndoRecPtrGetPageOffset(next_insert) == UndoLogBlockHeaderSize)
+				next_insert -= UndoLogBlockHeaderSize;
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+
+		/*
+		 * Case 2: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == log->logno)
+		{
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidUndoRecPtr,
+									  InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoLogIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(log->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidUndoRecPtr,
+												InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+
+	return sz;
+}
+
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
+	{
+		if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE ||
+			GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE))
+		{
+			Assert(GetSizeQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(ROLLBACK_REQUEST_QUEUE_SIZE)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Size Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as not in_progress so that undo
+	 * launcher or other undo worker don't remove the entry from queues.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->in_progress = false;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->in_progress = true;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || rh->in_progress)
+					continue;
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->in_progress = true;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself (a) if the entry is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues, (b) if the entry is present, but the size is small enough that
+ * backend can execute by itself and undo worker hasn't started processing it
+ * yet.
+ */
+bool
+RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	bool		request_registered = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * There must be space to accommodate the new request.  See
+	 * UndoRollbackHashTableSize.
+	 */
+	Assert(!RollbackHTIsFull());
+
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr, full_xid);
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	/*
+	 * Backends always register the rollback request in the rollback hash
+	 * table irrespective of whether we push it to undo worker.  This ensures
+	 * that discard worker won't try to process the request on which backend
+	 * is working.  OTOH, discard worker won't add an entry to the hash table
+	 * unless it can push the request to undo worker.  This is because
+	 * otherwise backends might not process the request by themselves even
+	 * though no undo worker is going to process such a request.
+	 */
+	if (can_push ||
+		(!can_push && !IsDiscardProcess()))
+	{
+		RollbackHashKey hkey;
+
+		hkey.full_xid = full_xid;
+		hkey.start_urec_ptr = start_urec_ptr;
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+											   HASH_ENTER_NULL, &found);
+		if (!rh)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+
+		/* We shouldn't try to add the same rollback request again. */
+		if (!found)
+		{
+			rh->start_urec_ptr = start_urec_ptr;
+			rh->end_urec_ptr = end_urec_ptr;
+			rh->dbid = dbid;
+			rh->full_xid = full_xid;
+			rh->in_progress = false;
+
+			if (can_push)
+			{
+				UndoRequestInfo urinfo;
+
+				ResetUndoRequestInfo(&urinfo);
+
+				urinfo.full_xid = rh->full_xid;
+				urinfo.start_urec_ptr = rh->start_urec_ptr;
+				urinfo.end_urec_ptr = rh->end_urec_ptr;
+				urinfo.dbid = rh->dbid;
+				urinfo.request_size = req_size;
+
+				InsertRequestIntoUndoQueues(&urinfo);
+
+				/*
+				 * Indicates that the request will be processed by undo
+				 * worker.
+				 */
+				request_registered = true;
+				pushed = true;
+			}
+			else
+			{
+				/* Indicates that the request can be processed by backend. */
+				request_registered = false;
+			}
+		}
+		else if (!rh->in_progress && !can_push)
+		{
+			/*
+			 * Indicates that the request can be processed by backend. This is
+			 * the case where discard worker would have pushed the request of
+			 * smaller size which backend itself can process. Mark the request
+			 * as in-progress, so that discard worker doesn't try to process
+			 * it.
+			 */
+			rh->in_progress = true;
+			request_registered = false;
+		}
+		else
+		{
+			/* Indicates that the request will be processed by undo worker. */
+			request_registered = true;
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
+	return request_registered;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * To check if the rollback requests in the hash table are all
+ * completed or not. This is required because we don't not want to
+ * expose RollbackHT in xact.c, where it is required to ensure
+ * that we push the resuests only when there is some space in
+ * the hash-table.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..08a5ee2
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,789 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 5;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.
+ */
+#define UNDO_WORKER_LINGER_MS 10000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker * worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo * urinfo);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		CommitTransactionCommand();
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.
+		 */
+		if (InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTRemoveEntry(urinfo->full_xid, urinfo->start_urec_ptr);
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+	}
+	PG_END_TRY();
+	CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	for (;;)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	for (;;)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7855954..d074d1d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14370,6 +14371,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..957f789 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -163,6 +194,79 @@ binaryheap_first(binaryheap *heap)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap.  The caller must ensure that this routine is not used on an empty
+ * heap and is not called with n greater than or equal to the heap size. Always
+ * O(1).
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap.  The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer
+ * to it in O(1) without preserving the heap property.  This is a
+ * convenience to remove elements quickly.  To obtain a valid heap,
+ * one must call binaryheap_build() afterwards.  The caller must ensure
+ * that this routine is not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * binaryheap_remove_first
  *
  * Removes the first (root, topmost) node in the heap and returns a
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f5db5a8..0550d2c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 6b4e8ec..72fa92c 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3595,6 +3595,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 067487f..cd4c26c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		disable_undo_launcher;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -981,6 +985,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (!disable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 905acf2..4658113 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -159,6 +159,10 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
 			break;
+		case RM_UNDOACTION_ID:
+			/* Logical decoding is not yet implemented for undoactions. */
+			Assert(0);
+			break;
 		case RM_NEXT_ID:
 			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
 	}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 7a4f452..d2fe91a 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -152,6 +154,8 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -226,6 +230,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -264,6 +269,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,5 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0da5b19..8347e1a 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUndo, 0);
 }
 
 /*
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index a5950c1..323e1e3 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -121,6 +121,13 @@ bool		allowSystemTableMods = false;
 int			work_mem = 1024;
 int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
+int			rollback_overflow_size = 64;
+
+/*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
 
 /*
  * Primary determinants of sizes of shared-memory structures.
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 4eba98b..27f6c62 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -453,6 +453,20 @@ InitCommunication(void)
 	}
 }
 
+/*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
 
 /*
  * pg_split_opts -- split a string of options and append it to an argv array
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9ba8ed7..6084b0a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1954,6 +1955,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"disable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		},
+		&disable_undo_launcher,
+		false,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2923,6 +2935,16 @@ static struct config_int ConfigureNamesInt[] =
 		5000, 1, INT_MAX,
 		NULL, NULL, NULL
 	},
+	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
 
 	{
 		{"wal_segment_size", PGC_INTERNAL, PRESET_OPTIONS,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 77bb7c2..9779082 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -749,4 +749,10 @@
 # CUSTOMIZED OPTIONS
 #------------------------------------------------------------------------------
 
+# If often there are large transactions requiring rollbacks, then we can push
+# them to undo-workers for better performance. The size specifeid by the
+# parameter below, determines the minimum size of the rollback requests to be
+# sent to the undo-worker.
+#
+#rollback_overflow_size = 64
 # Add settings for extensions here
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 64aafef..467e4e9 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 7f0a179..f079d69 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -29,7 +29,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..396193b 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..e1fb42a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..ef0f6ac 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,26 +25,27 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 592c338..1477ddc 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
@@ -72,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
@@ -105,6 +116,14 @@ FullTransactionIdAdvance(FullTransactionId *dest)
 	(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
 	(int32) ((id1) - (id2)) > 0)
 
+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)			\
+	((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)			\
+	((uint32) ((epochxid) >> 32))
+
 /* ----------
  *		Object ID (OID) zero is InvalidOid.
  *
@@ -225,6 +244,7 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 /* in transam/varsup.c */
 extern FullTransactionId GetNewTransactionId(bool isSubXact);
 extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid);
+extern uint32 GetEpochForXid(TransactionId xid);
 extern FullTransactionId ReadNextFullTransactionId(void);
 extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 					  Oid oldest_datoid);
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index fcd1913..fdf4470 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,10 +14,12 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undorequest.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
 #include "storage/lock.h"
+#include "access/undolog.h"
 
 /*
  * GlobalTransactionData is defined in twophase.c; other places have no
@@ -41,7 +43,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 				TimestampTz prepared_at,
 				Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..652712c
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
index d50085e..2505a3a 100644
--- a/src/include/access/undoinsert.h
+++ b/src/include/access/undoinsert.h
@@ -39,7 +39,7 @@ typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
 extern UndoRecPtr PrepareUndoInsert(UnpackedUndoRecord *, FullTransactionId fxid,
 				  UndoPersistence upersistence,
 				  XLogReaderState *xlog_record);
-extern void InsertPreparedUndo(void);
+extern void InsertPreparedUndo(UndoPersistence upersistence);
 
 extern void RegisterUndoLogBuffers(uint8 first_block_id);
 extern void UndoLogBuffersSetLSN(XLogRecPtr recptr);
@@ -59,6 +59,9 @@ extern void UndoSetPrepareSize(int nrecords);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, UndoRecPtr prevurp,
 					  Buffer buffer,
 					  UndoPersistence upersistence);
+extern void PrepareUpdateUndoActionProgress(XLogReaderState *xlog_record,
+								UndoRecPtr urecptr, int progress);
+extern void UndoRecordUpdateTransInfo(int idx);
 extern void AtAbort_ResetUndoBuffers(void);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 2cfce8c..8259cfe 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -242,6 +242,19 @@ typedef struct UndoLogMetaData
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogControl
 {
@@ -265,6 +278,7 @@ typedef struct UndoLogControl
 	uint32		oldest_xidepoch;
 	UndoRecPtr	oldest_data;
 	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
 	LWLock		rewind_lock;		/* prevent rewinding while reading */
 } UndoLogControl;
 
@@ -401,8 +415,6 @@ extern void UndoLogSetLSN(XLogRecPtr lsn);
 void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
 /* Redo interface. */
 extern void undolog_redo(XLogReaderState *record);
-/* Discard the undo logs for temp tables */
-extern void TempUndoDiscard(UndoLogNumber);
 extern Oid UndoLogStateGetDatabaseId(void);
 
 /* Test-only interfacing. */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..c6df454
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,138 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoinsert.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+/* Remembers the last seen RecentGlobalXmin */
+TransactionId latestRecentGlobalXmin;
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId		full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	Oid			dbid;
+	bool		in_progress;	/* indicates that undo actions are being processed */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId		full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+	uint64			request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId			full_xid;
+	UndoRecPtr		start_urec_ptr;
+	Oid				dbid;
+	TimestampTz		err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId				full_xid;
+	UndoRecPtr			start_urec_ptr;
+	UndoRecPtr			end_urec_ptr;
+	Oid					dbid;
+	uint64			request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+						UndoRequestInfo *urinfo, bool *in_other_db);
+/* Exposed functions for rollback hash table. */
+extern bool RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+				Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr);
+extern bool RollbackHTIsFull(void);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..e2a61ff
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 61df235..ca7924e 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -22,6 +22,7 @@
 #include "storage/relfilenode.h"
 #include "storage/sinval.h"
 #include "utils/datetime.h"
+#include "utils/resowner.h"
 
 /*
  * Maximum size of Global Transaction ID (including '\0').
@@ -429,6 +430,11 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 				   const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
 
+extern void ApplyUndoActions(void);
+extern void SetUndoActionsInfo(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
 extern const char *xact_identify(uint8 info);
@@ -440,5 +446,7 @@ extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_ab
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
 extern bool IsInParallelMode(void);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+					UndoPersistence upersistence);
 
 #endif							/* XACT_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 8b1348c..04440ed 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -21,8 +21,11 @@
 
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
+#include "access/undorecord.h"
+#include "access/undorequest.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -295,9 +298,13 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	bool		(*rm_undo) (UndoRecInfo *urp_array, int first_idx, int last_idx,
+							Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+							bool blk_chain_complete);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 9375e54..08584fc 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -13,6 +13,7 @@
 
 #include "access/rmgr.h"
 #include "access/xlogdefs.h"
+#include "access/transam.h"
 #include "port/pg_crc32c.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..15fc018 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,13 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest transaction id with epoc which is having undo. Include this
+	 * value in the checkpoint record so that whenever server starts we get
+	 * proper value.
+	 */
+	FullTransactionId		oldestFullXidHavingUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 92dab66..c6fa89b 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -40,6 +40,9 @@ typedef struct binaryheap
 extern binaryheap *binaryheap_allocate(int capacity,
 					binaryheap_comparator compare,
 					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Size binaryheap_shmem_size(int capacity);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index b677c7e..acc7323 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -170,6 +170,8 @@ extern PGDLLIMPORT struct Latch *MyLatch;
 extern int32 MyCancelKey;
 extern int	MyPMChildSlot;
 
+extern bool applying_subxact_undo;
+
 extern char OutputFileName[];
 extern PGDLLIMPORT char my_exec_path[];
 extern char pkglib_path[];
@@ -245,6 +247,7 @@ extern PGDLLIMPORT bool allowSystemTableMods;
 extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
+extern PGDLLIMPORT int rollback_overflow_size;
 
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
@@ -422,6 +425,7 @@ extern AuxProcType MyAuxProcType;
  *****************************************************************************/
 
 /* in utils/init/postinit.c */
+extern bool dbid_exists(Oid dboid);
 extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f9b1cf2..3cda35d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 7cd71bd..013c0eb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -781,7 +781,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 8ccd2af..6212a18 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,8 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool disable_undo_launcher;
+
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 048947c..a146bf6 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -152,7 +152,6 @@ typedef enum LockTagType
 	LOCKTAG_SPECULATIVE_TOKEN,	/* speculative insertion Xid and token */
 	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_OBJECT,				/* non-relation database object */
-	/* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
 
 	/*
 	 * Note: object ID has same representation as in pg_depend and
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..118ff6b 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -222,6 +222,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SXACT,
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_REWIND,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1cee7db..c6b8c5d 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index bd24850..8aa3249 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7ac37fd..ce302bb 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
-- 
1.8.3.1

#24Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#1)
3 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Apr 30, 2019 at 8:44 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 30, 2019 at 2:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Like previous version these patch set also applies on:
https://github.com/EnterpriseDB/zheap/tree/undo
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

Some more review of 0003:

There is a whitespace-only hunk in xact.c.

Fixed

It would be nice if AtAbort_ResetUndoBuffers didn't need to exist at
all. Then, this patch would make no changes whatsoever to xact.c.
We'd still need such changes in other patches, because the whole idea
of undo is tightly bound up with the concept of transactions. Yet,
this particular patch wouldn't touch that file, and that would be
nice. In general, the reason why we need AtCommit/AtAbort/AtEOXact
callbacks is to adjust the values of global variables (or the data
structures to which they point) at commit or abort time. And that is
also the case here. The thing is, I'm not sure why we need these
particular global variables. Is there some way that we can get by
without them? The obvious thing to do seems to be to invent yet
another context object, allocated via a new function, which can then
be passed to PrepareUndoInsert, InsertPreparedUndo,
UndoLogBuffersSetLSN, UnlockReleaseUndoBuffers, etc. This would
obsolete UndoSetPrepareSize, since you'd instead pass the size to the
context allocator function.

I have moved all the global variables to a context. Now, I think we
don't need AtAbort_ResetUndoBuffers as memory will be freed with the
transaction context.

UndoRecordUpdateTransInfo should declare a local variable
XactUndoRecordInfo *something = &xact_urec_info[idx] and use that
variable wherever possible.

Done.

It should also probably use while (1) { ... } rather than do { ... }
while (true).

Ok

In UndoBufferGetSlot you could replace 'break;' with 'return i;' and
then more than half the function would need one less level of
indentation. This function should also declare PreparedUndoBuffer
*something and set that variable equal to &prepared_undo_buffers[i] at
the top of the loop and again after the loop, and that variable should
then be used whenever possible.

Done

UndoRecordRelationDetails seems to need renaming now that it's down to
a single member.

I have directly moved that context to UndoPackContext

The comment for UndoRecordBlock needs updating, as it still refers to blkprev.

Done

The comment for UndoRecordBlockPrev refers to "Identifying
information" but it's not identifying anything. I think you could
just delete "Identifying information for" from this sentence and not
lose anything. And I think several of the nearby comments that refer
to "Identifying information" could more simply and more correctly just
refer to "Information".

Done

I don't think that SizeOfUrecNext needs to exist.

Removed

xact.h should not add an include for undolog.h. There are no other
changes to this header, so presumably the header does not depend on
anything in undolog.h. If .c files that include xact.h need
undolog.h, then the header should be included in those files, not in
the header itself. That way, we avoid making partial recompiles more
painful than necessary.

Right, fixed.

UndoGetPrevUndoRecptr looks like a strange interface. Why not just
arrange not to call the function in the first place if prevurp is
valid?

Done

Every use of palloc0 in this code should be audited to check whether
it is really necessary to zero the memory before use. If it will be
initialized before it's used for anything anyway, it doesn't need to
be pre-zeroed.

Yeah, I found at few places it was not required so fixed.

FinishUnpackUndo looks nice! But it is missing a blank line in one
place, and 'if it presents' should be 'if it is present' in a whole
bunch of places.

BeginInsertUndo also looks to be missing a few blank lines.

Fixed

Still need to do some cleanup of the UnpackedUndoRecord, e.g. unifying
uur_xid and uur_xidepoch into uur_fxid.

I will work on this.

InsertUndoData ends in an unnecessary return, as does SkipInsertingUndoData.

Done

I think the argument to SkipInsertingUndoData should be the number of
bytes to skip, rather than the starting byte within the block.

Done

Function header comment formatting is not consistent. Compare
FinishUnpackUndo (function name recapitulated on first line of
comment) to ReadUndoBytes (not recapitulated) to UnpackUndoData
(entire header comment jammed into one paragraph with function name at
start). I prefer the style where the function name is not restated,
but YMMV. Anyway, it has to be consistent.

Fixed

UndoGetPrevRecordLen should not declare char *page instead of Page
page, I think.

UndoRecInfo looks a bit silly, I think. Isn't index just the index of
this entry in the array? You can always figure that out by ptr -
array_base. Instead of having UndoRecPtr urp in this structure, how
about adding that to UnpackedUndoRecord? When inserting, caller
leaves it unset and InsertPreparedUndo sets it; when retrieving,
UndoFetchRecord or UndoRecordBulkFetch sets it. With those two
changes, I think you can get rid of UndoRecInfo entirely and just
return an array of UnpackedUndoRecords. This might also eliminate the
need for an 'urp' member in PreparedUndoSpace.

As discussed upthread, I will work on fixing this.

I'd probably use UREC_INFO_BLKPREV rather than UREC_INFO_BLOCKPREV for
consistency.

Similarly UndoFetchRecord and UndoRecordBulkFetch seems a bit
inconsistent. Perhaps UndoBulkFetchRecord.

Done

In general, I find the code for updating transaction headers to be
really hard to understand. I'm not sure exactly what can be done
about that. Like, why is UndoRecordPrepareTransInfo unpacking undo?

It's only unpacking header. But, yeah we can do better, instead of
unpacking we can just read the main header and from uur_info we can
calculate exact offset of the uur_next and in
UndoRecordUpdateTransInfo we can directly update only uur_next by
writing at that offset, instead of overwriting the complete header?

Why does it take two undo record pointers as arguments and how are
they different?

One is previous transaction's start header which we wants to update
and other is current transaction's urec pointer what we want to set as
uur_next in the previous transaction's start header.

Just for tracking, open comments which still needs to be worked on.

1. Avoid special case in UndoRecordIsValid.

Can we instead eliminate the special case? It seems like the if
(log->oldest_data == InvalidUndoRecPtr) case will be taken very
rarely, so if it's buggy, we might not notice.

2. While updating the previous transaction header instead of unpacking
complete header and writing it back, we can just unpack main header
and calculate the offset of uur_next and then update it directly.

3. unifying uur_xid and uur_xidepoch into uur_fxid.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0005-undo-page-consistency-checker_v4_WIP.patchapplication/octet-stream; name=0005-undo-page-consistency-checker_v4_WIP.patchDownload
From 90ff3600e1d0a6c9834aaf2648fccd8b3aa43f73 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Mon, 6 May 2019 15:03:15 +0530
Subject: [PATCH 3/3] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar based on initial version from Amit Khandekar and Rafia Sabih and design input from Amit Kapila
---
 src/backend/access/undo/undoinsert.c |   5 +-
 src/backend/access/undo/undorecord.c | 136 ++++++++++++++++++++++++++++++++---
 src/backend/storage/page/bufpage.c   |  33 +++++++++
 src/include/access/undolog.h         |   2 +-
 src/include/access/undorecord.h      |   2 +-
 src/include/storage/bufpage.h        |  34 +++++++++
 6 files changed, 201 insertions(+), 11 deletions(-)

diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
index d1625e4..d03846f 100644
--- a/src/backend/access/undo/undoinsert.c
+++ b/src/backend/access/undo/undoinsert.c
@@ -710,7 +710,10 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 				 * block header.
 				 */
 				if (starting_byte == UndoLogBlockHeaderSize)
-					PageInit(page, BLCKSZ, 0);
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
 
 				/*
 				 * Try to insert the record into the current page. If it
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index 7e585ac..af25700 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 
+#include "access/bufmask.h"
 #include "access/subtrans.h"
 #include "access/undorecord.h"
 #include "catalog/pg_tablespace.h"
@@ -26,25 +27,41 @@ static bool ReadUndoBytes(char *destptr, int readlen,
 			  int *total_bytes_read, int *partial_read);
 
 /*
- * Compute and return the expected size of an undo record.
+ * Compute the header size of the undo record.
  */
-Size
-UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+static inline Size
+UndoRecordHeaderSize(uint8 uur_info)
 {
 	Size		size;
 
 	size = SizeOfUndoRecordHeader + sizeof(uint16);
-	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+	if ((uur_info & UREC_INFO_FORK) != 0)
 		size += sizeof(ForkNumber);
-	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
 		size += SizeOfUndoRecordBlock;
-	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	if ((uur_info & UREC_INFO_BLKPREV) != 0)
 		size += sizeof(UndoRecPtr);
-	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
 		size += SizeOfUndoRecordTransaction;
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
 	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
 	{
-		size += SizeOfUndoRecordPayload;
 		size += uur->uur_payload.len;
 		size += uur->uur_tuple.len;
 	}
@@ -53,6 +70,30 @@ UndoRecordExpectedSize(UnpackedUndoRecord *uur)
 }
 
 /*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint8           uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size            size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload  *payload = (UndoRecordPayload *) page_ptr + size;
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
  * Compute size of the Unpacked undo record in memory
  */
 Size
@@ -72,6 +113,85 @@ UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
 	return size;
 }
 
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset = SizeOfUndoRecordHeader - sizeof(CommandId);
+	UndoPageHeader	phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page has
+	 * a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size	partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						   phdr->tuple_len + phdr->payload_len -
+						   phdr->record_offset;
+
+		/*
+		 * We just want to mask the cid in the undo record header.  So only if
+		 * the partial record in the current page include the undo record header
+		 * then we need to mask the cid bytes in this page.  Otherwise, directly
+		 * jump to the next record.
+		 */
+		if (phdr->record_offset < SizeOfUndoRecordHeader)
+		{
+			char   *cid_data;
+			Size	mask_size;
+
+			mask_size = Min(SizeOfUndoRecordHeader -
+							phdr->record_offset, sizeof(CommandId));
+
+			cid_data = next_record + cid_offset - phdr->record_offset;
+			memset(&cid_data, MASK_MARKER, mask_size);
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/*
+		 * If this is not complete record then check whether cid is on
+		 * this page or not.  If not then we are done with this page.
+		 */
+		if (page_end - next_record < SizeOfUndoRecordHeader)
+		{
+			int		mask_size = page_end - next_record - cid_offset;
+
+			if (mask_size > 0)
+				memset(&header->urec_cid, MASK_MARKER, mask_size);
+			break;
+		}
+		else
+		{
+			/* Mask cid */
+			memset(&header->urec_cid, MASK_MARKER, sizeof(header->urec_cid));
+		}
+
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
+
 /*
  * Initiate inserting an undo record.
  *
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 14bc61b..8519fd7 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,39 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint8 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	/*
+	 * TODO: We can update the value of the p->pd_lower whenever we insert
+	 * a record into an undo page.  By doing this we can avoid processing
+	 * complete undo page if there are no more records.
+	 */
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index f421bae..220d480 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -128,7 +128,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index a4758ed..aa29053 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -20,7 +20,6 @@
 #include "storage/buf.h"
 #include "storage/off.h"
 
-
 /*
  * Every undo record begins with an UndoRecordHeader structure, which is
  * followed by the additional structures indicated by the contents of
@@ -224,5 +223,6 @@ extern void InsertUndoData(UndoPackContext *ucontext, Page page,
 			   int starting_byte);
 extern void SkipInsertingUndoData(UndoPackContext *ucontext,
 					  int bytes_to_skip);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 85cd0ab..5be7d1e 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -212,6 +212,37 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * FIXME:  It should be declared in undolog.h ?
+ *
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+	/* Fields required for undolog consistency checker */
+	uint8		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -415,6 +446,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint8 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 					OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

0004-Test-module-for-undo-api_v4.patchapplication/octet-stream; name=0004-Test-module-for-undo-api_v4.patchDownload
From bf58a59291086f7c267e42c20939c646605bb7e1 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Mon, 6 May 2019 14:31:13 +0530
Subject: [PATCH 2/3] Test module for undo api

Basic test routine to test undo interface API.

Dilip Kumar
---
 src/test/modules/test_undo_api/Makefile            |  21 +++
 .../test_undo_api/expected/test_undo_api.out       |  12 ++
 .../modules/test_undo_api/sql/test_undo_api.sql    |   8 +
 .../modules/test_undo_api/test_undo_api--1.0.sql   |   6 +
 src/test/modules/test_undo_api/test_undo_api.c     | 185 +++++++++++++++++++++
 .../modules/test_undo_api/test_undo_api.control    |   4 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/test/modules/test_undo_api/Makefile
 create mode 100644 src/test/modules/test_undo_api/expected/test_undo_api.out
 create mode 100644 src/test/modules/test_undo_api/sql/test_undo_api.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api--1.0.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.c
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.control

diff --git a/src/test/modules/test_undo_api/Makefile b/src/test/modules/test_undo_api/Makefile
new file mode 100644
index 0000000..deb3816
--- /dev/null
+++ b/src/test/modules/test_undo_api/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo/Makefile
+
+MODULE_big = test_undo_api
+OBJS = test_undo_api.o
+PGFILEDESC = "test_undo_api - a test module for the undo api layer"
+
+EXTENSION = test_undo_api
+DATA = test_undo_api--1.0.sql
+
+REGRESS = test_undo_api
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_api
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_api/expected/test_undo_api.out b/src/test/modules/test_undo_api/expected/test_undo_api.out
new file mode 100644
index 0000000..5496ddb
--- /dev/null
+++ b/src/test/modules/test_undo_api/expected/test_undo_api.out
@@ -0,0 +1,12 @@
+CREATE EXTENSION test_undo_api;
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
+ test_undo_api 
+---------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_undo_api/sql/test_undo_api.sql b/src/test/modules/test_undo_api/sql/test_undo_api.sql
new file mode 100644
index 0000000..fa6f789
--- /dev/null
+++ b/src/test/modules/test_undo_api/sql/test_undo_api.sql
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_undo_api;
+
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
diff --git a/src/test/modules/test_undo_api/test_undo_api--1.0.sql b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
new file mode 100644
index 0000000..1aa4e02
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
@@ -0,0 +1,6 @@
+\echo Use "CREATE EXTENSION test_undo_api" to load this file. \quit
+
+CREATE FUNCTION test_undo_api(persistence text)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_undo_api/test_undo_api.c b/src/test/modules/test_undo_api/test_undo_api.c
new file mode 100644
index 0000000..24e7431
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.c
@@ -0,0 +1,185 @@
+#include "postgres.h"
+
+#include "access/transam.h"
+#include "access/undoinsert.h"
+#include "catalog/pg_class.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_undo_api);
+
+static void
+compare_undo_record(UnpackedUndoRecord *urp1, UnpackedUndoRecord *urp2)
+{
+	int	header_size = offsetof(UnpackedUndoRecord, uur_next) + sizeof(uint64);
+
+	/* Compare undo record header. */
+	if (strncmp((char *) urp1, (char *) urp2, header_size) != 0)
+		elog(ERROR, "undo header did not match");
+
+	/* Compare payload and tuple length. */
+	if (urp1->uur_payload.len != urp2->uur_payload.len)
+		elog(ERROR, "payload data length did not match");
+
+	if (urp1->uur_tuple.len != urp2->uur_tuple.len)
+		elog(ERROR, "tuple data length did not match");
+
+	/* Compare undo record payload data. */
+	if (strncmp(urp1->uur_payload.data, urp2->uur_payload.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo payload data did not match");
+
+	/* Compare undo record tuple data. */
+	if (strncmp(urp1->uur_tuple.data, urp2->uur_tuple.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo tuple data did not match");
+}
+
+/*
+ * test_insert_and_fetch - test simple insert and fetch undo record API
+ */
+static void
+test_insert_and_fetch()
+{
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	char	data[5000];
+	int		 len = 5000;
+	UnpackedUndoRecord	undorecord = {0};
+	UnpackedUndoRecord *undorecord_out;
+	UndoRecPtr	undo_ptr;
+
+	/* Prepare dummy undo record*/
+	undorecord.uur_rmid = 1;
+	undorecord.uur_type = 2;
+	undorecord.uur_info = 0;
+	undorecord.uur_xid = 100;
+	undorecord.uur_cid = 1;
+	undorecord.uur_fork = MAIN_FORKNUM;
+	undorecord.uur_blkprev = 10;
+	undorecord.uur_block = 1;
+	undorecord.uur_offset = 10;
+
+	/* Insert large data so that record get split across pages. */
+	initStringInfo(&undorecord.uur_tuple);
+	memset(data, 'a', 5000);
+	appendBinaryStringInfo(&undorecord.uur_tuple,
+						   (char *) data,
+						   len);
+	initStringInfo(&undorecord.uur_payload);
+	appendBinaryStringInfo(&undorecord.uur_payload,
+						   (char *) data,
+						   len);
+	/* Prepare undo record. */
+	BeginUndoRecordInsert(&context, persistence, 2, NULL);
+	undo_ptr = PrepareUndoInsert(&context, &undorecord, InvalidFullTransactionId);
+
+	/* Insert prepared undo record under critical section. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	/* Fetch inserted undo record. */
+	undorecord_out = UndoFetchRecord(undo_ptr, InvalidBlockNumber,
+									 InvalidOffsetNumber,
+									 InvalidTransactionId, NULL,
+									 NULL);
+	/* compare undo records. */
+	compare_undo_record(&undorecord, undorecord_out);
+
+	UndoRecordRelease(undorecord_out);
+	pfree(undorecord.uur_tuple.data);
+}
+
+#define MAX_UNDO_RECORD 10
+/*
+ * test_bulk_fetch - test the bulk fetch API.
+ */
+static void
+test_bulk_fetch()
+{
+	int i;
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	UndoRecInfo	urp_in_array[MAX_UNDO_RECORD];
+	UndoRecInfo *urp_out_array;
+	UnpackedUndoRecord	uur[MAX_UNDO_RECORD] = {{0}};
+	UndoRecPtr	undo_ptr;
+	int			nrecords = 0;
+
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		uur[i].uur_rmid = 1;
+		uur[i].uur_type = 2;
+		uur[i].uur_info = 0;
+		uur[i].uur_xid = 100;
+		uur[i].uur_cid = 1;
+		uur[i].uur_fork = MAIN_FORKNUM;
+		uur[i].uur_blkprev = 10;
+		uur[i].uur_block = i;
+		uur[i].uur_offset = i + 1;
+		urp_in_array[i].uur = &uur[i];
+	}
+
+	/* Prepare multiple undo records. */
+	BeginUndoRecordInsert(&context, persistence, MAX_UNDO_RECORD, NULL);
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		undo_ptr = PrepareUndoInsert(&context, &uur[i], InvalidFullTransactionId);
+		urp_in_array[i].urp = undo_ptr;
+	}
+
+	/* Insert them all in one shot. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	undo_ptr = urp_in_array[MAX_UNDO_RECORD - 1].urp;
+
+	/*
+	 * Perform the bulk fetch. 2000 bytes are enough to hold 10 records.  Later
+	 * we can enhance this to test the fetch in multi batch by increasing the
+	 * record counts or reducing undo_apply_size to smaller value.
+	 */
+	urp_out_array = UndoBulkFetchRecord(&undo_ptr, urp_in_array[0].urp, 2000,
+										&nrecords, false);
+	/* Check whether we have got all the record we inserted. */
+	if (nrecords != MAX_UNDO_RECORD)
+		elog(ERROR, "undo record count did not match");
+
+	/* Compare all records we have fetch using bulk fetch API*/
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		if (urp_in_array[i].urp != urp_out_array[MAX_UNDO_RECORD - 1 - i].urp)
+			elog(ERROR, "undo record pointer did not match");
+		compare_undo_record(urp_in_array[i].uur, urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+		UndoRecordRelease(urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+	}
+}
+/*
+ * Undo API test module
+ */
+Datum
+test_undo_api(PG_FUNCTION_ARGS)
+{
+	/* Test simple insert and fetch record. */
+	test_insert_and_fetch();
+
+	/* Test undo record bulk fetch API*/
+	test_bulk_fetch();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_undo_api/test_undo_api.control b/src/test/modules/test_undo_api/test_undo_api.control
new file mode 100644
index 0000000..09df344
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.control
@@ -0,0 +1,4 @@
+comment = 'test_undo_api'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_api'
+relocatable = true
-- 
1.8.3.1

0003-Provide-interfaces-to-store-and-fetch-undo-records_v4.patchapplication/octet-stream; name=0003-Provide-interfaces-to-store-and-fetch-undo-records_v4.patchDownload
From 7684ff1ed9ae1bf528bcff2d249e1913e9f8f853 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 1/3] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile     |    2 +-
 src/backend/access/undo/undoinsert.c | 1415 ++++++++++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c |  744 ++++++++++++++++++
 src/include/access/transam.h         |    1 +
 src/include/access/undoinsert.h      |  151 ++++
 src/include/access/undorecord.h      |  228 ++++++
 6 files changed, 2540 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/undoinsert.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoinsert.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
new file mode 100644
index 0000000..d1625e4
--- /dev/null
+++ b/src/backend/access/undo/undoinsert.c
@@ -0,0 +1,1415 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.c
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoinsert.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the previous log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the last undo record of
+ * the transaction in the previous log, so that we can find the previous undo
+ * record pointer during rollback.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoinsert.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Default prepared undo space */
+static PreparedUndoSpace def_prepared_undo[MAX_PREPARED_UNDO];
+
+/*
+ * Default undo buffers for prepared undo space and updating the previous
+ * transaction's transaction info.
+ */
+static PreparedUndoBuffer def_prepared_undo_buffers[MAX_UNDO_BUFFERS];
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static void UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static bool UndoRecordIsValid(UndoLogControl *log,
+				  UndoRecPtr urp);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Check whether the undo record is discarded or not.
+ *
+ * If it's already discarded return false otherwise return true.
+ */
+static bool
+UndoRecordIsValid(UndoLogControl *log, UndoRecPtr urp)
+{
+	Assert(LWLockHeldByMeInMode(&log->discard_lock, LW_SHARED));
+
+	/*
+	 * oldest_data is only initialized when the DiscardWorker first time
+	 * attempts to discard undo logs so we can not rely on this value to
+	 * identify whether the undo record pointer is already discarded or not so
+	 * we can check it by calling undo log routine.
+	 */
+	if (log->oldest_data == InvalidUndoRecPtr)
+	{
+		if (UndoLogIsDiscarded(urp))
+			return false;
+	}
+	else if (urp < log->oldest_data)
+		return false;
+
+	return true;
+}
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * This will read the header of the first undo record of the previous
+ * transaction and lock the necessary buffers.  The actual update will be done
+ * by UndoRecordUpdateTransInfo under the critical section.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ *
+ * TODO:  Instead of reading the record can we just lock the buffers and
+ * directly update the location in UndoRecordUpdateTransInfo?  But we need to
+ * read at least the main header to know what is exact offset of the transaction
+ * header.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	Page		page;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info =
+	&context->xact_urec_info[context->nxact_urec_info];
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before accessing the undo record so that
+	 * discard worker doesn't remove the record while we are in process of
+	 * reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 * UndoRecordIsValid will release the lock if it returns false.
+	 */
+	if (!UndoRecordIsValid(log, xact_urp))
+	{
+		LWLockRelease(&log->discard_lock);
+		return;
+	}
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (1)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		buffer = context->prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_info->uur);
+
+	/*
+	 * Store undo record pointer of the previous transaction header in xact
+	 * info and also update the previous transaction's header with the current
+	 * transaction's undo record pointer.  Actual, write will be done by
+	 * UndoRecordUpdateTransInfo.
+	 */
+	xact_info->uur.uur_next = urecptr;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	LWLockRelease(&log->discard_lock);
+}
+
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			i = 0;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(xact_info->urecptr);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, &xact_info->uur);
+
+	/* Main loop for updating the undo record. */
+	while (1)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * removed by discard process, so we can just skip inserting into
+		 * those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * Skip actual writing just update the context so that we have
+			 * write offset for inserting into next blocks.
+			 */
+			SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+				break;
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/* Overwrite the previously written undo record. */
+			InsertUndoData(&ucontext, page, starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int max_prepared,
+					  XLogReaderState *xlog_record)
+{
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = max_prepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/*
+	 * If max prepared count within the default number than point them to
+	 * default static memory.  Otherwise, allocate separate memory for
+	 * prepared undo and undo buffer information.
+	 */
+	if (max_prepared <= MAX_PREPARED_UNDO)
+	{
+		context->prepared_undo = def_prepared_undo;
+		context->prepared_undo_buffers = def_prepared_undo_buffers;
+	}
+	else
+	{
+		context->prepared_undo = (PreparedUndoSpace *) palloc(max_prepared *
+															  sizeof(PreparedUndoSpace));
+		context->prepared_undo_buffers = palloc((max_prepared + 1) *
+												MAX_BUFFER_PER_UNDO * sizeof(PreparedUndoBuffer));
+	}
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+			/* Compute the last record's undo record pointer. */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(context, urecptr, prevlog_xact_start);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be atleast one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+									prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/*
+	 * If we have allocated memory for prepare undo and prepared buffers then
+	 * release the memory.
+	 */
+	if (context->max_prepared_undo > MAX_PREPARED_UNDO)
+	{
+		pfree(context->prepared_undo_buffers);
+		pfree(context->prepared_undo);
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 */
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (!UndoRecordIsValid(log, urp))
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			LWLockRelease(&log->discard_lock);
+			return NULL;
+		}
+
+		/* Fetch the current undo record. */
+		urec = UndoGetOneRecord(urec, urp, rnode, log->meta.persistence,
+								&buffer);
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	if (urec_ptr_out)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogControl *log;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = log->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+			if (!UndoRecordIsValid(log, urecptr))
+			{
+				LWLockRelease(&log->discard_lock);
+				break;
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+			LWLockRelease(&log->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit then
+		 * stop processing more records.  Remember to set the from_urecptr so
+		 * that on next call we can resume fetching undo records where we left
+		 * it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..7e585ac
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,744 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_prevurp = ucontext->urec_txn.urec_prevurp;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
new file mode 100644
index 0000000..9ab39fc
--- /dev/null
+++ b/src/include/access/undoinsert.h
@@ -0,0 +1,151 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoinsert.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOINSERT_H
+#define UNDOINSERT_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * This defines the number of undo records that can be prepared before
+ * calling insert by default.  If you need to prepare more than
+ * MAX_PREPARED_UNDO undo records, then you must call UndoSetPrepareSize
+ * first.
+ */
+#define MAX_PREPARED_UNDO 2
+
+/*
+ * This defines the max number of previous xact infos we need to update.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous
+ * transaction in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+/*
+ * Consider buffers needed for updating previous transaction's
+ * starting undo record as well.
+ */
+#define MAX_UNDO_BUFFERS       (MAX_PREPARED_UNDO + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+typedef struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+} PreparedUndoSpace;
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+typedef struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+} PreparedUndoBuffer;
+
+/*
+ * Structure to hold the previous transaction's undo update information.  During
+ * prepare undo we will populate this to hold the information for updating the
+ * previous transaction's undo record header.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int max_prepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern void UnlockReleaseUndoBuffers(void);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern void UndoRecordSetPrevUndoLen(uint16 len);
+extern void UndoSetPrepareSize(int nrecords);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..a4758ed
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,228 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_FORK						0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_BLKPREV					0x04
+#define UREC_INFO_TRANSACTION				0x08
+#define UREC_INFO_PAYLOAD					0x10
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

#25Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#24)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, May 6, 2019 at 8:13 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

In general, I find the code for updating transaction headers to be
really hard to understand. I'm not sure exactly what can be done
about that. Like, why is UndoRecordPrepareTransInfo unpacking undo?

It's only unpacking header. But, yeah we can do better, instead of
unpacking we can just read the main header and from uur_info we can
calculate exact offset of the uur_next and in
UndoRecordUpdateTransInfo we can directly update only uur_next by
writing at that offset, instead of overwriting the complete header?

Hmm. I think it's reasonable to use the unpack infrastructure to
figure out where uur_next is. I don't know whether a bespoke method
of figuring that out would be any better. At least the comments
probably need some work.

Why does it take two undo record pointers as arguments and how are
they different?

One is previous transaction's start header which we wants to update
and other is current transaction's urec pointer what we want to set as
uur_next in the previous transaction's start header.

So put some comments.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#26Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#16)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 1, 2019 at 9:29 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, May 1, 2019 at 6:02 AM Robert Haas <robertmhaas@gmail.com> wrote:

Replying to myself to resend to the list, since my previous attempt
seems to have been eaten by a grue.

On Tue, Apr 30, 2019 at 11:14 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 30, 2019 at 2:16 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Like previous version these patch set also applies on:
https://github.com/EnterpriseDB/zheap/tree/undo
(b397d96176879ed5b09cf7322b8d6f2edd8043a5)

Some more review of 0003:

Another suggestion:

+/*
+ * Insert a previously-prepared undo records.  This will write the actual undo
+ * record into the buffers already pinned and locked in PreparedUndoInsert,
+ * and mark them dirty.  This step should be performed after entering a
+ * criticalsection; it should never fail.
+ */
+void
+InsertPreparedUndo(void)
+{
..
..
+
+ /* Advance the insert pointer past this record. */
+ UndoLogAdvance(urp, size);
+ }
..
}

UndoLogAdvance internally takes LWLock and we don't recommend doing
that in the critical section which will happen as this function is
supposed to be invoked in the critical section as mentioned in
comments.

I think we can call UndoLogAdvanceFinal in FinishUndoRecordInsert
because this function will be called outside the critical section.
And, now we already have the undo record size inside
UndoRecordInsertContext.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#27Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#24)
4 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, May 6, 2019 at 5:43 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Just for tracking, open comments which still needs to be worked on.

1. Avoid special case in UndoRecordIsValid.

Can we instead eliminate the special case? It seems like the if
(log->oldest_data == InvalidUndoRecPtr) case will be taken very
rarely, so if it's buggy, we might not notice.

I have worked on this comments and added changes in the latest patch.

2. While updating the previous transaction header instead of unpacking
complete header and writing it back, we can just unpack main header
and calculate the offset of uur_next and then update it directly.

For this as you suggested I am not changing, updated the comments.

3. unifying uur_xid and uur_xidepoch into uur_fxid.

Still open.

I have also added the README.

Patches can be applied on top of undo branch [1]https://github.com/EnterpriseDB/zheap/tree/undo commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

[1]: https://github.com/EnterpriseDB/zheap/tree/undo

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Update-oldest_data-on-startup_v5.patchapplication/octet-stream; name=0001-Update-oldest_data-on-startup_v5.patchDownload
From 64c21a48ca4b6de91596ce19123f93f5184bda1c Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 8 May 2019 17:16:47 +0530
Subject: [PATCH 1/4] Update oldest_data on startup

---
 src/backend/access/undo/undolog.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 9d49d04..956f277 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -1591,6 +1591,7 @@ StartupUndoLogs(XLogRecPtr checkPointRedo)
 		 */
 		log->logno = log->meta.logno;
 		log->pid = InvalidPid;
+		log->oldest_data = MakeUndoRecPtr(log->logno, log->meta.discard);
 		if (log->meta.status == UNDO_LOG_STATUS_ACTIVE)
 		{
 			log->next_free = shared->free_lists[log->meta.persistence];
-- 
1.8.3.1

0003-Test-module-for-undo-api_v5.patchapplication/octet-stream; name=0003-Test-module-for-undo-api_v5.patchDownload
From 919f2bb25035c6add73cc2540bd15734ac4a93f6 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Mon, 6 May 2019 14:31:13 +0530
Subject: [PATCH 3/4] Test module for undo api

Basic test routine to test undo interface API.

Dilip Kumar
---
 src/test/modules/test_undo_api/Makefile            |  21 +++
 .../test_undo_api/expected/test_undo_api.out       |  12 ++
 .../modules/test_undo_api/sql/test_undo_api.sql    |   8 +
 .../modules/test_undo_api/test_undo_api--1.0.sql   |   6 +
 src/test/modules/test_undo_api/test_undo_api.c     | 185 +++++++++++++++++++++
 .../modules/test_undo_api/test_undo_api.control    |   4 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/test/modules/test_undo_api/Makefile
 create mode 100644 src/test/modules/test_undo_api/expected/test_undo_api.out
 create mode 100644 src/test/modules/test_undo_api/sql/test_undo_api.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api--1.0.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.c
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.control

diff --git a/src/test/modules/test_undo_api/Makefile b/src/test/modules/test_undo_api/Makefile
new file mode 100644
index 0000000..deb3816
--- /dev/null
+++ b/src/test/modules/test_undo_api/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo/Makefile
+
+MODULE_big = test_undo_api
+OBJS = test_undo_api.o
+PGFILEDESC = "test_undo_api - a test module for the undo api layer"
+
+EXTENSION = test_undo_api
+DATA = test_undo_api--1.0.sql
+
+REGRESS = test_undo_api
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_api
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_api/expected/test_undo_api.out b/src/test/modules/test_undo_api/expected/test_undo_api.out
new file mode 100644
index 0000000..5496ddb
--- /dev/null
+++ b/src/test/modules/test_undo_api/expected/test_undo_api.out
@@ -0,0 +1,12 @@
+CREATE EXTENSION test_undo_api;
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
+ test_undo_api 
+---------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_undo_api/sql/test_undo_api.sql b/src/test/modules/test_undo_api/sql/test_undo_api.sql
new file mode 100644
index 0000000..fa6f789
--- /dev/null
+++ b/src/test/modules/test_undo_api/sql/test_undo_api.sql
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_undo_api;
+
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
diff --git a/src/test/modules/test_undo_api/test_undo_api--1.0.sql b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
new file mode 100644
index 0000000..1aa4e02
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
@@ -0,0 +1,6 @@
+\echo Use "CREATE EXTENSION test_undo_api" to load this file. \quit
+
+CREATE FUNCTION test_undo_api(persistence text)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_undo_api/test_undo_api.c b/src/test/modules/test_undo_api/test_undo_api.c
new file mode 100644
index 0000000..24e7431
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.c
@@ -0,0 +1,185 @@
+#include "postgres.h"
+
+#include "access/transam.h"
+#include "access/undoinsert.h"
+#include "catalog/pg_class.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_undo_api);
+
+static void
+compare_undo_record(UnpackedUndoRecord *urp1, UnpackedUndoRecord *urp2)
+{
+	int	header_size = offsetof(UnpackedUndoRecord, uur_next) + sizeof(uint64);
+
+	/* Compare undo record header. */
+	if (strncmp((char *) urp1, (char *) urp2, header_size) != 0)
+		elog(ERROR, "undo header did not match");
+
+	/* Compare payload and tuple length. */
+	if (urp1->uur_payload.len != urp2->uur_payload.len)
+		elog(ERROR, "payload data length did not match");
+
+	if (urp1->uur_tuple.len != urp2->uur_tuple.len)
+		elog(ERROR, "tuple data length did not match");
+
+	/* Compare undo record payload data. */
+	if (strncmp(urp1->uur_payload.data, urp2->uur_payload.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo payload data did not match");
+
+	/* Compare undo record tuple data. */
+	if (strncmp(urp1->uur_tuple.data, urp2->uur_tuple.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo tuple data did not match");
+}
+
+/*
+ * test_insert_and_fetch - test simple insert and fetch undo record API
+ */
+static void
+test_insert_and_fetch()
+{
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	char	data[5000];
+	int		 len = 5000;
+	UnpackedUndoRecord	undorecord = {0};
+	UnpackedUndoRecord *undorecord_out;
+	UndoRecPtr	undo_ptr;
+
+	/* Prepare dummy undo record*/
+	undorecord.uur_rmid = 1;
+	undorecord.uur_type = 2;
+	undorecord.uur_info = 0;
+	undorecord.uur_xid = 100;
+	undorecord.uur_cid = 1;
+	undorecord.uur_fork = MAIN_FORKNUM;
+	undorecord.uur_blkprev = 10;
+	undorecord.uur_block = 1;
+	undorecord.uur_offset = 10;
+
+	/* Insert large data so that record get split across pages. */
+	initStringInfo(&undorecord.uur_tuple);
+	memset(data, 'a', 5000);
+	appendBinaryStringInfo(&undorecord.uur_tuple,
+						   (char *) data,
+						   len);
+	initStringInfo(&undorecord.uur_payload);
+	appendBinaryStringInfo(&undorecord.uur_payload,
+						   (char *) data,
+						   len);
+	/* Prepare undo record. */
+	BeginUndoRecordInsert(&context, persistence, 2, NULL);
+	undo_ptr = PrepareUndoInsert(&context, &undorecord, InvalidFullTransactionId);
+
+	/* Insert prepared undo record under critical section. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	/* Fetch inserted undo record. */
+	undorecord_out = UndoFetchRecord(undo_ptr, InvalidBlockNumber,
+									 InvalidOffsetNumber,
+									 InvalidTransactionId, NULL,
+									 NULL);
+	/* compare undo records. */
+	compare_undo_record(&undorecord, undorecord_out);
+
+	UndoRecordRelease(undorecord_out);
+	pfree(undorecord.uur_tuple.data);
+}
+
+#define MAX_UNDO_RECORD 10
+/*
+ * test_bulk_fetch - test the bulk fetch API.
+ */
+static void
+test_bulk_fetch()
+{
+	int i;
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	UndoRecInfo	urp_in_array[MAX_UNDO_RECORD];
+	UndoRecInfo *urp_out_array;
+	UnpackedUndoRecord	uur[MAX_UNDO_RECORD] = {{0}};
+	UndoRecPtr	undo_ptr;
+	int			nrecords = 0;
+
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		uur[i].uur_rmid = 1;
+		uur[i].uur_type = 2;
+		uur[i].uur_info = 0;
+		uur[i].uur_xid = 100;
+		uur[i].uur_cid = 1;
+		uur[i].uur_fork = MAIN_FORKNUM;
+		uur[i].uur_blkprev = 10;
+		uur[i].uur_block = i;
+		uur[i].uur_offset = i + 1;
+		urp_in_array[i].uur = &uur[i];
+	}
+
+	/* Prepare multiple undo records. */
+	BeginUndoRecordInsert(&context, persistence, MAX_UNDO_RECORD, NULL);
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		undo_ptr = PrepareUndoInsert(&context, &uur[i], InvalidFullTransactionId);
+		urp_in_array[i].urp = undo_ptr;
+	}
+
+	/* Insert them all in one shot. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	undo_ptr = urp_in_array[MAX_UNDO_RECORD - 1].urp;
+
+	/*
+	 * Perform the bulk fetch. 2000 bytes are enough to hold 10 records.  Later
+	 * we can enhance this to test the fetch in multi batch by increasing the
+	 * record counts or reducing undo_apply_size to smaller value.
+	 */
+	urp_out_array = UndoBulkFetchRecord(&undo_ptr, urp_in_array[0].urp, 2000,
+										&nrecords, false);
+	/* Check whether we have got all the record we inserted. */
+	if (nrecords != MAX_UNDO_RECORD)
+		elog(ERROR, "undo record count did not match");
+
+	/* Compare all records we have fetch using bulk fetch API*/
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		if (urp_in_array[i].urp != urp_out_array[MAX_UNDO_RECORD - 1 - i].urp)
+			elog(ERROR, "undo record pointer did not match");
+		compare_undo_record(urp_in_array[i].uur, urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+		UndoRecordRelease(urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+	}
+}
+/*
+ * Undo API test module
+ */
+Datum
+test_undo_api(PG_FUNCTION_ARGS)
+{
+	/* Test simple insert and fetch record. */
+	test_insert_and_fetch();
+
+	/* Test undo record bulk fetch API*/
+	test_bulk_fetch();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_undo_api/test_undo_api.control b/src/test/modules/test_undo_api/test_undo_api.control
new file mode 100644
index 0000000..09df344
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.control
@@ -0,0 +1,4 @@
+comment = 'test_undo_api'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_api'
+relocatable = true
-- 
1.8.3.1

0004-undo-page-consistency-checker_v5.patchapplication/octet-stream; name=0004-undo-page-consistency-checker_v5.patchDownload
From 4cf6687db2e1ff6af4a4f3cfa09c3b968992a895 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Mon, 6 May 2019 15:03:15 +0530
Subject: [PATCH 4/4] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar based on initial version from Amit Khandekar and Rafia Sabih and design input from Amit Kapila
---
 src/backend/access/undo/undoinsert.c |   5 +-
 src/backend/access/undo/undorecord.c | 136 ++++++++++++++++++++++++++++++++---
 src/backend/storage/page/bufpage.c   |  33 +++++++++
 src/include/access/undolog.h         |   2 +-
 src/include/access/undorecord.h      |   2 +-
 src/include/storage/bufpage.h        |  34 +++++++++
 6 files changed, 201 insertions(+), 11 deletions(-)

diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
index 9ccea4b..49166d2 100644
--- a/src/backend/access/undo/undoinsert.c
+++ b/src/backend/access/undo/undoinsert.c
@@ -687,7 +687,10 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 				 * block header.
 				 */
 				if (starting_byte == UndoLogBlockHeaderSize)
-					PageInit(page, BLCKSZ, 0);
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
 
 				/*
 				 * Try to insert the record into the current page. If it
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index 7e585ac..af25700 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 
+#include "access/bufmask.h"
 #include "access/subtrans.h"
 #include "access/undorecord.h"
 #include "catalog/pg_tablespace.h"
@@ -26,25 +27,41 @@ static bool ReadUndoBytes(char *destptr, int readlen,
 			  int *total_bytes_read, int *partial_read);
 
 /*
- * Compute and return the expected size of an undo record.
+ * Compute the header size of the undo record.
  */
-Size
-UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+static inline Size
+UndoRecordHeaderSize(uint8 uur_info)
 {
 	Size		size;
 
 	size = SizeOfUndoRecordHeader + sizeof(uint16);
-	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+	if ((uur_info & UREC_INFO_FORK) != 0)
 		size += sizeof(ForkNumber);
-	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
 		size += SizeOfUndoRecordBlock;
-	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	if ((uur_info & UREC_INFO_BLKPREV) != 0)
 		size += sizeof(UndoRecPtr);
-	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
 		size += SizeOfUndoRecordTransaction;
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
 	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
 	{
-		size += SizeOfUndoRecordPayload;
 		size += uur->uur_payload.len;
 		size += uur->uur_tuple.len;
 	}
@@ -53,6 +70,30 @@ UndoRecordExpectedSize(UnpackedUndoRecord *uur)
 }
 
 /*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint8           uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size            size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload  *payload = (UndoRecordPayload *) page_ptr + size;
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
  * Compute size of the Unpacked undo record in memory
  */
 Size
@@ -72,6 +113,85 @@ UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
 	return size;
 }
 
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset = SizeOfUndoRecordHeader - sizeof(CommandId);
+	UndoPageHeader	phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page has
+	 * a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size	partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						   phdr->tuple_len + phdr->payload_len -
+						   phdr->record_offset;
+
+		/*
+		 * We just want to mask the cid in the undo record header.  So only if
+		 * the partial record in the current page include the undo record header
+		 * then we need to mask the cid bytes in this page.  Otherwise, directly
+		 * jump to the next record.
+		 */
+		if (phdr->record_offset < SizeOfUndoRecordHeader)
+		{
+			char   *cid_data;
+			Size	mask_size;
+
+			mask_size = Min(SizeOfUndoRecordHeader -
+							phdr->record_offset, sizeof(CommandId));
+
+			cid_data = next_record + cid_offset - phdr->record_offset;
+			memset(&cid_data, MASK_MARKER, mask_size);
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/*
+		 * If this is not complete record then check whether cid is on
+		 * this page or not.  If not then we are done with this page.
+		 */
+		if (page_end - next_record < SizeOfUndoRecordHeader)
+		{
+			int		mask_size = page_end - next_record - cid_offset;
+
+			if (mask_size > 0)
+				memset(&header->urec_cid, MASK_MARKER, mask_size);
+			break;
+		}
+		else
+		{
+			/* Mask cid */
+			memset(&header->urec_cid, MASK_MARKER, sizeof(header->urec_cid));
+		}
+
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
+
 /*
  * Initiate inserting an undo record.
  *
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 14bc61b..8519fd7 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,39 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint8 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	/*
+	 * TODO: We can update the value of the p->pd_lower whenever we insert
+	 * a record into an undo page.  By doing this we can avoid processing
+	 * complete undo page if there are no more records.
+	 */
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index f421bae..220d480 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -128,7 +128,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index a4758ed..aa29053 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -20,7 +20,6 @@
 #include "storage/buf.h"
 #include "storage/off.h"
 
-
 /*
  * Every undo record begins with an UndoRecordHeader structure, which is
  * followed by the additional structures indicated by the contents of
@@ -224,5 +223,6 @@ extern void InsertUndoData(UndoPackContext *ucontext, Page page,
 			   int starting_byte);
 extern void SkipInsertingUndoData(UndoPackContext *ucontext,
 					  int bytes_to_skip);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 85cd0ab..5be7d1e 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -212,6 +212,37 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * FIXME:  It should be declared in undolog.h ?
+ *
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+	/* Fields required for undolog consistency checker */
+	uint8		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -415,6 +446,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint8 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 					OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

0002-Provide-interfaces-to-store-and-fetch-undo-records_v5.patchapplication/octet-stream; name=0002-Provide-interfaces-to-store-and-fetch-undo-records_v5.patchDownload
From f26687fcb2a473bba277aff75455ad3d2e83125a Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 2/4] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoinsert.c         | 1435 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         |  744 +++++++++++++
 src/include/access/transam.h                 |    1 +
 src/include/access/undoinsert.h              |  151 +++
 src/include/access/undorecord.h              |  228 ++++
 7 files changed, 2589 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoinsert.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoinsert.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
new file mode 100644
index 0000000..9ccea4b
--- /dev/null
+++ b/src/backend/access/undo/undoinsert.c
@@ -0,0 +1,1435 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.c
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoinsert.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the previous log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the last undo record of
+ * the transaction in the previous log, so that we can find the previous undo
+ * record pointer during rollback.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoinsert.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Default prepared undo space */
+static PreparedUndoSpace def_prepared_undo[MAX_PREPARED_UNDO];
+
+/*
+ * Default undo buffers for prepared undo space and updating the previous
+ * transaction's transaction info.
+ */
+static PreparedUndoBuffer def_prepared_undo_buffers[MAX_UNDO_BUFFERS];
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static void UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the uur_next pointer in the previous transaction's first
+ * undo record.  So in prepare phase we will unpack that record and lock the
+ * necessary buffera and store the unpacked record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the header of the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	Page		page;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info =
+	&context->xact_urec_info[context->nxact_urec_info];
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before accessing the undo record so that
+	 * discard worker doesn't remove the record while we are in process of
+	 * reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 *
+	 * Refer comments in UndoFetchRecord.
+	 */
+	if (InHotStandby)
+	{
+		if (UndoLogIsDiscarded(xact_urp))
+			return;
+	}
+	else
+	{
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (xact_urp < log->oldest_data)
+		{
+			LWLockRelease(&log->discard_lock);
+			return;
+		}
+	}
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (1)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		buffer = context->prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_info->uur);
+
+	/*
+	 * Store undo record pointer of the previous transaction header in xact
+	 * info and also update the previous transaction's header with the current
+	 * transaction's undo record pointer.  Actual, write will be done by
+	 * UndoRecordUpdateTransInfo.
+	 */
+	xact_info->uur.uur_next = urecptr;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	LWLockRelease(&log->discard_lock);
+}
+
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.  This will just insert the already prepared record by
+ * UndoRecordPrepareTransInfo.  This must be called under the critical section.
+ * This will just overwrite the undo header not the data.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			i = 0;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(xact_info->urecptr);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, &xact_info->uur);
+
+	/* Main loop for updating the undo record. */
+	while (1)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * removed by discard process, so we can just skip inserting into
+		 * those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * Skip actual writing just update the context so that we have
+			 * write offset for inserting into next blocks.
+			 */
+			SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+				break;
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/* Overwrite the previously written undo record. */
+			InsertUndoData(&ucontext, page, starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int max_prepared,
+					  XLogReaderState *xlog_record)
+{
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = max_prepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/*
+	 * If max prepared count within the default number than point them to
+	 * default static memory.  Otherwise, allocate separate memory for
+	 * prepared undo and undo buffer information.
+	 */
+	if (max_prepared <= MAX_PREPARED_UNDO)
+	{
+		context->prepared_undo = def_prepared_undo;
+		context->prepared_undo_buffers = def_prepared_undo_buffers;
+	}
+	else
+	{
+		context->prepared_undo = (PreparedUndoSpace *) palloc(max_prepared *
+															  sizeof(PreparedUndoSpace));
+		context->prepared_undo_buffers = palloc((max_prepared + 1) *
+												MAX_BUFFER_PER_UNDO * sizeof(PreparedUndoBuffer));
+	}
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+			/* Compute the last record's undo record pointer. */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(context, urecptr, prevlog_xact_start);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be atleast one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/*
+	 * If we have allocated memory for prepare undo and prepared buffers then
+	 * release the memory.
+	 */
+	if (context->max_prepared_undo > MAX_PREPARED_UNDO)
+	{
+		pfree(context->prepared_undo_buffers);
+		pfree(context->prepared_undo);
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			urp = InvalidUndoRecPtr;
+			break;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 *
+		 * In Hot Standby mode log->oldest_data is never initialized because
+		 * it's get updated by undo discard worker whereas in HotStandby undo
+		 * logs are getting discarded using discard WAL.  So in HotStandby we
+		 * can directly check whether the undo record pointer is discarded or
+		 * not.  But, we can not do same for normal case because discard
+		 * worker can concurrently discard the undo logs.
+		 *
+		 * XXX We can avoid this check by always initializing log->oldest_data
+		 * in HotStandby mode as well whenever we apply discard WAL.  But, for
+		 * doing that we need to acquire discard lock just for setting this
+		 * variable?
+		 */
+		if (InHotStandby)
+		{
+			if (UndoLogIsDiscarded(urp))
+			{
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+		else
+		{
+			LWLockAcquire(&log->discard_lock, LW_SHARED);
+			if (urp < log->oldest_data)
+			{
+				LWLockRelease(&log->discard_lock);
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+
+		/* Fetch the current undo record. */
+		UndoGetOneRecord(urec, urp, rnode, log->meta.persistence, &buffer);
+
+		/* Release the discard lock after fetching the record. */
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	/*
+	 * If we have not found any valid undo record that means it might have
+	 * already got discarded so release the memory we allocated for unpacked
+	 * undo record and set urec to NULL.
+	 */
+	if (!UndoRecPtrIsValid(urp))
+	{
+		pfree(urec);
+		urec = NULL;
+	}
+	else if (urec_ptr_out != NULL)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogControl *log;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = log->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoLogIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&log->discard_lock, LW_SHARED);
+				if (urecptr < log->oldest_data)
+				{
+					LWLockRelease(&log->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			LWLockRelease(&log->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit then
+		 * stop processing more records.  Remember to set the from_urecptr so
+		 * that on next call we can resume fetching undo records where we left
+		 * it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..7e585ac
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,744 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_prevurp = ucontext->urec_txn.urec_prevurp;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
new file mode 100644
index 0000000..9ab39fc
--- /dev/null
+++ b/src/include/access/undoinsert.h
@@ -0,0 +1,151 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoinsert.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOINSERT_H
+#define UNDOINSERT_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * This defines the number of undo records that can be prepared before
+ * calling insert by default.  If you need to prepare more than
+ * MAX_PREPARED_UNDO undo records, then you must call UndoSetPrepareSize
+ * first.
+ */
+#define MAX_PREPARED_UNDO 2
+
+/*
+ * This defines the max number of previous xact infos we need to update.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous
+ * transaction in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+/*
+ * Consider buffers needed for updating previous transaction's
+ * starting undo record as well.
+ */
+#define MAX_UNDO_BUFFERS       (MAX_PREPARED_UNDO + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+typedef struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+} PreparedUndoSpace;
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+typedef struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+} PreparedUndoBuffer;
+
+/*
+ * Structure to hold the previous transaction's undo update information.  During
+ * prepare undo we will populate this to hold the information for updating the
+ * previous transaction's undo record header.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int max_prepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern void UnlockReleaseUndoBuffers(void);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern void UndoRecordSetPrevUndoLen(uint16 len);
+extern void UndoSetPrepareSize(int nrecords);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..a4758ed
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,228 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_FORK						0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_BLKPREV					0x04
+#define UREC_INFO_TRANSACTION				0x08
+#define UREC_INFO_PAYLOAD					0x10
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

#28Thomas Munro
thomas.munro@gmail.com
In reply to: Dilip Kumar (#27)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, May 9, 2019 at 6:34 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Patches can be applied on top of undo branch [1] commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

Hello all,

Here is a new patch set which includes all of the patches discussed in
this thread in one go, rebased on today's master. To summarise the
main layers, from the top down we have:

0013: undo-based orphaned file clean-up ($SUBJECT, a demo of
undo technology)
0009-0010: undo processing (execution of undo actions when rolling back)
0008: undo records
0001-0007: undo storage

The main changes to the storage layer since the last time I posted the
full patch stack:

* pg_upgrade support: you can't have any live undo logs (much like 2PC
transactions, we want to be free to change the format), but some work
was required to make sure that all "discarded" undo record pointers
from the old cluster still appear as discarded in the new cluster, as
well as any from the new cluster

* tweaks to various other src/bin tools that are aware of files under
pgdata and were confused by undo segment files

* the fsync of undo log segment files when they're created or recycled
is now handed off to the checkpointer (this was identified as a
performance problem for zheap)

* code tidy-up, removing dead code (undo log rewind, prevlen, prevlog
were no longer needed by patches higher up in the stack), removing
global variables, noisy LOG messages about undo segment files now
reduced to DEBUG1

* new extension contrib/undoinspect, for developer use, showing what
will be undone if you abort:

postgres=# begin;
BEGIN
postgres=# create table t();
CREATE TABLE
postgres=# select * from undoinspect();
urecptr | rmgr | flags | xid |
description
------------------+---------+-------+-----+---------------------------------------------
00000000000032FA | Storage | P,T | 487 | CREATE dbid=12934,
tsid=1663, relfile=16393
(1 row)

One silly detail: I had to change the default max_worker_processes
from 8 to 12, because otherwise a couple of tests run with fewer
parallel workers than they expect, due to undo worker processes using
up slots. There is probably a better solution to that problem.

I put the patches in a tarball here, but they are also available from
https://github.com/EnterpriseDB/zheap/tree/undo.

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-2019-05-10.tgzapplication/x-gzip; name=undo-2019-05-10.tgzDownload
���\���[W�(<�����s�#�����������gr�����X�R�$c�����w=K-� N��}������=����~��jw����N{��~��v��`w����������������Vg���c����������b1��Y>�&���,�ds�A��`�����?���p�����w��s��[I:�'����i����$nEi�5����u���l��7�|T3����Ss�[;:;[��o��n���i�.����9�F���~�������[�������;{;�ng��������|���YjN���<0��c���j�;
��y����q6�&��>��	��0�E��,�n�o�l%����h?6g����2��k�ph:��[w��z{w��8�u�'�M����=���nvv6�����f�@��Q� jD��~�L/c���Q�7��N�l2
Z�<����0��7��7�a���,�bD�?``�In��u<1W�phfy��gC���.b`���3��aw�������l0�'�Q�B�Q�N�$�M���j�L�@&q�//��x[���q�y���DC����3;�6t�)7x���<����&2��Iu�������\�"�I�Lgc��M�&-n]���HR��h�d)�Vq�nyW���	��M�M�4�0�H����O�;����J�DS�&Y���l8Mp1c8��b��|d��(����~%k3X�$|�h<��$��p�����d���C�Ms0J���h��f����%����F$E�:�IdN�.'��$������c���0���Or\,������_&��t���gq>�1���0�eW�x���Y�C=6���8��yuu�1��|p���F6���y'�J���^&goZ��~������������������=<{�}�?�q�,��&��??x�>~y���6�������F�V��0�����z���������x����O� �C�~�����~'�/^n��wP��Gf����W9��y�uv�w�z�g�8Y�8��B�$�
g�X�3�6.M��v�1����_��uv�[}?Yh����CC�|`�r�������|��#��!|���5ai�x�������*�4�wv�5��e�h��n�Q�������I=�F������Wm.����&�P����y��{���<���tb ��n�����5�����/1��Vs�����g>����yRw�
 U���,��������a����Z�[��Bt���a���
�h�}��0��4*k�{���d�k��,�8Lb'��n�}Z��*1N�9V�|_�K��I.���66�n�z������[����7��p���*'��l7�p�M@��Kc�c��Mc�U��X'����C�*t�U�dR��W�����������^@��2�P�'������&0.����z�����c�u���2?�;��X�m�w���;^8�Lw]�����p���h��7,`�]�\of��|����#���i��x���
�-�`�hv8�;����I<�M������n3���|[2=Ec^&),��m���0d�9��z�xl�����H�
�������)�k5��W��h0b�Z
>�L��e�|VT'���.0e4E~1�t�/��]�B�Jh�</[��|�0�W�3,���n[D�X7~�x�f)3���b{�'����3]�{�Q6�������(*�o�`���Q��i�gxWIp]F$����kl_G�'I�0�����~���
?�������goiC/O����X�{��Q6�6�yg�|.�����wG|2�$��B[���Wq���8��[y��2������c+�����k_��%���v����������2������1M�C>�8��hm):����*`�����`�h�����]&��!��H7��!{7�i����L
�t.�` '�lx%$A2����o�<_O����Y�����G{��kB��N����"����fc�Y������4'�E������>MH��^�'�)�R�$��Z������g����W��z
���m��8�7Qb�@eE������1���t
�6B��_���~�/�l������
*5��e��"L��U��A��&����;� /�ei��/3���^PL�G�{��T��b���EW��Hi7M�yC^ T�1 1E����bB����8������B�11$@p,�����;#��Rz��8(;��
�Y�'
t%}\YU�j�������D�<���r�qA��?��
4u{/���pqq��&�L/����	yN��*�|2y����!LK����� �25�r�u�h-0<}���
�*�����?������ph/���7e��+p�xX/b�>'��aAi�V�����&���;0d<�{G+����8�>�PUL��l���]J����5��b���x��J'���� ������Md��]%�;����~!��~�-}_��*�����s���k��N�����_�j������1,�����_�O�T�\Ul��@(����Eo�/"b�&1�u������_;�l0���!������[���� ���(e��L��y'_#>�/���\;�c��^|<wr��������7/�����p�D4�J���L���� ��HW����@o�����^Z������+?������S������z�"��*A~�f|�����O�����_���i���V5L�2�?F>7���d����%��7��NR��o�91��R8���]�����!�N���R?g K�%�Y����Q���X<���=�Ef�9L���7�Nj���x��_�{���-��I�_���9~>��a6]vHSq����7�Q�;��5��<�kj�y���::{�8��<�q��/[.,I�����n�t�3����'��t��RR������	a���q��Z�T�o��D�-��T5�vT���2�c$����'�R�������i@kL|LQh��1�rHW)�8�
	oZ���V�����*L��=8�s����k����|b��`��l2A�-lZ�sC
1��-�3�
��@�p����*��"�[����?����Q�>1G)����}l�=G8�sx���; �++h���J�?�Y��\,pR�>�u����nb&�����������#���4@�}Z9� �Z��^�����G_�d?O�/��������L���{=���p������xb���W�������z`i�E��I��t$���<
Bt�H�Qu���I*`��W{}&1�t�]l�H����Y����Q�W��U{�k���
!���-��%����\��;$8��E<G4�v{�VE������@,�>
8s�QNj��[��2����
	OG �nx�<��#Cqv��]i���i��Y��Gh����XR����.�]QD6'�sX>4eb��^,m���$�f8�.+$lD}�p�����"Y��]��Lx�km����? �W��Ap��NbD)x�����H���������lz:
����D�D_��/�
@�n}�+�?8��7<W�����
u���8��������eh�|1�H��<��6Z�W�<���.R�
��q+�x���7��y5h��b��o��t"���L�k1��/����"I����W�*
���W;���BB>��/����g����=y}p��h��)�������
w��j�������~H��/Rl��A�I�<����!�;��J�f����w6Mr�j��v����E)7(~wc6�B9���M4��*S�G�97`2��SF����x~zv��������������o�u��?��<�f�>���xOco	Y�9ey[�Q���	d�N���U�^_���e�&�e�5�]�Q.��S�No���G����� �7TZ-��ek�~�[�T��� �)�mb��D%c���aj�h�K����������~bQa�6�.�������JLo�R�_{=N:�$�����<H'F��3�GCDf�EC6�hp�E�[G%qW���G�Y�C���Z�gp+	����	�.bIM,n,�[A��|���-�y|��N�������������{eEc��b^E�,�b�nD	��PA�AU��Xc���b�"G�W��+OP"������J2�L��3�KE�)�<��&�
����>��������<QZF#
�\�J����*c0[M�*e���n���*}zb��d���e��e��L���������$����3��-=�}(��������w#Z�G-��x���uF��Q0H�;������
�}�m���g��<�o�*�+�e,������rbK�N�!!P|?������WV��Z����y/+���'���d2�����������#J��*������u�������5���z������(e��_��&���Ye�v�`NJ04K(��KB"jD�3�h"�������0<Qv������5���p�'����)����s�f1������5�����T�dW� :� YU;b�t�����>M��'0$M��;!�T��&ffy<�
ip��fFW���$$����'o	Su10"��13���6J�eN3D�97��	~R�A���z�|�/5���Xx�&{`��$=VXKB7/���NY4��>	���g����$��~SK�~s��;�����D"?��;�����9�:�����������oI�Y�m����
8�D��Q�=Wl�5���T���&^���R�D�t��u
��/�l�Y8��p�|1�6����(�0`��Y���"p4E���8�f�J�JP��D��n��f��(Cx3�p�
��c�(���bR�;��j��q*#U
�[b6�x�0�<NC�j�43��i�V�)VIG�����>_3w������A����$�+�y�����*��;�$aSr\B1�r(�s�����bi~�E��a���x�v�:>J�i�/�D�1������Sl��K��#Y<��UNJgm��r���������G=\�h&)h�Tp���]QY���[�_;�TJ��m�G*��������?�(Fz�M��Z���qn]x��;}���{����GX�
d��C���[hA�������Q-2p[������{Q�2^�^5By�b��Y9|[\;��-�BI��,�C�0|=��]O�v\`N����[a���f���-p�H����?q�E(t>������&������p�y��,����W��������*m{�,����Q�����3V7�{<���T��(��.f�+�����&��u�/����T�F�����-%4�E�J*�R���@����g�@d}���C~ji�
��/�xx��/���}��)�������K� ���#�@�J��.Y�[�!���?_A=���Ni��wF���_'��F�V���lN!V����l�NI�,,w_\��U"�a�7�����f��s�
o]��%�9�E1�������b6��
l�;����������EP�xN��5�_�\�fU7��
=�N�Zv����W���%^����{�
�c��p2O8�:0E��*�(O�L�Y������-6�(0d�b��n~
/o$��E��u6A^g����F
�f���7h�.W�E�Jb1FH`b����f�"�6�.����=G��$��0j�����lHL�`�d��?J=5�����>���o������{�����)��
�@C]�0���V��^����E>��%,����dG���n��PT�
�P��Y�j�}�����(F��#*/j
�Zl��|sJ�;e�~�����
:��Nf���I�7��i���("�/�m�X#�����+X����(��?�B��`�12�}IF�����f�y-��%BJ�PK^=<9y{�4w���W9��5��~W���ZB�Yaf��E<f=	�r��0��(r�++|p� ��e��RV����U{�/�}���:���QI3S�#
�:e����W:��}�M�s?��>:�f�����c,����6�S�]q,%�D!���,�eYMb	_����`���H>�i1�C?�*��+T��<-��vu���!?
X�=�!��!yn�rz��7F��S[���z,�^�5;2��?G@zx(\^r��O1�3Gh%Q��kFG��*D�d+T��<��(7��e��������yr������������K������������L����cMYK>>�Ar��W��+P��2����'d��
�e`��5�zbA��X��N��S�>xs���_����E���V+�|����Y��o
�X����(hd���u�b�-S���;V�m������3>(o�����X���QT��Wv�-�kV9p`�	n���R��~4onQP�� �F�n���R���r�*��\�P���p�������<�g�#�E�z�����������[�Sq������>��Q�����Z�m��s��A��xjd$����~�JT���}���c@��1��'phH�V�a�?�x3�H�����Z�]C]��e�?:H@%��Q�f��Lo~�Fh;F�$n���S:�S�=���<��[TO����k�x�{G��
3��u������xH�a�P!Qb���T�VL�P�?e�t�������%�+�pc���E�i�j<��?�0@ �}���ID~X�?5�����	��2=�5z$�{��s�����l��I��"N�0�x���)�%�UV!qo�V%n���Vrll��i��x��O���e.��.
&[�5����:��F�����\��M��4W�|��A=J���N���!=�<������F&�h$�(7(e������vQ=�/�T,~B��ZS#�-b�|��014TaW>�P9Wy�CV�X}��&S������
T��[
B2�^p�=�2����
�ac��@�*��<R�`�y$��9��y����&&�
d�9�"��o�^���m4��Kq������},�����M��/�cYt��@�p����T	o�����RKY8�W���"�B�*~����
������"4)%�{s����~h�r�����'�>B9j|�q���2S�tq�����S�U�	��B�����{�k�����
P> 2�U�;�(R��z�����}�p2���WBzD<�nnc���Y��2Wp	@���=]+�L��a����F�pq	����U�����W�� �S������7�*�V�B�����K�������������y]ld�^`!�h��Kcs`�������.���5��&��*"����Kp�~���0�������n��,�����qE�)�����:��`��r�
�����gqx�r�����d4���H���>��|�bM>�1����6�#��)<)��0��M�p�$�����6��
/4hx�E�K��o�`��g\��dr����M1��<U�A4E�TA��Pt����P���y:���<��=�*��y���������r$�]�kVO_Wy. �.DWW�NY��"���Dj����Z������WM
���(3���Y�]TB.����G���@��Ru2n�0���*f������_K�.D�kV`���CN<u��:a'
�p������h�����>�����R��U�,@������B�U�����C�
��
y�����C	A)�^X.U�m�8�}����S���L3�v9���o�[�L=9c	��d$�0��$j��=����5�po*���w��"�~�"�p�g��R�`���(���Dlo����z��M�Dol�R�-lCb9jG*��6�=7���=>�	������6iR�3/*>b�(�H�$"H����F���!��!FN��Nf~��Ht�Fp=Ff�3�y_����D�T$��B��B�,�U�c��#BW=��a���.���;�( ���)T<����cH"O��]HC�Z�o�Z����U�KR�����
���P$�B��Cb.^����T(���,������b�"��U@���TM94w��ji����9����$�l�����U>?��1��U��
#�x�Xh��:8��}n�T �'I����X!~�N_��<�hQa��
���U�����>7.����	����E�Y#�BA8���7q��:�'�������2���*�;�DF�o���f!�#)����s��l����[F<��FV���N�*3
��#!-�qn��
�'"�p�dGs��<��u��H���s�,�+�u(���)F�X�o���L7��2������������N=j�#�C���QG�h�,�L8�Kn
teKL�b���S����?�`��4=X~����[<$�������n��M.��SA4�B�-S	�6�' #����?����=}�������f��p��&��Igq��&i�y���"���{�!��v `����Q�9�F��[����E��rF�^6NP�9�+��Tw!���8��K
����#x��M�6��b�b�� �W�	�u��Fp�������>�������k�tM��|��<�R���
m�4:'����-)�}�_���2���B,���Qe6T��0���]�r�hjZ�D���9T>��1��L6��"9�����L�0H�l�z�,t�!���~�\%������W�b7f�yzF2��f��Yq|�,?���m�W���bW��q�Pg���
[O%��f��O��'z?�D�k_���D�y+q��
�e�����������=��>&��	�h�%��l��X�I��5�1����=T���w���X������O�VXyi���`�u����<�(Q�����hA7�&59�u��HZ�Q@��A�����)a�p9����hB���b,u���*}����P��Fy)�*G`a���R*��;t1����H������rM B����x&;��)�1	�p�B3p�vN�X��9���B�������I��Y��PptK ��x:9GW��:!��y��w�LF�
v�P;��(n�r��~�K�x%�1�Q�T���4�@mr�6�;��E�pe����9!"���U*�<�fX3��*��O��4;G�	-�W��V��r�N7����>Z)L��D\�:����i[���h)�I�3��aE�x���a@z���_�5=���{�['OH���Wl��]����gQ����\�� l�&��c����[���Bi��n�j�W�U>��LXy�
����ha.��d���^�8����-�6�I�6IU�{��������Y�^��B�q�F����/�?r)6ujUt�hj`�����y	�V�R9A��Mc�~u�z���x7D+mNi�E�0��F���3���BL$R-���'n=�l*mE����7�.����������,
$"J�p��i
������I[��~��F�=s��b�eBSd�K�����{��3
a2�u��V�l�������e��m�6u9Fjx�6����������
u�+"�.
I���:h����U���b���V�De(jR����d�]���@2����
f��A��A�2�����0���Q��]E���{���EW-n���5����1���H��BCD"��Pr��E�d�>���D�h�?$�brO)!���A����Yq..(Y�+�'�8Y����D���%S��|�tp�v�����*q��WQ���i��j1�n5-��g�z ���X�(���D���7�Y���v�%Z�0��O�9�@:i�}�t��,=A'�j�HT`o4N_�D�3�+
W�2���nD�C	�j�*�3uY[V��`��tw�?U����!�M�Q���v��S�-��X^dSm����,��on���=5\�1���F����XX�Cq�g@�5+�U��L�E=�ek�<����:�T�]����}��k�U
��lV�'�j��8������w7�L�x����
��J+�����V���$;���������*���<b�S���s����7���Tg*��H�KJ��7x5��R���|���T�����@�h����]��
G��v'�U<;�kt�Y�u����+W���
K���=���/�~S8���M�P2�=ke}�P�����6�
-���)����z��R���'��|p�������_Az�MQ��������oO~|���o4l������^���Z8�5[�!���B����X����;i�oN
^'�^f��*��y��E)�EEq��1��U��a�JQ�����-�A��@�WU�
����Z76��X2�z*�d������S�N��jk������V�zx�=��!������y��2,2��n�@��&@�@V�\��H
&���1���d�_��0Ug��o�����E������]�]D�X��Z�)� ��OW
�����u-����j�B ����Y�����mlZ�5Us2`>(w��%h��������C��S��b��I��s�n!C�%��.F�z�n�5���"����FE���W��4���6��eU5MS��YZH?b�sC�'����g�E�W|���^��tP�5n��a.@
�����:}f�}[	��<W���)�*~��k9Mg��t6������<���NKj?�x�N{k'��"�W�0W���|�&���i6��Af�����3�*����bX�!:K���oO�,z8��m�-?��D�,N���E�����\�6c}gy��?�>!L�>�)�-[����� �xYu��y5G�U��?R�1ebIvF\����)y��iQ�'	Han��lVG���_�����E�H�3��{_#}���S�����=~�Q��P�aY��	�����J1�����U:���g�8{��E��|����U���2�>C�U�Z�W���J)��u�X����N��LpO�Z"v_�.U���!��� \�����F;��������N|�xH�F�j��P��������uN�k��!��\����}�z7�\��%�)�Q���nm]������o��bs�Kd��.�=����/t�N� ��R��

�#x���z��;9)�^AdY���=}������t���op�|<�d����4�U��.������:�I��"�����z���W������f��]VR	R))���V�d�w���L���E���8�r6�TD5�����������YZ�R6�t�J�P���S����l�l�����Ev)L2�\������*IK�bX��:���������I2���W
�	#�=�������*��Y����,Mys���v@~��4e���5NU��y�w'�=���U��f/u�>�96�_gtZr��I�,�KL@�z�R%�s#�.{jj*�X��G�	Y��~�����r�,�&��4����)�~�V@Gl-��W��W������r��%^Q��l�����!�>�$#��3�`���U��j�B&�����4��H ^��^�\��.�2P�V^Q�@[���j�s��:��W�p��.��9�JV����x> x��@��jo�p
O|������:�nq�j��������H�����H����I]%�����;m�g�d�������D��K`�	�@�����{#V���IQw���1���l��_h.R��$q�~�4KK���=�����:I��4%������������6u�/e]M����m"����R4���*��G�U��%A�/��}�
w���������Dy�4i|�� ������� -Uy9<�YA/����n����C�P����nW��u����69����,J���rf�V#��g��K���'���7QT2y��U[��.�|��.<��g�w����y��*]��q"_�A�
�QK�?5#t�����J$a��~��0�|�E�1��C�"��F�����z5F���AB(� �E�-ND����������g�_��xx�����������������/�����c��Gg��q}z\���O�8<��+�R��S{������=�L��L�i��N������Jp
(����Q�U������4��K� ����!���������%/c�:��"KO
����U|���}S4�Di5G���	��dq�k-�W2V��K�e�B7b��A���K�\���+�=)Z��.#.��D+�a���.9��M��.�a��D9LD5�p4ro��\�1+{I	�rd	��Dt�*���le���plP�o]�������������-�Wm�������X,�'���Y�y��v���?w�u��#h<��Tc��[h��	�%�fe1b.�����������?Y�.������%s�>��+��W��r)k��^������3E�).�<�K��*"k�H��%�P~�\5�%��X��)�&�����@�^��{�3-(Rh=1��@B�o���o
�����������2�����b�����4�����J.P�A��I��<���aBN�-�����|�?y��,2D����2��O��"DS�y�k��>c2����W���cZ�Ii��b#Raa�C�=����oc����3�:�|���M�i��yt�k�������S]	���+o����@B�(

c������R��Y*t�@M����*�X���
��=�hE��
Ds�F�Mx�j�SbODa�����S(����#�U���2�a
=��yLGm�t6�#U�w��srt��y��T���3���A��9�jY�}���r��*��5�>����y��
�a����h����tg�.���-0�7�t��q`k�Ws���������M�>�����
vp��M.�f@,��G"%���L_�i��Z�#7�Io��OoF=T(n�\��l�g�K5k��r�l>���{;;
V������x��n������V�l������a�����/1�v�m�;�;;�/����V?.�?'�� i=�������W_;j{��&��<_O�%`��f:������GMh�O/&���W����E�9fc2���d����v0U��oox�&�W�9�'�1+D6I��.k������(7��=!�wl��y������YP>����K�z+
�����9�8�:�1�T,�A�i��?����VP��q���U�!	T�SrMvi{���#,8�l�v����'<<Aw��>�S�F�\�s[&?h�}�@u��Ih�&���h|���Y���vAH�r$���}��f9���������)���>��H������S�3R����Wf��k���T����$�!?~;p]��0��J�To���!�c+�v����)m�X�P�����g��p�����~y�B,�<�n�x��/�c�;����/����7�;�;��m���%N�n`�29�������
����Y3V�1[�s���x�
h�]R-���Y���(5j������(�z���/�7:��Z��N����5%k�_�z��*]>�=��-m(6`�0A{h�*��`wE����8fV�/	#K�"8<xqx�����s1�Z��k�(��/��Od&�V�
���}����NF�K��_�)������ �I�J2��x��r��9]�=�j�����_xT��+i�<cL��N�{y�RX@�'A�w��|-�-�S P��XSw�iC~�0^��H�Vm+��>�������\�vW2Y�/���7�~uUDo��J�[����������yA�gU�`;�+��/�>��Q���N���c[���O���T��}�����DO��L�ny���Z%���%�%��� �Y  �5��*P�T�o�c�1�����;h�������M��q�71	�����V
�YC$2LR*[>@�~Q�Y�RRRvI�$q��2����cv�6W��r��CP���l�<NU������ �5��j��b����K�����\E_���w]�j�<,tSn�j&��h��Fk���ee���%��Y�j�������@r�Nw�pG5�I]�mJ���%�3�%��3`h�d��K�~����|lOkuo4�W����k!����������AJ���|=���������%���[����]%����Oow[���:�?�}!�v�;������Fe����+.
�W�2Ou���5�����y�oqs�����W��o{����}}���s����&��-n��x/��������(so�]E�?���#�����?��A�����@����z�he���
������O-���g'opmK�[pqZ_q	4�����wq_o�K���<+�����V���8+�����>>|����[��7F��%N�7��D~�%���{�YK�1��t��������K�:�^� l���p���Xh{m_/����:�w1�k���=�5yA��^�����4���1g�f�\��b�'M�����r����_�G��r<������W�X�Gu�9���2��%y��c
i�y�j�%.�6���������`���^�t8e����T%ThY�/���g%n-�h�u�eA�����(�����������90�����\.�� </����O���/�M�7�r7��H����M���O��k��'���8Y��.q�K��;�����M�NA��Ub���w&�_{�v������
N�$+�:)�&r�)Oe���D�d���2�=2G~Q:���BN��G�8��F�H�cm�I���x�Z/{���d�$�������������_��������=M�d�d�T|R2����]�F��,��5b��<5V�ZP�=�P)�2DcK-��x#�.) -�ss���{M���$�	��*K��i\��8�N�ur��F��O3Q�'�d��[f.l��E�m�w6���]��|:��
K�aT�4�/"2�5�P�1�A���4��3�w��0�{���s6�!����x�v��L��o�0��D���:`M�;��yO���
oFN�r��
��B�Z�F�Wgkx��jhc��B\<z�7����>�������zQ��������s�co`����*z����@U�<q{����OCg��Xi��TE��M;_�g���*���%/D*���=o&o�����C��\�����z��{cf��=�p�����=k�>}��1��w2���8O��'&����-6����W����Q9����������i���)�ei����
q�V ��o�j���ITTO"�K<z'n�:�i[��9N���nP�,����I�9N���5��^��Z}�?�?���������	��9�M�.���!��Dy2BA��f�B��j�����DL�:NX�\U���~.G
��_�{.����[��I4�����g���\�`���p6v�h��N�i����3����^���mp(s�����G��Ql���Nm���{�C��*�����/{��u����Pp8s�_�q��xK���I��eo"��x
�.c����P����X��^���
���*n���/|C����+XM�f]���S[T3�8�qJ�WDs��W^g%8����E��.��J�V�?���5���*�Z
����Z
\�������rj���h��P��]-S�u�A�s�������7����*ne������D�hm�1W����x�����4���h8��"��T���]��o��8��/�]?�|�o�����my�Y�����a�s�����x���_
�{����������9a�A�xyQa\�~�������a���\e4y!h�r�����P��RK��u���5�hr?\|N'M^���B/�����_$��"F�F��7���!�T���&s�bK��vP��j�N��D�]�����	��e��b).��0�M��.������oH���j��,\��$C�B�fc8�e��@x��t��f�������&31 �������<�|$���%�n�������y��y�l��1�����
��a�)&��<[�-`�%����DJ�c���z�\�]�v��^�0N��_r�2\������4o��9���t
��X:Q��MQW����������}�������D:�<��lu9��������Kl�r%����������~���)]�t���ob��^s��S�FV~��Uo��*&���zti�p��IO�!���r*�!cz�f���>�y����"sb�����K"��Go��
!��_�����@����q�����������WQvn�����I��m,�,j��_~����hp���H�+�P5�+K��K��[����}�ho/z?jw����G[�����A���~[���y���M0������Y�`n_���x���9D1�%�/g�aP�r����*�/no��em�I���>Ysc|L��#�C�^��{;���������o3@ ���uq��.P�j�<c��;�Qc����_n[��\�������/�ZY)}�����}|����A����������b��������A��E�	��������v~�\�vN�E���s
�]���R�;`i�M�t�B�8>zszxrv��{���5�����
�T��_�'LT�q����
A�r)���`�P�FP!�aoe����;(IF�U���@�G���* a�Mi��#���/�L+�e{W�
�8H9��;}�W��1=g�/�l�+����_?���z��3�w�>Q�J�z�/RV���s`�X*�MQ��O8����-%�^�J��������Lm��0��1~��B�zn��	�������`�e��>~�h^d��F)���x�i�V���h����/0b'w�{���4F_�O���,R�R,����|�(���������s){����/�n���xs�����_>Nce��AZO�SG�P%�{m��������
����h\+�}���;������9C�?9t���R��sB�&�VE8��S���N��8�>���V@����|FP����t�����!N�S0fXo���
��Q_J��aA�g3��il��ek�X�yE#>��;V�K��8HG��>�8�$>�f���4\�_�/I%3`��rc�?>GiZU<0XQ*���A���#��{�\��y��$}[�$�{}�=���fz���?8�G�Rh��N
����k�b�k��7���7r�-0��V���)$I^��S�$��.���T����{��QJ�P����\A{�RG����u����Tq*?���lU��Q3�gx(D^�����_|�x�yP�6����o3�G�)�f����[+c,��0��Y�'T�)-m+l���%]���a�OM���
�R0�G�"�h#OM��M���FPjma
Bt"��<�D����a�*(�f�3.8T[Bh� ��b	A�-�����+��m,l�f����9����+�����93���4��ck�-~�U��nG�i��3/j�Hoe���,Q�S���7-�:o-��@��~�.��;�6�\���s���Zt�����m��r+z5�!F��H���e���^���u	�����#�g���N^O:aPl7WF#��B���$/�}=�\i���m8�|\���[����$���QvEL��,$:��jx�0��u��1H���{y�O�����y�8_�e�*�_?^�
-��,�G�a�� ���cA+��1�S���g$�+l��l�\@���W�5"�w����M������@}>�Rd�I(�On�XS���]Y�f"��g���Og���!�i���]F����y�k��������/[Q]�(V/
��gt�(_�o��/_[AaP,S�k(K�sd��(����>;7L'��YI�Z%�Ur�!�p�}���S���%�\������4+~�H-��h<^�#S,��T�\iI
�,��14���y9r+��������$K����
[�
���b]p���_�"���=N��@h
�����2�mU�k������h��������h�&�m;�����z�{TYt��?z��y��;(�O��=yQTh�O�Vh��)r��&,��X��l@�2j�������-}�^��D6c���l/���	<�V��&����{���_r��r~���D7�*��\��@������D(D����\�xW\�Z��*yyN4��#�<X�������	*�1-��}jR����8�e��l�p��h�P�8]L������A���������}��1s�=�K�>����w6����S�<�q����UX���_j�L-E��T��������dGnP<����tJZ������Y�|�N�6���h���pTL.�Ca8�&�0b[u�t����
�4�=�FW�4�P��Q�qH�������t(�=Q��0	\��D��=�\�}-��8A#���+�&Xe��E�z��:��+�i��q".����e89<>@/���gG�������\��x%C/k���=��`����@7���<�����+��
��d���Yz��R=�;�&Y?��&�������H������X�!{����s&�G���e��X����pn>��q�(��v�p:+�����Z����jKL���X�����jA�b�lU7���6;Um<`�f���]�P���L�i����P�G���.��O��})����Q�����%d�k��8�\�/��p�R���
�L��|�Ox��ej�^�t�EDC�8��*l\����6�B�F��,U�O2��������O�pu���:e<�.�i�f�(�>�v4I���`@����h!�C�1�������%�FV�}�����x,r�� %��h� ��0�^=�ld"�{�
#G��K��u|a�J�8EN�7�L(��w�E���K��s=%��Us���m�`�"&+��|l�VL�j������Q���gw�`�����k�X�'��=2Q�H��cq4A�LPf�%����+�!a�M�+
�au9�iW)wD��}������'�u���`��<,��^�#�.�����l��)C�����c�y�cc�|�,�$���o�i.EKCY��=�IA q�/z�+S�^����C�Q��{!��@rq�]���7�������1�wd����" �D��@�|KhbSH[�_w����b@�s
����mc`M�BR�K��^H�cCk��b���84��0���z�y��XT�!��^D��;b�pO��4AtH�O�L#|F���Q��k��Z����Q��h��Ls���J���1�j��_��.�0U���+���������S�e�O����]��}o0��f������f �L�������I�����p������5�Q�Y����*��9m��Y��i�sA�P������8��!�-��g���pT6�~�@-&(.���R�,���F�l+�k��%�{��=G�w��W*���h,���><_2�J�U���K�bp�*]}�2��jG�|��L���8k�|��^�w���p�K���K��3L>^U�TV�D��h��4`u����X7�]�&�S����c��R�9�Q�DQR55��j�t�>{J�TDT���x�D+(gE"_t�Ud��T�n������N1��8
T�-l�Q}�*@(8\�t�1��������5G�Wb_j�-)U'l�
�^����M��%��x�{�M�Z�+��^����^��gXv2�,����(����K�x�����J�r������h���|s���A��h��W���J�4K_��Hj'�R��.��C��~��{����y�����wKz�1.~`��7]���E3�&��c2Q�@�.W�vX0G����N_��#�U��&I}w��^O4��$(c���X��f�`&?J�<�S�g.�Hah�^h6
"��x��/~Q�w%0��m+,��Xa-�2�|d�������w;*��*81��6���[�A� A�����WdO�B9'k�\=)������e%7���Q
��P8�:~)<�TX�w�����M�(���3+��S)������j~}����>��n(�4yQ�� ]��X>x���)��5�"�t���y��4_�������.9��78���y\��z��3���O��2�����F�������/����|mZ��V���nw:��~��g?�x��g�
MDr[@r[@BZ����8|l��i��r�6y8����]�o{�����:�]�������l��wL{�A{{��L�w9|8���,�ds�A��`���c�����n���~����?����x�����ao{{�������:�^w�������c�y`����?� ��aC�������4�O��
:��\�nO���'~ri�i����?�q{����Y h7Ng�����������������l2��L@���P3��l`a��:����F�C����jMM�9�|N�+e��x�E
]f��I���I�m�~�{���d]���!����$���V��\N���������6�|s����0����b��o'o��?�����w>�~Lv���]���|�]������������C~���i��cD��y��������yrx����1�����Y_���\��b��b��g�"�E����C>x��R���k�Q������Pu����`�������v4X��ro���h����3��M�}.�X��-�8���O�??�Y���\ET-��pn11����zd��(A�������
=2xEf���"���3]�)�7�4��>YC�59v�2i�����f�/qo�s�z�I������&;���<u�@&�&���9E�J4�&�D
+����h�_R�,L.DJx�*�����M��Hl5�����dcX �,��h�!����G���K������q
V�G�	M���w�|B����*����1�
:F��c���>�h�>��VB8��8��@}������*�������(��a0o�5&����Q�9 �HW�O�@,c���9�l�����v��V���������:��mT4�i�H�2��"e��:,�=������m�u$lD�&d(�#d�E�$���>xE�K@�T)�A
%���9���|�}�������r�%o�Q(.�U������"��pB��l����E|B��w��D�+��J=<�����5�D��Vz���\�^��1���_"q��M%�fo�(�uo_PFE�������/g�y\R�4T3��4*�R@H���x	�K"��@��.K��O
5�_�Se����Cv��p��������n/�i�h��z7F���j#
�{����k�I�F���W��u������G)?H4��n�R�R[�`��f�#[v�Zs���5'��n����6�����7|�c^������G|*t��^�R�1�P6���G`!t�P��xO����9Y%�G�
�+���?��.�Q��As40�Y9/�����s6���2F�	q�@�p��A�*����P!���p��qs'=E�+C�SL7"�����^�"7jV��Q���$�Xvq�/A<��\�i�;r�w����!Nr3���{��WX����!���a�v�0����p ^N�@����!��E)W�!�u(���L�.	9#�F���k4�C�R�>&X_����(�I5�����F��o�t�t��@+
�!�A�0O��*��k���w8aZw�������������w�[F��� r��g�"��Ct��5v����5�D����p�M>!:c�lc���^���Q�P��W�����BX2�������9�Eq1F�N=���o����b������"J����t�EM�����{�R<������
����T)e��.�	��*d,�,�����	��<	m6A����5_��
6A��3� l\#���x��$��rb�"R��)���`�5!�3�6��o-���+����)'hP�T��a�Mb�s�Rl����k
�fb���O-��8���;���[�u\��|�Mm]�.>4e���$A��PB���j���!<��!,�#���H(;f����3�
&6������j��Z�r&L���h�Q"`�=n�I<�>Y�,)yZb.�0����5���v��HK�^
m
{E0��P��[����E>f��u�����9����p��,����7h���b+�ub��C�U�����>��N]��LR��a$���WhV�|g$�6���k;����M&k��A����2�Hdq�4"��@4� ���6UX����u��E�L���M�s''m=�G�:��[q��w����Y����t���Z�t�!������&���-���u�������H�V)�/��_`R'��BC2��+��6,�YcX$��,t�����_�7
���tS�P&�:!��'�d����n(��MQ�HN'2;
s@v����'���y�K�mv����
��CF1�����@�6-��=FF�g=��^^��V1��sX~�v�r4�V�i��CR�_\��+-!fQ��JdE�1l���B���.T�x����C��ca�;$^������r��G���q ��K(E�)�]C�j>K�q�����,i2���}��>����I���j�qx��I4�2/����������@�D�������K���h5�p��d�\���#���~~Y�3f�/��~{���G��L+�	�m��\"v���#[���$
/���zI1����f(�R�-���*s+w+�Q5H����r6�c��:K���c���(j�L�����b�&�Fr-����S����F��j�b1�B�wz�2+����Y�5E�jtR�y���� m�$������\"�+��m��g#�q�Jx��t^?�L&�������#�6�}Gp��a>�m�������i��W����$_��y�!�K���h�EJ���d���m�3��qD�c�^��:\�+�W��j�Ay/�	a�����Dr}��	�P�}�����,�d�ea����|�:��������@�p ?v(+=�Dj����F�I.0�����>R�aV	n�?�&�D�!���Z��LfD��E��X�d,q#��F9u��6��x�4sB��E������t�r6kM�"Q��xh,rF�->x�X�|$~y����n��)PH��y��B����\M�

�e�-��9�J&B�%��2����iQp�o�kr���G04�#�lK�k�jM=�)vB��G/��d������2��L`&��z	N&(:��2�4
j�H}^
�V���G���J' 6��NUu
�M�"�EY��dT:������Zg�\���_��hP��:��T�����Mc�uL���IE�3�2��KJ��z'^k"'lo��k4�MH�Bn�NB��M�����+��x�4��X�Q��12�WF�=��rCE�����D�_]sY��1#�`��#m='wBLD�?��~��L+��}j�����/DmH�|�x]�:�
4T��K���<th8�<�MK�8���_4�=f�Q��`��OI*Q����
��w��J�}`�	Q�����x�o&�J/�0��	���
"lX���1�s����A��i_<�����Rl���_:�6��0�K�ZG������H��{�_��O���
z���F���I�A��B#o�cH.(�0���;{mJ5��
g����&�)26go9�,����VM�Z+~��AwQ1��������o��ll���������r)�yc�-�U����i7;{f��c��5�+�����D��[��k��x��&�a��������hKQ�"<��Gk,����Mc,����;J��Kf��'�8Jr�1-&�@��R.i]bN��A6�>Z��^����-�������J��/ukZ|>��:�j{S`(��S���j�H��:��E/�J89�H���r�_�!(%M4n�?���Hj�ME�`���0�b7OlIa<D�'p�������N�V�E�E�pq��3��*�Vb)#����$E������wl�
�0
)u/#b)U���U.��S"#�M�M��%�Y4X���Y@���2t8#�s16�r��+�Gm��f�g1��!�-}2JbD������I��x�BQ�zy��9g��L�GC�(U�Vu"H��}^�rD�<#�`:s����2���)�	<i�I���K�R�����%&�����q4bx��NM3h�����d��G}`g�-������]Z3�$��4��#����Pzr��&���~�����d�6���+�.��2���6;m�����<B�}��VVD�P��]&��+������p���^>�������}R_�6���/&6��f
����x�+Uh�p�.5B(�w,6�h����`@vm$ �i<�W��c0�?u�����;(�:3�R�$�Y%���~���	]���z�8�`-��qA����,�_�R�g�h�u�CO�XM�
����=V�%q�\-U���#�;��VV��Au/�o�~?�$8���R/��W��}s�'��lO���/��U���$c�Z�����g���}���t/�QsC���.�
���������)����N������/n-�����_�3�����+��oo��?��������v��A��mG� Xu��^'�<t�<|�~�`�7�����"��g�0�k\��?�_��_P�;O���R��X��o?x��e�����8��#�PU�� �l��?y[�^[���}/�,j;7��������yh������/�h��^��K�����L��Oy��5�^c��:�}��Uo�u�)&6�����l��q���?�S��f}~?.d�=�	w�&�2�4���:�.�8���b��:v�g�M��s�wj���P�U^�.��}�?�&��mn=���Hk�_��Aay����=S��;�O��z1h����tp�oznc$��]�S���m�;�G����SJY�p������u�N�J,�(<����,vN Mq����<|dj�K��Fd�{�~^X�}ra?L��M�hr����GO���R�4�P����(�V&�t�z����[��Zz4~?�k����C�(�7�V���&�k����0�2�����O�GC����
Q)8�Bf����/S����~H%����o6M��f<�d�����o{����Om^P��j�)Q�^�Ru���~#x�������V���Xt:���t�X5!����uv���/!!���IR��O���
HH\����;^E��l4����������!f�,M[�X�G���4Y�J+:A���AuGWK���Vj	z��+Q�R���9��e�q�Q��>��,O��;��-?\���~����:{�6�������C�� Fw��f��������;
VR�W�C�8��v��r�E��
Mi��Vs�a����?J��c���hI(;�ou�]~�B��f����������q��RKc���C��e|��.U�w����\�����g���<%�Lf�Q1�x����b�����1��n�\`�o4B�k�Q_�o�eG&�7�lY���|��{.<��Tw�n%�t���$���%7�d��0@��W��_/5#��N������vcco���~0�~m��k���/7%s{����N�Zm����Z�f���,���d���n��6��g�qj�t��W����82Q�Ls��O����������\�A��B~������*��(��!U�����6��#���l�����ov��+��`:�����������_�
��'�����C��l6f�	M�p�p�}�������]�}���_63Do���������G�f��������7-�L���(�/~2I�S�	���\�s.������y�_�9;zy��J���ot7�Y}/���B���,�"j���*(��rW]�Y4�7��5�A_��6o ��}��_�SJ6�85�3e���Q=ig����������>8��r��$������W�������:w������),g���eH�|\������e�\��~
��x�����9��9�����O���r����n�bh��Sm���5�����W��"
�!� xx��	/V��&n]O����a;o�������x��t�����>h�;_������W|�}������	&��<�����k((t{���l�w�mlt�q���u�[��L�7�bD�5F��X�u��1���1*w���g�������S��uC����QF��s�/�&���7^6��L�'��^<����v��)u���m��Dq��h4�AC�M��*L9���b����O��$��)��o���3�_F�m��������&�~}pf��$��UN����z���R$����h�&�n�����������n�u��6{��h��1��+�����9��Y�1�W��ooa3��"���^���4�����{c�����k�e�����|�������g/�~x�f�l�h��� �|��z4�V�J<D��"�s��������X�`T�|�a�-����;��p�������v����F�E+�v$�<$��!AL��h7=\�����D%��/���~��<���sZ�/���C����\��"�wR]<�1cG� ����W~��J���NP|r
��hPT?b�P����9�\��Ghri�?���6��6%f���Q
�E��5���a��y7��m�UM�5�()�h����t�j~��x���5�n�"�i(\"�7s�`�
n���5p��^�u��X�T��3�Y��z��O��������[�n�:��Vo���������������|����b�B�����6	b��M56��E��4���v��~�����"}�R�W}�E��a.)���$�2��p+�Ja�r�d��������\F#F�����J��E�>�.7�f����4�qjJ��a�2NK�#i&vc�� 	�z��~R�A��xF�M�I�������S.����I��"�Q�M��AT���H�s����V"A46B�=��OWRf���`������WYa�:��p�i�]9�#��Pz
�^��|��}F���F��z���0���
�W+>G��U�����>#~�*�7���*F9�i��T��O�x>��w����_LB80���S<)���
?[��%{�i��x���,)��������{B�s������.��y���h8�?��h:/�U���C�����/}_���#|Q��/�)��l7;�����\�'�����z������y���U���]�s_�� �,��G��7�^��wbJs��r�5������k��/o���)E9�0�g]�\��������������?����=�mM&e�j����:��)Q�J����k~��t��n6��y
1e�]�rM`cg�����;M�X�4�~��U_�$J��'9o��$"~xCT�q"\$�iH�ON)����J{-~�����(��H�3���,
P�AD�W^Z�t��5�f.~��	F�b@�D�^e.�m�M��w�$�uv�v0@�W��="TX�9�5�%a�x���Yb�M;8>z��B7�}���!���{���I��y�s�j�2i���Z�P���l����uL?b��*��~}��
�OL ���_���$��7����:����w�w%��^_g�=��PV���z���_��+$aHT��dXx�m�qX�����`
��1S8������8o8�)O����\`<K��M�&G����V�e{�$�{S�|���u����!w7�����+y<�HU)U���~P�l�|B�Y��*��$?���y)p����]��E�:o�a�K6��|o+�1�3L%Q��G/$���q����T�`�t�����p�U����~>+��$::���l4��xD[2�E�IQIe�|�x��5q��4��a�DL����
�j���kyb�f��/�	<6�Mp�k�T�{���KB�u=H���F�7)#����4�M��x�����-��g'��*�����3\Y�|�O�T���G/O�"M��H����
�����tU����pn����������w�e�+������c���� �a=A�&U����wk�	�'p"�N�A�����jE�W ��tm�b��E�>q���rtq����
�Z�~!GW��bq�.���B�s)������"wP�6�:���:���U�
����
(\	��r
|6��pc{{U�(�����_�0d�
�A�D%�R��0qS�IK�IL	x9��T�]�*�%����Y:M�YL�f�������#9e�N�;��vM�������6��#!��!x��D��L�C�'��������������������j
a)�UM�q�,V���)Q�B��^����k���h�"�^�>�����;$���(����T�Q�� Z�K���1���BWQ����'�2N����l���8�&���Z����8�Z|x�o^xh���A�8<}�����������wg��k����8F�,����������i+�5�v�>�{�����������3�.�k��-�4���-o�����l�Xq����"d�P����]
YD�pT���<� �Uh�*N�<���XJ�G���?h���heF�r��^1-��\f�:01Z��J�QnJ8���$��+���GGBo��_m���QeUxS�;x�8�[�
y����(n��\PF��t9M�s�9y�#B�F
.;�qW^�4b����Q�����w��dy�
�"��q	#0/�)��#�l��k�����N"%�k�Z)�U��}��"nDFJ��kz��L�VFF�����������/K��4^��t(�����[�<&P�n��R�pu�+�oD��`JX6�������l�kN���
�B3��g}\��\?����|<b�/cO?�����	�e��������I�����AqF�n-2�.��!����M5|�*C����y��[s%����gd�����e��+��]K}���u�]�2�uR�R�m$���!(�*6���N�>��0�U�]�N]����:P����A�Q~��� P��#�G��'XB
C_������d{���X�����$���Zd0t"#�bz��Q=����A�*�@|4�PCu\���3%;Yf}4������I�WP�)%,{��!j�X�:Z��F!5_c�n{b1g)fm���������?�tj�q���^��s:��R��b�����t�u�N���K/KVB7G�q#r5�G����F� B*_�W���o��� ";�9FE�`w"�
�9��19��-���q�	e����h�I����Z�?%����k��:�O��Nr#��%�P���'u���!d��52�&-�L�
f��Px�o���n��>jF������Lp����L�(@��>�[��������c����8{�,Em�_�v�eY{scgmuuggk�����V�jn�{�)�q;������]�QL.��x�M��>�yrU�6���a�O$���9�70n}c������D�To��J�^�	��\�x��H�9���������:+$���$~��m�99�M��\���g���Y����;h��7��������oZ��j�<��Mr'<�82Y���]�7�'{?��A��u��3ro���i�����H�^/���y���]�"	,n�w�UQ����ey
�G��F�������F"2,]��%�$��	���7��o�4��jH-c��������H���1:�-���^s7��Z9��]xY��;0���A�^������Ji���9�,W�Rh/rb"��"�_�y�h��~v���F�8B�w�hY�v�N�1�(8(P�@��O�QZv�������L�������D~y����O����S�}�|�&�%F�]p�x��Yl�A)�mH�o�����(��\u�a'3�
�w��w�Sa�l��a�;��;�N�.�#�h���nd&9z�~'O����8����O���5�^����*�;��b?(����,��q#��(��$"f����C`vF	���Y'���>��P~in@���a�$3��a7�s�GA\�4c|
���;`|�>Q!T���
1��S�6-��WY@�zct�*�X[3j�����^��Em���/�$���A��	�����\���bt����VA

��?�����7FP�wXn��6bu],N�F�J24ar�X�Y]�k)gX�?RX#�cu�4�>���V�d���$z@B�YE�#d�p^/��H��N0De�5����!]T������$~���Sv
U�@��g�	���g�faA�|?&��fn����������~Pag%�.F�g�u
T[)�#5�<��)������������i�-h�,o�?#�(�	&���J#�z��}b�#��@�s�<�)<�/��D�-!������M�,�a���7"p <����[I�(W8��`�T�����ts>[
za�����e��R(N�k'�����M�����-���Z��7��%���%X���9q�����Y����1YDIh@1�H����q,�������T����"RL2Q��@���nUPU$�*�
�������Y��-|7�#7<����D;=������W���p/W�J�S:��{b�G��T���`��yA�x����=c4��b����������3�����1�:��0��n�r�h��8�� ��	�fn��m��Q1����>Y�2��):�L[-��p_n���j@a�B�?(�(QE�S	�z�����n
^`��%�^G��}�d!5u����������O���������-����/8��S�"�7O $���{�0����-�8�$Y ��mZ�X�cQ[sQ�P����KY���c�bo
<�{����P|�j&���01��}������D�/�	Gns����;�.C�^�%�F�VU�SZ�m�llJA����O����PJ�`�����b�W1apU�l��F�6i64:<�:��\|�)=d��dW��;���X���3����7��`�������8��P��x��,�Z�
��G�-���4��k��.G�LI���Q��S���W��zQ��#�7�6�d���b�V�rr��(��D#,���e�2L�<%��p���F�HP��� *����A���p3I]E�J�s�h����.W������3;������ �_��U�������Y'CE$������*Y���
�y������_�L{i�r(����q�8<l�:��/?���O�$����K�gt���H����
`c����c0�&����`iC`�^�q��2�7
J�#]�=���Y�z����y��@/6�qR6���s���[0�|+��e��PHb����0.JF��#�<�u�S�b���J?�;!2i����"����Z��
`��Ep���gkzq�����q�J��m��jcw{���cJk�:I|�F��X�������G�5Lq4��o�����3���}YG���K���������TG��)a���N_���X���e%p,��6�`�G�V��dx#��Y3������N���TC>�4����G��s%s�\��������[�>�\fc8����Q�������#���
���Dk`�7�P�<z(�����:��	��a�����0r@������q�84���b���b�%�7����wh�q�>�����H,��A���dX��*|S�m�����n�P��������Za �Yj `H'�j,&�'���%�������SF$9���c_� %��IxC��k�/�'/�i��I ��MmXz@����bt����f��e�b���B��O$[�*�������<�����]O���=�O����cR�Q�v��7{�sN.+:���!6��@���k�&����`^!��mH��F,J��8��Q������,
.
H���L��5�W�y���S>��;�.l�t����B[���+�A��k	��0#�~��xf�6��}��[,X��}���b����,���_�Y�bF,�.�!���;���,L�r1p����6��~ea�Ra�������������^���,�R�Z&��Q���A��y��2�����vf�.�N�u�Y#w>~�(��~����wtT:P�.����fX��������2)�q����r�e�gs}��jwc���=�!e'\��Kt^������)�S�b�ZT.�������t.��)�zs}�2k6�v���C(�����v-��aC��EY���4I�6HSC�v�%-�r�3N��;��=Q	A.��"]��`���`�@|�z}�PU��,�A`��m^P����sOy��J��������cTCV����T�m���
s�	�d����5����t=��=fy���s�7��������8R?��,�r��#�S�6x�������������:����}����"s:�m[�!�-���Q��"G*��F�_�	����M��H��Q@����]\37���d�(��G��{���W 4{�X#��M����|$*��9U�c9����@s�s�m�8������'�PfqY������i,��H7���f�+��^��b#7+����y�8��_�������iv���LC�C�b(��d�E�r��
�b#���}��H4"�ztN8x�fM�\��������9����L$���S�������)Y��?y2;/h2/��b2FB���	�c�?fI��%K����C.��b1����5���F��5��Z��o�{�}��� ;� n��j��^��������u��?n����5�Gm��<����z�7�#��6�WlQ����y����$����K���4cZ,��q�1��
^���V�p�/G?���R���;�.��,���#�^��:: ��|k�6����e���mfU7R�(�'H�)}�+����'�nD�/ia U�������T?fM��am�
%6�V�_�����\d���,q(Q�����*@�y���	H� ���d��\b.L'��)P2I�>p��r�����`L�._�~�g9f���'5X�iGtV�B�\�6��s���#�����N�q)A���y�������F��(v���K9�3�r�K��[�r��R����0���9���OFG�$�������������X��C7�U��Ex��x�H|���b�!�X���lV<���It	MS�������N��+F��xm@&�_�7����	
�������^�x�%�D��h���������$�r�#b�f��;��
�����J����F��3�y��j�p<�
/�b���ptR)��\b���Z�vN��J��:�XB�-�#��g���6Q!���B��m���Q��o���,�G�;���<'rn����9���29
$������8���5�d�#��p�����$�"D(T�!0�=H�qhl������L�J�+<0�1����}��+�]-7�!��/^An���{Mh*����*3�_!�ma�$!(��(8��D Dd�D�q�z:��|
!���Z�7���gvt�A'��%���v�(���:�pk�3��9�)`�A������)z.�Kvi�t!�i�J�1�A� �~7]�R7N����u���x'���`$���ZM�B�R�=J��u\���cz1�#F���/[��T8����YrPW���$|��j�M��?{#+��:��6WgxS/T�u����-9����Ilw�ga�Ou���������
�R�%}������7[��]T*��z�^�����L(T��p^V��B�gn�U���F��*"p�uQ����`��]���4���W�A���VW��w�3�+�G%�c�a��T��<��7Q���g��+%9k��|8�k������y�Z��j�j��|���Sq�m�eA��L��^�(�I[�H�^pq�%�S["�N4*�+�N��3��H��d9��,%���(^�/�Z4����f��c�J����Y��[�����[����41�EG����7_l`:�����R��)����@���JEF���iw#U�6�s����C��_a�*����lk�����V��@ ��q��1����K���V_�eb�>}��zXn?%z�#�;6(�4'6���*5q�?��K���Y���T�v!f���X����gh��q���r&�����M���-��X������~�(���G��k����������i]?)h]���������-(�j��>X����t{	wX�|1�)}D���w��w�A�E��7����~a����N}sg��Y��E�����������u�o�3����;��E!�2~���p����T$yT'�D�Hh��T���G���$�X�H�+��Q�k����F�����*e>1_�;����b�����i���BrU%>����(u~����N��I%%��U��T��TA�~�Ud9�vb���{����A�[�����,v1�/������\z����<_S
��,����b��/�7�E����(�vv8�����c	7X���T�w}S)gkc�&���R���������,
O%Y8�H�����/�s��� ����O�X�?n��opiLK�}hN���c�����R2lu&�C�`�#j:_;��o���*��{����w�~�����S�����ehJm��>����FF�A�P��zB]�2#8 �q�D�c �������
���;B�
�h�h�y���7,�E��..�8��i	�^rRYE>c�E��).1���X�pE��|l�r��qhS�a��<_#��������������G$���;�_���	�}������1sm�L�@����Bn����G:l�0��`2��3}����o.?�s�aXn=GxA��]�>�&[_�C�c�;%=��1|u���Y���/Gv�$�9�d+�d��o����y}}w�]�L�b���OF1'H�z$��2�+���A<�k�`j��ir��B��\��(�$\:�����*6h�r`��m�G�0�<*�!���U��l���y��R�X���j����L�Y�D-'��!��[#��g��Z)T�9��A�W�k��3����n�$�d�$SW:y�J#	n�=����V�>H�aR�i����Q����k~'���H���_E��Y{N��b{F�x	l�7�f���C��K#��B�������S\y���	&�����x����E+�i	J,���[>]L ��w
%���F&xa�y��I��nC��=���x�JU���*��� �s!Z�fd��|��I|g��&[f���*���3��?�x������~�|�������K�W)]�w+�F���vZ��hS}�b������w�������^6�z��
�{��:k���;>�����X����K��E�>�ODJ�-�����3w�v���1�����f���e�����8�:�����k�r��h�O��0XP�i��`�%�&�>��������-�VM8�(A_�+����2�&��J$��Rv�RdX\%�Q�"���|��6n�u�*N����T���z�cb�Yf��o����y�j
g][���V/�^�?-~�l�w���K�p�6g������,?�+����Wl��-l�����\2��*��m�\���N�^eW������V����X���\2�6�\2�Cn��-B&�o��a������wV���J���n
�0zaA�p�N�tX�5���"�k,��/a:�@zm�D9,��l���/�J�O��G�mjqs��7y����wOb���;�����9m��",�s*I�-�-����1\{��Qle�n�F����x�aYo6�-$�5R{oV��%
u+�/G���!u�����qG�p�<N>_lo����5.T�����x
��;��Dt�y/��a��*+���x����������s"�^�����c���.���&�:�o��}�����7���n�C�����
������D3|/?���������;�L���ap:��v}����V���i�[��������^�j�kN
��e��fd/�	����3�@��abV� ��:���lnH)���A.z��2�Z�Sg��<,�h�;��g�������7���
I�v�������>$�4.N����P����dhZ������� �x��!�����	�%Y'��Y�bF#��
&	B�U��gVy59����������OBu��0�1.���}/v6v���_�Y�	^u��bIN���%{U��&���+6������G��#�=��wY~�eK��I`J��l������Og�c�d
�#��8�b��8��j2{�����
�n��M'�F�����Y�
tm��|��r�#.M��LR�b���Y��c_�5prTFA��t%v���������LM9�������/6ftywgm�k����Iy�o�n�d{����>����t��i=@IPvM�cu>�eyz'SO����{�a�!���,���]�n��v�����@S<�xDmd��'�������k���Bt=;�������G�%��;(�6b��##zj��������K�(/�#�7��vw1tw��=���{��Kzp�1���%d����gJ�'�pK�����9^�KN��{JY�B��������`�f}��3�D������+��F�5�	+�W[������y����v��~���f�_�T��*uD��MEq]|+��NI��.$c�r�w;�nu����"�<t'�,�5�j	"]%i��'o��9��5���/�J6�����������
��q�����������?@O���?���%�
�'i�f�(�j�P��|Tr�l�5W���0���~��<r!�Zs�[]�L�w��W�w�HM�:��g�	uN%��W����k�����������g��B�����n�2���?fr���8oD��Q�����e�.I#�sEb���J�}��e����L�"��(����Y��Ea(�.���*���w�[\�����Q�n�����%��!�r(x�����.*{��~�R��r�",'3D�#W���!@'�aD���^2�FJ���6)\���2���8�+y��������C�E����[�����5���A":dH��}
�\LR��`?�!�5�����+�k��@��o)�G�6���{�������c1NV���M�~������1ma=���U���0U�,�B:hRRg��:8k*��M���5��Qub.P"f��p��Nu��.A$Gx H ���e������g|�I�^$��h?�;��	2������i���S�
�h{
��	��w7�0F;��E6
L��<tP�"FF3(3�Y�����.�rF+�HB*h�r��9�6'h��JQb:u)�G����0z��eL��Qv����B�Yx#/Nbx���4i��{�����.�����Zl?��V�Ni��FZ�{�[�8��2�(�5�������.7I�kr=i����tezjA���r������!2N�U~=ZFtA��
�46�{n8������].3����c+�m��4���D�����#
��n	��iC ��x���w�z��H#s�Pg�8��b�0
M;s8f�
L��\B&|"!�N�p(��0���l0�ggv�E`���)���������7o�Y�}'����R��B������)���h&+�	A@�+:T�^4W�(
��A�����^������9J�-����;���h�^Fm��jcJH�T�rc��w()�r/
D���0N��%��-(60-8�+4��t����j��I�p#��"����q{=CbX1� Z9�����5N������y:i� }fti���
���%��:=,S��~�x|\����F�Y��K��oGN��d6�W!Um�����)���������`Stq����bOP3&�py9��]5���L������
k�$��J���..�`a��2r����C1q������v�A!pi:f[��YAhh<1�	:{���G��nF�d����X��0A�iwe���i��N{E������w�(�Y��E4FMt�F��eH�gP������j����QX�,�&:�W9^
����hXC�J�KX�#�G�p���,��F��r�cu��+��>����OG�Q~��^:���9��(7��1��]E���l`�f�
�US���xhZ�����{�_����n�n:��oP�N��.@�@���6r��1�&&�LO�����M�������a�[�����Ly!cD^G�L<�%�+�=1�;��t���z�,t&��`x�������91,����d�If�'�`��~:e�F��B��rt�uV����W�4LmX�.Q2X`�-C����x�KzER[_C���]����\3w��&E;�������kZ�����o������Y����������	1��R��<"���+\����@���(k�$8��[���^���l-x*\�@�$��3�gs_)hkDWi�YaD��1���	w� ��e��4�K��_u�z��c�
a��`��g�BW.�VP�Cd}��%!�N�H����c��1��/.��"�C4I�EA^s���"�P��f�������o��GS v��Q^L���Q+�����+���o��~�m�!����7�%f�f�`�_O�CP�!H@��D��Eg��C������b�~p��u�����W�G��yUJe�TgQ�QD�]9�[��v��j��veT7`�������������$�P(��P���2��T�����im2a�&�`���!��g[�=s���k��'����s�����v{���~���{����F��gO�w�T>�T�Y�������,N@MR;k�������jK�A	��Ghn��������������iQ���t���	�>�}���������=m����p~��m�
���/{i77#�����~������F�l������w/��-�%�{�]��8zQ�Z.&�,�����M�j����S���I��A�?�j�MsU��3�S��v�!��7��02�t�������Q����U����q�A�7���"�7���$�����&v����2WWi�����{�M�JP���������}��=t4��+�<��7�3��M�����������?M���tVo^���e&1�{��g�~�<���~����m���U_^G��zf����v���zv����C��)���(��x^@(g�l������<'��[<�������A��������"7ZE~m���"%zT;?xw�8l5���V����Y��g�G�h|���,��E�Xr���[������q������:5��_�� '�6����_����<5�}Q���1�o��j������]����2��h��$$~��o�K��,��yw`��m��[��C�_�k�����U"�r4��1b��Cb��tL���A�s��1�>K�����l���,,���F}�"�?)G�_ W>
��FJ{��C�\�Q|�P������jGw�'#��W�^�iWl ��:2��-zd�zDB��������7��Em�Hh�=0��kO��d���R�������r���y����G��Vcor��(+��Mc������h��r
{'���=�Du��������FF��c��U��kE82��c���j��s�<�'��Ez�3(����%�������K�i�����{��� ��s������!���' d���5�U���IoI��e/����Z�FJ06 #��;�y�~����&�������R�j�.(����j`�5{,�ot">���0��^��Y���q�����l����R"�;���Yf�?t*t@D�2J��S�A�3��.�tB�cT	zH0��������4��kK��,u�n����yv��0����y��)sVb�e�c?��M}r���n�K	��e����1_������P�Y����W{���KF;j3����P�������R�n)|Z�hS�����6��z���=1$����B#D]�QqOH�,HRh3U}�e3�f�f���a��0�iO�N�#'#�X2����JQ��f����5��i�=l�;o5>\��������W��6�FR*���A��kBh��{����~U����=qj��Z���_%�������
���������Dj)��J�����)D;�7k�Q����T;<c��>[O�X���b�#[@Bi�{d������LP�M3�4����$��IbD��+��T�4���b������P�$�������(��3��
� E��#�
���
������>p�BM
��[�t$eP���KDH�8Y�����>�4a
�[v"�yDTF��J�iN6"���
l���������5p}�Ix�9�[�P�������'|���������qnek���x������5.Z�?7�/0���pn������r���>�F���&��_{��{?@�n���['��}�adG�����p7�_�oV��v/���^�}��0����Ed������ j�����^ZA��p�{J0�7���/�.�-l_'�D|UJ���BC��#�������T{�~
vT�m(�*Zp��"��z/h��jc�v��/�A!�'O��i3�(��d�������?��'����+L0����;j�v�Q3[3��0�K#��]�Xc��u�y�&<@4 �h��u?K�PV6��_4���q� /�	��Ck��}P�m��
 c_d�n�QX->�(��m���G�?�9�--��sAZ��Ee�Ls�fs����>�L�[[��	yS������C��&�Gb��wG�\K����"N��F���5����0���������6�D��'�SN�|nA1�&}s�gFn6�Y�h���e�r���rrU��w�~�8���	���h����s��i��f����[��:�e���>p8F�d�V��{���4�:��q�.��WI&��BL���������i7���I���C8\��z��L�nP�RJ�t)���V^��/+�U�I_�W	,>�?�	ta`JF�Ox-������.��}�����^��W����S����o�~J����0y$��Z0N�����[���2��F����c����3�l��KN�$y��0N���r����G��+�����F�����$�x[$�j���u���)�����S�-�z�����,�Y�������;�	y��Enz*�m���C�GVb�l�0��]�)
R��l����LS�v!�-$x8��:*$q���9G;�0Z1����2�a��B<��\�>dB�-w���nW��|��l��Y%Q=w�����y
�"�v&U��(v2��D%*B~~k���	��4"k��uV��e��%���`c�!�Z=��*@V��`PP��Q��������7��-~�������������"@�C9U�����P��Q��Y��)\TR��O%�����6�2��RH��t�5���Y��f��5���S#�ol��z���}
�hYB~���*�-�x�R��J�q�
B��N��� jk���oeS��.z��w;j�o"�K9`%�!\A��U6f��P,�w��q(��+�����������g��j/��ve����~I���+�b�.[qA��\�<�NyH�}Y�s0t�wb?�6
�.��>�����<�� ��"���<�9a�{�����	��0"�=���E��!������wG�2k�Q?��8J;�#������������lZ�_]\Q^N��B�^�������k������PU�Gk�O�6oq�0��a�@��V�t����A�*�I���*^�;}Z�K���e%�Ob��Z`��U����Wv����lY���vn�����k�/-���T��BbP7�6�x��}�L@�Y����o�!eQ��B)	
��>��6��f�������V��J�����l��������s�\���2�Q�+Q�p��9�-<!�%'y�q�r�q[N~��T ���v?���{Kd&|�Ps��}�����I��/��@n��KP����lMp
����C-!I�<�
x��"w��#U�\]h�SA9��I�&Clg	��V�����pQq�i���rC
�EC:���H����N�9]�n�A��
=�����-���/�G��o&�O�s>q��5
�c��O����;�2�W�s�[u��,a�/\,D��w]�C%�e����&dd5T
�z�u��U��)&.5�<�WQs*nMF��S���c�}���e�� �8,������VVVG�g��44`�����\����cH��#���JP�K�>r_Z�B���Z0�f,����KO�. �XJ���z;�FH�����u9G9�E���9���Ze���X���s9�C��y�Y��eO���$���+������sH���[�~��nD��$Z���6��GM��������d�� I��^W��E��L�R~���r�7tN3���9���5K��,3f9����]�Z�XxK����YH
��`^�&��{[��j��~�l�C3'��+�
���z���KU{��~�r"����y���������3�����?�%�w��exe^v��k��j�7�*�P�T�+:�.���.���+H���rf1��QD�5#������_!)���qt�B~�������K2�j����^��W^cz��|��U�a�/���@�dKJP��	u����V^��<��:�8c��5	`��0����|c��|�<�s�=����ja������u:�tJ<r���k>�ej�}w�_���N�o:,��,��7��I=�B���k����.Z�)\S��Cd���)�������p#ZS��8�� ���n�N��E.E�����J$��cU���_���Wh��w���?A��G����7����Jdz�����l��Q�$��U,�����=��V�u��J�1����~��qz�xH��SHv��s�9O}����2���T���2���	�V	������Z�p��Op������3a��k���"�6�^�]K!	|��zv��Z�=Bv��'�(O�[������T�m���?d��������������L��1~��+�F�A|P'?/.0�:��bQ( 1��^DeaA`��_���c�k�~V��
/��W(L�`Y���L�YJ�\�{6�Rk)��@�r<J��0��P�L9�K���M����;_�7='�@2PjV�u����c:���Z�;#1ia%99�ge-Z����,:E#E���nu�>BenEYmtlv�mqK���u�r�ta3e�L���a
��T�P]W�Fy����W�6�X'�"����V0M(� 2�4�����&�>�3�o�E(�`�JD��� �,+������I-VDO�b�o�.�(�y��Vd>����+-��i��w=�F����c��;?��z��Z+p���0\
�����nE�U�
�z�������6�;�nks7���\��|h��*&��w�����;�u��w ��:b9�G������7za��F�z����7+j��	��!cg��r�����@q�-_�����/.�?E�B�*���gX�QQ��N%fb�;]���;��31�Rl��&;�";�;K��
-��z���(ma=	VL�~$_�wl�^9]Z�CXG1�<��U?��'F�x�+��r���������xi oOZG��h��V���b�A�*���V�HuVt�OT���z��T�Weeua��XBVg]�����������v�r�s�[�}M��oo��w��(�R2�u���u;���"����?0�}*	4���s�0����0m�72��O6�
 �(p��,�t>�v�/�U\�!Q5�U���
��
YGC���l,�D��2��
���U�����)��K�afPL����%��*���_����@*�/��*�*�O�������	�\�OLBvn�Y�R��>�$�NN@���0�Bh��pb���C�\0����Ni�f�f�wFv�a�_�1j!�o1��Cx���M=s�\M��:S���/*�M�l�
!�m#HK�T���@(�� ��F��5���h W~��L0�
�w�$\2�-��I?3�~-"���@�G��S�������~4�ZK�t� p�H�gE���_{!�Q�"�I��.��A�����X�������\@�?:�M�67����CoW9�Ec"�rQ�%1[����~K���~��cp}9�
�������y!�=�;p$jyI>0�T�9�DlQK�G��j����$a��hb8�}���i	p��|��W�4��a�0e-��?yz�U�H�6{C�b������E��_%j?T������!��x����d(x�"������%L4���(��]�UM���2i_��T�@8�*�pn���fCq~]T�82'�c���U
�~%'���H��v��bf�V�U�����td,��o���"��=H9p���������ua7��]���B�����W�Ek����B�����0�Z�cs����m@5��}�Br����S{3��.��P��3�
�D�����~��G��b��N'+l�h�z�&��H��>�/�@a�O��ADKHT#9�S8Fj;����Cy��@?g���,+C5�������>A\��hc��Z���Q�FE7��h�^)kC��{���;-����Yr������e��D��,�X$�F��x����d�W��`p��]��O�Y��ZIr��L������KO���5��,�ua[�le��s['����CUJ�e�	[���A|<�!h�G�i2D^�1�I�"�#�@��o1S�h:2���X��0����"Vc��M���������P�H��e}$zs/W�Eg^����"�W+��F=��G�`���[\��d�a������@�����w�o���o��(�[�zbO�>�3�m�H�.u���$��Z	�i�2�"�]�����e���Rc�A)��+�e��r��@��'��Hb��Y,$/i���\���S�E��'�h�o�N���t+�[^�����F�A�\��1�.o�p�P��rvL�+�Mf�D����.3D�>�ti�t;�`�	&�v�QC�9�J}��_���R]�e}N�+w��]v�����88O�U_�����Z�-��*u��#�o�*�;�V���9�z�i�o��q�������q,���6\WFA������K��������9�G�Z%��Ha�`+~e����w��O�{����[�����1���O��d�-���%�a�&0gI��<M�D����q�/.�k��F��qf�V]��
Y�����������*��"'XqN?��BTt�hiM���&������33�d��5����6�h�B���f,7�[@A�&�KH�Pe�,�q$��vm����
��I������TC�T��o���}�E�1�
Zn#�4bP/��6�l@h��Be�w��Gl���(�>s�a���=;�a�,�9���-�8|����w����e$�Z�M�J '�j�J�a��_.#V*��*2$�
!�J����7\���R�����6 ~\j����S���}L=���Y���Z�y�n�����8T���%���:
��Vj��B�H $����dp�� �s�!�lQ��j0d�d���3�m�=,��Yd�b����`8tc�%�9��9,p���:�e�7�6�KtN�����|,�L�x���v"lK���U��y���Fc\4�N������6	�dP�0��M��wn�c%~J	�Q�7���"�H��o�U�SU���H�B�d��cd
U'�����L�t�d^zJ��������|8e5���w9�E1X%�W 5�W���=����c�O��.e
Q��B8��M�RF��T���`���)9dQ����~���BU������������='��3�}�����D�Y!H)gJ��#�z��^��8y��9vF�X\����4�����D�����$�F��!E�JL�6r��AaZf�S�����e�^YF��C�=�|t�u(e���:�������A�Z���}KT��(WR|2P�����2x��(�*�%���b>�>���@5���'��@����{���JM*d����R��b����4�`�����T�P�0!a����)|��	Ms�L4��%����g
?����������ox����{D�rD���Ri�!���q�u���l��������[��7w%�teee���fj�B���F�E�l�e7�)�!N���bE?c�|��,]��=��l+F=K��S��^L��Ly4,.b�)�X�&������8�����X��i���8�����r:�����3�0��[s�CP��{K]�i��Oa���l������z����Y_��m��5�Zi9����a�+�� 6����	���b���PV��� �O���
�o��l5w���#���y�%*y��t�{
*!p;*���W��gf�sY�-3^1o��_`�DAp�W�ED��/�/f	�H�H4�Bg��}zYd6D�?��*-�*�D��e��
�)�n��8j������~0>77a��n��b�2C�k�\{xuR-��m����.,�~��Y~�dv�g8��?���bn�r�����\�������m��y,�����RP��FmV=C��&y�
L���0U~�WL��LMqedY/v�;�c�l�w^�F?	�!��C�{��_y��_�6�_^.�2�{�>���XV�6�0o�Nt�D�Y�N��#QyJE���iR�Y���Tf�&�E��ZX`������]����s�^gF�7�����
3���x��
�L�Yv?�)�5�nKN�+���%�v�ZET?�LM�(�^p�����g�DF�p����N/;�_�G������u��l��M<T!1��C�)��	(�n�oF�j9����C��:p�k5�G>~4������!�� �>����R�{�����a��x�Xp�Y%�?RL�8A���kO�����.�n�?"d�/��K�4�����9j��5��Q���E�
�8�W<��?�/�3�8�=�$�d�mz��U�'��"�$/X�+[��@01;fe`��������#�H=�v����;�i[dj
�+�{�t��!4g�<_��j�u�_]�6�~h�_4�5[g��s���\&{��Dp@�m�����i��<��S�$��.������p��[�`�4VO����"���������j�)<m�qHh�E�e��V�7���'���%p���0�����i6�Bm2
aH$8������.���Y����G���1���S�Y��=������F�rW��5�2o���o�e�6�����_��9.�d���T^��R@j�� �|���UF��X;��t���y��0�o!���}�
|8�������pMEI�Ek������z}�C��cN�'{��-�����w������o�6���08��,�Be��qe����91>���q�^8����l>Gg2�1�(�����D0}���>^��'���ww���[����{�����)�U2/��1��gd�����M��nlHI��-��7�M�;�
�ak�����lw����������zv��y�Y��J��6�v-����6B�f:fy�!�$��%�A��N�{�DR<<yG��"�+�\{B�M�C����:�������t�����>W�}�5?s(�=��
/�8]�R����~�����d!���j���^�X�bD��x0H�X"Sd1�V��wu���	�_���'���V�[o9��0�Y��w�L������}4���>I��um���..w^\>����<_�7������X����.�W	�y=;�
I�/<�M,�B�y��I� �K�_�[
l}1�K�J�[�B
��JT��4�x�,��F����2R�K�� 8�q9��(�`6���)��
@���+�2vU\pU0���c0�9���\;,A>�X;~x��m�E��B�R���@DV�
�%��`���(�QQb�%4�c�W���e�i��Pp��	T�C�����z"z�B������$������\��Z��K�\�y��(����I�{)uT.����1���p�%������kX���_�~E���p8�`�����a����w��7�
�(�^t���n���f���,�<�k��'B�\d~)�;��������[I�s�|�$�</w`��Gk|7���3d=���d_�YF9BI��T��\H`]�:H*SK����H���JQ���H���4i�Y�:c���h�8)~�����+L��-Ev0�^:���
?[W����N��7�fD6�3�N9
��X P=V�����s.A"Q"R|��j�N�n�.l�a�gl�7��s7�	�3{*E�&/�R�d*��������F� \�|,�H9E}	{�����	��Z��R<*��LY:�l6�y#m^���9���.zf8#���c@���d�V�B��^R��"�~�8�o*QL�}�BO��3��<�(�kt�a�3b���2+���Q���/Dp�n
I��V�QI����_>3�3���<=�"
 ����a�e�u�+�[:;9<|&���~h�_��?4[�����m��]������jv���z"km��Ck�yz�~aa}�\�I����M����	�#\�3Ft"��[���%��M�9 Wy��>���>�)�]s`^�3���C���M.�1
���FE�[�}3��	�+j{�r�/������g�T�4�\����KOfT�G�/o��[��}|6�w��������(S��B��xr��g��?I���`���Wy�A��%�]��\����,.��?�!�~h`?�^B��4������S�1�?��K��e0E��j8�����"N���k���(��-�P�OXO ��v�4s������-�_��?U�}1b�jS�y��G���{/
�����{�\"��^
���Mi�����78�~��K�<{T����1��~dX��m2tN��O������� �n���^�.������
2D�� �Z��Ls��G�?����w������niI�����ot�����tFm��>��v<���X������b��{�g;��s3�C=���*�yyC*�9�b�q�#�[q�c-����,KHIm��7�
�h*�L�%�L5�h������'���P(���S�A��U�+i������]�d3�&�0��^d(�����Q������)�� S	FV�U"W�Vmt�vZ���%���`&����I�C.z<����c
h�x��7����v���d�F*Py�Y�^�!��O�:�i�*��p�����_.���4]�d-O�s[�Z���$������;����K���&z��Yol+������$�>��,��>,��^�$�=Www����"���W��0�{�V�R�<�&�@�/�j0������M�0V^{W�����\��S;}�j�^���bB/O��)e�����|6��	:�?��2��H�����:�4��c�u�n��x��T����
%��s-��w��X�'��]�<�]���PNRi�����Kr���W�[��I��Q��kC�P|����-n;�9r��������� ]������Fk
��G#�������������9�|���9K=��M����P���u�
�:�[��t1}
O��`[����!����M(39�����8f
�N^����3?'/�������Rdg���K�	��o��
��9���el��U��.����V����j�_J��:A��(���G�O�;v�R�!T�FUf�e�����%!����b�:��MC�IX�{��m��%��JT����!��X��p/����u'(SO�HP�H�t���o�(�A��*>����.f���`�*8��;�tR�L������F�Y.����U��<|���i�x���d��6�T�Vg�n�5i|��pg���:E+�6�tfY�G�E�7���D����p:&Fs��k#
�Ya_�2k=��������hm}x�����%��10���C',���P�����S(�dVa��&��������F)�&f.�%Q�&����P(&�C��Y��������P%:�0�"�`��}��H�}+��i�}�BR��D#T����A���������^7K!��a�nJ����>�v��e}@n�)xy���2�*/:'6�������d�%���#B��O~���)��|w�x�Nf���K�t����S�Z_�&e�96���r�_�"�Y��mQ�kpQB���2��<�O��o��o�=8���|ht<�Y�!
M��,+F��/p�N�5
	�<$�y�o���p�����K2�Yy��[*�Z��hM��)�Ot��������)7oa!��[��`�Gq&��S�H���@���I�������gK,���cJ����+�D�'��\h�	-���K���e��Ta&� �O��g�� �3%?�\�������w�����3������yy�����&|���vT���F�������7(5��x���%_�_���r
8���u�VF&,th���9��?S9�����Y�`T�gS��`�>�2����
J��Gb�02��
/��y7�]�������3�*�xN))d�*&�b����������R9�c.y)�q�(�Q�|��L+���T�����bQ���r���U�����(�?D���Q9V���Q�����QZ7C>�������c�[u�q?�jw���Q^ ���+�as�������LfV,��M0���!��L��@�+?X�����p��1#b�!����Jd�s]��<�ShE3(��E!"K��co-��7��"Q�0f��=���<�X��Nf�����#���G3��/�������l�:��3��By���X���e_�������cGF�Lrg��(^+��0���l#�`��:������K�=� ��r�j����9��dV��&��a���r��&@qY\6w ALgP��@.���Py
�WL��9's�0hSm������,���t��3���
���uA�{�]�*J=�UHF�4E4�X��`����Y�0ch�5�ik��W����+s0v�R�r`�eZ����+��jaW��<?�U��`��tO��7�'�q�D�6�Z0���Z���ED�g��^\z,�4�H���`2NVl������P�'�ePy�~�0�'��U"���Z�^�j�.��h�M`�T���Z�����m�u��Oj��V����#���w����u���<�?�UC��Z~���,�%���w�]O�q���N}��S0�~�����}Q��7`��+������$k��I�	�P�\f��wv��7Xy��u")&����z.��mF��^�4n�^A����'��?sR!80������W[�I�\�&�>��b�h�/�\�"�������]A}�.z����Z\~�������a��9��.�?u�AP��R��<
��nV.�he:	j�*��������ni�u�J
6RGEH��������a���G:~���������"^��	����5�e�O��\B����n$W<��;������������K]��{�'�0���� "$�,-r.2���v��(����~��������f��_E�eEW����S1�������\g^��@|�P�^��8D���`1���.�#�;�J`4�y���EoE"x�����0'�<KK%�	�ge1����D���J^��{����I�A���n�V�R�"O	����w��sX�j�d��������`��:T�gF�B4Ma7RVMS��vM�=s�f�����{>-��S���=u��!�Xl������o�]��-ST����!��:�����������c�-�CUh�n�]Yq$�T?I>�5�Z�oO<�w<>��o�����r�{"7C��J�!��Vh%�.,XJ�������0d��e
�����h�]GOLW�e�C'�J���x�����w��/�:�=D����Az�I�}e���r}�DX�~��8�}a���B���
�F�1T�����H�������y�wE9�I`i~�����B#��Rj}
�Y��Dz(��$e<bH�Xt*����"���
��R�e\��G=_�q���z���/�7����keJ-l�����<�>�P����u��N=	 ��l�;���r2�%��*	w��%6���0�L7���f*�.,�h����Kx�#��w�m}J)Gj�Xz�uW,�\�����y���f�Vt�2j2'�%�=+�2!�����HCU�
�e����vmo,=�V��w�i�g����}���1e��T-x0+�&<�r�<.��]���v�U>�S)
�o!j�(#�	2^�������w����U���lp��=L����
���
�9�����"�?R�y� �}��>yQ]_�Tz�/5��T��^�]����/���80*�V5@��)���=���!��,����������������;����'�p\�v���yJ�#��HpPf/z��U�������_��_e4��vcn�Q���n�m�E-D���dk�{�%�ND���m�����p�����kQ�k)����n�9����B%����aS_���������e�N��Z.���PGe�n���\� ����Q,h ��4��3o�]u�dN����H:��)��M]qi\��"�*4_�e���U�]����^���%�1Sk���R����^'p��,}Y*�{�����CZC���s������&s�I�����#P�����[��$4�p������n�F
f0�h������SI!��-6=�Sd��=��Hg��pYb�*f���K����p2��&.��@Mi�����1n'�%�a\''��3=�T���0b`Bij��%��K^u(+b�Z�.�_�Q'�d�-R��S�\�?~���*�~��n���M��������z��Q����KP��
TK�����$O���Q���W��~����P���WT"�����P�	B�H8��t���������`�U�@o���B�G�rIw�3Mb�C��b~i�\��Zpt�oeV�>���<��C�+��^j����0��?���Y�����a?���V�=�q�������������U���E��b�:r�C���iy�/u�8Y��U���	N�H���o���"S,jej^8�)�7��`�@/�}���.��*7f�V\�[�F$]H?%�.��Z~{�6��5Pq�J���=�fa9+��, R)��4S��\e���Z��4����s�&D����,j�Kq��YoU`O�P-�m��W��2�W��A�9]��#���a�T�-1�8p9���*�����{�P��j�-jak����W.�H=v�[��*�H7(U5����BA�,������ipd�F6�=��$��%iLd�
�~��x�����R�����,���+���[�W��KQ0�������0������`�i2L�H>�
KN;���KhQtsL�h6��_8��1z���~l�[~[y��a�Kd.7=�
Tc����c�����f�������+��%��2�Z�c�
5>j��h��q�A��Z��O�S��E}���_�G���U�z)("�Zy��AA�q��S��w�^�}s���~�-��;}�����G8c�i�������tXN���n'�|����y/�T��^�7	��[lg���X�(����o�������Ip-��U
���UMy��
����a
eu"A����LGb=-[N�d����+E��:q�L�\j���H
����r�|���=a8V�/(c��z��)���Ks�������
;��?���P���i�3��=���\���o>�[�'t�����M��'����[��'gG���q�=��1�~��,��������pm��N������V<�1��^��Qt��8��{C[��f�$�����tzJD���R��9�U	���G���� 1��}���wc�>�Rs���TUP�}0[�'+�EOz-1��Th #���w�n%%�`�����Dl���f��~���J����9a� ��A��R�AP��`{����j�0�c�����X�%�V�|,��?��������I���*�*O?�S���c���U�&M��*7��E��3F@*��k;��tta/�wbx���n�3C��/D��-l �\R�5��$%�C�2a����B�8�3���<�T���0����``D.���T�vh�����Aek�e����P��R12Z��@�!�C��Sw�;�MC�Aa��!}u�
>�R=�t��s�W�0a|����|��9���}%������E$#�������(;�1����]n��Y%P�������0��RQm��b��<��w����B���\r$~�����m��gr�Qi_��jK�vy�*/�o,qc��CW�n�3�lZ���$���pC�@�5�EYC�P/���������������ON����(q������qy
0pl��C����I;H&�\A��'�cB"s�b��-�:����-�t<�xb�
��"���U���Y�} �J�j��6�g�.�e�*F����k���4n��m�WDL�^�x�i���\�@�:��[H�s���Q^*C�F�d;��y,���nq?"�d�pC[�9-���-�2?����0=S�0Lo���l��`$����p�l��!�T�G\.���B��PQ�����3�o'56�7g��U���sx%��c�M���u�=����t��L5���>h����pTF�����h^���D���s\���*�.T�deBBR}�@�SO��t��L�����4c�;�
�?�q�S���b��\��"�]�����.��D9�)(��e�g�q&1��cig�4��,�o���C"=}�~��Oxt��otT����y����<�m�b�`��dSf*�(�i�,�����/���7�&������Ns�����/�?�e}Z��$���9+s��%����nt��[
�s����h. H(���}�7
\]�;���B��9��B�(LviI�>��k���������JbUN��kr�O'��h^��|���2���
�wJ�-T76���G�&����1�
�sR����������	t�M`-�d�����[_����i�-v�mTH�e��;�4�� hWI�FI�W��	����$=������f���~���=? U�y"�����g��a[p_r��G�
�Z�5�/_)�����I��]=\���;����K���Ji���Y}ly�dqo�(������Ha�E�)BE��_)ZX���x1M��-b��y���^�~�V	�}_�P0C�����u���f�>\vy��z-f� �Es�[�(�,u(��B�Xp�!,m��������_|��e<a\�R$�E��R8�y,~�Ta;��gL��T����A�3PZ�
@�O���������C��6[t�^����l>.����������R�!d��;7�s���
^����h��B��t��\X�����m4����.���OYs6EY�Q�{L������$����kx7��a���������l����x�!�|=/)Og��.>CpL�L`)e�.[��l[���^�RKq�)�Um*��!e�c�s�0����~+�&AdP�D�?��6Z�Az��+���������FGM���������%��2���,�3O����mUF��"h9����}W��vu����^�'>�Y���f]0;*~"���b+����Z9sk�t������~��l_@����R��u$���Ejn������[�R�V�
�l��a���_e��
,c.MF�����������Jr��I�w�I�L,sY�����vI)z���N��}���&V43����9�*����t������A�R�y]���g�R�>g}Y$�\��PC�0>��&���<���*Rj��FxI��u�Ga��9*� M�0�?g�w�y|�<kE��H�Z�7
n"S�o$���N������^�����1����v���qC}�����1���c��MRZ��)�������X=%��~�y��:��*=��^��"�W�!X�p����h�~��
^�����H����5�H���pi�p�����
��T�����*+�����~�aN�@�c��:},�\9��at?
��G�Y\���S�|\���QPy��d���^V.�%u%��� �\��%��R�V���a���+�u������?��|<��d�Ny��z��{T5�\���;O�`��I����t�����S6�ru&��GH
c^@�6�^�����O������d;q�;�By=^F��D���������[�Z/��^F��}�a��0���0��XAr�Y����M�r>����>����6\ �X�Kn�6�hz�L
;��p�
��F����U"��&k��������%����x���NHAc���S�9X�"������*�*�FL�������������2���_g����Pm>�OIe�������!{��$@KVV������Q�_�R�K�����C�����-�u�
2(���������ht��K�������"}�e�k�����J���}~���-/������de���,��w^�&��@�+���x�}��`�}��6 4���j{8����7��P�������������z&s���a��;�L�������!F�
&�=m�
;������������/{i7���y�J�������^r�yjz����,\����x��������AQEu��8I?�y��������N���B�����*�����6�%��UGd�I���f����:6}c>����	�+<��w���@Mp�k�@$����l!�B-�~� �I��Cx��
[�u|L�|tu�@rQ&�*�b�66b��n��9��
0l�r`[a��[n�����w���s1 �Q!a
����m<�]]��\�p�,��C:����c��`�a���_{ GwzF3�^X^��������
~D�]����'�(T6`��_��l��"�����c�E.�U�f�?���V�r-}m�
�9Y���A8���!�c/��Qz�=����y��]�s�G0�)��8s������g���L�q�-���ttN�zY�}hhvp��k�2q>����������~i�3����D�a1V1>�����
�H;�Uj���{\!?t^�{�[ys����y�� ��*$f�����
�\��}�6����%=���E�l�~����rs%�7�6>^���G����|{�;l�7��}c�����-��i�����x���S���4M���nz]`��%�?��}9*����ak�������[c�������p������J
��-�b-���n�5�)4�5��(�4 ��#+�?/.�������K��L
Rv��:�lR����l��i�!z$�+Z]���Q��� 
{6�#�o/,����	�^���N�z;��	WxM�}�>)�����������i/gWt���^N��!e�2RTB!������"�$7
Z!�k�R�=��*9���h��P���w��4�J@�'$g�[����u����Iko��p0,u�O���s^�DY^�a�i�!�rk���b|��>�d���/�p�q��#|�v��k�z�g����+?������o����Y�/�������O���i��fN������(A��'���|������`)rC�������#�Zi�x
�o�B����l��=@����"�H�^�m��~y�.�&�Gu�iU�=���� ���^x��a<��j�zJ9.�/+�5��RX�E���9����#��2|������������+��8��9����"��:�s����69n�|w��A��m���*�?���+��H�O��X�q������������G�<�a����?�FX�>����������j��Pb��8�C�7�� ��Y/�D7�v���Q�=���P"��A��6�"�b9�,YL�'!~:J\0����Ag��m+�u�����{�>q��}OZ�S�xo�\����f4��?^�]�����/_Z1sU�L��7�~�����b�>�^��$.�z�&7�!��t9������3��(i=f�e��B��K�%�_��*���=��v�z���'�`�E�tq�
���Vz�l��xF�5�v��i������'C�N�U��^%o�}|�:�89=5kN~��x�t��4�V,.�u�[���)&�G���N�u������d[��K���_G��y���l� ��L���4p����I���?�)�-���\j�|����d�����!��lF�
&#�L�Q��1��*������������E��T������_5�! b���x��\_;�'�����-C�[�_4�.>�*��z��,B|�y�gv�i����q3m%�8H��5u�KvF^�n��0�2�lpU4,�OQ����=.,B���J�����O�XM��u������Dd�!�����[��,��}4�>
�5���T / ���{'��������3z���Y���Y���.�@4MA���W\�k�
��GX�����cL/��������IW����p����$�e�>��2��i����������6�ptwJ������)��I
��%�8k�b�y���0��������G+gGz(yS�����k-����9�q}�P�'B#��=Q��n��f���i��0S�	�s�A���T��g;��{�6�N�X�[���*���3���+���H�BY�����Me�,�k�k2���'�qK�����m��=�R���nSE�)���se��5�+��'�����1E�	������w��@�mg&��^�%�B�j�����/:%
��%qz����=�(c��<��X=�a���J"��VDzb����L������f ;�K�A����s]��p��j�A��4��i��F�"3&<�z���p�1J�[���M ��&�Jp���'\�lByto��_r~`���q�4��g�w�������{�e	b��)�s�q�))�
�
dC��i�W��b�cS�G�)�T5��8D8�����tp�g��V��{k7#X�	�������P�����������S��Z�GI=X�'0+����a��6w�����K�d&�Tq����C
��oo�:g����N�P����������������n�wtq~�!�z�K��#+!�{��@��w%��`����%�L���F����<�?����
�wv���s^1n�'��'��������0��t�N��s/�	�y��J���LI���+x�����U��h{V�d� f_��/��U����H�'��N$
��t�Y����c&�4K����t����
v����c	��68�����C�&�u�f+A������O�����,�1��DP�,��������T:���8b����2Mk��W:�W��/[i���dv�l����#ED�������%��"���/����?����2i�����j�����A^M{�V9�
}U�Q`���!���^
DF:���"�x��_E���4���p��(�0\�E53u�p�;��&��1��	j�����mES!"��j�{yoc~�_��G��B��xy�oz�W?����%���L�������Y�y�LL)V��#��Zo�FrGrYNL���7���WR���:	6��*�xY-�����g+��'e7�HA<� rR���i"����=�K���]�e�|�u��^�+��L�0�W����w���U�(r��!���[�J���jV�S�o�E���Jw��=��3V���	�@��N�7���a.x#5���
p`����vxz����2�W�WKkhnm��;��cA;�8Yf��4	������	�M3o��o5<>y_92����j�T��t�m9w�;)���`��a
��t[%���V�g���^��g�+6�S�����/�Z��''�;��[{�sl*���0LUKA����^�����hv�'���'�U]W9\�E������j9���a���h����)���T������.^��~x��%��%n+����+;�[��8�����*7n*���pU���^�nQG�f�������U��<_��o��(z����su�X|
������Z��'Jgu�"%��@���nGG��X�[S�H?]f�������C���2O�s����X��$�q��R �(uW��o�G������
d���u�S'p�3��������
7r�������@��MT1��?(�5�s?�@;����3c�w���
R��SJ����a�v�&���O���_�<9�?#"Dumb�>�1�p]pQ�(��5�����#��mnO�,�`lo��r�������Gro�)b|?u�N����`�S��\tP�Jv.�0}��j�b9sX,J`�%�s�~.��pp������)��8�9�Lt��GS���g�8-��N�W_�eH������U�*���<[����9^�\���%�]k�:����X�V�2P\-{��* N���5	���*H%���q�<Nj��R1���T�6U3�9TC�g����tU;�C�H�9�����M4C&[��E<38�4X��.������,k�+p�*D��9P6 ������FRxW'�{���DcJe���S�#C�j��LfL}$����~n���K�!���K9V��#������ig*y���B�=���P�6����R�G�i�I�Xi���?����X U�r\Nd�����;��q<������)�
N�
��*����*�O��Db�ZM)�O���F4������.�����/$�5����c�*T���,�KV���29����d�J{��YxO�5���L���
Q/���J��J.�Ct�*�o��f����)g>��EY�Z��f�S�������1�&�^m��
�S�w;�T2"
����FO��V���5g�*'���\g���;������#T`k9�
s]��^�j�Dh���������O]*��L}���He���Q�T�yj�zS�;�7��.+1SI���#)���4,�f����=�*:J%�������t�&��S�p�CD4�x�!s*ISP�����!����@d"0%y����w�Z!�q����|#����������r%���:��1��1l%��!��{��f��A�*$�7V����	i��u<��
'.�1;�Xl�&�
��	H�{p�3�4�P?�"���)����`�B�Jz�M!jg��7�,Q����������s��5�e�����h�5G{tFW��P)������|�WW:f�!��}3m).�s&�;k�KA�*�)F�]L/����[v�e[��&�5�
��	X���0��������Y�i�F+�V[�~���7���F���^�}P4o3Dk4+������hA�'yH���0^�7�3���\�(�{^�$������F4�w��\~�]��z��[�/�_��z
�C9��7����
UajP�*@��Ggg �_]\�H�K��T����7=�\�oU$�y4�Hz��<�z���]��O�G�W�?�@������f��f����X��D�z�k���+O��QlY����|j���}8���Q8�Pr��Pq���w���;�j��������Z-��3e)8���w��v7M�?������K��������)���k#���x��
�����"x��"&���t4���<���~
����������"��gz��ipL\81f&Z-�3��L��_�
��Y���a���J^��}���Co����y �>�����S�.B�,�Z�q$i��!�/gLeL����g�iwfD�N�O�+�wr$�V*�\x����
�����7����x��h�Q_/�6�t\M�wO/����Uu@c_�/��=�z�'���+����7�6{��_9�M��x�u��}/��3��V~���,�F�Q���F:+^����������S�O
Of�h����)�k��|�,n3%�B.-��d�]���6�N�M�����k,8���$�8����N��q�FD�c,)I�d2��^EQY�h�����`����3��6��X��Gq���!U��/@W���k�.�o�cRnP�w�C4�2�Q�4��d����Z��/)��!����^J�2�������}$���k��:���G�pNJ�.\B����2��.�pd�4"������]���kIP����+LsB�p��vP�0!���M����N�_�H���I��qR��O������,��������@?����g���P��,����R���5�dSc�V�*�
��@%
"S{=���}�f+>6
��EH�%����+�	��D�H�����xX��{�%���/��~3���5���;�,L�����q��=�i�9�A�;�VeD�Z�RGa{}y
�FM�����{8�$�Pu�L���PN6���
qD�_�������8�
C��\uo f���p�-Q�X1or����l��Mk[�6�r�Z�~+�4�~�Zk�j��`����W���e�.;��Z�vm���������b�S�����^�����<���PA�>��&[�
n�}q:_<g��D�mPw��z�vky��0I"MW�� �?�,g.\	RWp�W����a�����nc18�3�dO�l\e��BU-�L��{��~����U������l�amo-2�}�nE\k�F�F?y��>�W��K2�|�
[F7_N���\	��w���r��?<���:�/������o�%O�����x!���c�IwH~=1���=6�=[�k%����
N�d^������i�f��T�3K��������AV@&��W�����E$j�2���F��N����Vw��G��m�X�L���I�n����o���9�lq�a�R����*�_�=9R 
>�y��]���x_8{�����nz�X9��Gm4w���_�'���,�
���t�_��/�����"����
%>����C*L�f�����J0A���8`1(�<9�bX.2]zp�u��(9j�1����� pIFVY��/��T��_�07�i���������w�*���"hQl	�7������O�9o:J2)@�����w��"���w^6���B�����%/qX��M�N��Bl&.'���>������c����M��L��\�`���9���0���(j*\�]k��QcgZ���m���;���0�Kv�e��Yc�~�gCr����5`��|XN�<b������>��+`1�f2��9>D�{��j��s���xX�Cm�7U�7�����M���.G��<���U��xuF'�!e��N�D&�2r�V>D�>c�)�v��Q�X�
WS!L��Q�=�|���QS-�Q�p�����i��n��x�c������y�X�f��`��e"6Y���L�v�?��c��Pa�F��h
�e��o
k�{�Z����*K{�F���i���9��e�N����~:�E���..�0����}�(�g�<L��
��(�����^���8#V�����-2!��*���|�W��(r�Hs�va��A8Y��!~g*A5��%G� �^���,X����w>t�T�g�G��R��s��~X�az�1����Q3�}������k��i��0X����u�Mx�
o����:�0�C^�
6���������<~��@t��;@�x5��������lt��9��n:\���KA�\&��7#W�jV��n���q^PPK�8l�L��T�[���>�������V����fo�N���9�c���/�������;	g �P��Y8�<����f�������9�t��I�)�7������`���-S��*���:|�r�����Q)����j�
�
�V�����G���Ns/���f���
I_+FZ��j��?�#Q�T���t����&�Xi��Y<�"�X��d�T��X
>��MK(	��'7�	w�;|�������3]��z��f�3��������Arz~���K��D#m��q1&�x��Kf��
�5����E�4vw[��v��Sm��*�p�F�AJm���OZ9hBb;��h�+��������?�'�dJ"%����
x2 �����th.E,���%�,���A�d�4�Nt��V1�x����d|�b1�s�6��OF��h��A$���#�)PX4M�H9=�*u���)���jP~IO <��?q��H��>�T��B��EYNxe��rlK���^U�S��Y�Q��t$�9D�T7yNpO�b������6�F����� �Pn=F�
��O�0��br8y \���D���R�_��s�>G7�T�`�(�g���Jj������S��_}������Z��@�UU�`U������b�'�J%�O>��eDE�}.�lq����XO�;2A�d]���������r)����5u�6��@��\7	�9����CI1�����U��k��J��bb�U�K_��
���!����O������k{�������@/FI�gM�w���e�9w�`8=��� a�0�����F��0�1��L�4��x�PG�?�*/� ����7���?�YRe��������0E8-�Qoq;��[���s��^��vz�
�vzr�m��\�Yz�&���m<����Vc:aJ���&n���X�w�����&X���sY��n$��N�����{��
����M�F�x������K����Y!N;���8|x>���d���,��
����^_�j�j�z�?hT�;���������36�Y�)�j�~�:��`���#�%h:�}6&�q�d����V%l4�ud�8�'�sWmvw��F�{�9�K?�8��U
Q����g����&�6�L���'��pS�l���r6�'o�����0����O�T>�Yl��9L�)��yI�}�X�J�e�b����m�_)�60�u����KY�6B��}2W���j5�
��x��u�����0�v��jQx+�h���������$����26�FdU�d8���!��x�G�IrY�^�p����j%shY��3e������l1��%�5�-��e��[�B�>����
���1�1ER0�5��!�x@�'��[E*h�z~��jv��*a�����0������
���X��<�������@�7'�8�������_��*�?p�{�]dD@K�6"�[,D���%��=G�@���z$�Ie���y���K�E<	��������/�I%�|F��Y	�;��^`���%Z���<��d:���Gdy��L�X�>�������*��=<�<�P����;F���	�[�C9��U��d�=m�q�o�(�3+�h)����7��`4d^�����v`�vsW	Rw�N&�F�Z
�\�'����V�_8�l�Q��]�s��\W��N����Q���7}��B� ��k��U������n��O`�������n�
����������;���2=���$�A���Z��=?��.�{;��@��q
���q��
��Ir���2n
��V��j
�j}���n���DR�s![�yIs���4�-�A�>�:��������v��$�����s�z�4����:�����6(TdR�V�	J����2�����2d�6jL?"�L�2�rJ�K��Q���+�Sb�}EKY���w�;�@�+���	g�bw	��j��m
v��_����q8���.
��h�������_UQ��V���vB���0E��z��>������zA�U~�yt�kK��]559��!�!uL��Z�=�=���	�O]Uv��rtw�>�-��:w[-��Z{�z��{9D8vI�<�������*,� �)�T��1(��D3=�8�Sw����F�u��t�H*�,r2��y��;E���E��.f�@�H_��q�Y��C]��
A���}���B*}��G�6�Q����<�a��^�\Og��iYV��,��������K�d����mt�^j	���l)�[��.oBS|���O�Iq/G�D�h�/U(���SA7/A{�*�n+�4��olQ��i�����Z�����3���#�$�0� [w��DZ��\�P�]2�9���g����W���-�2�x��LL�1���$��8-�����Q-����0�yT�kw�����������r��,��D�2C0���=J�����,��$���	�|T�	���]6�Af4_<]�[n�aF����\���;r�?@c�
�����*	�U\��r&7��w�
U�)�?=l��}W������zv�zu�b/��lu���R������9���{�������v����p�������n�
km�R���%x�x����5�/<��rs�=�T����sa�s�^~�3S��Z,)���������p2�?���|�t0�C+��M���D���n���
�7�1�%������kQ���s��`q�
D�Bw(z�"E�������V�������~�v��a���d9[���UH�J-�����p_O�P)t��"B~t>cL����90�;��������a��.�2,l����m[oW(�$���Ov��(�Lg�wo�SD�.�	���&�-����$�(�+G�8�#s�zXk��4��S��h�aX*�@���������
:�0/��K�����>�������Ea&S6���~��u�HO^�_��`0�!}�,3xo�oj������)�g���v��`�Z��#>j�GQ�L������>�)m�����q����~���$�O����/��q�����[q�(�)@_�+�F��N/�P�=H�^���%��b5U�(Lbn�Zz�-?�Wt���v�v�t4��v�A���Du��TH��Cb*
�+���3S2|x6�Lh�C3�E��:�*���@��X�P��Q%x��=�t�s_�+w�B��5m��t�<<��>-��Fo�t��J����]w�'w�e���c�5wK�r�	c������#���������cf��
�P��'�����s����}_�:�^�����E������/�?�}�������::�I�a���4��������szvx�o=�,�=AzF�Q�!����i��������lHN}B;S�#	��_���m����/@����nH��;w���;�e����M��������:~u}�}��GH@}�)}���9�����y�}�}+��[7�P��_e��k���V��7'����W���{���S������P_Qg
]���{���,�e�B�-Y��%�}�z��d�.e_���Yh����I��"T�M�~�O���{i>~��If���o����r���)�����!�+�N��\��*�5w�����wQ<ZWx6��K�o3l�"Q�[�-\��>o�<��:�$we��.@ ���)������X������t�;W'���p��)�+��*����Pw��	g�����YWA�W?c��T���E,�~�ua���.}�J�;�K�.�E�/~����[_]�)��kJ:���jRO�+�����������-$���}c))��_[b*��o!=���kKR�o�JRU�K���U�=_O���7���^�/��
��W��
�����2�z�Z��M��"^�/��
(�[Js���WIv�#�&R^��4T���P�e����W.�W��u�����CQ��?}[��vc���_
j�j���7������h:��3�&wM��M��V��w�����]��O��f�T���2�i��4� �hR�-l�#�<�����_��A�(w�~�;������<��\o�b~����x�7IV�'U��*��#��:����p2D��ZE=�4����=
����X����a�Dx�8M��[�
H�@{q����w�6��o�7�6��g`�ij��u�"z� P��� ~�8@��b���|����x?g!��L�@]����J9�:�����YB�p�#��ij-��������W�g�����9��,������/f\��uj�������/�LK������o��69W"�|�),p���*io�5�	�s��A�����2=���
)�Z���J0E[���j �V�CP��Z����=~���������|��M�!�K`)�������|qr��=�O1�G��E<�j��F��0U�A!�K{�\�����Z$��v���
x@����w����%b�#�J�Y��^��{���4N��>��������z=Y�&���r�
'�K0��=�?jD�@9x�w����4�YL����{�p��h#P CA��>��a���sDr�R}_Am��v}g��	���p�l����}O�Q~O��i��3���?�������o����bm���:�x OXO~fj�S��B��������� '�d�9)J�Zw���|�P*��'jp �%�q]r����:���;���������	�"pt`]����7����3z\��]�F
�$�T�x��7��G��������{���0\�p�g�_����W� ��6�H�:���V�r?f���%�l��~;�m
���p��������7��������pm����S���a`��3�eh��;�������*f[�L�1�N �^�>����N*���?3���I�><
E�^����iBM�<��`�R�9�zxyy�s�����U��-�>�j"�)t�{'��t�R���mdi��kx
|����4��cJ���=��.�L������Yby�<�1j�n8.L>a�.FF���F%��������l��#��f:".eO(��3�q)�C�K��U���r��hwc�;�E���LI�t�\���	�s*s�sD�dm���j���p�=|�5/�CS[������*��W���53�/��
�:�e��_�a����M�F��q��oki�^���v�������xRa���m����E���b���co�l�<�<u���n�7���u7�"��m�I\�)��N{4�������;��(����+�w�md��u��Ju�=���S�����@��f)�����h�_	�1RqWU~�0KM��������7�U8��V��|V	Y�H��Q�ohv��/YP�����^u���Ve��'4G���J�g
	u���f�G�w1��>�<��#�A���j����x���%}�{r��\]�O�+%^w�;QF�j����7��`?����vTH�F�o�2� �����myd'T���Vi1�9�(��A�l�"��������<����6�'C�������u��#2�S8��TrI1Q[�Wqn���Q&��vFF��L�-��p eX���x+�;�,���=�-��v���-��soI@��<p."o�7��45.n��w�W5�0�	#/8Y$�?�^�G�p9We�R��e�L�B����2���	�.����������Q�D���oY0�u��r<�S��pi_|�o*��6f�\�z@���#���t���j�G)0W��D��>�hL�oN�'@�D��c6;���
T�,�#�.����<;���T��ZKa����{�W��pG��6��6����Z����yH�B{�A�F{'&�Z��E�x��SA�g��!!��&��+AHS��R3`[w�������}Z+f,X��8���-�Y$!�
7�����9i���o8/]�y�d*��]L��{����i�k�69y�����.��{D��B�v�9�Dp+}��N��,����T��m��Im���|�������4�k��m����9
�a5�f%UF�pp�vt��:������o���Z�`w8j��Qm?z�g^diq���o����z�)��n�-�
��0gt�4������u��N&`��G�ZY0F	��}�k`��,<��D#�9�Ru-�)����`lS�pr���x��+[���v��:^!Z���%���e����K��$��T3P[d�W��}d��b
�(�����)���4`W�cH��)�%�-�x�)�E|R��c�h���� ���0f������K�\�c��e��N��etYg#��my�M,�n�	z���������J�e8
�wPNP�Gx���<Cq�~�
$��Y�e�TaQ����h���X�!=+%8a��p����T?��+JB��O\��,:#M�xV�D$^�����B���j�����+��t�i��*��(E]�	~�-Dk�pd�b���Y��xH;/w�y��%��"�����Wp����@��7��G� �*��@���7�����:�^"}�WH��<��+|�b�R����%[*���.����Uw>���&��N�7H����D�Ws�R�\��%��F�&��_��K��>��Z����8�$j������hJ	���"s���K5p���UR��X��D��J?aO�:�	C����n`O/	����u�j�
������a�J}u��w33��QWo����U��m%��qYk������?+N��`S���@j	��}�m,����W��on�����"=
����n�� ='���W�D�-����{K���Z��Cu�}]�9U��b���C��
������.���~��(���~���B�R��0��Z��7��s�F��Y}kA�x `��:3\�����~z�>�t5����M!�Xul�K����>��3�4s6N����ky�����F��`�%0]�$�������HM����|`�
6�
���M��u�����7B�������<��t���g����7�Z8E4���tD:L/~�dpa�P.��L����+�����?���`�{op�(8�:�Q������)���[�m���n}����1F/h��75�!FY��-!�����mBp(�2��7��4
=v�f1�$cT�I1�G�{��P~�>l�)rJ++�r�������3��wgZbY�
}PV�����:y~��3�UY��1�W����|O����������Z����;��z���Hz�er�����U����+~�n`�'��(���9�Z<0��L��e8�����Hb��p��z���c�e'��,�� VG��q1o��BC�����nV��BE'�;5;�&+`����X*�q��S��!K�C8Qh*��w�
��Y�E>w�a��[S�RiB����VJru/�������"p�i����D<����I������ic|L������2�H��
����G]�Jbx��\%��!�LGc�r��s�����rlE���d�k�^RAf��IE��R�bA��'������.j+s	��i�����	�D��n�v������S�tEw�,4��������%��y�tE:����w�z���
)�.b���I}p�*�o5gl�V�����R%+m5G�W���H��R���N��o�������s�����QW���d5�M���p����W���`k�5uFwQ���_I�7f����	(�F�[�7=�vz����wx��P.�����]^\]�7G,+
R�o9�?(H�����J��>1��S�)m�����2�;Y��(�{�����[ x��[����Wg�9}����0�nS��\�G�U����n8��;�4����`��.���+�U�5QI}�������~�Zo���~����R�Yv��p!v�J�n��~���%W�&����)^�X*��)�������������t�c�(K�����)�N����yq0t���t^�n���VT,��m4�T�hN?��&�ar-�'���t�����S.�8��6eC�Qd\���,H��e<���$�? �O������n���nX�0Tf��A������T�iB"�����`T��g�R����C�,:�)\z��*��j��:����b��3WG^��^r{���5e�����Vf��j==G��l�-x`4�����C8�(�1f�l#*��j�����[�gaO�9�be���g����y���y������:���-8�es��z"3�����*��/zL��w?]������f[�D��E��2��~��#��5�8���s�����C�>��������{V��`��pxO2���#|����7�����j�������z�A����4�.�B�q%����v��NC��gg���K�+8{8B��1�X�Jg�gG��x�O��]S�b�dP�(���@���sB2�O��w=�R���������`��)$MA��s�}�_�CK����P��o�+�[�����_('��[:��7�E�K8X.�������������Y���l�"��f��L���3 o����!���l;HB���i���@m��:�aEs�C���u0s�	�%����:�����,�����%^�oH������SZ��}�2A�
s�h����~����}%������1�a��0�i
����Zq~OY�m#hY{���o�UTt�b��.l1���\���/..+�~����y�7�������)&p�f����~�j\^�����x��P�qu3��E�wN;��>��@�����K��.���������sv�
t�Z��eo��s*�;�J_�%��
�VehH]V�98��F�?����(l������(�ttY����k��������
���C���_:�����]P9�t�zg�'�������U��s����������������Z��j����w	@%��[)��������
��q-������,���0�2�������B��Y�f��J�}06�� j5���F�������>�?����qT>��b�����#�L����r�������yJXx&�{���PU��`&�)_����� �C<Bz}r�����=<?>��K�?�-��E	'����l�o�5�~��#���
k���^}� q3�Qve2M��A����0�����i42��`I����:Dr�u��Pq.	�e����
ew�W���
�l�kj���<>����>��^S�g`��O�;�l����8�t�<`�|���MY�� ������p�v����5����T7��!
h)��{��WG���W0%(�I��r�r�%������c�Cj���lj��:��[}f �M'�~����������'������}�k,����<$���.���f����m�Y���,�3x����O.����J�6���Z{�
b�~�4��A}�����*&������h���i�
6���i�L�B�hK�����t�X��a��p�x���4�>|F��&��m����8���K��R�K%|�������������{w�{}z����9u$�{2��|9�z\&
�����r1�D��
Gi����]7�]c��1�Y��}x~x��v����q;�izU.� �~�/yM�\���I^�}f�f�9�R:5u$�l�����������,�)���e���z��tA����T��a�O�0��������9�E���=����"�o���m�Y��V��A�-pZ�;�.�w��vr�1I�j��w�G>���R����D�b�BkH��2��+7��[A~�Y��F�Z\{�I8~@��Z� ���D�4`�����;q|c�e�lc�|0�-U39������ZG{�Sk����aAUC��,?wnsMC�������k4G@hIl�]��)^�tM;*�����������8�F2��t��-���8��s���K���O=� �9j��.�h�����y�&�_�+
����7q�v�=�=�������)��k����o��{jU�����B

b��v�>��&k��G�>�cZ3�����za��;���w����Qx
|���iD���6�����������o�Z����Z���}8n�
�	�R��:C����F�1��{�-�Yk���Z�Q��7��Ns���v�A�:���
j_�[��-QJ���-'�iA;h6����@��7��5�W�w��Q��k�������u�_���aD�h/�%<����^P��H��Z}����T���2�iAUib����0W���������]��
��������kM���6�K��1�������������%���5U�������s�e?!'���qU�b�98g�^��7�+��h4�1	��`������<�L��DE�i
��@M��|�{�0��-�3��E���]����p�B_��;Q9�0��f�	� ��>R��R?{�`#��(	�O4�����^>GL
���\F�mUb	������<�G��C����S���7@N�0�;�L5�/�I|��9l1%�>|�nG�R7/���q-�m�h���ts�cg�L^��"����*$�����"�l �|��a�@�fX;)��N��[P!�I�/O����(��k,hM��#n��a+�4�_���Q�Y���pD��qH�
s5���Ql��.��|b��$����w��������<�8���q<�g���w�\`�fK�����C���,*5���#��0L6��Oqt
��?n ��2A�����b����,�7�j�<�{~t��fw��7:�^]����?�����q4o�k���N��^F�W���������}�����1�ZP����6U��Y�1�d!���?�`����`�_��5��}	���������Ojwt��!\��3��g&;����gT����Fx���?�'�L��4�T�_��x��w#�9�Wj�����N��t�������~fn�(���T����~f�������a��0>�_4e'�g������|���U(�oj�k<�\8�.>/���w�����H��7���52q��xB���#���x�M'����C?�a�y����aQ��S���z�rv3A�[�O�����5�_����I���4Z9�`��g�R[�}����yc���M?�76:.�ck��a
���py7��2��K���_�����������;;��Y��\����F�UV<,�sai���K?����\�����>����~(�h��2s��y�r�������G�d�XQ�M		�{�~�u	����yK�l;�",�R=�Aqrgw7`�.Jt���p*�J�77D8����?��}�9Y��>Z��������@��{@�+�Z��$�~�]����_�j�~���V�#P:Ga�?�e�uy��]^4C���AG�t��/��p#pG�������/��l7���Uo��J�A������U��,Ib9��'T���(^�?wBEOEZX���mc��9V�E��NT�$2��'���~\�-I�vx�q�Bf�]�<g����zs���!f���a=��;�<����q�)�h�\����Q�z��?������TZ�JJ�"�?����.fS��@7�\e�IB�
�!����nw:9s��#�q�c��*�)��W��6v��Cu��~��6����?�w~�_�y
�����y+�u��b�qy��H���h���v�r�$�{��)�_�����*A�!xe��#'�D�G����l�	����s~}��������������q����>�&�{"S�:��R7����n���
�W�J�`��M�d�GK��nG��.�!�?�^�N�__���3�����s/L��yt�9���HJ8����z�,�pf7������)X��l�������r���	&�C���t�>�7�4F����u>\w��}�C��d�������58x�p�/���k��38<�����sk~�d�>F�e��F�|������W�s��=����/���������Y��X�$�{�������}����"�:�L��w�6�\��|�����n�~]�B|`
�)�Hy����<2?<�i�'{b�o��H�.���'t�����Q����|e�Ge+����5f���+�G���2�J	&e���n�a���o����Z���Z;{�Z+���+�����M(,�}Uq;}X�-���SQSn�P����M1?�}
�U���3�4��i�v���]5����Q1+{#X��^;lV����0G���ZQJ��r���e���S�[�Bp��$E	����]��.�]Kf51��y�W����Z�s�%f�2�[��r�/������%����)Z���,Os����F��]���(���fa�IY�M`�������F	���B�SB������~#R���4���{iR��4��hG���K�U���:7��q$�����������r)�N�����f�H�
��
_"0K��uL��y�W��'sM�W��5�����������}�
����g��@��+@Q�G�nB� �����������(��k���V��������w�4���H���>���3+?����C��J�}���{c+�R��������ebXR�o�����#�������K����v;��~�Q4h������"��D�,G�����
��<�� 
8�f	Sc�
��$�a�1��8E���8rQ0
��s7���I2�*b�c�u��c9\�
+��lO�@&�U���b�P)/�A��DG��	��B\0NFfJ"�"����-��od�y�	ge5���;�b��^�#k5[��Y�SjT~�"�B3Rm<�nDggq�����NC��$3;w��[���;���9JC�Y��K�foZ�<����w���yw�7���sR]z�,���:��G|�S8G�0M�?%�s^������ tQ��@<Y]"�-S�b��N��|�������$���c���L��@�(Z��V�"A�$��yf�]]���:�������_����)������#A�DDe������v$]��<A�#�I������-|�>�Q)����=N�C�T"�1������j���__�v>��:��8��s��s�������]��g[�9i��v�:9�y��a��q �uT_���`_��K2��QD���f��3������6S��-�'e.1(�_���I������d=hMu�,��`�����\���T(���������h��h�h�r	��������'�����o�t�9�\)��]~��%V��j>�>�B��
/%V>UC3hF�,a#e��N�Id���/�Z�{)8{P�*�����U����KT�M��OU�C�[;L2�bYDECN��M��$��MA�I�.��pN?bLw��g-���Q�u6��Ur�Lb�de��!Uq�	����>m�~���b��9RVO\vO��^?�
!$����@�P�]�g=I��K�4�Ta�n����9y��N\���~w~�j.���R/#�
t��i ���[��
��.���tj���'��>��$�%���`�N�F�_�����:NY��MI���0���y��Swt��b>8�d�:z@���>&s
��f�H��O�1e�#���"M
3j&�6���\0�1���2���$�$��r��*@��U����,f<#G�	�-4�RB�,	b�/{B��<��,�7�y
��K�2a��$B��Db�Nv�k� *�0.�&�3*�tT�-����G*�Y��19����L}��Nr�_}���!��FN.��>����'�b�	�g�o>c\�\�ed�.x�����K'w����I%�L�f%������5:�Mxx��b�.�� !F\t(~w�����][�"|��K�[D��������G�'1��#q(R��d�X�1l�~g���G��	��#���<�q2��R�����%����(�� �$F�	��o���l�4'LE�B�)n�r����8�)��&��VL��������,�>(�/����N��>���D���sS���K��U�'������
�Xd!]���u{�ky�v�B���������a���,/�E��mz�G����y�0�bK�YZ�x:����5�JYI�_�"m���tvF�\2q����C���(�i6�p��c���?I���#@Yi��:�A����e��`���v:&��a����%���@g����r��8��� ��G�g%��x��!�|����N���8��?q����� m�d2�3���Jpv��������J�D�>!*Q���Z�(�����������,��9�(�����@v|r�9����9=�G���4b�[9�J�SD�`��5��I�r�K��/��B���
?qki�`��'A�j����$i8/�8j�!
{HH�r�`MC=W�'�3#�"JwS�9Y��(W(�N���-���0����L5�y��0�w�X�C��B<Jq�Ml��M'hg6���,�KZ@M��v?T���k���>�"�����j��$+�"GF�!$��O� [	��FNr9�/��r@�� ��p%M���F� 4�Fv%��B�d*�Mc��`&p4gaIv��G.�If4d^Bl�J%Et)��N	z8��@�`�r����"�M�6EZ�4�70]�R��S Q�V���U�x�R������'�Z��Sg�	q^UB2�x�Y�AN�����|KZ�gI�R�S?�A^��%z.pO��/�D4Ak[[�
�mVb��}��2�P��U�N\�o��o��>b�����������Gw$"-��/�lf��dQ��q��^s����� ��U��e&���-4�C�N�#J&��Qn����2�R�>�����)��Ou)xyR��27��&�-�Y���w7Q<-��I��$�;x%�h�FYN{GoT����������>��3M�7��:���!��l�6]��G���t�����@������	��O����~O3�|"��-j�1�p)+�Y��};NVt���� e�9�������eM���7-^ �����p��-�`��H(���lzV����R�O{�X&�C	ZxR?"��#�":��,���x�Q��t�U���l;���9��j>m�\�E����"d��S�`$�W^^Mw�%���*A���RN�\�ZX�?�M���a<*��y�)�.��4e�&S�x(EW�Z<b�%q?���� b�,����io�U�J��sq#�K����IX�{kdj�?r��������`N�
���!�u�Mo�#��`�E�-������ m���[jNS}��c2��b�z��Gcb�3������"��	�]8��.���X���V	�2�,Y��G�V�����
���/��r�4��D���{����|�Bk�Y�^�q'v��.;�N�uO��!���3��=!��
�?����:�0��+YO��,������}}x���{-B�*zFQl��}�,%��Q�#|k�����m�jp����Zw��k�KL�zx�e�����R+f�3��������"l�X�W�C���(��fJo��8XGL�Y�q��(1�G��/��I�������Sq��K��*)�%�r8?jG2����Wh<���w��O����H�~D����[�O
�i<��1�������T������"�k��p�\�(��=�O1by�������*������#��<�Tv�{���cN!C�+��u�pe�-����Y'P�-�1��?���	Q��ivSpG�m����|Q��`K�+���2�����N�5���o����0�a�z�|�$Q�0K��ao<��^����=�hS�ad��g�=��4Q~(_����xN���p9�T4�@Uq����
)�1�F�
��c�
�a2�l]��zI�����:���#����[V�&�u���m�(EG�P��'�1����h�d@A/>���IF�Ah����,"����0�\�C����W�?��OB��7=.A�U�������E{�0j&�	�z�/��Cd�k�y���`bz�����b��~+8����mY��������6i���|�:R����{mt�'�H>�z6��_t�a��|w2�D�f�������������,H�=���V��P���u�����o
N�j��oO�O�������'���L������G;p�67V����D\
ke����C�`��h��v4��(�,������l����U���p����v�����f9���.��
��;���=e�#�4T^�W3g<�s��fUZ<���F��^E������..���4u�����1\��)JD�����/*��U�W@���n�������em���!~���-�l�\ygT�����rq� ���^������r��?�U�$��w��9G%}l���a�L{�Z���P�+��P����6�W�U�z�P*�)��*���#�XD�e��>��J4������[�3�je��f@��/�h~����X����M���/���A�!���������`�b6���:S����p�Sj�i�nR��I�Y�za}��z8/\|yX��d�P��))M��<���0 �zS�`�AS�";O������T�����������:���p%BY���c�#WA��HQq(�4��3foh���'�<��>B4�i����cIp8/E��tlw����R�pf�i��.Bg|p�&i/�W5�����\��f��J�-E*�m]�
^���l��@�. ���f];*����6�V�G�;�k����w��"2�����G�����Z���8s4/��C��V	j�R�q�-A��%���=`�~�H���X�\��,�a���D�V.��C@X�d�0f����9	�����x�-N�m��W���ds�5�����Q
�������~6�4�Td2���V��%��>z9��(Tj����Y�L���C��W�"1��#C8�=Q>��y�1c�i��.s�(�c�T��D:;W��l*�����["���7�v�@�&V��Q<;/���f��6�z�;&@����"$V��"��YG�������;��%%�����\w�0�!����|����/�(���p�h9�n$F�U@Q������,H���.��y�Q��l�MO*���2}���)�m�,�������L��Jt[�[K5_���92u�
�4ABQ�cD���d�T��~�nt�'�%�L+H_Wg.�������o�-Y�-�����4�S]y�.�G�^D=JX$�2>L'��!d�����0ZLm���lH�S*&���5��_��I�&V:�>3^[~k����7�gL�������������?�Iz��jf\i��	�����L�	%�gX��#�1�J9��k�u$��a~�*������x�t<������n��l�-v?�R���%.�b�����RT(�L%�%s:	>�vo<$C�w�G�U�+y��K.�q'!����{i������2�v���q��/�O�8�~:q���+�4���^��v���&���'�$v�(�UO���X!��_�'�oPS�t��t��?*��N
<�� U��=,��Le�R�L��cA^���z8���D}�a��M�@ �L"n�����(2(l��h������b`�a�s��#�L;B9�L��6���I/z>��
�:�/g����W��g�P����7���^�Z�g�PO��eG�BX{_����&/�����a�[������0��(!��ff���=�u�\���"�	������K��&d���xJge�uQ*W#Q�D"���T9~�js��=�@B��X�8�W"����$�������)��,uuSSE�B���hR��JXU��������,���2F��;~���������������k��onH}�t��z:�2X�>�&���Om^���'^%��^8W�i�>L�3�����@e.��1�a��������I��&x���;��Uy�Q����f<PG�ZX�Hga��c�i�W���8d��s@f��Z�z7L�\�����^��<mj��*��6���Alz�$��&��h��@S�8�+U���F/��%H��n>n\��O�5NU�)��r�'_���g�YMR�t��M+��Y*���"����?��(�B�|V�m�)XV~�uO�<2�B���	�J�SI��15����&�}bq�(&&gh�M��9��;���B���v����m�ss(�� �����R$�^d��)o�m����;E$�mN��aX��'����~0�Og3K��V4��MZ�(�|c5i$���2��P�I����i�
�$e�"��I�}Q���
��E7t//6����_wh�sD����R)�z�#�J�~
'�,��]5!���QZ�/�p�����x��M��>2�����}V�(7��J[����������$r��H�H7Pa���zS��>Ry$q�d�����..��2�/�)�7t��&�,	�I��q�a���&�+�������Z[��G;�
�(�=!7S��nU^�R~,�w�����!o�{��.'3l�#<m2!���V�� ����
�f��xz���+[[����1���#b�2��0��4K�@kV�������b������Tclu�9Q��w�;�cPF�l��/}�yt0|�(��Gb=2�����N��������S��]���x��5��<��?]
��1e���1x�!����,
jA���H��Y)U(����}��ue?'@�d@x�T��z�y�������W}X�)���vFj2)�<���2yG��
����
�^�_��\�q������2�u�(�JYV�>������jF)o��F�\V\qO�t"������R��/<\����k�������s"��v2g�g)`2I3�Lb��8���o��V��u(���E*����"���G�ai3����`<�'������l���4����SNJ�xT����Q]$���f��R������5n�V�n�{���{��T����!�I\}$m�_f��5^Vp�r�t���4Y|�b�I��N���kk�xR����t�!�#���e���o�R�>:�<rr���'#�����c�����$��S'1���}��
<4`�l���i���;��j����y�����1(m���D���E�<�4����Kp���������Gz��0�"�(�*k�����a��f����M���0tc�cV���F-�b;�+x7��f��]�quv��w��>�a�������N�c�a�6��C��1t��~��I1�/�ok��*
�'Wgi�������r���d��2bGU�HR1�16�a�������m9d���y3�3})#f�I�Tn��	�DW�_���:G%v6c��Cb}�S��+��a����)�$hn��N�|h;�YVh	
���r�T,x�t�nQ�X!�g�������z�_�$';��rDQz�F�0�	��!�l�������pf^}�����sCq8(Q{���`��q�T8�c(J�$��X�5���.��*u��"�T}d	r�����u��s�agG����,�vx�Zh������VE���
u}��`���@P���j�abE��x�F�@:�?��D��	�h��E%�`�|(+��Y������PZ!��������6nb�{b���K,w��>������]+�$N0K����>�P�Ep�C�E�OJ��!�SX���N��[�w�/M�1��O��nw����{G�7�.��9���;�<����	�x1���x�l,	���B+�����Y���k������o��(hQC�9^��6��LEY���y��8,�������l�8�A�#�&)�W!�(D'��8+������h^��^q��A�0b�I�3JmE}�"���H��)����@e�$D��Z���'I>��	\�*�����=������ak���Er��K�&
K"��]LU�����0��%�;�P4E�iVa��.�����VX�N�Y����q�q �Wgd���-�s4H>�3z��*,`x��<�GOt^: �����K"t������O�E�l����>���{���BX�~�-)-�X��:��c<�m|E��1����<����P�����?m<�I��gx�.g|]������%��1��D��=�L��eXZ�������a�w�$2�l4�	�Kt!������l$�p���@u�
4T>}�j�k�Q=�E6���#�"Y��K����"�WT7��������y�c}"���|"����7F���R�NSs��iF>R����n���"
�|#<�v
��.�'q�x��d��`���Z�!�����Dj54�`�V�@�=��^"@���A��5����������}�����������B��QvDj�G�S0�~�����^e����P
�klg�+�Q��I[�q�%��c��,~v�A�~r�O(�>W�TV;�s����7�z�����r�������kc�4V�y�������
6����*��@��E:�����9���2<FIP���T�Rf8;�1�G���9s��J�7wv��v���Yv^�g���C��J��U�
.��m�X���\�L���O�o�Ty>�o@,yL�k��X��t�H�J|�b8���Xg��Ed�dH�`e��������g�����2�?�Z�����|��o�y������H�`k+���@|�==_�~NEG�����Q�-5Y��m��b�#Bl��n2�<)[J��(�e���bdq�RA��\�aj�F���z�#1>I��f���]�\sXL����A=e9���<>tm���Z�I�,;�<Y��%��@����n��-t|���4��E<��~^_���aS�`��h�;�5���'���I����M6R��bG�(	�E�
�z�C��D
����(L]�
��&E7������������g}3��V�}G-+XSo��d���lwuR��1�������*q����N:T�N��	���H����8�� 8�;��\OeR�K6M����6R���
�y.M�)k�?T�qJ����/���j&�����Ox���h<O�(��6�?V�zT���)Sh�Hm���G%
��+��a(�F�h��|�]�sA�r��u]����rfe����0��!�x$�]'�Kf�=I�����?F���r�8�N��F$sX|7�l�3	���G��fjO�:2�t�d�XDa����SaJ���x;����d���J�i�m|�nJ���@��0�\��X���2�'�4��5I������h�����N4Bk!;�x���$1����[t� ����r�y�0��/q8V9>��Qw*0�
��S@�@a�$�����'��4��N�6�������l���D�+����B�3�x��u}�L����Nav���t��D��Q
���/�}?���R��IEV
��t�1���J��m���+>q����uY=�*h���P%��},B�����/N@����UO��Y���M�+��|9�D�=�O�2���L�J!�A�h���pe�3���YOX����-�=����|�a%
�O�A��;�U������a��B_�s������
��O���gX���3+9L��IG%����O6?���Dv$>.�1������*���� 2�OD�����a��N���>����Xat]��C���"��i���3��:�*��[�� ���ZE�!R��<�S1�!A9������IIx�U(�HB��SBG�Z�\�����Pv�<!H������ps��v>�L��9��s�ta������C��O����j�T�m2J�It2bY;�C���}:
�=�����Dc%�(8K�����4Zzph:7,�4�Z�hn}�d���0%\�.��w��hd����/8/��taJ����a��x�!���$j �l!�\���']�T]����'��75PY�����:jD����j'�H��SV4�!�8�m&�&HO	_'D*�j������b�a��&�?Ap>M|�isM�5?4�&X�w0�g�)Hc*V�^����^V5	4?��D�8%rm��B�s	��X��������#F	B���
��	';Wh��b	�)_+~*Li�<�����Bh�R����1�@��r��=�l]Z�����6a�\~�v��xl�^1D4�\|�����@��@O���*S��[�����*ri�1p�1�0�q����t��;^B�Z1]uZ�&G����)�����N@��Em������N[/��r��s�]��'�t^�A��������9��3������@��am�'G�@/
�7�_	wQOXg���4�VA�����E�9�1�� ���6��Q8���e5�)r��U��
�V<��ir3��l�vg"bgb�S������&Wtqd�]�]^9!"]��{����I���a���3nO�
�9G� �����q���g���l����j�A���w������(��6[�kHZx��V����H������P
���3���f2��y.�pz������"�9@����#����}�(`�K`���[�M���Dgd�.��4,������7�C���?������VMoufdr��|P�����HN>_��A��V�����������a#�H��^(�D���O�H�qC������*M����Vi��A�������\�b���`�	�5���X�� tpq���x��;�"7������{��)'@�v��G������p']9����n�V���a������N�y��T�"P���r}�};����(@'��T���7���P|�@+f���
����C�+���<T�4[7uh
����8go�c��8:��<�H�5�x|O���[�d-��*T��Sv�oL��|�Z��O�5���������H�0x�D,k�l�V�!�'�tA9���9Z�@��������c�UA66���!���"�y�Ti�;���~O	rT/�X��zT�[�/�R~)9t���������:^	6�V[�#N:m3e��|�4����������[���a��n����������8�����'���_��L��p�=gPO�9����e��3OP����F44a[Nm�#���;B@#�=�U�2+I����U4T���'WT��������
x6,�EI�Y����VD"Z�OD������d�=]����f"�����k���ngMy�DCy�8�Gc���6��rn��1g�4�Q�zrSd_�����|I�&?}>�X}2#�`q����/��i����&~��F���9�z�A���V�}��_�6{g�'�O:W���K��\C.U�����y��3����U��$
�&Tc����k���kI����(���5�pq*�,����6���
0��f�/g�A�vi�
�y�A�
R����Dz��w/Q�=H�BI������������������P�R��I��1�������4��we�8���n������E�����?]^��^��v��:=�������`�d<�>N��z��e����/�n������S��e	A7��"!	]�
�,y\�� ���H@��~������*w�9��2���XoD����J�f��E7K���*IP��{�G��0=>��?���.�;�KY��X�,JT��J$dj�������bH���E���oR���a3�������b\�Bb'�9��A��b/�%xE�!��dp	Lx*��t��C�S!�58
�+��F�AXq�=[�3����@���x.aM��`Ni��w+�p2�8����S���*Xu��|����iW��@�
�#9���y�V�{��{
���!��R�G�V���������`���L$"���(��2���D��d�)���f1����]����{��l��Y7&���d���44�g�L�<��[��:�F#����=m<[2,�u1�q��!-�N�)��Cr0�9���ri�,k5b���zm+���/���ue��1�A�8�����J�b5'�J���6���t����>��PD�M	R�����$-��d$��j��9��� (�"'�\�)	���DQ(�D�ZZw���N\�@����$�Q*wcO�|g��*,Y<�������
MP������0���t��n���3#:���"���+�3}���[�Z-��[��h��g�E�C��	.���2~4�7��L���{�����o�n�������3 �����o�����R��M����5��(�Y?��gj���fG1k<��K5���Z�4�.��������h�}��D>o
k��@��:���J�L����>x�����T��~���}i���&����L�����V��3�R��:���Y�*�&����/5�����7�m��`=u�Kk�!'��%�L
�0�
j��9
O26E�6������� ����^7�'�%p���2+�nZ���I��vIr�-�p����CS��$���I$�M��2�M����H����X$�"S]& �!Q~�-������8������N�L����sS�����5�fqF)����J�����c'4c�������%ri*.bMD���Y�p{�d/��AZ�AO�O�TLnq���@�^}����Z���b(sv=�M��/�f��f��������J�m�J��?���T���_W��?�e�FI_�]�Y�����N���,�%>�0�U����F��~�Z��8�d2M�1����H$���0n�A����0;G��	�J��O�"$�
�]��	N���Q�7Y�Wr��"w����.Jk�L��/�wy�r�����>�<H�=��=�S������$L�������g|�u�f�u��Z�]c����F��M5-�D���:��"�i���#���;���PV\�@��m�/��LcW0�P7��0S���}>����'B�$���K��D�J8V�a�/�u�NM�$��c�F���3��V3U7����z-�L�D�]�������@J��f����|/�p�#Zj�9)�(@	��Q�i&�9)��8@����Wg��,��f��hB����w����)�qH�z�������t���Q����{���<�*���i}��'�L����'�� ���Y��r^Xh��8W��o����*����J�{Af�������H��L\9���i,�5C��.���c#��J��Qeh%�+�!���1�[�������H'���qi�=.N�,Z�^���\�f=�~1'���6.�������khE!�����!~�����L��)��f���Bs '���'��1�\�v�G*�`��<�	��8�Do�����E���d�}I��=R�O7G����a����������s��yd�[�l=a;��/���������~a�����B�y��?���<�`��Mb`���mYFbBi2��C�=!9�q��zN!�B�Y��
�i����o��}��������-�yR`;F����r�`�k���s���lJu���iV$�7�p�|;�(�Pxz$H�X�qd%�}����\y�����<���bD�T��)��5T���":���8U�d32E< ���SX�`�T�d�x.4����t�K��)Y���K��F��$ 
���`]b�)���X&�<69����g�3&E��J~[R�pZyv�@��d�
�l ��l�\���L�d��},�8G:�X�'��K'�����XB-� p����[I��`���-������N�F��_bS���v�������kc�"���*t&�!6���!Z�Lve�0H�:�:���V>%���0?�)�c�M�1{�G��~[!|�z=������o�������_X��s!�(�����;h2%";}�	.#�~$h&�q����^�=��Z��]�iIm�"�LB����B��L��@C�X�M�(,�NL�c��Drw�<)s����"TR;E�A��a�������a�kd�}-�xM�zac�p��:L&������`����)�!X�����Q�f���s'9���p8�13A/�=��DY>�N�~r�z�u�_d9�;���b���t���������>�|`�l�*6�a�a����PTqN����������i��-��W0��v0�=��f-@./-�v&*���F�,���,��s�#B[N���G0,0�Pj}����e���t�Cv���<���p���V@��XE�{��>j)�p����0���|	Q�����~.�A����~��GNk���(���A?���X�O����}���������$]�I��~���/W������G��$��Fb-�7�4 �B��t0;g�s�r�y�>��o}T���Q�4���J2��[���(Y( ������B���&�~/N�A��sq9���k�V�H
���opV����FS����6��:e/i*�y�%^���[�y���j$��h��=$���#��E��.�9/���2V0e��=�/���E�o:��o�G���!+6Hb���i[�����9Cr����O��[GQ�L���Z2�dj�����
&�M%e�n���������������3��.�X����qx{G���0���WS��PV2Wf��,q"5�3��+�����6
�����0>*��s�|��y[��9�������=������i��^������\�����`��!���������a�r�`��Sub��������Et�V��W���Y2^�:64�����YJ��f��RD.�|m����s�c��M����)�>~������lQ�z�����k�V�onG%�������E�VD��\����V���QY�f��H];#��������V2�I�0�����-9�g��t	wDtO�$@�GO1�P]|d��)�������#�*�QLg�"a�|�����C���pp�QuT���S�Y]�|<a���$S[!baT�L��x��d����)qbWG�k'�3#��]�#B{����2Z*V�8�TC���|q�Mg�P��k����oq���(ndb�H��Le�V�G�:o�;��>�����7�s%�K��Y�&�q_8��N���l�����B�>W�4,�19��_A���`���m��d
;c���'����%D�=��R/����'�MZa��?��?6���4����Z
�����5�,��^�>����76K��=�-�*���i�+���>�-a5��5T��@�8�=����������6vt��a
����(��TSo=p0�������ls���I[��������9��f����v�����)�r��)o�4(���&����3�P���B���d	T/���[����q��3��������Ofd����R����-v��GV���E�Nt}���%@b'��v�T��G�m8����p�'c'�����������aJ��`>�yS��$�����A���3;w��N�����D.��<�����D�\M�KD�"[���ue��/��wj��}Z&\�������u#h�[
$2�9�7|�}�hi�3>����3���!8����x��+'~.n.F]zX����x+[�)��C�R�]����\4�1��(?�	�jIK�J��p�����D�����M�]������f��m��fS��k��?�-?5d�&W�s��YI	8h>
���X�� P<��b���I�G�.�����_o��^�r%ZR�u�����
��P\���a� �o��7�������@�Cil�)j��E�����.��z"�!)� ����=���Ug�<�K[f.I�O����Q����,h-�*����`9�s�bR��?'�
���"2�E��A��go}8a���`4�%�	\��VT-@�T��`�U�$�X��{�G��t���"�3����K{�����������T�`@~�f��j
�GG���w�]ZE���y�/�{^b���m�q��� �u'�9�Nt
�w�@S���������NG+��D����t�N�VJu}h�I�0�(I�8����]t�y6�G�����Z���&d����{�z�	7���4lL�K�t^�����Wg�����h��t/�n����u��n��]���i�5��c��J�_vr�Z�I��;�.F0@t�.�Q�1"��*�P|��i�C���Q�#X%c��H*����d�:c�Z&����C����F�����'�O����+�nhc��Eb���=I��SM�B��ep����v�0�{�D'���5��G�)�_������:���1�(r����B�t.���0����VB�NiOs2�K�]��t����x���T�#,k!�jf�m�A��R�>KhM%v�� r0�����KG��vqw7����5�V)���O�qQx������������9�T��6��ntns/iF������r�	�����,�[�Q���M�#cj)�RF�������������&�J['a���z������J���@�F�����I'�l-�3�������on�������:�[gxi��eg�������;E����JD��T��i�XEf�s��mH�Z�����w��r�
�LE3��X�j�dz�����������u���,��K����?�'}���\n5�����r��!qCM�g��������Y���R�K��s���p
���+�(Q4$���2h����k���L��?�������$s�T������3�g|*OG����^�@�M��H�Z3����r�X�����n��@a���3U���bk^���srb�[��r:��l�XC��M��l�k7�U�4#:z�SX�^��K�;���y0G���$vCty�e��\�s��^-�0�Du�P~�1h�$N
42�L��l��1%�*�y/T��<���7�B2z�z/
hV��'~.����
0A<���W4
�/�U"w�u��y�Li�R�	�iQ=�@jT��x��$��sZ��7d�If
"�vx��!C���#q����������{$��H��$��u�S��u��n���f���~�4��l�\���G�;����{;������F���6�����>���F�6;�~���X�w�^9I^WS	K\N�%-%��8��9�F��a-#�"����QU,���ph��!�!-�	W�y>��9Z��s0KDq��t�x��E�����	��@=����QnUh_�
��]��zN��W��Y�����5���
0�]2p��I_:��J\]����oI�8S�9W�&�����h����/C���N\��^�=7����F6�C]��kCuh`�
qSpwv8��
���MK�Nl:�h�� �Y���pK�q
7
�S:�wZW�""�]'���s�I�YI164��U����'i��j	,��]�$@���s��	-$iXZ������JH���P�[����^c��Z�{R�!����:��:S�B�e�aj_��L2{�
�7����{��[V���/�������^`������hA�z�I&6����&|���]u�~S�B8,����?������<u�\�������]���4�pB���kg�pCMDz����z>
��(�K�����V{���^!7pJ��N���'��R��5��F���H2���3�����i�'���im�����\����?�������%��:�	L��^����<��u����F��EJ<?�����VzX��MlTK����U6�X�v��]�'�L������21�"�%�����P� �n���|��$��3���i�e&$��\0��*�$/���"\bcS�w�fA?��0�
}�x�Q8	����hj��z�G�t��X'H��O��L$Fs
�}
n�xv��t����uqo���=�O��VN4�MZ���j��3y2&�3��O��|�$8%8U�<�� -#<OK����%S�]����7n�+���6+;&��aR���]�����d!)UHS@h�;�`�zX���`M�=�U��Z2')���Hv`82�8�#�!�T������|���\����V�a���q�3$��+=�Y,����p����sq<��3�zoV�o��k7���	wC����Ujro�YK)&�<�D�s2���`�!{Ud��|n���%�I��C.Y">���l���":��]�o9wYWxHg�wX��0I��@4�g
�W�8�&4|�GZ��Ci�h����"����%V����U����.���;I_JvD�{�����$~�$�:9uN^�?�zT&�����t�9��{���"��u�KB���X��-:�p.,!������7;��� j"HD;����c�i�w��uU�g��B�Q�&B4����P!x�#pl��e[������r�K*>b��Z`&>���n��U��H.s0>
��*��f��+��;Q?���+hF\��0�8�����):2�f�FJ�vx��g��o��n��f_�Q��M��������e��~��{���L
�2�tw�4�XQ|�{zu���U������>SG<�|��c/
���v_Rt/�=�Y�F�Y�s��l�~��dt���g��	//,�S /u�t[��n�9�ZI�}�����d&���l1==��1��c�r�����|q�e*�������n���F��-�|X �<�m��z���?.z���~���M�x�����yw}}��Q�W^q@���m`�*�
`�����`�"�J��gZ�!���Q�w_��p/��x������Z
FU��c���#*��4����B��������Nj����m�dx���p�<��y8[��^1S�x�Y�	{��Ko��6O{��K\���������&l�.��^��e�WJxXhH��xsH8�����,�$���o��6��V�l/�A�����7��S;�(�t����=�����,\7GW!��#��4#�����J��bT�������Co��#z0f�f��i���b@��D��$�zd��,�Q�:>���^'3f���AEI�~��3<�9q�/�G���5Y�F+��������g�m�29W�����Sv]�	(D����)���$bW���B��;��8�^`W"L9�-��J�&���*a��v8o�,s��Y�Q���20\���Dv�
a9E��xI�V�gi�b�:�l0)���w�x�������`f.$�)������'�>�MD_������@�����e������V��DM���|�o�]�ix�<�O��������5|�������3o��%j6^r4�&�K�pJ;T�8�g�N �|�%g|J�b��b�A,83^��qn��_��S������?�D�}���O��cK4�t��j�l���;�o�o���0U�]�9��gj�pe���5Y��fG��?����Ic��>��3g&��/>��<���m*�6�g��Rk��
���n�lx{n���3G����8�X��)\=!�~ @�uPP��	��iW���%c���"g��)����>�7@t�����G���gF��l)��D���b�M/&�^������JI�Q1�Z��d5����|�
�������6uT��#��D�Q��A���
����-�x^��0�Efd��wYV���d����r��� �B�t��yt!�lL!E"#�!��;J�4� 	��<�1���
$�SdV�'c��a�-hR�$�1N~�+���e�+] �
�5�b�&~�1v�=Q1��f�-����G��
�����SR���aN��%���g�s����T�]����tf���4mZv��������i�~f]�0$��!�� t7�(`]���3@������oOy�b{��U�F�9�n���f�$gW��NM�@������/
ec�2c2!2G�����s��7�$�?u����?���C�;���/�I���h���W�E �G��"LH�q�z�	iY~/}(X�6�Q���.�F�K���CT<��X�{�\���^l�%X�&����S�8�P�W�Pm<��4�<E{	(�� ���	�a	f\R���|��������j�F�,}�
�ieC����0���s�HP]�6�e����%����Vuc�uHP����QL�@Y�2c�f���5Y2WO�x1���@�����E�@�YX�6E�	��)����s;�[0�e�f#��
|-���5�3D���(F�!���8��B��3y���*I�eXFj�Z��6��)P�>�-��C����#�v�8��e�E���)��*����E���<[�~xf���hC�����A���@����PH>�ZN�s U��y@�v�����Eb���,W�T3j��;���G�rh�C�"����_#�#P5(����}/+����$NG���Jb2���}�	�-������B5g'�dcrD?$Oq�A����a�w�{���N�d�{)c4Af^�&���������L��QJ�Y��#���%���`_�F���r-Y~k�
��]����-?Z��=�XY�E�F�_������K���]�V4u�����E�Kt�q�NYhc2����h�=l�!��_��0�����'A�q,u�|�8��Z���}�9�_b�&�d�9��Z��},Pt�N�s�?��B���Y��#r��q�1�O��@��WR�%�v��.<����K�_���}�,tS8Rg�j��k�D��P��-Ew��7������g�[������|9�3�z����+B�w3�����Ym�����"}�����JA���B�����l��������l�3D�L�>���lJ�����`%~�/������:E_�8����T��f$�.C�!Q;����U����U�c�kv�X�#�@��j�U"��=����y���|}��ht�JK F��0# �����>M�h��B������yy�Rk;6��M�)5�N`��:�[7�oSj�]eW��"�"�n\�N���v���(��V��	��.N��Q:�)A{�p������7S��
��y4���V�!Ae���
�Q��+������X_��JYC�u	�A���	���*-M�.7����J��F����=���j��z���<-T;U��+��$�]�rK,{@�S�#GoNqj��`9
d�h�%��i��������~$�\:����oQ,\��G��84���-�vwoA����=�9�1��I��Oap����9�Q�-�?8�������Z*���j�Z>�?����z}kwww�v�vvv6m�������+�oV��z������:���������Aq��=v�^���E���������������8���-�]]
�B�1���+�J�ly��v���~j5�{����:�FG����{F�n_]x���xw�E��i�c�*nT�)z��t�����6-�G����1F�aOR���@����Nq�AP@[A;�1��F{���_��2L5GA���6����Rix0��������
�I�-�dR�������*R���-��GKuro���w�X��|���SJ(2Tg�<�%�����N�%_9�_{��&���L-��W?:������U�A����p��,��Y��[���������n��D�kj� ��C��^�d05�q.F����pN�[���d��[o��VTBA
�\5P3����|��E�N^����	�:ru�9�����a\%9���.&e��<t����:������J�S�c3�HL�SQ[��l�'�s$1�6p�d���"�^t�n*E�;9�z@��1�G�����C�	��6�).@���B����W��J���7�f�5���8j���8��B�#{%^f_��p�PT��d2	�����8�m5���7�
�dC��������c��T0�"�VF��9AoO��0��n��Q�x6
:���#q�Y�N�-�@C�U����Wu��\�?���M��
����-���7�_�����^��tX*������?8�g_�iu%/��Rx'��e���gev�G~���p��~6�u���v"��}�u�m���������~��4��[����v�T�>����)���`�~.��,���i��m����W-��b�X�tk�}q�MCp
l���+��^��[����;?_�����0��U����������M��y��lv��:7W��yv�����8�	�q�
A����d-A&U�r�x���H��r� ��bL���A�WU|d������q=��z+���
�D�Y���QY1�;�S=��7@�\5�< Y�~�����
�N&�_��C5���0������Z=&����n0?���x'V1����D?�]g����#�Y��G�X��u�Sw��(>Z��3��Wh��X��y�_��E�G�I�1X��|����D�Z�u������\lz��4t>��*g�EFVJ+s�������n�cE%�vA 6����,��x�E�����U
���������-�#m1y\M��d�j���vZ��i����8M��~��oY�h#���H�:w�|kM{���1����zGW���%�h��*c�/�?_4!�p?���e�9�c��g�;����2���/��<R��y����@&��b���D�����V��.|e����G���1;����+��&��>��0��|���=�����
���HP~2�}�dc���
`GW^"��|�T�=u_T���A�t\����j�wx��E��t�e�(��_'Y�N�wN������Z��mdN���s����\���}��/ p�B��7�n��wy�n^�_�[o�{��y�l�N���yl����d��2����c��������Q�W��C����`�/����XX*2i�?��J�yx{c������p����	I`�k��Zy8,�*�~�Z?����l�H�*I)�����������A��j�C�F�����(��b�p��4*�q8�=gi�UT�*��*��i��)��<�A'��;�8���������`���y�O�by���0:�<�g~�`���i��NVQ���Y�j*��M�a���W7��eg��J
���u�A����?��U���&!f��r��G���[��y���<��@�~�<_��G������w���S�wv�_����Z��+������A�~\_�����7�[��*j��d�%�-��S���<���B�:�fO���4~~�<�6����8M����Q!��o\���v�C��vM�/���J����������>��p���9}���������� 1�����X�e�/v�z�1l�
`��Qz�Tw	���i�-�h��;;�G�&��*-���VtQ��z�������J�T���~�?,�6��R+��^MqT�k���/jH�>��/'����u\�lyW����r������-aj����t�QQ���m��wz��3��(�Vx�m�����<�E8�����j,�%qr���	�����~���Z*
��*��p���� ��%W"��x�V3MYl}��k��.�a�Y���*]P��5�G����h���������w�AN�����G�r��F�5,�����A����`�2���Z]m�~�wp�?�V��ut`U�EVb�*�Fg���9�O-��=F��g�E'�A����#!�
I];��-��v�
5��{<c���=bY�����$CH�Jcy7�N@���{�f�vp�����X!��\�X��`����t��&���(���_�(�`�������.�F�(�a��~O���V������N��i���u����%Y;.�������H���t:�ow�D^�|�������A����C��A���V,,��n�z����]�4�!K>�w���$=}�������Z�_�o�M���� 
�����y�������6�o�Z+��C�!YZ����$���	����0�8��wp����W<`�����r��z����R��yS��].V*���5��m���+�<�1^�m����`�',��"�����5�[�]G�+�d��R�L18����r�m�B
��NS����34~����og,��i��?�%\Q�����hP�
�K����G�j=(�/�����]U�guP���"g���p8)�=d;�����M)\�,��Q��v6����m!E]�i�0�$��������v�b�	(�G0T�C}l�=R��'�Z �����@����DJ���&}�f�1%���m����o~"�@��/����h����NWXMD�CfK;�;S;
����t(�������g
���8�6�%X'�Q��>Xg����"9u��n��v-G��"oa����(N��`+�jF��j�OI�B�X�<�P�{Zz��}(��CC�gN�d/9YoPZkL�,G�g�6}��W��&��:~C��&V"+��6y��h����^�6Q}l�������eC����Bj]��t9Mg���d�?��'��x��j%
1y�
!�o^�B{�����������9�y�����H}Cu����Z��d�W_��mu�ZmE�%�����������W�*�`�{d�g���+j�o��� (���G�����(�M��-�$d���>B��)`#p4{�����K��,�d��~"8��b�����&n�� ����S+�����l:��j��8
�lg/��� �z�np���4'��!��SK�5�����D:�**8���!5����m��T������.�Dx��TB��W"�=v�L�5�\���y����
uV������yG�G0��r�x�����m��8(A��\����dp3��$��������XCUt&���S��O�N�Z$0H��(2z�d��f��$�%G:���}�8��|FZ��`/0�2���r��RZ`��$hB��
��Sf�J�^Oq��JPB�k�7<_i`�TB�W"�iha�G�w$n<p'p�����V�\���F����U
F�P]$Lu���t�k E�"@Okv�V�0�X�K+y��s~D %7��g�.z`f�-J��������������v��;q����jZ_�-����(jH�(S�1nEl�_���VQ��;�W�	�C-�,��=dc�G�g �/�+��l���	���qR�����[V!??���\9��Z�7u�@Z�[�������w�%s���D���N����Nl@�q�����Ir���n���������O��$E��.�h���V7�<1�q-�!e-��+�JL��}������T�7�;���;(�(�P0i������Cx
�K�CH�,�A�*���`j�f,���1<��ab�q�/Aq"���
�N�}���f��$�<�d"�"��O7Y,�C�_��h<����J���(�D%\����(P8oJPl/�#^�n�� ��U�\{��J�_9:�����R��b��y�f���%��g���k�_�����8��3�u�G%J~
,�����x�V�
���4�������s�/��?8V�4��\���[b�������W���� khH%@��R����5XAMS5��'��j��Gy���,�_<����!�c�Y:��~}�x�����v���;�^�n}x�������`����W��������<�K^�Q����o]uV7���s�f�����0��%���n��.u�n�Gd=������_J�B��zXV��A�T��u�|xP��F�&��@���1�w�(�*��L��.��,����'�]:�Y�

Z�����a���P|�[%��<H<��E�Wu!�	�(�������'}�#�KHD���g9 ����I~��H:���G�
������e�8���u��������Y����b\��MfW��B��1�����9]8��8��#%��^��M�������.��H��;����/{�W|0n�R�~�����W/M�C4��
��f�*\�2�,�f��U�C�R'%Vl���>���2+mj�����#�;&�[�A�1��[���U�1:���0&g�<a#/���A��L�
�!�+��:����`�����}tH|}�j��w>�b������
��$�_��T�*���z�U��p��x3�����*��#�+�U�<��q����v��f�����[$Y���:�W0x�W��rB �U��n~��@z��y�4��
�D�Y.��A����Y�V�������6�@<c�hV��o���F�����G~��	�Q�U�L�7du�=�H��6�FT����oC��h�5�|�c0
%��}������e���s�Z���o�Q��q'���;�R~Ft6����K�So|R��zN�b=���������pX�K�Z��?��7�j���y��C�Q��6&6�2�����-F�_��|�Q��b���1���J/�-v)���n����U@V-�$7��1�FTe����K����I���A�& �:)���?6o���}i/N�E�y���[�VM(!�{V�T�3�d�p���sDw��M�F^�yv��Q��S�QB_j�\�.<q�M�qf�]����������;)�������W��h�~��� ��<��W���u������vvN���g��:����0�TC���������zr!1-����C��.zu�g/���u�Ss>�_rZ��P��^�Z�������w�����������l�CH����>��"��,�)������%)���,���X�	���
Edb��8MiYp <�T�0!����h�8�-�
.2r���F����S���D�(x���@�D�%A����^�����>��
�%�V���%�Z��dgF�~`��x�(a?�e�@�+�@%g$@h��Bv����_[^��_t�@�=�?)������4%���/����J����BK1���v	Y�^�\���~�4<<.��G����vA��2��)E!�x����L�'?q��{2�W;XYE�� 7�
�j��P3:�����Z�8.���n4,u
$�#Qid�GM���)������a�j��T���}��h=����������Y��<0��@N�����&�
GS���n����gN�O����e�t�> e�������/24��q�aw�
�=(�
�5������1���K=�������1���:�c"#�8�6zBj||�Z��3����& 
H��p!���3:>������=-� UvXGp\S����-8�P���~LS���?p7=�T��?�T�����=H,�MZ�,X�XV��X=��VpQt(E-	�X�jW��c)0���-�/x�r���]��y;:;�(t�;���������~����n;FdF�F�a<���4k�JT�
�MG�� �����#9.���D$1�� 
�]Xv�$�-+:���D]����#I��.��kx�������sT���+v��XZ�Y<��f\	v\60w�g�o���Ga�F�-yl>�����\��v�[�$�i.s	�l�?h�:���h^1������|;U��2m�V�&�a:��.��������$��d�I|:Q�%l�&{2���}O��,Gl���x��S|�����K���/X�hC�A���	�t�D��cN���� ���EN-��D5<^���
y����������f�(F-b���|9�!�5j��Z�/q��=b�����b"SM�B�q����R-���44����|D_E�����a�SF`�N=r@���g={	���b�������'&���F��f�b�l
<�s��I���U�!��G�L�O�����z���'K#���z,���&��"E�kb����EJ���n�3S,v8Sa'��)��;��������f-y�L}]�~p���X�����8����c��j��C}�M1%%�a��	�x��aD����Y�uG�d��V
��%���Z6K�D;�Q�A�)�V2��d�N���9e����_P^� D�E�4C��7�P�&���UE�oi��YvV�K�pK�~j���BdP)��	�t���0�|
��s:�Aj�L@���OO��)Y[��*�z�r��E��������A�)m����m�^.&�b%�5��w�]f����j�����y#`������$��T�>�j�}��J^F-������_S2���x�-������H*�:h����PH�E��|��$S8r`��#I����.���,��-9�^8RG�Qy��Z��~�����7��?\�gp��G��@j�{ Z>��d��9u@|���,�c��"����)2�G��i���/!K�Tb�����@���bM����~�|�?�I�S)������N���Iv�5������r���e��W�3^&Z��S.��5}��^���`/�=w=@8/���&������^u��	P� %Z!1D���.i��:z����,]�CUR1���ZC���OT^�<\s�G
{�S�}YX�"S,Y��x�����#~����_d�����Nq��o��� FN ���7�����S`v�?�u1z>�����V��f\q�l'�����������_����{�s�^�l�\���XQ���]}�{��B���7�|:����$$�M��Y����8�^��0���e�hG�E:����@��=�������G$���]s
�������Y� w�,��<Q��m��B)g�N�.���(w���
������c���jf�*vmq���d]���a�Q��B�n�%h:i�sd���v!	��z�L�f�S�@'���V��6�z������Mf��zU��&�+Z���5,�a�P%�!s�8VA��H�������8�cn���^��wJ�?'��^���*m���R���8G	�R�h�b&O����	��4���b
B��f����0���9&�KJ�4s��gb��(�/2��r<�5Yng��;�i�JFtJ��>,t[P���Aj��f�(�'�k?- #U�I���*X�P\�~������$}b�8���%0��O����NT.��d�S���#�lr�2����$ �
��'Mf��0(c�������8'���Q�E�ww�X #��q���,�%~V� ��������m��6�~��FO�4�&��9H5-qA37����N` ���p��uq�a�b��
D�A&�R`p��d���tVY
�������SD�C
�W6}���\ �l��8�v2Pgy��*����b"���a��/:�\��������XEww���������:!������l(6�CC�G[��.!Qk"���7���T!��G�$4�o���cT������6��Jd���4��"v����+8�t�������qm!dny��u�=m����h���^]\[���uC[���OS�a�-��q��e�_����e��4b7����:�9n68�RC&��Z7w�^�UF��sbWf�W��0�I�:��1D�.��Cda��n��Y�r���9��qv�'����/#�A5V��=��e���lT�c9����_0����V	�D�Z�$�G"� �
��(w����J���!�zC�����D%�	�H6��X�Ch�
��C& Eg�B��D�����4����l'%{���U
�I�I��,+O��`���b!��t�3;����w�^�	d��<M)�<��S�,	���G��T%�:�f�����[�T�[ld������lK�7��)��Zk���3:et�tT�#C�hcz���iYU�Q��AX��||���(���|/0D���Q���*�R��vT�/�������e)e�}�+dg���2��g��Y��WwEJ���]T9N�Q�0�V���_�Y���B��f���R���W�����R��������r
B|vMf������!:l����n��M;��Q��R���{�E�%��7,��w�e����PU�Mb�+������L�\���j���z#��o�i�dQ���4�a_�uK�qC��"�7!o�~��m���M��q~�yC���Ev`G��vQn�qt��Z�i����X��\(EJ���Um�F���/jE�Z��:�������_o���(�?<��jA/��5����^��;����'���iP�XN�����������	{
4�Q�`*G����9
>C�X%V��1���0P�y/y������&v�HK��P�T��v@M��b���et���9�]������:�+G{����^���d���MBT�/�����g:�����J�B�t����!\A�go�#L���R�"�"�(���*�K0�E-e��:B���y0F!�R�&��C��egf������M��,��	������1Xb1�J1/r*8Y��zf��������L�:z�Ij:���|�`r����uU�.U�Yp�������
����h1��w��:�3���)o�GN{#�K��F��u�p�l��M���,D:��t��������
ta�C�N�����������G�Y��Iy���j@b:�!��co�m\�m��������i6.,��j��^E?���*�v�xJZ��}w�S����=e�(����L���i�{���,�8v�N�F�!TO������y;Y��Y�h����?A{�n�_�%��6�i�����\=�O��O�����y��O!kB��0���;H5�XM�%
�����F�c�,<�!�Z����<U���)xC�/�K���yz��L��i�|����_|B�T���.���R����s��A�9{
9*�����%�;:����#9$���;����A<�9m4`��Rb���5}��e�|DC�w���+n�5��JQ�
7q�]1��&�sO	*3Bsy^�z'���X���a�p3��@��
�1tO��_��	Z� �|��Y�.��p����j@�{!j��u���#���A�p�~R)=��z0���������c��c��5]��]�|����
�K[7�,�����xn^��EK�����+r�M����,P|�H��4�����V1A`4���|�����\����=�G�%�|s2��wA��.`
vD9��0
'��Z��&��|�=���X����?�.If@����F;�����j�O�u@�u��:a�0��=*{A�8T��_/PJ�~?�����d���b�-�1��8����B����g:��kr������C��d��2��������|�����e������*����Z}��G���e�&_)�O%n?��y�Ym��X���4�4����y�Q�������Y�����/�����/'�])L���&|���O=�0?eA�#d`�BL0����2W������w��3u�CC^�����N���	������h$
��N����8����+ur��p~���9�P�D1,:�u����!�W����[����E��~��y������V�4�A����B��E�55.:=SN	����:=�� ������S��^�.��C�Y�&,�����v��p�a��`-)��U
���ce��UM_Q��@�����v����.�z]IR��`d������S��9#�T��&�(��<
)��c���=�m�	��a
��������;�}J�@���4���"��NHG	��q�x���Z�����A�2�W�C@$��7�;�{�\��a���j�N��]>��4�qg��ps�(J���W�53yR���U;����TBBt9�������*�aN���E�� +.#�����������d��D�E�q#��G���g+���k���S�
Q��k�$���{��+��B{�O�t��^����~^"'k=V�����x��^���l9� u�_���1�@{��m~9Z��<��r1��=�$�J�����p���*>�?��/���$V��N�x��pBh@o��+���kDN]�As8�m���>��KI��?��HxF���>>X����t���"�+�c|���p�����v�d
� @9�pM������0����j����H�����n<Xz�|�h�^ qb:�0�C���qu���/�Z����j��;�[DNh��
�����;i,����B�[4�J���;�]7�U��]�I��C�B�������9��i��v�G�����	T�l�����J�C��*�w�
�������@0	�������B�b0�7jt;q�}E~��e;)Fs�z}���QD�@���C�([�@�U�j�,<�?�F�FbmA�}I.0~H�����hE���"I���<D��\P��a�^��/��`�6>�o�Y	xAg�����-s����%Ue8]F�AMk	J�M��nsHWi:��2
���%kO���=�3��B����-����e�����O�@����[�Z	��
#�����Fe]��NX"���nkt8�����D�V��pOi�{�eL�'�_�sv��a��
]0�`�]��@�HW"�B07`3���&�N������\
���>Nv���L����e*$������f@���;+TS�95�
�SG��C���
.�fQ,lq1i�����y#`���>�#su��[T���jz�����jV�8Y'�b�`a�Q��6q�8�g���z�������������y~��e7��LG��^�U6b��p��DfO!U��"�@��J�i����3N��0
u�U�h��g���+���T�F��u���������:���|�1Z�����h�4�g
���E�M����t��r�����p��${A�r|�[8���/^��_����
�e��I���Q��]>�������hWT������K���t���w>�����S<�B5J�<�(#���j4OU^)���Z�@8P�A������ �3��8Y��I���Jp����d�
�s�H�%D���t���I���t*�������k��'��	h�r�
o��4���9m���u>8pP'#h����Y8�q3�1�L\����;��%>��$��LgN[�c�!�M+�\n5GA��V�)��r�.�)\���6��i�.v��[��<a9���*������DDp����W'�����{�"��/�=��[�D����g���^�)T�������A�]]��z���S�Y_b��q��f���|f)M�1������V�������rsCZ��������}C�$�L�8	D*7��a�k����T�B
4@p���8z�0��� �	�(
p�Q�{�D��'��5I��b,t4���m�Q���>�{���/a�Z��X�Q�^Rg�dw�R�C{��#R��F�A��A�V���G�F�>�cE$�z&~w��4���|���>Kx7����SD�s����BuR�LR��r11��4�����6c:�<�[�A��[4���d����_&�1��(nA�z���#)G�2�#g�6�"70��$G�Q�a��������{�n�n}��;��o�7/��7�����i�n�k�g�-C��\�����/zE�c�P���_�#{�8���`c���(�+�;�W��G+��?k��7���������o9
{{Q8Vl),;h�Ax�����uq}��q&��k��y���M,���&D�J6�m�i��r��bfWu���w�&@�/��x%�/Z&����AG����8|-�
gmMCR��,�L3��a[<�m����O��h�0s�E�0�/�3YPN(a��/[�Oz��@_��?�N?.g����67A���`���J
	a3���Y���f�9'����:C����Em?�	!;%D��"�<[�?E�mT��{���89Nzty
��/Q�5�V(FM+V�\D=���#����������������H�t��$���Y�2O%$`����z����\9*\�����v���,����Lrd����!��T����*�$=v��oA�+H�K����G��h ��OA�]pNZ�/�p��U�XHu^�����8�ViK,���O[��	l�����<O��W�����a�jp� "���f`<IO���v��g1[Ef���{mQ�u��^;����J�M'!�K�5��������%^���+�r�2��z?�$�O����4<�7���Ql�?�/a����v}����<�k���H,L\���Z����vmX�������G�� �__
Q�X�����u���m���2������=�� ��5�.�����*qZ"=�����x02F�
N_�Hg�q��BB��v�`�������Yn[�~�g�}g�q	���-��&��vWW������#���������V]��"-��0=���T[�-0����LBI�Q�D��t~����J�s��?�m�d�����:�	hj��?�4���o�ftH���`q�N�sB�X[��kc��r����#�����6����B��=���}�<f_AE(I��Z���LF��,��&���\`���{�h�F���������� Z�����4�Y|�Y*����H�A�E�0��c�W��r�G$�9�
������G�JKV��A�_�+����gC����������p��$���n���
���g��~����M+6�T���P[T�`(@���?�t��b+�����v�q��������"�`r�"��"$��T��t��nN�B-��\f6������4?����$N��9�H�Pa&��/D��<�R(�����:`M�H�D@:_�uH��(�C���!��	������N�{E�z�������%&��0�*���}�q��eRe�Q4��L��Oy�<�����)��H��N^V�R9e����K?���v�u���Y,�zh�|�:�'���������yK�I��$�����
����}H���RJ0�a��_���Z�T9�W�^�88\���VW�M+�i����C�T*����5�E^����7���|:]<-��y��d���<�E��E������(�^E�b(�Z�K�?��a�Uy��P~�\��`�����tQ��N����0�Q��bP�y�L��!��wY�$<Z���	Qag���?9/~W)����PZ(:��__t��H�����}1M�oa�\�^[f�����$�)c�3�?2W}I�R�$�J�ly�fo��-|���1������W�kH��`X��kA
U��$��W@��5��
?��T�W���7Z7��Oh�zu���ib�SW��g�U:?_��,����A��D<��ZYY��YY�[r���q�Q�H���i]41K��V����s��T�������M�q�64�7���q����_�hp���J��+��~�W�����M�����(B�S�l~PFb�*�W�p�'p+��5��T��W�Ts��yuQt\7�����y�q}�f�}���8�)n�Z�^�����4d.<C?y�L`1��G�E���u{�l@������|�k2B�����������q���|%��������������^B.xT�M��{�X��*�ly��u�\�iw[�?5�[9�[)���}AW���3��+����s#�������Y�y�Xh���O����=��R��`�;8��QQ�Y�����CuGo0�T��������A��(9��T;�m��M��V�:LEV����s���#�������Lx�'���<Y�����8�E�a��YK��E8��n��5�������`�����R��R�?����o���j�o������.�Mgo��Rn�n0��!B	�W���O�blkd9�z0�����C�sVnW{J����f��L�`��Gz���r��_Y���MV�^���ZRc���}#54R�T������X�]���CH�G�:A� #�)�{:r�	{�%����Wo�Rr7�k��`�T�������r����g����Z�l��Y9�!�h������4���7�$�W�f@�������?�G��}�����W)������v���S�������q���o������I�������=��'w�C~�k����4�_����J��G�!�l+_i=17_�==n	���]�1He�cU�IV�V���d�]1�5�I
�98�����ll�'��:�4�������f�A�8�����dW{���-���������Ii��T�C8�J" �w�Nm��jIm����������P��w�����^�\�����]|a����t4ge��������"4��t�����EI����6�x�z���`�����W����\��W+J��{��A���y�?e@���2^N���T��p�{���?��5��Vk?��J�^�W��a�p�_)��j�<����A�]L'^'�y�C�\~��������<�n��c?�.`*��W	'��o�~8*����[g�"Pe��T�T��4�y�����������:Kt�~��r��9}�d��������D���&f����kB�P]%�����H������8������Q.t�(���9��Y0
 7:���m��������@��0�v�A3Z�?A�L�Gw�`�9�x�:�-��2�3F
������M
i��*��A��#����59Me�L��:T��!�c��_��t���s�_b+�.nT�
)#�u��}u-�C�M'��V�P��{;�X����(��}��KEys���������w�X���{{34b��ho�w�h�������~������������������w����������(��o����G���
,��(9��F-�/����!�C�R�"r�|/A���x��1hw)K�+�ci%����@�)�go�����N=���n6B�S������1��B�+�A��������#u~�����B��^U]/&6�S(�����L�em����[))
��vo�������Au0��J���A��_;PGd2���*�l��e���:�=@��X�i�����B���Q%w�V5	�j�
��#I5�%c�.���r90_�oy����T�&��Cz�`>�C/O�m^��:[��Q�7���s����%�]�5��E����J3L6�������s���DV�.d���F��nDp���4o�`n��.Oqu�hQ��t^��x�+������<8(V�5�YS�bs��A�y����sP����^4:���h^
%=G�2�z���6^�Z{���SO���rv:��w��v�����dR��������1��8R[��~����=�EG)��#�HS7����u��\���h����/�K��h�|���4��xQ��P�H)��S������Q0�:9��U���j����^Z]�}Hn�����^|�Ty��0�~��^���������*��1���HK���3I�\�2��������_��RpT����������2��X9���6�����u ��	w��u��RP�v�7���+�E`G�����>�co�{^�Am�`�����A��wX������K��\�ddB����G�p�<��y1%�@		�f7���	$;�1���0%�>�Q�G��a��������n��G��j��v���1���b��k����d�,�/)�[�	�����C�'�}��I0W'.��@-��XG��(U0a���o�\#���C��b)�Rx_S�O^%E��]�����>���$;d�~r������\�=-u^A�[�2�����4���&n�o7G��[�]`+������(�J�l���:A��iM	m����5������"H�v��c�O���|X<BQ�v8��Xq�rd5��|�0��<S���x�-��3���A���_~X��q�3��	%�4��g�^b�Rt$#�v�xi@<�@�U��m	(��8��L�y8eX"
?���cDl���[M��}��9�����#y����@	�!�[��y"��#���A��t����g
�]�"Rt`rx��(��3I�������  �8Z�t)�<
z��
�D�M;�M��=�J�k7��M��Iop��b��|��=(W��y����4��
�@������zy5w�S�Y2^���
�*�f��z'�'/<�S��������8��@�R�����I-0���F��)AY�})�n/H����wV	s��g�0|���f�A�O����Xm&�-hg�0�^�W����ou���m�:!d�GM���/���zFq\���p�r�R�|�Bd��d���I��l	�-�$� ~�Dg���?�_5�*�I�P���b����p���y1����g}��A�N�o
R
"��.�����+J������g����g�g�
2�$\XR��Xa�Tq�$��
�-���#fz���CH��T�=�(D��ad}���Ot�����8{1�)w=Y*I|nu����n��6<�1g|��k��L*��L�7�Zd^;�L����D���n�yz����79����.�}�A3\sp��MM_�Q�|
w:yd��A
A��2M�J���t�!j�X����D��W�.8�4']���(���de*G=���g������7��j�g��z���*�*zm�C�
Gs�q0'u	'=/�@���<K��z�Zm��.��)s�a�S:���u�%E�=*�k����3�����pPu����
Vs2hszD�$r9>��E�x4pe	���g�e_�$)�@�J^�����vE/������<���0��
tI���E�g������N�38�G$��k��b���IJ�:]����"��	��"����n��:����d���N��\�]='89SGu�������uO���I��pg7�1@����<�(z��5F��Z�����+f"�J��d<?_O;D�)	GL����R���?���j���`$8����o�,t$P�{��q�:-z�����=�����\�=��K�Y@`Y�-���Yb�!�<��2��p��z�Q}���9�2{�����I��.`qe����(x�t�-N��,@q�m�����9��"���z?��];�vvBM��Q}a���!����\���X9Y���p��
���Zod���ik�q��E�y�����L�K�&�K�e��Vn;w��@h�����$t�[�b�rY����A���yXG�}�����s�M;*e�k�C#r�ni.8�N��M�)����P1,���G�R�������^��b����2Td�A���2:-HdV@�� �2A���4xP8.y�S�Ay���;JtN	!��,�'{��k[���<���
n�=�x�$r�P������EB�+�Jx���\!�7��V��$]�R�5�{^�`X���z4(����^���;���.��2�<�2hX<���C6,��R�{�,'y�����WEu~�"��X�����aX��7���.��8w�=���m-�B]�%f�5��V3������
�o�\���s3�+7�n�����>e�Y����x���]��uM��k�����+~����^�SZ��Y��Y������+���$�jd?����������W���S�����Z�F���m����ikk�b�����17�M{��~��_VW�Z����,��Z�_��k�{f��bJ�������lpx���N�n��s�?�'c���p>]�xC��6�O��c|R{�K`�"`&�A4'����)[H!($l$�����\��KerH3�l�&�J�I��|����Mb����=CI�aR/�+��pP;��J����qp�����&���7@X�����G�[ ��-��ZF����c.rr��A�,"X���"���:��"c:%K���O�2X:�(;��,��%CVy��q���:Tx��f8�o!��K�(S~\�	�	[�������2j�m���Z�mJ��}�"�j���!�5y����ux~����Uw��.��J��`�_b�F8vU�(:��W~����_���~%��~����_�?��*�kA�������7oo�8�������(����m�R$%��"yH�����`�c����om��� E�&�1���L�]]]]]��FwPw���v��hT��V���{���������G3��"}o`���[e`e�	'P�s�/���v�X�����"P#�8P~�X<��D0�p�>�m�Cx���p�F\���bN5v�(L��H6.�:������bn�KN�~1�5���n���M��~���������6?�~-���?n�'���������Aw����
/���f-�9	�����z2��v���*~7>8���P"�d6
`0��2��`+��(�I���T]3T��g�ST��kO�w�I����q��JC�4M�m��V�Zu�N��-�����;|�k2�k6���6�L�����A+�#9o?�C�k= ���=x��.��g�����
��,����=�'���J?���������6����*�^?�^
��[PO�������~���x�F���/&o�HTr�I&�6�Uz�[���_���S�>���W�)&.�|��y�W	��.�)�jObj�KWe�n*����x��:���?�|{�=�,{/�3�������'�0�iI�7�8�v��`*aa)�*��y2TG1�.�e��W��f���n��dM�t{aL��
�|Q�#��Fv�V)RU�`xM���D,���>�@����N�8
�s��G���s 
��`�s�O�T�7�)�GSBT�_pF(��|l�s�e)��p�r ��Z;���`L!8y�H$*�BoPn���ch��Ef�J�!KB��(&^55:l�,��P����R�]"�.9d�|0J�E9����!���x�&+�F
����aT�Ktn������ �<a��w�9NYG��s��l>���B���X4�d���Or�]�Yt&+&T�����f^������S�%����rf�4g���N
k��Y<-�M����e�u��q���Zm���z�ot����6YP���L�74]�����&���n�wj��R�jM���������M���q^�������x�B���w[����Z[/��t�)_�	��"��j�z��Me�0����f��>M"A���ii����n)d�������������Di�(���^!����|~]�G��
��O��P#<�#G%$�-��[�_K�ek�q�:J�?p��aK�v�.�S����r��YX��7W�����x������J���������}��4�{Q�x*N@�@�.G�����j$�����K�he*{��h"�'m�E;O�s�q���y�m'oO���Y�d/\!G/�G��n~5lg����o�����X�@�x��m�4��i����[����-�31�*�m��O��fK�f�y���2��C7���S��B�
t��6j2��o�Nxs���4@���2�O���$��0�4^31���,(*:9\��p����0Xj���7<���l�+���y�H}��l�����h��(���x�����'
}�~�D�;�#C�8��W�i�F�(�9��<��6����w]�4�w�Tk�B
�h��T{sI�y~��N���9<D�S��s�/��Fv�g�ubCC-���[�Fk�j�������0��%�����t0��,��H��p�Pj�H����A�|�a��"U������1[K�h� �aS���y��n��#���ZGt	�N�����h8+A�v���QP�,~6t�Q&���~�I�b(8t��H�K
Sj �\	���HL�	6i�8��%�G/�l����>���P�C���;�j����8��-���>=�h����bF��(<�v}X�(�/� �������5�I�!��]�8I���}B��=v{$x�'�,:}Si���+�
�������1)����D����l}�U�-V���KG~<����i����$h�-;��TM���C��XK�:w�! �P��o�Cwe�A�Y���OQ���"�J���OH��������2�gY���/���h\�Y���H������t�2��Htk�D+�9�IJ�����]� ��6w�w[����:^�Z�vk�[sF�uW`�R2Tr���/�0���Q{�1�B�p�����p���xw�S��6J6����dL�["���c�az��j^��'���T1Nu����F�*}�j��;���h���DgJ�O�'���D�����i�^�� �����`���M=��Dxq����mHF���SE��Z:<�����9-=!.)4�_lUh�q�w}F�i�����7j�[��G��h�9��-�6|+��������_!7c�T�%����_9�1'�m�=��|��f���Rw^V� l!i��L����M�
c7Y�+����w�m��>����>`Z�U
	H�l1�T�v�9��W�h�W�f}�B,u(:{������B�#��-����P�e�g"(j��A��z�2Q���@��?����g��h���Av��g��	,�����7\��[��F}(|���a@�F	�\>�h�H9�*I����^����*C���V�?��t)����@�@�0BI�C2���s�V��o�pgTnqW���1�U�Z6
�����{�]S!�,����r{�R�E�.������c�nw@�Sh&��.H�*x�4�kGz8l0���N(H�|q{�'#�[��/��q		�6Z-�r {��ouD�@:���{�n�M��ftZ`a���&�"GZ������N����8�n^��%qV�<�R��h�dK� Uj�,�:��#��CZ._�����h�![�f��V}���l�|�K'��h���;�r�w�|���<��"�3)����{�}����Yk�(6������������~(�R��JB��>��cL��f�wiU��!���6L�TI���������FU��}~�D��[e�_�BD_0��������~tq�����dX����x��$T��JH��
t����vL�L&;.7�G�9	���~�M8�Y�0#Z<���64�d\��<!K��EJMNs���#U���W(j�Q����2����p��Z���${&Z����"�h�_���N%���J��1N�,16�IJD~�<�s���e�OZ8C�3�D��`��a��HQE�s�B�EuB	���Gx���?����(���W.��<��-+���9�g� ����1���z�o�it��?��������������b���z�F���~��t�^����|g������QS�s�������aH�z��w�,��40&M�zo��S]���gh���s�v�BN����d�E�Q��x�R	�F#�3E<�a�f�e��2���A�������C�����"� ��K�K[g�����s���C��)���.4��E~1v+V#J$�9�%-��F8P3����x�� 
l�N�v����������s�:t�(���q���4V�\F�H3
4��w
fXn�,���@�G���1�8�����/v�
������������v���U��N�����Ml��N�7�:�LTw�^����z��u]�i�ZCo��xc������=��_V���'�<�@@$�7�G�����3)bd������{�QX�� �d�\����t�b�0M����!�+�H<��4+�$�p��4�E�yi�5$r�Y�37c~�T�Z�&�����mJ,�.���]���ki����aG�84
�#��|����d�(	���~�d��"����3/�����B�7�pJ/��@z�k����a$����y�7��A�w!��q��y�6E�%h�J�{�V!��J2�Cc(���{��5H;��SJR���?�VIJ���EE�����a	�V	�d�I���6���t��i���4<Pv"z��x��QZw�.��t�@Lu#IN���R���'���p�8�&�J"�����^k0��009���2H���v���s����RR���U^���G
�^|Sk([�H$�~�$	���
�]�tC^Z	L�������s&��������/Y1���������.`%?�mX�����Z|w�I�����:;i�������7�r�y����1����j�0������5v�|�gw��#
H�sKJ���,�(Uxv�k���Y�y�yO`m"��>�`FxS�x�~~�c��������%�3S=��z�)��b�
��u�������IXBf{�P��y���������"���m��f��4 �|��m��s_A0
��Mr�����m�����(��!c�
���}*o�������}����?����������M��h������=N>R�� ��&����U��A5Z1e���)����v}|H�6����`3�:���v��8����U��Nk�����������)lmr�Z-t����t�8�d�r�["��R��F+>\~�&g"b��8:?9�8~������������A�1�����[�c�q8���c�P���8��iSFn��������/��H��:�P��2�T�2����:��1Q;�W%����O��������F�*�cM|"�T{z���<��8�(�'���n�S��z�x���y��6�bF��#p�:��'���u*��*l����-�,pu[�}�g����4;����������C�_��m��C��t��_ou�^��v�Q�����^��x#�l���`��[M@�|�������F����,\V��7��)]+�?����>�w��y�   A�Z�����8���Z$���B���JE��p�_�G��H����\�zq��������S�U�I����w������BZ$�9-�������GMD\-�������3���D�����,&����&��V�����{�^S���$Z�
/[e�Eo]��2���y bB�����x����{�e�Q�/n���o����2[��L����A���>��^�0��[� /XT8�8�?�R�P��ae9�L��p������Z�$�ou��?������?gTw�a����n�>���������p�l�G�Fc#����?�E��s�Wc�'$M�fHSi�d�S&M� ��Y,�����>J��i%��:+�L\z���
�!���x���k��;u�S&����a�y��S��q{�8��J�#�f�A�L	LUO]^���7���J7u����[o8���s��j�m�: ���������^:
iI����M���_�!�f"d�nF V+$�w~�[U�!�F@K�7�&��dTLP��1�&w�^���U��l�T��,P�2�t����5����)l~�m������M���{l�4Q,�O>P?A��Bm!	\���61��7�9���I�@�E"�M�u-�F��=<���%�/�B~����!���,`������ll�&3o5&T7�!b�v�x|"
"�L�DB.�2G���"���Pv�t��"��<������9G�[<���9`#�$��C��T	j�)'������V����3;��������o���������8O9���O�#�&cG��%"O��8�b<F|��r��0B��R������)$|Y��/?]w;c�S�v���	|9�{|���:�k�\�IB;��?j�j�Q����s��]�6���k�_Yem�]a����v��6q/|�y3+B��@#g���j���_���D
�������I\��y�4��8t��`�i��
����Z���3U��3a~.��8S��8S�9S�k9SEn���*[q�J�3U$�kx�7+'��T�#P1��5P���sa���M�����"���3e����n���t���_������w����>t�
�����������g�a�t��Qm�����a}������A�i�h�_��y
����4�`�����JF�:��R�A�z.���>��GA8!P]�� 
00�!�Z!�p���A��E��V�J5T������K�8<�>yv�1A����X�C�Hz
D��$*��F:��:qR8�����Z��e��<^6��/�p�)�>b���S�{�[�v�z�f�Q����JjY���V#�~�KG2~�i�O�D����IG�Nt|�nw�
��������G�Wl�p����w�g�������#4$r�z����4��$v���S����#��w���J����P���H��e��a^[5�X��E��j����:N��#�mb��bM�[o���E�dy��z2%:pkr��H�~0��h��fS6��(���?���7[�X�����(�xi�����hd�WE;=����*qa;
����ZZ�(���eDn��1^��kFK{�M^`nDv��M������
�����wS%�_-C,��Bb�)�b�D1k)��6�B")w��2�P�E"���h��o�!��*�G�RQR�R�SX��h �%���,J��I<�|��_VzCA��)G�%1������q�:�;�����A?f������tV����0��b���
z�I�x4������Q:r3S$���P.�]Pr=W�uH��$s���n�h$���0
�0.p�^�L����K�t_������l��My��]�+������E�\�PT���q��������#y���*��]������LE�G{���������%nG��e�W��!3�>�!Z������8>��e��[���@��Sn
Y&%���`�������
�,A���c�����S����!B�����������G��:�'4��ee�)a��,:j6Ag��TL.�K2��	^.Mx�J	���x%������!z��cU��6�Fk��Q��I���	��~A?��H>���4o�0��r��r���!�%@�xh�����L�6K	��9���������#
o}�zW^�����n�T�j��q��k\QP���Y�`/%R8@��l��/�	����jx
B�K�����B[��'�p8(X71F�a6S(�����3�x
'�����G���l�Y��Gg�S0�U����l�?�Vcp6��,x^���<�<�F����DIL	@��'����������E8T�Z������;Jk����PV��@Bko�4v.c[��
z��{J���w�������p�h�$b��UP( ��8Q+n^?}+��X)�����A�{�l��k��J!��U��=77������.������&��Y	�F^7��[b��������)���W4����X)�g_4����9p�4���\��L�f��+;�c��R;4�Nl��b-g3�O�����E����>1��I)���V�(����6��7w�d�DJ�d�J�#����tsM��f�vr�����B�e����n!u$����F&VH�O���N��..�/��N��WG�����>��P��EV�t���1�-.��-.���-.��-.��..����z����z������R(D������.�x�5��I��V�CqJ�P��%��������}
����(�TBj��rK����:J�q�Fm��Fn��V=�U�a�v[��[�m�7�R������s��u�3��#�����~Z�&������N��������C����������frx�^
���g�F�(?�g!�p��G����a����������H� C��QOQw-hc}| ���j�n/���/��zDEH��wO��QoO���+,�����*��Q���k�.�U��d��I�m
v	[^VRNY�������x�q�!6�+����Bw��$�a�V�6���_w�C?�.U@:��y�k��@��]������0
���<�6��>	^NlH�����e����U��R�})e��}w���������������Z0O5����r���z-����3�*(=c�$��M���Sc�n�|��������������zN���?:����Lv5�{x:|�C<#�h�_��?�(�'M���s��h@�0]$�#�����h;��m�5o\�V�p|&|B��O���hV������h���>����9p��E�
Y>�1Yk���[�6a��nu����]>d��w�^��5jM��9~�Wk�����i��#�6�����o<��?�}��wa?��f����$��s
���5�1��1,�
��/��b:��asm�/�R��ky�������	��!���+/Xs0I	2�+{As��#��M�h:]�#���;n�>�k�^ep��w
+��w�^�����O:�e���N[�t�d���a
�q����HNV�<�I�4�#;N8���6��u�:�G����F(�2\�j�;�3X�7��l���^�]r7�?��a!'���I%7�MvEi�;��*?�h)2E��nI�o��U��qq��0Y|��+���I��-�+"�M�S9i{������_��m�"7_~�7��fq�LW����K�5�wF���t�������0+v�"s]e���P�A�C����5`KY ��%p�IY��Q��>H�CA��1�Q�Qn�(��VgF1q@[P��%l\KF}�f*,k��F!Z��=�X���ZEI�e�'��	��RSA��
e\�U�r�����;<��SGq{A{��rw\��gyr~���^��,3y���\����S��C��5K�z�j/U�^���������3��eu ������oY�������(�[�������&���}P@��!�53�M^[Cq��P�.E��g'Q����_%��=#�{M��b��~�A;dT�X_f4���."����?�R��
�K��6�.W�B[;���^�z��M���2���j�^�V��1x���_G���	T�����uNc����t�E��"���RG��u.�:����3�~�kR�>O����E�)^���YD��!�K$�����.~�dy�Hs�S�m����6����r{#�{<I�
�}����������"Z.;�0%��bA7p>W����?�OX	�K�����x4��������f��,4�D��s�c�Q�r2j[Kj���$�Z�1Z�lD-�
_���r����s��Y��)ws�;37�K!>	�AE&��{����Y��t|r�G+�����2wo��=��a$>0��E�VX�R���%\�U���Vo?��ztq��?=���|��.��������sJD:�u���"U0�mS�*�])��I�!�����z��~w�s���� ^f�������>U�= +"����� tm�j���:�6/�#5������$&3F����4��b�������H�z�lT�e��0�]�]�V0:710�,��;L���q���3�.�U9ym�?���^�����6�t1�j����W�Kh>"�I��_Z��q����z���;P�I��k���|{��\��Oim����O"�:�X�Xb��S�0y/7�F)����mZAHj�OEw�������|�k��|�iOl������9�?��vn�������?����t�Z`�n������u�����[+���.����0����?����������p�CM�����t�����HX����0�94��^�5T�F�P3��G��c&�)����^��$�����B<�
��������r�N?���,7����g�
�f O�$]D���@k�`IWfC��Cw	�|���sBr7�1c������^_@���na:
������KS�������<nD4�%��b��y�yb��}9�.�����x*�������;%���
Qtd�w�M2��
>���d��7B����@���'�+�!�u�	��(D���%�0��/eH����R���|��*$��,������<���{rty�����%7h�@���v��_����|y�c"��h�?�WV��u�^�@������K�gT?c_�1dMBm����A�F�dV;��)�c2.��O�	�����f���S�%c��/�(��x"V�H[��u��(C�����]��]����\aC�@�^��	d��v��V"yT�d������,
�`��A0��;�]�ZzY�b)�P
��������_}��)N3�`lDj0R�ke/%="��>��V�0�}�����n��-=�6�1�C4��E|��tE
[g�-�����>�������pI��p+�,�Cj�Fh{�����KD��'`	��I�����ah���}B�� O`����dg>��0���s�&��jX189U4n�A(���#[k��{k�]v�=d��� ��}�FY���p�_7�bG~N��}P�	����8��q5W&|�m_����R��n���Ma��K���e��~x(�C�dB;�)�b�����$ ���h� ���Ubc^��2��n��..����)a8����stvdr88�`|H�69Bg2�����2�����A;_��+eQGr`�H?���h�lU�7e��l�B�/�����w}WW�&j�%��CG��,�=�5�F������#$��	h�k�qY4�R�&��%��Qf��I^`=������y1�N�0���8B��r�����8�V7���]U0C����t9�{�e��[v���}�]�*:u�q�hT���V���x��������=�F8<	�j'
�j�S �$�pS�PlDM:Q[98��s�2������H`��;��������Gv�^#C!�ahW�@d#��vM<u)�C�B�����a�Apj�����,n��B
B�n85'�gT9)V�J����|�7EV{0N��:;<{�*%^��Y���;e��#-�O��b��CQZ5��"�'���a�9�)�u�B`G.�e��N�)�''����]MB��@@�)����+�|(�<�I�-�:b$k�������`%��Dr���Hb��TZ�-�C|�6����n�c�( 8�����	���#%�������}��b�1Z�� 2����qt��khs�%'�B�
��SY3D�tPL���@���{����]�1�0�<6����*��/��Ut���|��~�]o��H��@[<�=*.�����F�o��e�Vo��4P	]��:XW�X�\6�L����W�B�@�C�7�v�}�k��V2����N�*d�_��%����j_@�TVgc�p���](��&�#e��Wv��M?�Q��������;�~c�ZS�OV�)dw�����.E��AR�;�8�<G��zV�0�h�������8�<$�:���A�2�p9/SIp SIR:q�����j���St��9����Yi��:����w[�9�������<��m�{>�oq�b�I tV�����5�
1<�����j�|��Y��8�$4�<W�W�-��1,O�'=�7��R�
c�	�b�}��bH���7
I<�.�'����lv�����L:1lq`���B�qA�_�����������CD�l��M=�P�l4PYZo����C�f;e�:�O��_������/�8���il��?^b��5�j_��6<���z�j�B��]���w�_M�a�����nw����m]y�PSt��n]�D��(�G1�<������O.�N�.�W��N�O���_^�]���#�Y��}qL|8s��xz�;�
:������A�W�Z ��5���a����V���"��B��t��S�6�E�ah��������K�g�Ye����8)���m]����Y{v[�(y+��V�n�s��C�s$�+����>Q��b����
	�ir������hw��i��N���v���9�����*�@Y�dX�%X�������2U�b1[T
VJ,h�B�<� ��#�i��fw���w@Gw�.9d�F��<�f��Q�\H�Qkop=#��NY����=������)��
�q��l���
�;����8	�A���AE�{�p�f�eAa�vo����V	����N}�A=:��"�Y]� {t��4�b5"

��`��g�A7F�BPz�����
��I�$2�E�4��Q��y�"�LS��E8+j�W���E����:p��q^G�A�P<��xA�jm�����{�F����%�iP@�6��z-����c��!�E�m�AJ�Z�AIdi,�Pp
�t.�PEV���j�k3��F���`Z���KS�������;EV�Kv�
��g�E��n�F���[a��,����"��Ro}*������E�
� ��6���ai����PU"*�l�KM* �������/���u�q�6y��>��].���w������~m:�?���p�-n�%�(��)�	��`�xz!7�t������h��b6P�<;�,�r���WZ��e��6��f�S��8GV�e��n����:3Pz3&?�d�����
���lm�����3N1�2�X<�������Jt�[6�0s�`A�}�E������'�hM�E8O�������Cgh�S�nV�=��f�q��h�u�Y+�Ip�������&MIz�Y*f�G���w��A��Z�	1���d��.A#P5�S��|:�n:�4�����Q���g�4�����"����pkT]��b����;��hRp3��M,&�Pil�A����Y�/�9�-��D�L{��>���j"!�i�opl��8F��)�v1��_n�C�TZTl�b���"<h�B�_������
��$P�$*�DO����!8�B �2�{�� ���a�[o��j�#����_j0��������	~��}�0�����7�����?���&�{�
���pq���[�k��Q���&�,������TD�L5���z]"5-�9���l�9�%H*�������W��p	8V
���E�
���-m
���
��}��s��t��x8+��o�g���Mv� }#M�[t�������) 3��l1�1�(sa� ��#�~��
�*&0�����3<�i0+o�^�Yn��{������qn���z���&��VH�#1/��b	��h���,F�������LV�;;z=�i��/��"~ 2B��=����h}[�6-���:�(���������x�Q��t�
��u�����7j8��t����=����ui�+��o	`�_d�`V�*S�&��0�p�I0Fd��z����4a@nI�a���JX�p��"�������C�uQYZ��*K�m�B�L�@�CuZ�h��gc���=gu"]g������Z�[��� P�$K���><X��9�?@�1BTu]����N��� d����#&��0�����G�%b=���+��[���7�5�0�?JD���������D�����c��w����+�{��M!k�hc�YK���A����A���bx��"7'<Z����������g�E>��i�$m�\�:;FJ��:bI3Rj���4x\�����7S"����.g�`�Gul������f��r<}�������?AG*_�
�������,���WS�R��J2jY��~�����\KUH����#�k���+{3�e�y���m��Z�Z�4�ZkP����[gTR���!��:�����������I:6/������d����>�W�|3A.�$��c�";�~0����C�3>�x�Z����J���St�D�xN[���	�l\��N���~�F�E��h
�Ww��E�_���r��B�����!eUq"����$Z���c��U��|�mw�������it��f�=�k�3�VG�������������
(�&i����k��<�`����|�G�k�V�{������)��v�)����v�1�������"&^���VaO?3h��Z%���M
����N~�m"�[A���v���Fn���V�������%Xy����	�>����
�	��h��@�tA@���:^XF�������(�L����~�#RO%��|/Go�8��=���}0��yE`��_�,eDV�$[��dflc�������f��f�S;�~f%C��N����Ct����;��v
zb%h+	?��0[����X)K6�M�
���~+�~f%�kl+���b�k><Y��A2�������0"����n��T��f�J4���A�������X��4��������j
d�^��/Qt��\`��d9C�z����@KAH)�G1�
�<HA��8K;�x���G3Xe7t3d+K�������k��{��Zb����O��m�p4��������5i����������{Z�%/���
���������7s\	��0�(!c���/	�JV�S��)��:+���7�>�$TX�z[�> �GIh?����{��x|�#�r
���H���h=����C���_��I�:m��%��y5�u��`�+��C<_%2@���6A'g���M�t�V6�����k�l<������L���Rg�'vq�BW�Z&�	y�`@ �BN[��>[wD���z|g��|2H"��Xm�I��w����*���c����U��Z#�P��`6�[��4�QN��&���=7�I�R�(��������������m�e|�I'��X(��b��YHa�Qn$�c$A0v�O�<�I��'/���L�Z����s���$Y�i�i����F^��5����F��p�M�g�����J\����6��'��&�����{�+�I��3�:�j�-��g�	����!~������n	�)@�BYzZrw���?f�n�b*x[�!T2<�%u������#���@#QA���I��HIC
��P3�]������MP�������&�*�[���=O�qp�nV���d8��=��0�|�X2�&%���VAT0b7XeY��UK�QsT>/�%Yl���'�����9���l4p4���?��PT�d	�y���J����\3������{uY��V�)���������
�Mp���!E<SLZY���� �
��X������t��,V<��}S�'|"A�~�g#���P��lq��5����|Y���Q\*��~B�l�S�?4�id��������xN����l�i��L�����*���|�;����eXt�t�/e@��P���c�C�����=���?99;�<�������*�d��(�.+���'z#�S���V��:B�������a0*V_d�������m�ph�]�S��A�D=�h�2*���h�`���=� ���G�]2�A���@�@nZ����#����`v�f
y�8k�g8r��i���(p��6I�sx�{Q����\�^N&k@*3F;��r��lqVc��/���KqH������`�e�
M'����8�4h}��&���5����@Ji��-(����n��;�|@�A�,���$�����1M��\��
V E����M��7��6t*���� �#�Q�_,P/�N�4�}�X+�RL�����v����Y0e ��u�j�^�w�)�d`������v����������~����������n���1��a�r��Y<S��u-6�xl�I�N��^et�������fX�?���Q [�?����H��Z��z�{A�2��V��
o��\��h>��k���������6�|x�B4�^1W���;N#t������w�wh.��6���w�!+��B�H A�A� ���e�a2A���8Dk#V�rJ)��D�HY��������,yB[�~5e����F/��q_�����������>]^	���{b���lz������1X�v�i�;��C�N�����������(����+��rpp�#]�Z�u���*�{	���C�����j�V���Qc�"��������2�_������ji+p<?$�d�����Ysn�T�i����Aq�n�fU�� Y���������3C������=_&�0t�z**����� �I�XS�5�II�::�co�YV��f0GC�y{��2_���^b�\� �vQ���K*���- �oH���}�K�����[ll�����r����������@en:� ]��W��k��w�e��C�0o	l!����82���q47=��`�%L��_~�P� �������eI��AY_�V5�=K��^����3��N�0�mY��dl��eT��	�%��-���S-�{D���!w3v����[\�U	Zw:��A��i��#*B���(�'��������5�!�e
���[Zh�Z��v�����������
'�z��0������
�'����
{
�	n�.xK!�&)"����l�J�����>q���V^��W��}8UU1!�^��	y�����8�����7u z���XwD��3���'!�w`@a���Y_�5y9�S�"��nA>	�,�
�j6FSF_�'
��j\��A�t)�-*#F��:�Pc*xUI�����N�[�[������[�B��?���k�������R���2�o��)�3Z-�'�$X��d��7`��Up�X�q�������S�kH�_��!���Q��(�����8N����	�M��L���(�=���K��wG�)��#���OA��#�8����a#n���K�y�����h>�QX���G��
���Ro|	�`���W`p?�_����c����q���M�V�!����s��
��'����f�;�0�T�������F�����d�k�8��<�P�c`���zA�/��������B��	��A���SC_���F����$�p�B�?��#�������mh��f�����8�����`��iO�=vt���#M6�ZM��l+�s�<+A�1W�L���h-�y��F7OO���I�I�gc^Z���98�D�GP�v���������3�OE�[�u/��NL����9/����u�r�����+��Z|��0��O�@�6}�ppbVJ��g�E^�A6��l��"����b,�z�lfH
H�jqba{��D��]�?��(E��n7)4	&�A��b��,nc����!�Rvl�=�vBT�D��,z�x���g�*04�u�<)��!�:���:����]���|�������fD��w<"�H��x{�[+�+9��������o�7+<V�;[��u���$�&d��,�?E�f��dJ1y�fJ����D6q�5a�RDT�Fk��u�j����A����[8��e�1d�R�G��:P	�A���&���t��m~������r{���yW�,��CZvq�*\"!)�w9J��;]���2.bS���b��*B�v�J71�������s���'E�2�{Iks!���B��Qk8��
P��G����}�w���[PD��I6\P��;�5�<@D��`e	
=�#�s�����^c�������Q"�����d"�$������b�j���FZ��L)1�������d�_��'b3����k�Vf��$�.���!$+��R�
��A����j��FMg��3<���KLv"��J��.��5	4�Cc��q���
���7��;��c�H��������\����F�����r/�Y�V��;����\��L����l���#5�;*|s�xs��/��.v�)��T���OBs�"�p��j�'��dU������fU�'��s�5.�����\��~v6.w�q�����Pb]��8��F�ag��[�����O|4l��W�) �f�+�����8�����a�`I�		�6���IP���,�p�r�<s_��J��o3Z�!�B:u���������yNqVik.$%���%_k���w��`�zP����yM��O�����JB�Ry�!��m"�h���-������g�O��:At;�-&p@'�
B�M���9����!���/k�=]�U������j_c���W��.������T+/��7������i�*����SEE�P���������|�N����N�l�
F��e�j��K�q�#��LB&�HiN�B�;������� @y����1�Te�F:�P��G�3��Y�����><�����������]B������E�qm�M����R�����5R����lx�%X�-����=��]����l�]N�yx3[��a���P���M���e�=Z�$2�������G��PY���XLjL|�$�����?)��|5@��%'mQ��$6���0��o�+��Zc����[oW����i�����y�B��FzML�� X�c���t~-�_d���������\�5�tI�J��d�M!��H������p����	F�I����b�
�����V<=��� V�l��M��p���C	���L��������f��n�F�
��x�-�!�l�U���[��=��a�
��SL>
��t�����t'.��}�!��,�x}��eN�3�5F�*�A���o�}���PE����E���ow	�I���Fx0@�
��)+�q�]+���
�b�R����9��A.[�`����(l1���6g,��m�W���i5�����������^iuy��X���q�yb3Rn�oQ��`��z�������y����C��lI����p�h��S�-��� 8�g[����g�y{E�G*�=�/sR�}��R����%���}n�q�7"���2��|�/|^�������
�����Sg��3��{:�S��nC�U��9<:�m�bn}�u�}��|���>XQ���:������0�nn�W����V����;�<�5F�/s��C��,�X�x*o `�e�r�J��p�/���I4����]{��-���U�W�N�V,��?>��|�FH���Z���y��pA��O�'�U�����R��
����x����V�s\��u����o�����:��O�i���h�[u�Qk4U����K�~����2YM�5� �h��=�E���&�wx9��k�A��tk��`�7��^c8vn��5`+���������\98����T�Vs�X�uu3�����C�^-�W��#&����
����M��u�.�����t�
�&���yQ�c��Z�x���?\�P;��:�V�Z{N�'���U@��)�A ����b�*�����U��sF�T��('���\��.~Ze����R�����E9$	��]���u���YM�j����
����6(�r�0.��Km3C�A+�K�
������,fS
���?����5g�;�����L�">���s���^ep��x��y���b]Ni��;
�	YUS�;��/�i�����rl1$����_���u���H�������ew��o���y�mSj���6��V���=J}���n#K3�M�i��u�Q	���8�����X5m���&�t�*���l~LG3�!BG�P#�1������\*-�k�+oFc�:TO��������;::'�t����C#IF�����;��<8����X���v�q8%��k�\������	���d2�z��0����� G��Z�����x���o�� �����6����Ek_��q$��h�=�V���I�X[�����Z�|4��~�g����_T�?�����Q^����+�*Y1P��&C����oc[��FV:�����ht����jW��.#��5:[Yu����u�Tb�&+�.�FF6�
��s�����Ki�T�^�/N�4��*�Q��� @_p��U� ���Ev�Coc|�i{6wE���zM%�2�Mb�y��Z�G��;4s��E��!�����:/�k6�e�������Vm�qb�w����Y��A�w��o���c����� RcH-g��F��6����\q��Yo�����������4��As�q�]�q���m��f�qZ����z�����'n0��w������7	�N�E�����7���i�����HS	i���Y,��H�y�fOh���!	��hmR������z��`�g(	�D[�i,�8L���>�����1���W�d�-ri���NGU��c��j$��F\��?��we�p�����$��7�
��#g4�VaV���=t2<����3�q�v;B�jw>�+b?\
���e��I��K��������F�^���/����m�������gQ�k�}I �	��)Sh���RDT��q��T�!{��W��E�e���f����F�Zmvj����������
��(+	;���hW�F�8r��4\�Q.9�����hA��t��9$8aX:��J�>�c2a��>^���?������4�N=�9DBQ?�4
����wLy�9�Y����^�L�Z��jj�j�iv����vzk��n.y����a���F6�����}�_��B���q? GVP���K�g��������w�
�?+�BkN��@��J_���-V����0s���~A��1pf^>��>��sl]��5]�Sw:��5Z�"Xr'�����,Hc����K�k��c������S��%��p�Q;C�kU�=��1����	`/A�����g��B_=��u�������!�o�^��z�N�;�G��l��q�Nw������o+�u^4z/��<�O��c�4�l�,�9�HH�V ���t��L���a`r�)j9�����9���P��
1���H<��oX�~�dE�6�2����Q����d��0L�.��c}�T�T��c�fCi-��l�\��r�)-m���X����N������Zo$D��m����3d
�C2�,��\<	�;E'��#��{u����f���u��j�Z-M3�����c�j$�WS$���o�/0���QZ��>G�/f�a��2���p5E���h�`u�@�?���j2���#�����8�uS�w�w~��=*�ka������]��mM��gZ����;tzdw���D�[��pv����?��{-�8�k!��:S)}:>m�/�.)F��WG���g�*����Le����{��?<�<�4O�4 �E�=�o�q&g$[�`Dfz�G���?\b�����w��928����K��5�?�A4���LB�8����QtZL?������nVH]����t�uT\]��v85F��%s�����v��(s��&[���u����[�mf����b:����q��~��x��n�1�
�p�Z}��sH^Vv������UG�WG�uq����f�>��z�|i��R��\/�\�������Q�o��p��	^�{��m��7[���dtV(/T�����]r2�V�1���5K��}<=�J4�T��
��T�M���<�Lx��gg�~��%����8��/����z�A��|��a�]]}�8�T�GWg��6�����	�B`���G������g���������uyuq|p���/�ON�N����������p����;�V��WF�<�]���`���2�4�$4/T��R�Z�'��g�S4����qV=�~t��K�������Y����~�hg�hj�K=3�y�5�&]��j���[�7��_���G'G0�h���]�}�u�$o�|����r�f�zR�!��9�?}{tq������n�v�*P���}=\.J��1����P���/�<�)����r;hH
��h/����%i�>�m���mY����:��{��CX|<"-
���_DT)�FD2E��ed���/����~�����5�y���#6��N��C�����;+l�����yB~�>y�Lb�J�3���-����5���)e��������<���|�����S����<(8��i�e���x)�fg�W(x~�������u��������x'~m'8>�j�K%A_�C��N<�A�;O���=4�M������Of�r�~m��|��O��;�yA�����x:���P�}�J�;j4�W�7@E�����j5���c��9��!������O|&�:����]���~
��8���MeqE�D�Z�U�:!��[�� Z��b��[�
�zM0��9d8�H�w�^�����
�f�4�����#JKtC�WB�0P ��sw"$9��;Q�H0�8�hZ���/c���a:������A=� !$B���0N���]�2M����t��cd��H�����$��G������������pn���|v�����	��:X8��
�v�K�]>tE`iq�
���!�Z�]�������8���IT6��n^0.�g�	^��i$#�{����'�-�[���mF�dL����Y��$� (��o�����[�Z=��L�tW?�!S�����K�r��O
��iZu��(<��[��f����9��e���M��F� ���6Xo�f�X�R�f=hShH�?@��>,�y)�O�7'X'��nw��N��������*3,�z����jf������\��5P�{Y���q�@2���&�
|�u�����9��o�&���������!�g�)TRy#�XJ�|�_~�qt���$q/\f��E�Z\Z����O�d��O���xT���h�6%H,��j�d�hoq�%)>��M�5�I�N�%�K�����k���f-Y��
���{���h���+��&�����&���%�mk���}G���1��S������LDepW�V��	�w�yI����A5C|��
w	tW���$M�;���R��z����H`Q�6pH������k��F�)J$ ,��j��;�O{��Qm�p�yI�G�0����U����h�
[�i8��"/d���J�R���6}�v� ��y�
ERF��8:8�8�~~r�ZT=���x��6���?��/K�x��.=�W�?����<����b�[B4~��C�p�G}O2 >_�9Vm�N��8]�J�T����iYE��C. �O��e(j��z�s�u,���9��0PZ1��q�
A��+�]�4�������O��������T�*�>�ZI$�T��r�����%���F�zQ!�
���T�3S���PO�o�����'[�q��������k9�Z�����b��r��k����j���i������K��J��I���y$��i���zj��9
�| u7�8������5���Y��O%���[G����-������y�aj�������%�Y��XY�]e�o������*V�b��.m�(�6u��lJ��Jt���J��
$��=u�r��B�'7���������;N��0=��g���]�}�B�	hM[��&��{������������������9������]� ��x3h7.|�;�*$�����>���1nZ��Z�+,y]���0[k�U�����W��8��������i���p��x�����u{������N�6�k��h������b�UQk`y9~��1�DAT���n;t�h�X��1�c�I������������0���}"]h�9��aa{������^���C�K�����I���>��%��b��D��fK�e�&�hKQf�����I9�����z'.��4/�-�E�dJ�1���"���
,"����l�g��L|��t�I!30����U#���;�������'����x5�j�)^�Q��4�' cTK@M��c�%>��5�h�U��"���8���B�
?�&��5�8���~1��F��v��Za������"��������r9_�����V-����zo��{�Woo��w��M�����������d�����_�>�~Z^v��L���o~�1�������4�t������4k�|�"��
�&��qb�38dg�->My�hAH���*%D[�:���~��j|��W�b�����W�i��M�����oeH^���e���)o6$���z2��v��(���2�	'���O��b�l����r��Sy8[#?[���![�7#����?Y�t�{��)'a8g���R���?�$�����l��� �]��A�l�����U���NK8��Ts������[7�Q�,����a8���?�5�v��|��{` 1�k�3DA�kXkR��
e,���*�q�~6����	sGS^</� �F:�����TN8��B�x�N���Z���xb��0��G2�`�;5�������g�_��d�|i��[�fj����o�=�'^*4O"H�M��m��-��X��l��c���i����?L���J#F��a��B���]�~S7���w��Q�n}���-D�=&��ZW�s��J�����=[������3'�tn���am��ll,�E������\�t��|�y�Y��L������c��v���u2O�����Y+uj�$�~s��A�+�iPw��A���j�o������Q;�#!UH\	�zM~�]�@��wL��tE[�� ���\��4�W��)��Jxi�|R'gg����������|wz�C��0�UI�U�,�>>++
{(���0~�"o���D�I|�
^�����OKO�U���'��c���8,��C�#��O7X�t6���Rh?����v;�V�Y�z��;�7^������������\�����`��i����#�XNqo�#QPE2�h�s��A/�n�9�z$g!��kD	6��2r;�ZX���Wt�#�Y�Uq���2�'v8h�N����v0�9G��4����
�=����<�Rr��`�.���gQ2�R���u9�>#7�ko�~��6��W�6�F{��wZ�����k��%�����&y��]@�r�r2���>��:,�Gt8Q�N��X�sB�Q/��*��+"aR��7�/�C���g���Z��n���G��Z��KXb�^}n�S<��i�9l>r�x�M�J>��Q�Ak�rt���J�|��C(�N�4R�<��9|��,(�������`Q�`�v���5��&]�e���Q���6F��?��XT����g��[�����BZ�hC���2 �d�����8����#������I���t�|{����g���[���;��v��{�`T������F����>dJ���H�e���T�U6��\J�_�	Z��v:c�yV�d^I6��*���j�]e�M�P�O]W���s�Q����5�Z��h8���Z��i7�f{�#�oW�=����9la>�wc�i�_E[A�@����������o�G���e���i��BwSJJ����6���6%���=	��7-
����L:mp�H5bpWB�
�����������g��`q����;r01�V/�q�5�/<�d7i
%f���nv����A�������7�+o��r�a+�4��e�$,���r�������D����7W��c8#P5:d���:�9��zB�{��/�^V�lU=�����H�*�0����#B�w��%<V��l�]��m�T��j���
�n[;��O0�k�n�0�n_'��X�_�@,]��{���5�����W4	�%��O����K�_7��84��e�������P,�{�.)�>1=39������k����I��p���F��`�xl$��S���1��"l5y��~������j���]���x��?�A�"69Fkcs�x-F��	��	3j���(A�)�������h�Ng��2�\�������gf��=Y���pmvs>���
8/I/�f��Z��.�{��
6�lEF���v��N��v�	�����jM�Bv�Ba�],�;���[�.T���`XW�7O���L_��^�1���&���gd�t�I�5��9���R����i��eZ��G����R��-��.(��L��zv���)/�����:������V�Z���5���=������MS"F@m7�57����^�����*L������l�[�l��v�����O���y�D�x�:��H��X�x�cu�+�i�s��O����s<����{n��0� ���9�b<>q"E�i����,���d�5��B#w�r������[s;�Q4q����X��)���He�`������R�{��� �T��>�T
�a������=���g��^�-f�o0v,��_��j^��`�y����=��)2�5�����=���6�1�1K?=>�*U�������,{&J�>��.s���h�C9U������)L��/3K2���7pF����;] E�1h�z���&U^n�TJ���?xO������;�a-9NZ\2��w��,|�/���2�S��x�-aF�g�%���t��#�r��=��D�����b��c���G������m�5�Wy���XB���q�QUhy�K�_�h��9/�$B�v��������V�V��G�vo#9����x2$�^��F5����o1�G��s�cT,�f���(���r��������/<����hj��y�KRb��~�Qp{�������2��_��;e�]<��;�
csF���	��qJ�Y���*��,D�*"���x@Oy��3������x��Ou@M���������6�J ,`���^C� �p����f�G��~A����Nh�C��T��[Pt��8��b�t��Z�4�^���V�^�5���5��9����xB��r����
_#i���/N���
h)A8���5<��(��#.���>�p����������X�x��`��,o.j�E\|���.|�r�~���i4��tX����D���r�����7Z����������7\F�D~��4�������U����J�����KF��S��">9DF����$���k
��3V�m�d=�=S�����Q
��=�O������|��; �c0��2���(@X�����2�s2���:�N��u����]k��Y�3/U^.'I�$��
[bSh�R��>���ZG��>H�X��11��8�$�;;��q<bTJD�q�,���`aEK(UJ��^��8����&[Nf���)m��Q!��w�Uxs6�E?�����hq�=l�7=��`��������/|��#�����2t4|��h[[���F$���]�,�sT��(b������0���qX�����N�F�
�'6n ��g�G�����p��[:bL����<:�f��//�.�.�g�'?b������a�[��K-~�O�d���?�l���t�8�EU����!7��q���%�i�������$E��P��U9��M%���gS�d�K	��)N��m�� �~��D�H�q��7���~�
ga�������{��y���d^'������G~y���{�
m��K�=mXV���������4�^�
}�������?�CF�4�$�e�����Dr�i����|L���1y45���~���u{�p��`�ab@l���:�m����wd��H����bg���t�����gt��?>|i��*���i��}�CC�~��X�x}�?>M8B����<�������]{w�6��������f�TD�Uo�����g;k;�������pt���ro��~1�	R���nw��F2	���f~�m�;{�0b]�Q/"��F'<��k�F�������VO����o�A�vz���7p;q���/��Y��j�T>��'��V��L��)����T�H�`�#N�#c���s6s^���x�Y0z@��'�%wqHy�����%}��
�}#��?�n�fts���Y��=��N'
�6��eP$�#w>M�3"Z��A���A4����M���W������UA��'����W"V�f}��8��	��d�JC(�a��1����m�������+��	������giW��
�����G�(b�+���.i��m��b
��\J�Gp�;*7H��o��S��@kr��O��
��#���d���a<��1�F��J�"@��'�[����"�"h��5[ ����A�;<�*�!���Q*�b�-��sD��G�u�D9�����T,A�[C������^=w��f��:��=����C4Kp��!��p$68?�'	Q��d�d~�Q�4�^���c}[��.~��.�������l$!
7��Tm�f�����Al����cE��A�t�-i<�~�(W�����D\E�u]�;G7��:��1�[CQ�puV)iQ���~��F�.Dm��Q0���`�Ek����I�!��������2���r���KG��!rC���u��6Vu�DH��)�r
ID1U#Q���)�����Kk:j�^�Jp����=�
R1u�r�,���F���4����6z����$�.n�|l��x$�D��p��P����O��%�n��l@�?-������f�b�LZ,�kh�*�A�f������s��1������g� ���L,Y��t�����0��]�w9������;��T����X��e'W	���b�"�B~Y�h����mD�f�1���Z���B?��^{���6W�6�)�z���7��k�H�����@���{bT�M!��=��uR�m�0R)9F�Q��vp����������1��
�K"�h�����;Z�����}y�}�3���k�Drx�d
Y����r-�5v�J��c��T��|��	1���%wic����!�6x���i�x�����d�eN�s^k���EdKIq��������=7�Fn���$�^��b8�v������2$�'�����|�sNE�'
a�({}����<��G���=���P��%���9����jAw78�:��n�3]�cF���/./���;L!��������S����h�*7��Z�lN�Z���2w�>
Z��AZ����:�f?X-TXZ,\���$Z�I`��u	�:�/[�~�>x��U����2�E%{������?���Dn}b���K,��1yW'�����;r�QIk��FD�x�����C:/��L�F���F8��#�������q����{���l)���(�xa���G�6�Q��2r�U���;��������Q@�E����y�f�0V�����s2:4'����T]�@^[�� S���R�t�a��-��A�7bS���2���ir���6����f��V��'���;�N��#���S��fT�}�2&HY����^����4�~E?N��x^u�n����Q�Gs�*�N3gy�&��0d������0��]A��|��C��%qI6�v��JG������k������X9���m���v��������{���w�a)~�c��]6�)���U��=�������Z.�b�D��^�e��O
��Z��`�[w��t~�>�4�n��k�n7j���n�k���2��s�r���]�m�ox�Z
t?��d���q��Y��E{��2���I�����^���E��n�izh���xR��������F�r]�%�����q/�)~�]&C���Z��!�.���lp���I[*/�UFI�T]��&r��5�Q�������e�����������0��
4W/���
���1:L5�l�%
��66�`��e�����.�/��!���NCn��`^c!��c/~pd�G�l�����Q��N
Djo,�&	_P=�-���7����7���kx~�m����>�X�0���:����a+)!(�n'�#��
u�k���VX����f%�dX?�KC��3�(�/�30m���8!cu2��<w�HL|�{[[���M�������^
��H�"o%��)i�3��)lL<Z��9���u��}suP���L����<�e_
����Q���#��y�7��J���0��$��T2�t�F'�qxKK�Y�
�?�f~$�h������LK�M/�]H�=�J�`y������:�f�����<zAt�o� o�����{�Z���{�W��E�����&>A�P[Y���g���p>	�����X�s��J�����y�W�&��F�]� $47��g@�/^8���*W��#���q���W
�����kC��6������cG
b)
������ka��/�����L�$�����n��jb���
��e;c8�0-3!5(?u�p
�y��.����9"�z�tFNK�VtF�4���M�O��qKr
j��h�����������,�N
�a�xqgO�������tc"� cn�Q�N��u�����~b@�����k����S����U������4,q@���B���I���OSJ�"?y?{��G�F+/���L[��������=���=X�=B��0E�}X��oH;��b�������'^����X�
��6[��%�s7�c�^�p�/v?����&�=�G;�=w�t�������f��2���F�]��oX/�M�������m^�MJE�4��{`�$��J��z���d|;�BB�]���p�Kgo�o��^��(��x���V�)�j�mF�����tjlFR1/m�y�:���������Ql�a���:���e��1(O����V`*��~A/�5���C>��b��t��|3��! ��Wo�0�������'���q��G
"X"���p�L�&�}�)��e#5����B�G4@�~,�1�y������
[��Mh��l�	q����o/��lE���S�P��2l }sX^mt"�����g"�S���;/��������2�X���Q�_<]>�K�RN����2�rn���u�g�NP^k�i���Q�?��gT��\���m�S���-�����'������������`6+����R�:I�'Al�fa�D���'����\/4�hL�B3[=����
�c�W���tV)r�"��m����8��Y���ix�
Q�d+��F:����0���~�����Q?Mk�����4M�v�U�CbNCH&�
�y#8��E��A�f��8����vw;M4�So��G�E����7�.�=�0R�av
���l����8�P.�=b
H��-���n�Y?p����J�3��w4!�Y^F��Hp���\���������j��lp~�+�D�v�j���$�����f H������='��8����3��s;�����-xp��jy�����E�c�������i������6�MBp��}�l���!�C	*R����)!$(R�� `��!}�(�}���s/>�JVx�v*�T����K�]�+�
'��[���;�/���@�NP�m��N������#{����/����=���)���Z�:�,�������%M�2�:�B�KGA���Y4�5��� }r�~9�:|<�1�)�� �o*t0Md��Jl�1�O��&� {)��u�e��('W$S�RCW�]k�����^.(�^��ya�Q������F�������&+���0,b�:��L��������m�`�'j��`��LV�r?�������:������p��	t��%8�P+x��g��i)��_���H����N���/�~I��6@6�=]��`p4l��`E/��I��e�<�I�Zj�ydoCw��z����@$��F�;D��\]�>:<9_�{;������H(�*b�G��@w�HQB��������=v(Af`���MI��QdJz�e3L�!cOyd��Q(�����m����.�;P�f�q���TM�~LM���1r����f��z�5h�a%��[�����V�s��V�p�SZ-�_�J�u_V��a�e
�O� �
�D8��c8!o�[�2��K���H����LQK��SVc&��`#vx���M@7}p�O�dYs�p�������O%Bv���i|B�@�H�L�|�+a�@���+'-����f8r
A_�r��j��\��D�*�SgJ�
i����V��yh��������C��i�]�|,t8��7�10��OJ�7'�N= ���,IU�c�c��0����<���'��V��\��6>���������u��`�60=,��N~���q+����BV|�)�r�C���5j�Q����V��N��
�~��.�R^W��a�UFZ�9$����^SY�B���H3������z�����n��l����1��(H�9z�����u��l��+K�i���� ��4O�\��+8R'	@29�}���RQc�x��0@x��,,��������Y��+jZb�
�9�=�@_�uZ�.�:q��Sk:���XP������!�n)�I���{N���xid��Tb��03�j��M��A^������K�&(&f."K�t[����z�<v8��xyRL�Pj��L�O�A	%��H:�k���X����?M0vq���t�A�F	�`���#~)jq�y�����(������R��Jjz�H(a8�45�0�
���o��]��$m)�I�V���*&�?�L�z�����h� �G(���nQ�tv�,��w����J0�lSbIz� y��J�i��s��Hp/������Z�o�����o�Kc��Y�+[1:
h��(����Bd5#2kcH���~@�8\L�>�|a�7y�977zeEe�q�7�F�'xx��h����^�W��W�h~4K�#�+�o2M���9��k�W��+�����/?��;|����������L����,����Q��S�B��fRY��k$R��s�4P�1�)�q~��)U�^���	����K���nH��-�[������5�8�&�s���l�si��7(
SC���V~/X��������vPwu��q�}��&q�}��
�[���=a����P.��G�����[u�������/����ou��-����l��v�l-����JsK�E??��g'B:��Q�)�����%'h���������if!t��8@�^8[V����6g::�dx���[,����~-&�(t|/��	�R,�:-���:��{�xW!��u:NSE����\6�����p6���{����y/�s�>���t?C?�gw��>�����#�����l����D����Q(s��Z!�@���0���F��9��Ds�����^�[o4#����/�
����]��S�^~��D5�M8vm�xN,K�=[@
]���.��� hF���p��Y!n�d�!04�����Y�'�D����oA����Y	�h�k�DU��0���uuV������
����!�	fKs`�C��/�HW<;>;�������P=��ge7�o2(t)�M�kI�4��2��jO���-�}&�'���I����%�:x����2����������~�0G��o����������$RJ��TG�����H�����{��^D_��
�]�\o�,��3�������N��[�u~�����$_C9�����y"j�<�.J'�{��Zl�a�~��;��z��"7��������������b08�J�/f9�<;����gA��2���������%5�P���������#J��)S��yk��e�q9��^6�����|�#�;�6:��=����9\4v�1��.�c'E������B��G�CB�O�B���1^�EMy�@��4[��oV~��y��>B�7D�L�CU�J<���U����q�,�^���s&>�dbZ�n����VG_!�|��x���0�����BH>,::���2!q�B�1�;���wm#X�=�\N"��( {+V���]xW��������k�L.�>�j��~L��R�Qd�����+_��xF[qj������r����k;sA��z������x����\��o/�o��\��������w��!
��&m;+Bl�5�Jpt-���R|w\�����$��pR,�&�J������d��{�������K���Q��� �T�T�����Fg*���6�?8�1y��!�d?=�J�c/��'�����&�t�x��G�/�@�d��:y�I���$��|q�AkIv����C?H���g��Y��
|8������!����0��X����0Y\a����4]{^�Sz�l�J1k���Q���1OU�X>������1dq^����$^���^��7j�^_��m�
�QeuTkq�B����:|!Y�nf�R��ko����n��f@- �}���,�
�����R�sx���>��	�
�������)|�'������d�.�/]���� 1X�(����6��Z������z��f�Dg(+O#��A�
.[�?�Hp�v������U`�9�?5k�J�C���ET0����C�
�V�D�������KX���R��duQ����*d����p<�5z�z����Fn�q�����^�j1s����FX���Y
�����q
5l�#}�����	�'��N�L�l�%w��!��;^ha��s�t8a����skT!v����\��[zy1\]]^��+m-�hi6���>���������������(����Z�d��������B�����mA�x������������J�Q�&��b�>~�eN��x��6���`A��4��
��K���sMM�nY����=�(�	���ZcE8��-�;�|��!b�Op��$���b�Ww�Ww���/e���q3,A�O:�����W��x>
g���s��3+��j�N��t��(pK���-����vbnC:�	�c�;o��a�Q+y�|Z��_�m
P��Q�]�;���������b������x��q�eT#���l2�}����'oV�����Bl���Ns_����elV7|8&wF�{��x�� ������h�����x�0L����+�������
ADV��L���@B���pqzLp�����~����������������������%����=����������M�f��vJ�����_4�B����S/��Ge���D%���N���������K]�������7�`�hhH����~�����l�K�xC�(�w<�3����)����������P��zN�!$��������+o,;LE���3������Ll�W��wW��e�2����.�uQ,���&��� ZT]by7\��������Xcc�mi������/������}.k�������S�w D����4-\�(�lD4i}^������A��Y��_�H�j.� &���4�g����#��\��M2�����T�F;��[x����6Q:�6Zl���,�q��.o�&�@U�Q|�C�
>
��[�3s��:>�g��Yy�����:{Y�?�D��!������aV����SUY<!ZU�I�O�����R�`�{�I�D��J��N�� kA8���H3��.,@����
MG�]�bdu�&�!�s��?���/�)8t<U���\����E����J�Z��#�����p>n�Kf���aA
o���������H�"<���a�����q?t\�6�����.���l+C�h��-f���*�<�of2��C"���NJ�����)�m��&�<�|�B	����B%r��%���"��YL�q���:�3����[����?�������9�i%�C��w]�7���Z-���z7jx�
ez[+��$u$k�t�FCn��p"��������<Q������|D�*z����(���[��YY�}�
��(��������h+j�����a�j}�d�T���I�
��f<Qgar��?ms�~MQW>�8H�K�1���j�B�Wh5�x�w�7o�O���P���L@C�g�i8��*!>.�So�X������`�C��[B��%y�.���O	�wb���0o����>B���f��kV�
�W���l��86�r��G�x?�V�~��(�����Z�����C8b�8L0QOAz�4A��-�Q^�A�����2%�(/��+������>�|J{�������y��%��d����-
Fb�S*������Q�bA����z�v@k��������tm(��[$A���nUU�i�
�
�N�g����2�x�w��*�Z�o�c�,v$��I1^��+df������`"8�pq'&
@��<��A�`-$R4���0��g��������/�8	�tnE�y�v��Q����Z��=���<�f�.lI���;~������B��O�Xp)0��	�4��nZ.�,[vm����dgp%�(��������k���^�������B+�H:F<;M��36���
�e�]r�~��b+4�JpDI�$AX����S<�h��{��n��~fg�������z[L�f�V����1rG��]8O�m�f���s�����F�T�>'����a��U���E�}��Ld�Q �8�QB�\��$����d������~�/�
'�L�-�tiS������C!:.�E�/n����\A��*���x[�D+�X��+�x��|�����������X�b�����w_�}
��:f"w������g�+���*X��:
�@�J��4�(�{T>���~x�0�q��~P�oBs�J��������W�ldD~�o�]�D��,�Cm���\o��e�_��5�N~
eg�&�l�����b�3r�aV�L��7����6�~+����sk�v�l�z~�m[N���J���Rtt���t^�����;_*�G�n����{wQ�-
J��K�I���0�Pc��fz��s��[I�a<�2M9+���I����B��,��CZ��	��g�y��h�pe�-n`�;Y�(�T�_V��f���'pac��V!��L�W�4����6�jvi�.��	�bW�P��P6�u4���������@&��f��J����������� ��D��L���������<wL:���V��`�sK,JF�E����j~���u���'kfb��^=�fDS
��0���D7,��~5�x-��*�g�(�%d	��<z}����[o��A��-9���*�%T)�]�x(�Ka���)vEy^�NY����U��v���2�X-������l�f�g��Iy����$M�`���(�MfjPvj��Cg�I�a��<�U(
�
&�����z�ZFD�Y5��s
��=��*�i����A�bG���#]D�t��+���DE\[�G}Ym7��I)J8`�Q�F���iDmP�Q�u�^5�!�*�����,�.
q�"��YC9��O�����-�`�L����=������1���@�r6����{Y0~���K|�q8��[��_��
��_y��S���nW�Z:�j�&��W�!���0�-��<y�"X�q���#�`���&���f�ea�Rx�����4#�Y������p���UW��_�E%8���D����J������8tz�}�x���qF�0�'h��8fL�M���Wb��h��~@�<�4N����h��<��TEz�9���d.
/�(��y����vNNK��;�B|�8:/����6�9ez`�������DzX����c�*���
�$#��.���)*������r^���h,��2r���p��v�Bm���yz_��&m7z��E8TKr47��#������H��/�
O����?�0�����]������gK�QS��x�qv���9�2x�0������H�2�1VV$Vf�%ian�z��~��ak�<��Wx&�J
d����O�(�~HB�T!X@����H+4c���ja7C�I,0��m�]�o������y>;��>����4����Qo{=�����x�Q���'��_��a������`qz?Y�|jP���QT�"�Oz�F}\��!��(Zc����6^�������������_��)��3��=��T�;�m+��5:Q����U�w��������U�P�NDE���h����6Fq��~]d�f2�J�$*�I7(SpJE�,��,Or����R��u���UpTGY�t0��P�.)8W�8i��%���6��+#�F��G��	��w�Y��T������r>�y�����pT��`D9���6����M
]���S�[�5��kb������8�����?���I�b��$S��#�A!�[��N�������j5/��S����7b#�|oS�2n9�R��[_��L�����n���R�����H���X����MP8ReG�LeJVP����.5��5���#+G:)�}'�v�n�6zm���A���N����d�>���`��bK���%'��Q*X��Kn���v������"TFG��v_>5��1E���;&x>�������C�I~���)eL	d����(��P��4���F�(F5o�������~��N�%z�-��5��	0�k6R����[�^A7j6��6�Z���m�tB����m�g�~�|����T!X�i����,���0j�EE�Y�O�xg�(����������o*�x�p��\	���@Ap����7��-.HI���T�_#�������RQ@���b��td(�6��}<��m��qx�hI�7��<@$�;?��[Io*�Y�"J������l5v�����z�yQ���#T+>����X�0)��55A:�gS��J�s�~q8!]]BB�����5�Z}{���>O��������y�<}�>O��������y�<}�>O��������y�<}�>O��������y�<}6��?���9�	
#29Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Thomas Munro (#28)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

In pg_buffercache contrib module, the file pg_buffercache--1.3--1.4.sql is
missing. AFAICS, this file should be added as part of the following commit:
Add SmgrId to smgropen() and BufferTag
<https://github.com/EnterpriseDB/zheap/commit/d2eb13a80cb67d06c8798976c17fd760be0da332&gt;

Otherwise, I'm not able to compile the contrib modules. I've also attached
the patch to fix the same.

On Fri, May 10, 2019 at 11:48 AM Thomas Munro <thomas.munro@gmail.com>
wrote:

On Thu, May 9, 2019 at 6:34 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Patches can be applied on top of undo branch [1] commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

Hello all,

Here is a new patch set which includes all of the patches discussed in
this thread in one go, rebased on today's master. To summarise the
main layers, from the top down we have:

0013: undo-based orphaned file clean-up ($SUBJECT, a demo of
undo technology)
0009-0010: undo processing (execution of undo actions when rolling back)
0008: undo records
0001-0007: undo storage

The main changes to the storage layer since the last time I posted the
full patch stack:

* pg_upgrade support: you can't have any live undo logs (much like 2PC
transactions, we want to be free to change the format), but some work
was required to make sure that all "discarded" undo record pointers
from the old cluster still appear as discarded in the new cluster, as
well as any from the new cluster

* tweaks to various other src/bin tools that are aware of files under
pgdata and were confused by undo segment files

* the fsync of undo log segment files when they're created or recycled
is now handed off to the checkpointer (this was identified as a
performance problem for zheap)

* code tidy-up, removing dead code (undo log rewind, prevlen, prevlog
were no longer needed by patches higher up in the stack), removing
global variables, noisy LOG messages about undo segment files now
reduced to DEBUG1

* new extension contrib/undoinspect, for developer use, showing what
will be undone if you abort:

postgres=# begin;
BEGIN
postgres=# create table t();
CREATE TABLE
postgres=# select * from undoinspect();
urecptr | rmgr | flags | xid |
description

------------------+---------+-------+-----+---------------------------------------------
00000000000032FA | Storage | P,T | 487 | CREATE dbid=12934,
tsid=1663, relfile=16393
(1 row)

One silly detail: I had to change the default max_worker_processes
from 8 to 12, because otherwise a couple of tests run with fewer
parallel workers than they expect, due to undo worker processes using
up slots. There is probably a better solution to that problem.

I put the patches in a tarball here, but they are also available from
https://github.com/EnterpriseDB/zheap/tree/undo.

--
Thomas Munro
https://enterprisedb.com

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Add-missing-file-in-pg_buffercache.patchapplication/octet-stream; name=0001-Add-missing-file-in-pg_buffercache.patchDownload
From 19d8d04ec09a93387d71db2a685aca52938b1ad5 Mon Sep 17 00:00:00 2001
From: Kuntal Ghosh <kuntal.ghosh@enterprisedb.com>
Date: Fri, 10 May 2019 16:11:15 +0530
Subject: [PATCH] Add missing file in pg_buffercache

---
 contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql

diff --git a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
new file mode 100644
index 0000000000..527774063d
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
@@ -0,0 +1,7 @@
+/* contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.4'" to load this file. \quit
+
+GRANT EXECUTE ON FUNCTION pg_buffercache_pages() TO pg_monitor;
+GRANT SELECT ON pg_buffercache TO pg_monitor;
-- 
2.17.1

#30Thomas Munro
thomas.munro@gmail.com
In reply to: Kuntal Ghosh (#29)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, May 10, 2019 at 10:46 PM Kuntal Ghosh
<kuntalghosh.2007@gmail.com> wrote:

In pg_buffercache contrib module, the file pg_buffercache--1.3--1.4.sql is missing. AFAICS, this file should be added as part of the following commit:
Add SmgrId to smgropen() and BufferTag

Otherwise, I'm not able to compile the contrib modules. I've also attached the patch to fix the same.

Oops, thanks Kuntal. Fixed, along with some compiler warnings from
MSVC and GCC. I added a quick tour of this to a README.md visible
here:

https://github.com/EnterpriseDB/zheap/tree/undo

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-2019-05-11.tgzapplication/x-gzip; name=undo-2019-05-11.tgzDownload
���\��i[W�0<_�_q����H �����������L:���T�KU�*����������Ig{�������Y�s��,$�N���lm7����~Z������w��Vg�������������T������oj���L�g�M��2��i2�4���u�� ?����Z����`��'�4�zMl��$�O�'����1`?v��������������l*x����o����[����_��X�
�[�����m�a���oo�����u:��~o?�������$VW�D�wU�����:2u�����M�A���V�����:m�_o�A4Z�'�o���4|�^�QC�[�Mp������/:�j�
]��f���{��{x}��j�l�wP�J@s��z�P}
�{�E��(T�$�a�*i�7jz��a<�T2T��%7�z�<��Rh��I2��[�$Q�mr��I�A8�Ed8����7�������c8�`"U??���j�^����������o�����zk=������o��l�o�j�E]+�Y�|�O���En+\N���A|����G��(����U�5
2`�!��y{��e��������7�^D��j6o��
6
p�+<�G� ���;���p����^_���p�5l�2>�]Iu8��>��W���k��5����?���:>��R/�?��V�f�nv�M�q7M����i4��,�}�K����/�����g��(������?���W���m7��q��V�a�Z�+�w�7�u=�����-t
��������jo��w���F<�r[V:n_�������������k��7���N�����R9��k���U����?��?=��\�\���'�O��N/�U�����Cx�J�{����O�N�O������j���t�������	� ��zwu�}���+��{tq���[�yr����
>�p�^���g��h����p����p�kQ������\a8�`��Yo��a�n�������E��Y_�X_w�r��ndi����(��|chBM�h�������0�@s1x���r(*=���v��������`i�-�a 
���'(�7"`| ��Q�p�U�O��TE���Y�Tj�*
��N��OQ��.O�O����?��m��e�z������Q�/��V����h��I]��Y�w�4���iLU�W)��dF8����KQ?�<\�Q�A<P���� �z.�<����������W�kHPO�U��^�D%���<���	a���������a2�M?����M��p�d������y2��`�1\����w��W�����5�I?I���+�L��]���9y�P��=��������u���/O����������3uu����9�C��������Y�P��0��r���4�L:��B�g���'M����G����TX3�v����W�x��$~��}�n�or�bc�,�����P��O��a��~����O^�;�V��|����i��������!`�?���vurv��{x}y��[���%��1�?���]��=�h�����5�i������������*�M��@����BG���my��P���
�^�������Wg'y�}��y!��qY��w�A8��=��H.{<#������Ay��A�IM9<EN�2�DCM�h��|l3{��hb�}��8����}�xC��WA���!EZd��4�S��H�:��L�x��M��dAO[
u�hF1��8��5��'G�(���Q`��x�u�/��Di��|��	/=[�}��@.<���@������_2N(�����#�	�|0�rO�Q��(�=�MA��@f
�C/�xJ�;o�==:�gwO�__t��^q�}������",����o����(��V�1~����'���{qz����j�@hA��mpz~��YiS�w0�e�;Mk������]@������[|��!'b�����#�����q/IF�Z8�L����p:K��pz�z�_����U5����7EDD�	~=���c���l���<����C������M�����&���,L������kXx����m�U��]y
( &����3����$��+u���u6�i}�h��S��(�#��=���l��\�H�����v�o�a��DrT1
�, ��Vz��Q�1�LlS��m�U�V��������P���,beu�b�f������Wb�Ma>v������������R����Q/�����k�gx.���r��io����������������G����P#Jt@E��������j+a�����������k���]�t�/��W������>9^��
Z����'Y8m~
F34��5�� ���y?��H/��(X����*�N�`�q��p�f��������������_|
c�7�q#�)��@�!���D��?����$q���TY�+�[���
.�H��V5��8�yN���]~���+|�t�
�T�����Ku��oO)�@�������I>�x 
��J���#
��u4�$���o��]�lL�O�����Ww��{��x�����G���nu��de������K��8&���/
�:���������3(�y����jdY��ZY���J����t�h�L���X���{6�����x�HUS`Qbp����h���q��,�<�x�<�A���w���?6�����w���>J~�8	�"!��,& �M5������c|1
���-�����J`���0��l��lTnfL�p��|5��B�_�L���/�fi�=��`}��7�`���|"/eS���~�Pqd��3\��[�l���arD��	,;���F����e8
���GK�py���G��� ����������+/"��;$�6�l��	0R�c��GK������pU����(�i������n�%�^��
�sC��@G�����
�_�3p���^�F_�p#/���S;ZC�iB��p.����=���=wo4,6��m[y��x���W"�f�@E�kevH��E��a���p�B�FA^�F�������{yrtqy|���Jm���
��(m���/~�'�"�r���A
_�������;K'H�)|��u�	���!��l��|1Ke�jYRc<������'�vt�(�J7��;����q�g�u
(��Jw��y�-����t����r|�^�Q�=L��
�������������]�xX
t�L��O0*G,�:�8��*���������`�z����7�����4^=Q/��WO�����]?|��4���F��<<�:$y��#]�H��#M�%����~�^?<���S�5�M��;��~�.��g-cobw�k=x,l���D�e�J����7�I��$��������m�*l���MfS�sE�b
�w5�i(����eO�izc�D=��K�/���~���B����^y��
(����u�"�/����z�`g���������G��m���:]�
k\b�����p/�3�����Y}M��]R�����[�6���[������-���a
�K�T@V����tT���^5����������sE���(*��)��&�vgiw���_���N;�����i������C�_���`���!a�?������f������mn�{��n������_�/�Z/�v������@S������@�^�@��]��j��kB9��6�e���0'��B2���x�;V�(�f�����b��`�5���+�Y�)�;�Qk�_�J|�f����WZ��O��x���������W�������\�9#-\`oa�C����n����:��V�����=g���1�Z�	T{s��0C��i�����f�����&I�h(�oK��,Lf�&	�\��	>%q�U����wM~W
�/�����i�������Li*]��_��3��j��9�U�y��V�������{��������no3��������&xH��z������
Q�Ig�{4�������Y�������AD�i5�����O���Qo�j{
�j�7�����vr~|E�y�S�\�P�?vK{M*�Z��K�A�m�����rJ�~���o��Z���������`��.uJ=�=��o���n�e��#T_��:����m���,S����S����~_�p��}
�Q��b�Y��q�t�Z�+�n���8q��25I�!�k���������+���C�R�v�����������@���
�~���t1
`�X3[v���c����K����G#�@a�90_�:��3X�"���k�M�O� l��w��!�G�O3�M:�Eb6����u�v�[������������C��v���6[����pg;������p;�n�;�`����E��q4�&�o�q�����G���(��n���o���f�og
`c,�~��{��Tk���"��#>P@TY%�(��� A �B��z�H.
&A�7�'{n�����w��h���J���E$��x
%=��������Fp��h��F#�X���2Gn�E�L����&�:q���df}��-	�i�<�
�K�)oH�����w^��dZ8�,3@��a��
S6c�f3`A'�v����H��� �HF����	|�&7	�����J1��f�����N=�a�d$����S�(��q��`0 [x��,��/���*��I��1:B�d&�}Y��u� �\*�pJL��{'p���h&�Rx�.���`9��Z�-�D��
��$+\�e0�u@w[�?E�]���p"�(��������Fu*�U���~
4P�8"��n8��1�3�^���t�������[w��$��^-�lF�������ys�����I�z�_;[��y����z'��z����`��>�'mx����������o�HF4�bQ�r��*CdT� �~wyrx��d��I�;�;�<������;���6��d��#�P��2���L�����%���X���\���g�m���;�V�����@��B\���~^�l�E������:p����s��p�V?���s��R��-l���~(x���>�j�{��p��W-B�vV�J�����#�/
{:�����*����8�|���71�FBe�F%fc�g�x�Z9'��J��"��Q�
j�q��*�8��X
�����l;�V��t����
���;��0��b�]����^���1:	Dqz��V.�h/�'\QC�"�T_���`;
$� <�|����Q����k>;�����,?����R��Z}�=�a��}W�����^u��@L��t�����8I���bf�R��\�56��
L7`q	h3��
��'�4x�&���sR�/8�&������F���=�[_CW7�Sn�7����A�������-�Y_���0W5�zE��:#f�����	�5,����A�����B�E���|-���%T���6��v�$�<s9���3L�bb�-�
��L�������$^�;<9�v���=�(�p��N`�G�A�'
p�F�'"��j�k���p�$����khnG�'�i3������~��7}�����[�T�\_��L�Kb���3�Q��3B�X�yg������3�J�����N��0��q�����4(�	>����i<5��������F���$ `z�/
�F\�@��&��`�dD|�{ff��-E'=��Ty,��tq:�z�������7�]�|�/^��rt
�8�`��N������Yj�u���������w����4�R
�5�$�J���� Z�]y�<��=�BO�e���4����?�4}�)Rg�'��a"�G�x�T|_���4�u����a��9��'���ywRY)c>�����D
�iHg��$0T���$�H53�V�|(���4z���y���0[�%`A�FnG�Q?o4�G=���S���$Y!(�)�qEU��r
"�����:K��Y�}U���o6�x��N�H8�0�f)��:�I|e��<#l�V�1l�]!��@�S�	�S#7�Y�������A�[�U�%3�[����������Z�l~e�p'+p��[/b�>Eh����&pouN�'���+{]����U�������X����I�����]7S)Y�;��F>����EWz�����4���!� ��j�c
�B�1�KF;v?W�<�[@u�c����^�+����t�'���|�dB���-��A��6���z7V�1��=����tn�&e�8_#E(���0 ����)�������\.�Gk������An�?� �z �_K����L�����B�I%�.��
�u������Gq�]�-��_�{����J�mP�C�2��o���X��.�q(�	���pt,�uuGwd�F��D�S8Z�uV���&������Rtk��~J@��S����%'a��eq��\��L,Pgo��Q���t�q�|���7
�+������l?o��(�.��*�x�
o��l��M��>:M	W����7v�1�y�w���,�W��-���T`S/�p��\`��pr�H��*�>e�/{��V��� ����t�P�<}��'�m�7���J(�Lurd��*:�":�]WE����(�x���+}������<n���x:	�;�����qU�&�M6N��j���i��q��c�3����&/���PGC'jKB�&	�h��9$���U��E�@��wAG"��~\����v�W�2X����#�s�xN�=]1����d���Z?��m>�,�Ec4������S$++
��d������(��Oc�~��������� �A7�%���z`i�A�@Q��t ����U���DB��{G��b��������:�"��\��=����x�+��W9�5WQ��c���'��|p��E8�G��[�=I�6�6�sb��i����(�w�������Fe�Q��J��������IJ|,]K-����C�r!�T�B�}�����_� 	3�����D,��	(�� �Kns&�-|��#y�5�=�#ww"J����	��R��O�����(L�F������XCU���S��k���W����9fX�Y)
J�Ik��������8����C������+��|\M�\WsG5n^�{�-�	��25xq��iM#�k1���/��+L�,6���T�p���m�����Hv�DC�d��N`�_���_\�9<�m4���
�������0���pK�A(J����_>G��Kae���Vl�����t�@��'�W�75�/�m��A�Q
����#0����(�
����
9y�PF.z�����Sl���`0X�7�$��
#��=)�H���?��Q��9Z+�)��3�������-�(�B����QW�1BC���
1|�Qv[c
��	�t��q�d	��Ga���� �7TZ-���c�~�;[�T��� �!�mb�{D%C��Tr�����nD���3<�5��A9�q*�(:�%c@��T"1]x~���x�tdID+i� �]"�����
�������n��N�"yv`�!+c��_O���Y:�;_���
���"��D�,�A��|��+N[�����������������eyc���hxa�x��Ux%�nC	M��UT��cY���`��Q��^�N�8@�������T�D�v����?<�lv��t���HQ���1�<��'�����2i�Xd�T��
_T���j�Vi�[���H���+�����A#�t2�M2=�:?34�O�{ZL
��w��A���^�:4��������w#��C-��q��;�.9��G� M�����j8��n[�o�:;����f���=�M0|�d�yx��`��}B��=e��� ���~�f/��l���(�"��G^�q�N��'�>H��y���yGi2�����qG���[�?.E�e����������bV�����A���*;��kt���l�$Pg��D8��g |A*���5�f{"�pE���ss�i�
?�G�,��n'V��N{�Yh��xD�5<�N5Hv�
�3�UmaG�s��!b@�4�9�S��G�	IS���������bffY8��8�������:���0�uryA����Y���y��7��(	�8�U��Hs$�Q3�8z%����\�	�@#��`Lq����Y���E�A���.�fS�OBm��&I2��,r�_���<� ���M�UB"?����!��9�:�[������{yr|A*��m��!���9&"����hWc#?�3��k���5�p����%0g��k�+|�bYf���C��5�T�V��o��@�C�8�>��a��nn����&��M����SQ�pF��6
2�Q��MEn��+�:z|���T*P�n=�9�L��#@W�p9�=qN4���d5�S�������a�x���>%�])�5I����p����E0u����[��N�IX�l�PL���9�����h�T?����b���p4xu�&<�L������	W��3�`������f���J�s�������|8:{wu���UO����&���8�De��?�o���>��TJ�mrO"�WS~gb����^')"B]+8�RA^g���|����bE�FC����R����<�������Q
2�K|������A�6\)�5Bq�b���9���{�Zq�L�A��}=�)�W��Y�9�K�Oy�&k��_�3�R��*�O�g
����"�ipzuq���� �J�����X�J��Y7���L�^1Joa�bT,��I�=�X����ZS���/{0��X3��qYj�����JH������|{������	������6(�����X�Y$jw��Z�{C-��"�?A����;Nd��/a�����;k:j��Ri���KV��cH/-iC����3v<A����{we:�����N�Bl>[�*��9���UO��Y<%%���:��J�0L��vc�~t3a�9d���a�`�cQL%�)�r'���	�1����h���������fT9�])x�T7�h��3��=U�Rt�{�
�����^G�<��>�sY��h
k��F+�1�8�+Qf���KI[l*Q`��.��"0T�N<�����d&�>�����0�$�Eo�]�v$��+���8@:n>�x�0�����}AO��:�F�-�QN��gCbrL��l�����|6�
�)L�����G���	�\��M~���J����j��k��Y^�^���^���NAv�����W��"�:������������zR�5���8���d�o�p|s
�;E�>��!\@��,���������E�|�M��f���rb�%?���e��'S�C��#��m��h<[\/Qj�W@G�Q ��"An\��+[D �| �������3��T[=	�r���0��(����k5>�qL��L
SYY-#+F���������q����J2���"i��2N2��*
���U�u?��>��F�
W���e,
����6�#���$9�P�������D���s_EC0G��D��8��MCo+w�%�����iW�^�>���F1�Y2"�-�'�},}�w��n��z,�^�4�3�>a�3�
g��c�S��ZI� ����Y�
�-'���<��(����wt��b;|Y� ���K��N>cr�����Q#�Bz������%��	o�&�����b���G��y@6���/�+D��%Wc���8O�������o����E����Rp�����z��1_�U+�F�=�S������-R���w,��*7a��
=`���b��@�������Z���l�W���5&�a���=��9c��n�6��-Ul�P5c��Y��;l$Cq�|!��6l1���%��Lt�4i�o���;caT�b8�*��8����8���F|4J>RM%�Zb�76���Qp(9�KO���e���_mf�}���c@��1�D�hH�V�b��P+���;\�E|�-���]��e�?��J�����a���(��8��,4I*�Rw
dz\v��a�n�#����se�5�b��%R���]��L��R�4a�(TH���'�0b��<�)R�������%�;��}z��i�j����(�0@ \0W���G?,��*���`w0�9P�<F��AwR�O�	�l6v��u���>?OFQ?�RX�>�2$�L������Y9�WU�������>�[<}��o%s�$w�c0�����Ln�3�l��~��������N#p'�j�g@!�YWG��
��d^��^�42i��`��R6���
�j&��IR1�	���
n��KD�����v����sy�;d��W�dJ�SV�/����V!�u�9��Uc������01�?���*���S�`��H��)2��s�So(��U����3p!���������y�FS��e)��%C:������2�	n�e},������8q�*�M�Z��Xj)�����"y�.�����'�}��,7�*o,*B��gQ�~o�
���&@Q/WX���~b�#�������Xf��.p?{�9��r?�V�H�#�|�i_��&��eW���A���c�"�X����iX�W�'��
�J�������%�p��f�;�oVK����a&���L�Gk�D�8��g����wtM���T�.�b�����t����M+c.~B����MdC�I�������9���
�=�B���_�����~�NB��v��<4��v5���P�+[�?'x/�
eo'~�C�����n�����m��qE�)����,��,g�pr��F����g���������3��h�<�#5s7�T��a�q�~dc�}WMBG$�S�R�/a��ISw�$�+��06��	/4h8�E�K���`��c\���dr�������a(�S�=D��L�����V!��*�������N��?�*��#����8"�����(vE��=�.�&\@
m$��]�=e��h��%����c���|hx���ZS��#:�D<��qo����2GE�����#P��������Cq9��Qx��>+����e�H\�Am!�HM7��Q����v����.eE���I;T}�?��n�O��eA��A�A��A(��B1K��q(eAF!o�4�/�O$�{a�T��e�P�
t�K�}�I�G3Mzosa�|n(3}���K����@2�
�]H�������]s7���
��jqE+,"��72��~�K+�
v�8����(N�|�s d�~?d/���Tr��X����r�|Lx��AG�cAl�`a�����6���!f^�����"��� !_&.��W�q�1�r�uH�Ht�F:�z���c�!��B��K(`�fi���
u��Bf���3[�����G�����&�YD��6N��i�$6�`@y���6�B�3�:0��^����@=KwJZf��$xA}*�0����w������r��*���o-������b�"�������U5��i�X}��o�o��Fr�����8�e���w�h�zi-e��`�T,�fv}t�9��j�NR��	:V�_�����1�%ZT�;�Fb�{�j�~lD���t�6	����'E�#�B�A8x����Sb��G�������4���2�;�DF�/���d!�-)��W�s��l����[F<��x#�;XR']���S���?87M���[_��9�vy�Z��Q���y��W��?:������g��7��j��h�G��8R�e������N=�,Gp�*)'��%���X2��3,�
)��-1V27�p�8�/�p~�����(�,�e�Ds�
c�����n��M.��S^4�B�#S	�6�=' #����?����=�f���usF3�����N&
��B_MM�v>�����A�������j����{�	����r��7�1G}�Pg���d��s
�W��R��0
_�Xlnu��\��Xgo*��h�{�C��}�(O\��� x^��t�S�A����|M�����<�G�B�4 �G}A1��%~b������K��(}�����
!�C��QE6T�?���]pr�h��`���gs�P���XR2���E���2��3I� �1�9���3�m��7�K�*�e'�V�,�r{�(�o��3�nl@7#����e����l�*��:\�]�v�^�����������d����=L������O�!���X7Y���hpP�����k��#����64!s�f4[R	�r�U�/����n�-�����ou����$_�������#��6I�
0g?����yRQ"�a%b��NpUjr���]����6b���O�:�S�".)`s4S)&]�	k2��U�z�����f�ZU/�
�T93=I�B���X��"y��bT���5�%6KZ���l��W��$���^��^�1�dI�g��V-��E?����}�od��}��N�2�i�EW��<!��y��g��L�J�
v:�v&�Q�@��]��
�HnC���+�c���&�M�ff�@��H��=��8�D3��J������kF9QeB:������b��0������3N.��s���b�W�GW
��*����4�~��k,Z�3)x������.�W���vNG���9
����=��bk�'l��]���gQ���<\�A8�[VE���<X�a��B�a���a�D��^���	�d��;T�����.KV�����3-����nc�Do�T��g�<��j�������}�e�7:��+h�e�G.����J+�k:5
0n�@u��<�]�C�� Y�/��_Y5^?���h��)��(�������lF��F�	D����3���G�I�-���t������N�L�]LU��,
$"J�p���i 	A�}�� 5}�L��{��s�\��������"�+f�d
l����r��I�G��2���M�r����mRUO����3SY���N����txH���4���v
(�HZ��	������EM
� CrO��:�Qi�b�H���S��$5����X����?!�zx��T@���Duw�}fO���s�������7Z��L&��x�i�C�%N�!"���	�{��I��b2r�Sri�nt���$�|rG)!���An���Yq..(X�K�'>8Y����D�zfK�*�
D�`��M�k��T�,t� �4�C=��b��j�O���uO��g��V
r-����o��f����K4�a���Zd[<���t��?�und�	>B���D9�F��k=�L~����$�h��x_�,��L��m��Z^s��S�iN������R�0d�I<*��u�t����q\,o����3O��,��kn���=�m�1��c��!������A��D��r\��
Ry^������n�{�(BP��*�E��c�^s�j�-f�0�?yUSf��<���5�^x������
��L+�;e;|�RR�
���������KSUN��\b�)rw�\�V��<[}�G���H�KJ��<��4���E����maR�z�.�{)�J������JT3�!f����n����g�^�����8�����/���~q�k�S��q���J�0{���o"��uO���Fn�������yT�^w�����e�7��c�6���k�^dS�����������o�����A����������Zm%�c�����/�.��M�4��I���p��u��%�O��Ma����\�BSTT7�SY��#7�Q)����17�2H��v����>���T����t,�����T������h�!\'��Fkk����Z��?��z�FfM,lH=3<wwm�f������7�m ~ )	\��D$���vb���R���S���z�s����C�����p�����N����.�D��|�F�)�!��lOG
�����u-��n4e>��ap�$�RQ�#���`L������iw�J�m1�]�8Rq�����k�,O�.9����ZbY�|t���fQZg9�R�N���h��T���\:���6���eUm���+�8�~��0��~�����Z��9)W����z�
�B�F��p��s�a��$�C��g����?n`W�tyN��T��&_�i:K�����;�v�KIu���<����Vg�]��+O��_2o!���]�M�i0�2�t�v��+_{��a���,
J*�=�����jp���>�H�PH|#?X]
b�S>���U�����8��h0f��7l��L�T�99�����_��a�We��H��)+�3��5X�yB��u��!?{���v���du4�l�r�lM.,�DJ����:����*����<5���0�C}��6�B�;~�vO�RU)�T9�2}I��yi�Y'����bE��1_�n����}������cU3������T)e<�+_�������	�������K��(o7�<W/g�������z)o��x='>��t$A#V���z�����oj��x�S���`�b�-�����u7.�n�r
F�>L�[S����"���7�L������?z��e��Z�R�x��8��\�\�?<��e_�?�R6_��6���=7{�����%/�����X9��F';�1�f�Q��T�t��N�.���O�3������#N�d�_��P�wQI%H���z��J�����{R����F�YZ�$��plRUp*����2�}B6Wdi1K����*u][G���%/oek�g#
g].�Ka�!���@H�&mYIZr�����I���o�w-�DSc��9�����1R��.�������J_�J�c���)n�2���T��L0������<'��RgO+�q%��Y�����j������\�g��v�P����G	��������
e�v�#����� da�QmYUq�\c�R|���^<?z# �#�.��w��g
�0����b��%nQ��l���Vc�v��l��f�)��O~��ryumsB&�����4���I N��^�\+�.O��Q��Q>O[���j4?f�'�u�K�T�]�C�@���+Y�+]�����x�];�9��1�t��P2]pf�����q�
�f���E�&�V�Dx��O�*YFex����t<K)�`,<��F= :&^V �h��L�&�	1��o^��q{ks��/U���s�bd$���MC--�;w�p0�B��I���S"����%����V��AeS���S�U4���&���x*yc���,��n$[��Y����Q���p����0�����,:QJ��O'����#�RR�����U^�&F��41��1���F��A�W�����;��:����69��N}���9���(�Fr��DE����+����;�W29��U[�U��F���u��ou�e���G����b/D���^��-��i ����o�
�[��H�����f���adK	���{�E��F�k���d���Q�> !�������'�
�X����/����W�^w�vr��{|x}�=|}}r�=z�~�������=��O^TB���Z��	����p�F�vsj��2��P�F����:�������;eI��]�+-�J��X����z	 �$���"�\��@v��0��a�g=��UC�h/�v_:7o��*A\��f6k+5Y\��G���Uh���C��A�(��
1����hP�$c�M����-ZZ���P�+�a����.9��I����a��D9LDk\ak��L���
bV�������Dt=*���lE��;qlP�o]����s����N��}���w%-��4��E��R�����N����o�)��6���r
GC�j,�"}
�#��d����(F��4��q~�<7�����#K���\'s�_�h�e���p���rW��5f]/qP��bM�b��O���2
��<"?aII"�_.�DEr	M}��)�&�(���@���sw3-h��|��y�����9�
�']�9�������m�e��b���{��@���&)�@��&Yp�����p�x �T��������"Cx�yK��� ���C4U���&���;^q�.�%����_lD�M����3�O��M,��Y�G��)� {q�k�wp���`(�8M{���|:�)���`�z��#J����H��I�����d�
]�Q��B�����&�g�/��x�tE��	D��F�M��r�/�����)��2O�@��"�<[�f����)t�c�1�9��h�T;��=^����(��K��)(;c|��4��s����wgzP(7��R�\���������-�v�o���g��KJw��R+����NW;�/l�Y�r�8��V��X^<2	��U��}�����x����s����H�~�`����iD5�7�#7�����V��7�>*7p,��,��zK5���r�l>n�Z;[[uV�����n����w�N��l��A�i#��?����������j��Z�������W�y�/�cs0�c������HN�J��~u�����I��nG��>%�����j���4;��~Zd��4���3��(��>?��dB&�o�d6Y�����q�
���j`5Gxr�Y!�4�3��6~���@��*"�w��Lx������L(������[g���>�o���.�A��b�
�X ML�y�
��J���OB�
�8���@U��k�M��
�a�YeK�#�g�?��	��F�)�25�p�m���Y�MU[���W���Qx��I!��O	������H������b:J���s��G��4���>��H��������3��������W�]�`OgkIC>��O��Pk%�J�Tm���!�c+�v����)mA_�P�+�,?�W:�ma{{y����Y
���\���I:�K����?�.�m�����[�;�m���&N*o`�2Y������O%���3f��c���r@[j�z�V��n���~QQ��)���j���t�p��>�_h�Kj=�`�*
t�'��)Y�����N�������#m���b~�'h�
P����hs�>��������%ad!_�w'��'�^[�-��)8��&Es�eW�����Za����s����t������������9�	_��$s���+��CO�|R��T���7�G������N�H���`��x����Q����?����g�j�9���W��z������:�DB�*?1���������u���cW�#���!�}��gWRE�q�,��e3�~�y;����?+;�!��p�`��w3�������������-����L*��o�������.�3�{�Y���l59t�-a%���Q�|I��\��A������g�#��a7��M�V�Q����OI��b����FLZO����!&)�
 a���,�
�)))�$G��8DY��R����1[e�-yS:W�!(�{�l�<NU�����D/�5��j�E���E�|
7�p%���t��k3U�a��rk�3!/D�O���[]^V6�zZ ��%�Bhk��V~�$g��������"i�g�R�V[�9�\zc��i2���G����/�n������
:���X�u�l �lx8����(�z�����������i��"���E�=�����������;�\M�r�����i{�����:��Fe����3
?/�e���sj��ss�~���+���^�w�A�:��9��?�i
����/q��{�	�l�cOOz�{�oK��N������$�K)�	D������6ZY:uN�D{��@��"�S������������}����9���KM�:<#�>���V��98#�����>;9����G���G��%N���D~w%"��k�YK�1�������
<O��K�:N3/lk��h��I�����>N������QX��/�����1��:hK��s������m�]r���]�t�8�����*'a����3���A�g�t9�
��(/�<G�Q��$O�.�����VHP�b�c�u��O^]i+�:;(��B�C6���@UB������v������[�m�?�����h�+�j���Y.������l������20���$�|Q�(����!@�@����eNB���\���NNu�q.��}���.��������A;9a�g������8����~�}w%��zAV�yR�C�(UJ��D��d���2:=2nQ:���BN��G����F�H':��i���x�/{���;�C�3������3�?� )�A?3>��t�&= 3�����I=��������\G�`�#����
�\�s�')e��U��o����o��%�Y���������KC���)��d=������(��X'�.o|��4�n�Lm1�eb������c������r�};��3�a�&����DD�
�ga��������:�nq�	S���:�8'3�J<���l���������	��3���z�����xe����P�d���� �L���	��!z]�F�ggj8�gkhc��\\<z������>��������A�O�#�!q�������w�N�JI �V���k����OC�-����R+��+�h���
�7R%�!����H%VY������F9t:k�m�������?�_q����Z�-3��66��x�Z+����pB�)��M�n�����RygSv��Fz-^p��m"�gT��DHn�B�~}m5eF����0������8��]�;v�&���I�;��%�����q�T&�B������%�q��$w�mM�
�i�hz������.�G�����9.k��3��t�Di�������y
��9;�������J�����U�ee����r�^��=�|�*@����D3�=��<��h-�
3���3�[y
|����o�k8�����e�;���2��
7-��K�o�/���+j���t�I���^j��m����5�8�����r����	,�*�������1Vr�]�~���P����X�_^���	��JN���;>�j�_���v����e��)��i�8%U+�9��)�S�v�$��a)����S+��2��
�����|_��TKA�rU��+����T�Y�A���M5�����`���n9���t�=<-Q�P�pXz���Q�
���)@�
F�6�p�
]����8W�&��
���9[d;�
����R����������������u�xysF�ElV67a�~�����0r'����\�����A�0^��oF���'���x�����oF�3WM�/���#�<*^�j!��?7|��C?��
����&������M������wM^#� F���D�?�[*c^y���bK���W��l�V�*
D����������e'c7�d*6��04�$@Q^��
�?�<0��~����W�p-v�]������E?����Q��y�&wj<c�>����lz��R�������\���~��;����E�<���5���3����!�����Ms.y���DRj�&���B�R�}��Q���]�%,��lj>���8��[ uN~�3���=�NT�eS������g���k�����=�t��_���������?����%69���[�����Ps����)]oP�s
�_��O_�����Y�$/W����v����e�Y0&m��C��dT�A�t;�0
G.�
�UUE��*a��yT�D �oO��E�Bh?���+���?����mKV,���r2'����s�{4�O�,�a�dQ�E��cI��E�[|I�_���s���1^����U�%�����N���z����~������[:�o��\0�����	����m��5��.��U_
�a������/���F^������
_����yu�I�7�(x�j��
*{p��=8��������bMQhz�� �`�z8�@OP�J�8b���������=|�\��'L�����q��/���
��7:��x����k���BhE�}W��m�tB�����?��w��2!��q�\����/�����sQciF�\�l�����6X�}�-����C�!)�O��N.������?�HD=Jn��Z��/a�|�k'�u�MQ�G���
Q\{+��lH���dT�P+����H��B$��)�������S!�
�y��Ky����������az�e]��W�����>����g\�6�������_��<�yg��Tz �V}q�o����`{��}toWw$�ZU���|J���r����&8�R��o�!N�u��}����:N�
b�d��$�iX�Bb�)�6f�����������r��eOX���X�����Q����F}��~�J���'\~	V�:����qs>+~�8��!"�"x"�R?�Ub�{�J�;\�>��0y��nu�s��-x����7X<�nH�'�N<�+*���qNh��`q����������N�g����
�}���J4}��6���k�b`a�>z}���Y��aA4��R
��_P�����A�h���f,���1o�W���E�Q��#�Z
Q$3X�]���-������@�����4-+��(�i�a'eg��w��B���� �h`��dt.�2{_��L���N�Q6�_�+�?+E�Z+����y�N�������9��G3t���$7�p�j�li�JZS(vR�`�_��QJ�P��b�\A{�PG��������/���@�lU�uQ3�g�(D^���~������W����i����#�zS�Y���5c,�1��U�ET�!-M+l`	��%]���c�OM���
�R0�O�"�h"O���"&p�P#(���!:���f����^��a�*h\/
�,��f\p����A�;�%�@��k$�v�$�n#`aJ6
0}L_!'Q�am_�q�����	_�,���I��z��r:A��RNW�>�I2�e�����T��"�����������x��z��k~�vN?c�\�N^����_*����cbd��D
Vq�>]0�5���.A���^�%�Nqb���I�[����s��0a:�_����z_����,����]��t����E��X���<�jy-����<VH���$}��^��G�~�Z���o����e�*����/��.�[�&�"\�c#s����(��-c�FSz�H��xq�l9�b�7��mDFg3���M������@�MM���E����MV�{A�dW����c�^q���Y8v�������w�������c��{�r��Q�������(V/t��g��([�����-�0��)�9%�92yAo�P����i8+��R�`�E&���;�X���hI�
������&�����������L�XNc-�JKR�$q���������T*C����%$�X"VW��'����3�.����/S��0�Wa<��BS��t���X��lkua��Y�XZ�l�n��������h��8�f����5�,Z������}���'G��y��}��B{�L�S%6aQ=��w�!���|[�����
}����D6c���l���3	<F��&����{s��_r�vr~"�t�n�U	���
8n�[�'B!��� �&&���j��b0����DS�<2�����{���I_�g�r�2���N
�5�����,���-.��J����{�_���|{��������������a,t,��0��7r��(��VM�'��c7��`��n�-3��~S=��F���������xV_��tJ�D�����b�6@�8�m
w7�"�U�(�\���p|��C�M�m��k�-_nPW!�q<&�
����2v���C�n���qE�M����� \p�������n�S��	)�E��Xa��F�:��z�U[v���DlB����pyrv�^�������+��k�w�R�8������\u�?���E��%6�v��#����;�H6C�>�3G�j������w�VW����=b��@�N$C�M$�C��[�y,����@�p�9����T���2�`�����pn6
�r@/��vI9�gtbSG%D�HY�%�wEb,���j�� p1M:�M��t���6�H���frV�U��\��D:ss�b(�G���.��O��])����P�z����%d�i�S~��8�ob��4���q��������Y��$�����F�q�9U��D!
�~��
�����<MX�[x*���_.���i.E�)+���$���!�B�iGC�T���;:����6e�|S�fVZk�HY�2�
���q_�4N�AJJ��pBHM��!�zb��D��0����q7��{a�J�8Ev�?KS���h�7���e�EOIM��9�gY����P���P61[+&rm������Q���cwt`���;���1�O<�gd������h�:���2m�D��"��J�C����+
�au9�if)gD���T�sq�KV�0��=A��9X���G=�����P7R����Ur������r��c	%p ~�Ls�(���$�3O
�e|��_3�V���H�L�%e����
$��U1Hx�_���#{��\�����S��M$\
�o���A���j�W���m�1 �9�tk�t��1u�&Y!)�%�C'�D�1�5�]�t��X�	�HiDN=�<SN,��9l/��O�1J��Oa!:��'P�#|�:'�
�,B�p��B��#�������$1k���!}"���8�(s]�a����S�[��3�7�KN���8>qV����&/�}{�1�4"F���oM�2�x��[�:���vKr;A�;(���_�)��E_|��d���vz�d��{>�	���7
��(�r�z�j(�AeC��Tb���sJ]
�ewx�D�6"�����t�+��m��Z�p����7���K�R��L0��d`5S��gi�k��S;�X�{teb�������+������R�-�]��[^�-�\`��a���*[�f
_��+�XO1��-$�
y��A�T8@���sXtf�4���(x�(�558�r���.{E�TDT��P9�DK(g�D�h���f����:`s���L�4P���A[�^���p@d�i��3k�"�:>��^��Q���A�<	`�TX�@��v�(+����}l����]�������|�|�e=����~`�5��G�L�l_��/<f/k9o���'�G�������2=v�_��&+QC-}ht7} ��I�O�8�z��f�-�V��F�/����\{�-������
����k&G!�l�?��:��D=;_\5�aM����J�9}Y���V�(v��<z��T'�X��)<zc�N:[�3��PJ�a����9Fr]��Bg� ��(��t\��>}[c���$�B�����k1�	�-;��
9�\��B�����*l���y�>���K"��\������N(�dM��'%P��~�CY��� `TF�Z�N�O>�y*,�u���_��F^�����,�N������U��w��8��0�uC�O�'������G����� O!]��%��+\����%���x�r�-�w�nm���f<�������{�t���	x5�MU��w���z�����w����|m���f���j������l��W��o�)"��I0��>p�y8)�wwg��mu�o�io��T{skg{�����T�N�����j�"+������T��8M���f�����e����`t��������^osk{��j{�������c�[������Wo���
'���Z��?���c7/��L�A���V�����:m�_o���������'|��@e�6�}�
�O��^l���l��6tY������������Tk����"�T�_\�T�ut�����=I��[�'?��Z��mv��*�a��������B`����j��
�p��R���o^[���`s}=������poPL���C�#��):
�7;��]�|�9N�L�������2Xv����T_}V�a~P�1����@?@U���]��:�H����\��������0>/�����@����������@�x
Pp���4���C�f��!��T/����O�����i��Y�is��g�4#��	"WD��Qr���9�a>������9����n�/��5~�����{{;�����`����v:���A�no�n���N�~I������T��������6b������T2��&ZO���@f�e��4����-:��X%���S�i!������������_��64{�/�e��X��� �GY�e0��v:�d/66&���f�����p2������?//���t?���l}��8L�z��u����y�]^����7g������V�6�l�|Jwyrx��D�k��[����x��M�;�Q��.���v�df����T3c��2t3C���������/Ike���Nz[��@����[��d��f���G��_�q�.u��d�����_{������T-�CqnI1����Z�F�8B������=�xFj��"��3���1����5���8*��c����]�U�9��pL���D���kS�}���0�g�|��A&O�NA�K	�\�j<F���.z���`��R�DL.GFXxfS2��"��z,�z����^%� ����h�'�;���O�(t.E^>�&�qf=@�9
���w}D�
���
�����
�Fz���Q�KOu��F�^"�W�a��D-�w�R;[hXE�w���I����q�1�fTe�F��td�j�������8��n5��C$��;[l��C�bud��>.��:76:���[-�I/�gh��Xi�L������e!�"�F����mD��c���"H4���#��)��]��J'���
|�a17��!a{�_��h2�(w�).�Vm����<��pB�!I���F|D�6g��7D�+��J=T����O�4� �������${�$����#
},��H9���Yt��#
o(��1�q�-/��;��$���:��RI�,��{�K���@��/�<]>j�����*q��/�%w��T�^2;w��1 �'�Z_��yz�z��h�@sW������d�����k\��b5J�|!����+����U}�V�G�L�{]�my}Rv�X�w�q�3�x|����&�M�:���B�8��,���������	�E�/@�g���R����#w�k�����n�hw��i��,���H����1xAyc\�8�!k8��Pv/�V�o��H%���8��1E�������rw��;�j���Z�G� &y����&d_�p<*����o���?��/�$7/]+>w�zk���������F�iEG{�p �7���6�]��B8J�'��@
a���X�O���3*,X��.�is�jK���?���!�#)�)�V_���F�7X����MA��
�� ~O��j����cuta�������������s���J0��"�1Z�T���(�������������I�'�O��v����]��6�N+���)�P���
%���BX2�����������
|Eq�F��:9��#���	��$���r��]0�TQ�v	�l�J�������!�%�c���^��)@���>P��}/{8'�67@����=0�7R~&y8�$E����u�pq9l���'%@�8G$�����ITB�������e�8Ns�h�XC�;�lC����.������#�=:>Axb�,����{���c����zl3��
L���@Wb������xk�le�(����\�h�1������C	�����U�B�7�C^��s�N�B�1.���S0�� �F�U�,�	/b�d.�,e ���V���+��%�HE,�-x<��	��,�)����M�@�^L`2���V�����|�,����2���������c�����
�����%�\V9+ccS�l;�Z�3�E���`�o�E��wImC���6�<���d�������W�1E"�]�I��b�IO������L���37�z���2�|fwv�D���u��33���������@Q!�#���S�����!��NU	�TG�FqS�"{\�x����v$^k)���_`RST�>�b��9~&��1L�t:W�GC��V����`:)@(X]���^4��yr7w��(|$'����� ;B�
�����dY���m��������CF���h���k�f�t��g�����������2&�x�/�j���k���������@K�Y�qN%2��&zY�B��k�.Z�����zB��ea�'$^������28�>�C���q ��K��)�]E�j6��~�����L)��B�>�D�������^Lu�	���At�a^j�3��D��&���B5LwH��X"��M�h%I9{h4�����-��~�Y�2fm,��~s���Gw�L#�	����\Rf���=���$
/e��zy!���&(�R����YfF��XF�A�$nf���xz��L
�4,k���E��4�8��C$��P�e��B#9�&r�#�)cQ�XgM��	���Z����Yw�gI�����J}h�#��AZ>]H�,'y��H2J�����d��uj�K�����/d2i������!�[�;���I��'�W������&�Z$�X$Q]O7dNpI�
_�O���Qw���3s��<�������:�x|��f���p��������[�Sn���$E*�s0h��m�I2���O��>����a����QV�#`8�;���r"�t�C���%$�n���>R����H�L�����la|����a�����:b5F��
2��c�Y���R�U��
^��i���6\��DeG\�a`�Kbn��3�r;� �����Ztk�L���4]w2�8��!��,W"����b�`I�s����Pm	(��3����d���-rMv\������u��moto��K��So���P3n�M�19f*�$D"��&0�j���xX�
���.��R+���1�������A�S���������NYa*kdKX
�T�3G.t���LL4(���T*���	z.��}�#1q$RQ+����������9������0�Lg)iV(�J�.�I���pz�zG�f�7�'
�5B&�JtbrF�b��:0�4���+�6�=2f�1�����~��H��07�P�i�r<O~'v>�Q29�D���U5B��`(��2�\��F��[�_4��#&FA��`��OI�Q����	��{��J�}����(�w��sA�7DM/�0��	���
"l����2�u�1�>'�<!���x
�+J!���2d-f�U>t�m`��Lo)k	*���W�_=@���AEx[~������{k�j@����,��-4p���0����21	�h�����T#��-�W�._��my������S���lU�����9���}���;�Aggk}}������n���j?������V��j�w�����q,�����2`�!��t����+v�&$�r����lP�u�[�����[_eQ�	�lb!>��<��l��a��;���q�!�!�!	���j	�)f4(0d3�e����$������*��)��R����3�3��:&�2�8�y^�������.�
���NGv'��{��/��&���] �!S@� f �7Y���[����%��'��H��\S�g@QhR��\?$���5����B{\���,���;6I���������@����6���##�E6�M��%�Y4X����Y@�F�2z4#�s16�;sj�hW
���U��B@/#�[��h#�����M2V��c����G�s&���T�hh�����@;����<�Y9�k��D0�XUUrdV{���4d6@tRn�:2��vI�!<�%��2F�#L���D����(|p H�����Yl��0���t:io�$�e*�������a� ]9�M����~5���df&��!,�Wd\
�1	�mw���Z��4�����U�&z����6���4Y��{���5�����d��T&4���Z�zs��`F�U\rk������~n�mjL�P��Xl���Y�K����H@��p���g�`Nw��}%b�wPuf��RU��J�#�$������k���	;c@h���b�|�`��y*����w8�t�)�>��f�^h�%q�\-	U���#�;��j5�{���s�
�O7	��������IJ��|c8 ���mO����L�Jf���C�Z�����g��������t/�PsC����6��s�?����?%��m���c3���8\4���X�������n��kk���������v�A����{�pgo��vv�����Vow�5v��^�K��_����l���t:/������/�M/�KC�F�:�?�����$��[|�,flKF`��(��Av1m�����z����XH*�d���~�g�8�)	��{��w�
����8��D�/>&/����S�C�c� ���x�����"l]O��nQ�	�]�J�O�����vs��e$���TQ��@�izgm�"�3P%���w�r*d@�W�t/��t�~$�tua	���eC�11�\�vS�)#��|��W�_#����l,MB9�Y��s���2��3+�C�������!���R����� ���t
���F�O�+�[�}f��]��� ������'�X#����������++�����V����?r�68;r��d�7h�^�������6AkV�����:����t��H?A�����C������b��l���a��������9�jxe����Y�	����A��\��|�;*&����  1��f�X������{y����~=����Z��M�����K��A���!����'�|*?�I���Q�30����%_hfx��E/��6e�y�E()[���id�o
t���a<�����W��{�zD�t��$�)��J`���6�_qY��� �,�ei�b�@���h&Ar�I�E��c��~
���;��@�h�����hZXR-�������h�W"Wq{t)|�������I
���3(����]_����B�����6'[^�
U������f�f���[67!��=7�5�C~h;�o��
'����S_���3]9r�r�
��X��L����?UzY�F����P�"��<�����+�eE�-���\G����|*����%m�����O��"K�8+�5U#��E��\�6�bx/w�����������(��A��p��1�-�0��'�Q��_��e�/i�|����&�Xt��"R�:���&�
��Z���%�"�T!(.��8�u	s��j57�e)��k�B�(����^�"D�5O�����;�M;��x�S�����e��
�+����l�	�+�R)��s��p5����f�|m��*�J�?�C�4����n�7�.O��W
���@fT�a��(3��{�����F�0�{I������3�46��y�A.TicB�[�A���S�j!	 1�@����P���`O]Q�a��|�R�U�S9C�x����U�Y��:BD�z?��H2������	_�L�X�0���J�C�
�>�B2]��"���4w��z��*���.*;l{kwmm������tw����d}��V���6Z�aS�#����%����T�����0�Z���]8~�sQ���!F;��������'�N�R|���k�5.O�����9��K�cKq����+���1q���_&"@�����0|f��G�RF�~����Iae�d����2����L>#�-�$���yu���(�9�"9�-M ��8���2����);k|���b8��j�������	C��rS�L;�G�&���JX�YM���a��0b��o���������2�wA�����-��mCk����2*@����5������7�v67r���������S�������nD������j��iov���n�m�uZ���������F����k�f}������w�`6Q�k�f����D�5!v�ts��c�I�D��]���4�Y������b��������	�����>[D$��E-�>l��/n�)Y�����e�n[�d�����&DB�a$�Q2p5t{�:hP����..<.����b�����SN;�N}�s�=,-�_)��q���P7;'=�]Fi���B:�$�������(��Q2�Gsel���vY�3*�������ts��M������F�77:���G��>
�����%������<pUu���Ne�)�7W� ����r��Y�
���(W�}B��,e����n	���m��x�Qro��W��=T;����\fT����[����K�\GQ)��L���H�FY%�����F�N^���Q����H4��������O���_���xm{��m�����{�����m���!���,��B�I�K:_��"dA]z�!�`An�H�}
����^�Et+@��y��uNBW�&t���(�n��e'
6�
�w\
�����rK���;J����7�i��6!�
�yTt=������GHa)-�j�]-Be���0�@��'����<��F�$���F���������`�i����pv�����
h<������W<�d������������g��������b>;BR�G����KJ�xzx����5P�	���I���E�p�tM9�������j�)a��UXm�A�}��dR�����=�������������962��7��1\X�J�-
��}��=f��AHO�	��F��|v�k��������U����@z��Z����f`�y���P[�V:t xy11���I=g��q �J�
B.]~�i�G��a;��4��g����.��/��k��������F����n�mn�u���3��\k�WZ�ZH��@���YY�twJ@�L��((j�4���:TE(�U�������=���l���Xd[����u6����6�do{ee#�l�t�������Wl��t�2d�������� ���`3t��o��r�$�Vj+0$r�{vZ	n�/��@�����K�o\��������o�o�?�J&\U����'d��{���_�P^���	��62��;d����� ��P\��a��X�.�2��'�.4�`�e��I@<4B
2�*���
��tK�}ELQ�x�������*�P��E�b�	F�y�Xp�t{���`��e�B����c�*Zq���a��^�i�G��]�L����iG��po�9�)B�#p���bX��/,WJ;���6���N��Sa��3���q��@V
��eu�������p�:��DqR����o��G��"v��oH?s��t��N����D��PM
�+|��%V.������XlP`�'R%Ym���2j��Oa�N��(PX(x�}�Nw���=�)&�G�������4��z)�k�%2x�==O���O�s^�w��z0,��j�����7k
������hUdO������PX�B��V�NO�l���I�M��������j�9�`x3�8~j����dc��Q�~M��2BAx1�mb�|��3�7�%+ `N���v�STw�y���f����5 �D
M;�
�*�f�Z������T�6��M%f���fa@���I6FxS���y`��>�U���cp��N�p�R�e'�0�����NAiYvR��m�\���$����6���F2��,���i���o*Y�5��E���pU�d��������x�X�D���(IZ����I��\��� =�>�(P�K}J;`�(���Z�Z[;$�L������%]r�@Qq���*�F�0����8C�D����-+��y�0K�8nQ���x�]���Q��������LQh�XS4���&6.O�g��h
�!<��zT�F
�U����-��=�����"bx���qr,������<�o��C���>��.x-U���q<�����t^��rjB����t��,�
!��d�w�������c_Pm�>}K�<�D��W^iKs8s�R��;�-������^�5��w	'�*�@����Kf���
�~S�`��K�_WZRT��rng��[��#����5E��{r�r�4X�~��/�Y��;�$
�XA9j��7��!/>%���nZ~/|��}���1���H�.�n&���%��
0�).�{;���:Kgp�wI>�����YS\&)�u��s��"��	Cq��������nh�$�'Y7���;$F=?>���r]Q ���TC_�?�Sg	7��H������������Z�
US�z_g$��t��)3Q��VX2�������p�����_�{��v�?��2��lo���`gm���9�!��BG�'������-��������~C0�5�ut����MF�&"3#CB	/���v��^�=:O6q��u�����
��h�e��9fW-���M`ii��>���T$���RE���GKK����Q&ma���!�����\0C)�H=���
;�#L3�pL@@[C���K%w�4G!��~{^�L�Q�������+^N=��y$��(�5�Hx'��Y
{��8�'2A���R<,����R�;|�����8�s�"�����3�������5�=�)��;{�����������F�tv�Z�v�LQ��2S��!O�UtY0���R�
���!S!�@�n��qV�����T��z5����*��X������r���!��bJ�� D�K`B�����8]��2��s|��m_�X���^0���������vVV�VW[�����VR\�������A�"�-���Y�,�]�Pr��,�������t��K1����N~��
���x���t8������v���o�-��$#�mt�e������}uo�g�O���??���e�KeY�n>a���s���N�>���|�R5�#��������M��M��M�k�����U�>�|]�G:�/�~����_���7��.�xX�*�)B�o�/�1�e�����o�������m������
(��7�S*�����{�r�[�u/��s5�g8
����8,���X��������/�
���U��hmoo��w���]o�2d��2l"��;����f��	,�)B���pJ���4v#�=�\����+�^w�`<��>�<|�����s,+<��@/5�
u�p�^S�3A��"6�-����[��$a.��a*A)�d*s�2�rq�2M��WCB]��;|aS�&����]��{*$�n��k{I�����������Kv��f ���f�.����,��w���e�h�!�\���9��P����>���^��o
���=�M��`	*���*�9s������#CVyk�T�>ye����(7 ���q����"1:��{��ba�V%-�8:OcPm�z����d	���Y(b=Y��f���	i���_���_@D������U�35�ks}m}s;��������_��?�����]k��v���V+��lo�i+�Y����{;�������C�o��M�_����
��{"���3�1#��,��P*d����M��N�BN�*���x��B��5�g#�}��K��j#���(#��i���_'��S�:�!���v]�a��K�����8��70��=J~aF�%����#��O8�>:�d���@���g��"�g����6�m0Za/�l]���q�WC3����"C-]ge�������ms�z	��^u��9N��fT@�����Og&!x�qZ�������9.�7����}�8�0&�3C�09��������y�,�,�;�.$��-x��iS���RB��+	���#&�J[��p���a���]LiP��pB9^$�����xh�\Z`��1������&�����sF�=�8^o�zy��6~��������fk�����_�����u�hw��1~�������9#���������(p��(���������E����)�~��Yur�zXg��v��
w����x�Y�24;XX��D�	{"[�~���n"������7�u`7�Mm��9��?���;{�:f�%|��-w��X�h�����_����$uz���w��_��^����Z?���F����s?���h�C��f��G����}l�����C�!��M���b��F�������,��97�����&�������~%�XqP3���C��0'z.�v}���W�.w��:���q(���s��S�7�.�}[��]��Y���nh�e���O�7�UG��_c�9�xZ5r,��^�J�� ��%1���|&T�nyu6�u�'�P���+Q�E�C���u���:�a�����I6~��a���fH[F����hc7����[�]��/�{�{{������J�j-�:i3���;��D53�R�`<�������&��1[++��vg���:���l;���h�P�G�n�������N'����W�����U]���Kk+7����7�'�W��
~�#��6l���
+�!�F�&�0���[�Q�D���l����H��&');���"u�{F��t�@sRF:��<?l��-�����Nk����WV����Z���7���
�,��@�o���V���^�b�y��N~D�k���hc��1�^����
`�lymV��-!?���?2�x���M�������?��V������W��2:S��
��:�z�������o>���;��������?j���������y�����n������)5&��EeS�]��l��7��BG�Q:C.����1|/�iG���������������Cp�������1�\fN,����[�y�4��`X���,�Y�J��%$�����k,#	���	���S�? ?P���'3<~�{�O����P������'���=L��(@����v0���.6F������������ep0�����|�p�RpH�)��������b�>�\���p	N��{��OW����I+�
q^H��UZ-����y^�_dy$_QGB�H-D)Tyt�g���7Ky���k8��M�*�M]���Q�����C���LH��'����+����h*{F�mmm�7����nkukcgsu+���;�T�v���-CS�L�� ��-��0,fa���i��b�U�����U����Uj����++���Nw���lA)���U�^����V�j
%�rb8ET��������)�6�j���(_���F(!���rE.�p�	����������<�=�i�D ���,�	d���������x�����_fR�U�H�������x\9��6x_���G������J�{G��M�@,�����:����W���y�	����m�Q���!��H��GG����X���=��%����\x�#����9K�����|3s���6
��Y5������������S�[��v�����W�+a�������7���y%L�CJ�\����o�P��7��H��@f'"'Br��l�uf�~��/,e�iK���T�)e��S�J�c�X�7�g�R����NO�@���{p-�Z�j%[�[�'�A�%�	�H��7�n�<FP��}C���'
D�x�����P&]����6�����zi7i?�{����r{.6u�-Z��P�hlN�����r+!�(��ujl�[����+t����e��\�Z����g�E�eW��kx�0��A
���pf����)e�o��� kf)}o��p�o���MS�f���7���>����
^�`���ro���=�%$(}��s���N��t�.y#���K���~����.������eo�6��",�P�|�gK�g�5�^���x�K�q~���=f�h	^���>A���FB�2r�]
 +�2Dd$'yO>$T�<a~0��(���a�����C�Y�(���#
k�J�Q=b[�P�6�9����7�
{"�`$�cq�%	Ftd?����S�/��*��}<��^�{#�D�GB��8U�
d��2	�b�i�q�M�	����4?�Meq���O^kR�����o|:y���<��<��y�8}�����������e���P3-i��K�V�����:�E(`A�1����/��2����G������Kj�G�����n�
�,(f�	/-5����c0�����n:�U6��%i��{�J9�
O������hAp|r��W�t����w��g��G�y����%�������4K���v�'��j����4�5"\cs<h�o�,Q���M-v��>���u�-�M5�P��{������C5�.��b����0-J#�r~�o�19��U���0H���v�)L\c�����I��?�wA���_�"��'���o��`#��g6�]F�2���.O;�����4��(��!WA��8s�'wf�{=����JW���Q�na�4'/v�����g�=�_���Q���zd5Mj����C8B{�`��������D������m���]����ai�2 �uD���-0e����Dm��D8�\�����7"�X�}�#�z�]:F�QT�a�
����Q�2D����N|'���\(I��y�����6gRg�D��	��` ��;��DU����]�COL�#�	h�-�0[��7g�D��
�������e��0\�P��[��w�)gmw���]`�����F:ye��T�'�^1�G�~�EK���U��������I�t�H[K�����fI�4�#~
�����!�Z��C�33��-��^���8:$�$i�
o���Wp�&�V�������=<J��	L��|�Hx�~���
x;A���(8R�aT#C�X@3�c���/a�"�h�B� "��h�	���?V�7���?A�-11�h�T�l�K�\�x%�*
3^
f#=b��e���8�3�C��E3ZE�(��XD#��b��_��wQ?P��k�p�x���$�L�/z��W���{�B����q�Rw_]�<�,<z�4�G?6�*Y��%6�7��(mP\�m�q���l|<9;vXBb�W�>wL�j�;$L��
���3of>�EN�)_�!����m��O�������r�p��#��b=B�CZj��6�����oSCs;�2i��2�z��s��R��S�������T�g���%(�]&�wp7�~x}}hd�����F�������%y���\����1�VM�k���������h��.�.��Q^�N�n�����3��I�o8��i\^Cv|r�8�>��)?�G���DR���[��A�2c���o��
_�T���f]�5����5��G�I�bf�g;	VT��O(L�SnOLt[Cw	6�/����+���an�;��`Y7�iF��
�p�Z��'�UD�G�U���[#[v�vz�8X1�vUH0�k%��$��:�n8����I�vgA���ev����O+�V�V��i�^�E#wl\5������X�P�G����U��K
�79_�1�������@s#{��B��BZB��m�J�R�,G	��H@������Q���L:DK(�%t	���(������Hd�[����C�;�m�nP(�����Q�jt[������Q�\H��/t!O�����;_b�L9 �q���J�sp �S�-���{	������9�Z�z�s����������/[�X~��!*@`����R��k����#@Y�'��C\���$.#L���	`����0SS+�A�r��Eq7��O�����]�i��������H�����Pd:�����b~E�E�~�f����7����/l�V�){[���.���� �����]BE�f�F�N2�}�%�����pP&}R���I�����wUE#����Y�LW�]2���*UR�g}��E�F=���}�)���������s�RA~3��/F����}��$���(-�[�
@�?q>fd��?�
Js��#Vi�z�U�����)Z������8�o�`�d�� &2=���S�����YC�	�PD�$����
pk�>������I�g1TG�Q�J�f��_�x?�|j�\��=N��m��-�0���_�7�mJ����4�-X2�E8	S
����x'�����	���I%_��$����8��.�������H�y2�<�����P�����>l���oo4�s��
�-lO�>�(8������!l-f�Kn�L�7��+�?�"���m�;�����s�^�����3f���%���U��`Lw��3uf��%b6������p�h��@?��c�f�ecP��(�����+����=�T�Ks��Q�f��ew������T���{����e����W'�,�r���\�ku%�t1�N.����
�2�$��{_^�j�e&j�~V2n`"z3��J�G�X%���
A��h]�t��t���#
Z��SQ@_�[��N-�b���w]*��(y�;n��bb��������3�T��)<�2��#�Z�i�������/v�#�[vhSM�s�	����
���&��MC2��H�Wh�0����$Y� 6�i���"�u��-���4�V���G+h�"@*����!(RE[������1.�^�-
�cy�B�J���U�]�[��%��L���@�}����:ErG+�U��Ox������A�N$�-�1���H������
vSt��m�&��R8�+�����=Q��f�� ��T����������]��0�h4
K�B��5�\H��@��1��{�g�w��i�E&�nS��70��qL�i������-n=iH��v7����mfA������?�V21U �U�����cj�L�-	_}����y']#p�I��5�I�q�����j�@#P�5��N����|�����5�/g��y����e���'��.�5�����n��!/��y����1+C�O���sY<�k_�������#�� ������_�+�{��4[�/��U�����5�4�#�HFj4>�\]+��J���4�C��]�������D��X�JBk]m\^�_Z�u��C��&�V��PJ����n��d���i��������G�����M�$QO`�&������DX
���cR�c(1<�0?�
�v,h�5�d�{����^S�~#�TNPS�f�3���wQ�1����S>C�g�D��)(�7Q��y:�V��+;_�����U����u����������H�h���dX���
DT�iGT��RD�V�;H�	�nl������R�������_������8�����r�� ���Z�����z��?�TV8��w^��yW%������b7m�������-`EM����bJk�>eKZe_�������w�`��<���X�<��:����5��� |c[�w���j��`�3��3�bo~�������`S�q��i�A�!j���}�X=�r��>5�+Hc2��Y��0���&l&
l57�4�Kj�����I,��o�#F�]��c�&%=o�]p�7��Y,�i,6m�A�&�w�S���j��������#��C�����Q* �ju�yS6H�a�aC�7t�K�}��_=At�i��X��88��i�&7���f�D3��e<F��0�=����V��h�;\IKu�V�-��E��$��6�A�r+��u� @�,�XX����g����UU��9_��V�}����X�dp����o%9s��> CMQ�!�+�U�VDp�l���:���9�_�:��G�r��2K��8�tb�j=F�U��)c�������Xd�H����WQ"so�%��?��E�{�s��'J0O��K���4��)�d���TN���{=}�t�JM-�]2��E�m���p��]D���g��H��������M��v��diF��tM�=��5a�#`U�YMT"l7��$+��Sn�
:i�!~�:	L��/���}fj����(�A�h��}�
)����4����a�O�]�F�7��	���@�l��������5�#����d����x�k���f�G�@��I��\f�NGIdA�����G�oV���8��M�W���yCY���J�Rg�������4�������%��*���+���cs*�0���(W�i�V<��GS4��&��@.�`v�� G1��
,��A_(P@R
H/�������&��O��7cW�9��B�N�6�������R�O�>�nS����Q�0��FV"�LFVc���m�q�d��~���-L �dh�h�HO�&0�<n���$�l`�����2s���"��{���s��)�,�Oc��V�k������O��B��H���MN��������
���%�Q8	�%�7\�1�;����
r��|��C����6z��|o�(o����P���!jT;jx���SM
��}/lkJv�u�����]'f��x�����0b���;���/"��Nz��&07)��4	�?�Os'��$�,�A����g.��O����M�^����8O�icu��Z��/@S{;���AL,��>&c�$�|�$�U�(y`�qJx��\��2��R��v�T{��N������fo��~��|��N�w	h�,5;����E7��\�c&�fA�1����_�T�c�C����?/�.ha�Ra@]�D�S�=�:@
Ja�-��`��6q�@/����Y���A���5 ���\��D���e��~3�jS���Y��vQ�/{�
`��-��Q�a�S�+���8.��-�����r�+AVtFc�cd��E:h��g-p����u��/&G&8�����4�8�X�������BSp�C
!3��B7��������e/�v�%����{En[�a�\�|u�fdP��`�������(���kA�v7���o�J�gi�X������f�P��Z���g�����U�x.8��C�'������5��nh��O�Z9�"��i�����m��a����q��v&
�h��fO}!'t�dE�8�� 	��g�!yg�/��u��U��lm��?�	:���j������_*o��9'�@�R�P!4"QNs�iBW�FSPZ�
���V�v�\�Gt}t���R�+���H�;L����Rz��$<)���~�}��	���+��{�<�� �����7��,��7���$��I\�I���w� g�P�gf�2z��TWI���2i�d�vY)e��bn�KC�A{�wrD��<��P#����Y�'��g������Y�����vM�JC�����T�����[���`������g���Z�sU����w�+_�RU:!��~{����S��T�����C�W���`��
�-�-N��
=�!f�\����&zh���h1���N6b�i�~]`��XU�D�k���������t^;^[���;�r��*��;n�
0�Ulv�r����{}��m�n������D�L���N��o�����������>�yO������\z

@0�07�0��?��K|��k*�0y����b��6�,�-�Gq�7:��A�7���-��ph�)d
Qx1|I���z�TcBn�g���G������:c
�I&g�n���]@_���V-{���]����6�D�jxWn9��
�&�&�I\d�U'��,���X����yJ�9�jl>�h�����9[�s<���>^�&��o`w��H����7W�a��i���?�QO�_9=� �&u���I]/�k�������	K�l� 	�O-���1��Z�mY5��������r<�����]�JTN.�\p9������+��F.�)w�|H��(j�5�8�
��KT���S7�l�����8�-n�l#�����������
��l�'r�"�i����"�N�@���G&o����r% ����)�)��wV������I�R�9������]��z���2����-u�N?E�fT,�o���f��.?�c��TSO��-�0�"^$9�M���=�>���	�=b����oA�9R�e����S]����8��z�1�}t]����T-p@c@�_3s����|�o����/+M��R
{�#��5�H��L���L���B?Oob9Z<?�SS��M����S��q}��������w���S��{`^
8�#����C�q�=�--���������Z��;����
KT#2��#��8
5l��`ZLw����m������2}-!&5�IF��)*-tr��^�zS]���Z���%1?si���2S^�G_z�"*lf���!xk�����(���V�
�'g;-�k��/F}��t`?.���x3������c!�'��� qv���`f�?�����oy�7�w������u�X��k��3Y�\G��#��A�"HE�h�>9c�YS$^j1M���P�:�hu�2L����W?������NU�C��
�|�i���mB�i��%�����'�q�\��9��a7@I�
�&�2�O~�V��v	{(
\�m1�P��_)
*�CzLU6�y�i0��K�z�ol������}����brK��V�|D�	��"��-$�	�Z8�	Q�D�+�

�P��
V��[4P������
{;����D����c��.�hw���0u��G�>�x�Rs
�L����U4���x]f�+���mY��FmoL�����h1� {s K�!{�6�(��;/��8��i�&u	8��n��tr��{CrBF ����7������C�t^a�N6b,*�Cj��X>�G4qC��fD&�!�&��C|�h8�v�
��5Ul�R'�|�3o���C��E�o�����
C�@�|A��e��]y�39�:�,���7�y9�+�c�T�K�����������2{'~����r���F���!���-eR|:�#��Aoq��k���t�����z.��(�������C���l|���4�J����%��F�����#����i�������
���0JzX��r��C��g�Gf��)�%���!=��&z��x�-h�L4��#����Y5��+R+L��!:@�
U"��2bI;������c�Ud�(��C���H#E�@1�s�R]���\
I�c�h��*�@��2C^�f���0�������G�9	"|[�E��Ha��a��Z���%	�3��Qh�kA�n����M����g�������	����j&�X��V�G��%�I�XB��e\���${y���G)�%�:�>O�������X&��������)����w�hm0?����ak��q^B�@�"l	����t�����RV����6�������"m�����}�|���.4~�g���e������kD�"�4�9��:�n�9���?it!���d4�z�3w��q���(�r4F8(���T��1.���h��{����h&�[:;��mt4iJ���l�I�������Tm��������(%�o�)c�����O���7��
�J@�8'\��U��/�L��cLA
��QZ"��t�.����|�~���
��ezu)%���K�f�9�TRYK]_obDj��T~��}1gz4A���X.�4
F��Wb�����qm�����i��M
�X	S�db�[�$�qT���bpx'1b�2��G{�a3.�z��}��}����A�Z{Xp9��2Sa�Y��w��:�������rKE���V����W�r���v�1A��f��Txt���1&�q�c}�yC�`F�E�����{���^��>��U�tKK	�FL��z+�<W�;�>}E���� _�s���ihZh���
�L��Z����V��~�}��lp���I1������{3�8%���_'^�����=brnnN9���(���+��I��x��i!�2���I3Zf��,�.�C��s,�����h�z�������������a^c!�+*;��H}(s����}���4�N��C��K�	T�������<G`���:|BC��+�q��x���������(��P|���G��Z���$�%��J����#=rK�����u.Dx��f���i��Ek.�Em�7��j&;���c�D�� 1�H���!&���
N�n����B!^�:���dZ���r,��q����������
u"Y�i��!��*�y,�m{�J5yh'Cp��L�$j'��#lN<�0)�9���HT54n"|?:9���X�"�Ik��z��u���.���G��s�x.�6�f!�/��CC��J8�M���h����G@< �y@��w��.��~0�������z�u��F��.[�~�(/x�2~��;��_/�����������J()l"�U����K�_r�x�(L�D�q���}D�7%���{��������i�cWf>v`�y^�vb6�<a�����(q����by�e��F��a�=�Z�[g�a`����3TV!	��n�}\�J�����������z?��`e6��<`��@i1�������(0�A�I�3�y��������4s1|�t�31CO�� ;h�,�y��������oRR$��k'\#f��Z�������I}�:<�P�7l�`K��i^q*�-�����`]_U8W{4@�*��n�!(6<�Q��	��nG��`��E�c��*!��7tc�`	�)?�,�"*K P���g@�y9�/u^����G�M����E?�^���5f��t-��xp:������pnC��7I��pIY\�#k\h ����� �.�7���K&�2�h<KoQ^b1Y����pC�|6R���m<�}�������(��6�&���}�}kE�2�Co@�)D(���v`���C`B8�!�A�j)e�I48)�����A��CS��p������@���(��g�{F$'l�{^�,~���b�0�9��6�!�	G���6�;�.&'�������	G=W���
.�
~��4�'.�'� ~�=�'6u�u�����-��x���z��w�>xL�*W���m1N{.��>��������"����<��5�5A3��!��sl�Md)G�3���xHI�{7#C���rge�li;.�	%z���W��<��NS�5���<��
����d������=V��Dk�������X�w�)����q�����l6��
I�49�L���v�Fj�;'X+�nE� R�����`Z�N�Th>,���F�>���h(�	O�� -�^^-��qh��lh����3��9(���V�9���m#M�=`S[���7Db3.Ppo��]>��y0���t��t���!c2��v�����O��A�h�����nFS�&���p��gyY�Qb�vG��k�i.���F"{��Z�z��\!c��7���B��x)�p���C.+�a�q�%�������J_�L�p����!��h^�G'i(�3�h����c�DJ�����-����-�a�'��T3}���h�3�#�z2h������Y �L�->�!
��B�B��z�P��02+
�
����8e�Kk����	���L��^���V���X�Z�� c,�vxz2��p���D3k(�.'�{�����:����hVs�?��t}BeK��� �#h��i��i��^�L
D��d|1J����z�n�^x;x�D���-1�
@�b	����������,�.���[4�vw�������m�AW�����~>��sP��j'��Vr��.��w�-;�s���|���M:�z�����G�&��V�#�yU�T���M����R:��d��ir��F'���9���S�D���A��0���A�1����T�^[&v)��'������9����	86WfZo'����R�|=�y�N���}�����u~~�Z���}�������8����%���]J;������`:Ys�%t�7��r@�����I��my�6����? 
��1Q2$N����o�W�IGb��O.1�%�����j:��35�$>'�+,�+�E�f���}�����:������n"��A���E��SS�	k�������I'��6|��pEV��0�j���}��N�<���~�~p���FF��������EFH����v����f���9�Gv����lum{1:9���l����=i\F���J�����|L����Qu�73��X�U��%��"�5����,�9�Z�M����M��&��o�H0LF�8�M����2��FcG��c��X��yj�$A�*�8����`�j�!�L�Y����
F��o��9���cm3�'��>�9@n���@�M��WG''y�������oft�u�	���e��~l����,`�������i�i�yw�#���d��6���O=�y<��%�3�H���R?�}U�P����e��p=���?���.�;�K������IT��F$���'����ns����E���������3.��oe#"�@��x!����6EO����Kq	�Pq��>\��I-�r�)��������J�G�A�B���V�����(t���59�%��TIF�����[9)�{�!`��G�n���[���V&���t;�G�$��%o�������_C`�e�:n��+@�)a��Zr�����D�������=4��D����8�*�,��C�_�]�k�m������[���0eDB�$#�'�1<�h�����?@7a�|�����,HL�����G�g�<�Hzy�@�d��s���*!M�Z���V�R�$f�����e@�~��0��`���T0g�W)�9��-!�o��?���f�^���)�P�k�8%is_r���j����� (�2/\�)����DQ(�D�Z('��]�'/}�J� z.)�{������>�CI�
���wVY�|�nCE��#E�|�{XqJ��6e��KC>���,����3�K�0����R�����v�ylX�u�os����M�����
o0�&e5m��)��O����4��g@>^�\�D}�L��tH�:3�jyZ�F��f�hp74k�0�4;�Y��>93pty��~T5�����B=����d9�iA�MmF(T��{U��lS[UP��'g�����c������JO_uIL�6|`�����P�s�L�}�u�SfM5����d03��"��M���\0{w�K��.���%�L
�0�Z��#��%��x�K�e����|u���f�t����.�����8�N���KR)Y��������e//-M!-o�l�������M���	cI�t\�Ks��T���kn�������s	��x�e��_Z%���
t�0�U5�nqF))�zH%�h�B�����x�g�y�G�+��4���"{��,������?HT�iI�����-�?�9����1�k�${30�x/F�������%��a��$l����d�V	����_��t>�����O3��^��}Wq������A�����g9B�
t��l��Wp�5���(~`NF��!f�}��3E.�g!\�99�L�f��4s_�u��!A��?�����i�������J��5��}Y]uQ�!�v4���������
�;�������vZ�+ z��yUM�M0����y��I��j�T$��H����M��H�E]�!��y5-�D����j��l�p�����������;+�y���6���O[���+���pJ�)�j��Z�m���l�&��u)��(�@	��7 �%W.`����'y����}���fb�F��P?�������Fl&E��d�gts2�i,��8VL�{���/0��U��b�����n��'��(8��3C�p ��6q>�>����Z�����G�!9�)��7��V���G	������uOUU��iKG���G��c�RM��9�L�F�z����y�8���Y7�UG�c3�����\D�9a;P-M�:���9nkt�k�����L9��PHp�*����J�C0V�R�gI�2�o��O987��M��rr�M�{Z�Y��^���H.t3�f?��)�*s/&����khE!���A�1�����L���)���\F�"�y�]��0��=&����R����
�c�H�t_��f�1�u�<�&'}�l�/i��'����h��	�����u��A���;��$K�U����S^��o�����J��'}yM��#����C9���\�-Vh�:.�-e$&�&�Z<�N��!�C�;��s`��.��%�����������:&n���{k�\��6����S�_2J��i�,�+���#�)�%�St��Y�hH����U����C52uO�J����Q�x�)[}:��T��O_��l/���A��)�,����"z��8U�lK3RI<"�����I��E��&[z#
"u�K��Y$�K��F��, u���`]b�)����X&�����D�����" ir#�N(
8�<;n�CK2d
Q��#�)���63����>����V�>�%���i���M��XB_��GdW����Dxy�������RN2O��4�Kn��Jpl7z+Y�+�'��/r�	5aSP�[����s�2,;���@����)n/q������:��P�X��vq������Qnd���k�����oK�u%�=m���v���YD!���up-�Ed�O�g�����t	��|����������z/�C_��I���U6��&�����x����
Kn���X����.OJ��V�@�J*b�X�"Hm0L6W|��ba7<�c�,����<���6���+���a29�>�6�F�M��z����=v��l���yw��0�!�	���q�	"���-��v����
���	�"�a�{���9����FD���g���4#PA'u�*6�c�a�g�j�]�>����;��m��C�����+X�{��;U��L3� ����
����zGo�'KPs�1���}E����&�6g�K�i�Z�g(��m�#]����3D9�3���j{lP�(����
��G-�J�p5%����l�	D5�Oo-�s����f`9������f&���l�b<x6$�6.�'t|�}���LH�����~�B�7��r~���9�����X �6k��!�	I���'�����r��~L�o��U<8y�?���C��������`���,ls�����a,�oP����(N��Q����r�����N���I���$���z�x�����v��6m/i*^FWq8K�N�����-��H8�-�t�{H�_G����},t"���I+xgi����g�8a��M��EM���	�r@
��%�
�U+U����������W��T&/X{���8����$}����2�H7E��E�����Y�V\���c���6*�Ey6�.������0������?�&�Le��0�e�D0�3��+��9��6
�����0>���s�|��y[��9�.���a��XQ�FPF5��	/iLuu����X�)��]�|xs�����l��PV��.� 
��,�������j|���=��W�;��ke(
�=�K�~��������������o�7-�vT��1�=��;?�<��/|_����V��oG%���q�Y�2@
+	"�H��I�A���C������k�gU17#38��J
?I*�rv0��!�f?&p�AD�`D�y���GF9�r��/� 1�P!�b0d	�HF6|92"�������	��S]�$}<a���$3G!caT��x�������)���#������.I�2B{����$�VhF����`4��CC�����:�&��+.R���F&����Z��\(��7��C��9�D�9o6���F�+�tX�����W�����d:�b��A+b	�"�n���a��KJd�
r��{UC����1v�V-�'����%D�=�t�K��l������z�����f�P��������0#/�Kn��O�e���� ���,��O��}U���V��d��/�H��Xk
q22�������l$����N�j0��'� SK�I����:K������������Z���ut?�������!�`w�(�
����!_4��PA��i�jB��6�e�K� ��.N���3��1���6>������"��4�����	��lC9Y��,������r�mh8���=^��D��y|_
$:K�����e��.������]1v�m93���6YY� ���#���8��r��oq�(�@a������6'��U���:�K���0{
����@��Ix��Ud�"?���-���r�N)�C�2���|O���R7�5� �y�1���g�����p���~o�s�yBpN���D���+'~.%n.N]z
X���ix+-B�����)b)��ZZ��],�1��(?�	�Z	eT%��n�\i��R�~��_hZua��+�
��|���7���?{
���U}�+���-��4����4f�������"�w�����K��q�.������|��Tr�������Gv\Ss�0N����N�/�KS�(w(��K��Gq=z7i_
&#�{2�GRXA,����{
�1���3x,�����s70l��FC�z��������������}�I����+0�"��\�)�	������R����[&�>\��V4��A�4����U<�$�X��{j�l����?����a/���\aO�<�z�����y�j
��������p>ptD>�}8j�*��]j"�|��

�����h* �X�����:�i4|��M%�g0$��Y��V/�����t����VJm}:9�@���Q�q�%E���q�'��m1p�'��
.d����{�z�K������=�?VkJ��^����G�������l]7����'g����e��|8�:�n�5��c��*�_vrn%V�I��?�>F0@D��K��d��/T�����F$g�=�*)�
�Q$*�3�H�OY�����K�����Pr)���Ga�",p���JEUQ����6�_&Z�����!����~aw�lj��r���jF�H8�?�=7�2���C����ou�l��������|L:�\2��6>��kC�i��i#����z����]X���6['K�>����M�>���/TQ�l�m;i- �����T0QrwP�
"� 0��������o��ywSJ���qk��][�)s.
o@z�uu�����(y��X*��1��7�%Q(��@:�7��@I�KD�����6�����bd\+��WQa����/���`��tH������YZA� ��N��A`wc���5�����{�1b��IF6�E{������������o\]�~r�����t*�-�����{�iDt#�*���5�t����iX�bY[�AC}���,�� ��T6���	��I����A9<^^����Eu	#�4��{q��3�����E�23���<5$�~'y��p�,rg�f�CoJI�_�C_�t��y*��NH���D���
�A{�\{,?�"���a��,��6��}� ����G���^2U��8���T��M��H��Lt��~���0�W�>U��	��<S���~����|��u�}��C��W��N�&�R^6����l��U�=N�),�W9����:/���\�Jbw��J?,Ek5�y�Do&crE�!4��4i�X�����}1%�&�Q+5��(���7�X2z�z/���'~�\�o
Y`�xD�:+�?����F�n�=9k3SZS�7a>����H��
@T/�qK���b�J���@:�d\d���� b�n�d
Vw�A����.����9|��d�	�IaJN��r����yqxyx��n\6��nx�������q���Zo�7��!�j�nb��.����Kc�y_�]�)��6�������~��pHX�r�,i�$�fdw	�����00�$����|#�FM�������;D�4B*�	_�<��
B�����Y��0l)Y�i�k,�vCB�
��R��h�*�B��*�]��z������������HA�|W���DE�K��X�K��U���-���6�f���� �A���K���!�r�N\�^�i�
E�+ads>�u���95D3V�����~�l��-:���MS%�H��/%�)<D(������ED,�^VW��u����bl.h����17�'i��j	,z��E��xo:]xzBIJX���Z����J5�$S���k5���TJe�8�.��#8�>���	���@_��L2{�
������{���WV���/�������V�����r��"�d�LltQwc#Lt���A����(�*p>Xf2J<�����y=y�b2�mf�>����	��8�h[	&��v�8�6DZ����v�x�K��(����8�V{��]!?pJ��N���'��R��X���*+�6��d�0af�q2�t��E����T��i��<�n�����gx"{�7�&>������i�9��q/-�����������J�m��F��v�=�xpPU�����������$z
�+Pn�����-��E��
qw��3�!����L�H�(�0!��vp����X�����.�p��k6L�)��am��k��~�B���i��Y0V�J�1b� �r~>I{��
�)��5�-&��1�}q�\?����G�zz���TN4�MZ��J�G��<o�c���gyY�l����*}THe���C���	�$���T����$���2�����N�I
<,������VcO���T-���Xw�`K=�Ar���&AoZb���IJ}3/����^#���ep������T���z���`~��w���x\��h��T�bK�v$v��,��JO���:�������c���>��p7�;��?P�.����@1���%���d����"#f�s�v�p.�O8�y��������Q�M�El>]��9o9Y�xH��w�(|X�$I���3��U$g��"A��H�V�?�,��(��!�J�[r��#�������*3�${)��F������~��)�uq���0���I�|������X��3��1D���b/	��b)���������b?�t�Y� Z"HD�?�r��1������u��*�3wJ���x!�����P)x�#pl]���w1�|�����������
�$f4����f�D!p���Qx�T�KF0�����S��D���;�fqs�"�������:2td�f���X��,)��{��4�i"����������gG�'�g���wWto���I�[��.���C��<:��/��������!���AT���E�6�Z~M���3��z2�%�I���'����>� �+E-�4��Wxy�`p���2�J��I2zl����Fbn��_��&3�2���X���Z�\��������[��0��@�=$}2���l+�0=��P �<�m��D��d���y�?���6�u�@�����������h��uT���#�q:l]�P���Lc1$R:4���k�
�I���yt����yj��Q����\���XV��t���_���,r'�L�C]�)�p"(\���(���1��W���j=�L�6�m�V���q���s>"x�^�t��c�\�s��,�J	��?���G��!K�Ixp����]1���t�N�>y�8�zP�Z�/�q�����x�7o�xX�����s���{F:GC��b��]���K���dD��Co������[ndZ������&`e��z��HJl�.��G��g��e0%H�(���k;�	?'N�������Q�I�7�����ii����o[�E�R���t(���]�bJQa,ge�~c=���� U�4y��'�Jt#�T���X����X�Q%��`��gw�+���eJ5��8�W��>[��2',�h~�.Ik�}�v.����o%�v��o����!�gU03�
�WY�*G��	���Q�����E�����qv�)Z4��������9�~83���qU�����u��j��;s���GS-Z�a�����0O��'��	�2�Z�	��������)�����7�e3�������cj��m���g�h�O[����s�c�&�G�u�m��Z+������Ub���4�:a
��������'����;�f��7�L�c�����s������O��M�"6�,r~c�V(f�6���"��Ws������l�9�-��pWO��P�A!(� �1��w�1��.��Ap���8cJ&j.�/����]dww��g�a�gF�Q�u��&��r9�&����X/B�Y�wt����G-feRdsqA�����V�������X/�����J9�{zT�6x/8�v8���������3#S'�KY1�S>����j~��
�������k��1���������S�� �$���gc�9i*$H����/����[���$�1����)�>oY�JHp����d��I��|������r`���L	�S���'j�7
�����SR����p�|v��1�����h.�=�`��}���\����x�GU�	8��C�~n]~7$�:�q���R�>������`��I���S�M9����*P#m��C7�{T�r���hxk�C�l����z���3�1��L����:�k�q��'I���}��T�����n��"�z���P�R,�=
E���tg���������>,WQ�hX���������O7�����K�2����o��~��`�E�wZ����s�6�`j�&/�^J�4�����jX���7`�A�$;P�<�ZQ�������O�!4�lh1�v����0r~	j����l����q���n���`�u���T	����$����.��d�\=��g���]]s,�1:��:^R��@�qo?>��sj�i6rkze��e����x��?��H3D��g3��|  o�TP%��k�H�`[K���]��������*R�0�t�	������goz�b#g��k��he(p��%������5�{I���'nc�h.�����%�) ���	m�F<v����}7
'��\)�0g��yw.���������E����_#h>(�Z�?=�������cJ\#�%m-h_7�����*F�E�_a>�j���8��T�j��^c�J��~w/L��.~�U`�.�,
j�����R��}2�F)�f)��L����
[�}�;�����1�+�?ut����V~��U=�\Y��G#��/p���B���z2p�)�����z|P��%�����S���c���3�<j���;dc�r����<���I��-�9������Ql�^<'�s�[N���l2B__��k�@��6��}�������Y��#r��q������`�O�5vD������������.%~�:c7�4)tS �CD5��5(��!�C����������1�ay���6W�O�
1��'����!��~1������(=�-+R��� �HP
�wO��$^(���&$o�L�`+@�!�e��Ad�gS"5E�4�G���kX>c��uu@_�8����T�J3RxW���������&�����*�1J:�;1,�.��S�[5�*�U��G���������������i	�Hu3rH/��!��:���P��u��8�k;�n�M���D'0wSWO��~���J7��"�"�8<;9�X���[�`H���g�)���08�KG��������9r�z�{y�$�:o�������4$H�lV����5F�z��x��v���V	!���.�<U>r��[e��^"��._�V�,6
��HVlat
5#���u��P��v�b�/�����X���S�#GoN�8���3�%�zt�������^O�-�a�O�K'�N���59{������[��hy�D�W����u�e�I��/ir��d�:;W����^[]�lw��++��xo��������������s~waiii�o��D�k�k���h	�][���w��g��U�}��:�f�V���G��HOZ����z�2�r������NZ=p�M�9?(��c�G��#*����-���r���o������v1�c/������Q��N�h���>����F�6E�����9A��m��t8��5v"��cB7lI�s���G��){gph��+e�l�/T�w�n������������v���www����`k�-,�ekv��{�l�����~�71�{�(����d�$��l�v1P�lCC�F�����^.^���	�E���:�
�Z�=osw����[_���z����W��q�23�"$����:E�6:.���:$��.��� H�s1"g^�D�#��*�>�n�������X���f%W�Z	-��]�d=#�Xu��Ps0F�18x����Jr�8�.&0YQ��e�`���2�R���B9��R ��@��v����IL���L��R����� �]�hu'���b�~�2�Q��bx��a�>���b����x�l��Z�Z[��m��z��=�?�G!�6�Q5A��d���r�+bu�j�L�Bxt@))2��f��S���5e��C��V	����$���k�`6�kh�8��4�&��	z{Z���w��\�w�^�=�V���Ed;
��
E�����qU;����������Pm�����P�x%�������n���wwVV�7��xk���.�vCm/�P)��VWW�R����D�{�w��t�L�����#��G�p�H~�xxr�l��qv���x~���U����}�������!T�������`�q����},�U��4q�c����'�w������������
,e�)Ms�g�-�~Z�W?��n����������WF|o\6�ON�O��O���/��)�;T�qt�dc?�1e�#��Z��]
�1�����D
�'���.+��{c��n�>rsue%��Z��m�m�u��wi��M^Z����U�X.���Qf��F����`��d
�f����LZ<nM�f-2�0�I`^9�2�;���yL�3��mg�����N�bF�L
�~6������gy�>�A����b�#3X|6�sy_�k���u3���4vT��c������"��G1�
���-*�����A�U��}*��"C�
��9c}t�F�B����"i�&0��
���A/��z��4\���k������?_��X����i-��@/�0��]������0�&DQ�i*���l������zlE���q������z$��@:�h����m�k/*��B����?���x�7����3]����3����,���)���<2<���3~�����/o��:��{B�P��f�o�������m�_���X��
$`���Z��e�_;���-(?�6��|������I�%�z����e���2w�Z���YY�[�����vkg{�������P7��&�#����S�c(cDc��6b�%2���K��}�?����b���S^<5<��U���xyq��x~y��p��7�g���#|����<�+9��t�G>�a�+@v����Q$�7��C��v���d�/�Jb�w,0�yk�s�V����~���^��6��6H�o��Nggcckc��]YY[oo�o��o���mh���`���0�(����(IJ�U�j5J�U�,?Eae�k1��s/�@���K���,��jm�pk����I#�p
�<�w���xxQ���Zx��#�t0@�
�#�,�#�
�`�2�W���E�
E���Z��6S����9�_}xs}yxv5g��������$������N��B���5�%l3�k$����^~R=ES`Y�����qdVfU �=o���L��32222Ny�����BU	���?mU
��,�L^b�(��������[������{o���o������pr�><Y��*�;{0,{^�U�T�b}4n�[�j�]������7�]���
J��2�*�.��S����iP!~�;y���A�6~r�=�w..�%Nr�Kr2�'}�="�x�f���M�/��i�9���!/��5v��st-�G��T��Y��Y��8Q��	$���+�U]����QX����`#����Iu��V���u�G�H����?51�Wq)�������#hPk�jO��b�6�U��p\lq9�
_��(�i�b_��RR�O��14#8�k�]q>){c�zs������K�.\|����Uw%�
o�m��)*�+<[������Z���i(��/)��,�������^j�+����*�������^wk��p�CB��h%S���]M�R���b�
dy��
P/�1��_�@��,:#"���>1�s�a8�`��y��������������Vx0��G^�R���9��a�RW6���P
E��*E�!dS�`B��3���x�r��W�R}E�;���2�ozx�%���L�!�j�Gk�W�1�Ep3�H}��0��R�X�.�H�"�WI�g��,U
T���V�L7r2���58d�QG/�#�%���������x��L��T����l���4��-�kO2����o���W�k�{ R=:?;#����I2�H��i����<�w��r�"��
8���*72���o����wZ�����w�����u�b����S��Sxj��_ H��i��mot/O�`����v����)��_��^��- (�p���
	�PF�8��^fH9G���q�
��R{Po%Xk�aB{8,
����0����yo��.�e���g��`SnD��|%XD?c�	�=��s4���/y��������5������W��!�9�����o�8;,SG���
�`W�������2���4E�k	����l6Z�R�5j�%K��v�R��%�r�k�^�u%�<KB�C��E���I��x��s�4�=��w.Cj�������0�r��Q�;�3�� bQB�@���b;���_(K-�*}�T:|A��X�I��I��� �fH"��
�*}�d�!
�����/��������/{�����
ut8���+���>��q�2�Qhco>$�P����&�)P@��g��r��/A;!I���`�YJ��d�J)�����
��X������xE ��-�%D�g���A��I�"G*�G�G���\���	9IH_�t�Bi�2����8�6J�/T)���bh�
~��h�'&SU$LU�L�'�j���VQ{���$/e�����#,�����&��r�p#��2����2�I�$W�R�����E.t�G�]\�pY�t��1��E�_���g����y<�zg�
<`�e_����:�]Jl/�Z@>�B����|���Q�!w����d�e�9j���vu���q�Q�7�v���&d��b2��z��2F[���1x4[���/�O-.A�^��-��B���]@�.�vq�}wqy;6]�Dg:�K.�$��0����g�j���L������������ jv��k0�t!>�����P�PF���|5��<(��7�P~���/y�d��!.G�r�[Bn-��lN���1����yj���I+���Fr��,Z���]*Zx,�Y%�Pn��?_q�L�`�ER����t/ �(��S����<�L�9��K�r��bxe��A��e�#8����
k��{*Ou���C�8i�|����Q`"e���V��c�]�q[�`�>���_(�BO�+UD�"��2�/B	a�0�\���}�)Dv8����C��w�Lyf`N`?a��}����J����`"W���"���\UC�!��^*�Z[�����	���	4���#
Tr�"����L�eA(;��xs\���|y�����t�W�b�����WnK��,F�'�R1)F��7�6�����V���
�s�w.�L��=d�K����8����L�wN'���D5����|�� ����/�{�wv���
�����{����+���
�zIT��|����%x��M��YJ��y|~v�SAr�O���T�d�mf1v,�Vm�����Z1������U
l8�w��w����$7�������HT ���`��;�g>/^B��;�"D�!<�xqK��nAWx����5Ml6�@�0N]a��*�&z�\�Ds�3�%2^0�|V��O�Y,�a���I�||H��W\7A�*%� �`OP�7E(v����7o�B�U<\��*W>��j�Z�J�\,z�T��v�����N��M�������Ni���[��>���X���T���������`��b��L�!��j:��'X$��/W�;W~�}W��9�Q�m��]�dM "R"X,>���p�-�M�E�������rx���B&g
Q����5xx,	����W'��W��sqp||r���������SN��C���r�H�>	�_����r����1~�KZ��<���4����c���(q0���}��������&|�����_*_�r��,�����X,U�v�+5��FlT�d"�*��������z��%���gdY�;P&
�x�AA��r���E/Z����la����&1
n�����C���A��C�
�>$@�4�E=w4Y��dH?�5!�������	v,��~���"��A��
���l
9�c_���m��!�9�[D�g=�-��}0I��7�CA����6���
��>u.N������\96�r��F��S]��H���;��h/�k*,�l3`���x|M��K���J�&���U�p *`��6zFav�s%Ql3P�Ex��B�j�V���@�N\��� ����S���Q(8B�r���d��� d	��7���M��0�BN����I�L��

�;D�T��[��P7(5��0�u�s[q0�e}k�A�����nB��2X�G�P_�������9n�@y �aYeV62h�v�r~���{y�;?�(Y�z�!�G0X��+�R�B������=t��������T�r��H2#���7��r�8P+6���{*&�������2&Q�(��M�����}�����3����BR����p���{����m����Q����eQ�1����0cL�}�9���ne�- l���P8���
'���)<�'!�E��3���CNG_R<���*�7s2�����E����x�fuTk��a�k���fs��j,�����8���P��1������D�O1���	�P�'R�4|��^�V�=�.�=�)���=.B���h��d(�4�P�
,�/��M��/��^��`
5
�P/T�_��~{��\�,E�E���9k�����#^�Tr��N����$�'���o�(���s-�z���H	c�Vvvn����I��8�c}�� =y�Wo���L��y;���>e��@���GO�+d^[�d�!�����"D��N�}��|�)4t�X9��A0}h&�=����������~�
��g^�8 �I�=����K�,k������V���6�py&����] |w�;���A�jbg����r�Z*EH��z���F_���'#���S��X���2Bl�XdA��PL^�45��y��l���!�SU�
�	�:N3��x����"#����d �|�;��}<�tV�D"���w���V�6���WG���������Y��d�����u�M�sI���g���>�Jy!��s��k�!�:�7��$�W�t����%�jtH�n����1��~dJ����!�XJ��Z�A�TF��W,����`�j�������\V����i�L�'*?���L�{�����D�HEo�07Z������|[*N�"�q�}�}P����BX7*��A@���[��9n4}��T�b���u����cYmm�[�!������c2J����6�<�xz����}�A����>���L��*+m�1��	d~��i��c��nw(
�=x�K6n��R�A�b��b��.���-J����$>�����d����	�������kH�7�&#�`�`������$G�iS�I�*lB8n����n������q��{�W,6|����fe�(w8�?Br�l��`�J�����J��ZT��X����������T��X
Ti����E�Tn����7��o'�\'�1�h6�z��X7{��n�GdN�F�q����4�J�p���6�Hp�L�I{r�J���b��A	�`1��
ucxY%�HlM�5p{l�8Q)v6%� s
A����(yu�+wL����j.=���'@H��[&��������xQ04#����1z��:�(�������i�`(����zu*�m\1��L�1=�sJ|@W��b�+�D�0t���B<��w��GIK��,?��3I-a�4	����Z
��Y��b��J����I��������C�I��S*�x�t�J?�D��
s�����j�x�j��k4� `7��&�AFW���j����|1�p��,�9HA��#wA�,���DoOc���"�d���&1[����_C�MC.��~�d �"�gF�����rf����C�>Y����B��Pi�q�tA�f�I��[�Qz�x��l|P�WC�6���	�����2e?��c%�K���+Kc���G�X����,��,�������5��v���X�8Sa+�Y\��yFE��q9�����*7[\F�,��9���4��3���Vn�6�h:xbZ$Jf��fg�&���X�vg���fsj)W���=��B&����UJ�[����J�\����=�����6
[����[Rnx?@�E%i'x�oQ�*�"�=��"5n��3a-;�������|j��C���)��b5��F@-��'��be���9wJ@�;���jdU������(��,,���4��K��~h���p1�,F��xU��3��Y��d^c�mi�ob��<����J���@�K��>RIy_F)S(t�l�����N���]-$2�G�jD������q��r��|Z���s�P
T0`\��
F*�:g���l���U�u�����$�#����X�����8��c��M�Qx�%Z�|��%Q�U+pD��d�'����<C+����>�Uu|��h���0,��<;���%d
W��]���_���|MV�u��s�?��;1������N��,Nx�%�;���o�G�2N���1/����{$���b,�%X�����p�e�@��)b-1O#��;��Q� %[ 9x����h��8z�����.�,)E}H��)Dd��D^�{8�x�,��
��G?��0��S"asx��H�MF���k���m�-�{<��:�I��>�����9��.�����A�s��H�K,WP�_����p�V����/���:g��|������z��	&p~	#��G?��������x���q�)���=�3�.	|�gr���!N���V]�����������@��#�$���*�z@� �xv�V�vYb�k���b�%����g D	�n����H��b<H�p�X�r����`����d4�@�=H�y#���-�J*�2�=L��"���
��X��b�#3���s�H1��Cf�5#�|�p��7,$p��o���a�]���������m�W��e�����)�J�C1)�������kT?7��~s��m�/Hz!)oH�Nd	��-����2"M�x~��Jn��%T����1v��<V��e�b��*�>�?��5�s�$/�r.u�?���\#%�O��T����Q`��5_���;�8E)��a���8]��G1�`��<l.��d5R9��a��b��=��������d\�+�{3�`�C��j\��xR*����->��NdbSB#��M|b��}sf �<��X>"��r9�i�}���r=j#6���^9�L1�cl���\��JYK��A''3A��{��I��~������B������4hgy
Tsy|������{�{�� ^�)�LN��Nk��4O�O�ZYE��Q��e��](F�Sd�
&l������2X��@��H<o��3���;�P{����!�E�L�g�T�tNE���ME��o6���$}�hvg��5�`E^���H�"�j��+/),a�W��%��|��f���N�����n��}����+��H����K���zB���3�����G�G��j�`�G����4�5�iC?�]��Kl���I-X��
�zg����m_;��^�������}I�
)�\�n1�D������ab6?t7�Q=������!Hh/I,��D��J���H��v,}�E�s��X^c`����j*�L�\)K1���T	K%�T%b�AQ��JS0@jy��w�`Iw�H]\0�
j�(���*h�"0���,dh��}n>R�LED74n�<�U�dYF���\[x�*RU��Q��\�V��;�X�Q�;���$�qV���:=/yD�tu�33���*���	����M5N�I�������%����	�:�����Mp%��.A�.��;v��8[������:o�Y�����=�\�k��t��D�9u7p�W%s�;o����������p��
FT��ZC�6(��v�U��F�v9#��������C������9(������U-����&���0�}<��IF<a^��O-�84�1	���0�|��L
K�Ae8���b�]��U�]/9��g�/Pv��l��f�m_��_����%�u��
��W`/��2�S���7,��;�5[�}�&/��$��S�mCLOR�s��j�x�^0��H���y��8����5$�������_�����B��z������������k����Syt����6�n�_�um<X7�}��8#J�R��ne_j#PG���~��bbk�J7���[[�J��[�j���h���0j�U�l�z����I��;TOS��(����^(%
��T ��S9	�����RP
Md������-:���Q��a�--�\V�CnZ
�
I�A9oG�� Tf����g���{��vC��rkk�5-�
�!��-�O��Y����=<9�$|��,��������~GET���|���}���(�#b���Q��tl�K�|-Ih8;\�`/U���je��Y������u���U�3�`s`�8f�vt�F������
V�`��yaJp�7��h�=��������������?�Y�F���
��,�,8���6��u}yAc���y����W�c��o�Gv�Q&L��F���U�=�&\7eACw!?�I�����A��FX^on���v��nH�!"��Z��T
a=)c�
HOL�2�+y�M������F��Xx�Q��=������=y.��!����^��_�����=b�(��H@&�h�� �����<g�N����L�8��9f��U���'���b�'3������'H�/�'�O�����k���"�'\UdT����m$���<�,�$�N�IN����u�����M�^��3�s6������D�@��A�x��k�����"�/��&!��& y0[�$�y�)�"����FL�:/oI��e���2	��%�3:��j��"���<{�`r���9yt��bj������Rp>�#=�t_�w��:zu.����8��7
8M6,�9�b3c�.!��P����~%��8�P��A���d4���s�|)���y5��V�y�`�F��d�qh�A��J
y��AZn�S��r�����EVjFWS�S0]M��Ntf�t�}�-5r�2���z��,m�|X���V��s�G(Z���ON���cj����'j�N�"��h%J�H�:���b|[�����xgH��������F����<L����`;L��0���#����o�/�A9���V���+�3�YN�z4�
d�n,�����������?�Ca/������7�%���Gz{����l��3�*��a��^h:p�����\���LQ��=D�g[P:�+[s+u�������30�gP�������&�������\9�%n?5�S�m3���1��]���P;��cm������T kX���4�ClY���"4$gV�.�`yN��I�0K�A����q"P���5{e��	���b��PM�����1m?��9q��Y,���nC�l�0�b�e��cn�\Rn��`��=��JS�8��{�:�(y���D����I��s������ov�����0��������b\�#���QO������Ih�ya�U��w�E��]�PrVS	�E�2w����d
kfcI����RH�����O�W������<'���;v��goO�y3�:��6YA���o�����%�QIUlJ�E�t:��c<L��k89��A4a�5p�}�c�K6\?�����/����?"��:!)E�����b��W�o.$� O�(��G���B\���~��8�wl�eC
�e<��u���5���X����HL���#'���4��/�U�q�,R�Ct9l��5��	����L�S�P2AZ\���p���f���5��4RS�'-��1��'�*iD����������#����k+or�9���WY'*�1����&{1YEF�9��8���5�����^��p���������gH=�fc:��7��������pg'�T����@�|1����6���SB�O�*��Q�>*�&Z_�O�nO�������<���.?J^s��u� ��?��K�,�uNtM��h��4/�@N�����
��j
����B;�{�{�������]-���0M�G(����������*@	�u �"��7y����|���C���|��.��CY�p������ZB���,�s����#0C��"�Ed�NK�Aa0�� r����9�g�
e�cy0���*��):�a ~�o�:f�c�=��������FrO�	xf>}��4��� �K6���o�:.�A &�����{�r)�������5�w���%�����" O����WpF"�=]}��F�e��(���d)��"�"�������R]����%9�����?�?D7}v>G"0��*�:����=�b�|��/��h[��^��"�E:���(u'FI6��(fP]"BI��ow�x��q�uV�����Y{��w�	��8�r��+n�N�o��E�����a�@�yV8p��U���
�	Xys^p�	�j]����D�����hp����D�V
aS�X����L���D��+����]S_�H�F�"C��n�I)�g��)/������t��x�K�4
	}6��t&�����k����!6gc�������q��q�i(���I���,��-�T������p�LoT�F�V��(>�L�-Z&��z|D�Q|��	�dP������F��.���I<�D60��w�"e���nZ,��
�.���e"��x��(�r�O�"0�)P,�"�Sz�H#'R\`����9�2��#����[���2Sz������~���,�%�����L�~�>Z��y�?�^w���O����{%j��q5�kS�~0�4�^���l�[8��(���"������b��2���[�o�������Eq��m��;�0~j)���������]�_Z9�3/e�Df��	6@�h���y$G�R����j����\
|o���y�y����h]��f�T5��D[��P;��V=��^
)�I��)�'YRTd���	;�>�����S��4Au�*�,�tt�a�����>w��A��PO-F0�D-������#C�"P�zpYCb����2�.��=���M�lnu��>1�0R�
�1tB�<��Zu0���?��	
�o��d���C�^���?3�"���2!.)������z���9��pK�?������k���hyP����`��t&_���dcFMl�3���_�\|C5C@�=�QL>�6x�Ae]��� 37�CK!�=6���[��!_�P&��T('7��ac��w>��������l��b�cN��	"�0��sy7bk�(�|�@"K�X#@���V�{��]l�#��)�~����k����4�ww�RCG��G8ac��A��Q���@����/��"
'�QM�w'�4���|����6KH�(&7�o�I*"�9����BqR��8������q'��C�/9�f�C�X;q �t�&�h��h����_F�i���p�2�[����T9
03��sV���M���	L2+
���Q�a�L���X\�����e�U��:{q����I��{v�������Q�����b��"�s��o:�.���>/r��=�����`On�1yhv����WG�r6�V�z�=:�\v�^������$F�T��� l��������I������\�������,��
T��+9�lTCV@�(�v��%����d�z!,Q3�%�5i��pg��kZ8ck�(%�c_o0�x��mE
L��~}��@�D#yc_E�b9��	oI�e^���<1�I����?	��y.��:�c����?��a#y#	EhJ�y2�pb;��N�K�
��P4[t�p}�8���<�P�k�=[��b,:mdnZ������:YFztx�`k(�DY��[����D>�]D9��#8����,
*,;��t����6O�5LR:�U"�T���3\��@��I5 �ar�^�f
2�r��~��G���*S�7�u�HLv,�{��q�����6�-p
�}N�_!I�=��g*�������	f=�(�����+���D�����X��IK������n0$OQB����#_���[v+'��pE����'��9��Y���qBW�Y.-��X��@}�����]�Pj[ �����k7���F(�nS��Z8e�U�����>z���do����l�71a-���t}�������3��Z��-�[�E�����a+:CoHy��~}(Di���N$���_r�o���n�}�S9T!4�0��d!�%W#VO$�����J����`�e\�LG).�K���%h�4 ��L�����G2~��1�`�Kx,On�~ofpx�pu����];
@>���3���cS����������bf�����(��Pt���N�z����^nDS��Z�M�����	v�.�1i�X�-�D-���������Ull���	���\L$3������\�I�!��Bg��%�>Jl�x_ACPRC)lI#3E�I�K�17��9��X��t��Hup-���M��g������hs��
��,������d�i�f��t�e����\fk'���UU��z��@'_�����������S�x�3$]����(
�ZH�����)r�Y���D�>��3i�g{�*�3�C��-
c�	B��@�t��l��p�.�����N�S��Q��TnP��U��De�JV��w���7�J��33�r��s�4?M�j�:�6���
@����p��1��a�|/��Bi����4p[z@�$
��9���"�L��|��A��\�� �`v��+�4b�w�*��5��]f�)UQ���c����8a6����j�n���Lh�L_H���%�v�����(k#2����)1X��d���B[��p�c��j\XsZs&o�ZR�9���H���O������7q�9J��<�j�^���R�X,�jeoPk��5q_\m�OQW)L�Xj4!��>})�1F�m
��<!A�
���"��
�~�/)��,��o0���W�7�2J��W$r6+> ����&�UV��K��lx���N�]����)�!r1�&��B�(S���"�.�����GK��=c@!*lMV��&��or"������������FJU/$��9�jq�������0����^��~A�2v=�����sj�@�����do��
TK���1�{{<���z��������;�P�H��~���bvm�h�	31\��y��]��?�f������U�����?.�+p������.{�>�(�t��^��~��������\IV�{����v1W�������O���������W�������a��'��$(�Z]
Z~i4l�k�A�X��j����F6�J#C�eQ�(d��Ay�M�/W�d���w��(��8y''~&a��~yZ�\t.;''��~��BB�~y��st]�=0��]^�5��KC��c�Q��	,�2�h������%��;�c�,�������2��k��������=����'�w���j�+#��m^Dn&{	�,`�
�Z��L���,�����:\������~��� ��A���R�4���8�+�X���s����gn�f�0����m��l�
N|��<h��rg�%8��Z}��3zpR3k�I0Ow
������,u�����&�g�c@�U��d�'���d���sv|�U�a�#���R�
�`� ��^$]�(5��X�Z���f�^�
/HcX+�#oX*���v�Ro������Fz5�����)������o�(Cs�_,������_'�r�Y������di�Ps�������� ^��W�
��~�[	�-���6MYo���������������B1������5=����[	��Q�������^�L��y���bqr��k�M)u6���_/��C��_k4��R)���������4�T��������8��>��]\uO�G����W��j@���jz3or��?��X������4�{��{�����H�!����!:g?�\�������;3
<z���##�r�*��_�yH)����+�YB>Z��pY�����f�S��������K����r�B*���w�+�w�uE���eom	�������t�:X�r����l0�giX�Z4!�Y���k*K���_��E��+4���>+��9��|#��	EuA�z�Nn��JQn���������X�r��R��\>,�����ht|@���Q8\�;/,���X�P�[o��QB�,�����g�B��[�T/�r������F�*��J��?D������LW�E���,6�yO�����8���������7*W�q�^�����{�J��|���T�5q���?��(�����<
����q}N�H�(��%�*"`�����"����8�/v����D�ZQ�f�P�Oj�'5y�������

����/:�Go$����`�A����1eqw����h��$>�����$�B�:�����x��J>�E��d����"�a'�wN��7/�B|�y�.(VX2?���<zrx8G
\����G��?�~_=�t�<��/�O?�����a��
J���O~�7.���u����T]��EW��V�z��x��JpO�`Bf'��N����M�#�]�j]��
�P^+CbS��%�eW��K���=��w;���R��'hAbI������wmf3s���W�z[���^����b��l�G��W�m�2��%3_���*�@�g
������'A��G��x�?:��C�����|Nw���#��w�F�oO����!H�yay���|�0�:D�6=7�`�|O^�����=�i��'���<�K�5�^p[r����h���8|#r���������G^7�����#�LR��'G�M����].��|*�{v��=��2z�����m<�|��_��a	}���m�?�U��)G�$#o� ���c��a�x�;�@�C�Rz�n��!��-c/���ql t��*@
�G�fh�NL�1N ��I�o��|��p�\c�����y>6���
���h5\���v��s���x�^�eY�qI���P�Ucivc������L��L�?-y��"P9m�b5�+Z0A7mXe�m�y6����B�exF�A?�2����#�K�Q1���(tL��FyXqJo	(�$�2��| s�	��C���>��[��(�@��)�0���?����#�������p���P��0e�r�5sl�������QS5oY�'�~&�x��E���%/�^u0GA�s>i.��/�Y`eg�4e����4iV�4-��M�����������Q.��Z�X-5*��_m��5�m�!���,���F�\�(c2u��	V��vU�R���H�����������+(Z.?���'����'"�Z|u���Exg��K�	�|F��)a��C����r����q�a�}�K,���,�l*n�.)w����U���F{c��I  �C�3�|_<����3����p|y<�{��b��'q�q38�w��R�2�Z���4�%�>}A��62\�f�T�����7Vu"	��������^��Cfv�@��b�~�Q�#t$R�!�!��8H2��v��o�gj�`�['�E�c����(��MGeC<J�9,�[��q�b����Pf|���IA�`3D����*�z�Y���:����y���~+L��L�`l�h�����u_����[�3��"sl�:'/�p)�o��Tesx\'r*2�@���!#mn��\nI��D{%D�3c�(��Sb>H�D 	�6�(�
�9����8}u`��g�3x�}@��W�����3���Sg�]�(���Vc��c��=������%���>��1�2z��g�:��`�x�iK�U�<H:yS���y��� �g������y�;���g��u���
d�`qd>��5�
5�#��4]k�j�.cV
?oM�)^���!3��������UQ���"\��0�Q��!fJI��bO-3h�����Kr��b����s8��h��n�K��W�����)8���U�J�n(�tB�����"�!�0�p��o���#�4gSn�$��9��v�j�>���
��0�wK��>�������@�_1��Cf�]��E���8�����es�X�1�D�@,�9Y}��|��7��x��'<=��F�6�n��7�f��U��b�����V|)]R���I9�b�pJt�p<B�q��wx�yf�AD	q���R>X�����K��-��H�B���7B�4�X y�Qa]��K#*�6��>J
>u"BfO{�b=)�9�E�#6�G4
��n
�h^�'R#I.1[6g����X*W�����W�7G�b��*y^�<n�S�Y�8Dr�{��khT��!zrv5�Z
FD�Va!�`�iA����V�`��E���X�����G*���y��P?1�������$���-��*u
s
������
�z��N):�����&���F��F�]hT��s�E������U�0AC0�v*�
E�����]�2A������=����p��QE����iI�Q6���5����[M���#o����f)�bY2����
�+iX���BM�C���
��=�qb(wKK���
�E
�m&i�L���&�V�����C�
���^�-���\"���'�V��e�.�15BcfM"���aF`�.��
��rW4A��o�|��B�1���xt���A�\�z���t���K{
�.�>�0�/��>��7�i|N��";������������t�n�����LW�1�CN���O���&��D�v0�;��(G���#�^Z�?�O0�@�=���>xq!�9�de��._�d�"-���v�����fI����r3�U�������\O�����V��K>9	���/��qlT���{N��#z*("�L��*�C3vL�8�	R���qIq{-���w
���6�@��t.��t����Q����*�Y�E[���n��������Nj�\����N�_L�� <@�)�t����	����
<�X��lw_]D�\!F�� ��a���x�)4P1�$��m�jE�O������@��tq���	5��4m�z������D�����(�t�����������c��~$�t��nRS���T�P�U��"�DP~�:`;�*��|����LIQ���u��*S�	�!~I(E��k��+nc�B��	F�z��J{�4 ��E�B����.����&NL������/yG>	��B�G�V>*C���-K�s���Co�t2�lrX�6f�_�	F=#���%y���88%�G��g���P8[_%�e��� �;�����QA,�##�$G����&�d?X&���3�8c�������l�.���"*!����'X@sl�6��o����.5��%1������Q��m,�������r�YI�W���S�����U��Qu<5*��������?���_�Z����j�G�����I0]��x6������D2��"~�]Y�����8��������b�T��-�KM�'���(�K�� �`���)�����YHzY&FJ��c�����V����eX�h_j��q�X���V�oW�a��m}k���r(��Q&�����}`�]���Z�A>x1�X������s��a�e��#��\������x�h�o�ylC�W����1.��\���w��{?vq��������o
��~�1���q�+���������tp5���m����P`N�ypB���#`-�o���	�_��K�����?���f�5l4F~����P��T�f���y
��v{T77���� ��x-I��x�o�W��R���B;U�Oj��_.!�W�n1�
���]/��Q�x�%�I$?2��e���������v�C=����<���>$�������3��r9��I>���������bz����!�A<�����OU�o�� ���gV�Ct}=�N�j��:$������*.�&��,+�K{�7�H2��D�������AL��H��y�����iV���q�^���!�i���E��P�-��5=��H���xe�J	*����9Mp��k�6UM(�h%	�P�l#A�Q��K���-WJk!�f�%in����`�5l�]m];���4[m�Q(���G��NGQb�z��� �� �y�{ U�:
b�V
���y����pp�-wX.';4�q��d�8���1�������P��gHKG>1�P�(�
6���_�
�kd��H����������4�U���wN���Q�:��h�����RE��sHrL7��N�,|q-|
�To*73�qc��dL�)[�_�Mu6.J�Y�A� /��L"��b�D�`�)���"�A����+����j:��eV���'b����6���b���b\��#uT�;�������su�v��@wwh����t~�h�V�j;�C���MwX�����Gr�6�p�!�L��n���H�\�	���c��#EVg
�dAS����L��)$�z�RlcL3�i9��������P�&�8�2�B�����x�i0�K��� �%�"��,�No{a2�/�&�����6Y��i*����4�P�\r�w}�P����>Mg�T��J&LwE��t��5�U��� ZMy����� ��
S3���.M�6���?������&���� |pe��?����\s#�����VK�H�����T���C*�im�K��
N!q��
�[�Fe����b�Q+���q��U7J��
f
��EQFM&�e�O����-�y�
&�Q��cs�Z��]q��/W��,���*G�B�e�?G����1!����
��r���[�"���4��|�F7vS8���l��-��TW��L���"�W��4)�S���PM=��_��$���3����hy�����
�	�v��P�&VR��^G��+��������}�G���6A���n�[�A���L�u�F��Z��buK�B`Xl�0_B��P�36�]{Z�kg�{�������Q��w~�3~���a�FT�u�����"�A���*nw�x�J+�N,�y(&���J*������@rq
����!�r�w�����}A�_��O;W�����z��T"����������������po>����W,_��s��`�G��`��Q��ao%K�����~�����=��I`//T��z�J#$>c
��}3�����m�
*B��FM#PI8j��]l&��<��=��=���$_���'�DM���i��'XO���LB��R�4��j�v�X���`XU��������(��[LZ����?e%���5�Qeo9	$�<�T B������SL~���	<
��S1]M�T�
 ��^�{RM�E����\U7��*�����	����=�C�Od�C�u��r����'����%�
��J`�0������[���s�o{g��Z����i�Z�q'j��S�����:���Hb&^��E!.�,�b�m�����<�z��2�9�,��ut�h���avD:�U���cTG1Z�����x����,��m�jWH�f����kCY�
�\Y��bX��L��������������nWKy����'0��b|���H	�$�g�������F*�P*:7�]FE����f�T�$miT��J��U��iK��L��(�WZ��+Rg7n\���9^�+�:��/%���m��t���)�=��p��'A���j	�YI���3��fc|�o_|��K�^�����"�Fb:(�?J^�@�"�x��<}}�����A2�����~�5�0��y�=�&(�5VB�� t5e0VP>551���B�3�%
����%#*���n��q�(
X��.�?_�JN�}�w,�E��"��X�XY���&5�k�L\�7k���*�J�:,��AeX��jc��j��q�e1�N���w��mOd��/T��7H�%U��;;7�h���R���<i�B�Z�����!Kd1Nvr�-�]��������d@�	x�R)rgj�Z�����}}�c6��
�G���D�wr7����$�/X�|heFx���T0p�QK9e?9N���rXc�\���XE�Q8@���pE<F���02���^#`�������-R
�l~#a�����.ho�S[8�v����_��>6b��
}u��%��5�	�%�b'���Mo�!>;V%�j%���Ke��y}�3����FPxP�o~C�?#��r����Z��
��	���#�D"�g��g�k�e�U��@��^p����lS�`�t��L��U�i�R�\����#��CR�@�O���&�������
���zY�(Q]|��}���iq���T���v�;��EQ��n������"
��N�"~����<.�?�2�@���& �O��p~���k"R�%V�cGQLa�d.^A���V�5���4�Y���"9K�|�3���GG��d��g���-�3��$IPd���dX������rr7�3���d\=|�V{�c��u�<�F�cb�Z�"��d��[����V������[��&�r�O�>��)0�'�R���$3���@��6��S�[|��it��P���?����I�&��HC���K.n���'�+�����/���]�#S��%����d_�q��������2wI9_�8}���d�:'��g���R7�Xaxf�yL��U�5�5q�u�^�w&6�����*G/w�|��9��{�t4'?��v'qCq��v��|���%I������L4�Q�Z��D9�������5�$��u�apE�*����d{�8����b��8(�����\`���� �S����������^�)�W����h$�_��G��Q�80�\����@<��+_��kA������$��(]�0�c���C����AJ�! *B�S0b�/�,B�"���&����x��whP������7��iZ���G!��� 8�}�8b�}�BR02�\T�@�<fQU#�%���w5[����5,@�h�-xQ���U$ [��M����q�	|���)�?����������c�_�'����gV��
r�m��n`gG��,�Z$�Ia��lU�E����a}�$mZ���7��`���~}�S�{���\��[6�g����w��t�&���x��!��0������sp�^����e�s?Z�xw�Wq4@���,�Y�k�	��n�_wO/�,	���AK�/.yC�%�����/qt���)3�R
R���
G
���SX,&R9U�����;'����>+�8��n��;������Ex�����ry�@�v���j`�~�l���F�f��^��'�G�[�t��6���X,!DIt������I�*8�����(��9G�������)�H����������f��:�c��
*g����$Jlc!��A|��MR��e��y���LU�%��
��\_���0�\b�`I�/\��Q��lf����C�
.�@��_He����E�,��[E�W$�z��@�f��&E��$���n�Cx�+�3�`87�����>F/�M��p��/��pqo�b��sW���KI��\r�u��
���u�����z{��������o/��r��rg.���ue�����V��J���>h����9����R�������km-k��m�4��(-/�0���6�}�$Kt�Z������9����P��/�nN{���	@�30��d�������"��%�,�)��q9���#��-*��\9��a��x�o7�8�H����9�@��Zk�`�Vl����bUhR�+��$���V�)�}a��Aps�,���j8Y	"��|S���8�>f����f�gr��B�E$�<5��
d�����������Q�������2C��o���m~_n�B���&i�����{g����!$��EH��B��e8�&����[xYo�+��4�Q�����I'���y/:!�$��A|]����8,zQ%]��F��8g�i��Q+p=@���} ��H�������@�+��:]�<^mDaT����CM�F�j<�
@#tiU\�z�@M��Mp�w�`:�n��Q-��A����8n] Fc	��%~M�a���W�Z��f)��,�L���r����t�
�����Dd�$�dU�BR(e���+$27>��#��@_�+�-
��&�;�^^��?��4�a����/(��t|7y!��C�D��j�:��L`J���6��~���]G��n���GNu!�(���M�w��}|��������=��f��7�B���CRM�����U��o��d$ n�?\�(_*�4��"��4��
���y�Bb�!.4�������+#B�����H?��W�"�\{j����]�{'I�����?��M��H�vR|:����L�2o���o_���#'�it��3��cq'���:B~�*�f���e��8��m����Z^��(}qi����Zi���f�����h�5��m���f�n`~�F�qG�`-��v�s��~rY�����f�o��Ys�+hW��I-����d�H���Mp4_v��$��	t����'��?���jG�����_H�<Mn��~3.�yy~��Rl�D�.yq���\v���v'
�����)�%q*���������o�WI�P';2�/AI��������4k�@1\��/��7/�ON^v��
�{?�0�/�����;{] KYh��	����Um_U��:�H�;N�@#@p�D���aDA5td���������3�^��Y/YWV"�}���p������DXK�	r������
�}t�!d��|}n�?H
�[Y?�D+�Y����c�7!�gs��m�&��v�9���N�X���JC9�(��.8(������0"���I!�gZ�iB�����JhZ��_�������C�����z��b����d>��C~m��F[�lK�}���_hq�6C|�x��]%�#e���� ���l91
��Nh���f;$&>z�z��@�gK���@�QT��s�N'�*�_�Q�q�J*�^�,������7A����+��Ce��$���u���<�D��_#/8�,�! a������n�f�&v��P���>���Q}�T��:���
6��1��������r�{c|pqd@�X���P�,�>���w�m
��������)����J�7�5��y��W����s��D�p���d�%��~�4R�Y�]�	���CS�ldk �3�]���e��xVzus�����u�n3��J��H�;������`�pDt��z*"P#��Z�����;�����\�����#������^=�B�����u������(l����K����=g>����?=�^�����(1���5���m�
Hl�@�D!cj*XF�~�l����*�%������X�>����0�����t��D��h�#�r�s��S�{(�8�
���1��}Rc���>�7Bp@<<�'��r���d��?��KIK��J63N��'Z�+
)1�7B��0����t�6��
�<,W
��}�������7C��6� /%�W������hOM��$��4 d3$j���H�yE�{�& �)R��+��b����Y���aL�#����[P�2�����&�.�Z�Lx'X;s���������^����F�\�����9� fG�1�f��!�`��i�-^��6���X����������6���j)m�hZ��`�
�ALj"�f��8@�0�<_N�4�R*U��1����I�|��������-%6Xa�@�[;�������cE)�~���\8����A�6���:�:���1u��Z��4�
e������6�(���o*�V�m8���� �e�L��8�N��l(�b��I���������X�J��eo�����
0`@A���X�L�<�}������B���0��cj�3a��6�%���-���p�zNF���pn��*�gl�d�(b7J0n���RU~� ��.��c�(�W0
}����"������*�l��e�,h����s8���r;���'�*���R�0Q����y��	p������OEU(H�l�D���8������w�-��4��k�^�o�8El��HS�5��K�KJ='�����ZEv�Z�B���_���������S�{��`9P��"������bTjk���x�m��5�J�c��<@\�7Z�3��0�����,8��Z����17b�4^��X�RA�5�eTt������f�J������b��d�����X�W�w.;''��`���9��?� ��y]�L74�U�62�k�����u.�@`+���cci�`�G"�Q7����\��H��fdz�o/��j��-4����p�7��(�$��� �->�[m���d��!���)!��+Kr�*�X�Q1���
��,��FM�����I2��D����?��8!i by��/�v"���8R�R�V�v�\(�����N��dCFp����@����u��N�J��?{�+b:����H53���H��;$!&f�(�� ����2D�^sr%��b*7Hd.�?"�x�(ePV���S|��E+o�9����Nb�z���T�Ej�c�
�$]��<������-�6���}��MH]�����*����H�rN��4d'��<��B��W�CN����C;:�����W��'�
�^�/�,�:���Y�0��-�&8K�WK�k�6mli�
[�������F�r��Vf��F��B��^mL��bw�'�f�w�=�Ja��Ij��P�xd�;#'��52}M�����r"3nW��d�<)����X��)�����;A�w��&��$��0��z��2�E��j�Y�`��U�}C��MX
�	}��j:�X���M��]�����;��Z�::���Z��{��������>
�CyTh����K��������4Q�����r�3+�`�m��M���m����4f�h��qU&��I+P<GY�!�+�;u\���z����� �C
����:Y�TK��%=&����LZ����hw���f�����=:��F�)9��g���%��~�hn[������[..Q�a8����	��gfF2/Lz�e�Zj��.�k������y-S)��8�5��M���XmE�>��1\���pi������D�E?����9`�� 
��O;t<��0��'Mz�:�������������j���jW��	f���;�v�����6���r-(��"k�A��1���L��PW�
�����,}�!���zm�Z����fM�QE^��!iR��M�)���i��C���.�!��	�Mb�Zh�m�Q���*F�GUQg{��������,P3�j��B��i0p�VL��r8d���+���BK�$�W�����lX:>�1Y6���P���
6x�-�QU�*O�;����<�V����w�������b��u�j���g�
�M@�<����}����\���#0����VTC�S���wNm��Rne4��Ul��uJ�fn�����#��j�]��k��Y��$�{h����w��������I^+%
sW����v(�_��|����j��m���\��e�S�y���*ZKa�>��-���mE5��[����JH�e�0�q��|�8��]��u�K�ZFk��\�/��rn�|	gL���,�pH�Y�A����K��.9��ZxNq��&[��������
[��m|�D�����6��WLI�&��'�l�dZ�������[�aLx������T�$I���_r��.����f�5Z���Z"��Z�V�l{�}���Av���n��Za�je����_K_7����~U{[��S������P=���1��)��X�,�=1��H�����}�0��u�Z`Jt���o�h�:t_v�KBx�ph�QtN�Jl�����{�����h-�(o �8w�nm_[��5���/�W7������<���]Q�d�-��p���=o2�q�3�y�;���M�����fT(���38�Q�f�T�$�z���e�B��
v��
��M�0U�i�j��e(_".V�(N�S�d����<b��bz�[&����>xm����Bl_l��p�m�MV�(�im#�=C��;�_Z
k*a�)	��W>���w������V�8`/QL�|@�b�������.g\*1�c%�0�&:�=��~I=mA�'Q[#�����8���n>T��/��%Tm����O�>d��H����+���q�P%<�������'��Bd�	�{�E:���2������9;�����EQ����c�3�%��@!�c	y����{�K�y���*�*z��]�c1�5x�x`A�J���;.���F:l��ro��X�y�K�VN-�pI	t��2���4D��LM�.b�3�J�vt�_�8����.��k�i���:���yN�� �7��x/�����.j����AVh��
r�#�?�Co�:�b��!B!�=`�A���<q4�X�k�C��T�Z�P���<L�u}�#���jj�CJ_<�b-)��
ES��WU�b��F�S�-���
���UO�}� �d`�D�ZS���|�����Z�'�9l{�����(�����Q�k5ub��xOb������WjV���,�������i��F�	NmT���<<���'�����qBMI��x=���-H�k��RS���
��9g]a��*=3b&[������-��
���"��k���eMJ�|� )��6���������dM-���I���3Z�:%�������5�;[��4����w����1@uE�PW�_v��*C��������lf�3M�o�m�l�J�:����	B(���

_��}��&���]:�P���U�"t#rj>��#��l��:X7��`?��h0��z�\a��k���5Q.���)��{u��������t;W�>����|M40�c�nyq�c���^�D����Vd�:���{����vm
+&)�"���:!��9�#%�	��XR��}�����T����f��Q~G%mT1fV�]�N/%��*�/�&�s��~s��j���4�r�DP��F)a>
6�����c��N�Z��*uv;z+�w���W�V���9�j���Vb�J����O���_^�1�Pj�	J�R��`b��N4=L�_�%z�#�36�@i��\�_��������<t$��O��$+nG���c��s�c���g.�������'@�qkN��Q��2��l��k
����u���_���(�
.i��(i9m�OrZ�e%a2�Z�c�$]���H�����vg��|b�]����&�~�	�V���F!����$��[N�J�Pmn(�u)_�s��y���=��S6�Z;���u\;�cQ�m��Z�}���%$q�p��v*q���$��$��t'T+����7�QE���u�o����0�f���*���tqk�����e��}����;�+��i��]���z��������$�����������v�Yh��v\���{X�4����*��b�����N���S�j���7�%������	��6P{���z�~���{"��*�@����h�N<�p��b����]����U*4�v��l3���;�%�4�x*b�-�B����W�ksB@8���&l]�}����Cc�[�����99�p��BXr�����7�����W�
�M�k-�@�6���l��^�=�6���_���M��4��Z��V�h�([QKx��m��hH�������Q����R�;B�
�h����x*���/���~ap�(����r.@,�0���Nce�Kt��s>�(\1�F��t����qhk�aq��K$w�0A����jy>���]��c$�;���(�bEgj�Ga�^l37���7����)O;V5�������/\-��E��<��Q4���m������1��\d]^�j����K�c��;v�{��c�j�����v;�m�d����t���ktk���r{�U�t��8����"w�����2���	^���k=��N��wv��������+���.sr;��6Z"X��}��P>L*���g�������nB�"Y� e3�����������'!Q��!�v���(�g�e5������&zeT{8���b����h'�,�Xi�e�Fl�<�^.q���ZXEGf��%���?�>R��g�"����,�i�5�gDF^
��I3��@L!\�K�q�0�y�X��S�p�7,t0�vhZ���^`^\�2�R�`a����-_�&W��o����W%O���<���W��]ir{�z{yJU�)��W�]Dw$D	��4���j��N�Ue�l�jl�v����,���4��o���{O3��.
O����uv�������N��L��*}b���7-1kZo������j*�+�e����Y�w��0)����y*��?�G�J�%6'g�����3v�)���BU�7jr
u��������'�
d9�4��Dah,�6�E��w$���	��3H&&Oc�b�U�	�5�)U��8��:��q����Mr��d2�J�G�
_+��Jk���X#�E��V��*�X���-�`&���mC���}+n:���Z�G��"=���
#\��.9�������w�Eo,�Q�6������bYvub;���������HS�d,��%��#�����X�5�5oT�T����%Sk��%S9����b2�y~�KI�����������hJQ��wQ8zaD�pO��V5���$�L��_���F���R�r�G]��J����c�#��6���oM^�x������9��7e�������7�n#���&?EJ=O���H,�4M���gnMRVy���$����	��]��>q����(��?�t�$ddd,7n��ww	��JRzKn�|7sv��r}[������7h_~4���f��RRZ#�w�f�-^�P���jt�\�Rg��h�Mw����������n�������
�����A?�WV����9����<,�Ze����P�6�6���b�{N���k[;9~���>����RG�-��0?R�&��7�_G�P�2�r�B*�5j?(�����E�(�zv;��
[i��;�N'�~�n���9���=�4��c��7����M��[�Ot��Q��l��7����C7����v���2L�:$��B��;�R�8�a��>��
�k <`����G-|'�������X������!I��}<7�x�8�������s��!��27���Mi��z<Y�pjF��������,�h
1�����!�j��7�����oaT��dp��'��m^����}/_l���_�~}d	�$�������f)�K��[MV_'1Vlv�����OP�G2{�K�����,x���H���u��3�������G��I�(���q�?�d��Y?���`�2N��C�������������|4�cv0I���&�oeq��i|+���Q	urpp������~o,��^25��BC�B���������\�e�U@H����2� s8����$���L�J���hF���/��;�y
8.v����a.�fy���z�us��c&�A�M�z��=l#��=�7���^�����Y�A����V=��$��?@��s�`�+P������W_�XFy���Mv��C^�=���}@|��g��i[B6q�m�q�$|b)	�\t��x�Sr��?R�
:��� ]�k7���tx:WM��x�,�5���5��yMXI��ne/�/�������l���`3��^�U��*uD��MEqK|+�V'����w��������[_���[������%�WM�Z���G�I������y
�iM�9���
���7����*�e�F*�F�����u�o���w��S�c��:������I���)�z�g��u1*�j�5����0��~��<r!��t�[_o�{�t��w��Dj��d�7S��I4��m�7
��
-�>�}#�j?����#r[�xm��-�~�����-p*6����+�r�W#��] �F6����#�������:T�my��E��q�aY)�����P�]���U0�'���
�{y?��+�*�i�7K8L)C��P�p]�W�]T�&A� �Do��
DX>Nf��G�zk�C�*N���n#j��d���.~;b�mZ���U�#��q�W����Zww�_�/j��F���n�(��
�� C��[��b�2��1!�!776�^�^K�zL}K>B��(.�#G/��/�v��q��v������}���Mh�I6i������Jfi�As��:�v��YS�5lZhw������s�1k���u���t	"98�A�-�b���?t~np�&�z�p��U�����C$�0�f�B���<L�gL�6@��)�f\^%���8���C�,01C��A5����$fa��o��0���"	��e��j�,����+E����T�.����1��Y7������F^����i���xya-]�}�9;��~/�����!&�������q�me�Qt=j��9��_{�$Q����m��R�������(����Cd��n~;ZFtA��
�46�kn8�������V�@pV������_N��d"x�i���r�?��k��!
�v<Z�]����Yb���[��u��b�0
M;s8�.L��ZB&�:DB8����K��[���(�]����Wg��f$?����;:8���g����X��K���/3BX�����;`����'���P�{�\��|h4��#j,Zx��j�Z�8��`������z���o@�M(!S����_��L��4�f��8y�V`�������X���J�QC�R�][&����N�������a�d�h��FJR�8�/��J�����3�HS��]���G����Le�b�y��q��gQfR/��9lT�� ^�T����OP���cz�6�~h�L����{�?�=A��t��ek8�C7�j��M��1s!��Ir0���%�]^�����e�hk�aC1q��������A!pi:f[FYAhh<1�	:{���#��\7�m�n�e\,Ln�"��;�2��Q���B��~��>g�ww�0��3��Q]�h����I��j��W@5?�X�v�1�!
���D����j.��5�T��5?�|$w:���Ik���+7Q>Vh��L����J�@��t���'@����_Q8�Z���1������
����u����q*�V*�'�����������N�;T�<�.z&����`���I(��`(�!�|�������ryX�Vsb��.�,G^���Q9�9B�l��mO
�ND�3�4 ��D2�I`<�aj��.q`N���R2�$���W�`��~:e�F��B��jt�uV����W�4LmX�Q2X`�MC����d�O�ER��D���uS��e[.��;ca���jA�e�3�`s`��7�tq���U����������	1��R��<"���..�`b�~ �cf
�5_���-s��r/\�\��<.Q C��&�3�����5��4��1��������\�e�����
������:�a=��1����S�B�	�3�
@�k��5��Y�3.GIH��3��a����tB��������;�MR�aQ��\:8���-T�F���b�h��&��`������@�WSk�~�F���#��!�����;���{W�E�sye�MaI�Y��Y4�����a�#6Q4t��@���$"�iG%���*l]l�Fb ����1%tvK���,J4*�h�+{+��Y����j�lS�q��<��q�8���d
���1V�Q�*x��t�?�M&���d9�w�5d~�l��o���|
���"|1�ra�B��}���w[�����)���&�6�/���|r
�����%%�\Y�����v�20��7�����$�,p%���Zk�$[�k��[u���9���N�}&��;AG�{�Gsz���im��>� ��_��^nF<�S;���������#�<���-�C?�^�[�K,��{��I��
��\L�Yp9%�����������kn'�c!�$��7�aTm@"�<Oa{���p���^������$�[Z��G��Wu�;n�Q������T���#���{�
���W_u�\]u��"���ko:W���}�e���{�w���	W\y��w�6@:�q�?�.������{�����[��Lb�����V�&}x�������-8���n�F��zf�6�Q��	zV=�Gwk�!l��G�x�>T</ �3x6iG��r8��$����{�;8hT�#���U�F��o�:���D�k�'��O��W��	�k�yb�=���,_��E/�FdpQ*��m@0��A������y~xysr�h^
��OG�
��N]���������<5�}Q�{�s���W�*p�E3}	���+�i���:SIH�
�o��-��,��yoh��}�O���C�_�o�����U"�r4�1b��Cb��tB�����s6�1�>K��W�l���,,�����"�?)G�_ W>
��FJ{��C�\�Q|�P�������jg�'#��W�^�iWl ��:2��-zd�zDB�����������Em�Hh�}0�Nj���d���R�������&r���}���g������.�QV�����?@������PN�����q������������(�]b����
~Bx���f?ql�>�A�a@MsvN�����HOqE�Z/oA=�n��������J�����Jl�I��gL�o�0�uS?!���o!��8�L�+�-{����,R������pd�s��&x���0��$������BTvA@�(���W�+��[`Y������_�����&�;����fc/�-�aP����E�2���S�"b�(=SWLIi����/�	Q{�Q$�!�<�'go����Hko,�r����������u�������ks��Y���a���7
�A���Y.$\
�asd��|�zB��T_BI�o�;$�_����.	9�����^�nB�?�WfKA���i��ME��������>���!A-�!�� �*��{BJgA�:@���#/��06�5#�g��)�H{�Pw"y4��iDo�T����7���������O�w������o.�xx����W��6�FR*���A��k@h��{����~S����#qj��Z����w�����q{K��u��~��e�D"��O�I�R`���������(��s+*��1CT��'���dq1��- �4�=��ZZ�wR&���Ju���i���$1"x���h��n�SRz�n���Xh(\��B�X�ak����t�}�"���I��LW�auE7�3���P�B:�!IT7!�3NV�*�:�O M�B����w�����r���������m���$�=;{�_{\fN����)<4��&�
�	��������9;�6��y�8����������4l��`te������cqE���]�k`���"WL���}h�RvK���:	/@0#;����{��vr��Z�{�������1�@���,2 +&�>�Q�=��{�h)w�a�(������������}���U)�

����b|$����S�S�)D�Q�G����h��r����C��Y�-L����d�h�=K|���t�X5����O%��^�T��?��0-�h\�o�Zh����G�l����ZFN�*������(/Lx�h@�M�k[~�,��"l.���hz7��VA^�P��6C!������@���z�B��Z|Q1��6������~B+r[Z�c��@���������	����������G����)���������I��X��?��g�%���ug�P#�P
��J�P���U�h�Cq�D@�_��)'�U>��A��9
�����jV2��,�bm�0�����'����+���OP?E��_��oAK�,#n���H���,�&���a8�$������������ru�J�0Q�b�T|$\Tm��{YwRO�/x$��~��p	t��yo:�AMJ (���zZY{c���xs\�&}Q^]&�@��d&���������Z�+am�]e)����oW�L@�_����r��.o�~J����0y$��Z0I�����[L��2�����k=0up�S�\6��%�C����s'���
}O���W�#U��jU�L#��z3�	.�I�Z��>k��-y����bK��|�f���K{V���i��7��uB�f$A����r[o(��A���69L)h�zJ�Cs1��i4<�T�]�w	�C#-��
I�2�9�Hb��!F+�=�7Xa?l�y^�������L��������������[7�$��n�u���!��Y�x���
Q�N��&���D�@������y>�r�Fd�>7�`up�\�)Zb�@��	6f"���W���d��
���e0	P�m�(��pCa���R�7�>���K+.��ZLl.�=�SE(��m<P�������M�*��:�|*Y���l����	e�B��-���qW����D>p 0#��atm��|{w����/�k�D��#��T��n�����$T��m��w��D�Q[���|+����p�k�<��Q}x!_�+i�
�_���C�h���`~��.t��S3�����	j��F��y��f�%Y��zG�d�-�l�%A�rA�@:�!APL�e���������b�(p�]$}E�+;y�sI�Ef��y�s�*p�������gaD�{�q����C(����o}���d�X�~�q,�vdG?{�w��9!����1&���������b�@��`��#�4/3F������`�Dm���a(F1���	�+�G�UB�~��U����p�@�J66������O�&8?�5���p�����������������_Z��e6������n(mB�X�~�����5�
�C(���]�R:���Amn����S#������t�-�W�(�c;�O����Y{e���W���.s�[xBpK.�����������!�@21�����
�M����C���x`��^ZL�$e���`���&,A�*ck�5�)���~�8P�$��(6�eG��i�T�su��N��&QT��0��yX$,�[H����*]
��,��������
��t�)K��35��(�s�2����9z8�)K�[DO�_���5�N{�j���U��L48l�Y��=�bb:�(F��^��o�M(s�8�aR�p�M<N�ui��������Y�P5�����_�=~�����d�d\E�e��5q	XB{L�F����z�����������
k�vXYY
������V{�6s�fj��!�����.+A>,���}i�
afk������c�z,=���c)aV����!�[�T����Y�HQ/4_�dk�Y�&lPX`�f�����&�yg
��=U���8����p��6��8�!qJ�n�?�mh
��G��h
�b�8^5E�nG�fG��-��$�z])�nA�2yJ�%�b�e��9�D
j���.�,���������w�ab�-��Fg!)���}x�����m�z,��������9�uv�6 S��}z�.U���O��������3�#~�<g�"N��-4
�T��k^v��k��j�7�*�P�T�+:�.���.���+H���rf1��QD�5#������_!%���qt�C~��������K2�j�����m���5������D�6�2���N��x�P�-���l�M������O���W���&lf�t=���c�z�Ob��p��g<��^-���>��.���A�GNRs{���L
����v_�i�
B��S��K��2��Zh��c������M��"�k�t|���2�X����Q��znD��#�O1��-�w.�[�S���K��i���(�	���XW�,7�W��}�5���d����g����2$����v�"�g=�>g{(��z&�<�b����������.�sFW��9 D��.��&��'���:�t`K='����]a��*�9O�mn)��{�n�p���k�@p�k��$��g�@�����/+;�>����--Bm{�������7��Wg�uxD�����)~�����OMU~������5f8�)�6!|��40��w�_���J�Qb������Nf�X
HA����EGYZ�7Wh��X�����e��<��3<X6�6g��,���
��ZJy/P������8L�3�=S���07�	��y������t��HJ�����7~Lgs{CQkvg$&-�%'�l���E�Vr�E�h����t����G���)������-n	}��NY��.l�,�	U�b�1@���C��J�(��Z��j�����R�S��
�I�D���4��6��z#�-�E<D���(-9@�J.����WR�����X}�����f
ra���O8:}�Z�,l�����o���9n����/��>.��
��j-��.��i��[Qm�������Y���M���9Hw_�l���6�N���������;�u��w ��:b9�G�����9"o.���
��B��[8o^�\�H�C��h��>Do?�������^]
9_\p�B!�fU<'���t�,�t��J��=v��(-v%gb���T}MvaEvA=v��
Z�W�}������$X1����||����z�li}a�`G�|0gW�@:���%x�������-�g����)����h�^���Pl5�z*�Dp�Rq��k��TgE��d@5z���LuvUVV��%du�El�-|ow�v6���A����������]�`�Q��$dB�D����b��O?;�+��g"�@�xX<gC�:�Y���c#��c��`�o"�����N�mq[���UCZ�-��@m��u4d����b`I*�
���*o^'_���M0`����f�4�!�Z�Y��J)�M~�
���r���������y�Py�P�%���� d���[ �pIB��T��
SK!�;�
'y�=����Y����d��o�+aFI�`d�>��u�����c�`p8�ga����+K��4m�3��Z�"���������b�600��t�A�9�	��2�B��`�Z#H[�r�]��`�Y4y$��q� o��/���k����&>���D��-����G�=��4I����xV�.����e {�d��B��$���?���A�Y|�o������!kg{����8$�v��P4&Rk�1!�ZR�U���t:�;�|�'8>����o��N����m�,�"���G��-�9����.��f b�Z�#�gTs�?$	��S�����ENK�s,�[^�F�!��2�)k"\����[.�rGr��������d/�E��:Q���5\�D���d|&C�C����l�-a�I@�w?�B�j�6���H�:N�����`W��s��4���e�#�Wq�:��^���W�q����jw~,f6�`�[��Y�DG�r0����z�!�!@hQ����<I}��NJ�Xv��E~�.���� `}Z��m�,�
�
m��%=1�X�����vTS��'N� $;!��>��wC��"�	
Eha�;���Hd�+���o)���t������j�X��Tl��0�����t2dD��D0�;��a����?���s����2T��������Cd���y1�6�j)��~�lo��Qt�:���r�6N���V�i�����G���ST��l�/�d%��eI�"����7j��KV|5x���+>`�T^e��$��$�|��LO������_����Y��V��;�u��/?Q��\v��%m���
��|����#����+!r9����|[���/@����~��]p�0��p�h-��h$m���v���u�8��EJ,�#��{�2�y�����B\��>���������pU��1�sk>�+��BD�������E,���+G�t$oy$��=����\�e�#%���.Pk���`xo%��1<�@�:� vu0��O����FJ�Q����$��:����O��#}��g����5��Bp�JNI�N�����;-|z����ny��;���t+��Gc�]�l����g����W�������	�2D�>�ti�t;�`�	&�v�QC�9��J}�>^���J]�e}N�+w��]v�����88O�U_�����Z�-��*u��#�o�*�;�V���9�z�i�o��������q,���6\WFA������K��������9�G�Z%��Ha�`+���
U+?��NE�r���	����5e�c�7�/�.���[�Y�K6��M.`����y�Z�\��Y�V_\���9�Ba�&�R��
���(��=��D=��U�wEN���},����*���(��?�M�����g�J�$k��m��
�����:Xn��8�(�2M���z��
YV�H�s���`��6���=yu�.�`���K��K5�(�cd��v�2bP/��6�l@h��Be{��Gl���8�>s�a���=7�a�,�9���-�8|����w����e$�Z�M�J '�j�J�a��_.#V*��*2$'�
!�J��hp�7\���R���L�b?.�b�?�T<%��S��!�As~�2��6������
���b��U����J����[�S	��Y��|���y9�-*wT
���l��~F�M��[�7�Pl���3�F�n��4g^<'�%�`�W��L���F�c���63#\|��e����m
`'�f����Y���
��)`4�M��r%Ml�i��J�eS��$���6:V����z�*�q�$h��_��T^��D,�NF�;F�Pur=\_+o��J�I����
}�m�=����SV���{��^�U�|R�xU<q?`�?B�������t)k�j�Q��l��2����@�0��o��O�!�����}���.,�R��,|d����8���������n4'��
AJ9S
WI��4���������3����@MN�1����$���$/'��zRt���l#����e6����
_F��q�ed�?��#�G�X�R����#;��_�_u������D%��r%�'�	*8+�g�����r_��)������T#��}�1��.;@�8����BVyN:(U((6�`\H0�IC�k�Lu�	��YK��w��������DS^Xb�����������������w�w<@3��#"K9"~^`�4��t���Ag3�^v���n����togOM�����a��)D�����LV���f�1E1�	�;T�d����R@��r�s����b���g�{.1�����)���E�>���t<2��G�=k�!4�w�41���PN�uo8Ytf�{g��s*to)�K5M;��3l���a�nm��w����������oY����S��6��.
b#^���<?(�.L�a%��	"���y�p���Vsg�M:2mP\dX�����HW{������d�0�^��n���yC��C'
���:4e�A�^�0�0�%PW �"y��
�}�,�e�����^�����uw�Ar7��mz�x�1L�������������J��Te�V�������Z���x�i|]Xt��S��b�������o�@��������F�?rs���{�S9t�`��X����5B���9=���{��M�����8�a��v��Z�������^�_��b����������Bi=d��q�����7�5kc��U�rk�W�pZ��e5is���D'O4���4�:�w��Q��<�&��E.\Je�iR\t/��%�~p��oX@}��=�<��mftx�_�W���0�/��wi������e�����^3������6-�Y��i��UD���T���7[��H?�%2�����=t*x�	���#��lf��S7e�gPoz��
�	�"�O��N@�u;}3�L���=�@ar�����H������z{�\]����h7O�J�k��S�
�#
��^�bU�Ife���H1AJ�q��=�3��g���U���y �\��.i�<�zr���y~quvxJW�b��/BW���?��X]��|���a�	$�&��o�+���?�=�y�� y�j�X�������1+k� �d]0'��MqG���;���AXM�"SS�\���c��9�@�!��7���������������������5���.�#�]"8�����bn�C�4g@G�
��@��@
��BS�u�g��-i�e�'��Mn��\�~��x�X����Y��8$������[���,��w*�c	\c����acs���P��B	�@�'$7(���pw�ya�Qq2d��nx
4���G[�y9�����om�����/��f���6�����|q��?�w�'��>��u-�?���F��.���;]�zh^y6��[�;"t���0����F9\3DQ�j����	{���V}�C��cN�'{��������w�OXl���w�WXkXa�J�2�g����2��e����JG�8�x/��S\^6��3�L����CP"�>O{D�y���H���kE�M��J���Dx�����*��
X��z�3�wzb]����i/6�$�
�VH�[�&�[���5t���~���d����N��b����vwqk(�6����7[��`�?������ld��[���~�;��CI���=Q#����r��6�]��g
�����>?��qvo���\+�y��tr����6�8�t�J������7
>�d ��'7wQ��?���2#J���A��
�"�i�"����[-���HXp�����=8-��F���x�|�Qe ���2=��d��F��Y�Z��I���h�-u�z����������h��5u���;c]����zv��H_x�X�D�����A.������j`���_Wb��R�VW��F�)��eg5RU�D�����X����P���	���p����MTlB&�^��������	�����Yod���a	�������c�@�S,rp�X�k4.5
�GdUh��["����R%�XB�:fxu�
Y���Gn1�B;�n����/��-$n��\�p�d��Yu����V��{�~�n�����>��T��RG��< 1M:)�G=r?�A����,���=-��W�����!��)����|�;�~���@P��lk���dv��4�54Dpf9���H^�U�8b�"�KA�q/��XTlm<ln%�?�	��������b@����:C������V�}ig�%m#R�Cs} !�u
� u�L
,v��#Q��*E}
�"B����YJd���������@���kb�0!����P���W,�T���R5�]v�/���(#�����]p�QX.�����8
�vl��p	���S\VKu2p��pa
�>�`�f�� ��!L0��S):5yI��%S���'Om�5�����F�)�+���
��N@��*���P� Wf��d��\�i��`m��@4F|p�3���b�4'�<�"�b���Ee���������D1��
=)�d��������GU��iW<d^���{�F������)$�~Z�F%i�A�^��0�3?��|%zT5D@R�{+����<�d]�[��8=}&���}l\�4�?6��'�Ok1��K`�@���������������������KK[��*L$���nr�6�eHp���0�1����Z%�[�_6�p�\�#����������y%��Z��.�6@���(h��7*�_���a�N����G(��+��Z|�IE`1JC����-my2��?�ps�6y���_�'�g��m�/8:��2��I�.����W kpf[�����O�y�x��4nY�Z������$�ky��Y*
��C�e�i;�������vO!��4��h���
@���P�j�r��8U�=��O��K�@B�>a=�������K
&�]?n1���������W�B����=��<zih��������j`���nJ[��|>��1�]����L�����"���l��s��0:��������u����:t��}U7T�!���/�����d���<2�����t�S=-v�uKK��]W|;�G�|l�3j;~��_����������~(��W�p��3n?7�������V�1/oH�9�R�s�{d{+|���{�e	)�-�����a
e�	����c����������
%�Ur
7�G��`|�#��~4�X����b����������|-G)uc)n�syJd����#��*���b+�6:u;�t�����W0�h���$��!=|S����_�u��F�P;�bC�Y#���,p/��r�'B��UXk�OIZO�/�:%C<O��*Y��������x%	7>�x��N����1=�DO�5��m�����$��]���%��K�D��������B_C����V�HB��jQJ@������ebP
fQCU?���	�����~������w}f��_/����]L��	t3���!�?�����r��#A������S��`	Tu��XG��q��	��O���A5���P��=W��`}{G���y��!�E�3����]�$����O{$'�0Iz���_��e	�6t������s����#W����{�{�����j�Ql���i�aq4��Jn���MZ���M�Y��]�N�Y�	m�w�d@����sn8�!��sd��Px���4�t��\�,lBA�����7���1[h(v�B�����9y�|�}�U]�"�8���__aO��U���)��-c��9��R��Zm����~%Y��!�����[�>
���K9�P�PT�5w�Y��f&��p2����s6
]&a����e��&a�U�bhU�����*H&�{�e�5�8A�zPE���@������G��U�u�0d�W�a��a���d�$�]�7��r9G�t�����Kh�@LKw�m|��	b��S�[����7���������u������g_a�N��_{�;{���p������(Xd�}����p: ~�K������]���p�ZP���N���
�B���faN��X�������.�2
������D�t��mBE��tQ>f���K���>C����4����F�e"Q��`pP����	H��Pe��^eB��Z�BG�,��F�e�)ekRP����y�p�������aN��������W��Zb��!��N��	MV?�Cf��_���wg��w�dF<����AW���<u���mR6�cC�g�)�u-2���&��7%4�)�+�3�t������w'��~����0�9�B�	��e��%N�)��!����0/�����Y��~K�wko�AS�W��)8�����;3R6��--E�~s:��(�e��bJ�8=��>����y}����{���0�;�$�����"K��q2K�����bh��k�U�L���)�Dd�I3��~`Dq��]H�y�;���N�]Zxf�:q��5/O���=���o�>�����������7���
J�������MC�������p��p��:[+#:4���������A��J0����Q�Z��`�e�c���#������G7�����|j�L	U��9Rt���2��b���I�oF�HCq�������x�D��(�D��o��_I*Rq^�p�(���V����*����h��E��3D�r���JG%F�;�Gah�����#c�yn9�)��p���'Fy�hdo�d��mZ��rdC�W3}�Y��6��nF�ZJ09r;Y���`-.�/�V���j���O����*����utnc�4N�]����/�Y
'{k�4��g���1K�����_^��q��Tt2�����Y7�>��O}y(~7D��p�dS���E������>�X��n�.c�b7�{�I8vd<�$w�J��������62	Fy�S,��k>���
(�.��F(��|�#�oJf�lmB�e����� �7������	b6�� p�r)w����s�bb��E8��A�j����x%>p`YN�������k��N#�M�w��\(�x�V!�2���cu����w�g������l����_�*F�����J
��i�iy�p�o\�R��]e���W��
���#��u���5�|g�,3#j��Mi4�#Fxqi��p���#����$Y�i�fkO�/>�@e���A�9�����|8/|V�PSVj)zmh����o��6��SY:~j��W����Mk<}��[��S_��O�w�>����|������1��V��.���W�>"�u�{t=�&y?�+�!�O� ;�y�bBT���E�����*����	"�\AI�J/�@��<ie�!�wv��7Xy��u")�-C@D=�lr������/����	�WPe�������a����FNL�"�7����$B�=�����kFh�X3�K"�H������PP�����w�W���fk:�zx8�\KF���c� ��V�Vm��N7+B�2��G
�xE��Iu�P7�%�4��:h���"���KK�_E��������#?����V�y��{����x�k��a�X��������g�T.�p��w7�+M����H���jp����.��=�u�B]J|
t�9���J�QZuUEX���p�+^�e����#��&��l�Pu���CX��]r=�3��b �P��/\h�Rk\��D3�	�R%0��"X������"���UL^Zx������������b��W��J���{����I�A���n�V�R�"O	����w��sX�j�d��������`��:T�gF�B4Ma7RVMS��vM�=s�f�����{>-��S���=u��!�Xl������o�]��-ST����!��:�����������-�CUh�n�]Yq$�T?K>^5��'��.<�w<>��o�����r�G"7C��J�!��Vh%�.-YJ�������0d��e
�����h�]GOLW�e�C'�J���x�����w��/�:�=D����Az�K�}e���r}�DX�~��9�}a���B���
�F�1T�����H�������y�wE9�I`i~�����B#��Rj}
�Y��Dz(��$e<bH�Xt*����"���
��R�e\��G=_�s���z���/�7����kmF-l�����<�>�P����u��N=	 ��l�;���r2�%��*	w��%���0�L7���f*�.,�h����Kx�#��w�m}J)Gj�Xz�uW,�\�����y���f�Vt�5������g��{�'?D���*K�2_��H��7�g+S�;�4���F���d������

�|5+�.<�r�<.��]���~�[>�3)
�o!j�8#�	2^���;���w�����3o����{��"�=��;M�vs�G��KE8����Ap�PK}�,�������_j�-��/���q��_���q<`
T��j<���3�_�{�(@`�U����sG��UM�#�#�SSiwF
!cN�#����rQ*����B0���
��^�
,��%��A[�s$2�-���h"W��������"���Z�(m���V���ND���m�����p�����kY�k�(����n�9����B%����aS_��������l+k�`t-����{��2T7�v{.W���(t0�Tn�[��7��:B2�����wr	$G������4.��H���2F�����.��sN/�u����)��]jr)�YG�8���,���~���u�!�!�a{���w�DXTi�9�b`R����\�YR���k�A�B���u��V7�{�3�x4�dU�������i����)��N��~�3}@�,1|�����aK�S8�Exp������c����.@�����L*U,�*���G��fI ��W��������m�	6�kS�z�!����~�k�J�����z��,<�u)����b��m����z�R�+�`�,����p�k,�U�y\��o(�>(���1|�;Tw��.�l-]���m���B�9hU!��F�����Qk��N|c���##C�z��A�/
��zX���������g����#=�c(wV�K�����
�#�c��#�3��p=���K�g6������}8YR�p�y�*R��HS,[G{h�2+�����+NV�w�x��|�S#�3������Z���N�q
���"$���yz~|�sD�����5Wq�V�I�/���!4�������b
T�����{y*�YX�
`"�T
�&��;5WuE��j)�����\�����2��:�����R�j�[�S"T�&D[����l��z�B��cqNo����rc;UK�*]'���J�!�@����)T��a��E--a�������.~+�\E���~��W� ��V�10
����&�g9����$��L�Ca�_��kn�����vx��-����F6��0��&����R�#�#�v�)L(9d��t8�u:J�H>�
K�:���KhQtsL�h�6���8��9z���O~n[~[y��a�Kd.w}�
Tc���S�f���f�������+��%��2�Z_c�
5>j��h��i�A��Z����S��E}�����''�G��U��)("�Zy��CA�I��S��w�^�}s���~�-��;{������G8g�Y�������tXN���m'�|����y/�T��^^|7	��{lg���X�(����������q����Z*m�������B"������D��0�C����zZ������I)W�2Gu���p����g�2O��J���K{�p��_P��	7���S�����0���o�v`)��V�R	���)�|��fox[;n���~����cKW4���<M>�����6�]\��$O����Oa����`a����L�h�t���t'�w���a��J=������t����2�$�lV���S"B<�����J�5��>�X�|��>��
��0��3@(5���JUPU���z��QZ���s�M��22Ky��^R�
�X�o�N�V���k����i�����{�	���T�,e�-���\�+C>�Nm�5[Bh����1����Ll�-;y|���������?Ez)H;Vz�_�i����r�ZD+9S`�B1���sNG���IoyV��u�bH���Hwt��
$�C
zF}���ah�[&�7�W1�C�'}�2��g�*���l���a�JM%o'�F�	���Q6GY�k��2
c�o�C*FF��AH!$s�px�n�`�i�4(l�7�����S��G���v��&�op6�����S���W�h)Y���[D2^	�^����������Vy�U�p��;�$�{�
��)���+�!��N�s�{���/M���N�#���O�}��>�k�J��>�V[����Vy�|c������u����IE`��M>%��,�����/����P&�zY���'��/=���-vxr��<�E��_e�����h��c�`����.&� ��rQ�������p`�����jA���RJ'���'vX�p\*~/`X%<������a��n�mSy�	��P��b��#�y-#=����R�����)��O6�at���-Qu�N����R��m�h��
���,�.�yKp��[�O�?�w����fN�r,zK����(�0LO��2���&�8�et!&�+,`�<����������>�E�8v4���0��I��������`{���^���X.Ex�_q�c�u�?+��6S�{�f���lL!��7e�����>�8Q)�����a������K�4Y���T_90�����.�>0��4B�/������O|\��!���;>7|�HdW66����K2)Q��D

+fYY{�IL8�X�!M.1��������HO��(��=��'-zah^�k�6Ot��=�3����J(�i�-�8@=����.��-��G�x�!������;�z���ObY�V|������\���B�kF�H7:n�-�?����s4�$�w��>��N�]wG�3X(q��>�{_��-I:��}��\��~���RI���)�tM���d��+���uYfS�Q��A�/����F�8���B��A�7�Z�}Nj�����b��C�vX;�hb�8�������@Za�wR�D��E0�
�"�UR�Q���d�5EEB��#�IO������~�h��y�����HU`���j�����Y�n�������hC��_���7J�se�Y��eW_/�����?G���x�~�4���<�>��U��7��(�����Ha�E�BE���(ZX��x1K��/b���j1��>���4�����6`���h���$��R�p���!���/� ?�ea��,s�����
�c����������v#t�~�]����q�H�$SQ;J�L���GP����1=����!C�\g���0�,��+0�wy?+�>�9/l�&<� ���u��|Rd���������S�!d��;7�s���
^����h��B��t��\X��O��m4����Z�O���9��,���=��pz{�
�J	��Y��p�5�f�0J�N�\{��C6�M�W����u�������W�!�
&~.��2}���f�MXg��z��8���6��\����1�9n��	�f��L� �)�"���=D��!=�
��Jel��\��f����o�]�AZ�
��_CzRg��N��'f������*#XK4O�`yR���Xl��3��B��3��,���	���?��|�����Z9sk�t������~��l_@����R��u$���Ejn������[�R�V�
�l��a��_e��
,c.MF�����������Jr��I�w�I�L,sY�����vI)z���N�����&V43����9�*����t������A�R�y����7W)f���,^.�a����Xz��B����R)�'��<�$������n�J��x��3���8�i\5	�"XI�d-�;7���7�H�n������Jo���{��h�nXe�l���>�
d�o�������j�f)-y�AOr�u�[��ul?�<�KR�XS��+�Qi��+��,V�~~c^4Y?�a�/L�NcV�mi��c�SAW�4B83D{~���x*ZT�RZ������Y�a��@�c��>{,�\9��at?	��G�Y^���3�|\���QPy��d���^V.�%��^PCl.��}@�Q�����
�[v�5Y�����Q������I>Y�{2�<��B}
��{T5�\��;O�`��i����t������l:
�<�L�?�����.mz������O����	��d;q�;�By=^E��D���������[�Z/��^F��}�a������ub�l����������|!���5�}
�%mm�@����m0��l�v���<2&�������DJ�M�^o�����K8F9����C����><)���s��Ewi��!#TBU��
�i/�/��������{~�M���>@���?#��N�3�.���q��Le,Y[�5��n��_)��
�:������P�e�t!�-[������F���$�����/��,�>�L�������N�yc0�[]]�c�����f}3Y���xy�����}��Y���M����+�d���&W���r8&��p�0F]��^I����7��	�|�Mx�_��������c����#�~?NGsz��v|�
�q��	$��_��^��y�J�������!�������!j#\����x��������AQEu��8I?�y��������N���B�����.��W�a�m���UGd�I���f����:6}g>����	�+<��w���@
p�k�@$����l!�B-�~� �I��Cx��
[�u|L�|tu�@rQ&�*�b�66b��n��9��
0l�r`[a��[n���������k1 �Q!a
����m2�=]��\b�������b����\0�(����}��;}�����n,��	sCs��~�?��.����}*0���[��l��R����Wz��H�Vuma����0��V������Q/'��F������N";6�b2���F>j���^�6��<�9�xD����W���=m�L�2mL'y���#�2�����>�%�����~`��e�|<r)���1*S?��Tg��WD��b�b|��mm��v�nj���G\!?t^�G�[ys����y�[ ��*$f�����
�\����6���l&}���E�l�
~�?��rs%7�~<�i�^���5���������8��>���[���+�Db�a:�Z��z2OF���4y��z�m��-����r\�539���i��a?o7'��i^��������]�@�����V�5�`k�Sh�kh�	P69���O�<�my�7��\VdZ����&`�j�^�d@�L��#q_���M�j=��i��1�qki�p�l�L������v"��AL���x:��I���/�.����^p�H{93���ht��r�w)���:�
�(��'!%�Q�
�_C���)�@T����Es�u�"u������P"h<!9�8����g�C�\MZG�/��a��z:�����%��:��N�0
��#������h�I�#_��|)h�������+���b��H?�n��\�����6~>y{�h^]��y�8{����_2;M����s��>�%���b���O�>:S�,EnH�4��;G��W+mO�����T�����
W��9s��9C����K�
��/����n3�jpD�A�d�����0���[�XO)���e��&>]
�����;��@��G��]��)��7L1����N�\Q������� My��9]��l����������^d�~���\_���h��B���d�����k^��{0i�xT��	������q��n���S�OX)l���T����N&)z�9V�?�z��%��������!�%�q�@
*�P�)9�1d�b�.8	��Q��)�#D:�����Y7(]l���������E?'������=�~mF3��a�����1_oZ����3W��t��C�����<_�����k���[��$��;��.�vr��c�^�%"�������V��s�o�%�P_�W������y/�T���b�hB�.NT�2��J ���m�������.����;5��z�ld�	�
������O��7��f�������N^������z�:;�D���������#X�l��u������]>_�o:��(��F0S)�:
�Dd`��yF����yJl��!#��6�$y;�8#}�e�8�C�k���:Sb�6{����
i��i�����le�&�
�	*
���?./�o��! ��qx�A���yZO>��4l��4
A�nq}sxu��RQm���f��+�{0?�kN�wT������h+��A�����S^�3�v����4����f����r��������"�X\�������5��y'�����|�@DF�/���D�%L�r��G�����^�*�L%�
�xtq�h^����#�_���n^^5������Ds�9	��m���P/����Ytb���uuPu��5��w�����89��������i:*=�4��4���
=�=\���Dw��i
��zRCat�&�[�t���� A����� �2������J�����b�e�����8A A\�,��B�����cO�d�������x��>��uB�{k�2�G���������~P����g��
�����M���5}�Q$l��,VO����2k����5YPC
��}K|�-1�Z�.����&{��d�*��X=�a"�qJ%Q�B�,=����	�(�h�&�q��d�Gr�Z��n�_E~U�<n�����FZ���]�����	�����58�R��C�5jH��@	�$;y@��&F(���}�k�H}0_�K\�������<H��zO�/oAN�����\{XoLJy�c�b�B�+A�sT�mr���JU��.
�H�����-��]\�V�U��{u�����������U���;��=G�xK�'ZK�(����L�^��<��9��������s�!��?�	��������z��>�5���GG������#�m�7�o�^7�G��
��]R�Z	M��4dn�+��e�GQ��Z1������^������UV��5�>m���[��J�M1��^u�y�����I��x1-p���O�_U+��v����F��j^5P����&�6��I�,-��Z3�&;�F�@���w�������pB��D���t�����lv�"�k�pUkp���G��f�l1���
�3,�;��Qe:��=pg�& ����*�����ZJO�`�gdVp+X�e-�u��z�����J���$�{��������D��s��&?�{�Ly���t]���\��7�?�f(���"���7k��j��J�x�<����!2�8��D�t����
T����9��&Da4a:��j&�N����#��=��c��o�f-O�������^c~����Q5���L�������?���ks9	�<��q�X����<&&�|������H�I.���yPy�>Zz=H���I��@j�0�y���Ob�^�2�_����������J����X��N�0l�{�b�!�[���zW�<��a�����N�B�S+�0r����+o�g��5+>)�I��Y��D�;wO7��0)(�C�)��������zf-� :5��Up ��	�����yM2���W54��Z�M��H��.NVZ+(MB�p��dprW�,f���O>V�L!2��\�M����"xL�Ed���ob�������q��"���V�g�xP���s����L)���h�R��
]n�V�v�M�'[��Z
���o�.�������Jo"�%Oc���r8��
=o�R�������
����R>�	�I�@���|�(AC?4�D�����m1�r1\��g�ul��1����wP�qSe��U���z�@���x*6�p��GC�ap��r�|�b���!Q�Y���quXt��q�e����zCa��JF��nn����2��[W���]#�����9v�����y8K��TUQ��(�Q���!�0tW�	og��)���d{FC���8�PMs!xglU�F~0��`�E0�uC)&�X��3A_C;g�����o���������
R��S"I���a��w�������_�yr��"]�XE�g4����(GY�������#y]��C��,���*V�-�e���x$������`����X�]�Z��p?z�vK:�������4��<X��~
��w���p(8��Ldv���1zNbJ�F�x������ ��f/�S���%V�G��z?�0P��J�=q���U����r�[`�O(�[+�X�>\�R[i���6�7L���E���.��[���dc��U@���C|�X���3<mP3��	�B�����I��N��I"�y�2�-���W�e��u����2
�t���v�C�<����RB�Z��9�t��	��N�w[��dg
�2�]��Q"u�7r'�����!�7tX���y6�f�XQ������Yg*z��C�#��r��D[��x��h%���J�����X3���3�*)�� ��FVe�^'Ol��y�k���5��!M���pX*8:����%�O��J"���SJd���f���&���w�D��"����+�dA:�
B�����_B�]��%��jx��g*��J��t4�QvO�=}��'�p��"�0`1��	�T"���U���-�����T��PZ��T�;���d� x2�v@�����gG�EzRA~b~�Y�����D@���9����u����`��R����|������s�
��s�bmz�g/�5�K/�T���b��}��'���f�L�6#J�t\�6-�7�P����S�Tb�H��������iX�fb����<�%I����0Y�6�~�6�SX���!�
@<T�9E�d2�u�?7�>��YFdB0%�&�P���F-�oTb@0���3sD0\0����+WbKn����_�`�09]�l~��
o�[�� d� ���LP���	���]>��:%!.�3;�Xl�&&V�$;��3�!�OU���2��&�\U_I/��	�v)�}����%�H|;P�:��M��e=�e���g�dT�@������T�>�����@ym�f&�\�>��Jq��Y�'j��~5rW�-1(�lz	l�Zn�i(�6��&��^�DDk�'�au��NQBC�5a������Ag�`{/]_��kmnoomo�4��������E� ��n}?Y5�}�hI	��x(��1^��3��<��0�{<!�AA��P�	D4�O����S������&���U��4���_�`h�6�MBU���,A�C��GggF;����_�(�����
{�}�4?���z�<�zI�o��������e��������GH��!S'Y���d�����>(���tc~���kz�j	0�����{yk�en���]��J�>f*N��/�_����^_?�{�}��`kos���K�)���w�v?������n����[�O�.�hJ�D�ZN4�C�w'���~Ps]$��������9�va�K�MG���������������w�)�iPL\:J�9d&������	$��_�:��9Z�	��f���"�F��P��G�����y!�������p��8����:��"m�����T��	����9y���i����BEGB�S�s���
l�5�_}����#�C�}�N<XI���M�W���BT�W�����7��&�Q����{�����7�1y���w���%�W�6q�������#t3��F�!��'5�Y�G>��x�v�/�G��t�����y*���K5�&w��E)�"�
4�N�����N�<����jL(�Ac"cI�BS�W�c�:h#,���$B2�����,j��hZ�x2Hn�D�	������-����'�XE;�0:�6��y�����I�A�O�K8��R:7��#"�l���@{�Hk;�{?v�ps�^�m"	o1���m���*�Y+������3���j.����E�\�f��rLq[������������S;w�,�A��)�%[&d���!�������/�{ @hR�9���	+
��nf~�O|�7���g����x��L�B�bY�1���)X�k�YSc";�J�����PI���^O��Gd_��
�
�ak��u	����:vz'�8��^��4�uj�xod�������-����4a�n7���OG��{����z������t�������0(��������"��K$������sI<@��P`2d'4D�qIL��eiO�8�m�f��s��1���f��xm�����������[3�1��P���a����i��/��ik}}soo��������V������x;������j�{(_�A^L:1�+��-i�l��D���
������n����&���������}���V}kOiy��IM���D�TY�0\����y��(��a�������l���~�3�j�M@�*��� Ti�d���b������\_o���/v�t��Bdj��I��r��M�4��v��cy��D��gs`k��k��tr��$����R��p�O���Nn�GF!p�L0���������q>�u&������_��?��G� �ge�Q;I��9_�"$����o�np�H2s
�����:�']5A�F��c���~�"Q�����V������k�����|�mm�M�#S��lR�-�\A�MV�C�:��AX����V�����)P_�<�����pbx_:������n��X9���n�����e�%j�hd�`�l����
!�V�B�"��#�1������(�6��S�Y"�2.KKf��s�
��������a@��������g��f��d�<�
[����$d����v�:���[��G���h�8x�_��A������%���l�2���_�#�!�������-a7|�%�B��uYP�+1����W��?��,���`�BlF.%��R��R�S���PDz�C(0���7h4���mt2�0.1�YM�Kl����v��i����{/��v���N5���i�U�lN�$��lp��142��A
�25����G�<oC~�����a1�fed��cx	��*C��������N�
�1=����	<��;+D�l��v�����2W#W���(��
�<aG�!2x����Z����N����G%�:W�&w��g�?���*7K��Q�pE���w�.�o����c�?�F��]���[���Y�1/�
�;���jG���%z,7`*�2������^\������������2�E!�F�e����]8m�
Sq��Gw`��>��������G\�i�0h?Z+ /"��-�����z�N�3�a@������� ���B����}{3�2?������u;,���$�V���H���Q�k������G��9cR�.�z>��k�I8Do�Dp��~��_|{sux~���q�� X�g�#���o�������������|y�Ch���i�{/q��;��Q ��C�������GO������g�"�����'�.�R�<�]M�Y��������V�O�eg2����v���n���-�����{�{������[��jFm���	�/�_T/z��A:2	���#��mw{��`��,'G���j:��$O���L
o��d���������u�����K�/�3,ETu��R������^lt��_����������w����1�
��i
�d�@� �P!<��Ex�xH.V\�b���=�BY:h�:�����u�"J����!1�k���g������-]�
���n�����a�c������!���2���	F����bL���Ki��
�5f���f���a{o{�`s{gF�Q����E?4R���8�w�4A�Y_��
��D���4�����A6�Hi����<i#�T�]z64#�[��S���[O������;�
P������i���`���P�G>�������0g�	jN�`��FGJ���Pi�,(�Ld(c�����{22����`��>��l�T��0�I�W���6'I9�E�j}�)=��)*_zG��9� �b}���*����-A��m�^�oR��-J��G�V�o������������k^T�9R����G�>��j����X!
�Z��&�oO%>����}���z�Y����*pfW
�?xq)��
O�|��$q�����7���p�����MG-��z8�%�d@��M�5����K�����C��ZFB�d�'v����J�<� ��?�K��?����5;f'�~q������4��55 U{V�-��(�����������14��
�����x����,��������$H3V�������m'��w ��-7��Y�S`Gy���Z�!������/_� �Y�N����^^��e�hn�q������*|N�����F�Y7;=97[	�\�]|���.��5���Cv�����`?F<�����`5<�J�~�{�F��j4o>\]|b�������q`l��F^���=8q.���7��i;y1���<�-�����7n��.G���H,����f{+{��^_��������V�=��
:��e�f���B�2�aQ�H^#4��>v
e\u�M�-U	�w����'�*�	�]�������4�9�� ���WR
Qp�����tHV�w_�L����s:�1
v4����r6�&o����� ����n�
T��]�j�W\&jI-u^�p��4_J��2��������W��6Lb�+(�eY�6L���h��k��X_����Afx�aA����m&a�v���������M�!{:`��r����������`��"���7�q�I.��kguR��V4��`����-�V�LG����+�t�����
�n
�P��!
���_��QuGD�Xc�!n!�C�� ���
X�6n�e�.|,�;������v�0�������v�rg���Qm�&���+���"av���g��7~��kf��W8��B#XJ��`���C�CC�#F �jxM���2^v��5?����5�<	��!������/�i=�|F��YO�l�^@���%X��)���H�(���pT{z��� ����ePce�)�����o�?Ii������o�nG���O��nw�X�E��trW����~������C����^#Jq<3}?���$H����s��tG	A�P(�~ur~���'�����t���[�E�������W��	����������Kh)�/"���!1R`�B����:�R+s�pC���J�Q�Z�';%qtz�n�����f_��]_�/.�W%q��<��8�_b�M��)�Pw�������N���
j���#h�[c����{�7�����xt|�����e�
=L���� �opS`�-E�����9�o�*b�md������cU���$���^^�F�js8l6�Q����j��l
�M$y�s�bFd�v�[j�=���>�:��������O�C!;	�>Y�zNY��1<@�3r/L`��M3�BE�
�����0[�Q]��[$��(LP�M���O�2����X!�S�]:���'s� %�i���JZ�����m<]O���L�B����BJ;:�1e
v���_������<�"�����?�k��6�����V�/��r�_[
�
���8
<�n�
�;�:9V����V�D'�T�:N�t�c�����jlYm�TH�?qU����`z�\��cK��6�d/n6�%�Ay~�^/G��]|����)��J��F@*�S��JIc����������]p{�;�K��UX��"w.s��[�q�s������M�
a�#hI�db�,j��^��
A�N���y��l!����G}l..}�])xV���L{��j�8���a��gg�f�\C�my^������e�rx���������l!D�|��Ch/�|�(H�{9pg��xS���OD0N�<��e������,�[����V��G����3�I�"bvCt���.T�H�Ut��E�B���vv���}��uliZ��g�db��3���/�C����5/��N���.,�A���Z�n�c��dR2[f�q��_h==H���Y����N���x�W>)�\
�.]1�^��/�.$-�����Q�������~J��������$M������J#��P����]���O@��B��`a�+�W���5���w�|q�j/���	�b��LN�[O��S��IrB��r��w�f���N�R�w[Nv������'X�X����5�,����2�L)o����\��r�l��g&��v��'�z@�G���7�K:�(�`2E+��?O��L-������N������[� +�V�.{h���+0��"a�q(������%��T��j�9gX������g�����
�B6���b�a3Z��n7�B��OE�|!�_�&d�I���E�;�P������~9��]�"����`�:�9���y��c�G���������r�����`�{����l���c��xPW�����������e�'��}$���TH�t�������=���!����u�4�*��
�W���j9bR�����E����BII����A��L����H5�����y��v��{���6G����*�	����
���b��9��}�R�k��q�0�eG�'�+�N�+�����1*��;�V*�n
@�c%� `�=_�||������M���F�((B(�UW�3�0�s�����0o����AwM��w�U�A�A5h�#���xugS�eY�]bJ5�+���3S0��&�Lh���f�E��X�JBu��}J������$���g%A�?���L��M5i�����\1McO���4L����4~���%|2�KV<0�8�]3Y��dCv���������Q���F��CJdBD����SR��L��oK��������c��# �N��H��?����O8�>�:�R{�>+1��1��o~R�{��]��Oz��$�G���g���1�g���ck������E�
���hf�7�9��d�k�G|���k��a�=����$���4��e��)�qI� mP~��W}��oWKh���6g���N��Y���� �����n�P������5��x+M���)�}O��7�U0U��D�[D���P_Qk
]�=��J=+\����~Z���W��!@p�����D��~�e���W�=�y�U�
�$��7{�����e�;�N�Pr�I�S������+�M����.�8�7d��)�*Yk�g-�����.����s!�m�m[�Jx��R����������#�rQ����1"kN?�������}���i��H
���{�L&u+�~I.����Pw��	k��A���R.��_���R�Pf6
�����m_����+}��Y�v�R��)�W�#��\������N�c�L��~��h@���V�~�����������?����{�B{�~�SkR�Oz"�*�!O�a����i[����W�c-,g<�F���o������ij9K�7���d�����p������{iv�-�&Z^��4T�|bO�e��Y�\y!z�����7��+����oKCc�[���j0�V*N����Qw�:
��L;cT���)z���������x��OSY|�_v�"�[�����F�hAG��ba���
:j�[�?_�#Q�R~0	�ex7��K�7�5@e,�W���y�eu�A�hF���R�B��@����n{3�2�%U;VlW����9�]�����9B�&|�T�����'���^��gk?��Y�,DOl5D�E;���F����^1
��q��!\�3�b^#���P������wKw�r^��|	�����#��X�zL�����<o�����/|NSmr�_��4�}�����QzE)8qG�y1�`�x3)�=��Z���vwKtR���E�R|X�������h����G�2�
���c���9(�������o�L�E�qp��=�dk�}K|/�?�������{ls��6�h��7.�J2NI�b?�7���w����}z�T<{
}Y�5�JbP��K	�r�Z���j�s|yZLO�W�?;�.����l*i�9
���wqDG���r=��(R6�f%wb����uC���5O�JI��`�����jD�6@��Z|��b������'r���sW�P�g�2$F�D�	Wm#�����
js�9]�>j��J%��F�]u���Q6���;*��3����5�����W��?����!��k�t��@��l?"���C���J����n��NN�u)��$/
c���C��E)I����+�]���@�������8x�����m��8q��z!�"�t`^���Q	n4��9T]�������I��r�0���c��{:n�_L7�������a�)�^�%g_�mn�0�6��(�pI"�b�|L���'J���R���jzN�v;�zH�f�H����G*�\��2�����'�����	����-]���#�Q�[�b��0A�	��K���3�I�Q��g�A@w����`9s�S`w��!E�rWs��K��dk�������������e���Z�*�B��Cw�;_�)�y��F�Qx
���Q�92L�������@4���*����BpX� *�;;�����agA��?��
���HI7W�	��#���G\��P��%���V��%
>fE�u��h�1�����ncQJ���\���q��e����������������w�����A�kj�����,��e������@�F=�;HZm������HX����C�ix����Z���h|��Y���t<+����v<D��������))�&��l2=��u����I�{k�	�8{Zi�����
FN��}��Q��������}M|��Md]��6�R�6m����d�Z��B��z����sZb����+a4�J!�T�f,RC���_sRv��
�Q�(�^.J�Q!Qd*6��~C�s�}���������fq���bU�^���(��/F����!]��Q,��^���W=�r��4p���6�;��0���{�N/�W�3����D����Z��]��T������Q����f���H���x>����-U9�	*P����b�x�^$�����ff:�#'@�l���~��2�'���@r,����������~7�������86��S�R&1����!��
�mw$��(�S�z+�wV��@�{�,�M�:�-��35�$�ga8'���7n�j�]\F5�JWW9u3�3LF8Y��8�J2�WT��l��^�4K����kQ�B���"���a@B`I�����i���S;��n �;T&P����(���S�bE�I�|X�D-���#rW/����3P��T$�\5�e���	~s=�2&"��1F��"��@�Q�aPmf�i�P[]�{*��x�kI������=��W��p<�v��y�v(��^��#,2*�}���/�[���X�����x��SB=g^��CF M��S	B����9�������a���3�ez���@��"$�jn0�$��u2�#^���^���Y��T��cJL=�R)Mk������1U����d�;E4���o�3�����y���9fR���l��-~;�M>�^�n�DFoj��S��)�`.f �����J��n����l��j{o����l������l�����]\� c��;�t=�_H��f�oV��
������v���u��*	��n�kmL����}����%Xxl�������	�V�����0�mJ?�-���`e�py������B��KhY� �����������I`�l[d3/|XA�(V	���`�?��#h;s���m��w��*Y�U�Gj��e<@����">�h�2�w4�N�f�S�P�3��:g��B�p|�R��aU�l��a�k#'�1my�ML�n�		�����*�g�m�@���!��c('�

}\�Tf�A\�ohI>�W?��>UX�*w�'�_���#��Kue
Nh��]�:{0��64�����x&.��4:#�W��V���^C��0�\���i����
��[�b�q1��fJ�]��)Dc�pd b�j�Yl��c�f^f��naK�=�|����[p����@��7z�{� �*��@���'�����:�^���WH�P���y3b���[��q$[(��l��]�.�Z�F=��hN�0i��d��L�������|�|�Y�����`Y���$�����K��+��!������&Q��"n/�9�y��+�7�'`1���+%�$D�ksE��J�0�m���!����I0�����T���j�
PQ�Ee�0i�6��X�_�z��.>��'W�����vc��c#���~�b�Rc�E��C���K�����~�����~�;Q�G% X���TR=���"Q[ ����T@���N�|��<�6�lN����cU�TL��x!�.	��$A�M����r0����BO�`%�On&����'`���W�0���$���Y�S�Ug0�0�����C7sA���8��)�#��y�_�Fbp�l�9�
��X���>u�y�y"g��iEr"�G�i<��u���O��%����-j���!���T��m+R��9����\�����x��;I�����{���SL����MG����/��'�q�e2�`����3��E�4Z
'���F�������g���6����CG�%�6��5����n1�XA�������e�����J��7�	Y����IN��>�(�HB)��$Np+L����C+�����6ZEa��Gy�������3U���J�bY�mPV��_�{ED>����""UT�G��x�;���/�S� ��v��u�vP�[�a��rj���G�J���������{�LxLg����t��8�y�7g��F����%"������~+"����0J������%�X-%����qL
a��R}�[��-�t�Q�J��t��
�15�����3�����A���r����u|�L|$a��SSj���5m���4� gW���j����*�4y"�c�����=��{��P����h�b���A�Zs��r1 �4�������7�����\f�9V�Fh`:��B��hf+�R� �\2�K�ke�Ps�m,�'������C�f��x!)9�]��n��LT*�F�j�������t�l�(H������)��,Q�!�cY���U>��D���A�	�����68x���Y�3�s��Ep�������#�+�	�@��B��b9U���B3�yW��g�e��"�}��`G��;���\��aOC��66�����r_c��h9��-iG���)�x=[`"e�C�vCB�����1x�.��	������G'�gW���%�7
2��z�?H�w����R��~b�y�s�|[	5e�g�*�Q��M��p�MP���f�����9�,F+Wv�R��Qv�.c�W��v���a��C�rT�%��?b�f����+�Q�QA}A��u���v*���N������K���D��������g��x���l�sk�x�J`�[�$*7���U�5��zgt3P=����+Ow�N�DT��<8���t8�W�nF�E�(_��[j�*�K�@9b����bJ��V����X%#]2I�����
�G�q�(�Y�."����N
�3wy��?)nJ�V����V��:#t�	�N�SN�>�"���TR	���9�F�#�L_/��4��a;�K�\�����r6��������j�@�_����7<l�Nc)s�4�Q��i�^�	1������A�l��S!X��C�����0�^�bI���S�=�i:��C3����W���N���T�%�7WL�+��fT43�B���)[�	����V��G��^5��_];w��fD|:�E��"|��o�#���q&����;���J��}������/{�����c�7�R���#����n��~���+X���Z=h��|�6i�f��T�U��\��A�N��cT4�����D|�x'�{0!��V������v<�.'��i���)[2W2(E��e�~��9!����b���O)`���5������mHe����t�3�D_{3�
U������y��s���r����Lc����z==���sP�>�W�rn��1��^���5c��[��tZyro�"����S���	�3�&�	jB6�V�q���2��qp,I��1Bh���7��������V���!al�F|�!�����G�C�P��0S����r<�o)��������5/hc�������6�d+���A�2-�MhYS���g3Fva�!h?g�������$���\��
�!F��+zs����.+��*�����?�8�xY"
QuuS��<*�������b
{U�8;��6��EV�c�J��������*� ��7��9!��j���L��J�����s�R�5�3���J��F�Q��l���	�9F�"h�e�������=���O�����/���x�����NK��{����A��������C}�������J�����Y��%0X*������O>�0��X~t��>���9�!3�#�G����:�kz�����wF#��#���TZ5��:�v������ga���2i��i1$���{�QF&L���b:��|����2�,�����K��?(��`�t���Y�5��+�A�������i���~����H�_����9'���Do%o�j��a��p}�����j�Zk;���������0�W���a$����X���=�)e0������>��3�R����L_�3�D�]��Ez��D1[���r�����~������+��"���i���@�������}�3�o��pF��������b�5=���1D&�d�,@C�x����X-�/�/�KP������m�h����2{�)W��fS�%��1�w �����l>+	�Zvx��=����g(������g��&2Z��>�1�f���T�M�Y�/��0x����OG��/K�=�Wkt��n�jV�u��Fw�����&�|FP�����0����P�����2	K^F[���
�q9�,���5�;�������E0qo��V��\������5U��B��..z�z�Wg?���O��{���� ���$sr/��������O��p���g���p�&�e$]��������f����;�����A�aow5M���{��7��w�M��P�����e�gQ�b�D�B
�&B���9���\��DH`�����&R��/C��or	��������?��d[VO0�0�9�A�Z����b��,"�����m�Y��F��B�.�Z����
�+a���n�'Iq���f�?�����hxCk���|�1
���2��+���6���h��kQbp�npg��M�[���-;�����%�����L!�6��a�R9��Q�x�v�����Wk�aP�r�����y�6�4d�\�0�^\^�%�BKb��z���n���Q��ky3V�����l�`�^
V��h2��s��c�vM���� ��k��G��$a��v�X��.G�%��4����k���:�%/~�!�i.������J�_:�~A.9����vM+���mQK��J��-]�U�� ��P#>��A�Yl�O��}�����*5�R�������;r.W�e���V������_�
��xY-}re*�A��TF���<�*��-�����t��n5��Z����l�j��7Z��Sk�F�Zk������PK��L���<�����.B����#�j���j~=h�G�������h�0�n�o�:���u<q[�K!���V_��V�:;H�%L�9l��	v��aE�*���y3u���h>}�����k�$�U�[,����^:��j�-���\��K������`�}���@�4�3@)[
������IJ0����;�&��{�������z�(<z��!��;���uuTrI�@�PRF!�H� ��;tx���w�-�5��2�S�N����\#��_e�BX{������T��@����F�!
�4��e���E�v���O|�P���d��l�0�������N3zN�R���n��������M��r�Y��sh;�I�~7�����H%���D��O�
�p?b���[Z�D��6��[�W~�SI�X�)G?��b��-Q��3r���fy!��x��r��T���9�����5�o��`������Y����sI�^7}��{��_�
�	��
:��E�����*��� �t���K�_#g�+�����p=T����8�)(� d[pC�RQ�"p�K������x.;e�y=A|�{f*I�'	���l?�%
%�@�`&��*B���P.��v@m?�&��(j�|5�	����o�!�dD5�����i�W��!y4"~�Z�S���]��C���������2+G�s��o�.F��yM��y�x)�]�z�P�b�	~K=��8��B�!��PO���
�t��UK�lpq�����������o���q��~�~6(�<Y�#I���\~P�V�A�mwAru[�:�vG��6y�6�.Y�(�:��t*����3M�(?D���8'>d��.���x1R�������7j�F���e�,-�
0������
$�R�yVH�F�T~�T�J�����I���I��J���J�r�Oq)��[I�rR2�>T�������d��B�%'3�j��(�����k�,��V��y���|����y^�#U����o�������n��������~�?��
[^�ov�0���
�~�����5�z��i�:#�tk�����,�����?x��~�O����q��ys��R���v�A����zY���j�n��h#<*��X%��v6\F1
��0Nr�TNBB~�<��n���hNwV>�1Y��w��1�9��3�Y���?����7p|���_����8�g��2	|E���eHG�O��$����jd��K)fa����fr{��$������K[7�P[�V�Uw�5�w+��D�p��������VZQ��b��*'$�O����h<���E�?rt&�Hm����:9��?�����|lN^�	�������@G&\��F�^Q�!,X����N���q����a6�$]=M��s�����"��b"��aV[�$��[�QEO����(���3��m�1��m��Z�kq��D/[]O���-�������I����T3�h~0���������9���K%������`%�HkX��w%�$o`+^���l����!�]�lt��Yi+{�?��2brI+����"���('i�!0��r>C<h�����h��UY�T"F��z"5����Pv�����2���1���u\d"
]���C�&,�i�!\"%I�Za����������S����h<@-(7c�g&asr���tu���G�g��s���=e�����yy��M�"V
��?`� J�:��),��`g/��E�_��6���tl�������t�D{��2�%?���)���9st7#��hY��2/(�i��h��~IXEX����L�gl��|��8��cB��Wc(��#�1c�I�(7�B����
��l�\�<��^�6D��$���w��W��En_�����D�^Q��L?����)F;���8�����x��C~��)�������x��=7�BR����O��x(v�()F���#2�7=�����{����7G������K�����C��������;SW�7(	�M�4���I�t���@mH��a)IZ3c&DQ6��9)��N*�L�76��]�b��=����2Lt-�����F:P;&/
JS��y��=�I�x��V�)������K��j���	��
��U���)7s�JDG0��|VQt����
t1�����H�
�S�kL�
��z�R-@�J/�1BYJt0�3Y�8�d�%28`���k�'��Fg��z
�t'����H��h�������Ii8].	Fx�|.�k������y��� ��X�/�r�y>���\c��r
:�"zLG�P(3W��C P���&+��(�a�S��si$�~���No����������E�T�\�b����g�����%7��6S
q�d���)	��-���LT�ltJ
�h4�N�NA�O��B5&	zl�Yz���-
���I�F����Q�{����vi+k[/=pq��[��b����A�me"��Q�����^����D.��EQ5��������r�!��&��0
=p�0������/��=Y���+4�Nl��b.��G6�\H��EZ����1��I�u�{Qv�h�W�K�?�t�D���G��Q����1��Z�u��O�� ��m���BjK��x�RT�L4����g���������U���O�QF��#N�e��K��/��S��g�D�b2O�m1��V����[L��.&�x������t�^�b:_�t�]���q'��a�c��!��O�(�;���PX������7�ZV\2�>�qi��P'��%�f�99��:*�q�zu����T*���[0a��-��
z����tv�~8�����As�my>�e][/W[�o�d�L���z)�KI<K=@	;y��{�hD���B����9�$����S+�,dvk�;��������}�)��C�e5��T����<�����LC$�#�J��+��x�'��E]�;Q,��'�k�"�C�`��c�^����-/	I�$���M��(A<w�9�2��q�$	F�*F��������)	?RQ������Z�K�E�pbC���E��z�FD)d���3��;������:��Gw��J�5�0NU��5�s����M���D�eG��",�.~�������� /h����\����={H8�?xO�<Y��t�'Q����x�|�:S��:Dn&���dwd�sf�>-G��m�f�k�����^��O��4��|^fGW��WN��T�<v��U�2y>�����8����&�?Z������?���j5�-g�v�j��t�U����A����~��8-7xJ�r�h�Wy}�^6���{N���� ����9�r�JW�~�iZn�'Y��IW�~"�������B;����
�:�����T#.O�_��eC�	KF�.J!��0�A�:o���#�"�!q���9����������
��/���v�v�������/w0��:i�x�Z-�����+Z�����A��/���k��^���_�/������)X6����
��:h������Ag���^|/��]�J��3���4\�����~,�::m%��r+�tlu�<k��S���F>��=#Q���a��c�k�����n�v����9q?���
��
�~Y���W0�oT��C��(?��~6�b���pJe=��X���0�;)�Y^K�/�v�K�~C$������xF�I�	w�dy=&�n�����?U��r��������e��������Q0���T�G����U�iW*�j�z����	�!�� �f��:C��>�Y��]��(	��'
m�Iv�`����W90�v����w���=��P����:_%��/����TX)V���>��X�9z�����6R'KHk����=����Th\�[�j�����(�8n;v��C�-�����A�x�u|�����L4t#mgQH�</�(H��Z��
��1��{%��������U�W0-��x�������-�yX^��U]���y�+?�@�Z��x6�����g�:������M�6����5p��"���� fz�WY@�&0t������__����.yU��to���Ipq]�
����\�!�EG�����a�V���&�}��7�����w�L/�V����%O�`LVe����*q������x��g6����W�������m�g �e��/�M�4�I@��r�����1�#c�Q��Oc�L���J������\�����J�/�}�w�����F5vy�4��Y"��Gvv�������9	�K:*�
��n�O���������n�������U�O�"�Sk�����z{����������!9��0X<�L����y�����,�w`�J��w,���O	q�Z�������UE�~QtZ�*q���1s�<��"r�L��O��'_b�<:>�GC����	�s:�dt:�����1�/s�|��7�V8a^�"�oO�_�
z������Q2Zo�{���T���y�2��G0cn�Q@T|C|Nb�QS��
�}/��x�kt���?�R'��4���Po@74'�{�G0'���9q���N�����0}�O[�g�3��VxM����G��F����V)!��n|����L�bLZAN����L���	��!G�qz}|�^�[��#|?���O���J�����M�6����X�@Wi��N�e7���Y���dvv��������Q�"�d����&���(�A��2VN��������!Y���3xa�t iD�,��HnVF���	�`�{�)���j���x�Pl�J�=�H8�O����'��<�O�������j��������GoM�9��
LU3���j�o�������e�{3�w
�B��1u@VZ�0��>�.�f���m�%����8�`|�f��q����!�����v_u8��]�j<%[aMI&>�`jRF����L"�OD$��Fb��b�$>����y*�\AUU^<�S�16P'II�}��Y0^1�q<'[ �P�fM(��)�.OT�����H�ic�[�.�p���b���h>���A��FD��bV���y`^h�����w��WGg���;���]��n	�Z�t���a�vQ~S������[�=J���B/��;��c��'�3�e�b,n�8�C�b��J�H���@��I,�������y����!jn��40�Z��a��ia(9�����s\�=(�(���k��0��RE �U��,�����*0���
���.0B
�b���G.��m�������1��<�.���������@�]	 ���O�>�yg�S�����D�\���K`���	��%w&Z)��B�L��)�B�`H �����7��*�lt�@���j�k���w���������a�p��Oz?��?�]}�,4���������9�T��
E�,]�h�B
5D���M�GfKw��w��>f,��X\*3^���D�����3(L�A��L�P����B�A�&,�A�Ars�����%�5X�k%2D��5j��b(�d��'���5��P�I�;�9�fi� �����[���t���B3�S	FG.��6���V�i2��E��dT�z���gP�B��������>���%��t�J3��K��U`�H������W��|��2�2#��T� �15�~K��dj��?{��Ir���w����8,��d6�@.�-=n��"Rv��)�����F���m:�P��3��]�YH|���vPgZ����6���(����NI����Z����_��1��h��K	3�#��~��#�'��!��U�c`���^��e�`��w�C�A'�y�k0Ln��W����=c�HG���5B��u���)�8 �H�M��w�����,�&���<&+��bR�@`\�0���~�p�kp
���;�^��B$1d�����c]�W���Go��0Y����+K�F�B2�N��M����l�k(���Y�������Q��=
�D�N��l��a �7��z��4��������c��{z�'�%I��|��*�'~@�9��=5A4��xERR�H*��v��=�@��hb�W�W���(/$!�I��Ssl2��E��2fr������\�\R�"�=�_��?;<{�&!#�T��v	�qw�B{z%�#���/�\��W��n���	W,�Ct�_�w��:<�$9�����]��|���j���B@�)�Cpq#�W.���aK2-	�#����D�&.��&��'z��H����u,�(�V��IK�����a��[M�)
J�-�����	2�GR�u�h������)M!�`0��uc
"��4G{Y>��;X
�Y"|,!�h>5�e�F1I���_#&�PyK%���M*	���D�/.^ z�aF�� �oL�G��T���4���-^����0t,)�&c{���d
D�V 1 �SG$'��^%n!!��a�,�������z���(�bYP�G�,�
����F���}��/�R���J�e�v����xiQ6������)���4��� ��k���w*T� �VS�O��!��Gd~�MiR�T���p�d��	��l6a��$cM�Q�[�������G�U����#���")�8������mtjm�6�E��=���U�l���;2ae]�?{���������~���H�����.`����h��G-�l�>��z~��I#���z�����#�g)�����q�+Uj��/�����2L)>���F��;-�n���1�I;�-6_�_��.����~�������EX6q���i�Q��F����Z�+�:4����>�~+��^x�/|`�S0�N`H&@�������y�_���=�����J�@��]��8��/�C�e��I���-�Q\��h)zA
��#H������d��C~��T�WG'������������/���XO��W��{.���A�gB��q�f�>lv+��[c:mlK���8_�?��P��}��xq���n5h������_m�����4�1n�}��&���.D�����.� y+�]��T����G��2��F�7�Q�'����}�r#B 2�����(�8n�.�K����tQ��k��x��\H��(�36oI��?$�m9�#d&|�KT�����>"FPF������V/��������H=���%S����+y4�8��S�<�-F�����"�wJv+N��T��'��;*4�	��#�*���u��I���i��|F�2���=�K]�1�]<
;�?cbdDn�z6��{������f�b�W��0��z!m���,��z���F�H��X`�p�Q9���=Y ��i06���b{���%S?\�E8*b
����x��t2�l$\	���0����k(n
��\<� ��<���2�B������k�Ko�Y�����q3��������E���b0E����
WcOgB���;}�R�V87COD��4�5Z���s#���_N��*E-�5D���%>�c�!�W�������K{���ICc���D�������>�@n'`n�Y1�l��!h!�D`1D�,�+�*�s�����=�/���"���u�N���.W 
��{�L_v#O��r�����p�-n�+\z��
�	��$���B�`�����t���`�),�3	���gh��)E�Y���`��O�X�����(�9��;Gu:u>�H?���k�L���HMF��xl0�Xhc)�����q����b�sKTNg/�)�Y�-G �������*h��IO���~�G�8���KU,��.�����*��G����9���������Z���������&9��4L�������`e��3�11Rb��'�":��$�,����q�A������l�%L�O�i���'z�E�P72������b�������Rp;��E,��5v��\��,���~-<�D�Ms��w���T:���-}>�1�3�x��&��E�j,��O9�c���J��8�`��QKr��W.
&8��T��2�����S���|������f���|e�����`��p�����p�O~��������-Ron�
_<
'�:�������{cg����]Jj��Q4��&�����x1�G��w�k�����DnZ�������/AQ���\=�5~e`�H)�J	Q)�"����S��&����g����1�2^��x8/����������q���49o�nL�UF��e�0r���%�t��e)��$�by��/�����k��� ���g������-�����s>�q�f19U�4��B!eR�H�Kz��g�(��k�c
�g��}��L>
Wv�zD�:s]V]E�@���\�����}����m�7��1F��}��m�:M���A��p:u��u��m3��N�,!��t>���7�3��l�
���f<QX�0jrJ
S�p<O�t�����1�.�Mh@\RgX1!!�6�������'nL�^��.��Ll Z*l�������Fa�>�`���/�:��8O�V���"���hU�[�6�'���h�'c���G+=��+xU/,����-!�y���2���1x�����J#V�A��sE�~K�����VM&�!TH�8�ta��7�=��6"dUvc[���+�����?�d��6��59:om�]��
B�]�nc�P��[K�r>�.?.�'g?������4�m�u.-�#��/bh3�j��Tip�|at��n�T����]�������u�V�^����Q�����ao�S��|�G�?�.�~a%V�^�XK�(�<%g�=��1���(�}U�7�,�6.���}����x���iV�j�Ri7�jsX��:��KgD){���PC
cM��#����������+	GWt~^rv@OC��+S���B�A����';��=��A�U������aH'��3�Q� (�q��N���~�z�I�Ro4����_[�P����U%�tt���YCJ��D�'��I �������:WI����6G������VeT��F�5l���3�T���7��7l�����m@���Ha���c����; ����`�����*��?�f�]j�zj�f+�Y���]��W���X���4�7��ge��^�?W���M6u#�z���6�� p�
�[eS'��W�����-w�3"���&�����&X����I������;5<���7���r����W��E�4g���T
��{)��Z������~�����QT��P����ZF�5O�y@���M����w)���W��nfQ��b���E��q{�!��}�``�,AW�"�(�W�",��"|%Fe����F��oR��bt�mYRX����'�>]H �z�^4
F���i����.����U�!�8�2S���6�eh��m�T�3`P���=|���T��6�IVs�'���4$+��`+����)2gq�������ca���N��m��cyu +�U�b�#v�M���6��w��i��htJe����1~i��W���V��{
��o/����d������7�\�h�*!���_�+��_�yO�&��T�.7	��Ra���e)��<�^B�������'�f#����%���G��,Ue:'�f����B����Y���������I]��%����U"�@:�)t�L�!��YT���J:���B��=:;��	�$,
qFvb��1!����DoB(�<H�Bz[>��P��EdyVO��Q�OId��^[%�1���'�Y��||�z�y��*F�3��8�/��d/�v����%�d��{���!�������\��eSj���1�2�gc������(�|9��Y�a�qdz�H�`�o�@yb7����,7���L�X���g��
��Y���I{xC�N�nx7r��-����p�x�g������J\����o�!O� Mv��w���/�&�������������'��j.F�(����/;{E��
�U����K�1��`ZC����n��P�pw���������
S�b��h�R��M��0��5�(C����&�`�)n�V���%�p>UG�8���/���y&q��a�$�3,�}�R�-��%3�d�d����� D����e<��Z��Z��y�!��w�d�z�O���l�f�� 1����9�Bi'O��@��D$����A,�F�E�W��)ZE���y)��� ����9r�s�L���*�Oz�h%�'=���M���r����7e��+2A�o�jd5�:��?�/��UT��/+�^�k��<Q��!M��?�������Vi>��`��7��3S��,��R�����
������3^��$]�+�!�}(^��~���E�
 ���n����.�W	$E��.��2�2��F��c�2������S���2��^�^�O�r^4�A�1>�����)���m��i�����m�I����^QG&�SsglO��&�=z����%�H(�MSy<#�H<g'+���Y��'������q
���1n'�]�BNv3os�x�3�������rWmm��1K����
d��H������x�u�
M;����4�s��-��$����9��,������d�!�����~��3�%|DRB���d�'��(��P'(���U`��^+�Te{3n*G�h<��q�jT��K��`�1���5����%��M���Kq����[k0e P�u�l�����S<��
�������v��������w��]\\�_�U��8�K�Qx�w(����&��X���.���Bz����!��
�oX
�?)���@�����#��>j>R�����dc���H
��B��Kh��Qh�����[�>[���YQ�x�\V�^l;��[m'
�.�Cw�lA��S��������F
"�15
�TO=�	��,�!z�T�S����$�E�0�Fn0:���b�Z��U�������}�F�����?^�/�$���j���l�����f:$����4��:�|7�K�mp��~|��|
���e���hK�=�~��2�	�A�l���ab+�\$j�2���^c�"\�������rx�^��@!8���4�x~H���
�%(]�"�K,�]�S���v�#�bO��
bK,]������M���M���l������������Z�����D���k0��������f�e%���h�	3�n}2���Z�C���+���$b�d�!
�{I�0�1,�Rho��-��������-&6�m�����la������g�[o��I�l��������V8�����SxH
�-�-��!���!iHq)q�4=����4pq��B������.~�S
���kQ�������eR}6G��z�y%h������A
�Q<�.|��?7*��j���#�$�
��q��h����0HAW$xE�����MY~���mD�<q}W��|��� �
Y�P?j���������1^=��F��q���4.����z^��)���%����
%�6:�2�]��B�M2D&��kw�1�*���,��o-��r�����X�S���A�'�vJ���4=.+�e"��^ �����HE���=	y��C��'�����<��(�1�T���-)&���>��^�'���+��`���KG8��.) �E9`�z�*L�"1l�Z����[�v���,�S�B��?��?h���W��xSm���2�o��)gg4[��L�I��P=�%O�8�+`7������!9�F�$P���C:�J3�xA[6�����`?���&��)6�!��Q���Rt�
"��]EDq�5A��#���c��
X?=��F�R�S9��.c9c �b����"��+�V��j�Kx�*����c���������������u+6a�[��H�������Xl����G��.����@����NwWI��eGM$�_���qP���Qg
���D8R�b��U���W��+'�"���N}���S�����-~
�*����b��I��u(��6���Q�:����5AE����;�4��l�-O�K����9��k�B��p��l-�<	zN����'m�p�jER�31/�Cs��~"�#(X���������3�������A����=�*��.����u�q������k�|�����g�	�y��%�pc6Jk����$$o�� k�)G��X��8�Kd �2�Z�Y$������"��.���"NQ8��

�Np-��3�&m�v��a'2���
���N���(�t��'������Z���A�[w�������1��xC�}V1�+S�������YK����'��$���hpc~��03Y|�px:�%�r�b��*����o"��X
!�g%�5�����P��3�P�����5��=*P���Q��v*������WmDj��qd7JQg�dPI{�������2}~���(��r�m	���Q>[�4���U8EB!R��RT�Mf���d\����K;���D
3+�D��+E�S��P��p�W���Oz�K��-�y4GA�V���J���Z��]�I�s&���3�H�p���9x��� ���k�pH��S�1<�(x����N����"{��4*D�f!��,dR2��Y�o�e�0�m�46�V-q�q�I��:�9>��u�3E�	Zi��I�]t`��HF�J
76F�v��+��t���-Kp�Lf2q�W��V�V3�IB�<6��g���t�|;~2���m`��kD6��x�i�����Qf���^o��li���y�]�9h�.��#^�u	������=��0<���o���^|3���G'^"�M�S,���"4f���&����L>J��0j�������N���O���e���k&����o��t����3Z�
%������{A}���f������f5{�j�9�o�r_��L��?*���Q�)��}0K�EP	�P�<�p�2�<3o�W%G�����m���.=x{vvuyu�;� g�m��������m������]*4��U��5��"E(��"���)���2���$V��+(���%Tx�����w\��	���|9�
:9TRn
������4}I������'�	�^=��j�_��V�����������%�~L���a��w3%
������9D3N.Z������8�7����;p�Q�2Miv�{R���*?�)T�4�M)��vM�����(@y~�	-<���=�<B5�O�����m�{|�
��	5������]B���1��U�Im�M�2��R���7��7���|9���W�*�{�����p}q6�.g�"���0]T�D(�������u�}��$
���<��8G��QY���hXLjL|�$�����_)f�|1D�o��� `N���$Lg�[xK��n�>lxN���*�a�k�G�Qs���>����n��k�	�>��3z�.n$�yv"4���� 8s����e�)R%���+$��2%A���/l.�/w�Yw���"3��c��laIo������	b��Oz��3��/���y4e�^����76_~u�6ro���������f�s�n��v��u��2+�L6��v�	����v'C�M��C`>
Y6���i��fTh����,H"�/�~�A�L)�"������FM�7����J��,�p�	'<J8F�
��)	�q�C+g��
�`�J�^��s
���R���z�o�^�b�#km�p'������l���W��_�y���lK���S����8����rsx�X������,�?��K9����v(�������nc�pz���������5���� ���3�V���x����c�x�Q"��U*#�?��Cb�3i<$�^�lfP���	�q��Y�{L���V���0�m�:���Y`09=�6����G[���������>ao>*k������G�d���a,�nnBV=0��4����?�,�5N��2��c���X�y*�#��e<���q������I����o�v��0�`2g��N�Rs*��������?wR'��f�q�A�n�����w\���2�40�(%(�������H9j�n���i}F�4{���V�>�5��
��N��j6�Z�U�Z��r�MT�W��2]����rP,r�����O��
�N�]����9t�^���6��_������6�#v��z'' �/��p��C{I��Z��� ����v>uCq�])~X��
u�"��6���
+����l(����]��S����!�K���Z��z����z���h�R���wu�AT��N�W������9����R]�*�(�>r����3��pf.�8����2!�i���D%�<(s��m�H�_B��#�������p4��4|�t�c���xS	���3Tj
���i'�)��e���TI���6;(�o����_�l�x�sN�=��;�5�2>�;����{������q8ZS���'��a����j��BwJ���UF��i����������n�����P�&��0��
�<�%�4%f����v��k�p���P���N���;�cR:�&����
��^^i4l4���5r^��J��5��.^�Y}9E�:�T�g��O}0x1T�m��\"7�K�����`����{q�������~��@v1 ���&������5�A��N�+�:�&r�*�A�`�(R���$&���4�Uq>��u�^��1=yEn�Qy��g��F<R��\�p���*r�_��b������,*�����@4���F+��T:>��ZQ�9\�Z�"X��{kg�-����	UV����R���q����4�i�K���m��b�6�qJ�����������4[�J}����y��V�Z�V���.%O�[t��!'��q�����w\�El	ejJ�k������U���|c|7f�*s��i�p����1��,�'�9#D�K�*�����K�b�����G�E�b�:�JK��Y�*S��4��h�A&|�C��\��]�?��E�w�=�+�Yly5/���2���:�Y�������k�(z	���tZ����?��^��U����U�����a���NwX����h�
[_���L���T��-�%�A������j�e�������4W�(b{��:"��y��i2�"�6<o�4���������n���t:�x;��!,at"zwy���.<R`���/���;���>�:c�CQ��-���t�����vk�����.�k�2���	K���4�8!6��pJFr����h�SL:${d�s$���a0�AH����w;������qp�3��x;��z:S{�������}�pA�S�z*�>Y�(c{������f�!�xyG��W@��L��}1f�+��S>���i*U#G���V��R`>�D�
D
��	�q�Z-����www���Ie�������~y������?�}W{{{��{�8�W����?���t���������`6�����_��?}����9���?���Q���'o�05�r<�3������o��j�a�Rl��5�Z�RIT)��J�iT��S����vPO��Q��oD���
�u�q�����P��d���P'i7������Qx�����t��g��t/���l������Pr�Q�7�R
4*�?U������+�']���s-�0q%�ck�����}��I���'f���w��;M� (�jD6U�	�H^�R��O�>��xpn[g�����G�3U����7�Q5�t2�0\���GR��}T��co&����V���W7����h,)����8�?k*��f[q^�.��\(��,rV=5Us>���s�e4�B��N���_L�n�	��}���Is�S�<��
K���a�e����������zh����R<�y��!�<�.�7Y7��F�pA��(��1I�6oif;L�A=� �a������]��M�������s'�z�0�����L��-UW����rn�k��;'\o�z��=j�&*'�tf��[�"����j�@{�0�Q�1T�8��Y3���	�v���[�K0������W��;*���L��2W�v$�EV���+o�����wI�|��:����:�5��v<��T�z��9^����0��	3o��s��:�.'�~���n�4|���u-\O�f�7,���#D�>;;���z�������O�v�S���_�X����l�%N,%E��~/D��&vE�<�9P.�,�V���?��-=����XmE;9,���!������e��I�ve����'�m��n4k�J�k��Q�Q����k�h��'��#5��������?@���Y�#d�^�e.������^��_�b�����^���	��^��MP�u��t�/;{�:������=��gKm'��U*�Y{=�u�����,����+�a����Y�Q��9a��� ����d�;)��S��V���'���+��u�n���v���{��9�1�6<����c�����d[C������p�^���������e�iX����+z������_���\\��x���4:]�;����x�y�mS���E ������+l�������\C �����*M�'eq���I����q��J��(�8��=��x,��t<q�����K��\��8����������[���:�zs�U*
��6j���n%����@N���)5(#+2y�!L^����ZzV� ��8��#kG�B�"y3��9�@���R�zZ��{���a���w�;>������+j�6��a7�p�>��1�u�]@eG5��������]�D�s���B��8;��p�Kq�L���=���A����/���"wE}	��C7�@����*Z��\��)^y�S� /�=��Zk���0��� ��x6C�q�CD��An����<z���#�T���l<�J�?����~��T;�y?�A���PN�7��X_�:��R�xJ�}~�����1"Q[~������1U[U�����Rq;5�o����9�)?@�:,�����Z��'h�����k����N�_ ���j=�){~��'������V����=�����Rz���$���L�C��D���&������#�P����5U^i�C�������*(B���h�2���Q~��:4�M���7��a�y��[����w�����n�.'G��'����T���l��T7U�*��q9���Y*8��IA�2�}�-p�xJ����A��s�����
%&������IGyw��R
�j?��U�@+�<�.�_���j�����8�W3����ZWx��U���.\~3sW����]a�G,�bo�Z��eNI<S���@\���U��v� Z�� � ������X_���x���Z�U�ZO�*za ��+)G���7nX(<U�$m�\?�m�T��j���
n[�BAd3���'�*�������5��
D�R�zp�:��Q����E��,)t����n�~�pb��5S$�cj���K�1l]R;}�qg���a��T�O��~(�h�������-&�����]�b:M�h6x��~���q���_�]�%�+�C5H��C�<�������67���{
��i�Y(�m�k���u��J�p�+�[�x�X�������V]��gT��#[eo�uI{�6J5P�j�V��}�h�]-�s�W��]l�.���e��[�(�����JU�l�;����\��hp'w�}�����#&�E����c//vc���u�����\v����y���j=-���d���l��2�2����A�d������m�G!>���2Q�j�V��%�8/�����^*jU����Vd�8��V��P����=M�����8=���H�$#�<A�/�����<|;�O2���8����^$1���io�(�E&���^$!q�v<��1^�#_�^�lTr=��T����w����G���Q��o��B���_r#Uyb0�.��3vl�n*!
�4���4�Q��V�������a�����06���1�+9u�Z��>jMoP���s��� �J����MCo��$����}u��}�,i������A�#�I��b���-��^4~�7�i��zha�
��0O���Ye��)�>�stX�J�e��P�F��Ec%AM���ze~��w4�����c���qS|c���Y�,%Y�:A�=rkNX���#��l[PV7���iL�$��i��9����?X����B��d`:�y��~a�����������c��eeT����1��{#����sz}|���~�������+Z��n��������XA���^`� MO�F��K����+"�a�jt��	�V�R���
o�
�6(�\b��/F �M������w1Z�c�x_������|����4�JB�S����R?�v�wz�w���EvF)��]1i�Y���isGH�xq���e�;N3cf��}��Op i��MW�N.� ��x���:s?�!�����}q��9���:����(����"�87p�_{��������
�{��S!�w��~�I|��sm�qo�G�IG��������~1�	R��v���i#�@pf3��( �d��p��&
>N�����?�/8fnBMC�?XU���dM)�w/�_F!v���~�vZ�Z�w�~7�� �W4W2���h
��������p��)x�EV\���~�N�9��%<���X)��P^�����C}I�����{2����`(�.Vp5������![_��g��t�qhwphwL����l4\��Pn�Lq��K��CI.YM�!�4f�6���p�������O���on{�WW�� HZ;,��P�e�������6�7o�k"vII32��xI?�z�	��^}K+�����8��j�m!���>�wD�|at~�
 dY�Fr�/���-%��v��v;��
!9��F��j�\{��$WxtSm�������=��U6����a
g��h8��J ]2���NgV:!��H@mA����K)�����r!+�8j�Q��J������U��57U\
�k�j"����}L�AV�N������.@�|"���&
2��l��z	�H�z�n6$�{@DH���Y�u}F�o^	Q�Q0�T]I��i�;��u�Y��	�7��V
d��-���&>����B7z�77g��7������G�T�"��>�h����R�,��)�$E1W[g>���~�>�$ �A�8���*J{�k�T�<���nbW.��8�6��c0XP�$Z9��R��'-h5+BW��^*$U�v�a����ZM��Q�u�A�U}���Vo�\�
|������8�	����7u	�YVN��-�����I��f�DwF��F<�x����l#�85S�O���'[[������
<;v �~x���N�`$f[��d\$��2.��������@��:E��hv�+����o�o�\��v���H��.-���� ��$+�X	���z{|~�	���]�����KD�[��(l�D��HG��o�a0h��Z��w�a5�����o4V<��b����~�����m�v�����Y5�L
���lL&�����^�
R��u�G������CRs�'���rTN�����&�rd�x�1!��I��o�j��������n�t;�5H������j��C!�f�\����4N�h]��D�o�hn����*:�<��H�;���0�O�gW��D�.�0�6����L�;�����P0�������I�h;]�F��6]�eM���fh��<K�z�U�m�f
>��1]I�t�����Ag�^-�s)1�	�����L������bX������Baz�GB��������a8��1�F������x�I��H�`t�Wd[�<�z��}�6(�������t�I�� Je�Pl���D�����A}���OH`R�an
�R<j�nFz���5��}:�p���7W��,�Mp"�h�#��
����r��d~�Q�4�N�{���1ve9u-�w�[u��^��
6�g�������`�D���������mI��e=`����q_���i{(8�w�nL�uN/�c�����!>�R��="���N���6�D�~�o�^����5ZE����$i�p��eKQHz������35X�k�L����M�f��a"$���-Ma���9��&�N�|QZ�Q��JT���:�P
����F9i�D
����|��jy~g4;Q��5[,[�,��Qt�/��D
����O��%�n��l@�?-�������f�b�LZ,�kh�*�A�f���.��������.��.�������U�J�q��n�������k��d��R�G�RbU���\%�Bl��&�,�u�f
I�?���~��j5���aw���G�����-Hi�}�&��������>;Sp2�!�UW:f����T�$\1�TJ��~����\f�n���� k�n�����3�hc�/���b���#i_�~����{�0��2YC�������{�]����s#Uq.�����������1cd������a�4c<���ZM2�2'
�9�5Y��"�����Y�����`6}7��v�E��Hr�U�,�ip,���(CBx�1;�^N'p>w�T�~�Q�f�����d��Lx�hl��L�	��~QR9���=���tw�cP����v:�u�>�'���]^]�)���0>�n��9��
��R����A��Un�]��-�����/'e���
w��Z���q�S����
K��+�R�D:	l��.�X��e��7���@}�rR����a��c2�����~���O���t�%1<�@]Qt?��h���JZ�w6"Z���7�y�ud�42��72��6����ld���Ff
�[&_eK1���Q+�:m���������(���k���=����k�<���/�=�of��{>�Ds���8h:'�Cs�
y
�J�e����25��Z�!�L���i��jdy+6eh+)#L�\�&��ji���.*n&�i���p4�/��Q�4�<����y
~P�Ae��/c�������et_IL�:�W���k��U��6�(z4������0q&��8����}��O���]F#�+Y�vc��	��H6�f��JG��p��	�������rVoI�\5���Q���s��I����R������,,�^8�3�nQM��{~��b)�K�-�5A�P�J���
�5�L����.�N7p�~�>h7�A��n�
��ow��Zi6�&���\�M�-��������`�������l���|V�6�,����%���F��5��P��������x�����'���){J�Ojd(���7Ar<yxI�b��7�e2t����;-������8�'
l����Xe�tI��Ll�����b�
�iU�j�E=-��4J .@�5�&����S�:��F�mx�!5<�	=�[�8<?�,���,�s��ddH3�h�SO0�ZX�:�������]�B�x�A�Q���S��K�I�T�Rd^�|����~{|!�zW���/L�x����@���o~������
q�b����J�K^�_\�y�E1��i���� �(�������gb
�xk��raNH��P�^�y+7Li����.Ja`����|��f�{����Aa63]��N���})4�CG����T�}�����J���h��5�K9e*��t��t�K�Q,���97�bh�(���x�=B[f9�LK%T��.���
�l��e�G�k_�%^.������/�.��9�=�B��@:C������?,�^���jP�p��<��'�j+�@r�/���(��XNTZc9$�I�*m���m���	�A��}W<	�M������9t�
��m�H����i��������:����v��M��->�y�HA,��|<�S��a-�9���>�0]r�$iA�9�\��� !�����[�3��
�2R���!Q'�`�G�������T�WKg�4��iEgdM��?��4�Hw$���v\D�6��G����VfY�pR�
c��;{j�����&�ps�.�����q����~-�v��m����c����U6���'1��������\9����������<���Y���SDE����Rl2me���2_~�g�+�G�������>��hG�����������_z7�o�z�g�X�>������������1x�q4�v����[��d��h���e�	w�_d���6���:Mw�f�a��6�^H��;)�����~#h`8b��dA
 �
�,�����hx7�B�BB��
��p�Kgo:��+�:kQ��0%��/R�.���H
�6������b^�R��u����������2�$�0��u.���
��cP�B1yg��T����^dk���|���\������W�	�zw�1�o�/o5<�n�����>j�!4?��$(`
6�L��,���@�Cz>���c������|�P?���_���)�v��/����
����J���P�O�;�E9-��7����F'��.��&�8�K���R��	O,��)3���{������S�*��T���Y.C. ��\��K�8�t��Z�N������b���=������LoC�r�+��nfJT|����zX"�2<{�c�:��`6����HJ�$��)���q��n�`��or���1�K�\l���W�*<�_
W��Y��q��H���;�����f�k��6DY���BiA=��V���f����
~��6+Y1Ui���-�&���F��:�Fp>�K����$��E��t>N�v��D3�h!��H���b�F�E�'F�5��Aq���
u��J����$j���~��u��s����g(5�hB�����+��P�;�z�?�7g��g��W�g�;�2J�i���J�yKRX+��qi���G��I\{�s�j��9�7������TH$�"���<�����A��[�k��9�N��
����6��Wo�	�z�5�
��%�H��lG'H	!A�����+���E��C���{�yW����Si�����]
��{dX�pU8���bG���tA��w�:hm{�st��56&����t�P��'�!���Na��b��aeaPt����.,i�����"]:
�t�/��	�������{���������iO��]P�}S��9hb �Tb��a�#r�5�K�����
((c%E9�"��Z����Z�/%����rAy���V�D-�V4c����k����&+���0,b�:��L��������m�`�'j��`��LV�r?�������:������p��	t��%8�P+x��g��i)��_���#H����O���/�~I��&@��]��`p4l��9`E�f��I��e�<�I�Zj�ydoCw��z����@$�����?����]_��>�;�8;���]��k�\#����A5�!��#E	MH�g�z����������7%9F�)�1���0IL��=����D1��/�dk���>^���� ����)�g��������9b��m���~A�k��� J���y���
@/�����4D���~���R�	���:�>�K��+�i�������I�o
h��H�/i';'�:[63E-utXNY��\`�����W0����>
�E�qn�Uf-Gp��x*��.����y���)3}�A��}��:����>�B���)}��Q����reu�Ne�)�+�Y������|.��TVJ?�L���e����B�N}S@����D!|s"�����9�R�T���0�;�
[����#�XHpZht?�a�����c�i�qg�q+��L�����2�q�J��!����s
�����/�D�^��� j���l5��(�����-�u�|�Ze�U�C2:�x�
��M!���?�4�����
���Mm���F����x6i:GO284��V�-�"�re)0���wD�1��)���vG�� H&'��&{\*jLg�� ������y�P�� ��uV��������s~�"�g���K�N������N��-�����wbH��;J{��p����x<\�)2��F`#������o#�k�W��b���R�	�	�����R����1���3�;�qj�8)&S$�Df��'���{P$�5;^J�����?�0v����t�A�F	�`���#~)jq�y�����(������R��Jjz�H(a8�45�1�
���o���<9�=���('yZ��6��xl�2m�Q`b����<BI��t���#d��{'Y��E P��d�K2���s�U:�y;�����W�Z�;��
��
���,A��6f��l��(�Ij��!.b_
������!�6FT�U���l4`��60F�C4��F���5����;���j~�/F������F��YZ��[��i��/�^��\�?-�����~��G�h�HM�J]�N��A�X�*��?]��[�0�J!ni&��F"�>'Ku3�B��7��R��5_������(��������m�����q�Q��Slp>�	��?����Oi�|��oZ�Q�`m���Fn;x�*B�A��!�f��v=r3���,@��p>���� ���v��y	�Es����Ggj�V��0��` ��F�m���)����l��v�l-����JsC�E??''B:]�Q�)�����%'h�e1�9�M��B���ax�z�dQ=�.��������{�`6���q������(^���b��i4Xo�|h�v��B���<M���Js�d
������L���� �O������E�4��=�C�PSj��{�t��W���#C
D�p��#�k���
a�������4��X����%��[U�x%u:m����N�D�8/l���f	�NQ{��ST��&��59`�8�,��j��U�t��/A3�O
��uF�
q�$S���M�pT��:>q@8$2����G�T^&%(J���QUTiz�X����Y�f��O_4��\,E�O0[�;z����b�t����S��[�.�W	��e?+�}�A��H��`]K�������V{�'/na�3I=QW�L�_�v,���;���ns1�� \�����sdZ��8+,�X������D"��XO.2�}��E����������"�J�TX������z��`�`����~�6�u*V���K��&H'�����-���Qk�uQ:Iv�+��`C
���d�#���}+rc(����������:���]�����J�/f9�<;�0���a��2�M{�������%5�P��7������#J��)S��yk��g�q9��^vN�����G���&:������9\4v�1��.�c'E������B����CB�O�B�LC��!��
����i��3����j#�'}�	n�8����.�x<a���/k?��Y���'=�4J���i��!�Dp�-}�<��\O�9b�tR��gB�a��9t���{��������h���i�rQ$E�[��R��B��������H�_��d`ruQV�,�c���z��"{�$$�]������S�=�����{<.���]��CU��V=uP�����t���������������_�V�{u������M���[�tM�]�������Cl$�m!$	����	��E������&���o�+��o���s�(��rG�
*�K*�	�vi�[D��3F.P�����K���K���e��������qxBa�j��w�#��f���S�K^u��<I���"�>h-�����}���2������I0:�"B����'�8"��5�DWX-bE#;M�����^1��R�ZxE>w���c�S�1�O!+��p`Y�W)�i9	��h�Q����_�u�b�n^��+��Z�+R�,������p3��Zd^�x���n�D5j�I�����d	T�O�>�*_�������(Y�XW��H���'0�{�N�#8)�,�&� �@u	}	���$��r?@��#p^���j�r�����Y<]NB=��PV�Fe�
|����{$8�;�N�
�A�*4����5d��!X��"*������!��K+i"FyCenF�����n��]��x����*d����p<�����l7k����@\��X��[-f���8�K�2 +@A0��5n���t��18�z9�t�������q�m�����?$�{'C� ���p.�'�T} �pn�+�.��y�K��|K�.{g��W�8�J[�zZ�Mq?�p2.�����'B�=����Q�_�b��q2���BKJJQ!n�n=��0������s�k�H�f���J�Z��}���'{�|��6���`A��t>_��#��q��88����<�^Ge={�Q������p2�K[!vz������
?���&�=_-5������/e���q3,A�O:�����W��x>g���s����Eb���Z���^3��^�7r�����!�.B�����8��m4�"�5j%o��O�#:��K��
��0������������b������p��q�eT#��r2N>�h���7�U�r�][!6	��e��/{����2��>�;��}�q8E`{k���Z4^]�����|��K����+�������
ADV��L���@B����pz�Cp�����~������7g����W��^��n����F�n�?�C{!���X-�������nF�Z�h��
���Ec*���J0������,q9��d�V�)�H��'8��A�\�����T~��=0EC�@��G.�%���e{_p�RD�����w��NQFX���Dnx������s�!Iv���4��������T4�8g?��]����������KL��n���l���(�`o��D{i-�.��.����lSqsi��1����Z]F����EGFx�>�5H��Ew@���;!�zAA�.@�l6"�4>/MDG��� V�$B��i,z5}f�^��|o��|T.���&��eg_o,~�Q����{D��(��7�!%Kz�����I6PUk���x���c�V��\<���i��+fV��������^�5�80P|:��s��x�*�'D��;)�i9;��T
��a�9��(t_Iw��d-�F�Ai�����W���T��@��hC�+V�����=�v���'uS�;����jt}�k�8����ZZ]�[+�qd�S����������s�,��]����:�{	S�GW4���t\\5�����F~�R�E5��me�B
|�����4V��g��L�qH��o��\�?�2�6�Z`2��db�B(At23\�D�^�sY���1��=N�9P�<b����x��1y�G�QWW��2�2��t(����u�-�[�Eq���c?hWp(��Z�,%�#Y��C4r3=��'m\ �nm���jd�]���#U�C/�TF���rN�'eU��*0V�x^:>�6�2���u�WG������ISe��$�7O��DI����� h�����5EQ\��� �.I�sh��)
)^��@z�
���!� ��?�{��B	�2
�����p�K��p��D��C���-DD��.���,��t���>%\.������LB�n�h}X�^�]R�Y�+4^��W:�Y���Xt��2Y���Zm���P{�)��F��'���p�f�(�D=Q�y�Mx���lFy��C6����������B�H��`�A�C�������L��`<a����]��K&�n���`$�=�R�����)f�xi��`j�V)�`h��L��v�~�E����VU�v��P�@��yv���@�N��^�YK��~J�����<)��t��L��t�����b�d������=h!9������}p8y>������|���L�Vt���v������u;�Z;v��
��f�.lI���;~������@��O���R`�?"Xi����\`Y���@����J�Qb�g�������������}0	�V��t�x~���gl��6���9��d��3�Vh�*����I������x\��F�r<�~fg����hyM�)�F�U�
����{�v�,����r3�Z��q�����F�T�>&�Q��a��U���C�}��Ld�Q �8�QB�\��$�����d�C���~�/��F�D���tiS������C!Z�E�/^����\A��:���x[�D+�X��+�x��|����������������;:�a.t�?|��du�D��_�!D}���W���U��u��0��ijQ��|��
��Q��O������^���o����W�ldD~�ov=�D��,�Cm���Xo��e�_��5�N~
eg�&�l�����b���hFaV�L��7������A#���(�j�fT�w:�kZN���J���Rtt���t^�����;_*�G�n����{�����%��%�$cVF^���Xx3=mj���������h>�2M9+���I����B��I��G���M���&
����V[&���w��Q"�&^�������O,������B�5����i�IS�]Z����z]2�r����(��lB�h��e#���c��L��8����OCk�-7=�K@N{�`��"���9=Z?
y��t2�1��$����X��,��ie����/��e������4��<�M5����N3�������h��l�����d��%�"��n?n��{�m4����]K����Jw	U�|�(J�aCp"`�]S��7��SVn4�u�.��5�g����2V�"�d$c6����Y�dR^�#��0IS<Xs2?Js���|�N�r���1�9L�����
�a����_��V]�H��:�&3pn���grR�"7����z!�Y�hx���U��x��C���(��k���/#����<)eA	���0����-?nz���~�Tc��R~!Q������1�)-�5�s��dz|��r6�Dn�|���MJ�`l0���lmD/g�-������k��@�4���S|�e��%���PP����M�1u��1�v���c��oB�}|�<ig���!O��H�j�!n�Z�pMj��pM���0J)<H��l����^�����n�oZ�����/����FC���]\��W��p�NoL�`��>N�a%��6���Ir �	AQ�J�V?Q�H�����	2Qm�C���"��A;�LE��b�;�<���M�i	"_`���/8�G�>���`2�LOC,���~����H3t� q,zA���H!�dDA�Ep�;Ee�x��U���eXQF��9b�7���A�-�9=O�����F�s��jcI���>}���w5�	���Q���}����Z0Z���u����o�?j*O:�/�;��QoF����
�Y�1������Y�pI�[���/=lM��z�
����CI�L4^p��E���B��*hSV�i�flt�Q-�f��7����m�+����5v2�g�4������F6�1�mozg�����x�~���'��_��a������`qz?Y�|jP���QT�"�OzG��>.~�a�:�����9����W�1��1lj�x�lAhz��\�����V��Ph*�����jv�V��zbi��V���~Z��
*�����M�����(�`x��k���L&�L��dA%�#�e
N���������I���Z�P�.��
��(+����*�%�*'-:�D@_����xm�����(�#|�e�
���b�.n.���p��	��|G%�F�Sj
k��,*�����q4��eY�|�&��i�},`��Q*h����s6��BV�d�x�8,��p�yc�j7;���Z-�\1c��`��y#6R��6�-���)����8��/1���X��� '6�Dz�W�J_�u�m���*;�f*S������w����NEY9�I�^��;a��z�z�w�b'�w�� �Z��]=���}0Iw�6f?L��.H��KN*���T��%��F�:�f�%�yIE���
R��|j*-b����wL�| /������C�I~���)eL	d����(��P��4���F�(F5o�������~��N�z�-��5��	0��l�p(1'��;��v\��AT�k�A+jz��
J���F~��w���\�L���f����rN��S�f\TT���T�w����lL�����^Mi����'
��J���
��7$�_oqAJ�����������G�^��B�=%CL�#C	�����{���Z�������	$)��O��hpp�p���?�o%��0g��(6Fc�?���=Hg�6�q�E�*w�PE����J�b���p����d�M*+a�������t����c���^������������y�<}�>O��������y�<}�>O��������y�<}�>����iVRY�	
#31Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#27)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, May 9, 2019 at 12:04 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, May 6, 2019 at 5:43 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Just for tracking, open comments which still needs to be worked on.

1. Avoid special case in UndoRecordIsValid.

Can we instead eliminate the special case? It seems like the if
(log->oldest_data == InvalidUndoRecPtr) case will be taken very
rarely, so if it's buggy, we might not notice.

I have worked on this comments and added changes in the latest patch.

2. While updating the previous transaction header instead of unpacking
complete header and writing it back, we can just unpack main header
and calculate the offset of uur_next and then update it directly.

For this as you suggested I am not changing, updated the comments.

3. unifying uur_xid and uur_xidepoch into uur_fxid.

Still open.

I have also added the README.

Patches can be applied on top of undo branch [1] commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

[1] https://github.com/EnterpriseDB/zheap/tree/undo

I have removed some of the globals and also improved some comments.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0002-Provide-interfaces-to-store-and-fetch-undo-records_v6.patchapplication/octet-stream; name=0002-Provide-interfaces-to-store-and-fetch-undo-records_v6.patchDownload
From 02618df119a370ee0e1784bbb88701bec2832eb7 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 1/3] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoinsert.c         | 1475 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         |  744 +++++++++++++
 src/include/access/transam.h                 |    1 +
 src/include/access/undoinsert.h              |  149 +++
 src/include/access/undorecord.h              |  231 ++++
 7 files changed, 2630 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoinsert.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoinsert.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoinsert.c b/src/backend/access/undo/undoinsert.c
new file mode 100644
index 0000000..1a7845b
--- /dev/null
+++ b/src/backend/access/undo/undoinsert.c
@@ -0,0 +1,1475 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.c
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoinsert.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the previous log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the last undo record of
+ * the transaction in the previous log, so that we can find the previous undo
+ * record pointer during rollback.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoinsert.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static void UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	Page		page;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info =
+	&context->xact_urec_info[context->nxact_urec_info];
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 *
+	 * Refer comments in UndoFetchRecord.
+	 */
+	if (InHotStandby)
+	{
+		if (UndoLogIsDiscarded(xact_urp))
+			return;
+	}
+	else
+	{
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (xact_urp < log->oldest_data)
+		{
+			LWLockRelease(&log->discard_lock);
+			return;
+		}
+	}
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (1)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		buffer = context->prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_info->uur);
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&log->discard_lock);
+
+	/*
+	 * Set current transaction undo record pointer in previous transaction's
+	 * undo record header.
+	 */
+	xact_info->uur.uur_next = urecptr;
+
+	/*
+	 * Store undo record pointer of the previous transaction's header in the
+	 * context because we need to overwrite the header undo record in the
+	 * update phase.
+	 */
+	xact_info->urecptr = xact_urp;
+
+	context->nxact_urec_info++;
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			i = 0;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(xact_info->urecptr);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, &xact_info->uur);
+
+	/* Main loop for updating the undo record. */
+	while (1)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * removed by discard process, so we can just skip inserting into
+		 * those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * If the buffer is not valid then skip actual writing just move
+			 * the write offset in the context so that if the next buffer is
+			 * valid we have the correct offset of the record for inserting
+			 * into that buffer.
+			 */
+			SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+				break;
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/* Overwrite the previously written undo record. */
+			InsertUndoData(&ucontext, page, starting_byte);
+
+			/*
+			 * We only want to overwrite the transaction header so if we have
+			 * already done so then stop.
+			 */
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+
+		/*
+		 * Record header is spilt across blocks so go to the next block and
+		 * continue writing there.  Start writing after the undo block header.
+		 */
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(context, urecptr, prevlog_xact_start);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/*
+	 * If we have allocated memory for prepare undo and prepared buffers then
+	 * release the memory.
+	 */
+	if (context->max_prepared_undo > MAX_PREPARED_UNDO)
+	{
+		pfree(context->prepared_undo_buffers);
+		pfree(context->prepared_undo);
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			urp = InvalidUndoRecPtr;
+			break;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 *
+		 * In Hot Standby mode log->oldest_data is never initialized because
+		 * it's get updated by undo discard worker whereas in HotStandby undo
+		 * logs are getting discarded using discard WAL.  So in HotStandby we
+		 * can directly check whether the undo record pointer is discarded or
+		 * not.  But, we can not do same for normal case because discard
+		 * worker can concurrently discard the undo logs.
+		 *
+		 * XXX We can avoid this check by always initializing log->oldest_data
+		 * in HotStandby mode as well whenever we apply discard WAL.  But, for
+		 * doing that we need to acquire discard lock just for setting this
+		 * variable?
+		 */
+		if (InHotStandby)
+		{
+			if (UndoLogIsDiscarded(urp))
+			{
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+		else
+		{
+			LWLockAcquire(&log->discard_lock, LW_SHARED);
+			if (urp < log->oldest_data)
+			{
+				LWLockRelease(&log->discard_lock);
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+
+		/* Fetch the current undo record. */
+		UndoGetOneRecord(urec, urp, rnode, log->meta.persistence, &buffer);
+
+		/* Release the discard lock after fetching the record. */
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	/*
+	 * If we have not found any valid undo record that means it might have
+	 * already got discarded so release the memory we allocated for unpacked
+	 * undo record and set urec to NULL.
+	 */
+	if (!UndoRecPtrIsValid(urp))
+	{
+		pfree(urec);
+		urec = NULL;
+	}
+	else if (urec_ptr_out != NULL)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogControl *log;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = log->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoLogIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&log->discard_lock, LW_SHARED);
+				if (urecptr < log->oldest_data)
+				{
+					LWLockRelease(&log->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			LWLockRelease(&log->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen.
+		 * If undo record has a valid uur_prevurp, this is the case of log
+		 * switch during the transaction so we can directly use uur_prevurp as
+		 * our previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit then
+		 * stop processing more records.  Remember to set the from_urecptr so
+		 * that on next call we can resume fetching undo records where we left
+		 * it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..7e585ac
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,744 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_prevurp = ucontext->urec_txn.urec_prevurp;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoinsert.h b/src/include/access/undoinsert.h
new file mode 100644
index 0000000..c91252f
--- /dev/null
+++ b/src/include/access/undoinsert.h
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  entry points for inserting undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoinsert.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOINSERT_H
+#define UNDOINSERT_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * This defines the number of undo records that can be prepared before
+ * calling insert by default.  If you need to prepare more than
+ * MAX_PREPARED_UNDO undo records, then you must call UndoSetPrepareSize
+ * first.
+ */
+#define MAX_PREPARED_UNDO 2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+typedef struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+} PreparedUndoSpace;
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+typedef struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+} PreparedUndoBuffer;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern void UnlockReleaseUndoBuffers(void);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern void UndoRecordSetPrevUndoLen(uint16 len);
+extern void UndoSetPrepareSize(int nrecords);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..4ce1e76
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_FORK						0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_BLKPREV					0x04
+#define UREC_INFO_TRANSACTION				0x08
+#define UREC_INFO_PAYLOAD					0x10
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 *
+	 * TODO: instead of keeping in transaction header we can have new log
+	 * switch header.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

#32Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#31)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, May 12, 2019 at 2:15 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I have removed some of the globals and also improved some comments.

I don't like the discard_lock very much. Perhaps it's OK, but I hope
that there are better alternatives. One problem with Thomas Munro
pointed out to me in off-list discussion is that the discard_lock has
to be held by anyone reading undo even if the undo they are reading
and the undo that the discard worker wants to discard are in
completely different parts of the undo log. Somebody could be trying
to read an undo page written 1 second ago while the discard worker is
trying to discard an undo page written to the same undo log 1 hour
ago. Those things need not block each other, but with this design
they will. Another problem is that we end up holding it across an
I/O; there's precedent for that, but it's not particularly good
precedent. Let's see if we can do better.

My first idea was that we should just make this the caller's problem
instead of handling it in this layer. Undo is retained for committed
transactions until they are all-visible, and the reason for that is
that we presume that nobody can be interested in the data for MVCC
purposes unless there's a snapshot that can't see the results of the
transaction in question. Once the committed transaction is
all-visible, that's nobody, so it should be fine to just discard the
undo any time we like. That won't work with the existing zheap code,
which currently sometimes follows undo chains for transactions that
are all-visible, but I think that's a problem we should fix rather
than something we should force the undo layer to support. We'd still
need something kinda like the discard_lock for aborted transactions,
though, because as soon as you release the buffer lock on a table
page, the undo workers could apply all the undo to that page and then
discard it, and then you could afterwards try to look up the undo
pointer which you had retrieved from that page and stored in
backend-local memory. One thing we could probably do is make that a
heavyweight lock on the XID itself, so if you observe that an XID is
aborted, you have to go get this lock in ShareLock mode, then recheck
the page, and only then consult the undo; discarding the undo for an
aborted transaction would require AccessExclusiveLock on the XID.
This solution gets rid of the LWLock for committed undo; for aborted
undo, it avoids the false sharing and non-interruptibility that an
LWLock imposes.

But then I had what I think may be a better idea. Let's add a new
ReadBufferMode that suppresses the actual I/O; if the buffer is not
already present in shared_buffers, it allocates a buffer but returns
it without doing any I/O, so the caller must be prepared for BM_VALID
to be unset. I don't know what to call this, so I'll call it
RBM_ALLOCATE (leaving room for possible future variants like
RBM_ALLOCATE_AND_LOCK). Then, the protocol for reading an undo buffer
would go like this:

1. Read the buffer with RBM_ALLOCATE, thus acquiring a pin on the
relevant buffer.
2. Check whether the buffer precedes the discard horizon for that undo
log stored in shared memory.
3. If so, use the ForgetBuffer() code we have in the zheap branch to
deallocate the buffer and stop here. The undo is not available to be
read, whether it's still physically present or not.
4. Otherwise, if the buffer is not valid, call ReadBufferExtended
again, or some new function, to make it so. Remember to release all
of our pins.

The protocol for discarding an undo buffer would go like this:

1. Advance the discard horizon in shared memory.
2. Take a cleanup lock on each buffer that ought to be discarded.
Remember the dirty ones and forget the others.
3. WAL-log the discard operation.
4. Revisit the dirty buffers we remembered in step 2 and forget them.

The idea is that, once we've advanced the discard horizon in shared
memory, any readers that come along later are responsible for making
sure that they never do I/O on any older undo. They may create some
invalid buffers in shared memory, but they'll hopefully also get rid
of them if they do, and if they error out for some reason before doing
so, that buffer should age out naturally. So, the discard worker just
needs to worry about buffers that already exist. Once it's taken a
cleanup lock on each buffer, it knows that there are no I/O operations
and in fact no buffer usage of any kind still in progress from before
it moved the in-memory discard horizon. Anyone new that comes along
will clean up after themselves. We postpone forgetting dirty buffers
until after we've successfully WAL-logged the discard, in case we fail
to do so.

With this design, we don't add any new cases where a lock of any kind
must be held across an I/O, and there's also no false sharing.
Furthermore, unlike the previous proposal, this will work nicely with
something like old_snapshot_threshold. The previous design relies on
undo not getting discarded while anyone still cares about it, but
old_snapshot_threshold, if applied to zheap, would have the express
goal of discarding undo while somebody still cares about it. With
this design, we could support old_snapshot_threshold by having undo
readers error out in step #2 if the transaction is committed and not
visible to our snapshot but yet the undo is discarded. Heck, we can
do that anyway as a safety check, basically for free, and just tailor
the error message depending on whether old_snapshot_threshold is such
that the condition is expected to be possible.

While I'm kvetching, I can't help noticing that undoinsert.c contains
functions both for inserting undo and also for reading it, which seems
like a loose end that needs to be tied up somehow. I'm mildly
inclined to think that we should rename the file to something more
generic (e.g. undoaccess.h) rather than splitting it into two files
(e.g. undoinsert.c and undoread.c). Also, it looks to me like you
need to go through what is currently undoinsert.h and look for stuff
that can be made private to the .c file. I don't see why thing like
MAX_PREPARED_UNDO need to be exposed at all, and for things like
PreparedUndoSpace it seems like it would suffice to just do 'struct
PreparedUndoSpace; typedef struct PreparedUndoSpace
PreparedUndoSpace;' in the header and put the actual 'struct
PreparedUndoSpace { ... };' definition in the .c file. And
UnlockReleaseUndoBuffers has a declaration but no longer has a
definition, so I think that can go away too.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#33Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#32)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, May 13, 2019 at 11:36 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, May 12, 2019 at 2:15 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I have removed some of the globals and also improved some comments.

I don't like the discard_lock very much. Perhaps it's OK, but I hope
that there are better alternatives. One problem with Thomas Munro
pointed out to me in off-list discussion is that the discard_lock has
to be held by anyone reading undo even if the undo they are reading
and the undo that the discard worker wants to discard are in
completely different parts of the undo log. Somebody could be trying
to read an undo page written 1 second ago while the discard worker is
trying to discard an undo page written to the same undo log 1 hour
ago. Those things need not block each other, but with this design
they will.

Yeah, this doesn't appear to be a good way to deal with the problem.

Another problem is that we end up holding it across an
I/O; there's precedent for that, but it's not particularly good
precedent. Let's see if we can do better.

But then I had what I think may be a better idea.

+1. I also think the below idea is better than the previous one.

Let's add a new
ReadBufferMode that suppresses the actual I/O; if the buffer is not
already present in shared_buffers, it allocates a buffer but returns
it without doing any I/O, so the caller must be prepared for BM_VALID
to be unset. I don't know what to call this, so I'll call it
RBM_ALLOCATE (leaving room for possible future variants like
RBM_ALLOCATE_AND_LOCK). Then, the protocol for reading an undo buffer
would go like this:

1. Read the buffer with RBM_ALLOCATE, thus acquiring a pin on the
relevant buffer.
2. Check whether the buffer precedes the discard horizon for that undo
log stored in shared memory.
3. If so, use the ForgetBuffer() code we have in the zheap branch to
deallocate the buffer and stop here. The undo is not available to be
read, whether it's still physically present or not.
4. Otherwise, if the buffer is not valid, call ReadBufferExtended
again, or some new function, to make it so. Remember to release all
of our pins.

The protocol for discarding an undo buffer would go like this:

1. Advance the discard horizon in shared memory.
2. Take a cleanup lock on each buffer that ought to be discarded.
Remember the dirty ones and forget the others.
3. WAL-log the discard operation.
4. Revisit the dirty buffers we remembered in step 2 and forget them.

The idea is that, once we've advanced the discard horizon in shared
memory, any readers that come along later are responsible for making
sure that they never do I/O on any older undo. They may create some
invalid buffers in shared memory, but they'll hopefully also get rid
of them if they do, and if they error out for some reason before doing
so, that buffer should age out naturally. So, the discard worker just
needs to worry about buffers that already exist. Once it's taken a
cleanup lock on each buffer, it knows that there are no I/O operations
and in fact no buffer usage of any kind still in progress from before
it moved the in-memory discard horizon. Anyone new that comes along
will clean up after themselves. We postpone forgetting dirty buffers
until after we've successfully WAL-logged the discard, in case we fail
to do so.

I have spent some time thinking over this and couldn't see any problem
with this. So, +1 for trying this out on the lines of what you have
described above.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#34Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#32)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, May 13, 2019 at 11:36 PM Robert Haas <robertmhaas@gmail.com> wrote:

While I'm kvetching, I can't help noticing that undoinsert.c contains
functions both for inserting undo and also for reading it, which seems
like a loose end that needs to be tied up somehow. I'm mildly
inclined to think that we should rename the file to something more
generic (e.g. undoaccess.h) rather than splitting it into two files
(e.g. undoinsert.c and undoread.c).

Changed to undoaccess
Also, it looks to me like you

need to go through what is currently undoinsert.h and look for stuff
that can be made private to the .c file. I don't see why thing like
MAX_PREPARED_UNDO need to be exposed at all,

Ideally, my previous patch should have got rid of MAX_PREPARED_UNDO as
we are now always allocating memory for prepared space but by mistake
I left it in this file. Now, I have removed it.

and for things like

PreparedUndoSpace it seems like it would suffice to just do 'struct
PreparedUndoSpace; typedef struct PreparedUndoSpace
PreparedUndoSpace;' in the header and put the actual 'struct
PreparedUndoSpace { ... };' definition in the .c file.

Changed, I think
typedef struct PreparedUndoSpace PreparedUndoSpace; in header and
PreparedUndoSpace { ... }; is fine.
And

UnlockReleaseUndoBuffers has a declaration but no longer has a
definition, so I think that can go away too.

Removed, and also cleaned some other such declarations.

Pending items to be worked upon:
a) Get rid of UndoRecInfo
b) Get rid of xid in generic undo code and unify epoch and xid to fxid
c) Get rid of discard lock
d) Move log switch related information from transaction header to new
log switch header

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Provide-interfaces-to-store-and-fetch-undo-records_v7.patchapplication/octet-stream; name=0001-Provide-interfaces-to-store-and-fetch-undo-records_v7.patchDownload
From 869eab6d27291ea0740d905613f341ade3577bed Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoaccess.c         | 1495 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         |  744 +++++++++++++
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  115 ++
 src/include/access/undorecord.h              |  231 ++++
 7 files changed, 2616 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..f41e8f7 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoinsert.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 0000000..b3c9cf3
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1495 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the previous log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the last undo record of
+ * the transaction in the previous log, so that we can find the previous undo
+ * record pointer during rollback.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoaccess.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static void UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+						   UndoRecPtr xact_urp)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	Page		page;
+	RelFileNode rnode;
+	UndoLogControl *log;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info =
+	&context->xact_urec_info[context->nxact_urec_info];
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	log = UndoLogGet(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (log->meta.persistence == UNDO_TEMP)
+		return;
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&log->discard_lock, LW_SHARED);
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 *
+	 * Refer comments in UndoFetchRecord.
+	 */
+	if (InHotStandby)
+	{
+		if (UndoLogIsDiscarded(xact_urp))
+			return;
+	}
+	else
+	{
+		LWLockAcquire(&log->discard_lock, LW_SHARED);
+		if (xact_urp < log->oldest_data)
+		{
+			LWLockRelease(&log->discard_lock);
+			return;
+		}
+	}
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (1)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		buffer = context->prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_info->uur);
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&log->discard_lock);
+
+	/*
+	 * Set current transaction undo record pointer in previous transaction's
+	 * undo record header.
+	 */
+	xact_info->uur.uur_next = urecptr;
+
+	/*
+	 * Store undo record pointer of the previous transaction's header in the
+	 * context because we need to overwrite the header undo record in the
+	 * update phase.
+	 */
+	xact_info->urecptr = xact_urp;
+
+	context->nxact_urec_info++;
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			starting_byte;
+	int			i = 0;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/*
+	 * Update the next transactions start urecptr in the transaction header.
+	 */
+	starting_byte = UndoRecPtrGetPageOffset(xact_info->urecptr);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, &xact_info->uur);
+
+	/* Main loop for updating the undo record. */
+	while (1)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * removed by discard process, so we can just skip inserting into
+		 * those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * If the buffer is not valid then skip actual writing just move
+			 * the write offset in the context so that if the next buffer is
+			 * valid we have the correct offset of the record for inserting
+			 * into that buffer.
+			 */
+			SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+				break;
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/* Overwrite the previously written undo record. */
+			InsertUndoData(&ucontext, page, starting_byte);
+
+			/*
+			 * We only want to overwrite the transaction header so if we have
+			 * already done so then stop.
+			 */
+			if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			{
+				MarkBufferDirty(buffer);
+				break;
+			}
+			MarkBufferDirty(buffer);
+		}
+
+		/*
+		 * Record header is spilt across blocks so go to the next block and
+		 * continue writing there.  Start writing after the undo block header.
+		 */
+		starting_byte = UndoLogBlockHeaderSize;
+		i++;
+
+		Assert(idx < MAX_BUFFER_PER_UNDO);
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	urec->uur_prevurp = prevlogurp;
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = 0;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareTransInfo(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareTransInfo(context, urecptr, prevlog_xact_start);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/* Free memory allocated for the prepare undo and prepared buffers. */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogControl *log;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			urp = InvalidUndoRecPtr;
+			break;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 *
+		 * In Hot Standby mode log->oldest_data is never initialized because
+		 * it's get updated by undo discard worker whereas in HotStandby undo
+		 * logs are getting discarded using discard WAL.  So in HotStandby we
+		 * can directly check whether the undo record pointer is discarded or
+		 * not.  But, we can not do same for normal case because discard
+		 * worker can concurrently discard the undo logs.
+		 *
+		 * XXX We can avoid this check by always initializing log->oldest_data
+		 * in HotStandby mode as well whenever we apply discard WAL.  But, for
+		 * doing that we need to acquire discard lock just for setting this
+		 * variable?
+		 */
+		if (InHotStandby)
+		{
+			if (UndoLogIsDiscarded(urp))
+			{
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+		else
+		{
+			LWLockAcquire(&log->discard_lock, LW_SHARED);
+			if (urp < log->oldest_data)
+			{
+				LWLockRelease(&log->discard_lock);
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+
+		/* Fetch the current undo record. */
+		UndoGetOneRecord(urec, urp, rnode, log->meta.persistence, &buffer);
+
+		/* Release the discard lock after fetching the record. */
+		LWLockRelease(&log->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	/*
+	 * If we have not found any valid undo record that means it might have
+	 * already got discarded so release the memory we allocated for unpacked
+	 * undo record and set urec to NULL.
+	 */
+	if (!UndoRecPtrIsValid(urp))
+	{
+		pfree(urec);
+		urec = NULL;
+	}
+	else if (urec_ptr_out != NULL)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogControl *log;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		log = UndoLogGet(logno, true);
+		if (log == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = log->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoLogIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&log->discard_lock, LW_SHARED);
+				if (urecptr < log->oldest_data)
+				{
+					LWLockRelease(&log->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			LWLockRelease(&log->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit then
+		 * stop processing more records.  Remember to set the from_urecptr so
+		 * that on next call we can resume fetching undo records where we left
+		 * it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..7e585ac
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,744 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_prevurp = ucontext->urec_txn.urec_prevurp;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7966a9e..592c338 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 0000000..3e2154b
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,115 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* txn's start urecptr */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+	UnpackedUndoRecord uur;		/* undo record header */
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..4ce1e76
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_FORK						0x01
+#define UREC_INFO_BLOCK						0x02
+#define UREC_INFO_BLKPREV					0x04
+#define UREC_INFO_TRANSACTION				0x08
+#define UREC_INFO_PAYLOAD					0x10
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * This indicates undo action apply progress, 0 means not started, 1 means
+	 * completed.  In future, it can also be used to show the progress of how
+	 * much undo has been applied so far with some formula.
+	 */
+	uint32		urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 *
+	 * TODO: instead of keeping in transaction header we can have new log
+	 * switch header.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	Oid			uur_dbid;		/* database id */
+
+	/* undo applying progress, see detail comment in UndoRecordTransaction */
+	uint32		uur_progress;
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

#35Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#23)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-05-05 10:28:21 +0530, Amit Kapila wrote:

From 5d9e179bd481b5ed574b6e7117bf3eb62b5dc003 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Sat, 4 May 2019 16:52:01 +0530
Subject: [PATCH] Allow undo actions to be applied on rollbacks and discard
unwanted undo.

I think this needs to be split into some constituent parts, to be
reviewable. Discussing 270kb of patch at once is just too much. My first
guess for a viable split would be:

1) undoaction related infrastructure
2) xact.c integration et al
3) binaryheap changes etc
4) undo worker infrastructure

It probably should be split even further, by moving things like:
- oldestXidHavingUndo infrastructure
- discard infrastructure

Some small remarks:

+	{
+		{"disable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		},
+		&disable_undo_launcher,
+		false,
+		NULL, NULL, NULL
+	},
+

We don't normally formulate GUCs in the negative like that. C.F.
autovacuum etc.

+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)			\
+	((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)			\
+	((uint32) ((epochxid) >> 32))
+

Why do these exist? This should all go through FullTransactionId.

/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2923,6 +2935,16 @@ static struct config_int ConfigureNamesInt[] =
5000, 1, INT_MAX,
NULL, NULL, NULL
},
+	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},

rollback_foreground_size? rollback_background_size? I don't think
overflow is particularly clear.

@@ -1612,6 +1635,85 @@ FinishPreparedTransaction(const char *gid, bool isCommit)

MyLockedGxact = NULL;

+	/*
+	 * Perform undo actions, if there are undologs for this transaction. We
+	 * need to perform undo actions while we are still in transaction. Never
+	 * push rollbacks of temp tables to undo worker.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{

This should be in a separate function. And it'd be good if more code
between this and ApplyUndoActions() would be shared.

+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	SetUndoActionsInfo();

This function name is so generic that it gives the reader very little
information about why it's called here (and in other similar places).

Greetings,

Andres Freund

#36Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#35)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, May 21, 2019 at 1:18 PM Andres Freund <andres@anarazel.de> wrote:

I think this needs to be split into some constituent parts, to be
reviewable. Discussing 270kb of patch at once is just too much.

+1.

+     {
+             {"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+                     gettext_noop("Rollbacks greater than this size are done lazily"),
+                     NULL,
+                     GUC_UNIT_MB
+             },
+             &rollback_overflow_size,
+             64, 0, MAX_KILOBYTES,
+             NULL, NULL, NULL
+     },

rollback_foreground_size? rollback_background_size? I don't think
overflow is particularly clear.

The problem with calling it 'rollback' is that a rollback is a general
PostgreSQL term that gives no hint the proposed undo facility is
involved. I'm not exactly sure what to propose but I think it's got
to have the word 'undo' in there someplace (or some new term we invent
that is only used in connection with undo).

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#37Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#35)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, May 21, 2019 at 10:47 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-05-05 10:28:21 +0530, Amit Kapila wrote:

From 5d9e179bd481b5ed574b6e7117bf3eb62b5dc003 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Sat, 4 May 2019 16:52:01 +0530
Subject: [PATCH] Allow undo actions to be applied on rollbacks and discard
unwanted undo.

I think this needs to be split into some constituent parts, to be
reviewable.

Okay.

Discussing 270kb of patch at once is just too much. My first
guess for a viable split would be:

1) undoaction related infrastructure
2) xact.c integration et al
3) binaryheap changes etc
4) undo worker infrastructure

It probably should be split even further, by moving things like:
- oldestXidHavingUndo infrastructure
- discard infrastructure

Okay, I will think about this and split the patch.

Some small remarks:

+     {
+             {"disable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+                     gettext_noop("Decides whether to launch an undo worker."),
+                     NULL,
+                     GUC_NOT_IN_SAMPLE
+             },
+             &disable_undo_launcher,
+             false,
+             NULL, NULL, NULL
+     },
+

We don't normally formulate GUCs in the negative like that. C.F.
autovacuum etc.

Okay, will change. Actually, this is just for development purpose.
It can help us in testing cases where we have pushed the undo, but it
won't apply, so whenever the foreground process encounter such a
transaction, it will perform the page-wise undo. I am not 100% sure
if we need this for the final version. Similarly, for testing
purpose, we might need enable_discard_worker to test the cases where
discard doesn't happen for a long time.

+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)                 \
+     ((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)                       \
+     ((uint32) ((epochxid) >> 32))
+

Why do these exist?

We don't need the second one (GetEpochFromEpochXid), but the first one
is required. Basically, the oldestXidHavingUndo computation does
consider oldestXmin (which is still a TransactionId) as we can't
retain undo which is 2^31 transactions old due to other limitations
like clog/snapshots still has a limit of 4-byte transaction ids.
Slightly unrelated, but we do want to improve the undo retention in a
subsequent version such that we won't allow pending undo for
transaction whose age is more than 2^31.

/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2923,6 +2935,16 @@ static struct config_int ConfigureNamesInt[] =
5000, 1, INT_MAX,
NULL, NULL, NULL
},
+     {
+             {"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+                     gettext_noop("Rollbacks greater than this size are done lazily"),
+                     NULL,
+                     GUC_UNIT_MB
+             },
+             &rollback_overflow_size,
+             64, 0, MAX_KILOBYTES,
+             NULL, NULL, NULL
+     },

rollback_foreground_size? rollback_background_size? I don't think
overflow is particularly clear.

How about rollback_undo_size or abort_undo_size or
undo_foreground_size or pending_undo_size?

@@ -1612,6 +1635,85 @@ FinishPreparedTransaction(const char *gid, bool isCommit)

MyLockedGxact = NULL;

+     /*
+      * Perform undo actions, if there are undologs for this transaction. We
+      * need to perform undo actions while we are still in transaction. Never
+      * push rollbacks of temp tables to undo worker.
+      */
+     for (i = 0; i < UndoPersistenceLevels; i++)
+     {

This should be in a separate function. And it'd be good if more code
between this and ApplyUndoActions() would be shared.

makes sense, will try.

+     /*
+      * Here, we just detect whether there are any pending undo actions so that
+      * we can skip releasing the locks during abort transaction.  We don't
+      * release the locks till we execute undo actions otherwise, there is a
+      * risk of deadlock.
+      */
+     SetUndoActionsInfo();

This function name is so generic that it gives the reader very little
information about why it's called here (and in other similar places).

NeedToPerformUndoActions()? UndoActionsRequired()?

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#38Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#37)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 22, 2019 at 7:17 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)                 \
+     ((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)                       \
+     ((uint32) ((epochxid) >> 32))
+

Why do these exist?

We don't need the second one (GetEpochFromEpochXid), but the first one
is required. Basically, the oldestXidHavingUndo computation does
consider oldestXmin (which is still a TransactionId) as we can't
retain undo which is 2^31 transactions old due to other limitations
like clog/snapshots still has a limit of 4-byte transaction ids.
Slightly unrelated, but we do want to improve the undo retention in a
subsequent version such that we won't allow pending undo for
transaction whose age is more than 2^31.

The point is that we now have EpochFromFullTransactionId and
XidFromFullTransactionId. You shouldn't be inventing your own version
of that infrastructure. Use FullTransactionId, not a uint64, and then
use the functions for dealing with full transaction IDs from
transam.h.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#39Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#38)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 22, 2019 at 5:47 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, May 22, 2019 at 7:17 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)                 \
+     ((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)                       \
+     ((uint32) ((epochxid) >> 32))
+

Why do these exist?

We don't need the second one (GetEpochFromEpochXid), but the first one
is required. Basically, the oldestXidHavingUndo computation does
consider oldestXmin (which is still a TransactionId) as we can't
retain undo which is 2^31 transactions old due to other limitations
like clog/snapshots still has a limit of 4-byte transaction ids.
Slightly unrelated, but we do want to improve the undo retention in a
subsequent version such that we won't allow pending undo for
transaction whose age is more than 2^31.

The point is that we now have EpochFromFullTransactionId and
XidFromFullTransactionId. You shouldn't be inventing your own version
of that infrastructure. Use FullTransactionId, not a uint64, and then
use the functions for dealing with full transaction IDs from
transam.h.

Okay, I misunderstood the comment. I'll change accordingly. Thanks
for pointing out.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#40Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#37)
2 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, May 22, 2019 at 4:47 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, May 21, 2019 at 10:47 PM Andres Freund <andres@anarazel.de> wrote:

Some small remarks:

+     {
+             {"disable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+                     gettext_noop("Decides whether to launch an undo worker."),
+                     NULL,
+                     GUC_NOT_IN_SAMPLE
+             },
+             &disable_undo_launcher,
+             false,
+             NULL, NULL, NULL
+     },
+

We don't normally formulate GUCs in the negative like that. C.F.
autovacuum etc.

Okay, will change. Actually, this is just for development purpose.
It can help us in testing cases where we have pushed the undo, but it
won't apply, so whenever the foreground process encounter such a
transaction, it will perform the page-wise undo. I am not 100% sure
if we need this for the final version. Similarly, for testing
purpose, we might need enable_discard_worker to test the cases where
discard doesn't happen for a long time.

Changed.

+/* Extract xid from a value comprised of epoch and xid  */
+#define GetXidFromEpochXid(epochxid)                 \
+     ((uint32) (epochxid) & 0XFFFFFFFF)
+
+/* Extract epoch from a value comprised of epoch and xid  */
+#define GetEpochFromEpochXid(epochxid)                       \
+     ((uint32) ((epochxid) >> 32))
+

Why do these exist?

We don't need the second one (GetEpochFromEpochXid), but the first one
is required. Basically, the oldestXidHavingUndo computation does
consider oldestXmin (which is still a TransactionId) as we can't
retain undo which is 2^31 transactions old due to other limitations
like clog/snapshots still has a limit of 4-byte transaction ids.
Slightly unrelated, but we do want to improve the undo retention in a
subsequent version such that we won't allow pending undo for
transaction whose age is more than 2^31.

Removed both the above defines.

/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2923,6 +2935,16 @@ static struct config_int ConfigureNamesInt[] =
5000, 1, INT_MAX,
NULL, NULL, NULL
},
+     {
+             {"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+                     gettext_noop("Rollbacks greater than this size are done lazily"),
+                     NULL,
+                     GUC_UNIT_MB
+             },
+             &rollback_overflow_size,
+             64, 0, MAX_KILOBYTES,
+             NULL, NULL, NULL
+     },

rollback_foreground_size? rollback_background_size? I don't think
overflow is particularly clear.

How about rollback_undo_size or abort_undo_size or
undo_foreground_size or pending_undo_size?

I think we need some more discussion on this before we change as
Robert seems to feel that we should have 'undo' someplace in the name.
Please let me know your
preference.

@@ -1612,6 +1635,85 @@ FinishPreparedTransaction(const char *gid, bool isCommit)

MyLockedGxact = NULL;

+     /*
+      * Perform undo actions, if there are undologs for this transaction. We
+      * need to perform undo actions while we are still in transaction. Never
+      * push rollbacks of temp tables to undo worker.
+      */
+     for (i = 0; i < UndoPersistenceLevels; i++)
+     {

This should be in a separate function. And it'd be good if more code
between this and ApplyUndoActions() would be shared.

makes sense, will try.

Done. Now, there is a common function that is used in twophase.c and
ApplyUndoActions.

+     /*
+      * Here, we just detect whether there are any pending undo actions so that
+      * we can skip releasing the locks during abort transaction.  We don't
+      * release the locks till we execute undo actions otherwise, there is a
+      * risk of deadlock.
+      */
+     SetUndoActionsInfo();

This function name is so generic that it gives the reader very little
information about why it's called here (and in other similar places).

NeedToPerformUndoActions()? UndoActionsRequired()?

Changed to UndoActionsRequired and added comments atop of the function
to make it clear why and when this function needs to use.

Apart from fixing the above comments, the patch is rebased on latest
undo patchset. As of now, I have split the binaryheap.c changes into
a separate patch. We are stilll enhancing the patch to compute
oldestXidHavingUnappliedUndo which touches various parts of patch, so
splitting further without completing that can make it a bit difficult
to work on that.

Pending work
-------------------
1. Enhance uur_progress so that it updates undo action apply progress
at regular intervals.
2. Enhance to support oldestXidHavingUnappliedUndo, more on that later.
3. Split the patch.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Extend-binary-heap-functionality.patchapplication/octet-stream; name=0001-Extend-binary-heap-functionality.patchDownload
From 1b69381a9444cb372d43b575d6305b341e9d0794 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:02:23 +0530
Subject: [PATCH 1/2] Extend binary heap functionality.

Add the routines to allocate binary heap in shared memory and to remove
nth element from binray heap.  This routines will be used by latter commit
to add support for undo workers.

Author: Kuntal Ghosh and Amit Kapila
---
 src/backend/lib/binaryheap.c | 118 +++++++++++++++++++++++++++++++++++++++++++
 src/include/lib/binaryheap.h |  12 ++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..5f7454d 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -212,6 +243,79 @@ binaryheap_replace_first(binaryheap *heap, Datum d)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap in O(1).  The caller must ensure that this routine is not used on
+ * an empty heap and is not called with n greater than or equal to the heap
+ * size.
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer to it in
+ * O(1) without preserving the heap property.  This is a convenience routine
+ * to remove elements quickly.  To obtain a valid heap, one must call
+ * binaryheap_build() afterwards.  The caller must ensure that this routine is
+ * not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * Swap the contents of two nodes.
  */
 static inline void
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 21f96b9..ed9e8e8 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -38,8 +38,11 @@ typedef struct binaryheap
 } binaryheap;
 
 extern binaryheap *binaryheap_allocate(int capacity,
-									   binaryheap_comparator compare,
-									   void *arg);
+					binaryheap_comparator compare,
+					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
+extern Size binaryheap_shmem_size(int capacity);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
-- 
1.8.3.1

0002-Allow-undo-actions-to-be-applied-on-rollbacks-and-di.patchapplication/octet-stream; name=0002-Allow-undo-actions-to-be-applied-on-rollbacks-and-di.patchDownload
From 9c564435d384a6de8cdf22ad25ada5b62ece525c Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:20:36 +0530
Subject: [PATCH 2/2] Allow undo actions to be applied on rollbacks and discard
 unwanted undo.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

To improve the efficiency of the rollbacks, we create three queues and a
hash table for the rollback requests.  A Xid based priority queue which
will allow us to process the requests of older transactions and help us to
move oldesdXidHavingUndo (this is a xid-horizon below which all the
transactions are visible) forward.  A size-based queue which will help us
to perform the rollbacks of larger aborts in a timely fashion so that we
don't get stuck while processing them during discard of the logs.  An error
queue to hold the requests for transactions that failed to apply its undo.
The rollback hash table is used to avoid duplicate undo requests by
backends and discard worker.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

Designed by: Andres Freund, Amit Kapila, Robert Haas, and Thomas Munro
Author: Amit Kapila, Dilip Kumar, Kuntal Ghosh, and Thomas Munro
---
 src/backend/access/rmgrdesc/Makefile          |    3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c  |   47 +
 src/backend/access/rmgrdesc/xlogdesc.c        |    4 +-
 src/backend/access/transam/rmgr.c             |    5 +-
 src/backend/access/transam/twophase.c         |   49 +-
 src/backend/access/transam/varsup.c           |   46 +
 src/backend/access/transam/xact.c             |  554 +++++++++-
 src/backend/access/transam/xlog.c             |   29 +
 src/backend/access/undo/Makefile              |    3 +-
 src/backend/access/undo/README.UndoProcessing |   96 ++
 src/backend/access/undo/discardworker.c       |  207 ++++
 src/backend/access/undo/undoaccess.c          |   83 +-
 src/backend/access/undo/undoaction.c          |  325 ++++++
 src/backend/access/undo/undoactionxlog.c      |   59 +
 src/backend/access/undo/undodiscard.c         |  396 +++++++
 src/backend/access/undo/undolog.c             |    2 +
 src/backend/access/undo/undorequest.c         | 1464 +++++++++++++++++++++++++
 src/backend/access/undo/undoworker.c          |  806 ++++++++++++++
 src/backend/commands/tablecmds.c              |    5 +
 src/backend/postmaster/bgworker.c             |   11 +
 src/backend/postmaster/pgstat.c               |    9 +
 src/backend/postmaster/postmaster.c           |   11 +
 src/backend/replication/logical/decode.c      |    1 +
 src/backend/storage/ipc/ipci.c                |    6 +
 src/backend/storage/lmgr/lwlocknames.txt      |    2 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/error/elog.c                |   36 +-
 src/backend/utils/init/globals.c              |    7 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    6 +
 src/backend/utils/resowner/resowner.c         |   11 +-
 src/bin/pg_rewind/parsexlog.c                 |    2 +-
 src/bin/pg_waldump/rmgrdesc.c                 |    3 +-
 src/include/access/discardworker.h            |   20 +
 src/include/access/rmgr.h                     |    2 +-
 src/include/access/rmgrlist.h                 |   47 +-
 src/include/access/transam.h                  |   12 +
 src/include/access/twophase.h                 |    4 +-
 src/include/access/undoaccess.h               |    4 +
 src/include/access/undoaction_xlog.h          |   39 +
 src/include/access/undodiscard.h              |   25 +
 src/include/access/undolog.h                  |   16 +-
 src/include/access/undorequest.h              |  140 +++
 src/include/access/undoworker.h               |   27 +
 src/include/access/xact.h                     |   12 +
 src/include/access/xlog_internal.h            |    9 +-
 src/include/access/xlogrecord.h               |    1 +
 src/include/catalog/pg_control.h              |    7 +
 src/include/miscadmin.h                       |    4 +
 src/include/nodes/primnodes.h                 |    3 +-
 src/include/pgstat.h                          |    5 +-
 src/include/postmaster/postmaster.h           |    2 +
 src/include/storage/lock.h                    |    6 +
 src/include/storage/lwlock.h                  |    2 +-
 src/include/storage/proc.h                    |    2 +
 src/include/storage/procarray.h               |    7 +-
 src/include/utils/elog.h                      |    3 +
 src/test/regress/expected/sysviews.out        |    3 +-
 59 files changed, 4666 insertions(+), 62 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/README.UndoProcessing
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undoaction_xlog.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undorequest.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..1de1bbc 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest xid with epoch having undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..6238240 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 5196d61..ad3dcb7 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -927,6 +927,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1011,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1043,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1484,8 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1523,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1612,6 +1634,31 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	if (!isCommit)
+	{
+		FullTransactionId full_xid;
+		uint32	epoch;
+
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id
+		 * in TwoPhaseFileHeader instead. )
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Perform undo actions, if there are undologs for this transaction. We
+		 * need to perform undo actions while we are still in transaction.
+		 */
+		if (!PerformUndoActions(full_xid, hdr->database,
+								end_urec_ptr, start_urec_ptr,
+								false))
+		{
+			FlushErrorState();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..03c31bf 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -301,6 +301,30 @@ AdvanceNextFullTransactionIdPastXid(TransactionId xid)
 }
 
 /*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
+
+/*
  * Advance the cluster-wide value for the oldest valid clog entry.
  *
  * We must acquire CLogTruncationLock to advance the oldestClogXid. It's not
@@ -334,10 +358,25 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUndo because this is the oldest xid whose undo
+	 * is not yet discarded so this is still a valid xid in the system.  The
+	 * oldestXidHavingUndo will be only valid for zheap table access method,
+	 * so it won't impact any other table access method.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -404,6 +443,13 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	curXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
 	LWLockRelease(XidGenLock);
 
+	/*
+	 * Fixme - The messages in below code need some adjustment for zheap. They
+	 * should reflect that the system needs to discard the undo.  We can add
+	 * it once we have a pluggable storage API which might provide us some way
+	 * to distinguish among differnt storage engines.
+	 */
+
 	/* Log the info */
 	ereport(DEBUG1,
 			(errmsg("transaction ID wrap limit is %u, limited by database with OID %u",
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index f1108cc..62a9049 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,13 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -362,9 +373,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +734,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +921,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -997,6 +1013,24 @@ IsInParallelMode(void)
 }
 
 /*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
+
+/*
  *	CommandCounterIncrement
  */
 void
@@ -1885,6 +1919,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1968,6 +2003,14 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2245,6 +2288,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
@@ -2264,6 +2311,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2280,7 +2329,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2433,7 +2482,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2622,7 +2671,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2780,6 +2831,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2898,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2961,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3050,13 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			UndoActionsRequired();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3008,7 +3073,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3052,6 +3117,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3065,7 +3148,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3087,7 +3170,9 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			UndoActionsRequired();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3192,9 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				UndoActionsRequired();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3160,6 +3247,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	UndoActionsRequired();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3175,7 +3270,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3191,6 +3290,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3301,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3216,6 +3320,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3227,6 +3332,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_END:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3256,6 +3362,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_ABORT_PENDING:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3267,6 +3374,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_PREPARE:
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3278,6 +3386,7 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_SUBINPROGRESS:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3292,6 +3401,7 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,7 +3414,120 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
+	}
+}
+
+/*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	bool		ret;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
 	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	ret = PerformUndoActions(GetTopFullTransactionId(), MyDatabaseId,
+							 s->latest_urec_ptr, s->start_urec_ptr,
+							 IsSubTransaction());
+
+	if (!ret)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
+	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
 }
 
 /*
@@ -3633,6 +3856,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +4050,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4168,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4310,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4099,6 +4330,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4157,6 +4400,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4192,8 +4437,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4266,6 +4540,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4660,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4404,6 +4682,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4422,6 +4701,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4473,17 +4768,29 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	UndoActionsRequired();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4510,6 +4817,14 @@ AbortOutOfAnyTransaction(void)
 	AtAbort_Memory();
 
 	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	UndoActionsRequired();
+
+	/*
 	 * Get out of any transaction or nested transaction
 	 */
 	do
@@ -4529,7 +4844,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4546,6 +4865,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
 				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
 				break;
@@ -4573,6 +4905,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
 				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
@@ -4666,6 +5011,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4705,6 +5052,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4722,6 +5070,14 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+	s->performUndoActions = false;
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4909,7 +5265,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5336,6 +5693,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5713,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5740,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6340,162 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * UndoActionsRequired - Set the information required to perform undo actions.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing the locks if required.
+ */
+void
+UndoActionsRequired(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
+
+/*
+ * PerformUndoActions - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ */
+bool
+PerformUndoActions(FullTransactionId fxid, Oid dbid, UndoRecPtr *end_urec_ptr,
+				   UndoRecPtr *start_urec_ptr, bool isSubTrans)
+{
+	volatile	UndoRequestInfo urinfo;
+	uint32		save_holdoff;
+	int			per_level;
+	bool		success = true;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (end_urec_ptr[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				bool		result = false;
+
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+
+				/*
+				 * If this request is not for a temp table and not aborting
+				 * subtransaction and the request size is greater than some
+				 * threshold then push it to undo-worker through RollbackHT,
+				 * undo-worker will perform the corresponding undo actions
+				 * later.
+				 *
+				 * We can't push the undo actions for temp table to background
+				 * workers as the the temp tables are only accessible in the
+				 * backend that has created them.  We can't postpone applying
+				 * undo actions for subtransactions as the modifications made
+				 * by aborted subtransaction must not be visible even if the
+				 * main transaction commits.  It is not advisable to apply the
+				 * undo actions of a very large transaction as that can lead
+				 * to a delay in retruning the control back to user after
+				 * abort.
+				 */
+				if (per_level != UNDO_TEMP && !isSubTrans)
+					result = RegisterRollbackReq(end_urec_ptr[per_level],
+												 start_urec_ptr[per_level],
+												 dbid,
+												 urinfo.full_xid);
+				if (!result)
+				{
+					/* for subtransactions, we do partial rollback. */
+					execute_undo_actions(urinfo.full_xid,
+										 end_urec_ptr[per_level],
+										 start_urec_ptr[per_level],
+										 !isSubTrans);
+				}
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then
+				 * remove the entry from the hash table and continue to
+				 * process the remaining undo requests if any.  This request
+				 * will be later processed by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTRemoveEntry(urinfo.full_xid, urinfo.start_urec_ptr);
+
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index d247547..e0abd96 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5162,6 +5162,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6613,6 +6614,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6629,6 +6633,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7317,7 +7325,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8714,6 +8728,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9626,6 +9644,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9683,12 +9704,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUndo =
+			checkPoint.oldestFullXidHavingUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUndo =
+			checkPoint.oldestFullXidHavingUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9728,6 +9754,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 049a416..edf8248 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o
+OBJS = discardworker.o undoaction.o undoactionxlog.o undodiscard.o undoaccess.o \
+		undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..ba5da5f
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,96 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue which will allow
+us to process the requests of older transactions and help us to move
+oldesdXidHavingUndo (this is a xid-horizon below which all the transactions are
+visible) forward.  A size-based queue which will help us to perform the rollbacks
+of larger aborts in a timely fashion, so that we don't get stuck while processing
+them during discard of the logs.  An error queue to hold the requests for
+transactions that failed to apply its undo.  The rollback hash table is used to
+avoid duplicate undo requests by backends and discard worker.  The table must be
+able to accommodate all active undo requests.  The undo requests must appear in
+both xid and size requests queues or neither.  As of now, we process the requests
+from these queues in a round-robin fashion to give equal priority to all three
+types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just remove the request from the hash table and
+continue to process other requests if any.  The discard worker will find this
+errored transaction at later point of time and again add it to the request
+queues.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..fa74298
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,207 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 73d396e..8077101 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -63,8 +63,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 static void UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
 						   UndoRecPtr urecptr,
 						   UndoRecPtr xact_urp);
-static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
-						  int idx);
 static int UndoGetBufferSlot(UndoRecordInsertContext *context,
 				  RelFileNode rnode, BlockNumber blk,
 				  ReadBufferMode rbm);
@@ -202,6 +200,72 @@ UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
 }
 
 /*
+ * Update the progress of the undo record in the transaction header.
+ */
+void
+PrepareUpdateUndoActionProgress(UndoRecordInsertContext *context,
+								XLogReaderState *xlog_record,
+								UndoRecPtr xact_urp, int progress)
+{
+	Buffer		buffer = InvalidBuffer;
+	BlockNumber cur_blk;
+	Page		page;
+	RelFileNode rnode;
+	UndoPersistence persistence;
+	UndoPackContext ucontext = {{0}};
+	XactUndoRecordInfo *xact_info =
+	&context->xact_urec_info[context->nxact_urec_info];
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+
+	Assert(UndoRecPtrIsValid(xact_urp));
+
+	persistence = UndoRecPtrGetPersistence(xact_urp);
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (persistence == UNDO_TEMP)
+		return;
+
+	/* It shouldn't be discarded. */
+	Assert(!UndoLogIsDiscarded(xact_urp));
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Initiate reading the undo record. */
+	BeginUnpackUndo(&ucontext);
+	while (1)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		buffer = context->prepared_undo_buffers[bufidx].buf;
+		page = BufferGetPage(buffer);
+
+		/* Do actual decoding. */
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/* We just want to fetch upto transaction header so stop after that. */
+		if (ucontext.stage > UNDO_PACK_STAGE_TRANSACTION)
+			break;
+
+		/* Could not fetch the complete header so go to the next block. */
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	FinishUnpackUndo(&ucontext, &xact_info->uur);
+
+	xact_info->urecptr = xact_urp;
+	xact_info->uur.uur_progress = progress;
+	context->nxact_urec_info++;
+}
+
+/*
  * Overwrite the first undo record of the previous transaction to update its
  * next pointer.
  *
@@ -209,7 +273,7 @@ UndoRecordPrepareTransInfo(UndoRecordInsertContext *context, UndoRecPtr urecptr,
  * This must be called under the critical section.  This will just overwrite the
  * header of the undo record.
  */
-static void
+void
 UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
@@ -219,6 +283,12 @@ UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
 
 	/*
+	 * We've pinned the undo buffers in UndoRecordPrepareTransInfo, so
+	 * it shouldn't be discarded.
+	 */
+	Assert(!UndoLogIsDiscarded(xact_info->urecptr));
+
+	/*
 	 * Update the next transactions start urecptr in the transaction header.
 	 */
 	starting_byte = UndoRecPtrGetPageOffset(xact_info->urecptr);
@@ -761,6 +831,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.persistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..0ba1441
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,325 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+#include "access/undodiscard.h"
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block == ruur->uur_block)
+	{
+		/*
+		 * If records are for the same block then maintain their existing
+		 * order by comparing their index in the array.  Because for single
+		 * block we need to maintain the order for applying undo action.
+		 */
+		if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+			return -1;
+		else
+			return 1;
+	}
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else
+		return 1;
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * nopartial	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	UndoRecPtr	urec_ptr;
+	ForkNumber	prev_fork = InvalidForkNumber;
+	BlockNumber prev_block = InvalidBlockNumber;
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	int			undo_apply_size = maintenance_work_mem * 1024L;
+	TransactionId	xid PG_USED_FOR_ASSERTS_ONLY = XidFromFullTransactionId(full_xid);
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	urec_ptr = from_urecptr;
+
+	if (nopartial)
+	{
+		/*
+		 * It is important here to fetch the latest undo record and validate if
+		 * the actions are already executed.  The reason is that it is possible
+		 * that discard worker or backend might try to execute the rollback
+		 * request which is already executed.  For ex., after discard worker
+		 * fetches the record and found that this transaction need to be
+		 * rolledback, backend might concurrently execute the actions and
+		 * remove the request from rollback hash table. The similar problem
+		 * can happen if the discard worker first pushes the request, the undo
+		 * worker processed it and backend tries to process it some later point.
+		 */
+		uur = UndoFetchRecord(to_urecptr, InvalidBlockNumber, InvalidOffsetNumber,
+							  InvalidTransactionId, NULL, NULL);
+
+		/* already processed. */
+		if (uur == NULL)
+			return;
+
+		/*
+		 * We don't need to execute the undo actions if they are already
+		 * executed.
+		 */
+		if (uur->uur_progress != 0)
+		{
+			UndoRecordRelease(uur);
+			return;
+		}
+
+		Assert(xid == uur->uur_xid);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them in order of reloid and block number and then apply them
+	 * page-at-a-time.  Repeat this until we process all the records for the
+	 * transaction being rolled back.
+	 */
+	do
+	{
+		int			prev_rmid = -1;
+		Oid			prev_reloid = InvalidOid;
+		bool		blk_chain_complete;
+		int			i;
+		int			nrecords;
+		int			last_index = 0;
+		int			prefetch_pages = 0;
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+
+		/*
+		 * Fetch multiple undo records at once.  This will return the array
+		 * of undo records which holds undo record pointers and the pointers
+		 * to the actual unpacked undo record.   This will also update the
+		 * number of undo records it has copied in the urp_array.
+		 */
+		urp_array = UndoBulkFetchRecord(&urec_ptr, to_urecptr, undo_apply_size,
+										&nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(TransactionIdEquals(xid, urp_array[0].uur->uur_xid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urp_array, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		if (nopartial && !UndoRecPtrIsValid(urec_ptr))
+			blk_chain_complete = true;
+		else
+			blk_chain_complete = false;
+
+		/*
+		 * Now we have urp_array which is sorted in the block order so
+		 * traverse this array and apply the undo action block by block.
+		 */
+		for (i = last_index; i < nrecords; i++)
+		{
+			UnpackedUndoRecord *uur = urp_array[i].uur;
+
+			/*
+			 * If this undo is not for the same block then apply all undo
+			 * actions for the previous block.
+			 */
+			if (prev_rmid >= 0 &&
+				(prev_rmid != uur->uur_rmid ||
+				 prev_reloid != uur->uur_reloid ||
+				 prev_fork != uur->uur_fork ||
+				 prev_block != uur->uur_block))
+			{
+				execute_undo_actions_page(urp_array, last_index, i - 1,
+										  prev_reloid, full_xid, prev_block,
+										  blk_chain_complete);
+				last_index = i;
+
+				/* We have consumed one prefetched page. */
+				if (prefetch_pages > 0)
+					prefetch_pages--;
+			}
+
+			prev_rmid = uur->uur_rmid;
+			prev_reloid = uur->uur_reloid;
+			prev_fork = uur->uur_fork;
+			prev_block = uur->uur_block;
+		}
+
+		/* Apply the last set of the actions. */
+		execute_undo_actions_page(urp_array, last_index, i - 1,
+								  prev_reloid, full_xid, prev_block,
+								  blk_chain_complete);
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urp_array[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urp_array);
+	} while (true);
+
+	/*
+	 * Set undo action apply progress as completed in the transaction header
+	 * if this is a main transaction.
+	 */
+	if (nopartial)
+	{
+		UndoPersistence persistence;
+		UndoRecordInsertContext context = {{0}};
+
+		persistence =
+			UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(to_urecptr));
+
+		BeginUndoRecordInsert(&context, persistence, 1, NULL);
+
+		/*
+		 * Prepare and update the progress of the undo action apply in the
+		 * transaction header.
+		 */
+		PrepareUpdateUndoActionProgress(&context, NULL, to_urecptr, 1);
+
+		START_CRIT_SECTION();
+
+		/* Update the progress in the transaction header. */
+		UndoRecordUpdateTransInfo(&context, 0);
+
+		/* WAL log the undo apply progress. */
+		{
+			XLogRecPtr	lsn;
+			xl_undoapply_progress xlrec;
+
+			xlrec.urec_ptr = to_urecptr;
+			xlrec.progress = 1;
+
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+			RegisterUndoLogBuffers(&context, 2);
+			lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+			UndoLogBuffersSetLSN(&context, lsn);
+		}
+
+		END_CRIT_SECTION();
+
+		/* Release undo buffers. */
+		FinishUndoRecordInsert(&context);
+
+		/*
+		 * Undo action is applied so delete the hash table entry.
+		 */
+		Assert(TransactionIdIsValid(xid));
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+	}
+}
+
+/*
+ * execute_undo_actions_page - Execute the undo actions for a page
+ *
+ *	urp_array - array of undo records (along with their location) for which undo
+ *				action needs to be applied.
+ *	first_idx - index in the urp_array of the first undo action to be applied
+ *	last_idx  - index in the urp_array of the last undo action to be applied
+ *	reloid	- OID of relation on which undo actions needs to be applied.
+ *	blkno	- block number on which undo actions needs to be applied.
+ *	blk_chain_complete - indicates whether the undo chain for block is
+ *						 complete.
+ *
+ *	returns true, if successfully applied the undo actions, otherwise, false.
+ */
+bool
+execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx, int last_idx,
+						  Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+						  bool blk_chain_complete)
+{
+	/*
+	 * All records passed to us are for the same RMGR, so we just use the
+	 * first record to dispatch.
+	 */
+	Assert(urp_array != NULL);
+
+	return RmgrTable[urp_array[0].uur->uur_rmid].rm_undo(urp_array, first_idx,
+														 last_idx, reloid,
+														 full_xid, blkno,
+														 blk_chain_complete);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..c1539bb
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoPersistence persistence;
+	UndoRecordInsertContext context = {{0}};
+
+	persistence =
+		UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, persistence, 1, record);
+
+	/* Update the progress in the transaction header. */
+	PrepareUpdateUndoActionProgress(&context, record, xlrec->urec_ptr,
+									xlrec->progress);
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..fbc1927
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,396 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard the undo for the given log
+ *
+ * Search the undo log, get the start record for each transaction until we get
+ * the transaction with xid >= xmin or an invalid xid.  Then call undolog
+ * routine to discard up to that point and update the memory structure for the
+ * log slot.  We set the hibernate flag if we do not have any undo data that
+ * can be discarded, this flag is passed back to the discard worker wherein it
+ * determines if the system is idle and it should sleep for some time.
+ *
+ * Return the oldest full_xid remaining in this undo log (which should be
+ * >= xmin, since we'll discard everything older).  Returns
+ * InvalidTransactionId, if the undo log is empty.
+ */
+static FullTransactionId
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	bool	log_complete = false;
+	TransactionId	undoxid = InvalidTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	uint32	epoch = 0;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		bool pending_abort = false;
+
+		next_insert = UndoLogGetNextInsertPtr(logno, InvalidTransactionId);
+
+		if (next_insert == undo_recptr)
+		{
+			/*
+			 * The caller of this function must have ensured that there is
+			 * something to discard.
+			 */
+			Assert(undo_recptr != slot->oldest_data);
+
+			/* Indicate that we have processed all the log. */
+			log_complete = true;
+		}
+		else
+		{
+			/* Fetch the undo record for the given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+
+			if (uur != NULL)
+			{
+				/*
+				 * Add the aborted transaction to the rollback request queues.
+				 *
+				 * If the undo actions for the aborted transaction is already
+				 * applied then continue discarding the undo log, otherwise,
+				 * discard till current point and stop processing this undo
+				 * log.
+				 *
+				 * We can ignore the abort for transactions whose
+				 * corresponding database doesn't exist.
+				 *
+				 * XXX: We've added the transaction-in-progress check to avoid
+				 * xids of in-progress autovacuum as those are not computed
+				 * for oldestxmin calculation.  See DiscardWorkerMain.
+				 */
+				if (!TransactionIdDidCommit(uur->uur_xid) &&
+					!TransactionIdIsInProgress(uur->uur_xid) &&
+					TransactionIdPrecedes(uur->uur_xid, xmin) &&
+					uur->uur_progress == 0 &&
+					dbid_exists(uur->uur_dbid))
+				{
+					FullTransactionId full_xid;
+
+					full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+																uur->uur_xid);
+					(void) RegisterRollbackReq(InvalidUndoRecPtr,
+											   undo_recptr,
+											   uur->uur_dbid,
+											   full_xid);
+
+					pending_abort = true;
+				}
+
+				next_urecptr = uur->uur_next;
+				undoxid = uur->uur_xid;
+				epoch = uur->uur_xidepoch;
+
+				UndoRecordRelease(uur);
+				uur = NULL;
+			}
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) the next transaction is not all-visible. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if ((TransactionIdIsValid(undoxid) &&
+			 TransactionIdFollowsOrEquals(undoxid, xmin)) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			log_complete ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If the transaction id is smaller than the xmin, it means this
+			 * must be the last transaction in this undo log, so we need to
+			 * get the last insert point in this undo log and discard till
+			 * that point.
+			 *
+			 * Also, if the transaction has pending abort, stop discarding
+			 * further.
+			 */
+			if (TransactionIdPrecedes(undoxid, xmin) && !pending_abort)
+			{
+				UndoRecPtr	next_insert = InvalidUndoRecPtr;
+
+				/*
+				 * If more undo has been inserted since we checked last, then
+				 * we can process that as well.
+				 */
+				next_insert = UndoLogGetNextInsertPtr(logno, undoxid);
+				if (!UndoRecPtrIsValid(next_insert))
+					continue;
+
+				undo_recptr = next_insert;
+				need_discard = true;
+				epoch = 0;
+				latest_discardxid = undoxid;
+				undoxid = InvalidTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				return InvalidFullTransactionId;
+			}
+
+			/*
+			 * If no more pending undo logs then set the oldest transaction to
+			 * InvalidTransactionId.
+			 */
+			if (log_complete)
+			{
+				slot->oldest_xid = InvalidTransactionId;
+				slot->oldest_xidepoch = 0;
+			}
+			else
+			{
+				slot->oldest_xid = undoxid;
+				slot->oldest_xidepoch = epoch;
+			}
+
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = undoxid;
+
+		Assert(uur == NULL);
+
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+
+	return FullTransactionIdFromEpochAndXid(epoch, undoxid);
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		FullTransactionId oldest_xid = InvalidFullTransactionId;
+
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		/*
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.persistence == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin the try
+		 * to discard the undo log.
+		 */
+		if (!TransactionIdIsValid(slot->oldest_xid) ||
+			TransactionIdPrecedes(slot->oldest_xid, oldestXmin))
+		{
+			/* Process the undo log. */
+			oldest_xid = UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+
+		if (FullTransactionIdIsValid(oldest_xid) &&
+			FullTransactionIdPrecedes(oldest_xid, oldestXidHavingUndo))
+			oldestXidHavingUndo = oldest_xid;
+	}
+
+	/*
+	 * Update the oldestFullXidHavingUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.persistence == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 67b08e7..2a795d8 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -145,6 +145,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 		}
 	}
 	else
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..441f736
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1464 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/discardworker.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+
+#define ROLLBACK_REQUEST_QUEUE_SIZE 1024
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdEquals(xidQueueElem1->full_xid,
+									 xidQueueElem2->full_xid))
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size == sizeQueueElem2->request_size)
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at == errQueueElem2->err_occurred_at)
+		return 0;
+	return -1;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(ROLLBACK_REQUEST_QUEUE_SIZE, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+static int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * ROLLBACK_REQUEST_QUEUE_SIZE) + ROLLBACK_REQUEST_QUEUE_SIZE +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < ROLLBACK_REQUEST_QUEUE_SIZE; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo * urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+		{
+			cur_undo_queue++;
+			return false;
+		}
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+static uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoPersistence persistence;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+	Assert(!TransactionIdIsInProgress(XidFromFullTransactionId(full_xid)));
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+		Assert(slot != NULL);
+		persistence = slot->meta.persistence;
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * If the corresponding undo record got rolled back, we return from
+		 * here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+		next_urecptr = uur->uur_next;
+
+		/*
+		 * Case 1: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			/*
+			 * While fetching the next insert location if the new transaction
+			 * has already started in this log then lets re-fetch the undo
+			 * record.
+			 */
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			if (!UndoRecPtrIsValid(next_insert))
+			{
+				UndoRecordRelease(uur);
+				uur = NULL;
+				continue;
+			}
+
+			/*
+			 * If next_insert location points to the starting location of a
+			 * new page, we should subtract the page header size from the
+			 * insert location.
+			 */
+			if (UndoRecPtrGetPageOffset(next_insert) == UndoLogBlockHeaderSize)
+				next_insert -= UndoLogBlockHeaderSize;
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+
+		/*
+		 * Case 2: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == slot->logno)
+		{
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoLogIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+
+	return sz;
+}
+
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
+	{
+		if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE ||
+			GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE))
+		{
+			Assert(GetSizeQueueSize() < ROLLBACK_REQUEST_QUEUE_SIZE);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(ROLLBACK_REQUEST_QUEUE_SIZE)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 ROLLBACK_REQUEST_QUEUE_SIZE,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Size Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= ROLLBACK_REQUEST_QUEUE_SIZE)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as not in_progress so that undo
+	 * launcher or other undo worker don't remove the entry from queues.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->in_progress = false;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || rh->in_progress)
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->in_progress = true;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || rh->in_progress)
+					continue;
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->in_progress = true;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself (a) if the entry is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues, (b) if the entry is present, but the size is small enough that
+ * backend can execute by itself and undo worker hasn't started processing it
+ * yet.
+ */
+bool
+RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	bool		request_registered = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * There must be space to accommodate the new request.  See
+	 * UndoRollbackHashTableSize.
+	 */
+	Assert(!RollbackHTIsFull());
+
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr, full_xid);
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	/*
+	 * Backends always register the rollback request in the rollback hash
+	 * table irrespective of whether we push it to undo worker.  This ensures
+	 * that discard worker won't try to process the request on which backend
+	 * is working.  OTOH, discard worker won't add an entry to the hash table
+	 * unless it can push the request to undo worker.  This is because
+	 * otherwise backends might not process the request by themselves even
+	 * though no undo worker is going to process such a request.
+	 */
+	if (can_push ||
+		(!can_push && !IsDiscardProcess()))
+	{
+		RollbackHashKey hkey;
+
+		hkey.full_xid = full_xid;
+		hkey.start_urec_ptr = start_urec_ptr;
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+											   HASH_ENTER_NULL, &found);
+		if (!rh)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+
+		/* We shouldn't try to add the same rollback request again. */
+		if (!found)
+		{
+			rh->start_urec_ptr = start_urec_ptr;
+			rh->end_urec_ptr = end_urec_ptr;
+			rh->dbid = dbid;
+			rh->full_xid = full_xid;
+			rh->in_progress = false;
+
+			if (can_push)
+			{
+				UndoRequestInfo urinfo;
+
+				ResetUndoRequestInfo(&urinfo);
+
+				urinfo.full_xid = rh->full_xid;
+				urinfo.start_urec_ptr = rh->start_urec_ptr;
+				urinfo.end_urec_ptr = rh->end_urec_ptr;
+				urinfo.dbid = rh->dbid;
+				urinfo.request_size = req_size;
+
+				InsertRequestIntoUndoQueues(&urinfo);
+
+				/*
+				 * Indicates that the request will be processed by undo
+				 * worker.
+				 */
+				request_registered = true;
+				pushed = true;
+			}
+			else
+			{
+				/* Indicates that the request can be processed by backend. */
+				request_registered = false;
+			}
+		}
+		else if (!rh->in_progress && !can_push)
+		{
+			/*
+			 * Indicates that the request can be processed by backend. This is
+			 * the case where discard worker would have pushed the request of
+			 * smaller size which backend itself can process. Mark the request
+			 * as in-progress, so that discard worker doesn't try to process
+			 * it.
+			 */
+			rh->in_progress = true;
+			request_registered = false;
+		}
+		else
+		{
+			/* Indicates that the request will be processed by undo worker. */
+			request_registered = true;
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
+	return request_registered;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * To check if the rollback requests in the hash table are all
+ * completed or not. This is required because we don't not want to
+ * expose RollbackHT in xact.c, where it is required to ensure
+ * that we push the resuests only when there is some space in
+ * the hash-table.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..fe5de7d
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,806 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 5;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.
+ */
+#define UNDO_WORKER_LINGER_MS 10000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker * worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo * urinfo);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		CommitTransactionCommand();
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.
+		 */
+		if (InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTRemoveEntry(urinfo->full_xid, urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	for (;;)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	for (;;)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 827fe4b..e81605b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14461,6 +14462,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f5db5a8..0550d2c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d8dc0cc..4c906f6 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 8e098e4..339dc9c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index f2ed561..d075aa9 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c3249..56b5d08 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,5 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0da5b19..8347e1a 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUndo, 0);
 }
 
 /*
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de..c6dbbea 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -121,6 +121,13 @@ bool		allowSystemTableMods = false;
 int			work_mem = 1024;
 int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
+int			rollback_overflow_size = 64;
+
+/*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
 
 /*
  * Primary determinants of sizes of shared-memory structures.
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index d88113f..e6ab9e7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -455,6 +455,20 @@ InitCommunication(void)
 	}
 }
 
+/*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
 
 /*
  * pg_split_opts -- split a string of options and append it to an argv array
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 866f389..633b720 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1955,6 +1956,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		},
+		&enable_undo_launcher,
+		true,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -2924,6 +2936,16 @@ static struct config_int ConfigureNamesInt[] =
 		5000, 1, INT_MAX,
 		NULL, NULL, NULL
 	},
+	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
 
 	{
 		{"wal_segment_size", PGC_INTERNAL, PRESET_OPTIONS,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 5ee5e09..a6f4099 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -749,4 +749,10 @@
 # CUSTOMIZED OPTIONS
 #------------------------------------------------------------------------------
 
+# If often there are large transactions requiring rollbacks, then we can push
+# them to undo-workers for better performance. The size specifeid by the
+# parameter below, determines the minimum size of the rollback requests to be
+# sent to the undo-worker.
+#
+#rollback_overflow_size = 64
 # Add settings for extensions here
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c4..3b2a28b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 44f4c41..5b49cd4 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..396193b 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..e1fb42a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..ef0f6ac 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,26 +25,27 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 0ac7f73..5de92e9 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
@@ -72,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
@@ -225,6 +236,7 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 /* in transam/varsup.c */
 extern FullTransactionId GetNewTransactionId(bool isSubXact);
 extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid);
+extern uint32 GetEpochForXid(TransactionId xid);
 extern FullTransactionId ReadNextFullTransactionId(void);
 extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 								  Oid oldest_datoid);
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..e51a679 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,10 +14,12 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undorequest.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
 #include "storage/lock.h"
+#include "access/undolog.h"
 
 /*
  * GlobalTransactionData is defined in twophase.c; other places have no
@@ -41,7 +43,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 3e2154b..c20ca7f 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -111,5 +111,9 @@ extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
 extern void UndoRecordRelease(UnpackedUndoRecord *urec);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 					  UndoPersistence upersistence);
+extern void PrepareUpdateUndoActionProgress(UndoRecordInsertContext *context,
+								XLogReaderState *xlog_record,
+								UndoRecPtr urecptr, int progress);
+extern void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..652712c
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index cd36734..6b428a5 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -266,6 +266,19 @@ typedef struct UndoLogAllocContext
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogSlot
 {
@@ -431,7 +444,7 @@ extern void assign_undo_tablespaces(const char *newval, void *extra);
 
 /* Checkpointing interfaces. */
 extern void CheckPointUndoLogs(XLogRecPtr checkPointRedo,
-							   XLogRecPtr priorCheckPointRedo);
+				   XLogRecPtr priorCheckPointRedo);
 
 /* File sync request management. */
 
@@ -448,6 +461,7 @@ void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
 extern void undolog_redo(XLogReaderState *record);
 /* Discard the undo logs for temp tables */
 extern void TempUndoDiscard(UndoLogNumber);
+extern Oid UndoLogStateGetDatabaseId(void);
 
 /* Test-only interfacing. */
 extern void UndoLogDetachFull(void);
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..f1339dd
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,140 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+/* Remembers the last seen RecentGlobalXmin */
+TransactionId latestRecentGlobalXmin;
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	Oid			dbid;
+	bool		in_progress;	/* indicates that undo actions are being
+								 * processed */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	TimestampTz err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+			UndoRequestInfo *urinfo, bool *in_other_db);
+
+/* Exposed functions for rollback hash table. */
+extern bool RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr);
+extern bool RollbackHTIsFull(void);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..e2a61ff
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726a..d9f7d89 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -16,11 +16,13 @@
 
 #include "access/transam.h"
 #include "access/xlogreader.h"
+#include "access/undolog.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
 #include "storage/relfilenode.h"
 #include "storage/sinval.h"
 #include "utils/datetime.h"
+#include "utils/resowner.h"
 
 /*
  * Maximum size of Global Transaction ID (including '\0').
@@ -428,6 +430,14 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
 
+extern void ApplyUndoActions(void);
+extern void UndoActionsRequired(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+extern bool PerformUndoActions(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool isSubTrans);
+
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
 extern const char *xact_identify(uint8 info);
@@ -439,5 +449,7 @@ extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_ab
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
 extern bool IsInParallelMode(void);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+					UndoPersistence upersistence);
 
 #endif							/* XACT_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 3cc9c3d..26e93cc 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -21,8 +21,11 @@
 
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
+#include "access/undorecord.h"
+#include "access/undorequest.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -295,9 +298,13 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	bool		(*rm_undo) (UndoRecInfo *urp_array, int first_idx, int last_idx,
+							Oid reloid, FullTransactionId full_xid, BlockNumber blkno,
+							bool blk_chain_complete);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index 9375e54..08584fc 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -13,6 +13,7 @@
 
 #include "access/rmgr.h"
 #include "access/xlogdefs.h"
+#include "access/transam.h"
 #include "port/pg_crc32c.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..15fc018 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,13 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest transaction id with epoc which is having undo. Include this
+	 * value in the checkpoint record so that whenever server starts we get
+	 * proper value.
+	 */
+	FullTransactionId		oldestFullXidHavingUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 61a24c2..2f595d7 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -170,6 +170,8 @@ extern PGDLLIMPORT struct Latch *MyLatch;
 extern int32 MyCancelKey;
 extern int	MyPMChildSlot;
 
+extern bool applying_subxact_undo;
+
 extern char OutputFileName[];
 extern PGDLLIMPORT char my_exec_path[];
 extern char pkglib_path[];
@@ -245,6 +247,7 @@ extern PGDLLIMPORT bool allowSystemTableMods;
 extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
+extern PGDLLIMPORT int rollback_overflow_size;
 
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
@@ -422,6 +425,7 @@ extern AuxProcType MyAuxProcType;
  *****************************************************************************/
 
 /* in utils/init/postinit.c */
+extern bool dbid_exists(Oid dboid);
 extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f9b1cf2..3cda35d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2fff673..f9dc0bb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -785,7 +785,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 8ccd2af..c8c694d 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,8 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
+
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 986bb64..ba53fa9 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -145,6 +145,12 @@ typedef enum LockTagType
 	LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
 	LOCKTAG_SPECULATIVE_TOKEN,	/* speculative insertion Xid and token */
 	LOCKTAG_OBJECT,				/* non-relation database object */
+
+	/*
+	 * Note: object ID has same representation as in pg_depend and
+	 * pg_description, but notice that we are constraining SUBID to 16 bits.
+	 * Also, we use DB OID = 0 for shared objects such as tablespaces.
+	 */
 	LOCKTAG_USERLOCK,			/* reserved for old contrib/userlock code */
 	LOCKTAG_ADVISORY			/* advisory user locks */
 } LockTagType;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..a7d0602 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -222,7 +222,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SXACT,
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
-	LWTRANCHE_REWIND,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1cee7db..c6b8c5d 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672..1d12994 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8ef..0b227ab 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb..6034c5e 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
1.8.3.1

#41Asim R P
apraveen@pivotal.io
In reply to: Amit Kapila (#40)
Re: POC: Cleaning up orphaned files using undo logs

My understanding is smgr pendingDeletes infrastructure will be replaced by
these patches. I still see CommitTransaction() calling
smgrDoPendingDeletes() in the latest patch set. Am I missing something?

Asim

#42Thomas Munro
thomas.munro@gmail.com
In reply to: Asim R P (#41)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jun 10, 2019 at 5:35 AM Asim R P <apraveen@pivotal.io> wrote:

My understanding is smgr pendingDeletes infrastructure will be replaced by these patches. I still see CommitTransaction() calling smgrDoPendingDeletes() in the latest patch set. Am I missing something?

Hi Asim,

Thanks for looking at the patch.

The pendingDeletes list is used both for files that should be deleted
if we commit and files that should be deleted if we abort. This patch
deals only with the abort case, using the undo log instead of
pendingDeletes. That is the file leak scenario that has an
arbitrarily wide window controlled by the user and is probably the
source of almost all cases that you hear of of disks filling up with
orphaned junk AFAICS.

There could in theory be a persistent stuff-to-do-if-we-commit system
exactly unlike undo logs (records to be discarded on abort, executed
on commit). I haven't thought much about how it'd work, but Andres
did suggest something like that for another purpose just the other
day, and although it's hard to think of a name for it, it doesn't seem
crazy as long as it doesn't add overheads when you're not using it.
Without such a mechanism, you can probably leak files belonging to
tables that you have dropped in a committed transaction, if you die in
CommitTransaction() after it has called RecordTransactionCommit() but
before it reaches smgrDoPendingDeletes(), and even then probably only
if there is super well-timed checkpoint so that you recover without
replaying the drop. I'm not try to tackle that today.

BTW, there is yet another kind of deferred unlinking going on. In
SyncPostCheckpoint() (formerly known as mdpostckpt()) we defer the
last bit of the job until after the next checkpoint. At that point we
only expect the first segment to exist and we expect it to be empty.
That's a mechanism introduced by commit 6cc4451b5c47 to make sure that
we don't reuse relfilenode numbers too soon in some crash scenarios.
That means there is another very narrow window there to leak a file
(though these ones are empty): you die after the checkpoint is logged
but before SyncPostCheckpoint() is run, or even after that but before
the operating system has flushed the directory.

--
Thomas Munro
https://enterprisedb.com

#43Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#40)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, May 27, 2019 at 5:59 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Apart from fixing the above comments, the patch is rebased on latest
undo patchset. As of now, I have split the binaryheap.c changes into
a separate patch. We are stilll enhancing the patch to compute
oldestXidHavingUnappliedUndo which touches various parts of patch, so
splitting further without completing that can make it a bit difficult
to work on that.

Some review comments around execute_undo_actions:

The 'nopartial' argument to execute_undo_actions is confusing. First,
it would probably be worth spelling it out instead of abbreviation:
not_partial_transaction rather than nopartial. Second, it is usually
better to phrase parameter names in terms of what they are rather than
in terms of what they are not: complete_transaction rather than
not_partial_transaction. Third, it's unclear from these comments why
we'd be undoing something other than a complete transaction. It looks
as though the answer is that this flag will be false when we're
undoing a subxact -- in which case, why not invert the sense of the
flag and call it 'bool subxact'? I might be wrong, but it seems like
that would be a whole lot clearer. Fourth, the block at the top of
this function, guarded by nopartial, seems like it must be vulnerable
to race conditions. If we're undoing the complete transaction, then
it checks whether UndoFetchRecord() still returns anything. But that
could change not just at the beginning of the function, but also any
time in the middle, or so it seems to me. I doubt that this is the
right level at which to deal with this sort of interlocking. I think
there should be some higher-level mechanism that prevents two
processes from trying to undo the same transaction at the same time,
like a heavyweight lock or some kind of flag in the shared memory data
structure that keeps track of pending undo, so that we never even
reach this code unless we know that this XID needs undo work and no
other process is already doing it. If you're the only one undoing XID
123456, then there shouldn't be any chance of the undo disappearing
from underneath you. And we definitely want to guarantee that only
one process is undoing any given XID at a time.

The 'blk_chain_complete' variable which is set in this function and
passed down to execute_undo_actions_page() and then to the rmgr's
rm_undo callback also looks problematic. First, not every AM that
uses undo may even have the notion of a 'block chain'; zedstore for
example uses TIDs as a 48-bit integer, not a block + offset number, so
it's really not going to have a 'block chain.' Second, even in
zheap's usage, it seems to me that the block chain could be complete
even when this gets set to false. It gets set to true when we're
undoing a toplevel transaction (not a subxact) and we were able to
fetch all of the undo for that toplevel transaction. But even if
that's not true, the chain for an individual block could still be
complete, because all the remaining undo for the block at issue
might've been in the chunk of undo we already read; the remaining undo
could be for other blocks. For that reason, I can't see how the zheap
code that relies on this value can be correct; it uses this value to
decide whether to stick zeroes in the transaction slot, but if the
scenario described above happened, then I suppose the XID would not
get cleared from the slot during undo. Maybe zheap is just relying on
that being harmless, since if all of the undo actions have been
correctly executed for the page, the fact that the transaction slot is
still bogusly used by an aborted xact won't matter; nothing will refer
to it. However, it seems to me that it would be better for zheap to
set things up so that the first undo record for a particular txn/page
combination is flagged in some way (in the payload!) so that undo can
zero the slot if the action being undone is the one that claimed the
slot. That seems cleaner on principle, and it also avoids having
supposedly AM-independent code pass down details that are driven by
zheap's particular needs.

While it's probably moot since I think this code should go away
anyway, I find it poor style to write something like:

+ if (nopartial && !UndoRecPtrIsValid(urec_ptr))
+ blk_chain_complete = true;
+ else
+ blk_chain_complete = false;

"if (x) y = true; else y = false;" can be more compactly written as "y
= x;", like this:

blk_chain_complete = nopartial && !UndoRecPtrIsValid(urec_ptr);

I think that the signature for rm_undo can be simplified considerably.
I think blk_chain_complete should go away for the reasons discussed
above. Also, based on our conversations with Heikki at PGCon, we
decided that we should not presume that the AM wants the records
grouped by block, so the blkno argument should go away. In addition,
I don't see much reason to have a first_idx argument. Instead of
passing a pointer to the caller's entire array and telling the
callback where to start looking, couldn't we just pass a pointer to
the first record the callback should examine, i.e. instead of passing
urp_array, pass urp_array + first_idx. Then instead of having a
last_idx argument, have an argument for the number of entries in the
array, computed as last_idx - first_idx + 1. With those changes,
rm_undo would look like this:

bool (*rm_undo) (UndoRecInfo *urp_array, int count, Oid reloid,
FullTransactionId full_xid);

Now for the $10m question: why even pass reloid and full_xid? Aren't
those values going to be present inside every UnpackedUndoRecord? Why
not just let the callback get them from the first record (or however
it wants to do things)? Perhaps there is some documentation value
here in that it implies that the value will be the same for every
record, but we could also handle that by just documenting in the
appropriate places that undo is done by transaction and relation and
therefore the callback is entitled to assume that the same value will
be present in every record. Then again, I am not sure we really want
the callback to assume that reloid doesn't change. I don't see a
reason offhand not to just pass as many records as we have for a given
transaction and let the callback do what it likes. So maybe that's
another reason to get rid of the reloid argument, at least. And then
we could document that all the record will have the same full_xid
(unless we decide that we don't want to guarantee that either).

Additionally, it strikes me that urp_array is not the greatest name.
Generally, putting _array into the name of the variable to indicate
that it's an array doesn't seem all that great from a coding-style
perspective. I mean, sometimes it's the best you can do, but it's not
amazing. And urp seems like it's using an abbreviation without any
real reason. For contrast, consider this existing precedent:

extern SysScanDesc systable_beginscan_ordered(Relation heapRelation,
Relation indexRelation,
Snapshot snapshot,
int nkeys, ScanKey key);

Or this one:

extern TupleDesc CreateTupleDesc(int natts, Form_pg_attribute *attrs);

Notice that in each case the array parameter (which is the last one)
is named based on what data it contains rather than on the fact that
it is an array.

Finally, I observe that rm_undo returns a Boolean, but it's not used
for anything. The only call to rm_undo in the current patch set is in
execute_undo_actions_page, which returns that value to the caller, but
the callers just discard it. I suppose maybe this was intended to
report success or failure, but I think the way that rm_undo will
report failure is to ERROR. Or, if we want to allow a fail-soft
behavior for some reason, then the callers all need to check the
value. I'm not sure whether there's a use case for that or not.

Putting all that together, I suggest a signature like this:

void (*rm_undo) (int nrecords, UndoRecInfo *records);

Or if we decide we need to have a fail-soft behavior, then like this:

bool (*rm_undo) (int nrecords, UndoRecInfo *records);

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#44Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#42)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jun 10, 2019 at 3:00 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Mon, Jun 10, 2019 at 5:35 AM Asim R P <apraveen@pivotal.io> wrote:

My understanding is smgr pendingDeletes infrastructure will be replaced by these patches. I still see CommitTransaction() calling smgrDoPendingDeletes() in the latest patch set. Am I missing something?

Thanks for looking at the patch.

Hello,

Here is a new rebased version of the full patch set for orphaned file
cleanup. The orphaned file cleanup code itself hasn't changed but
there are some changes in lower down patches:

* getting rid of more global variables, instead using eg
CurrentSession->attached_undo_logs (the session.h infrastructure that
is intended to avoid creating more multithreading-hostile code)

* using undo log "slots" in various APIs to make it clearer that slots
can be recycled, which has locking implications, plus several locking
bug fixes that motivated that change

* current versions of the record and worker code discussed upthread by
Amit and others

The code is also at https://github.com/EnterpriseDB/zheap/tree/undo
and includes patches from
https://github.com/EnterpriseDB/zheap/tree/undoprocessing and
https://github.com/EnterpriseDB/zheap/tree/undo_interface_v1 where
some parts of this stack (workers etc) are being developed.

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-20190614.tgzapplication/x-gzip; name=undo-20190614.tgzDownload
��
]��iw��(|��_�[Ild4��O��d�h
������
�����������}��k������������j��~�!�'A��l6�Z;[������l���*�w��m�w�_��Z����^sw{g[5[�����Q����d�$Q��0�y��������t�{Y�����$s�M����L����CoT���-�q�X8����������������]s�����G�������z�e�~�'a0W;��;��{��{���7�6[������3����:���"�U�]���j6_��
@S�n^��]0w"u�[���o
��?��o��w�c'v_�����VS]8
��|�}��}�6[�e���������ugp�A����������R��/n��"o���*�����<�V�w.�u�8R�Ta{5n�F��u'��&!|�E0��;j��.�W� t���a�������+�z�b�v�|v�Ld������������5��m��\����^o5�������Im��C���Y���������]\����Dj|���������x4n{�U77���������2V����m��i���V�����NU�~����*���������:l�v�	\�V�q�v��ns�lNd|���*pPe}��O��sP�S�����_�7���z}�V������h>������>*<�������.�������%���/�8I����*���?�����^|7�E����m��J�]�|]��{2�pd-F��ml��wZ;.�������'�Yn�J��k����jp������V�fe������;y�ko��Qe��������}����v���o`���A��vu�r��;�<S���y}zrv�=�����u�U<����!~��K1�:���N����w�������������z^�����������[�t��f���pNxq��{�����~����)JF/�Kv����(�f����j4����86���#g��>�O�h������0/@�q�,E��PTzPO���������'�m��i@�z�t��j!�R��9�w[E��[���T$���P�|^S�C5���x�:�u;��2@]���;��M����K��Vg�3��` :[��/�	��@�GN�����k8�X�.�
�=	x�@����5�~�y�����?Q�$����=��,�>��<��]�l"A=�T�z�x���0����v����f���/b�4���8���Mg�m����������G�@���u7����wW'�'� �T6;}�R��].;��5��Z����y����s�UG�?��
�u��9?���~����:���f��]<{`*�_��\.xt:�B�pY���L"�������3�Mj�����ba�����i�V�x���	�}�n�o����,����cW��/��y�h7��T������@�4�yYv����������V����}e��=��1��NzW��W�-�/�Q�C�����5B{�������Z�������Auc��sy��JDqX�Nd�o:�w�o�����}{�
/��l�/�h�����nv����a~���T�������i{���x�fO�����8��W*;(<YSO���/QS���;�7��2���OL����(X<��7Tu�R���(j�E��.��)�ju�8X��b��A�x���������A�s�{_��|r��<���9fZ������n5Q��i�e}�K�w����G���/J��1,�8a��7��?�
]����S�
g�����h�L���{�� co!7��WKL�������y�������j��V�:�
�����ye��hv�N����@���&kkkL��*���;������j;[������.��j�>��qz��:�X��=�]���f���a���Eo,��_���Y�W��GA0[[s�����q�}7>���z������{���c|U@F4v`�A����@P��d���<�b�	D��z��W|y��8By�+Q�!N�0�_��a�c��Nkmn�\��>���!g�@G�>�����9��{T�$3R
E��
�8�YpO�{
��d���{���)�1���
?�����f���CMUG�Q��[,�O3����9�)`v�z�����[:SU��,����B3~[��zW�\�$�a>�Jm�LcQ#��o��R�;w�Y/����5��4��a�x��7*6g��LC�[uys~�~�Y}wu�1bLTd���������Z�
�1�8|xtu��Cu��/������Uo�=���|=�n�����8��MEx
$Gt0�>v|`��8^�����,3��#p'0�H�P�����!������a���d^��YG	�F$����������LWI`.K����G�V��a��r 	iK�mU���:�[7����V�~���k��\�
��[5����(��������[�O�!H���B���H�c�����������0�lN�O0E�.�%��^<��L�2�F=�(����[=�k���a|���E��}~�1	f��xi,�IV��z��4�Ay�{D��V#������u �����O��z����>����%�H����x�'HX�	�H�3�"� ��
�������sgL0Z}�C��IC����k�������X�"����]A�P.�XE��.�<U�j^�E�Y�����o�;x���#@���	�a�e��	�WnjL�p���5"�����e�!�'�ji }���H|����4�fc��lv��?P�aJ��=bmE�4�5[������E�����i�_������D�
M��.��3�L�I}�Pqq������%B_�"q��f��1d�����V%������
��|����\�lvW���
����5u�]�&��k6���`�-}�Of�O\��=zU}��V�x��*M�=r��G/��r���
���t���F<W��U��|�<�GevHmnz��a�7q�:����x;@uxsy|5�u��z�}���}�K�c�zz����V���f �%J�O�~|����v�o�}yj��I�@������P�@��D �J���E���E>�~<�������FM���	T��`<�a��`�#>���h�+�	��@����H'�e���������������a8�&?4���w�n��V�{ �n)|��3]H�P���h������w�u�'i'�v�5j�V�TS��Z��_W�����'�q�����s��Q�i����2�u.����?��F�#�E����,���0@;:B����ty���1;G4(�#���C�,�2��u�o6x��k�@h{�B��3��.�3�'�������� )�X2��=<���Z�+!��\�Uw���H�x��]�<g�v
�d���$�>��u�i6���MMQ?�'-{1
]73�/�s)9�9��;��O�~BP�3�������6��T6y�*Tl�OPS�A��jQgozp�n��~�ZT�����b���Dp�.���_�s�'�U/����#/+�b��X"h�j4�,,���;�bv|�����N{��a��� T`V�*�F:�T���?��]����V�
��`��f8����	��V�_X�_G rn���a������9����V�?�_��?��5�=���F�����������?����GW0������wv����GT���0������.\��2��]��e`Se`S,�n�*���i��W��-�����C��������������y{�\��E����#
?8NT9�D�^��8^D���Xw�p����Q��.��>nw�]}������c����i�3j�x?<�q�w�������v�������&m��F�6CO��mY���^�s|�e?��2?.����Gvm+��;�Y��>������\����3y�M�6��z�w�����Y����Lm�����7�m��R�G��X��Dfi�euN����>d�Y�� �?����_���jR�5�.���RT65��p�
������s�@#�������1#E�oU����pvlC���V�<(���Q�3�=(�#���;NpLt�f3R)�����P���j�d����5�VB��r���G���K����A�~b[����.'X����9l��F�Sq��LF�7��7����a<��0��R����S�YO������Z`�1:�*��c��m#=
Zdk��d���z0	4<�M��Dr�A�R{;j��0��`�����e�E�X�|�+�/q��ZG6��&*Y���t�h�A}���G�@q�%��za�6����[R���CsG5>7��TF��&����oy��Xi�L���a�hX1�A��L��#z������|	�I�{"��H��;�N'}��� 7��W�Co�3��������w��UPQ03w6�>���g��;����<��p��/���4o�gjs6�{K���2�.�,��@-����#JA�`�f���.��;:�tX~0�.��`�E�1�7"�;hh��h����c�	��4+��pk�g$1��n��d�#�m9c��;�E�8d@ �c�j��g
5�������ie�Cp��p�������p� vE7&56�Z�d����-n�l�%z�F���m�,��?��d��--=�8.��.$@����n��l�������b
8���J#��l�
g����a�u��N����P��`���n�g� �C�WBg0�q�������9�2K\����d����B> �)�83f�
�8
�x����8.����,^����j�
`�_���A�C-�.�!&����^p SU��E0F�^@D@eB��s�u9�uF%.��4��;EN���ap��~
,��GV��P7��l�;�a.�k�b�q�m��J��pNlMJs%�\Ww�����BLv��6h�7����S���2����w�1!��#�H/Z���`_�	�}�K7�Viu�� ~ CO��C��t����4n�����g�}���-3�u���""�d���z)
�}f������t_I��S�OGHZ-���fT9@����r���d����h�
a�3V�>@�l��h0�5�G� {t��YL��9���3�+����KHg��������A�K~;����������)@��1P%?H"���	��-*d,d���1	�h`�d�bq�'!��
*A���+��4A��w��9I��� �E�%"9����Kt`QN=�SG���-�����>���:G-n�!�Y��s�M��6Zw��z���!�\&����<l���Q�n���#f��� f�/b������������C�������m!�
 ��/<���6�Y+�����^�����#���GGT)��H�Q/�}�Z#N�W��h��!*�c����GwZ��E��2�{	�	h���B-	�:�K�1Eh������_0Q�q����:��(�K0x*]71C�T-&9*c`G�^��b<=_�~�	}��'[@�I����+�����L��
@����,)�5v��Q�����C�Y�b�n������>$H���Y��t`����< *�}��O���G���MQ�:���=$�.��8����p�D�:���������;��;�~"�!���iP��h�@1���H�&3�0Id�Y����z��o
2J�tR�P&������7COY�+R��jj��A�2b	����� 8B���;�w����HL'��c,+�!;�,bV��UU�f�i����9�h<��0zQ�[���a8%Z����#����'��c�<����D��,2BK@+Y�������&��t�7G�5L��h]��#�"Z47�!���?�
�,�+p��	qL8W��%#A��C�����0�0a�
������G�M��sw��K��W��^�blFq�]f�D���U$��0& �
t����(����	�D����q~590����"����?�,\3�
�&�����L#�	��l���M�#���v�Fe �I�� ��3!�{���N�,c1���22��2Z�~=�K�	z�f�j+�k)SG�,�PJ��1�"���f��H9�:��3�#?hw��&�6�G�r���*�K�<��������O�����\�H����Gb,A#�;�_���	/������x���������f�����x4����v{#'_0���Tk�`��&�*����?�K�P���CL����N�l�T0��C�c�FO�;����`:E6��A%=��z�%�����E�5�7��u&�F��"	� �
{[G:��(L�d+���p<��u�c����BN��F��l����6zAF�5aF�m�>X&�D����4[_�	>t� 	9CGR]rtND��s�K�f'�J<)X)^�t�������I���(��ud���������w�/KX�����j�L���V&\�	�=�����M8>Kj(����Pa	(��3�_�G<'��[���q���#��~uH�A4
����Nl�M��j���/z\�L��f����H���=LP����,5��u��o�j(��������Iw�hS����i#��(�#}	D��`�PL*��#:l�W$����6�X��Y&���]#���A�g
�y��@���+�#j�x�^��z���:q�N���b��6�I�#7�G��%G3�����!M%zZ�AT$���������
�P���K�/.��o�� ;������F"��/\m��� .�����@tj�x	�o}�mB��T.:4�e��F%�0Pk����}��(�
�Y�a�k�$�J��%C*�v��W���g]K��Q�K!�"mBx �B�����N��Y��d�'Dq�OA"�E)$�Ht��H|�8[�"vML2��4�[��kHyY�o��O���������e����B�XAk��G!N(��0�����Z���p��UJ���6���z����|����%_-������B��nk<��'���Fcw|8���m6��l:���*����Bc~�Yk��M�g�����Q=1m;�a�d	��L�9���5dW���a(�:�_j�1��4������:�����5��"�7�nZ�
,iE�^�< F�$����e8	��~�X5q�6%����'b���P�6A]H)��}T��?���N�i��$PK%�kc=@����#�
��RNGv���{���P��^X�t ���&�P0�$ ��Y���[���}N��fa�D��X�#�(4)ZNn�c��;�XJ�u�=6�D�(	Q�;��NB(PtA��r�Z�W J24F'g#����&�������Y4�/�6.��2'=M8��b����&�G�3��1$'
;�������exK_MbD���;�&����F����n������X�hh��fu��vH�yJ�
]��$
�� UU G��jj����&�����P��$�8�r�w��`<q9,c�N|nh�"b��^������q>i`�1��p����QZ���s��2����A���<,\�r��&��D�*j�6����������E���3�$�����A��T��=���+�Z[�CN�w���r�������F���^d{yK"J�%�OH}
�[U��aO�1�.���.p����~n�k���A ��?IW�0kq&��E�	���^.���w����d:�yM����?�.�v#V�aw���T=c�uf�v��W'���N&��,_&Xb��J��uJ��#������A3��b6���_��QV�������FZ��`{�"'�MB�C�z�6�z����l�5�����6�'���K&e%�1a�HC�����S�/���bs;����^���9��������<���������P��q
��I����%�H`��~F:�������v+����j�������C����;���hg�}�� ��;�y����q����{;�����?7���������e�_(�S����H
��RIML����\1� :N��njS���$D"g�3��6���(z�sv����\�Q�5U�Nm���V��f�6�[<��N��#��?mDe�������y���;�r��t��G?�|���mM��#���5��v�`4q��������y�g$�o��f�x�1��h:6:��.���<�����/��!�-N��F?�p���9�����])�UoP�Y�A�m��/4d���E:�{�I�A�@�94��6/}-�up�7�>��4G��~�`�i�������h�AY�07�j�)���}<����0���S��G�]
~���Xg~���[d��(m	�����l�k���T��.:���;G�^�)���.y�0J���&�R�o��}�,��K7��{9���;�;8p��f���:hn�L����O:�%=�<�%m���o�e������������"����a�����'�����u�c���Z[��j����&J8,K,Bw�	PP��������������*�l��������e�%���>�0�;��.b��U�e79��<��7������G3���'ps���oA��-�x����������;�	�F]|����C?Gu������>����������������+���8�)������:�>����Nk���L���mwz�R�#�_#���,������/�������.B��&#�	�%5T%�w���Q�;�j���]��p(B�2J���������J!����T���Ig�=o��wZ���=�}��.�9��|��/�C���c��(������&��C�5��z����$�'!3$����D�k#r=@u�H(�5�-O{��Q���4z�`Z�AM,���p��������|D�r���='�K��p��%-��?bCIf81BZ����j�j�R��������6��mx��K������
=��[2�Db2PW>�:�0��K����G[���v�������7����t`7H���hEZWRF����\g��(lX�tn���	O��*�S��GJB����
��co�-������.N���N����5���H�It|t ���nk+��=+-��Dn
�4�|�/�kN(�S���o�|��F�"#@A��������Lm�U�XQ�k�5J��^�W�X�:+�����9<hh���1�v;�o��f� �vUIxw^(���T��+����V����AS-�+/�A����jk�/���H��n��"jz�,�s��q�8�b��|R"�����p%�l�n�cs�VkE��-�Iu�VA���1Q��i�����^Q7���-k6Ck�����w����i���Q3��K�V����OT{U���k���X�M�^��Gk.i&�#� =�e��+uWh��[���:�����Y��T��fb}��j�`^2IUh�c��-��M?���m\��	��,�n�[lWz�r�
n���x�^.oFp\:WA���QXX��}�8TUn��r��;.����b�����S���<l�u���_��#Gu��6C>����!W���6�\��42=����*M0R��(���Ah�n7w���������O��+��Vt�4�I��RHj�
I�J�R�S"�4��k��yp�#�E��������0�z<���7����/����C���[�!y����
Q�O���?���qrNi�b��{�������B�-���o%�E%�@6����d�[�sC��O�_e���M������& ��kT���*mO{������������w��uH���x~���@U��v�4����;/j�'�/�^�7h���{�{v\�
fa��A���bc{���
0b��tD'3�>a�������{��,I�����A��	�� ���&�bk��~���������u*:�����������������7�������Q��P�FvZ���j������tu��4�:;�x�������J��t�ln7Q����vw'��������RLS�����}t�����N��������b��~2���)���ZG��sy
��b�0A&rHs��J� "����������^8����$�5{�;��z?F�y���P��$�B}���A7�)�;L^���P���@������b)��n#S���&P�^�_;,���Z���9���I��5sxj9�`0��adJ'���%���*����]��:�����������=����T�"��h��@����=a�t��3H�$�N����c+�b�.����?Ge��������9|>��#$�p<�?O��LI	$
4�:)�h�T������{����������I��^�?`��@];��������"���b��3
'��.�7���� ������Cc8@u��M��j�0���N�Pmno���T+��3�� ��T
3�0�������H�`"�v�R/��M�[q���$��9���b���1Q`�U)���u"T�d�y]b��Y�d���V���|MD�g@�U�L]����g�����n��U���UqQZ2$�����u�(��H���=:�� )����iN�(0�9��QTG������"�d��E[�l�z��\�u��@��.n�so<D��a�^�	�+�=A��xc������rn�TM)��i��_-�S��@Vj��2����%%bW3�`�N-9��Dq����f����I��H�9�)f�u���������^bGu�������`��Ob�Fe	�d=��6U�m��=�Y��<��������e�X�J���A���d�A��H�C�Z����8]����T�$�-��N���G��l&�0��SV��;���m��Qc"��0�	�A~u(������O��\m�q"�R���'^t��b������;���HGtY1h?�����g������.ka�T��]�8C����V�����`g�����=�G�1�d[s,c|�u�+���7<��h�Z���V,�aZ�����e]���z�7B����r�-w�X�X�o������4��������4���s�d�����:S�#W�=$��=�@�/�%#j��o�o�u���}��C�[Z���������*��HaaZ`{������Lx&���_�w���-�T�\���b#A[���;���o1T���l�F*���*�f<�����/p��l��f��;�n0�����m��u���1�������q��I8v��}r�E>��y~L����`amE�ZsT���$���o;���K���nL����d�i6��t��N�;�O�
������C`�m�5X�_�����j4l8!��5e+u`g��_��x�mF�5�*6��a�A&�*n�F���Ah���F������0�bf�n�����za�0Z���.�}f��7+�^����6d��k��:�jg�83{o����]]/E?�N��b"�H�*lV�enM�:k!n��8���9m���8I�LL����I�:oGM�2>b�{��o#�R�>X*������z�����#�:�Iye����X=;f��G�����n	�.?�������$����S��qv�J5�gdK������[q���T@��W ����J�g�KQ����|��|i��oLp����
��M�"j��o�v��b��]km/;B�S+�������8��Al	������+�k��H�HUo.a������=�`�������OiB�<�����u��=f-��}e���xK�t/�7k��6��&����t%�q�Pg�\@]��Sodvq�6�`�!���}L���H�R���sm���U�oUR�C9��Kxm�%������s�>�\���W�`_��2l�nL����Q�I)������W����#�{m�$�v�<����TW���Iv8��-�x��RE
E����F������C3���`;����O0��l�m\��r[b��{Wz!!����q����� �����������BCN�(J�m������znb�8b��o�%���`��<!JV�;N>Z������>O��6Ww�������l����s:�!|��1�"���-�hx����gJ����B����g��gSl����{��]���Cl$�5g�J�a2����&��4�������EO�������oBB�����<g��Bk*^���������������uR
������0/=w�IX��X+t$'
��
�8��5��'"�����-������#g�?�i>*g;[]'�|��mN����K��]\��0��o6���-�_Ei�����W4=h��fL4u����X�9

�B��/$�&q,�$w$P���t�����)�g������U8���d����� Z�����;�f1������P��jC����,�cgz�N����Ak<���@W��S*U�������6�����N��!��K�(J�+�������d���s2��a�e\!��fJf�L|$����I�I������-����l+�H�x�J&<OJ�PF��AE�����T�lQ��':Zw���Oes`��0�b*[R���):"��"2�KTW�����Me��"����t��\�H�9?�� �<����bU{��dB�cq)}��������7�t��M��%���]���w0�PQ����"���YC���6t�0Z���yi�"�����
�%4&���.�8$��:��7����WK
x���x�bQ�8�m������#T���V�91��$���-	��'�1���T�����1�(�K�
�"T��!n���m]��V�����6CJ4<�%���MJQ7�w$67]
n=bGbJ���X6 ��~�
]��`1X�6�w��1E�����/>�����#��{�U��1daW;7D���!sV�0a�">i\	��W.z^�U����(��&?�Y���|~5�Et �����!uq�IL�+2b��>*7@9�������}�#w���M���/��e)^��B���
s# �X@�'t/��8}�L��K�������{>m�)8m��V��u��\���XL�}�hJT[����\��E�;T�Q�-�_�pr��d����D��k+���������Gnr�����gM�����Q%��cMb�V��Y��E�S*��9�����%s������T��v���Vf����6�C�SsL�q2�z+z�%;���5J��
�6,Yb
��d���R��vrc��7�1��r-�`���dv�3
���R�_���3e���g~OE�t�^���v�m�S��^����,�:pe�a ���0��t�7�~@�)�p�����E��:E�vY��i���V/����b�v����1pq!�K�`,V�>��Y�`����(��9!L2����6 �d�Yrz<��*��d�����E*��`����l�UqZ��k�:��p���\x.��-�ZYVD�D����4� ��������3��S�#Ieu�*�������}�
������9>�����.���������c����)��>e���n.�����+���s�z�T)Uoz�r�]������dA����p	T�{���v��`o�,s���P�]:��9�����@��)���e�>�W��I������3B�[������X��1�La������|����)�R�3+b�_�]+�{M����N7��W����2��S�x:�{s���ZM%��Mq`�8sJ��Y'��nQ�:�\����'c�����������)�s�/��..�8E���8@�����-��O"u4
�%���%�S��Y�����I�9N�Y����hIB'��+3:iC���TB�%�����N��'��W��R�X�(���~B"�q�K{�������<k��K�4��������ur���h<V�������:����]��N��T�Y�]1 #(}���H���	a=h�,�l3�#s'���Hc��\m�����Q�!��OK��!�������xL��#��u-��Q�>
�0\E��M�[5sT\(�I��|��k�If��jF&�C ��Z�g�+��}��I��x�B4U����CH������Z����@�	)�����l��v������m�ZnM�/[~�7{�>2?j"����l�0����T��z�����*����^����l-Uc��.�8;�)}�����@��{�)�z��y[o����%���e4�^����D��������SS=�VW?@H��=� �������9fZg��������k�j�$�*��c��a�����H�r�Z&��T�~mJ3
bo�*��&�k}�����e���X�f,�UD
���t��y
�MH�7'�S����v��4�}}[*~cB� �[��a4��_M�F�E�Xg&��R�_K�R����N�c��[�;E��!�����oJ��;L��u���'����q�.�u��}��\&��'�����=?�\�B[�O/�Y0����o���[�N����tgK_IJ���:�����`��A��:9���+{����O/�����dI-����E�������?;���;��>{�m�����\�j_��hF���<#bi���;�s�.�.������Ewx
���G�]����o��������;������o�Sy���Z����XX�3�"�8�������nB�$��:O�n�=�w<������9��s�C�9��~i�e:��
*C�_�����y�_\�j������Y�"�	�]��	c<�����a��W>�K�Y�nF�����)Q��KA=�*���N�(�sJn�}7>����/C��5�<��w�0��X%���{e~v�8���1���N�%��������(@$<�9����x@���L��+���
�H������� ���^;GG�~_��F���[b	���~wxtuy�=BN���(����<3�ij������K.z���<in��"FqBC�;����Z�n�:� �5�y#t<������/��3;}��zf�D��k��b���������d��Nx�+�N��7�=9C�Y����kc(�����Y,�z�y���yRJOS`JG%�_�fB2��S`��H�"��n�����K�F<���|�#?�����@�t���I���^���\��2�]���Hdy� �8�'1���'	w���\����N*nf�`�k��������h���\	>�>=����~w�"����J�])����������T��i��"
KH+�T�R'p��H���/f�l��u��r�a,���C��Y��B"�f�G��A��#U�1v�{�����$�,�D� ��$�����������Xp�.K�$X;�JK��O*Mt�}^�����;F_�#I�;��p�x6Z�]�?C��aI
���0���H��1����TXl��0l��(��da����yM��;����`*Va����V^QC�t�)�9���x��Eb)�I8��p���}��|���������N���~xr�9�;7�+	���J��
�Gr�����3�|��'�8�7]�2��9R����M��������Y�n�}4hd�w��������������,K��s���>��N��5lM�0��^�l]m1A�R�g�iR��2��]bk���Gb�Og�D���H���"2�F�c��z�z.<qNgG3qBrF�����R��C�T�U��h�6Xro�c�O"�#9�C�����K��������� �m�k��bMb���X�g,�B��A����x����H�#�R�V<�cE���3���^��/�W��E��zx��>X-�|��:�������,��g}���rx�9����&�"3\)�m�$�%������%L�ws=�W7�����N��=2v��e���@��D8�)��zG�H�y�N��D9r��(����xF$����*G��P���c���j�Fi�����1\z����5�h��o�e����^�R�b����/�HSR�i���T5k@j�.��y��OP��*�|��,6��O�cA�_���%����&8���w�J���V�]��������<���0��X�?&�<���@<�!
��V6��}
�G}=]��Kb:*J�l��t�f^&:����"����"t�$��zrZ#��[svl2�,cHG�Ge3?�-ws���^�f�$C�B���g�C
��w���^k��h�6����}���<-� ��j�^�SJ��S;P��7����h��(��R�XoLL>+��
V__��TK��*��I��]B1��e=���L�A7c�MR�C�vMd_�9:H��f�@��XSe
���d���^�A�LS�dn�p��<�4�]����e�a@!�C�M���k{M����P��La�K�z
�����KLg�<��+�Yj��'���`�$�M���8T�����"/��be�m���d���p�kNE����G�&x-�p�t�3�k����0|5�p�k�;�7gc�
�)M����u����R���<�{"��I���E��F��e_�G�P� �����zEm8�;�4�f�w\�6����<��/~������p����a�Cw�wj�M{Ka�gO������>p�p�i:��5NF����&0|�sJQ(�`�T;&����;?���woHf)Q�e�b��EF6�Ct�x��S+�u�s���rv�!ei�XRL��0of8x��v�)��'KCb�@�/��F���VK�.Yb2P"�f'����G�*`mEE��W����/5��0HUA�qAE�!$��Z	)j1�i��I�6l�>=�w����"�������w�=�����������a�/��C&/U`[.�J���'�&,����N�#�����8����bR���{�
����$�%���$3NVVZ�jXm���0|f�775�f�(Vf�-�x 6F����H����
.F_xB�=Et��]H�_��}`Bv��]�Hh�����K�����f5�v��D��w<����'�Vt���i�2m����E�i��+�9l���������}IMU����#9]y��Nj��!n��q��81A�d���>#�`�_V���Q������d����������>�%����<���
��!�	8z�.�\R�-o�=�Ty�"E�`�$\�k���Iy�����FJp <�>��������j�}���e��������^�u�;��5�0�b�|� "rUX��&a������Yn��Icc`6�i�F��'�����^�+�m���P@sdO���7&�����%'Tk�����������G�	p|'��O�*=�d>y4���,�<�"��������
�#�������W����N�+�|�WC�}�"'�6�[�M�FSe����@^@��m.{���#9������I����"�� ���Jw� P�����$$�lCm����"N3w8������y��������\�J��TJ=������d��]�:Z�&x���h��^�xp�W��&c%�M����3�1�)�Z�L��=�7�u���:;��M��fy�b���gjvP����;6�����i�*_�m\��U�����������^+_�t��G�X�69�R�':�u�
�-Q���Y�D)s��
a�\Y�+�K�<���0}��I�*]3��:������*#�i�M�>I����{���Y�43JgX�7��#2�i�u�mf��(Ypd��4���(���8L�1F1���?��D&���Y)��"�)v���@9�c�|I�Gu���$�����D�$Nq�I0u>��K���3�-"m���b
��H�*���S���|2
�C����RZq���V)�G�Ei�S�#Ue�o�[M���c����7%��8��T Mq�Y�d0�b��|?hE\�5�2>^�.��U���p���gM�
��|�y��mS�I�L����7�md��On��]�d?�7	W��������W������k8�54�k�����/1%�Ii`����&�ue>�����M��%W�\��h�N%�-,*�Q��iQY�7���~�T[��IP�A<���?Z��fi; z�$�<M����$]�K�������i7�\�j�}��=M���]O��q*�����UR�^\�dn9�3�D���|G9>�g��b�F���aV@��e}r���F9b��YH��7]M�����S	�������q�Fn���&�N��NKp��:��"��sE�J�R�2�R)��
.�QZ%��0jF�J����	���,���kq���i�r�5�+"t���O+kC�A�%a���fS��>�P(S}9�h�����:���u�������t��N����O�C�Y�S�!����2R����|H���������2���X�
'�w�q�V�Lg�M#���;��B�/6"��rq9�
]FNG0���t�i�b�Z@�<e�w%�i�'a�k�2��il��������r��[���j�O����,+=Nk�f�)xJ���s���Y��4#7A��s!��r2m�J�>�y�t�c�Ji^�>b<��dbk��!7AV�Z�U�}���,���z���TUk���H�j��,	����5��q�C{��q����1�RA8,�BMai��M{��\Q�1�,Yd��'�!���T��>}����RW��x�����X��X��$"�w���$q ������E����id��GR�U2Kl4�i��%)��134�B"d�	k�iG���|�����E����q��M{�������^w$�a�6��f�������n��G�W�������\[3�[U�D�E6��c.��z���n���� �Nc�W���=��j�Q��)�(5�*���_x����.7O�6�8kyV����������+/5g\�.3�T��
|�w++m���}�z'�X*	2��|��@N���8'FB�# ��U��`W�|!]a9�Nm��h#2��!��i�g�4ZyI��fV���f*A'�tb��k�[�I�������8Y�/_ X��k��~f��,,��u��e�g�W�*�xT��6�*2O-
2���p�=e-��F A
	�Z���a^���������	������|���K�*J�,����Q�@*�O���������Yd�.($�4�Hou��G�z����&�})j5&�)��]L����w��d��0�+�F�y��a���
^
��.#����}�|�]�$-S&K���isM,�Ji����<y}�d����4%G�KM��4f��rz�WF�b����on��C&Z���t")�H���n�r���F���x����tt~�?�k��y��qz��i��I�G!�q���Y�5(�q�O��.�x+��o9��e ��r8H��x����>?``��e�|rM�m�eVl0��ZqM6��%#>��s�F��Hz+��B��^5[am_AfU[C��N�l�}�Jz	��}	,kD�{�J��k����5�Fv_RH��R�8��zJ�R*�H�O2_�e�j�y��E)AU�9���~��	� �|���"w_���o�m�����"���,���\&R�L��B��1�+s�_&g�FN����8��nl-��"	9�c��0����?�2%�S�!S�I�;��FL�WS���KRW��48\
����,�C����1�3��S\OL�^���/�9^y�pY�-e9�vg��j�����8�bi"Vv�9F�r6PQ�����$���
}��UV-CL����F�����,S���[K�'��^�.��]���������G�g�f@CO�s�s���&�O����y�R�Zi�$�s&�7����f�I�- {��<��:���5Q�����s_�M.�lV'��*'��C���$��Fj(����W����s=�lZe�������LP����,�'��,��#IE������-ie��c���Mk���p���|#�D�N�����u�kb�]���N�6!* T�F
�&�����uF!Y��Yy\H.������>h�N�Z��
�`�]�U#�A��_3���&�>��q��?�L�)iJ�J����)��7��[w����#Q�u��J�L��Y}�b��J�o���7�~�)?���\��,�:3��R��4�Y���dm��cZ�i��X�"����^P(��p�5��y��fpB�rh���%�g�`��/E�+Q������OlU���YP��5�Z����*�:���,�k��VzZ^����L�F�d�+R�s�<i��G\pZY�F4Z%�+J2�d}�4�R�Q������H�7������V8�,K�QH�����x�dY2�*%�QXLP�����Z��>S�%O?R����;L�5;��������`�Q�1��F���J�f���N����oZ��}_MgK�Y��Q<��Se;����{�^;�'K�&�g1�g���f�,����a6|���K�e'������*g���S��4�th	7i�-���0�0����1Z�R8��w���e���=�[Jy��?D��p5�b[ �R�e�u������#otU��0^�dCBM�m�F}�$
����,EJ��i������R�P&>U�����A���{���B�d9.k5���cT����1Yk�{�W���"�#�C���n��F�����m��U���0���BO��y*O�j�h�I�����\�D$]\.5,-!�_��K4������L���E_�r
S��H��"��<���8�W*V�rO����
}�a��������G6��d��F&"�f���'"���V�w[����^�]��Y�cv660���8*K����Q�����=��'9��/q%C�I��k@��JtAr�;��J#T	��E�)�;M�:Y+��*��
�+�>
Je�C �����.}Y�����ps���������R5)*�)�[�{m0a� �Y�3,K����E��*U�����3{��	����b�{ /q��D�zMVZ��c.?F�l�L�usGf���:���)�R�[��,����`��T�*�VL�����]�Q�J��81��\4������e�G�P�j��dY�:������fF��P]�CJ�T�^�v�������lAv���7b�����pV/��~��X�@n�P�:F��?�=�&���*��I��w��{,(����������t��7m4�Iso����?��r������EO��b���@GN����9�[��u�<�����.��;�:�h�.�������;J�7�
Cl�^����V�����rC��1t��������k����IE��C6�4�r��Nk��7��wF��~�q8�Lvw������N{|���o%�U�������Vv����S�W	P�On]�����s���w�wX��^����U��A���J��A>����4�������6^���n��}���:AX���s)���'wo�=r��D�������+�Wj�b$61�c��T�-2�M:d�N�������"x�p����]b�@,�1�xIVp;yW?�S05�F�0�Wn��L��e����
�J	����9�5�����)�<9�����iL������c����R�faRD&�V�1������xbF`��e�>e����i�j����(�����i>���F5 �g���3r=f@"�WE�����#�����K��>�~��(�XMbR�E�3B���;z������\�]�����-�K�p�K��>�d1	�LJ��=�]LO;o�6r�{���=�f�5��G��%&-��1�����ljTA����U�}�h�rC���K��e��W��m���������i�3�'KV <^�g�2��]��+=��wt� �(������X����=�W�2S��,��\���A+KI���6�ryNu�rp�f�"���"R��L������'��E@)�R�L��m�]{�I����������x�}���	{�;�>�����S�=��4 JF� }���$��,��m�������l48����6�[�s���$X�|q�"�G��:�'H������?r&[f���^��
|���C��s�������%1����+��J���c79O�-�J-T����LSI��F:��b�X�3�i,�������;��;��K\x��>
������xt�7�05Y�$L�&��
a�Fl��j��!90�P9#��1�F~���9�y��!M���^�v��������<wl�(�bb�(������BB��TS��MA�T_��^�hxvyr5DI���bC���M�?��������%��-�O	�������r	i��,��{�W�"��p2�na� �D��KO��qZ���._J����I��`�E�d��"H�hc��n��5V�gv���1�������DQ���-��x:5�R5��l�T��/j��L��D8�t1���]!��~iH[���=#�H',=����s�j?���v�T������x�S�����u��C		��A�ku~Z�@���|����F��
{�q�W����_�������Z
���Jf�0�eb?�{�!�E��7(�&�������
���Y��	
��z��f�����t����j@��$���]�GAc'�}��T�R�z��0�f�}X;T����N�����#����{���W)�J�B����R	�L�B2#�_���s��y������_qN�J�xy�kgPI��c8 ��	�4L�������Z��b�Zb�t�4r�4=a�h�x�z#DC'��/������U �):j!7�l�Y�������$�1&�-l���/�ml�(���
������J�d��,OBN������L�LQ���PB�~tc�U�S[�ERa�$aq�E�],\���$��c��m���������U{y�g6;��L�e.V]�,��<=�R�$�W/2�e�x+
W����|;�g��>Kv�)Eo��?�@�H|����G%�1�G����tf_+#<Y�����\����/^8y�49�}L3�b:N"XW��\}��L_��u�|��92�`K��������=���a�-�TO���S�����:���y�ZM@�����?��N����&
8S�-1���[-���{0N^�t�?��&)�����5�`���i�[�}.�-Q�0����q���jG���r���7S���@Y�k:�
P�����9�~+���
�������o�^�:c*����[��Ot>���S��-�m2\���K�����e������~�AvC�	,m��D���
�z�1������0��uo(P$���ZN�5���������R�N����dA��X��F'�U��C�k%W��d/p6r��}>���Zk�����H��r���h��	V��	�/�C�JC�Ie�N����K�0l6�(���!+U��;M�iD�^&�~�X7�!���e����[�2?���\�"��2��T��:T��,�]x\��6��i��j��ov���N���������{1El����.��=���hr���2Z�����T
"��S�����~��;E6��LP�3U�1����eUHM��_!��d�O^5�{,��F����'�c�l����xC��
��0I���8h�{Ckog�Kw}K�@"��P#u���BE�Y�`/�v�T�"��ygR� �~u�	y���{q�������>Ve��^��(���:&6/�0W�G��#\����n����s���	��H(&FD$S���������o�I�iYt(�s�OR0��������:��UK�jx����;��z�_�u����������!Y��v���B�,JC��IMxG9�\�)�����ZD��������(%H7�����t�`�q���C���n����h	D?��9Q���g2��2����|�H������i���TE(6-�3O��Hz��K����h2������k8�����j	3&Wp������$���]H������1�������Sdi����A���@�d����*�j�*b~�=IR�����A6 `�O?Ov����V&� ��(<�Q6e��W������o8����D��������da�vu����S8��)e��u���N<N)����CF]�:[��i�6
����(��eT�c���������O�A�7�*����k�s�/P=�r���R�f��M�>�L��q�8�S�`b[tUC���/M�(P@�.����&�"���.O�s_+1I�i��������,<
���8��7�O�P�.s��{b���eeN��h�'J)/���8�3]F3S�y���[�#��wf���Z]�~��y��������b����E56����NuA�BW����XVe�x��gR��/��x,��/�?Wx��X�"��RZ�e������p1���7G��d$��JQ\��<r�;o�
�h��`&'�D��vW����jF����S'��9�B�q�^D�m����|H]	�
�X��C�e���x�f�Y{��s���(����H�j��"I�"\OS�1�^��od*wO:7������������eC�Mr��dd����2�'�/D\�y�����s�.�#�Q!�
����R����%��%��rv�?;�ps�I+mpm��|1���T6�0��
}�U�a�Rr�xJ��Q�����79���'A��#	r���@*t��CY��a�3�R\�^������&�����r����?��l�����i� �I#+F$��L��&�zJg�8q�52���������|h�Ft��+o����M�I��Q��"��t�C�iV2H{�K���GA�������R#d*�i�9��rZ�<8�<s������C;�sh�_���@WK�|�� �x�o2�1#3�����p%�w\������2��tp��i�MG��t��X4�c��k��;9�~:{�vz��������n�����efw�<�2���}u�4��������U��3������?�EY����~��K����Yt���Ji\
/�^����7M���mU��VIH%!m2'��rOa��a��H���~���e������F��s�����.��~QZ�=�8Y���5a{*2F�s�b%"��w�H�m��{�����%�+W��B-7r�9�<��Z�2����aHU��_��	Ik�`j	���~O�^�F�8�'���&�1]����+J^=h:�\��G]H���|V�)�b-H�u���K�q������v
;i�B�K��c7���P������@���Zd���K��p������K/�q.e�fU\7oR��m?����9�������zDV'�1�
y��W��1���U
2];��=�����7<CE��z����r��t������V$-	/���
N<f������A>J��7+��U��hC�O��fW�Np�������5����7������C����A���l�$�4�^@�����&U>��z��2%�t������������:���������3����PT��/-y�t���d�_��3���>�nEr��_{nv� � ������XT�'�ze6Lb;p�����0��~�<A:�1_t��'qx#��H�%��q��������dp��T�����38������+�(I�\_��>�	�����e�y^S;g�a��X��e�����\[P���6a�I����OU*��P�^7����	�Q����6q�eC�ndVdf��J(��
dc��1o�����]F�3XX3%���p�<+k4���
r���,s�YF�������J#�%�������_������WhT^��hb:��
�4�n���T�\���)�IUL(�5����U�5%����e��1����t�
dA�\��Zw����
�W��
�p�	R��1����)���T���
��zl���������d�d/��O�������G�������
/e0�'�Rb5�9?�m�:�����yY���FQ����+����f&\.�{�/���N��/cn����Y?��b5�M��[��G5zi��y|�["��+PHJf�oy�g��I�F�oy� �8DU�z�d��%{�D	U��P��D%V����;:_����D�f>*��
-�4V�D������yA
�	��Z��l��>��g� ���<�e@./�������Z��Ct�|AGZ����UbYP��r���=&�Pgc��u�i�(�������O�m��+���_�R�i���H�����Z��]�x0#lv���_9�$�1M	�Mq��oH�1�|j�������M���V��[��E��m�������Eq+r����+����w[����q��_v��~�n�i�>QixcK�%B�����\Q�����5�X)��<U_����f����i~e$?4��N��C��o.����Q��W�`^����{y<<�:�;�������K��v���EZB����@�m����)���"�����a���OIK`��E��y������7J[j�mY�t;�����H�kg�b��%MK�,oj���;����� N���.�������A����_3 �/0l�����$�n�A�;'������c�<J�y���L���b��l�B�0��V8���g��V�l��4{,~!6���KL��J���iI���l��U���GU�Vf�����.�����F�}%Z��������o�I��f�~��x���Z�#�|����������&�.�3����+�U�O�\�&@i�o�?VV ��_��t�5�1��J[��x�R����^K��|P	f�JY:�pk��qTC���l�7�&t���l��K�-e$�(���Tb<%S42-a�j+d!�=8�WY^�D_W�0[Qw+��k[�3���M�3����]���v}:����	����������e��D�'��=�����G�����K��G���G�I-}�>=��$~�Qm
��9P6�.F-��-=����Mk���L2q���)?��J�o ��E{7��G$pH����Ex�� '��\�
�h�lF��8}��8��	'�YJ��41�����)��9]�)Tz�����/.]�5��"�+f[95�������G�&_GM&Sr���;�=�	0N�p<C�t���X[���}������C!��r��I�k;Z>SS�*J�&W�����j����'f��Y��|�0����ws�-��� ��/���hW6ekl�������/y���\~I6`Y�3���-X�;)��f6�w�0����~�M{S�+�g��$�)����[��gp�
���S��[-��Y�C�������^V%�����Ga[����'���4��CWS��t^��s��4>����P}�V�RRv%����
��Q�_���}��(�G����t�O�$Ki���|�R}[9p���k&��@7?��"���!vI��`^��jCU��;�3����q�)�n����K�g������:eL�����ex��+��BK:�^v�OU<�����c����8�|�_��Y�y��wdI�5�k$��g�d�h�# 4XVNr?����Wwu�l9��������k?��)��2dw����J�-�����0'����5b�����M�

�P7������u���������W���i�J����1��y�����H�G.m����<�^P�|/:[�B|�A����IGh�
��%�WK�&t�f�r��?� ����n:����V�>�S\���0��:���>���K����Rc����^�Ea�Z(�V`�m�G�����g:HZ���Tq����p)��uN��iaH�>r�d���6�Q�[�e��Y�cz�$�w���'jRJ�	�0��t*p,���R%'����%O�d���Z�:NN�����'3d�����`h���;�?p.	�����g��QU�T�No����C������<��7{�u���dY�rZT*Y���&��������O
+��&�U��������y���@�c40?"�|
�f�}0�W�$�-Ws�����_B9�����9B��:T�8�c�q0x���b,���W���Wd�m"���bh*��9����e��v��������U�D�\/{������1����byuw��	U�?\K�0<)6o�;)����8�B�Q�����E���e�Kt�U���ZI������2����Y\_��4�DO�&�>FcT�26�qn8C&2;��N
��Q
_��j����F�p{��6�x7�Hpf<�H�����Z+l9���gJu���v�q�1� ��
�aL���LX��J~l�*���'���r�9���j��c��]}�����=�f4�>��:,i��K�W�%�d	��M�
�����w`WU`��P�}���� ��NW����! ���d�p����d!� ��M�dn��Mv�g���<�aMj��7�)�
7���w�n�
��l3P30��Wn��)�P0?Y�&���
b-������V�zV�{��W��LP��R/�3
��J�R���3�M��tNz5�2���\����R|�5(+g*��RH�x���	�`�j���:����0��W����2��=�E?w�f�����0�����F��
)g#1�'��.���"��W�5��O�P�����O\���������D&�7=B��8Kl�@n""c�g�
���p�8K�1�\�&�S�Ml?����N�"��>L�y���[���~�A��d�Vq�v�M�>���#��'Y&���6�����)}����v^�T��x'pv3���R������^S�
 ��.��n9��iK�O�P{���)%�3
��:��3�!l�Q;�P]����`3=���Q��{b�b�a�����	���
�)�/)/�@��i(~������n���X`�nm��N^2/�|(��Cy��"�Rj�v��=���2)�H�)5z�������I�N���iKp��L�w�d>J��d��wV�$���jn�h��m}q{�o��<7U��,��4��)�;_�k�4w��n�t��nw��F�n�����[Y�[��-F��$��2fS|�eF�O�,3��j�Q��4(�/�8��z������JZo����[��R���"$��l�������d��������c������=#[�
�H-��a�G}�s�~T������M�����`�����!yj��6��I
�K(�x�B2����m��w�n��n%�����q{4h�a+T��v�6j��F3{?�kKoh9�$�r���?��Q��{�%�v�B�}I^@��L��
`��o��}�mN���)A�z�7z����r�
��}!�d��-��e����"�h�.��/@n��
H[�]k���z��0�R�|�7���=�p����lTU����;���vkg\����Zg�	��h�m�������Y�����������U����7��O�����x���/��K:|�{�~x��!�*t�C�I��&�!M�3�$C������y[b'�'�f��X+I�fM��.�ng�mT���� �;�)�8�+��F~�,�bJ���RRy0o�Z�x�v��`�j�����������0��l�U_�V�Kb�&PT�w���!Q@$��A�i
s+��O���l)i_0�?eg���l�(j��!��[���$s8�{�?�M�k��<�!]�A���1�;���q�O������+�n�9�<����#*o<���yxF��G�\�T�$
����oD����\�����"����b�^��p�gzO�y��������O��O�iK������O�R0���;������M��N|m����}����p��[���� �@�@�e�J��RT� �@�/m��ij2��
�G)��.�T�.Y�F��h�j���G������cy�JO!<�1���y�M��c~;�_�<�}
K�G���e���d� �����ALV�N$aO��ex���ct���M0�����|��7������"]7������`�a�]g�C5�����|l�F����1`|8�S��9��w_�n��k��Kh��h��8��=f���+L�j��q���Z�A2X�����X�$U�������q��yA\��h��,���.v����%�v!��?�?%~tH��l;���}R)��F��"m&{���.��I ?������W���D�WT��^���5)CV��q�]kw��qg=y�V�M���a��o�m�7&LJ��~��V�(����@t�2A=)�^�������O�]�TTa��SP?Z;�
"Kg�0V	�x��pT$KT�[�Z{�����W����Rxrz��m��S��5�u��~����B���8@��J����t^�#\ <nE���5�0DE�W�V����g6�5��h�6��d���#���z���VLe-������2\'�`5s��[^oO�V��N>�������3�"p��Q���j���NU�^�T���(7���D�%|���
i�w�D��H��zm�Ab&�KI��G�y��@�=�?
O��W����$^�5j�M����Q���?�q����f��������N��|z�do�
����f���})f�B��T�_	 ��#v��~��7��~�������*P�S�}��G�	��R#%�^Z�`:�����d�1��O�r���U���
~S_�=+���Z�5k�&���.���*N�/R���Y�����w�{�������=?=9}�]���st������R�&g-��V��7����w��A�p�t����?
V���`�Z�`u}Iyp*J������Ow<����a(�#1�%s��`7!�*��IV�*�
�0f�.y&��z(e���>�+�@Y3l�6"�5�R��w`#���1�C�����,/t.�D�jI���������Nf����(����6:'��D���A�P+��B�����P����=;<8�&�L��\��S��~/K~��3Y��N{0����LVk���X5j�,�U�UJ�t����������Ym0�\�A��dD�)H�J�����&F�Iq[�M���_��������`
���r
�a:#�w���T�?���4���)��d������k���c%&6�Q���u���
��a�Wa��fV�y�L8f;H\���"��`_�cp0�
��t��a#`!�TNQ�����|5Y2so�H'���f�����I1Z�����v�I�m�_|�R������`�����`�)���q<=���E�]�D��.�y@����b��;d���v��K5�@��*���y+�]�����x���)�1qnt�xXk������m�&��o�a�rk��7�B����������3���0���������9C���_��(kJ����P� ��LkHP/�Y�ljn{z$h�M�_@��\G�X�a�2`���;�x���K	�u�RGF��ogB7���f;|����Ac���zX����=���%��(��S�Wu�b(�������GN	j��(C*o':��t�)f���y+���g�5�����^AK�::d�G*40�A�	:��PSMB�b�Im�@��4e�}}TqW,�W0d�+
��`N@���k=���Zy-�U�9�Um���m�tB��m��Cc5��������~]�JV���"�,2�$�����sG�_�j��+��0)Z#u�^���4�_- _�?E���Y�{��!��t�)}.o�dS��2�P���>���O�cP!lR���������j����F���Z~u���)�M��E��3	���8D���p(�H&`��#��E�C]�2�>�\��K��e��G�9�/sPm,F�?*��~�H�_r�fN 3�?p�Z�����vv��Y��`4lJ&t���u�
u�Knb�Dg=�S�p��}S�
�4����L����lN#�*�
�b�'�_q�I�� ��NV��"N����s���[����E��]IQ�S�k�V�$;�/��k����<���WD���{�Q/���n�:�agP
s�oei��-FjB��b��b�'�(Ifb�/�)�G���j���;�����N���QG�Dtv�FK���lV�@L���B���WB���{�)��	^R�tS��-�
^�>qLL=9�A�0����F��S���`���29�}wz'�����p�B
�4�T���w��&�ar��j|!
K^��\� ���+�T'�����W������ S���6�PXg����%���Q|8:}��4���?.n���e*��������:*��|Nv������;��wz�Q�3�9��tM�C�.'��3���k�p*����Bvq
�����'��ge&'3qz"����l��\)��#�x�N�~�������/����e����
x�W�������zD5��*'��;m�?r�W�\y��vHC��b��q
0����<����Oyk%VU��[�1�;;��x��V�=I\��N����� �c�#�	v1���{�����N����<o�7��>'�������d?�88��O+����\���e�i��������+`�k��`W��b��W���=��R��a8�/l���v��
��0�xH����
2J�jz��e�����ZgWa���gl��m���~��i����L��)����4����'�G���[����VIeUb���U�[04[� v$��h��Z�>�c|R��%U�|	��K�yA��&�h
�����Q,O��{��{��<����|f��r*��U�~S��������[�����<;;������
�������$��TY�O�cW��5j�zT��v����������j�g�������6�SoxXMHB��.������BED�r�������\{�k���72� �����:��N#��M�L�/��(�T3�ky����n���S�L�v���%�M9�hy���(�x{4~�2�{���#~
�t��iy�Q�V�7i�����������ZG�)LE������_�����t�����R*��=�%��7�:;�Z�3�V��z��k�vF��6��+w�R������[<�(o�M8D]�L	-�(l�o��m����f+�-E�]��>`������~�������nLi!
x�r����<�����w���G�o
���Zv
'���� E�@�ho,3�]�S3T��V-&�/w�)b��_�����_�������7��u���fU��|������F^�4�S<c���@;�&v�Z��=D�/V�J���l��|F��I	�!�������`p=a$�Y���P����U��ws^O,0UaMsvC�[����b��<��������O�`�-I��=��j5<���SN�~�����+���]��Rq���h�������Z8n0������f7�V�Z5��Y��kOC0
&w������a����C�81/��)"��7��E��}O)�t8��~W1������a�z��N���5[�v����x7��P�
���������8D�+B����"A�.��3�'��N�������^^��Y���A��
�U��A����?n�e4���R��H��6�*n{%����z�_�����Kd���J�����!J+d9��|�"�c�C$�X�pUf���8y��U~�������0Z������N\A���� ����F�Q�������
�����������j����8���$3�(����U0�Je5��5,����`�s�U�l���Q��;�6�[k�w��Y�i�E����j��NM��/;�$j���+�r��.f9�d��8�9
F��$^��~gg���zg4��0l����0h����m���h���c)]�s!)G���
�i�P�3qy=�	bqS)~X��*N��^��3Z�n^��e(�
�������'�g��g
��h�j��zD<;�8���F�'!7�0��7K+ nNH8F��X���Du�����������Z�i�o7����H�/I��I�P�����y���T��1�a��+��1����|�Z|'�����/�\N���@�t�
��B���m���uv~���!)���TJxaN��������d�+EQ~�0��VRD;�cb����O����&,�����3�D�Q������G��.�K������3�$�~��>`�)����[����L�<����r?{���0~z�to� �����;���W���ux:]|��u�|u���n�y�hv��f��<��Y^h���-�76�Ea;��=,#�0>��Cu(/T���m��}�}P�m�_�B�E��2�����R�z��ZI�1���`*\���B�8D|��]�n�����!�[�|ClW�Ef���/4�������A�$�����oxL��QY4MA������U���H������Ao���&9�=�� 3���(�w��P2�A��it���$���%9U�cJr
S�;�gq(		"E�`(�����/o����P����(7Pi��C�JK�����,i�����0��xBT��	���A�J��dS�)�~>^�aI�>
����/|dU��}���{sE����bR=����v���eiI���<\��������]*�MF�������3
F�
���������N/)4�7��������������<��w�d��4�N�F`���<���@��>E�uEayvaa6�G��,Q�m������<�N�j�
��um�I�[S������!��#��9�2@E?�<X)�����������"���}�	�P������j�9�`�$y>����	���
�7*oR�;�+�w��R��S��#��B����T�%|u�ok�������J/�eU��?0'?�x��Mh4�s<z�Ua��j�@]�9����Q��F��h��f<9��z���;9!���2��D9�&#�����H����?�u�A�
���b1D_N)=�4�0�������������v��Z��no���J��*M��z����)��y�J�O���u�s���1�o�}���S��
��z�a�������v���t\���H~�G<�7zL���Li��A��d�%�%�v�%IV���U�t�n_����wo�_'+7��tK�}ELQ)}���|h��wM1�HR�Vc$8�>��b�!�h��I�@?���]85����-��*�-��&��<�]_-T���:s�S�>.�5��w������Bf���jM
m�~dd��Q�I�Hl0���[0>#q�I>�Km��EQ�+;\Ph�4Q��6��_j�H
�J�t*a��Z{5���e������&����	c���vAC"����O)DS��A@�$������c�T<w"��`���q~�Iuz��Bn�O�������},J�.*�h�����o��K����hyb���?��y������<uK[�5�P�`A~gY�Ln�2���e����PT�B����'�����w��7��[������N�U�c�Arf�ql�J��Ub]��3����jx!�g��7�r��G%D(9~*�������,P �2W# %9w��:'p�
�~u�S2�l���'S>���z,�T#��y@��3���) eA���1����*Pr{���^��Y%����b��;x�u>���'Ueo��"�"
\r��y����)n?�u���Q�S"�az�4����B�������� ��g�1��%�y7(B��$v��<]�����)I:�N
�$s�����t�W)eype#O��X�C�1�iz��������H�Ne��cJ
�%_�o��)�Qs��+��S�#�nZ|6C\��E�c]����YN�q���*�����W��F%&G�	��#�����U~�W��y����x����������������E�u*�-�y�����V��B�-��k���I���.��Y���T�)G���k�����=������A�4�H����)�}N$��|jx��-4����p��s�4�2��x0E����L=cAM	����r!&1�4I+Q����\U�G�u������DE�q '9i���q>2�	���J�D~�[�1��o(��J�g8�z�J���j^|��
��8�:$+5]�I/��@�
I�S+�8��>sk��R���,tri��JK��{T��4Q��l3���F(�V��,8O��_LG�;�B����H����+IW�R�j~�����M\���bA75��>k���v������y8�U{[tI�5�J��gj����w��38�]�N[M�r�n��$y�NWu���o�p�P�~iL7����lYs�Rr_���>#�US���K[$
v8����Y��S+[
�jo��uA|��S����~����+�����|�.��]0��p��@%;�-2���C~CJ�!��j`"��������S|
H�9�*������#��3�����T���]�p��E����Pb��v�'�����C����l��>[�aZP��;����{s*T.`qe���>���#��[,��Ds�tz6���t/�=L��Bw������<^�(��b��!S���������=4[X�����&�N-�������B����"���M-2�&�TMl��P�,�������GR����D�3�L.�Y*d�J_���?H����^�>|������9�#y�b�sc��]�lM��(���
�s��
G�V��S���p��u{>#EfM��"��+�)�����@%����,�
����!��N.�M��UP�LM�0�Wg����J�<M��$V�r5����np�S&h����ND��M%�p����j/p���d��|W^do�����b��������FwT��j�A�[���Z5e��Xe8�g������f )9� <�
.�������$^V�Ve�Z�l0�������.8�8wp<��GZ����#UH&H]k���{��w�����-hr_s3������6S���T�+�e��.���2����o���j�G|���=�6{�6C�6C�6�/]��W���>������_n�>S3��K��|utq����
��N6�L�����/������)	_@?�I�oS4�����4��V3X�������l~3@�D��xj3�=)�8,�����?�����MP���������M����o(��FnJ�~?�I$7�F�L<d���>�#��h#M:�����"�F�K�/��RL�#�6�e
��L!O��q�P���^l����H�_H��Z�^85w��j�3���;����/[S�zr����T]�[ ��w��*�����@�����d�
�������e�F��A�(K4�%;���U;g�����5��U ��(e��r�]�5O��-�h���);>�����2jqt�����������=�k��f!�z�-;���\���������y��!��";�����K
� ?�����k�v'��������{����as8
��vPG���1�����`���rTs��aw��Z�����y4	���R��_�5'��"�!��v�*�����RX���������v��� 0w�"v(�P?lF�x)�@��tdz��&���B#�`5���5B_+j����F~�B�
We�R6�+���b,���j5	H?�X�dG��VP������q����f�	�mpG�1���:d�G�!V
�F�:��;TV���b�\w���h"{���&X��U/�!�0o?����o�g0p;���c��g0i�
�x}��f������:�(�5��`���q��+�\;�9L�����^�kG9�_��@/����}���F���I=V���@���&���O'�OJV��3�YQj���|1c��m0�n�z-��5S�f�����E������p�����tsYtf���6~����8����X��c����q�n7�lm���������Y!�.�/k����Ow~<�*��������AsT�vZ5)O��;A��|f��c�tQ�s�x�z����$�J���?XE��(Zlau���������]���"�U��6K}D�e�_Q����i����
Pc������-��J}�9d�r_�U�����')���W�.��i,��T��HQ�l��|�t4��o���/���|������Ul�b��Q�RpWR��^G��+$�����7w����h�S��Vn��u������ej�6n����v�)`��,i��~YDs)��R������UlLu��p��������.8/����6�L�����Y����5���/�u:��|!��>��Aj�]�C*	w������������#����Y��\E��D ��|��2Y��	;dGUR{���&�P6D�2W;������^Aw[��^����N��z�����<v�dfl�8�������wZ�v���������u�a^�fL�@2��86y9g�����D�}\�q�&cts��.���1�����U�.�9���w�!	��r�X���@�S���5.��Ak1��v7��i;�2���,e��[����^f���717�Ct���Rc&EF�KzTdv�U	L�+XW�&>���o��}HX{�/�E�'�M����r�a�����8E:}����5���h�����z�]{D��2O�[(F4s�W���_%��Eas��\��T���$���
K����d�������\r��,��_>��<�Z�����y��N&{i�b
u>�;�����G���;�7�N����)�Q�]�Cv�;}�4�,���iH���N��I�#iu>z�A
h(Dv!+(��r�W�r��,�@#�4�����j1����P�z�nT
�I4}�N�I������?���tdB�LC����*���c,=�a7�F�j������gL4?���������v"��[L��5�����!���+���=U,�����������k�`�u �g��S�YQ�(i�%WG��&v
��g�<�0+�)
��Ba.�~�a
�����`aE
�������x4p�A|���r����O>X(���Ut�oY��E|i$tH�=�C�'�,���w"g+&d\3���_`�J���G�5d������f�dn;�	rO�������qd��#�H��qLy|*�H'����YL��/X|v��<>�<�z�Q`*8���t��0==fB�K����F%&��x�%�RmQg��M�������|A��K�4�����K��<�.(@meE.n	�T���)��KW5�R��4��8u�?9=?�="od��+/e]�3BG7����m���5[�9��o�+?U���;�	/8�OX�
[�,�3�f��WK�+f^P�����Bf~��X5���XqL�V��9AL��Ndvz,�
�,U����mVecr4/9Vrw����%�6A��]J�r�[�����X,���b7�W3��q���������e��Z%��|�y���Q��
�K���xlm��J���#�s�Q���'
������zxN�������;�.n_��r�\�|;���Zj���6�C�_�$r�y�7`0Wlk�L�dT�lC9<�	�O��������@8��A�����w�����a�4�F�����������MNH�0-����2�
�����"�"����y���h:
Gfz�����G��*�2o2}9������� p,��w�S�M��6�$�����E�)R��{Hjl_'-��qSkE-v��a���mj�H7f[)���L�M�-�vy��[63������=�s��p>*�7
�����zD�W���S=�y�(��#0�H.��Q�Kq5�����3�&����G��@�����(��({k:9��#�����^J�K:N����K�;!��r�N���U���������Qc-��>(#��%C�r���2L����0��l�p[���u��y�X��/��Pi������A��?��TL��[��������V��'O��1��b��T��N^����8�y�Fc�����h����kRK��������P����.�����St1V����F��~������YG�8��#�a|��
�L:b2]�T��t&p� ���'��$Kf��o�l(�'
k.�1[j�R1��7oR�Vx4`���?�����]��s;�3����73_�h�Q�i�f����og���E/���'+���������A�3f48��T&@�D�����h!�y�9C��D����7$��[`�����jr�s8+�n�e'!WC���&���*7����T���v������Hw���`�s�[��������'��7�8�G3���
?���Lr���q7^�]�C`|�2����IM�g0�����+)A��r���06�euH���G���~�*%rT���z��^(�
H��o���I��UkU�>�4%k�t�E�bDM'4��r
�J�l��e5�p\^��bsp]��	��sJ��*�	�Jn]gR�z�Z(�B,f�t��������,�.[��@��&���;��?W���*X�\���?i�.���"='�M1KY��)�� *�������@�IjM�k�s���)$����"0o��&��fC�/EvYV'��Lq�"|R��\����e��MH]K�����|t@oCJYH2%���R�T&�U�D�	�U�A�U���G� w7S�����P�&�.9V7� ��[dY��,���H�A�*"�K�����D�}�]��}�U�����W��~��au�s�Sb1��-	��a��8��A5��P9�N��QY8rr��!_5cv�>@�\�fqH�U8
�
��<���dki� {@ju%�����"�m���2�L����T^�wT<�Z�d�*y'�=P���[.~$)5�B�5���L^lJ�����n�O����6��b6Z
U�������{N&��
����4�����'�^k�p;8�����
�����������E������(���
�����x#��}����	�~pm���No�K�rra�-2����>�k72�}�������K0�)Ze�W�sH�t�]A���B���2*��cZ1�dm` �����)��gq
�X�OX����Am7�$%����������&��K�l���2k��F��T�����Dp
���Q�T��Na8��������c�G�'s:�Bd��
�O�r���E�xR��;/_I�5U��Lf�Npc����x.�o0\����*ot7�sz��N��d@�������JZ���PN���.�[O�6��WS���A�?�+���(��Q�&����S�s��'�[�,P����3�J��&�&�p��`��C�����e\�&��:��a�G8\N�1 �Ni�`qzq�����8��%�u[kZ�TAs��EI����+"�,#��%��5��~�����#�	�Q	v�;����1�uu|--�e����PG
@�Z�,��"Z�k�w�%s�h�U-�%�� �����9�����|�N��?f���r�)0�lH�{���4$�`b97��Mm�M�:���������T_��4L %vl�N2���	zM���y��<f!��K�����.����<{<�9��k}	k
��xx�5�[t�$���=�t.��7@.I)�����<���G�`m���1�,+A�RW�s��OR�hTI��M��G�z�A��x?��D��X?d��u|��H?�iF�#L
��$����TY���c�'��q����e�/&�����qT
V�F)�����\�&��'�j���z�)���V,,��hvw(�V�p-F��~�hQ��Q�V���2�K�KF7t[��r3�k�#������Z�(�x
�0�����������&S��:��k�'�����G�E��������}%>6��uv�BZ�q�=V��Cuv�E�����|�`H���Ve/s�������I��43�[N3��B��q{Q���UuX�4�+�����r,*!��S��z����JkR�h�a$~�g .!�5�������X�0�%��i�m�����������RI�j0�b��!��,�-�����9r �~g�@�Jl��Xe������c-�����I,����O�9zs��
Q�;�f������
��>���P����GZk��
eFDg=���������4H��Aj�������;�;3�n4�Q����������Ls@�[��������M���;�{�~����*tJ�On9�s������.D�1�d4��B�C�:f��r�*��P:aw�l�M/��@y���M��b&�����c�J$��*�*Bg���J)�N����]�������nN)�yW|���or����x����d��b�G�E��u�����Q@u^B��1�:�7 J��$�+HN�E�9��$>::}s���r�������,����6
��T?���2j+q��~��y��@r�]<��)�'c��J4�P\�!�,��&lG���s��:���
�����`�`�������0o/tG2|������� ��R��ZV)���&�9���{�c��B���'�R��3i�t���F�8�R-��$�
E.b}q�
�T����>�-�&'5��������#v{j�9u��+/�>�!xw)n+�XW@����o06�Rc&�r�e]�N6�u������D1���~_�1b�X."�m��_8����Fs(9��j��UKOo��=k:$��r����{45�4*YA��X�<�|Rm�O����������AU��qE.�jY���z�����&j�$�L����:��������p��2�a�J5�@���f�&,�*[r6&�3O�]�>s�T&���!���<�h|5�_G�����4ag��JT4�����f�z4F���:f���{�K�9L G��5�Y���r��T!^����D&� C��la�$?��!4y�O(���M�_�t��h+������6�-���Lz~e�`*��p��������@g
�\V�z�����`_����5Fh�U������� K��+Oj�5
e��lS������C�F`�B-��,�\S��������l�b��`�y ����k�M�s��*^����������u9������J[	w
���NL8�bD�N��O�{a��X�l`_���e�� �T]�R�5�)@|�����]���;�V���
��`�$����6�ek���h���_�f�>�x�v�wg3�yr�[$@=���`�tlO}���An�����	��y�+�'�	F�p�|������9��s�F�!E�#lvf.��_����G��N�}���]��}�7���e�xo'�p�MJ����
���6��}3���I-��0
��j�|�J+�fb��I8�1��El\���1bo�����Z�v�A�k���l���J��bnqDgk��M���
GTh��B���F��C5���A
R������������7�����~������p/�3l�]�L����<�G��Q(�C�{���/��b&Bp�G�y��� ��@���f����_�k�t3	y������z�W�T�V��o���E��	��d^O$�L���1�y6����QJ���������@�1�����H��
R���>��m@W=;>D����I0������z�6���j����{�A�62w����N��!$7r�R�EJ]p���C�	{��J�T5+(�q�����������E7Ma�����db�n�5/�NZ6�
��v'h
%���A��z�����K��)���$���Ryn3��p����r�������T9y�����]���|���%o�	��C!W
R7��OC�
�";SZ�o����3�vz�v�{�ZKn���d�m�@��?��{�8�����)q�������u�}���n��p���[$�u[8~wtyh��X�G\�������Z2�7m����x��Z���q0T�<$�����{�r�d��_���[�QY�Q;�����o�6��`��A
�
�yCV�W\9}�����bx�G����T�u[U[5���W���E_-!l���u�g��>�k�g�~�^��~�5�[���
���1���(]1	g��7���h���'�>�:��gmKr���������K�V�����n-^��������lP�d�(J7yq���W!Fg��:VU~�M*?��
�x�=3���i8�\�9���m*����_�H���:W��`s-�J�G�Zg�����.������e���i6b�^�@��c+���3��7�M�>��VV�bb$$U*K�&W�E���6l�
��(�?��� �]���M�����w������_��B9&)Y6j2Hq�������4��$k[�F=7��Q+�[^��������Nv3��d7�0Pv��
e��-8����1����CsL9{�[pO��=4'���qU��<8��3��������W^���s�#�i��sg9���8��+��rmy��w��rv��������8��|./�9���1s������4������N�h{l��v	V7��a�5�i5Z�j�>�
a�k��N]�
G�j��h`mjk�x
:U��=x+eu���'�2��ZJ�r0r��DY<��]L����{�����������BEt�����@{���ux
�B�;�0����EXy�o��v�C%�1�&:��?�s��l���-�y��*��,�"�U2�c����*�Hy�f�����Ulc�A/��G��N����[������=��T��g{'�(leS�S`����o��gB�VP����<���
cX���|����c�����g����G���l-���T.���c,���b5hY��S���V��/��(�B���������W��$�����`�z���8�l� p�W�^^jX�%�t�������?�S�8��*b���.���u�y�k�J�l���@<���	�f����3����M�8��l�^��(���9���Q����^�;������lMu��8Y�������-��!��������������=������jH��\^������"���y�@.�Ie�^�_!���c*;�������F�-Y��FO.H����d��G�sR(��,n���%\x�)��O�I�t!�&��������2m���:)|}�I!+�zk����H�/&�"'��Us|m��.���?
t�f��������2���Y�>�	#�pWs��:yA~�������p��a�$a�F�Q�����c��YG�������j�Z�@��tTD�`qW�ae����K�gyW��{�QC�(���N��5���So���6��T�����Z�Yo5�I�����?+8��+7��b�SN�s��`���O��5bW����6�i��@.H����v��y�5��n3{�X���\�w����
��E�����<�?�h������k�`���p4�J��(�X��+���8�lBQo=�5�5�b��n�������3���������}���$h�
�����-wG#F�Z-�������6C�E��~
���	of�A�L�F-N��"��(4������.�� �t
�ts�;���B�,Bd���qK��C�W������q5]J����,���X\F�	�[�F���_E������%Q���G�P�l���g���
0�
`�@�F���-����+[�l��T�����
�n����7�j{��j�F��������(���*�,WUL��C�7���z���+��-?��p3j��x��n�%S�x���G_2�V�j��j��-Ok���������WG�o
y���S�KX�~�cX�t;��
r r�&N�������`�����c
%�,_�D�y��
�8�C��-,)n9u�uP�5C�7�O%[!'EgF3����4�[����Yb���(����o���Y0-��A\���2OX����-q�!����%M�KY<6
mh���_U<^�/�}4IC]n������yv���X��|$�f8S+������A���J�U���;��7;�g�N�j���z�%���?����j�wz���a����L��$�;ju#F�������V�f�w����b��*i��XJ�
x�����`9���*X-A�'EaDd�ruk���[H��[�_�jLo����M��=���f������g�������S�`����P�^%��\Tu�p���&��E�������K�9��-S~*~�^�����(�m�S5�Y���?�z�p]�2�+DH����Z��p��<.�A0	�C�3������/��h2Z�c��4������f�_�"���}v�����4)�4TD�C���JI����?�����"|v�eO{��&�J����L��kw�{1J�.�������2���������Z1��"���?��
�9��B�g�8?����	�>��N(���9��A�����S1XE�Qi���m��:�!j�&�|lp
�|[
eo0�]g������N���/d���6eX����9�#�j���[�^��AF��pW�/rNq��#h�~��G@�WA8Z�?�Z-	��z��&���9{�b�l�1�*�N��T\��Mm�����R�j��G�Y���YJ\�%�*a[���LIo�����F}��z��T���v�n�Sf��S���"�����_����G�j����|C5+��]�V�)V��X#��e
��0fk.���Y��!�����<�I�\���v��p���b�Y����v�\o����3�&��bw�+���W�������a6G��<�-��
X������4���gj��W���w����%��%AJr�K�}�ZP����6�0$�:<�=�+z��
����?)��r8/}�U���J�S��f�E+������sm���F�Ukt���N������������N����0�����=�	k��N6wZ�0h����^����[MY��~V��v���u���;��s�Y�L���E��Z,��C5����InK
-�����2�$�]�1H�9��u4��A<�6��jb���:����l��hY��`@i��(C~e�1��������jF�$@���s�u�Z�,p�V��lqj%��y���t��h���)3�?`E���d����{���(
�|������"�O&�3��+ �(.�lm<�����OJ�'Y�e(�p<�����N�+U�L2� �
�Y����^W�l�4��RA�g+;*�Z�|60��LL�I��]��(P��{1�-��+*T&����������X��P��M���x���'�"���;�G���h��d��?G�����/��Z�n�����r}�p"*�8&�iP8a�:$�R�V�����e-�#Fyx ��VX�x��]�����G���6�ibe�d�V�$^�GT����o�Of�z+�i��)�U�G����hB���zIy6��K{����8S3@�p�"7���
� Jv*�SnjG8I�����+�P��t$9z�9��T�vDr�8������W��!�s^�\���������hx�y��7�N��6���(����j�tr>,�T]~^Z�=�y���g���{wn�Ob>$����-�{7r��^����{����=���D^��q5�����	S�/ o|������z�{�������H�al�H�2���[T+���}��P�G���k�����k�V������|3o�r���c�����(��V/h�;��N�Z�i6$c�������|�Y]��
4�����[
F���x�(�(+��\��T�W��Jv�-��j�z����9v���. -���3���xW������J�@�t�%;�;�F.#�i���}�����C��b����ku��V���V=�v��`��u*�E���:��N	i��
�L]'��0gYn�|������Dc��8��Gkwh�:�F6~��05�U�e?
&���QU��|
���.�nB��$�����,'��i}~y�Zu4UHA��J���Z���&������	���f��:]3 �.�O<��ra��t}Y�$��i��d���Y;���_�P*����n���tV��gN��oC3B|"��jA~�S5�Q��:nC����*��1N%KC3��
}��s��0{��v��<%�m�.�E��VX�3�����X~~�kY�_���y8y-�����
�(T����F.`,��BhqQ���������,\�V���w�y�~�@��&~��F-^�5�lM&��2#x����O�yJ��HV�q�t��x���������������y_~w��+��M|Uz�4��v��G[��bf~7����NJY��A�,C���'s�|w���f@4��+/����XJ�0r�,z��X����]��xN�-!kJl���I��Gr���@�����r��N�~�_��=?���u�W8����^B��!�Tr���Yry���s�
^�T�H��������R�e�~�~%h��N��jb��R�^a_�|�-��yp7�#�������iu�]��E}+��9��e�M���t?��Sc�/t��s�D�:����? ��{��Ma+3����[���:�gf\f�f�~bk>jr}H0j[
������v�b��tG��^�W��4��N�Vk��p���\[�z�G��f�Lv����
x�-��#=����Z��@�����$cz��M��sj�5���!�&��DE��5�v(K����K�:m�{,�������6
x
[��x����{'�0�Y��6��1��U�wE}4pz�(�|�����D��_U�r��QS���Nb��Y
X�0rp�z%,).���P�����2���������W�R�J�J��W*T�"�S_�S%�n�W%���Q�/n����\�V�^��-�T>D��](�6���D;\������:�4p���>eUE�����4oh9K�0	�
\�)��.�7���Su\.���+��2e�.]�	�����la|��+C��$���,t�Q4
�PJ��W��F��|_cv]��5#O\g:���yB���`i���P�I��A���Yu�����<W<"h�6�d��~����Q�XIe�"��6�b)
�d�O��C��|�l��T�����4�B~�iF
�ez�aC�`x�j.��������E�l��=��<B���	3n>�Q���������D,�S��X%���gT��TPN��Dt1�$9�����=�(��~N!���v���6��b��	������^��|�$���<�ta�.m�����)%���9��'��5p�<����S���U����z���O�d�8�qD;RD�[2i�i�-�����"������"����>H��9����8 �^��7��*�5�(Z���6^�Z
d��C"�������kM[��u��ks��X�,��`��x�b\�[���Oar�]\�����3#����.��l�j��
!��B�_������y���D�/��ik]��uY9a' ����I{3a��_�>��,�B4���`n4��%Ud�>,���3��5������N>j�J�'�����������H)N~4��e���M�|s���e=[�`i�=�-������H��*�����P=h/kc�1�1���<��� ��d��J%s�yV9����X)@f
��yz�c����;�(4]��������������B�dH{�������"��z��p*qB7�}�
�k��s��9�k��1���ay�W=84��u��Q�?�'��B����>��G"7������:�xz�;?F^�<�M�����za
;�{�c���TPI�����lj�����l��{{��J<1����'�f�������'��@�8������q��o��@�,���}^/�L4j�K��2����f�R]��P�~��!�S�������^S�"�+_��a�Tg�����J)��~m9uR6/,���t��	��=�c)�����M�����������zkQ(t��WO���1�z��4����.����U�Z:��'�k��4���e^�+�������u�Y&�������}&��[���u����ue��������k���0�����dzMK7;�P�f��.���!��_w��'��[�����}�7�x��_3'����_G�3��G<9b����=�_�1�)w��y���y�e�u�����W2����`^B�\��9o<�8���I*ax�����7�NQ}(,�-�R�N��T$��:���{���^K.�N��`���eH��K)J�"I1=_KS<���D%��L��Y�,�2��y��.-�-~1���&�W�v���o��
i@��++���/#T~2V��f�6^�*�2�$���E������I�"P���4MJ�c�+��xK$>�Y���46�d`�����$}
!��[��^��"���)��e������$����ul�Za��jI���~��F�f�Mv�<��
	�?��y�!���F����8�XW�K���>�^��J_�����\]��
\��x�z�%)�h��E�
�������m����Ud����Ir��p�O��k*�;a������,���g�V�f��*�4��/�o�OB$XZ.nL�@d(�
���skkM�V�����g�w~�N�J���r,������������6AOu�6S@�Y���w@[
I����QE_��`�:���j��+��`�+�c��`Q�s��.ogT�������dvu��9^�������"U��bVv�����>����d�8�[=re���raJ�A9�*�R! :�o������UR���������G��F��3K���w:8�f�+�m������]�a
��LuH��vrI!���z����,U"�1`K*�E$/�������7)�L�
##$�� P�j!~PMud����S��)�v��!�Q�	*
���L&�`�S�e�wc�(��f�������V11�@l�N/R���:��g��u_"�q~a�/j-4�.��%�����T]�%�t�[re��	�;��1kT� �(�����{��Up6��h��3�
Pr�g����$�N���0BY�@���f����J�z�$_GS�1����
��f�����:�H(��g�koh�V��@2
�Wp��K���sGNZ0�cN�c5��N��s!n�y�C��U�������q���R�\D����2~�L�������A��p��hy�h������	�6��^����c��Q�>(QG�����]��@E���l	�]�d�7��	�d=Y�)�y�c(����������G;u*=��hK������O
Y}���5�X����A9]�f�v�c�)pi$�%�T��ch:���><���v<���E:�tdv����I�:/)7���R8�C#)�'��4Z���u9�l��T�����|'���_�}U��&����Hn��/k2������U2RD���M��M���y����)��������,��E�`���&��-m0b�v�-�<B�o8��X����,�auf�/P%N���"�S�\�����$�@H�k,��%�&=����~�����^>����w�a�|��80������_��%��������k���'t������1g�7���yKw��[�fg�&7`�4��������������>g����X*����}!{�$4�������%����|����hO�Xr6��.�9aY�(;gqy�����%f�����P�����;n�K�n���*|�n�t����!z�7���P��,_��$����il��m�I&�~i����?�����;��M��s��k�s��X<������K��}-����-.�]n��?���}|^�e�KY�������%�4�0{V���0T2�f�`�C�`
�F>O_mA,���x���,ui���4�A
�������������9�
�4Z�	���^�4���c�R��Z���W+�Tn{��2'���YFk��oE��HM�a����������
�>=�������������dJ��9����c�d+���_v^v,9GjQ�S���9t�������Y��f�4&_� ��%)	�$- ����J:xO:D�A*T��(���/���bS��|LbdN�9:��BE\r*3Kv��p�#�l��&�*nKf&��((�A �2�D��z����~sJf�{DP����<L�in��idb�Q�?�@�jD\+-X!� ��(A��9[-��K2u9��C��	[O�f���g��ZM����,B������?����&Z��'(�WNr*lG�2��n�������{J*�t��em��q�PA Y-x3��AG�G�
����@v�I+&��T�������&\�NI�1�0���,��T"����tr��%�B	��$�T3��VL2I]��$�7 5�3�Pb80���1�[H�6v{��
��9�k���R�$�e����������`���<Pf�fS��0 <Z}+���s����X�9�&w��|�VI����[��=�z
�,b���h�����ek��sc��l��Xy�2��]�������W`mc�[� �Q��Z��><����?C@%a�(���5�V�1���������h�x9J\g�fH���r��Bn��4�/����/��G�G�E.�:-*p3c��1z�c�)z8���}�l��V�G����Lk���!^
����0��!)�o�a�~�3H�;^Qr$G��k�Y����Hk���{���|��E|��SH��s��8����"k���'��:��c���nG�U�~M<�`::��;]��R�6�F���~���Y�<���F���p:g��#�����
n/Pq=/jx�����Ob��}F*p M���D)kpF�0���c�z���@�Mk��rG��*��S��P���a,��������k��o�09�����Q�xc�@�p#���9�T�9����hFY%S�h��� �&wU~�2�������/^������F��^ekpY�_x������I�?���|�?�^6]0uP�����W��������D'���h]s�K�=�Py��x^����A��W
�	mV����$(sT��I
���QxJjRw6`��Lx�j�"A{�a�nUE�T�2o��/��D��"�W��$)hAx2Jx�R�������#�}��?������\�� �3�YV�B�q=�5�c����"On0�^TR��v�V
����<E��aJ�Z���5��.�w�g�ky�}u�����[!���R
�s�����E���h�����bGjF���X3�����-��{����`F����������������������I��{�LZ���`Rpz�p>��s��[����BJ�a�z��^B�a�,*�8�T{)�a�A��ce:�-/�$���*)a�{C��e'>�+\	B MXt��.-#��������O���1`c�@�cd����q�tr���:P��c�����|q�v���B��#q9%��Y�YRC*��T�����,<�������r��[m����])4�,�hy��czC
��U�[�:���	Q��d#'4�qb����kI}){mg�p�d3N�{�<�aQ�9I��zI�w/�i<��'1G>��������'^Z����1��D�:9��
���:�����������M3����Sc�]�Yo-
k�a�s��i�
��D��1Q5�,�I��`���fk�[��������)�V�rn���leM3G�lN?�T"��_W|K���s��==��!bG�E\��
��f���-8�2 A�e�!�jH��F��\5�y�YR�\$�&��y{����jnv���:�U�j��yU�h����Q�]��3�K[Y��%%�gzo�Y���G����]���l��%��G�q���X���_���d���#�h��"�nrbY��F}��a9�k����iu$h���&T�M�-M����=`���
V��j���)���a��:���������r(��8$����v��n<$�4!1Z����n��O���1�3���35!����C�� IY��q��q��e�	B���<���x!���m�J{�������O*�?�h_��X}��m
}��d��e{PM)G���z�[������r�K8���R���J�36e,f�>�q�Iz��Sp�?���9���	[������%�9o���E��'@A6F���gJ��q�&�C��AG�f����������A<�� ���q������dYpRc%�hk����E{����Ef�����5#-�D���X����%���_qE=^0�/e��R�WT\D��MV�t���8(����_\���RuPh2Y���qj*F�t�`� �P,J����k�n��P��L�'���qOqU�[v%��M��0*5p!�CHu�S�ia{��ft�������;��������n���B.I�~�C���j�H�m �	&e�hr>t��>�U���2.
S�k��j�V Iz�2��D�sXX��ynW|����
.�oo�Ny��2���,SH��[��iE��$�V�<[K`�A��N��H;(	���S��F�0�D��i�����Z�GN���q8�L�RF��'�MQ1���T��
tF���~�`&�����:����"��n�eh��3�4"`*���}�4��*�A�8��|$���irr�	���E�u/��������i�Y���;��j�w@�$��		b�N�l7s"xl]y&gpf"�8�2�Q��F�p���#yr����Bszi��f�[��������hH��q�|��=	_V�P�av�Y�X��pYzC+����>�����1����n$�sU/M�d�h��H�_H�~�����	���jE���#��eE��l�q5x�vb�wb7-'hR��xvptp,~� �w'h~��?����Y�^����]6D�r}���l��Vyd��,R�9�9��c�Xn'J���y��|RV�����������,��91[���{�~�U|w���d)�	 ��%�zB[7��mY���7��M��S���������m�����z���{�fE�O~�6�����W_��f��e4m[�h�-XH���+Iy:�v)= ���M������%-�^����WQjI�n}��ZC�hQ��Q���`����,�[
���*LZ�B��l� N����1�����?��Z�A�9� ��u�__v�D:d����b�j#�:��SQ5�������"�����a�g.@�ys2�*��z��^t�X��e���#��������>=?��������`��#t��ZA�TTK(s0����:-*S>_�$��V���������pk��C�'"�NOj�a�;qc��,h��������b�����������?���p���(LVXT����|>���+���@����V��������Y�v���������d��N�fQ��=�����^Q�c���
�Jdm�)����w/.��'(��xF�#v��8��t^{rM��R���]g��4^��s~o
x"��\
� ,!x����Y���� �Z�h�NB����dQ���tRG7���&�W���-{�����t�%(/���}T�/��P�UG�6���7��5C���f*m�`b`
\��`���5��2�P�=@���S�y~d����ijQ3��RS!d������w�M�1d�X�B`}��<�X5�p��#�����\��ln�X�yZ�<������Qnq`1� �1OH{N�O��$�$���"I�N�Q�E���;�1����kW��>���h��Y�S4$�9�1���:�R�M���5���Yn)�r�.@��%�Q�q�=����4.w'���bm�2ZH����)h����KO��B���txr�*<�
�!Q��0�i���� �X �O�}
��(����JL����R�|��Tg����9%m$���'���8'�V8��M��L2��Va�`}*o30�*%�������E�!�o�6��]��&!�W���3�0x�? �F��p2��xY�W]���2`��EeVw����e��8��0�{�_	����:8�M�Z�)*�2=I�Rm���)7.��CiP�c�$vD�6�v���s/ceE���/�f77��L���+��o%`��;��W��:�)W�6����2�-��q�#���f
N���p�������v&M���18n����8C/!��	1��,%��+�1^d��r�jg��.R�)���-�����������=%c>9~�`K��B��Gj5�Jv�����i8\����wf�0�����1�����>>$�P[��T����>�y8�<�N��WnGn���2�M�������&*I����i�
vO��sy1��m�����`2VK��zu�A�0����e�N8I��
��O��������,I�_���rO��!�0�{0�.��2������'�LA^KJ�R2�����7�����v����~��<'�,q�������s`��*K����JYS�
�����������9�%��)��[�`�G]���``n�{I;�t9����Q�L�����soW8��s>�F���U���qDiq�
�pB�#�
�I��W���X�m�u����&����<��N~At��V�����C�|]�H#��4�A8���d���%y ���F���l��J�u��y��s���U�2`?X����
D � �A��s>����������O��%!�����k����H��-����pxCl!�����7
2KE�E��&W_/���F�Y"��B��_(�2�������d������a�`�ce��X��L�+x'�_�8�qO���Z�?��z��(}�����R��)�;_�Q�����"���w�+k�b��f����rW�+-@/�V�^����~u�Wf����0X�U�d�L� a�V5`ar[O\�M�R6�'��4����i���R�����J4 q��x�����LBC9�t�����I
���?�[���T\�i
���p����������|W���F}r�i%�#`��WI����Q�-f)������k����;c5J�(Y��pb|���'#�0����R���^�)�a��^k�*�]d���8�������,�%��z�V�"��O!���?�����4M��>|��b�(�"
�>%���0�k���G����0���]�z�t�Zi����jG�9ahv|Z�*+D/?.�4o�e��h`�]�y�D��jYhTPi�t<����8/^wAb�Afc�T��R���yd�t�T�N��Q��t>;�p��2��Hm�|`�M���^������yQ���?.X���xm�:���L���Cv�(����}�$m����Ah����li��e�f���YB9�Dg��o|������w�JXj�]�*yQ��T�c��
�q�7���(aM��K%tp*O0]��N��_�&��y��|��R��8��\u9);��6Cb)k������D���"�IL.9$|���A��v�g"���K����a�����zg'�����0S=M�����( ������cYt��f������������O�Oa1����xwu��VN���4Jn���n|	0�6n�~�z@t���l�I��.���zf�J��a�3������>���j����<��v}� �nS|a��=������Ok��Z�O��ef����L�8��.�)|3�{�p�%f�M~�?As+2���bPG;�0���`H�<���o�[@1A���I�m2���(�}jo����-��;�@��R���K>QDN��"���zYFE�&�f�2���^ ^��X������d1S�B=�&����Kx���Yz��v*�y��f!q�X�������-a�C�j516����&���u��5E�%Y�H*�����Z;BJF�Y���S�����G#y_'��,<�������'q�����p�1\��qw�?|i��-[��eN�z�a6�an��\.1�����]�|'�bYn�H���N��/�:�{1��L�U�����r��K�����r��>\�-��[�_A?R���@���� i�E*]����v�����,���x��RFnX�0��N"��`���������!7��.\��U��0e80U�R<���N�HW�1��3+�h�q�ZwWPt_g)�L���E_���4N��2
k
�����}\�[�	{7���������+/������mK��5o��%^_����B������m�����A�_����^]�tt�9�$�#����Q`�6bm
���+�� z*�1�����0?����	�0� :���������I<���Q4~��4sLS����`��W�Yt�����]��t
$lK�~����������R�	��L�<3G������r�������~hX���fEy��xl%UY��r�%qzy,'�)�!����s�]i~(,��wB�3M/�x��*��jz�!������U<�H/=o{��<'��wD��If���
w��������c&��g��Wn�������5w�zP���j� l��Z��{�Z��lnloo�������u�����m�j����mz�'����?I���M4
��	.^���
.^��W�����]c�;��7���
�M	��R�R��J��-C��b���E�j�rp��Z��z������Zm�w�aP�w��+���Z��
�}��]i{[�cW�)9Bni��4�2]���L�B&�h���YLK^)��r�-�3����j�%���D*������J. 9&����y_6�
n��m���a���jg'3�%�Q�md����z�1h�[{�j�������]+��|h���o��U���p���nM��B�PS%�0M��������v�	��-\�V~ap-���]�)���D_��0vD��RF������3u��>a���n����z����+6�X�b ���M���5��y���h�������~D8��yw�}����%	g�t�pER�����3�'o��Y���(������#�Zp�w��n��Z��Z�?����j��p�T��V���.��w�o���*�������w���w��^�V���'w�:o�2o��Q������A���Q��T�'|x�C��J�=�����Lk���.N��l�	�$<H���z��+��t<����<S��������s���.P��U�
~���b�����&�}T�.�w�]w��; &iZ��s�R&�p3��<��I������H����G$��b������G#������Oo�g�HiOO���������?���x����)4��9o43�"�O���������s�����&��������r�rY���e���9aR^���9,����3�`���n"�MW{
-����/��?]]�_��&Z��1�H*���p�3�2�$�����z$�FGY�i�T�{�����WqEV�R�{~V�HT�|�.�����:�
�VX;�A�Zm�����AP�
V�c���>H��&�Qo�#���9����D��n ���� �[O����?H�<*Jw���Y�����n�<�p������!�������T���=���(rrxA���
':�Id���xA=������v<��>z���:{j�����2��z'������F�[o5{���~�4�v3�k�6[����9�Z�Y�WZ�h��������t��,�x5~�K=ZJ��[�K�)�!��:��m�/�����d{HU]�������^���V���<U�!�sE����^o�Q8�H'�O��8b+��h�A��c~z��M�'d�W�/V�/�/�B����7��~Xk��9���A����J��a-�/���	2���k�0	��(��	�X+��@I?���G)��X]P+��bJ�h^��Re����T9I	S]:�'�!���>���n�bo�H�Vg�G�%�����8#���-�&X?l��<��$��W���z�z�Z��C;A�ig<���qE}��V�)�����A���r�W�~��I����3I0�
����S�%4#���x�0�n�f^������� ��V0�q�o=�^x'��pO���f�'��%���uvP���R��2���!����9R�%���p��q�c����S)k4I5`�m�>�T�R�
� 0��,�	EY��|�J��'��"$���Y?�������?��b����E
�4���nQl$��������g�W7��T����pG����X"���kW�B�fN����.��N�2�VlF�a��p
�S3�(y;�l����K�y:�h��f�����!��^���<�u]�Wv������T�����R�B�i�D`%6-W,�5�M "�xn�4O9T�5g9�?�v~Mk�������yK�%s`����"=m2���#B��+�H�k�{C����vh1���Mj��
��|���q@L4BR-�d>b�������cCR���E)���R��lT�,t���������R�@*�M�zfu�[�
�����){�Q����j��P~�yN���=��B����
�=zN����I��\e�WWQ� ��%D�.����BhT�K��5����&�g���.
!��4���S����Ec��)���{��r������f��^����)�)��)y}�y�4�K���'E���U���We���mz��q "mz6�1@�@�#5Q��r�x���R�����F�����������+w���k���5VBJ5P]�I%��i>�;��zX�.�_F2�������Y�b��C�m�����[���3)�wc�L��.���VQ������=��s���t� )��#����}��\��af���\*<�����V���*�h/���D_��b9�U$)&^�}J"���4��P2xX�[�"���`�}E�!���rtX��_�S�����U������W$1U�
_�Ee������vQ����g��{�t���;;\U���������'����K9b�9f>�0�>��q@&u�w^�%j��
G�Uc��3���j�S���Q#CP0|+t������f�rD�����	�5Z��V]����Y�[R�8Ql"ng����<�:�#w�������vb���
R�'������_��Q1�����P��m���&f;�)�Sns��
1Cn�%QCn���rA�"���y�cRr�!��k����0�uV#���"��z��>��o�������U&m��b��CV�'����j,`���xg�g������y��C8sF�����kU��b�?�t�./���U�{�����m��s���^�7�c{����u4z�����PR�q��a����*
3�,,0�����K���B��S�D��Gm6ZLU��b*��%��D�cq�L��$3Lq�D���)e�MJ�\�N����M,+W����!}[����X��>JX��J��5�`���i&��r$���6����-�E�����-���8����<���X�Ri��[m�9<ii���l��FQ(�4�*U67�V���H,&>r����g�V&>k�����.X�F��Qt�����:���?��B%R�t������������2z���X�s�h��{��1�������2p�{��s�`�2�b���+@���n������?�7�F�4��
lY��M��]r`���
a��7�:f�x2K�������~�M�_��%[�l��\@��02�j@��L����M{�
�)7���gm/�Qv���r��15�
;5�W�A������A�'�@��a�
9�h�4��/W��0������f�[k���0�0YtI5 ��=ra��9�'�Bw:��]���������(:S4�xE�b}�����k��-F�{?u�$�,?4l�9\���������qJ�:|O6�s��z���6i��+��!)k�#�����gm{��h��{j0aC����n�Z���_���?��������N�Voo�6�K�@n���?GM6)������VO�v��o���{�s�����]�k�Zu��hA�Zkw��������j����
C���xA;h6,x������K�����6�A�[��vx�7v���8���{��_�{��`P��x�����y��3���H��`�y7w�5�����U����C6&���^���'�,|���F����{��^����z����F��q=����y�<�9�	^���~�E=E=E�J���7N�-��h&"X|�}��{�VA������f�rf�K��L)���L>f�q4��NSK@���jF��Hh��Wq+A�����	\��$������l�<�G�j���v��:�d�c�d�����/�~��o�O���i�W������n�2�����O?7����������hX����r�u���Djr;�p�K��x^��mY�RF1�	1��N�����P�/GVT>������5�1Q�9TKv���[�.�R8�^�+�X���f�o��r����z�k����X
�0�R����f�����#�r|q���__]�yOvP*��z�3�w�9.C��|z��?������z��w��F�N�z����D����������xt;�39'E�%����p0�{���#:'���x8�D����L7�1���������<��?65Z}��y��R�|�*�����_W|����/�d?e}]�|��x�����^����=�1sa���]2����?�:�B�d���_�Y7���������E��
�!,������c��lz�@wq�.2T���)�)�E6�8o���l���rq�b����K�(p�h���,>�JX�q�;m�q��m0���qB!��?�E3����l��@��%Y������3
P\�U��YTt�'�A�7�+��;�� H�j��������R�KA����.d1�b�O��jf�@��e'x�O@n�aQ.h���g��3*���O���9�c���Nuj9�I��9l�
%�r�B����`T��AOXGT@{Lq*r`8R�W����^H�Ham�D*�T����P��>�iJ��)�K�)�t[i��{q��1���]i�R�.Q�m����S��&�
�E��/�4_�[�Z9�v�Z�Qk������A�����.�4��7������?(�O��0A�6�W���?�s��yk�����klZ�?k4�'��
�3��k�au�������z�/�'��9�)h+�|)���6�����o+�6��r�}1F���q}R�l����9P�V��+�h�w�HU�Ns2���{�x�}��^<��n�����������4)z\5g��,����5\���?0����z�Q�/9��}@W}�V�4j���[�:0��/o$�5�n���"y�W)\�z���-�L�)�?�; p��Fa�Z'���gf��5�,��W���2��%�#l�w_CL�s�����a�_w�|=%r��
SB��0$��D��=�"lX���
��_a�+
�{ayZ~G��}���C���}��_w�WX��V{�c{��U����,i��=<�_�r^���[4V0�	��,��=����@���6_w�v�o�	�HP�%eL�������k�S����:W�Y����p��9}ua0�Fw�s��W|A��W��y�9p}p��~�{�pYQ7ap��=��0y���D�������#��8�������G���W'a��F�s�������e�|�fF�25 ����t5&s��<?ON��G�0���N����� `���vp&�d�V���#����C�C��#J%V��<���B�=���t|"a|#P�!�������b)��B=�~���Z���`7F4&��SI��yH0K��.�}��iHx�w�����Dtv��mO��7�������Io��4p���2�h�6~�M���{�a�|2S�������Oj�4��9�`�Q��g�>Gk���Jh��(am�����T�����������>�\g&(@��%��X����D��%���F�y�G�gk�d�8)�8�e��t�Y�'T<�q�##���\ic#}4G��'�m���E�6U+�[�si�D��%I����Ew��������O����<>U��������'h����_�VC�A,E��8�wZ��1�����KN��V���R9���S�"P�����)2���
���z�R�r��p2�����V#Q�-����D���us���W�N9���{��=��hU��A����o��/��9PrTr�{��9���	�vaCS��y/��a�=�����F�Qz��
,�?M����h�S�A��G�y���X�a�h�Q�3[ef[F_���������/]
��$SzG��c���(���=;8���R7������<�K}��)��`:4����%���a�YEW"�w�����+�	--="*)8��/�*�e���5�V[c�s-j���w��h�)
0M�d�#��������^�b���@eS���!�DZ�G]8�x�Q1Q50���\Sl6�4EU�X'�s�P/c�0��1��D���[��#�����h�|�Oi`+q������I>��$"�q�L���h�D�pB/�%����|��D��J��?��e"5��P���Q��0�0F�y
s�>XyE���F����y��o��08(��W4*r����g���BWB�c�%�-���{4@��d�U���C�\���� B���W������!�V�����2�3�Q�P��� [�8scK��21[���(I7
����zcKC�	`XO�����*O��X�k�|�ul�%�o�R�ZFt���Z��_���RX#!���d�@���<y���<|���U\���a���������P�6���i��DE�U#��-BaB���tR0��Ch%�	���:�S$��id���y����\���]C����"�puaq����gH�gC�0� �
C�h�*���K�[�M���ls���	���.g������������gTF�lc��]�8��E9g��l�}�� ����(
h���'�)%-�4��M�cT�T�rB/�����	=vP]h�T�'�(���*d�I��
�ZhU���^���@j/�����Y���N��a�G��H�g �����_�:F��t��&��'!�$��l�K_�X[?�"�>I��Q9�X�?Y��'�R���1�mN�s�*�1c�
YMt������Y�K{&8�G�P%r�lY3.�
L�B���8!�C���z(�������N�# �C�����RsR�r��9�{?��>�(�l(QD%[���o������������wm��ke������'�j��9�����Z����h4������G���������Akw���j�f�������p�����Z������$F��9Hp����~�?
��o���z�Osq��<�<k�y[�V#��]�'�o�P�1�8b�nl���y�f�~c�=2�����Yx�]��k�1�H����0������I�s1]����[��O�Z�����L�}\E'r�W�-���6Lu�'�{������k{h������[���/��[}?��{�������n���g�F;��-L���v�=������5�r��k�o�7C������l��*���j��sa��_o�~aN�U�g�&�Sf�?,X�X����wo;�^t���l�[��t�~Z�����������1�}����Q)�"q&#4>.�R�������5f��|��S�J����)3=7��Q���t/�|���v�/�_������.{�������������S7����~N6�d�������NH��*�p�����G����Q��a��6i��UY�K7�&�T�s��mn]}��bT�Z����/E��h���j������_s��:ps��8���t<��N���-�E
c ��L��ero����g��	y4�MR����H�tE���IK
XZR��C6���3�|�]�e��E�p�������D[��`�������[�:��0[�:���/���kgU������A��������.��a��������pK������u�o���`�5�(���mB������{�������y�a�l�'����9��5&�~w��Z���U?�$��������k���]{O�������������G�o���x���c������{�8h����F����{$��9��B&C��o�����0����������n��GX���>���8�'��kL=T)#z����3�}���a��������]=$�HP�������<S��==}��s���>gl��s������J����B �I��Sq���JFL�:K�l�5�� f����s�����'g�����yxNMx���H1���l:��&eI����`�?w+�50J@]���!�E��4T<2'K;/�2��P��(;���@t��zX.������5.��KJ�����?����G}��
��?,�c4�������x�K?S��0sv%�
�����yz"��Y�u�mf�p�<���SO�����������2�<�2+��,JB�m�����^�S<�<�������[�ClN('��:Id���/���������ZP�������������
��Y�y��7���*�bt��6M+J8)�|��%�#���C*�-�8Nb�Xq��<^0���#t0�����%��f_�g!���yEQ��p��-������9C>;:=��������YN����d~"�����r��h*=y�@������2����X�c�|K'	W��Y���5��Rn������@�xG�>]�S��R!��W^�����a,+���������	uYg�e�QX�h������6��e����}�*�nE*��)�J��5�JaN��42h/8������^�����I�%�_��t���a���e�t����}��$���"U�x�;Ka�k����p�*;(t���@r�
R6x`+T�\�IkgF�I����O%�6�t���`�L����
��v�^��1+�J*f��&�ih���w���TrZ,�+�(�/�N����\o>��^o�m�Qn5��]e�w�p�^t����fi@	�2FcN����dM5�,������x�iPz��{M2��������-G��y���0�4�Q�oG�fn=��}������~�\v��~>��k�n����w|�E��v�@����{����c��-��������0���E�S�`����kU:OSZL�s�c:�6�}I]\��4f���%���|k��[�Du����Ur��]�@U�]#�2��
����2���V�f��k�1�{�c��|��26$p ����C9�v�1Z�q
���qt��9z�a>�0��������X0G��l�����T�rH�\u�����}��_�V�B+�$~��Q���$TwE��j@U�z��}��gKV�T����Q y�+�TV�1�/����E���N4��N�H����R���<���sk�h�L%F�\K��A-��<.�[�� �������.�m�N���a�IW
kk����\o�*���Q$6;_���Q�F{u�:��B�3��0<�
w�|qzRfN�������VL����k���l�������[��&��1h/_xO�O6��g��	���O����v1]�!��Fh��I��<��
F���l:��f�_�w���kQ�W����l'�	�$G@��+�:������{{{�)���^��/���?��k�����h���FpP��[�f���7�Z�^��j��^������������J�������=9�5���TR�6�5��v��|[:��RO�������
�EkH*4�7��n�l:&��)���%�
S�A�s�>����C�\G>z���y��=��g7�lU�M+�U���6r��,��\8812�q�Pr:$8�^�UW�3/�YW�L�`W5�������������W�m/��l�3�.[���rw������z����j��k�~��7�f�_�\��ZQ�����/�����_wo�Y���Dq��t0;���w���0��{RV����f����|yq
_�r~���p6�`!K�&�ztH�z �|d�24��u/�:�;p��1���)�.�,�h���stR4V�T�e�����*��������~#�p�V+�5[�Zc@�,�DLh�0���lQ%�c.;}�c�l�?�I?�7�zT��x����j�g� �p6��A�
���QQ��{����=~���|�����h�>����������4���G��"E���
fP�f@��iBu���(1�y�WEO2����a�F?��M�}� T7<�)�_��D���(4#��0�F%?���9G-K��'P�����Ac��i�q��8�O%�������ZP�m���h��T��\#��S���\��.���<bJ��i��y�O0Rr�����	�0��iUkvQ� ���:G�a:��V�������]p�u<Ue��h�t��]$6���[��T_&�L�*��phiX����-a'��D�{��h�eex��������_bu:>��R�c�^x�%������K1E-����7���.z�a�O�J��(��w�_w�n���y|Z�����.�qV�"�e4K�?���<,B���Pp�;�����PKXrJ��C��c
I�����g�`�yS�fTdIf�v���s�2���v��O�����+��������:'4:^koi��*�5&��i���k e����bH����>J��\��o�Q�OB�����^��Q����}(Q�xhU`��J��b(pD��3�(��#z�����C>�b~��S�^�*M��T�f8�l>��$/�8���'���z�9��B��V�S�0Xb���}H������y�E��qjHRiB�1#
*��s�s�'
>3�r�Pm��gE&�D%�vg��z.~���b}Rf��k���pP�d�D�q��w��b4n&#.������p|�)~Ry����t
��)M�?^��A���0�>"��h+���A9H$Y��I�`(Fpx������D���Z����7_��j���l/�n�6Z��.��
�SR���������������O���A�u�[iP�����R	�z
S����i�->�^	�l������
��u�A�o���{�m��5/7�Qp�9k�\s�����6tV�"4
�V^
w�+q���������������v-n���5�Ec���.�&wm���q)ABI�Wp�z����u�+z���O��j�J43H��J�K[b�[��?�F2���(��f��T�y�xj�k��i@�#���r��8��L�>���l����Iw[��H��W�#����dj`�|,P|�y{���>����w�7��K����I�s�]q�#�BHX��������fv��|~�}3��`�������r1�o����0]A�+!�����	���S����JM�|e1_����2�R@���/ �6�����+����|uX��(�v}�y��
��~�
���[��V��\��W��K��\�������?�TT��bb�qPv�b.;:F�+��������gi�X�������E����6�O��z)��;r��]`��|��	K�G	y����m�.vy��.���k�6��e�����C>y��7������$@)� 6�Wo���O8�e��G�\�Ve����W|���8t�j����.��+���H��b��*L�B��������}�2o4��V�k�juw0���AAie
 [(W��3p��C����K���
@XP�_Y��K���:�� O�p���h�B�K�]����w�xiQcm��/7�^+��vm��������4���A/\^�X*.m��p�*��?�5��]���Q�)�:W��oxS�jE��!UMzm!^��.P0�%��7]�P�
s���Y�'�� �"s.���)V^YXW��;�.��%���M����}�������f�*Jo���1wf��*�1�[o���A����f;���n�����O�������?8�����~�����F��������:h���|E��k����)\�}V�{V�-(\k���(eA�?��B��/i��J��aUK�nl\����>�9}��Xk���"�`ZuQ9C���M��0�lO�N������g���b�J���^��WeL4�>�D��J$���J+�MJ��d$9����3,�f�qb��0��=<��g�^�8���0>�ao�LN)��Dz[��l������2�\Tx���Vm��������^�_��V������9�,���n�&e�eS������6�e�_��G���+�;1�����r1_#��H���/�1��3P�Dl.�\������M�� �y��u��N���9�T�m�v�����G�o:W��K+t���L#m���;L���V�	go��5h|+�@j<e��9H����a}*)~0��������)p� ~��|2Q����,�����J��!�8�i��=I�Q���|&��(���2����=������U������Sm7��1}u+�������M�%At�O����moY9E
?<vw[���Zm���V�Qo6H?y��lu^+�	�%A�99�g�����&�	J�$���4��j��5�I~�
�	���d8����#,*�|	�	9mr����j�wk^�S���<U(����^�v�@�:��m0�Q(��
 D}�'I���<��6��HHW�C������������������G�w���_��B���h�k��_k�����S��_�m4��������~Pk�� �7��^���Z������-������6�On�m��W^��g���Z���oh�o��
n��7H�=IH\070���l�$B(v�G	�I���@T�#�o0<��g:�jVo���A�!
����~��s)�1
������ �P��_h��Kn�����K�t��,��
	�l��8���'���:��JQ�%H�������#u
*�����t��!�m)�uK*�
�)����"������%Pt/m]P�8�v��Gi�S)��*5�;��0
G����s��@��r/����*%�Z)d���]FM���2L�������Vd�R>���#����7W�������nJ�L���{��(�*,�I�9��T���Q�xW� �A��Ew]��*�a$�O��I��F����O`�
�>�n6�$�vv8_X5LvF;�G�p2��q�������?u?������������v�}�[�}�_�t���������?~l��<h"Q(R��/��(�w���b��T�;����D�����))m�{��nY���;N��
}(�w�>{��	�<r���<�
>D}b�>������-�+8����-=�L�J�t�������9���'�Q�C�R���|�(8����4�JJ��TGZ����������������(��|�� �G:Y�i���Y9��bg8k`v��C������^�����N;�GuWvW���JR�}�-[9�g����;���\X1�Jwj���=����O��|����}8U!zU��/K��`����y?�����O4&�AO~���}�`2G�L��(q�pPu�� �4
���s`�����~�C�����~�;��D�N��DzF��HSr���g�U�'�f���P-���5�	Ec�������������V��;�G}�Q�Lx�uyb��TP��>IHlE:����QWS�O���
U������Kev>e�������t+��d�1��)�a.�S�i�����=5��>��G�3����V�������D����>��i����A�CL�0���{�(��4����I�>�\��,w
��2��f��LG��q�`�J3���������������<�
������E$`�r������r�R��E�[�O��_k�~���V{�v���^P[jA2%(��FNq�M>�\�^AI�
�\^�����]�{urzu���7���(����?����]�O<���7���
��dB��J��W��zs>%�a����sI>/=U�!-,�E3��t#��c�:g����Z�f�����r�~P�~cA�q!��=�6e�G�������T,��$�{}Eeb�(;K��e ��6���/�u�C���@p��WD	�+ ��*��(�uA�>�}��G~������V<�����7
d�$�����oX��� �z�d��o}���=�k��&�E�/�uu����NV��0���~{�0'�j_�KFp�������mP���M���Im��3�q����n�����/t�8�<L)������m��T�%�������-z�)�	)`�`%�%����uK�Q1)�G�������a#G�<=�����>=>�,\2���C�|��Y��������K�pL�z��(����`��of���j�O��FB�)16p|
Pl�o���,K�C	n���<��i�Q"bi�=���o�������&I<���o�[rUh�y�U3����!����n������Vjd��#h>����*A�~IOT�7���;<��q�7<v���z����F�="3
�+��}M�TM��=~=���\NN����r'#V������LGu��<�����?m���p����sV!��(b/o��u�-����I������b�Y�p�v�[�M�[]L�����L6c����p,���l\����SX'f������%O����A'��s:)5r��;�]�l�]��
]�(�}��
�����$���J����kq�%e�O��N�s�h��Z�~�V���Z�����������E	���M�D��=�&�\V�5�o��k`j�5�}	,���o�l���~&G�\LK�6��}5�u<*W��4�a�fo��W��j3�
z�`�_.����?)�� 	�[�kV����s��vT��>��a1�E��=�,���M��> ��
��Y���D�'x��W�,t�*a��x����8x�{(bWR>�c`;]�c����N�O�#d�	�rb�<��e!�I�������Z_bk����0 ���T�d���{�~��3�����+Y�������/����Z�yi����Z�pD���^�����L�I��Pd���i��G0<~��,z%��w��'�L�W�
���&��&)��_�{usut~�~~�t��c9���
6��GQ��}��.����W��j����������)P�qsw���c�xR�(`Ip��2�r�&7~`*��g���+(��l���u���t������8Z����e���e*>� S���bc����dr�b �1���������k}����p�U�.����Y���p��'s���YQ0W^$.����*��7�����X`�r�0��H���#�sWu<���"�����>x�q������U_��R�V��m����9�N�3W�y����o��Z��os�E����f
���~��wo�H�>���W�Mn��Z��z5u)��i�/i6O	�BZ���J\�I8(�>��O���a�[KS!xj��k��Q5L�f���(�e
��>gB�=��Kp�B���^�(z����|��'�R���g^S�'�mc���|�U��4x����: �W����������1�D���z��$T��]k��Z]�����Q:��T<gD�w�c+�~�S�E�'�I�:���p2��O�N���jU�[��d�?7������y����d�^���{~q���-�%)O�
T�0����$/1���C�T�"=N�iE�Utb�	�1P�x@���6g�F�D ph�WnR�f��R|5�m,<5�gzhp�L��t���?�#&W���o�v1qU��Q��t:=d�-U����o[�:R�*��v�o�4&m���?_xW����������t�^���1��������KjD��"p�P����|�+a@
����n������^�|�
{v��q$����=~���f���?p&.��O��L�$������Vd����B!1�!������#��=��63�.�h�Y������4gf�
��s��(h��$���@��(g:�������������	�UIg��u��|t8Z��}Z�V=��}�*1s��$\�e��1���_�N��2\v�;�+�����n�p�x��w��e�����SM�{����O~R9d~�x���P�E���1
��+�m���}��O!�SD���:��j��^�������x����C�������b�"�O�����eQ�\7�\	��qY���[?�����������5�����w������o�t���2UJ��
��,����C��J�=�4)���V�G
:�l����d����������1���W���^���5���Z8h�d!!����vtmC�@�i��9��\��3�[����!���$�	bSC]e
�
�A�^�
�Y��8U<?I�~��T6n��
��%M*R���c[z�v�P�02=���FS&S)����9��BR�q\�OI�tv-����I��7���(����v�S�[z�ou2(����)�o�$�Gc��2���������j�$����o,�>�?E�Ff!!�w�-�V�3;-���7�.��]�s_D�~��������^���n�1G�c���������x�:��!o7 =�����s�� �0�_~���p������!��zi?���Q�j���O��\�.�M�}���K(bN�(�Y�%�E��pEPp/Gz����4�D��`��c��HP�8�$�����8?Ct8PZ=������F�@C+��1��%����8��/���;�)k���.���Kn�W�����]�N����=��������v�GWWG�t�:g�:W��/�i��`(���p��RJ!���r��"sr�Y���X�����"���E��x�:�HtP�y���L����7�;�3$'��%�~f��ST�q�@r8�geg1=��!='��\^�@�R0)p������.�~�A��t^T����!o�HT�.���~H��7[
�&��4��r�g��q���A�/dC�����c��hF��$�Q�O�v�Y��$�3u��z<�K�%*fG�mCkL����������l�Y�2/\<��]^r����
�R���s�������%�<�H�O�=�����,��N�O:*,4�F���e�������3�z&X#�s4B����4+������"���b�H4@#�S4�Tb�&6SHf�T6��SM��Jw������v���XK��!y�L���n)!&mF�8G����s�
���?��p)Ss=�)<�$��+4����Q�l���G�����L�/@n<���1���������S��2E)�&,�Yl`uj��u����N
��eU��OrF��������j���&2�nx������]�����U�t��	Aa����?���/�`����j�sn��hh�t/�9
U���mV���%��/Z*t����g���m�0�*P��i��;\j��3���J6���m����>7F����6B���5��{����|���G�B�>�r�������)�N�����4���^�j�)�M�i�A�g�/�����Q�t�\�H��3�-�*����e�F*�Nf��;l���?N5fV*��^��&�&�a��Uv\]0n��a^�h�������
o�����O�F��.D�S�`��l[�uy��9dG6�8��T~��)���_��2x��)�r�F�&��o��Rt\�%�"� c���81���%C��s!*��KS������[�l0�SJ3���������C`)��R�y:���(e���1�"y�H��l;/^x'��G�����|p}yt��^����m)�zA���x��lP���������������p	z��P�@Mdg��#U=���5�?��8���������/�����l�Vu���A_Rf[mk���,np	p�_�B
���8;������S�A8?
���!|f2�a	��I�
��z�8���0u����2s�*��Q�J��H����:�=�BU�H-5�y~������H�R�A��f�pv��$���8�4�R��`��E-��k��k���><��Wd��x�,��9�W�O����LQ"]aqP������bc�,�|XE��ymBcj�)1���!������&�
����\\�B1/��Gb�a��Y��������~�~��$�!���C�H��MR{�h�B�w�YI����ta��P��O>���� �^�� E�/!�"�m.r�L��yz�CrL�~$�)w�������r3�.[[�c��r9�_�A]"�%���(.Jvd�Y���OT2��U���]���.���1�.��Ny��f&�"U��,s�����NG��a^rGb��H���P`HiB�\(�3�(2p�\���O`����x��O8��v��
y=zJ��{?�%{Q����8;����"/�����T�8���$x����MU�Ar�FW�� }c3G1�)�4�,��:.s��&�O��J%�1��(��j"W�l	R,���>��$����t1Z�4F,MCU���������u���������(���1����Q���M�X�tc�D#���JQW�B��Kn�'D-Y��������c���L~����Z�����0y�!'����V���b��YZ%�	>�������>N�4O�5�R!���j����(�J�*��JP�������y����Y���"�/�v5��}��pL�*�aJp��YJ<Qp�1���
9����j��j��|[�M���A:����`�h�g�"�<�u
�1.N.�ySJ������0��s`�Z���8�t�R}����ch^�uM�L�`'�Ze��!XRB�b�HyN������N��&�i�#�u�U�P������P�������j��*��/�����~������R��?�\�^$	U�
a��BK��f]xN��t�|8~�����R���N��_P��\�KyH��nwc��i��Y���wcN�&��DQ�7G7������Z�����VY�$%lf.bT��!��������jxD�s��:�|��G�z�VTY
��V/���v��3��<�����|�0�:6N+bS��D��FO�wNV2���[�������.�Z4&7�f�>���?^�(D���C�r����+!�x���2�i������8��w�G�4O�2�?\b`����$wa�����v��"���
0�.�V;�
1w�GKd�b���+�*�Pi5Pw�%2�y�Sr�&5YJc~��_?�=T�>HY,��L���y���]H��r�j�?�-�����S���O����S�L
�3S5R���<�T�f1�%�h"�y.!��@Uf�N��P�o�w�MU�?s2�%T-���5M"t�S1����	$0����������s%[��=��Ps��{�C�l����s�;a��$?�s�*����G�7��������%Q�,=�u�/�/���f0�Xw,��������e�$2�a��"p]"R��a@�*�e�HCKq4�+�b�������;-j������+���J��.�t_������$�3����T��!BC�?:���!,�
�0e���1�?�T�#��[���_�k�;�`��E����^�����8p�t�2V������R]h����gH�@�M�����
F��p:�����^Kl�">�/�.L���vY[e)�qX�����dV.����,�_�����3���#K�oAU���v����C��u�$R���Y�1�_E�q!�����a�H�L��}�h� �{��C|2��c��g�5�Y� ��E�����XK>|�;b4h�:��O3e<��+�fUZ���R#�����x�QaH[��M����G0��S�XBn��3*���4B�����{)������JEf
�:����	"��+���=wER�����������n��S����$!��xM~q�J����f�G��9���(@�x�-�ts��mT�}�Y�@��
s�b��
|E�m1��2��t�b|��	�������L����ZY�`��7��<��I)�"a.ZU���<q��B���Ec��Jdo��6
����EM��?�%�bcs��9�f�W}��5�9��Q,g��xf�Y�B���$�P&)�N
z[�����:��R��$tO�@�70pv�������:���Y�5R��Kcaa��hw�0�]B���c����^��b����k����-��(�,�7l�*�m���[-q
Wv�����,4�K����"W\�'�b]�hK��^[�&C�[��%�<JP(V�����>�����M�Y��Lk���(J�A��'�<�<�"N�`�k�h��B���%U�bh�D+����~��@������$���eN����b4c��p�-[i�����"�1B��dH����r�@����0����������CQx|��B.��+�%�}�T^F�����]- ��)��S�MEkPSd����;>A�tuX��}H�-���4 �~���"��`F�/'�J�!&�[}C����QX��*�
A�S�P-r^�m�0d�O�pK�P#[q�'`�N�::����\����T��N��O��#?��8�sa]}2#��O*"�Y�75����q�B%2+XAt6'���%�)_��{�%���Gce�'���R����OH�M�JY3	[����>T���K�h��9,MkK����`L
N)�\���6��F�p�_��Lt�6�uu��Q��TrS�����*O����G#2�+$��4��h����~ �X)�����)E_���+�S��6�`�_R�)0zw�����
L�6Q��6���r�>����_O_:\����]L��`>�Z8s����gQY*!h�u�F[\@���l�D���\a�7{��hBe{������a�a��5��pil^��e�m��<%�c��qw�i���������E��>����	�	�3���#L4�8�� }6'�A��ahi�r�G����#�����{�w�n=/m�q���Q&�e�tg3���@��;�]�im_6�����e�r�����:oI�'���s�KE��$��P������A�k���H��?�pUB�-������r)o}FR=Y�y�8<*�����Es5�����n@�����5 �#Z�����C8cO�Y,~�c���M���^!�q��;�����Hl3���t� g�n������W�����>��JEWK�7!j�L�����=�n�>\�3!�����	��/�*����o����'d���H�(���g&���K�}�x�!������]��h��_*h6&��>����D��$�)L*�D"f� VF������t;�s0��+�� y1n8�f#��1+�����.K�K��^i��� ��!������w=Uj��Y:���Y����aS��7SBL��h(�x�i�MN�#��-I Px��	�{�>���0�0��{b��'��*^N�����n\�M"���|�;�%Sy4xU�}�����`2-�^������>�j���*K4r�l���r^��-�4�"9�E�8mA�s!����@?w��"������1q9d�V����F�����+W��P�C���-��<��4�DU���N��(
���7,�$�7�n�i�8��[g�[���m�]�	y=EV�����.���3���gUN��D�y�x����oi�xO%[F�w���5�pa��������J�Uty<���i}�?�\����RvD�����i��r�E#y���|[���w	%�>����H���1������H��W�[1�)���U"Y}�-���j3�=�<�`�J��g�B�C�V�FJ��-_)��v��ww���Qy���$��"
�8%��EZD���G����
6k��s�84�|��cW�Q*�C��01�kf��;VV��C�|�}���� +����W���$'>�����L���������2�s���;�y�
��D����V��=C���}�6��A@'W�V���'����`��d��X���8OB�������m/o#LN8��q���1K�y%�1��+�^i�'f�"��s�!������T���LVpI����4���B���-�Y�%����_���t���P���a�	!D�)��YB����@6�S���H|2*E��1z=>����7���Y8�hU
Ua���5>��8�#�$Y�Cj
��Wo���O���GS���X���S���C�����ZL�M)H�������,5�����[,��9�X�Y����a����qu�?GJDR��Ty�����wS����b��y��

�e\�y���y��0�"�������:�b�q#�����5�v���4�q�"�!Ukb��R���A�c��������n��X���@� W��y#k�/����1;
e����]���f����D�/;�8��
���9��^p��)���I�O��Z$��	��Lj��L����B�z1n\�1����r|��xwc�u�eK2%J~E�o����iZU����r� u��RA+��s����6G�Z�s��V�L�W�A���s/��D5�D������%-Y����������]O�������!��6��=H�f�r�h���bk1�D�����~�� ��m�z��4���;d�j�O������,m� ����������fO!UJ�]��f��/@���N�c.���6Ei�,�Xq�Uf)��2l2����t��8z�q.�0��/�Al{u��%wj���0�n:;��=��uO��?�gGo:�9NU���d��j`10�����1�g��@_	pk��*�.����#�/=���&C�FIa)y��*q�D�\y2�(��I�%�'��+���p�����)2kMN�N���:M��6�bU�uo��lF�������a��Ri�!j�U��������c<�K���i5��JYV��t��h�"[���`��d2>��W�����j> O���4�ya�g�H�^�����rc�!u:����u�a�Cm]�|^��Ax��LRao�c��'����"�����������`��V~suIRm;Bi��e]A�L�����Tg�����B]=6X��O�ZT�6��.Q�0�8�����B�O,W8c;w���R�F�D�gI1��
��+�e�����g��?JuPZ=��B��^�_����D7����=�s��)G<��H����`�y�OL��fdQ�	����EDe���PJ��)�NX���&��;����Mw����V��l�<J����a��h�]�]�Y���
�0��`�i��x�d=M��Z�!������E�(J��d�PtgN�pu{�k�������(G@17_c	V��HAV�X��(������2��|Q&&k�����P ?�@��Ko=Vk ���$�	E��xd������I|��;q AY�"�d��'���S���LW��n���s���d��a���1]��q�p?��(d,���h��b�P��}&�*O���x��*r@xH�s��k����������K�zj���7��C�f.��]�U�����D�M�3])4\R��xJ�����z}����]S�@���tP�8I�Gxa��:�0!��E��P���,��&���7�S�p��Wq�?���d�u.5S��T`X�'$���B;��Y���<���kR���mJ:m��sH^%8)R���d�O��0( Y�De��2����3�R�Iq���Is�ea�+
�qBAE�P;DT��c�����k�I��X�dG#���)M����
l$'�a�m?(Fz��c7Dr�����p��z�D�$~'�|���
Q�{Wy[�Qg7�l���T���K���?y�
m��"�H�4�������:s�����N�g��*#;��m;�N.+�3��!qbz����PY��v���M�\8i�?n���%0Wf��t�n%�����>Y`)/h�k,_�\�h��\��Cho(1��i'$M���N������}@Q�#���nC��/��8�o �|�j1F�!7����^�`U(��C��ix���.-e�7�-���x�,M�,������?�-l���:��pU��!m�)b�����Qu�/@�5����Y��h|��[a���uE����H�I$��oggg.|��n�Y0w����.��Y�_5S�_�[�6�U�U������(I�ak��j�5[|:'9�*�h!����d7����%��a��(1��>�&f*'�K�6��������h	�%���a6|>A�!h+�qG�|�j�k���r�JS{����66��=@���	*��M�������j�e�SE���Z�8�SD3�
��P�V�1�����g�P�$0��5����xR<����u4����H�TnL�R).�'���C�����Cn���&$�	�_���t�;(��&�����@5oYx
-�����2}��-�u�J��w6=��{�T�v�f-�r��F,�4\�>T�r���#Jw�r��X�A�4)79�I���yG�#��Ml�Q�M�l-��5s�#���,�X��D�����3�_m|���(�3�X�8F���1���l�5�ksj�Ei��4�c/��[��.�>3�%�i}��0�6�J�0��X�c���H��( >��T�x��$P���6�R.�_���0�TT(G��5���L��Jae-�����e��:c���B3�t��X��v%z��P{��)L���H6[�(�y��%�(�s�\�F�����'���O��i���fH��9���P	X�Q`����$��4b�`���%v	S����h s����������p��yT����"_�LI6,p����J���[�dQY����&����"���p:|�h=;�0N�*���A%��DZ��*R�����0���,e$�a���l��G�I��*Z�ML�vN��+�"q�FW��b��%y��R�I��!��Z�2���%�r�q�8t9Cv�iF���
�!�j�����lj�	��:���X�s�*Y���xvQ1Y�
��W.�mUh��� ���1r�(Q&!�':��G��}���{���1q��5���Y��X��]LE�Q/1�a���'������`�B ��?���������������:(4o]s��9x����dC�`��]�is�f�&�r�-O��	�4�%�!e�;a��%�6Eyp����B_�3U���3�P��_:���< \��+1�����^�hL�x@�f�xs�Q;�6�I�~E.U�'�Y��_I8f��*�fa�7��c�1^��)sO�*�a4�V�4�`@2.)YY�VT
C��������R.���Jah��}��a�Q��}�j����kG�	����`@���0�����L��&A�&`��d�\�SO����_o6^E��2�_p?(�����qUh�X0�c��h�����*8}����09!|6rW�n���*|@�EFQ����+6(��&����[��}�����{)!��U6,���P&��Qj6@P��B���:5��9-�����=�Z!c��B<��������D��2���
��4>��1���@��OQf}�J��SVYn7������tB7+��?�I8�5M�������aY�(������x.f|+��f���o!s/6m����gS��~Y["������D�VeW�U��0/B��A����Ht�3;Fj��&��
�������j�im�V���y����d��i���*�2�����(_��$������*���ZQrGQ����S�:��[�?U����}���[c��1�r��1U'f�R#2�%�z�M���2�Y���E�����'&�q�PN \�J�3���"���
�hQto,��c��'>��L�����`���4���������Z���^v5�4U@"kM��+������psW�8�����:9���7��.x��(����gDds��u0U~����ORX�A}5�������^��8�B�a��)e��0�R~�N�v?�:v3U�����=�5q1"uUi�W<2��Z�����
)����B���p}��m�����P�vLt7����y�p4�� �(un��J�*���,i���������|t|��X�x�^x�C�E���.B���
���v�a����y��$^!��{� ���4���xr{�k3hy;������Xu:>(�e���SQ��0ve'7���o��.��i��]�e5����-wk��N�����k�Ee����K�[Ah��e����/��H�������u�&���aD���t�]��uA��v{)^������.���u�	d���TcSE����1�J���L��~��j�qz��`f����6��|\q��M=/7���#/�
#��5�������?:�,i&�&&O�w�Esn�I���'�w����KB:��E�`��U~^�Yo�d�F��T<9�h5,�<s��T�F���mR�������E%�DDZr`L*Ny���3��]�%��j�p�-d�>��Z�r7��2��$�3�h:��If��QtJ�8I@?�R\����lT���#���q����-��u��_�����V�i���D��=��53A`����T/�3W�p��8����T���T�:�R��%��/mE���"����w��F+O���c�x:�,�*��9���u��Z��He��VS.�����?U9O�������,:S�1�OI�w)[<c(������y&�����&.�?�j�G�;}�<V������}��75>|�CzH+��)����7�lV��<)����4�D����g�M��H��������;�D"��i��"!	��LR���>8�@��` �v���o�������{��c�dUV�����<�������=�t�Y)>���Q)�G;�M]��Z+O���}y��JA��Bd����4���[������.��Z��2'���,��H�B�*4.��e�|��<�o,.)��|h���Sq�@�I�@�H��9���=C�A��'>+9%��x6E������!Q0<�	5J���������*m{��T��a�� �-�!(</�eD�>����D&��i�@��� 8c�ms����@Z�/8�V��v�y�����������.�~zx42� G������{��)�����![0�}�������!�EGk�x(dV�F�z����� ����6��O�����(���sL$�V��i����z
Tr������tU�2#�9t�yonfz�rR�~���G�{��gbgO�;��I�����c'�+1��<�6��*�����z��a'��6,�u5���7Ddw�&9��S�����FW2��K~����id�!K��-I�^�-���Q��I�h���+���3��`�����Z�����i�_�����������R�!'�TW��xRYbf��W0"��\������n&���_r�$F'��7��+�8����>���,���dj9�C����S��#�Z�$!��`��uu������g�+���i��r��]?o��^3��#��t�����������ZY��*��A4��x�k���j���w����.a�oZg����f~��}'q��l��uUc��]�u^�T�4S���8�JWy��5~�}��6�cV�9zA�~g���Uxs�p�Zn��$������y!���}&��������\d������dis@�8aP7����6a�������	������46mF�A?�
J�1+T�3���=J���mD�9��j�I�LN��H�qe�y7{N��d"��$8�-[$&�!�;0|Q��p�,fL1����V`�����V7������*���7
�R���L�\`�^2f�m�j���3��Wb������J���d��5S�lOA��`z�����fl��q'���*9���'����BW���P���@\Jf`5����l�q'3��!Q�j���h�w��"���Q�Y���.������H����@�l( O����:<90#BW����z9G�<��Q�&�j�HQ
O�n�m��WxC������e������t��
������*RF�P�zk�%���hr��zrE��!���W��?�jT��~����n��HW;X�RtV�]�A(����V�>�9�\}9'� ���?��������,a��T�Z�!k���p�+��f.��6�p}��6��|������r��|8n����4A��
�[�<Q_�v;�r� ���d����K�����dW~�X���K��upr}��uR�S�z|ry~�<��6� yh�^�����*� ��5�eU*	����u��(�<����Y���)UlR����XX���4���+o�+�ou��:u:�U�B)&,��!��Mw�I��x��D`���$}b0�������e�G%[+����y��\+����\�'�g�����,�|��,^�/^����P%�X������������s��T�?@��N�h*��?c���}�Yw���9(��[������x����4���$�%����~�G��sI�:���/�Q�8����������h4ctpr�9������|��?3)������������^��:������1��^G�����ti����1�:'`+}���;����I�	_����h����S0P�9M�
��;�_#bN�������?-��08rF�Yrt��
P��Y��he+��@C����k�����"����4f~�Er�mx��0y��^��K
/��z�.�1kPg��>�����d4K����s��<��b{i�_'��j�uYP�s�;� �
������03(\��IL����"�B���y������W��c���H�0�6�d���D���WJ7�.�R�n���������n��6�pK��������*>�<��
������rXK�M��e�8FM���Rp��5��A��(�(���{�A����E�F;�����`�	]����U�jRqm9�j�<a��~ �u>�s�"�Bm�����c��M�6$0}V�q�.%�!E���%�u��f|��F��?f��yE6cNW����P_e�D��p��P���$����9�4j��iy���
�"w%����������f�9�����L�LY��K�r'��(+��;�x��y��l��U�������� w��p����x
2�d�!�Hm�
�(X��Tt�O�"
���hI��y1�s���2�u^��8��2f:	3�WbU����:�=5�HLw�'4>��.'
����?;-�r��<���T?�pWv�-�S�������y,-�|����?w�/��x(����*A��4���������E>����9=xu�E������5��S��-�D{�b�������qe<��Y��<���>^�y�X��}�kf"�wso4�7�����5�Z9�-�F��.��]��}�q��h�!b�0�(�����dH2B2n�n�@^���E$���W�:1<A�4`5�"�y<q��f<�;����1
������s�S�;"<�@,�S	���+F�p���&1���#?�j�VApFnw��3��=��Z�ALP�1�%z0��Uk�����"�a��?�)�u�y��:&�(��hV7.W��2��jx���n��D�����1���d�N**�����������x�������z�B�;DVK&&m�|�rzZt����sf= ��3\�����	�kc<5�v��M��P�"������HQg|r���`�i�,O��{�7�o1� �>J�$���L��e�'�%�����$�#��!����t�����Fx"�d��<t �H�D����O�2r�H������~4U7�2h
���������S�v�w�k	t����:8[m(����A�\�"k/�K$��Ke��8���wTs���>��z�p�AmP�`�Y�����o�C�tNB�0�N����������������1����:��:\M�,H�,N���
�(jr
(�#;�`
��|��
v�	<�����)��M����z
iQ�P��A6�rN,��%��":;�1�C]������"~��H�Ow�(�������@���4�4���Z�E�1_Z-p�/t��@A�NER������?^mN�s2=�;�����	��������r{�yC���T�q���`��)*�H�r��N@?%x����*4W�g�QMB���"�|T��/d����e,��������d�~�g�(
11[8���Y1��u|g�c%��S�+���k���N����@Cl�,�6H�����_��C��n�_x���c�^�X��Q�YUM�P��qHa�2~W4��S��'����R!�i��:Ij��D��k>'0Tu��A�%Jk���a��I��kjc���H9|��H��N��3A���FA�w�\�La��S{JB>+��_��G��w�Ny
+
��!����X�������:\6�!R'(�����������\���Q�@8r+*��L�t������>O��Ia&�A�p�x0x���
������7�ZrT��� 	�Y�w�i* {�I_�@����G����Z��d�s���>s�h%�S������O��\�UNe�g��\-����gz���Z2�\��m���m��=��7�������k��G(��e�����
9#�s�%Vi���hM�A�B�R[�n�,e�QfP����/9��\��FEo�Jp�}���;��/�!�,�y��/�;pa�n�o[��T�V����&���[����JY�9��"�����P�U�����+�\��qD`p7�o�@IG��hM�e�aL2x;��u��y��&�����B>HY����B�OIK��TN*<�z�/l�E�L�}�]�FbR��W��+���|-�l���hX�?~N�Y<i�;?��>>a
�aU��+�c�>��=A�T��	�UTN�TIu�b������������J#�*�o�m���FfM��"6��H��b8�c2����|#�G�(��266*r��NK�6��$r��-�B����
|��q	�/0�{��+�I`�kz��	Kar0p;����H��`�W&�F1��1������P�E��a,U�8������(0��	���>H������Z"g	�K���*k��S&�R��jQ�����r����y�"v�#�_O�+K�PP)&us7�����H!�����TC�����
�sDFn3`��8����0�Olj��������FI
*�������!q�?��?�DGZF���{���������x�.��^��>����7����g�*+�����c3�����;�V�)���I�f;�}�K���qk���x�3 ����T���
vu��A^�n�\��|�C�����'��V�^���2����	82:���
���q����	���7��a�%���ar���7<�����>#���^��p|�_Z��!����9�|�Z�M0��q��5�d��^����������O.���@��y��(*k�����x8��Z,�����Y���	�o��S_S�~�j�?K)��8P��{�r:�7�I$�e�~���O	L��o��`4f�p�0���E��������Gdk98��R�R�`<�Q�>��j�	�?9�D96��J���:m��0��g��f?[�w��(}�Z�(�T����,=����
�{k��� ����[m}}1�hw�2���$M�T�����A$��� *!��D�Q�	�7���
c}��(D���pF�)],@�F���h��$^<����*	�#�c�G��C������Q�1t�jI�����2\V��������I����l�)�&[@��9|�	�=��~���$�og��d6�J��U�����eh��[z�PT3F��	
8�9����a�^�
y<����S��yNj�r:c��,t$Fc!��������`��	Fxr�	
M�R
��h�p<�+���'�z�C�U�y�{7�A<m���PO+2�Z���]�e��4R��h~S�F�A�c����I�W�9�q��K�����E�$i�a�$WVL'�yq�*��s���O%�����'^�w�����nD�>\���S�s�FXgj}�Y}%�6�����$���h���|��&������^o��m2�������J���z�������f���5On�����E���y�����M��IgX���,������=5�	e%Z���^-�����HL �����J��XP�"�TL����,Tg���&Fd��8�	���1g�^��;Q�JU����76Oc�Q���f ���3C�J��}fS6`+\��,a)#D�� C$��=�l�XM���0��� ���@)�I�f���g�L�2G+L(U
w"����&��?�hSd6���I�Q�L,�,��[��*��B�i����������s�V���P���
��(?w�RN���ht7�7��<�`Rz�i��3-;��U�5j�k��:g��X���:��`/�I���yFPTE���������`S#������\9�9z��R��&����e�YA�B��y�/��B�6��Q����^���o�H�x�&M�I9�$-��S(d����uUs�=>m�9~v{{���ysu|l�uJ'�OXn��<����������bW������"S�
&�hn�,L(n���p:+J��3��9Ze&�k�
�
�Z�Q�<�*d���>�.��f��J:� ueF�6���6���l��6iC�z�gWv�����d��Z�~�K��++�r+���U��b0�x������1n�"����l�N�`�f�s�l5]�V�����Q�9������'+S��?0vV�A���&N���9�\Z���C���&|��n��~n��N��_`v::rS���<s:�
#�1�x�T�*���f�D��a�g������J���u!z��N���E���]�]�YU��<����);��RMDn}C��i�jtp���^�>X��p�����(�H!q��:9��#���|���].�N���e����HX �����u�<�����T2u�^bm0%)I�V#��|*
>sUw7O2���9D&[�E� �y���p�>B.��=�|}|��d�()�����.~9>k�����������6���i��"�1���N�|Z��]�n�stFn�arR�%�0�u�,hS� {k�T����}q��y{�(\%@�;���U��@B�0��y��hT�%�2�*���RW%w:���cBt ��q���\��HE���Q��i�Y����B�`�Y��j8��`���8+w?�E������y��9��tr�PG�8�HA�/(.��By�X�?��Q�d~z�w�{+����=+�~��k�L!u������a����U���������������\8�����x��)
+R�b��z�N���mRf���-a�E��ZP"5�zy�
���$��I�����dL�E�6�����h;d@q�I��e8�_�5E�#VJ�A1?���J���:��%UV=��/�Bv�c�P��ps�_!}��;r�'����4�Y��lr�p�g��+u;����c�����NdI@���17<��Ri'����1����yG��19TqFu�Y��ly>��>�U�z7���4C�xo,���`�L\���k��q������+DG�g��RON1�{�7oF��w���K2��%�|=M��I4Da������2T�3���"vF:����b�*e9�FcJ ��������Q�v����X�����[Uh������&�&y�����#��U������p����U`8��K~\K
��L[I��;��F����O;�����5�`������As�!{)�8'�|(C1��k��0�;
e������"M ���TD15��x-������0�H-�����4��2�"�:����U-�w�<����`?g[e�w�����Y�� �k8��9�����8P��0�j�j�`2
��	IW?�4$V4�����)���P,���diL��K9G��"����q�x=s�@�\�j�Q	Jn<����`n����i��/�H�2Qt���f�N�Q8��DX�%�@�������|CT#��u567�@�����5�<�5U��!���20`s�*nZ�9b�����Yqy��nH3��"^	b>X&�7�(�>/�B��	�k�Fb���|��$qRY�h��)W�"^����Z�5&PO����hy|��A|t�`1��U���R�
�h���,^-��m.0/�����L^%V�����T|m��?;���8��S��u���4%-w��{�<-�fX��\�
K;�;w����Wr�iyo������.��^j�p4���'���\���l����"D-�����������8����U&UVq�,vK*S��[��&Q?��-bR��������,��%�����e�`�W�2�S'�V�gd��<+����6�~���\�_A�s�D
�I�z��kO�B6s �,<����F�����}���Kn��s�%`�7�>OJ��?Z��N/O�� �b��0DOS7���B��cbE�%��U�"6Q7'���kb�O����K�������t��w;}����I��Z!u��VbB ����f�8��Q^�2(���#7��\]�m�a�]q��S����B\��<�Q�#��,���I�F`c��
qU#���E�������Q/tDw���]=G�7��5=�u��>�k�����T������8�m]^������1�z���z|��o�mSp���o�'�g7�!��-@��	�9�y��2�d^T�~_m��@m�{x;��(��Lg��4S����)����/�	�������hi��d���N?�_'�^�5f������6��A�j=���H�	��I@vSQt��o~pR�������� )�&1���|�J��q-�frqfc�}����I^�7���.�oAr���e���,���[��<&p1�b�xJj��Zl9� �x������`�i�Cp����})���|C[7��E�qI4��k-���+k�Is�����9K^K�5Z��s��(�����H$��cg=q{���m�V��k�����v:o�t���F��K���;����
4�`�<�#uD�v����D28�
d_[�qTp�NA�#wq�����.-��D[�!�vc(��Q8���,A�5����m�h�xhH,PI�V��@o�
��r�=r~+�$��`�x�r�j�t`�6��@�Q��^K��L�Y]�*��g���sc��3���.g�����l0�[��O^��w�����t��:G���*P����H�o���&O�����w,<t"���mq��j@ld���,O����7|�N�����IoG�@BR>�9��hYxWU�|�m����j�2�x����hpI�'��[�+HG4�W�� ������-���v�������d,W�������m�S����r-�q���_�T�m���*-��m��
?�FC8a��#|��aF:�W�T�w��mh�'�g��Y�
��O�tz"��7��e�.��;z��I���o�!��~�v��UFY����������5�-����z���V�S������q����6No[������1��;|���t��,�!������F��|����g^a����d�X��Q��?:�t����r��lW� F��Ah���v�d\��c���=�
%�m�){�������-T�n�!	�)E���,(=�=�9���|�i�J�(�\�D���q�w|��/P�eJ��y����*��{���K�t�c��<�;�DAe�P�r���!aE��l��WT����Y�����c�(L��:-�u��m��EWh�U�Ps!��D�c.(;��!EhN���FfK&5�*�e����2;�����QP���	��?���^da�\��mne�*��|r���������c���F�-�C�1��N3�x� �)��n@�����Hi!���p��=�
�;�\q��js�j}=>����^/����L�p���J�����7����yS�1`��G>����7�����C������@MI%b�Hw�������Fa|��z��-\��J#0��1��G��Q�����XpB5��G����l�4��s$a]){��>Y^��B�M��X�+��P}��08���
�����-��B#������-(�VG�q22 ��\�@����r���1���]5���	DD3�B��_����J�)�V�����9g.�^�2%�ha��T��P�Z�� �r���+��P�������7����^/��z��8��R2������1�����������&�L-����C)���;�,��������T�����Y6�\J��3���K�/����B�e^>U����n���hT�'J�w����5�q���ul����C����5
Vh!��hP�u���|�PL	�K���t�u��}-���z�G������&WC��gdeD�p��d6"s��c�#���"�8��McS���z\�{p����'���1��k��
��b��)�L2FE��Dw�
��5w�i��D���e�-7s���d�\��C��-�/�fya�S�'�u���W�,=��~�[�����});;��7�_g]��	�*���^�dJf�,����xj�Hn���cE�e�\��E�t��c�vU1K1X>�)i$f��H�%�l���i�|<0?�����CJ2���2�M��o�<�O�����,���+���'nGxR�,.}"�an�]�k�Jl������D@/_���J���a[H�Y[�b!��:���2�>Y��a��$@�Dbm9	�gUF"��b���Lo��o�vm��d�'�9If#�Uz���0:g������E���c��&c+�<(�k��^�[L���BvE��?���(��>�!���<�4IV�������[�'7`gw�/&|M��"^O�k��?AH����A�UR5p��W0��=�� (n�P��L��p���w�\6"J�B���(�p����X5-��R�S�p�X���,�������YqG�)��
�P2�T&CR�3U����UI�9MI�����d�j0�����r����$�O:l�u�Z�C&.6.�8�y��zW?��,K���j��,�|���P��;�t�������&��h-\n��+&���������Q��@'��\��K;��s��F3f�}���P��9��Q���l�D>pTR����2�	��B��R����?�2�Xx��IbT��g�����d���;K�'9�KA^I�Y.���)��dK�����o��H��R�}�r��9GV�?0��$�@_4�|����y�k&�v�������$��/�1�����G�e���C
��6�aV3�{W����d��M��:����N��2�z�����~=@�r9����r�x���������xs�>?��YN��������W����"��C!.�H����������D��tE�RP�j[����C���]AM����<#+NW���K�����y�
�R���U0�L�I�!�"��fa������X�&��[�����c`K9��3������V ��&�Z��&�Y I��g������7R�I��|G=suD�U�B�8��=��<�|1�P�!W��4�W�����F<g!t�������hv���mwRh.H7.�D�#N>>Lz�A�+�[[5�f6���]��'��Q�PXN����$4�&��F=��� ����jD^���Q��������V���eV�jg{ok?��w�z���nt�:������������2-����/���g���Y������?�s5�?��;��]�}��E���Z�+��,}7�����b��\	I�]�9�Oeu�	S�y�fG���w�8�]��1x�3����a<ZV���(�^%��>*��k���E7�D������ o
����F����������]�%��<9�������5��������r�+�����!��d����P��A�����:��@�����^�wv�����<a���klnm�����&.��������y�<�
*�������#qG��3�P6��v�i�:��M���#��W���Q���q#����*�+���/���@�%L�Dp|����=���\_���L��}�U6Jq1� ��~al�:}t<��s��!��z�`�{��.�z��G��RWX�����sh'"�lE�%����2B5������~��?������vt��������
H����]���e�,���%��x�,dJyv&b��p��7���l�`��spP����[�����y����� ����/���4R�/^�x
A���b3���M������UI�<a���ao�����Nh /��b������P����(�H�[��d��z�� r~��-�u�B�x��LC����LL,�)�F��*�dY�������:�'}[�r�K���A�.P�����b��Yt�
�VJ.pUs�5��&�Ep��RBc�WLB�wF�'�~:PvIh�6vj��C�� w�)��
(9�'?�<�A�e�� PU<e���@Q']m�	�����h�S��k�m%���H_D*����D�h��N��������s�i:#<r4���H�!�r2
�A9�����2�Q6�0<E�A�v��A�{�Gjs��c@�u3y1�C�!w���c�������a��_�w�w���0:(?v����E��L������s7��$�<���'����p������>�n��_�����W��o��7��f��uu+�!~���k�	y����I<����TF��TL��'��'?_]��o�DE�������z���I�T-�^T���i�����dq�7���rU�<�������]����0t��
��w�*$-�*�7�X_~�
hx�M���y��m�7�Z���������dB�t����hr#��(z���Q&�!�R��zq���^�
>���e��?t��>w�s����qpp�}xp��*����DiQ:{�P��.?�Q
���{�O��)���7�pb_�	rr�ig��i4i�
�U�IV�a�����.�2��M�V7$l[LM����qGL�����v�����)=�
��p���C�lh�C5��u���_4fT�9F*��U�wk��(�[���dY�X�LQ8Mb���?�rN]��b0>t��"�b�C�[�XIy�VUXv��4d��?@����W�2=���j���5�	��m����M��8�&��A����6�oRI�)c�5)�������o�i�:=�}��sU�������I����L��B���=��w�/��++r{���7q+��*����R��@�>���g`/��t"�#D��������m��&Y��j�{=����Y�/���X��bHx��<����8v,��j���HPW2��t94������H?� ���d;:��m����[�z+���56����\���\q"�����0��q�S,���X�zM~%�R�@�w��kA��������p2��M��g���n^�]^���
(��m��y�:����������)�^���'e[E R���FM��[��k��@�+�^��'��gMH��]UO&���9��'@}/���&9*))%r�	b{����]�������{��RV_�6�JY���0Wbo��	�v^G��d6�6G�B�I��
�s`���������H���p�������[A����P){��^��I�G���!6
(+���P3�C����
L�i=����i�x2	)�a:�t�1�F|�F4rm@���H�\{[���_��j�X�p'JG�	W4AE�`T�0���^�^��OEAy���\��]F'�������n ���K��	�v���$��Tb������C�3� 6�T����kN�X����_����y}�>�8m�]��,^�����l	����R}�9}a����bLX>�op�Rw��0e���|D�~O���v�g��3�SR�l����;	�]qC%+�A��S�����B$D����1���1��;�
��~$xIn��L�&�i��
u#�����?���Z���P��X��f�������������L��kF������S���$E3�Qp�pP�I���DUC�|��d!W�0���1)B
o#X|�r�;���r�"��`��
�l=	����h���X3�����MP#�#0~��^��p���5am++!v�����i@�����v����B6F�����0>�<:;)�3CbjKkZ@���?���@1���N4��&�|����U�r��|�9��B�O���F#`���
TB����
<����_����w��L3�T��*D���4G�M���&H����u3��w���~����r�7I�0G�h�Os	���xhm����M)����?�1�v����|�BFU�P��g�F�'G��*���L��������M���yq�$���O����O�cQq���c������Z�
��S�{����E��c(�CN��f�`a��z�<8@�����N��gD�fn�kE�_J���t3�Mv�}�G9����K���#�z�I-$���%�>4�iz�P�:�|�T��5b����3������'g����WT��0eK*Z�����M�:�G��hj/�0���`�&����j �+&��vH�?�=�d���|��	BW�V4��YltD�`��G��@���f9IN2B�BS/��%�Z��fSm�l�pmkrV�AAwA�n8EO\��'��q��)i����C��������D2��'x�y"��	{�M���lm�aav��n�(��gC5x�s�ZK��NJXV�)F,�oi\R(�'����i:�|w@?����*����9~�������
�V�����9����OCJ�@�J�]��`
�{�e��e�;70@4��)����?����a���X���N���4s�{�#Lp��#8U�)T�C�V0��=D��.Z������i^����75�Z�s������������CI�2icV�{���E$�g��:�P���{���<A+��Z+o��yH^(W��
Vd{"����%g|&�JQ$��^QP����M+��h2�m�Fg�br� �xJ�����Q&�n"���T3w5�$E��K�L5����/O
���m���/��k����g�nm{�%�Z}�1�:��H1w4=9�=
0��6^5�Jj���0O������ZWj�
��K�����*E��2��d����#}�����p4r�����/��Q�`�b����w����v*���:wu�P�-i�����P0����~����+QIN+��H�K����%5��g��N�C�~���m
O�a8��h����S%I�HF�����\���n���Eh7m9�o�m����a�^�ilG�����7�j8�����O�o�����o�Q*=�
�`t^�`N�A�F]��a�pK�a<y��tY{�,��;_���4V~�8��	19��49��^B���;����:�@T5	���<��x�9Tw!vO*��;��hB�Y�H!�F8}d ��W9�PD������|��SrE���=��-A�K+�b1h	A�#�}MLIr��_ ��j�e��u�M�MSeS��f��(�SS�T���d�O:!�1���o�5�Y������#��e�����B�������#������� �N�p�����P�^-Y�yw|��}�<����1_"�|'}*�\::2'FN�q�H3R��+i��|�L�sn�D��];�&�J�^{��U�A��c�����������2���+4��y������nN����J
Z�s������F�l�~5��#�k�R4����J�P�^�J��;����^?���v����A��qT��T~d�2xTnom��i��I�9�Hn���'�9��n	d��S���� �|�Y#��P����q8;��	d2���V7�mq���
�J������nc%}�5�L���������wH�?�z���&�����r9,��n��!�`��B�C�F�:w&mu�	����z�o�����x��������}����oo�no�A$mlu��v�v�s�n
�*�XN!B$�6�"nF�)H�%�6��d�����S�mLdP�k�y9D�\og#��Ybl[C���c�dOr��t���M4������������,��(�U�����v2�����"W��i��'���eE
��r�T���t��7�_�^_�,Y��!�����3Am3E�>}��O��[V�'�GJ�_��}���>>G��_���{�pO@N{1x eD�����k��p�u���^o����s��xg���on�E�q&�8`����'��B ;��@ktV\H^W�>����k��������g������i
v_��@�A�$���.]
���u��pk"!�oI<Z���_�On��T��F�@,�<5\�����p����N�[� ����g�9A��(�
��^|��������,�������t1���zuv�v�N��Ngg+lt���%���J��W[��Rk��:�K!^f��N�����;���r�S,E��?�����z^G�p�����^�����\�!*�����ogw���
�B��Co��xedA�����~w�������V�m�".^�������A���H��g�,�x.w�6%T������j���T�RU���Z��3����uU{����u��������lP�i�
<NF���%W*��0��P��N��n�ww���n����v�K(���Qj�$�������Y/#+����T�/��+3����i<H_�����]��
���y���K�.�R9�yQ������U(��I52"MGJD ����)���^���x��2�c���~����F.������c�
�;�����~6�!<�SD�9�P=������A�F�{z�`��[��8E����'N��s����)C�9������g�)���b��������Ju���l2�g�C��%S���z��R�T��H�.�����]���)�����&zh��<y�@D�^���#T�������'�4�lDy�&����	�]R
r��pr�����F����i�1|	��. ��-�Nz��71x�>i�G����F��X�m}����]gCt��t�������(S��������t�����v�n�m2	j�����)h8���
6�q����X�9dm����6�W��+�n��qy�k��>��*�9�PACr����i�����p�pw�����y��}z��m�;N[H��M ������="��H�xn�x���;���H(���S�yE�~/�w$������nF[�����-@N85����:Ds�eG�� Pd���:�^��8L4:�{@�2
�"Ps�f���+�G4.;IA���G�X�{2����F'�D�}���?���ah�������I2~��9�����������a�]�o���J�4���D�o������ D���(���al::����Q�n!��Q��@o;��r"�d�)��s���DZ/R"e����:e�Q�W�bIyj=+3
�!�"�~��0�
��D{�N���#�-�L�������V�F;HX������/�N:��/P�(�b:�
��������C%��;�$�C.�U�!����{�Ots{`�6\�/N����B������	m��-�)���uS��.W����7D��b�������
����C44K�%�������"���%�����,rdq�����*F�?�.�]ra�;5FK�d����(Xs;���Q�<^���0vZ����b�&�W��v�0I���e>n����o(~��\�	�����*M@@(;��m���!E<��2�B
�����0}�l9�����Og,@Op����F&B����Z����-��|���'�L����C����
|{<�Ul�,��|tb��6	j�m�3�������|�Ts�p��!�^ ���h*����m2�����ENN����]��4o���7���n)h����fzf$M���b,3f���Dp:>j��B���h�T�i0���5��b��R�[���V�oc�����2,U��E]G�$�~J4e����D|(+l`�
�||~t)L�||�	yU�����c�>�Nud�2�k4Fk�\%�2�<6^E�+����4��8.1�	������w�iw��Z������vq���\������rk��.��Cd;�D�rA�s�1�������Jj����7�e����s;d�Kw	�����x�{)]��fEN����G��4�>��wd��G��i�2eJ�4�'��q�����������cH8{9��s��{��#�@���
u�*!�s+����J�o����I#��!_]�i,���A�6R3���e-�:k��D&��T��K
DQ���R�����a���6K,�<:��2�%���~����F��2�
G��:�C#�*T�R�{����������W�����������=��~�,��vy^�M!�[�S���3��n7OaWA��b	����,���~���m}��#���
���o�M��:��#0=��(����LF��`@�&K%����64��]�����w��g�w>g�/���	����[���>*�:*NM�(rt��|g6."�|G
�]�Y�i��0�J���Z�V���	�J��[��.��-do�&|�DV���T����&YrT=���M�;�v��F��c��
8�JY;��Z�^�RD
M�����F*�3�s����*�'��{-[��A��������a�
�3e�c���AmZ�b�e`���"L4d+�8����������J/d(��u�^���pV@J9�B����a��C�pP]*�vn��#�;L�G�Q�K�{�y���\�����6�/����Q|+0v��o��
n���W���]�T$"��Ja����s���v%0�����r	��+�@��}}��:�-�z�~��8���l)<��?�6�H�|g;�q�p���7���3�����L���:x�}�Lr�9Y�T+ JAG��6�!�
cS�aw��IZ1������;�s�
���j��5\(hf�W�����F0�&���<� N����e��x�����+U��Y���<f;����2B������S�B
�ID!08�}�g�#��s�M��+.�����M`W0&u���9C��K��5E{s��^R���=WB���}��$����,�$�p$}!�A��S�*"�`r������C�H��b��@����$�!"�:�����d�u�d��E��	3� 6���Dy��P�L�"z������<V��R���d�������4�+�����\�����N~w�d���<����h�!�
,"H��NG����)������2
�$�B��z�:���8On������x5��;e�����X*�U�D)���#C����_�<�K����D������z����R@*���r�	}�a&1n/N��WMJZ�M����c�S�Z	�4|}��x�7!�����S���U�GH�`�J�@��,y���[FX��D������V���n4t#9�J�.J��i)����F�]"�����id��o��mL����%d=
f@�c���Gv�a���x���@��Q~������tI{#2�u���8
���X?��q�11�����<�d����<�.��J_'��2��g+'<�HF��Ey'%,�n�8�"��B�Dk9�Z=i�M��X~�sp�eV�'w�l��}�Q��m )�DT�t��Gc��%�@ K�.� �H��������D�9�!
����s,�W]�bF3F�-�"��E���������'�O�B0T�hZM
�g�TU�%�G����l�_ER��rn�����%�����>X@"o.��6o-�����,
-�I>e�xT�m��Pz�aK:)E8#x����'�t�MO���vC�0L����^���d��D1����z�����\j�ex1��y����KR���@�R� &�6�J���u�������6�F�(�}g���9��9�tSMy����L�P�T�W��	5�`������P�������agk�_�on����ag��D��S��k�E�P��Ai������,
���>��jy �M`����~�����*_�S�v~n��P,��w��b��[�{����l*�?Z��y��/��b�����KnN�I)U�D�S���������F���:��{�[�z�������vw��)���<m�#��������s
�oH��9p���[�G�n�{����e�79Y1�<)�����;I��c1�;��&��w�X@����n�gr��B�����s�l��������~c�1'�4WQ	8E8������AA�.��M%��4��0������.��X�$97��4[�����]���hn��~����D1�p�)�{�~��0�U�=�+n�1c�{{����a����Gi����r����&�L�����"�*�^��#�!,����9!+�QKbZ�\������'��7����-�T�P#rryq��!�z�����=J�qe�&��@%�*Zz���%P��&2������?i��n0�������<�����&)o����S���S�+�S�AS�L^S��s��7����Dv'l�����I��}��f���	��T8R1����G	C9��$���q�^G~�m�#;8��v{;���p�^������7�;�E���g��e��M<����v�����s�xj��5L�-���"ON�<PZT|@)���A�!���`��7I��F�h�+��^4�!��k
`�q�3�k{_+Y6'�T���-��R
r�w��z\����N���l�Ey���/������8�uO3�|���[�a��r9�������s���7���p�o1"����l��/0�9�&�Lv.�d�*Q�&
�������|������*c�#m<�E����-I8�}RO��	��RQ.m������p��Y��}��m���j����
��l�+�7e��[�}�LA(dc4x��"=L�O<M�y�����A6�T�K���+
WJ���kgn$
�m�J��h�U�����I��R�[k�"J�nmk�OI.$���Z�����TH��g�8�P����i����.��$�t>{r~�o���Bj�A�b ���L�L2HJ��i	��Gb�]�����i���d������?_2?$��U@jm��L9Cg�v�3��s� Yb�j��������'�;L�I�]5�3-��|��nY�u�[�����?�.��4���*7���i���_
	����||v�������btH�>�����P\F���������v�����������n��W^[NH()G�.�C�$f	�H0Vo�PL_|3P�ogp��B�`"�����y�~1j�nF�����.��K�w��@8CTUtP�(���4�������v�����4G��>����9�W:�)4��
���1@��]W�,��O�k���C/h�/@�U����"J�&:��!;�\)=�u5��xE����f�� �8�����f����9k����"��8���@�Y����Z��qgJR���4�H�\��W{��x���j�	�`��L7?Z�����3�T�.N������Q�I$yQl
�>j�2��lX��� �{;++���8������x��f?���u�od��f..0�G�`M������f��;��D����a-nln����7����F�^�.d������?�@������?�����: ����Z~=�G�t�M����&n�Z�.��e�mtw���|�����]0��'I�h���
��T\c\|�S=�0�+�]��K^����r�p���n���C�	+e9���y_�HV5�����	NGc���J��$Lb��oE�@�B���~����.n�v��z�j^k�������tn8�K;�%e���_��%HB6lZr}��y]_^���&^��}����x_X��P��������pF�Q����*7m���}r�<����4a#���a%��2@1�
�y��q���[�z�7D�rRFq�#��D��PH���Z.Se���?����Vet(�8Ls��@��������@O�Y*H)>�����O�����(\=E�p�49�QQ�E����L"���&a/��f�U)��9�:���CT�uA����!1����s%�����C��q�%��z���e���l8����[g��?-�����	������&cy� ����tc�H4!�:�	�|�&T����x�%j�9Q��Q���u�(����E���/}��:'WWo����?�6NO�n��y����_����T��l���"H��� ������P�V����om������3�fm��F?j�%*������u���z=��u��D^
5�5���7�|�Co��p'���kyWSJ�Z ���]1�B��Qd�v4�&,V9������@x�N-�?a��:�E�.Z���	jM�*F#���	��+�=�d�>��8m#�Pm��I�d0CG�I��(���	���47*�z=�5�2�����*~���d���O�iu���f���Y�1~j�$g=�.��m��lc����oc���:��s����8_�p�c��h/^��o�bX�H���+E+RR_����`�Q�m�����z|N��K����1������*�:�r�-�=�0��lR2�OJ���wS�6���]��'w�����������b+xj�St��2�3�c���|��C1���
���/(U����K(��0a�;�K���1
��OQk� lP��I]&l����4�����[��R��<
��R��/(�����:�+RE-XC�=�d5	�<t�=?�����/������d}�
:'G0�W6�6�9#q��p9K+k]'{vz,:%�02�F�E����iLV�����4��'� ���go�/�I��1���X�S����uS�U��C�����G��x]:U�2��������mJ�@�����a@N��P��_H����D�����DV��_��F[�<��cw���<���8��;#y�t�|�k��,��t:%G�,�g�""�llm��4v;Q�^���������[,�x���6^1R��6T�m���2��P��)�����)~1+F�<���YU�TqQi�d�"�NT��
�����]�`�5��O��w�d=>���I����v�j[�_7o++�v���8es�N'[�����b4oB�aWL,��M�2���|w�x,�f��J�E~����K���q�Z�)s�7���#��A"aj+ck���g?>�6�-c��V(�0
��U���}�G�JG�q�3%#DC����>k]4�m��x}�20RnV�pC'D�~�t%E;U;����/�����>�	�|ka�$z�io�r=+[�,5W*�MW������V5�-��])����l�$Ybe,���t<��!��ww����+�X[��N>H�P���`�:*v�p�����iC��7-�RS���y �)q���Q�:�P�0cVHa�f���u���pe�������$�(|��� �D�!��t|g{{�����p����w+,�-���X��lg&�46�K,c|JDV���.�Xr��LBt���w�y�����M�!V��.�#z@#�'�YCy��PBFp{��Jn�{��s\��)tC%K��������7��z=�m��������@s�*]V�]�������O2~V5��L7B�������#N1C���XEPk���JB���h~��}����H�����<i��N��L�no�����;����{��������V�)�~����KF�}N)#�>z�w���@�A���<�?�U���$�9�L��!)�5����~I�����P{��oe�'�����9���T�N*z����T]��N&����\������Z�7?aM>=)��	����4L�p�z�^�X(��3�2���g>0K��&��������wK�����������V�<uDaN����-'a��P��
/�5����l��B��,OJe�R�I�=P�}������[gAp�o� ] �H���	
��;���((V��.&��x�d��bxu1�	@v�F����l�;m���W��t�;}l���H�+n���SlJ?9r�~@���
���<�
t��>���b:�����PQ�
���a��XY���nt���`#s�r~x��1XHT���yE"ykM��Z����A�m�[�����������d[�G��c$�;�
x&n�)��2�nou�a��$����{O`s�
b���
���U@�T���Rm��B��2#)��wZ�>�kq�����:���U��l?�����w�3�[��r=�h���L&�����]���Tt9�g�;X<��� ��-�N

+�f)�jZH��t�BF"���Q�
�_D���/�6?�}Z�8��0BI����OH�u�U>��)h��<�'�F��v�2m���o��d��:�R*���4���y6��3i��z���n�7Y�E�	����!^�����&��0g.l��W���RT��n9���B|6xe�q��$2U�K�#��%\��F�q���������D��&o�x�.�M�O��iXMG������/��?���:��e+Dd�Es1eXPm���H�����Y���!������XE����1�n8�`>���om�����.��z��R-���	������g�65k%h�����u����Q�n��i����.�R������m��&&���	(�}�f���y�@�&��_�VZ���n�>�Aw]��.��Y���2s���X�m��o�j���)����VA�����4"��!���b�(�f���z.��&@�z�����#���-��B��i
1�bh��1���#�e�)W.�b$��cnC���uL�������q;�oRb�A�g$�H�0�HkaX����Hf��a2���s}{"������I����\��a�v�0"���+��61��	$�#�8�Mg��e��j^�_4/n���_=�-�`����6Z����������o�G�o9'�e�T�����L�=��2lk���T��.���\�o��	j����N8�0{��*�<����-����;TJO��x�tCI�!�w���c�*��y<�d�L1������p&E�G�N�'jd��Zv`v>���Av���>��rM�F�����q�$	d����3v� ��u�z�?���C�����}c.�EA���,���D���A�{���?�����M8��kFX��������t"C�Q����q�����|I���T�����8G`�����[��w�Do�Z��5�gS�����4���H:;��.S����D����8�}8&@[���a	��Y}���z_�N.@oF����SV�����G�� B�#�!��k�{9�zI���?�=;LS|�E���`��-+T��e�"�#'�;�]��7�����F�������r�|I����r|=��x� �aYq5,�~����e%�m�8$�c����;&����������_�����e5���+S�Wo0�Q��`%a/c��(k�0����7�	^�#���~�l�:�X���rEF�g���F����(5����R���;������S)T�P�G��
��Jq���<�E����T�l�
a�H������������`�*� 3���C�;W��S;�c�yw��G���Df1��F��z����/��A�@�b����}��+X�����q�]������`H�?�;^&~I*��p�%�3 �.
���5A�D�M^�ER��}f�fz�#�L����(F��#�ek�.��#��0H��|����f�:�U��<��J���"fYRp�r��/d	��DhG�	��h���n��#Z��jb���g.�/���u�(�������Y������X8%�/����l(��C�F^��.0P.��c9�
��$Z3��J�&���c�:����=y���(7�����4p�l��&�M�W�d�V,��5�����N�.������	Y���'��W��<�u�i�>V�i�M,���>�Z��?x�����G�G����BW������K�o��VT������*��|N�����j�K��v>��%���R"��$�-{��go�8iK��D_��A"F5��*�Yf�[�X��$r�)0�_4����U�.�NO�.����PX������ ��*����{LqC���(��SU� w�74�H<�)x��#��)����������r^�Q�<_}#����$��X�]�����o2<A?A��s\D���{����j���\�`N"�kaZd��L�I��%m�����%-��k,`�L������HQ�*�����k".��~�=\����/���{����%�����R��A`6�K=�|1��t���������`w�w�
w:�n�~�}�������N�5�<�
�0��/n�A	�:��^y9�Ch+���N?+u�j�W<L�M��#c�
&��?�@��g�8�?�������u����n�^�;������������d��yo����~m��������^W���������i�Nk�#��"-����i����������[�u������� Uy�1�8.��u#�'��r���<��/�?2^��o�k�m��y�:�V�F�hw��;�M������	};��~Sg�o['�g�7o��3>����.Z���6L��]�7�<�7�u�U4�����c����
�_���5��E�mp��)��V�����{�[��@�����A��y�YH���R��E8���k��uP�B���`����w�*���{���&���x��8������js��Rq��jTp$�#y�X����6Ng��G���A�BF�����,
tP&P
��T�0�����mlK�v�<��|�	�
@������c������h���M	��]X���j��K����{�|j�������������uv���a�7��8�y�R��1Jza]�0}
����$c8�]�I����Nq�{|�"bp�����@���I�{�����6��
�t����~`��9��c��L�}��s��[��a�Wy��-��zi���A���s���.�-/m�q/������o��:���g�8N��=��m��L��fD8��P6fR��~�}�"1�g����WcT��u.;I7��l���Lb�},�JFNo��&���W��[#�3�	����������'y���i��)]�����(&������0?�d��[si@
}�U���}������(r�(�MH��U��@3X��kf���I4@��$;Kg�G�g	.�>�����m^�}1x@�c����hB!�QL�����|���D�H=����n4���o��!~��zG�M�1�����>��������\�����k]��6����q�{���y�:�Z�[���Qg�R�?'����c�'�t7���Z	��,���Q�fT��\*o67��S���E]E����[k��f�.�g��
���>H�2r���z����L��4p��f�R�N��{4�Ct�[:kW������wg4e����V<��)33�g�g�����>���(['��#����D�X�������2W���/0�
��v����Z�o`3�4���)�����iP�����|?��G�UW-��l��@!�d��F���2�a��g����~*��e����@���G������e%��V7;4j��0���K->c��$VM���BJ�o6^MP��Oo�Ue��&�C��2wF�q\��U&���p5�D��`�M�s���o���2W�'4dz�o�����sCo.�Zn���,9��7�go���\�\[�y�_g��S�w,#����C%bc�Q8
W�[�0�mU���k���qoz�m�B�wb�]o��,���{����-����5��n�8M
���p6V>��_�y�����T���k�JZ�E�����7���x8b�z)}�x*@����<����B?�D|h��|���2��7+N
E����U�[R��Qo#�o��K��p��{�&���8e��q?u�]��1�hd�Hu5�a2�;9h�`��'��d�A#�b������]7���Q:�2�PG�~��u"P����O�g!{o�@�wh��b��1�����`4o:��5%`�5���a�>Z�����5'����r�����U�������*�h�uZ�'�X�����m�Q-�J�~��s�2u�0�q�e���������m�d��� �p��n�T�K�9���h�������1�cs�BGY[8�Q�u��>h-n�mU�b�&���|O�����T��������N�����.0�@���mH�v��d��<�T*����.�O�Z8�+��(��-������H���_�7��������^����J�f)�~�v2�PJnQh
�����KxUTf�d�B�X5���W���A�W��!HV�q�)z]�W�AR�D��6,a��R�'���p'���j�����y������;?_�?����D��y�"������K�Mr�s�2Vd��T�#l�����6F�����@�3
�^�����}_�9���^��l�L��|���u�z#��
D8��
~���w[U�!��
���b��}_J�(y�3���d�!���Os$c����%�U��Q��� ���Sx������j��)G�?�z�;���ZJ�	���|J;</s�~�x#���]��(g�82�~d=x>g|2��^/x6z�!@[��p��)��)�OXi��E��7���,u��h������|�Xb����22�v��g�>�tA�si1��<o�o+��dD)�:�~�������%�W��j=w�G/}	����/Z���GnM��_�s�6=��A�(</���6?�:V]%�� O�\��I����0��Fj��|8>3P�����1V��;�1Do����O�`6��o����
�Q�#�QwB&��2���]���EQ�T�� �bP&��9}�6nbR+	*�"���v$�r;r�2BZt"
�����QCm��T�#hw��bw����;�����R��"�G����������]�d8��DL��b@���J=�<�K������=�	��y��R|n�oa:%�6��a��E/~���p���B|����/_��-��!��"SJ�H��{��B�5�T����vON����'b�P;� �G�/�=���*1E�H�
�d��I��^'�x�d@Jb�WCA�@�S���
�:�B6���;$����A��n1z�����h
������@��[o���\��U{����+�2Nf��j�$�E�7GtU�a�J�`qk���	��	t�"Jz>���QF���td;��y���,b�p����#&�FhHf�����(vk�l����4d"f�/9�x�E[m�J�����Vd����4�s���i<��?�w�2x/���C��zo#��*��f�L5���8�Y����0���BP1��X�
Z��H�)(�+�z������H,��I���4�U�l����A���q�yf������m#I��|�s��2s�h���,���$K#�e����$@	cnC����>��fl�L AR����>�3]��D����o��~��|��[f����J�h3=b\�(9g6����70)��%��8��_�?z���>�D������h~��� ��L|�4H�F�����D�SO!UW�e�������/����nN��B}��x��UG����)c5�0�w$���C��%�����x��s�r=C���d�g���������������*���t������^ho���~z�rq��������?��P|&9�p�X��hVa,P��q��F���������$qL��fz�.���MI�+�"�.u
v�]�4]�������RJM7��$�������NX�u�a����>�@*���i� ���f��O(������&|2
!�A�0���
���1H|��nh�r��T��6t4���9�m���C�;Xpn/,0C�*Qy���Sq����]^�����L�����0bU�lAu������Sh
U:l�RV
�|.������e��.S�LE�Xi��SN������u�.x�j#+ �H*$��:��	����2L��)���X��� `�L�F�Ib�	��
U�S����%���!Vy8=�����7~h������_��!<��+K	���y�!�4e���k�"	DG�$[N�N),��<�aw�!	�D������k�q��� ���2c%�U��Q\�Z!H��icO�>���l�d�v�������9P�g��$I����L���h���R�-�?;&2:7��� �B�B���?�d�\�������)�k[���
�T�U��qV6ChN���]po&��{��1�FL��p7K�{)��`Z��lgL>��B��	5p����"����|�Vt8�GR���!��Y�����U�r%�� /L&Q����t�vpq�}�QN_�����?���\����Z��/�?Y�h��E�^��o�]q5,{���}���;��O�l
����O�8��^��>0�%�����tzx�O={}��o:hN���8������.�����]�_^[7��{j=��`dG�Z��Y���h?��}A�	O]�f��Ct7��?U����K�����f��~.������aO�8g����.~o��uxOC���L~
]G�Z:|	��=�0������2��c��O������=���X�������X�M�B�-���G�6)?���R��&t�������)%���3_/i�����{�-�z�
r���N�R�r&�l�um�w^xt��m�A��3���}�D���������6�����Vg_�;�TGv�c�w�I�����6��	�:�51bJ.��hv���"S��{��M�e�E����m2�B��
�	�;v��/bsI��V{��,P?;6�����[�I��8&�i3��Y�h�i�^#�\�N��(�x���Of�l��u���W �k�$�[g��p�4�
n����r�C���g�� �Y���l�G�����>:@c|^U8er3K��Y�(�\Gfarj:�����
k��VNf���'�_H����1iRi����@,�i���0��L���R�=I�"����,����\����#�>Uzh|��cS�� ���dq�P%��5�e�!����d���\�����
��X"^Te "2�=>��	W�X��R���8�������?���/�����f��Yx���2}�S�����]!����i��1�aD������9bO�K�.������>ES�����T���j��V����#���\gS��|����-��[�+%[(�wJ�7[�79C�in+�0����:�zg.�$F�E�����7�{������P�B�9!�,U�^�������H,�<���6Nj��t���4p��A�M��G���Z�[5I�+���d���������sZ�1>�!36�s��#����f������#��%#\���{G+K�����d'�xz��C�D�����`c���@��9���C����	"�/�"���$�O��N|bLc�@�st�?�������j9E������p��*��+r�����-�����g���U]&�/���"C6&2{���U3
$!B�^����
��D����kz@6�����C�&���>a���W ��=����	Z����e�oJg�%�/M0O3^��;���^Ep7��@�G#�ex�u����LM�������Il���9��U����y=g���S�������������*L!Tj�(�"YXt����zM����(  R��� h6��j��6�������cV�{���(	(&5��p�z<���S����-�����R�e�~(M��w^B����������4[O��R1���O �%-�A� F`bXK3���e�?	���D��;�;:/�\=�wv����$L��T�M�&�R?��!]�3����+�>"���q��
��_����J+�K�5C0�I������4ET�TmW�"�s&��|��?��Z|���&���^/C�gT�X���rg�a�@��b9U��b����m6����_kV���V��zaw0�����v�B�Z��p%��^G8-�t1�����O�
���Ii9��~*��e�B�(�oZ��2�/o�e�z�b�D����Z�������	���k���X�`��.��.e�
6�P��A:((S��@d4�]���S�t���a�Qak���?�X/��RF���?`�T���}����������4���QsARaM�����]&#aY�ilz��G��_3���DX)��~����,s��c����4����{����ag���� K��v����
~��&���x{O��_��������>�e��~\�T��O��6�pyr��6F�t(������HnK�Hr^n��������o3����J�4�����Y���]�������O�	U�������//r�3
p_si�iB<�]�_3�5��`����Q�S�������+�{nEY�Zl����o/M�|�����Puh��C�~!E�U�s��l�`:N�x"�r�O(���+X�?�����w�����_�T�X��i~�e�y�!�L��r�>�LLh���!84
����� m��lo����U_�������%a�Nyv%G���w�/��tUI;X�]]"zR ��G����n����yy~���80�L�����jR�K��*�*Y"\�*"<�z���
�h��k�l�e��.���w���$3P��C�4����D�]��\=������|��������������@�R�e5 u
R��'��"%���~O����Q��e�0�tQ�]}z�g��8����LaDm �s3E�X���B�P�/�jp:W��*��h����
Z���$�����v���UQ��b����4-���RvCS���Q�~M+c�e:Mnrv%�}�z���JO@�e���}R��&\�O�yL��w�$;T`n��'�8�=%����^��h��V�6������V%��#�F��j"����5�";z������].>gI7�H���8���I�"�u|K��$��|<�t^h���}��SW� e[w������8��������q���;���S��L���%z�-�Lo���r���I�!����Mu�������;|���o��V�h�����c��`tPo���j�����:��XW�O��1M@�/�h�E�h��f`�U'�I�����p�)�~p���l=�8�<<=��/.�dm���xxt
g���������N>��� �����y�7
������w�g�T��*���n_�x��[�z��G�Q0�+a�5�z�o���1�&������\��=L���$��U�#����`-��gS��x%����?zp��_Cuj��Ta������w���I���Qd��>��e�bX8N�P�#�������%�G)�������3�7��q��Q������t��[dO'1i0Wgo.'�~=<=��J�,�%��i�G<�"%��$C��W�;#q��ox�l�C����_2�;����=n�3Q�W�^�{p�R5��
���>��{��NU��IHO���+���J�������=x�����L�O8N�y�TyK)X!�}����ez�3{����4I���	�v8j��~�nu&p4cx�{WOn��"�����X'�0,�ZA8��K��+�g�-pL�?���Y)�y��C�6�$���wJ�Al8E�9%b���z�����*Dy���D|�^��o.�I_M�������(S>���Hwr��75�U�(m,3��>���3����=�
���3���zi7@���
V�I�g="�-^��6�X��p5�}H����M�5����n�bn{��k���lb�������7����h�|�[�b�H�:'��)"����N�5��{J�MJX���u`:rE�4���������ae����=U�r��m��O}�I��q���H�8��:�YyF���@����l�,��q����O>�f G�����-h�m���N���31������m�_���9Lq����.�n�Y-�x��d��)��;j���A����a�h8�����J��[t�i���F�8H[^��? 
��r�x�������� !SY%������s���=����7���|
��1�p�
���o�#�4���nw��v-6���}�XO���~���Ca��������b���:C����$O%�+�������r
)Ud`�K)&�n���G������^�6�5�u����-��$���s�I����!>	�}��7���?����a��T������_���CO����Q0�b����g�������e�+u�j
A;������������^L?=N���E20��}��~����/�-
����W�P��W�Cp��M�J;kd��r��Jp�9=v	;3����_L<P6�a��ME�(N�dc	��0�-��KX�`�*��<�s��<+�D�����
c=�C��k�J4�D��U���Bp	M���,^ ��7	��N���G��&.4��z�V(����?�J4N�:���Z���@��]�o����b@���I�6l�
~K���r��!���g�������l����Wo�U�Z���y�F�^���W��L8<��L���|C9Ul<���L_���O����N�S�����v��u��i��z��
�V���ZXkxgJ��
^���j�����Z�T��;��C�M|�����	��_!��e�t�au4��\8V��s��v]VK���z�z���V�yK�D�v�V�Z#��s����GoU�g��oR��P�	~E�;�B��������U�P@�"���E�s�|�8���C
b&���z�o4+�"����2�9��	�������7������J""��i@����yKq�T�3F����Q����^��!@�=L��������h5�K�!�RK��(mq�t����e��Q��nH��0JC�E@/�{(	
�Q���$����{
I�� �`�S��6�������2��l-(�PhR�/?@�a�`�'L�q4��/��J���*&����r�������QfK�����Vw����8���<����J�?��/����?T����3L�m~��{m�^���y��g�2x��b��r�a!T����'�;��mvA}�u��&���Y&]���YS����;0�>�A;y������Sr.\�-�/������T0���N3����4��L?�|��k���g��n���MA�	��,�#j������"Fs?k�g�Sn	��-{����[,���F�zH*�Ab2��Fr��J�}�
w+�jU�����A=���9l4��x4�f���5&����xk��[�����m���'����6�,�����M�]Q��
����U�{t�����k��k�����,x����1����ML�#pNt�`�oN��
�w<��jFB%�>O"��8��$�1�h<�z"����Jy3�`z��C%�M�����<"�y��[,C�����
����|:�U��@Lt8|�v�,P{�0�"�p��bY�a��G)h��Z�D�&nt�S��&
�����/{�X�T�!j���3=aJ����	�
�"���4^�57z�ltZ^o�`�$��`�Z�9,K�UK!�����}u�\�S@O�<�k��c�s��#y�����I���+��>����,��"�,mah��7.�h%��n����B�p+h�t�l��z7����]��-`"{����,`N�>��\���<?b����{��������D:�3�36W�yq�fs���xnW�-�w}}P��iq��r��V�b���&���
�U���%�0U~���E��>tNz�<�r(}$%�O���+O@��������������j>vE��Y��xe�����Zcr��*Eg���(���pu���\�F�@)K�G���L�l������%�� l���p�E����cR2����#���Z�d��z�s@���rcl_+13�e���L�7`+��f!�����TIE����63�\!�����z�NRp4y�0���&~�bo��f4�`��o�`�'$���i��
`&v�<�#V50M8=�����C9�)�U8|�$S��&,h)>9�����ZW�
2G�[WR/�"������d����7����OO����R�y�T�T6Qn������!���9,a;�7?V��.^���N�>���th�u!@WR��
�t�/�h����R�L�P���n����V���}9�Of���[��(Ii���^L%Z��C
�'�g����!c�fc�<^U�����
��P���U�j��$�O� D���)H�$'�d���G��|E�S�@\d�S{����%7��JC�������u��Ow�$�����Ed#�z�Pq���Nj�l4��A�8���<FX���F���N+��k<q��Oa�X��m�Kf=[��		T�,�b;��Q�;�ku��j��@
f,p-H������3��i;�-��P�� ���jC*�h���Q�@h}�H2�L!�d��L����
d*�������|�j��H�Ez���#���"��wZ��I�d.Q�����h�>
�:	t9|4�4�1��4�Ak�8�\q��~Na�f�j}�TG��+�(�Uo��������K8Z���f�.�	T��p2��V���&��#�~�e�;a>���O5:p@)F�����oeT�_��){���b`J
���
!�S�;�.&oA��X�����xr���
j�S����7T��#�-�H��+����(;���zE`O����`��(��]��3��G�	��OE�8$�D*����/�)C�2c����h��e����/g|��Y�E�C���[��'��@��nI3��mNz�.��3,�����=���(W�[P���d~�&9�8P�����B�q$R�|�D�3��.-3������"�i�`����R	�	��%hF�9{?��*Uo0���v��p-.�SU�RO�\g����KA�����eW\EQ����.8��+��+
7��~�aouD��^����R��_��7���(N�2|��.���sX�s���]�����"����*"������	�7���{zJ�+��I�4K�p�����������'����Z���dt]�1���c<#1�H�*�~�5�
��P������;_���'�����m&�{c;����>����^$���
�w��z�����_^����*�����c����	]_8m4aK)1���^8����ju����^��(jW���o��\���+���<!���9^
��f�S���x��r��A2R��/INwRn����Eg%H���K��P�M���+~�V��#�%r>?�L�DR�*�zgU�����*��H����a�>#���YO�%��F77r-�)�u��l���S�L1����HL���H����~�u��������������������,T��i7����V�6��_t>��k�J�\������Rg~�s�������P�q���F�f���L�a.�]).wqy�F��+�����
��H�7�������u���Y��fZ7��:��EI�N��p������e����O�p�.\��4�[&�|$)�����#��M!���#��������b�����vQ�.�OO_��r��c��e~
��'���9;�R�w�65+�w����$����7VO�;?J�8�8�Jc�d��]����2������^���������QS�
tL��t��"�&\�`�y2����^O�����}t}H�������}%��e�R���j)?��1����Ws��m��(�z�]��]��	!���N�� 
N�7%!#��p�
�t^d���%ya������k�����w�}0�>�����>�r�M�+Q=�Ks6#�/%U.u~C�$)e�mY.[QL��z�t?����Y��������4������zf� p��V���a7b\y�co�}�'\YTy*�hd%Y;�h6���d&�@V#-�,4# �h:��<R����l����m�5�n���v~�����T�@����Z��g�L�����_$;�0&P���;�|�6|������n�d$e1 �0t��#�q�;�rOM~�C�'qf�Y]A�,%������g�x&�rd��@���D��0ENq�*W����@��h�C��y�w
x�E�YI�(vE9���:Ll��eG{H@i��l�D�SN+I��������D8�kE�b��Z���/3����KA�o�����}T����h��v��r��N���:������zL�.4xBd��\�t�����)@WH�J��&N����m��E�	�]`Y���=H�+���E����.B�9i�F���&��N�%���.G�����/KZ�<����L�(�1P��N=
�b��y�a��P�������������������f@��J�^���@�B0;����cS���f���>�!L��l��q�s�� W�z?��������v\�0#�=7-�N2���1��8��]=�S����e/����8%���g`x�Cx�Pb�8�
�����Y������6��D�@x(�M�wb��Ul��?���XyNbt����w��ZX�^q�d��{��
)���F�����H�@w+���'_.C��5R���2�}f���41}INC���5�z�z�1b�.M8]��Y�g����o����������$�6k�$ �U~�R�z��\���@�:��Xo��zYz�-Z������d�`^p��`��tZL��<Z4KC���k2Vj����zr������R����q��W�#	������F�Ij7]0�V����iV3�
Vt&jo{c��(uF7 ������~E>dvZh�v��[�\�H�{v��P����]�^�-�;p���W�A6����q�]��p�
���j��O����
$������'��Zr���3��]4�6�a�<��t��<�j��DN��a&�;8\
��_�I�C���WCY��W�9��o��R+����:�B���\8��!����=J��#�}-�UrS��}�����x��|3x7�4*��<=El��N��3�i7I��[�����zK���{�K��1 ���GIt?<���(���_�O����?z�����|
�����:��}��f�)~8�|�;�Iz���R���4EOHr0��Z��f�i�b���e�/Md���P�I���S�IV���8Zm����D��z�{#p��L�;^Yv�L���@�s���C�gb%D&m�=���s%SvO���L.�V�����D��7\��'&�^
,�F��,��gLf�`�s:�)�]C�h�$�����oS\�K�{-3���sa�CH,(����{�Y ��Y�I��e����@�*.<��jQ��>B+N�!�J
�{�|�4�|���0���&�&��g�'�+������s�T���B�VK���p����R���a����j6e3��Ii�R�)/��9{g���m��������H	�4�����������c�d����������,y�w�-�������m�.X�:������O��>����+�eZJ7�5�WI�UE�jC��@�oM|t�$��\b�)C*Z@���vo��jb����Yg}`�&�h=���0��=������l���������w��w��&%�����T��3�d����Ykwr�y'���'b$��sgK�@���V�)�&�M������I�����6��5��Z������2z�����0��|�p_�����0+Z�����`�r����I���n��I<g�g�5��������>��R���h!�pr��
��t�o��i{���_����8�baU�>�_�L���L�"����b_.����c�p~��h�g�Rg���l�e�q�f�]�'U<7�}u \fo����-���b�`F�����\EJo#V��N�R��gd��%������5Q�h���6h+��&�W}+���[}�#�yQg=��R|)�l7�}��RV"�i��@"������h����i��o:��4����&��Y��K���v;��!����k1>T"��!�
f=#�s��M��Y���
�D����JzY4t�f���y��%I�Bv�o6�o4�7� wbx���pw���J��A0[P�-��)E�qH;���`���E�cG�J
F\��Ew���1:�.���y��A>�����+�1�\��|������"�����u�e6�m-��v|�F3���-<���y
<������6�����j�Zi���/Q��r�*8�����>��!2�(+�-6E���	�RT����O��n���u�JX2������)��$�)y�����9�'i�z5Z�n��fn\8k&�FF�=9�8=9:�N�����d��Z�$6@�m�:t����d"�c��@�Jq�S�
��m4�jT
���2��8N6�� 4Q�@�jX�-:��e�am�D�����R�B��u����E�[R��7�I�;�n�}����lHq����%	I�D+�x�D(���m�.�j����X����K�����o3��z8*{������eKn�I8����t�������y1�;|��f��Cl�b"���<�c<LY���O.ST�W������6��Ye�p��T��������:��jJc��(|���n�,����E����k�e����[m�r���u��w�|q��$��M�/�D�������@�Y����w�?)[,_)�,e�J��WRq�F����d�&t�-����U
E�I�RhG���(���t��W�%f�p������ p���tXv�i��yr����:;������ �RL��t�����p��I��.��Fq,�A^���9��MB�!Y)��I@��o�����g^�(���i��.#�)j7����f�<`��Dy{)4@�{2�U���o	�]5x5/{3vC��M��n4��8Q�J����%�y�hv��������C�6}�J�M!���p���+C�I@Ag�(*�	��J����T�6#
�E�B�$�Cp�p��dR�X�T�tD�H��~�92�S^�Z�����o.^)��������vl�I�>O������'�rH���z�EOJ���m
�������p�`�Y�
V�5��I���/��%(Z�H(��x;D����u��t�.H�k"l��S��C�� ��A��X�������+yv���7oB�RQ��Sj��B��9�������23��� ��9���m�4��x��.6��T�(/#�2���K6Uj?Y�x�@��G~5%D13�S���E^g�yG +-w���&

��mL�2�����i���&Q����
��Ei-���'s��=@�8#�{��920��q]��r3d����MO~<^�������I��-��F�Ij��R�B0���O����"q�G���Ze����vd|�'6����Nu!y����0H�����M�pt0&�I
�X����a�[:1���Y������:�������'���w�l1�U�mg"��CPY@�X�����?Gi�E�S��R�#`%��?C�p7SOG�T�����D����{o�O�G�����h��P���Ov�J�qo�g����4���P��xS5��{n�p�?W���^��$���!���V���Jqo��Q�\�5=h�u8��N��>��v��,�w�~]��H��w�{��sl��Q�Jv���m�t����U����1��e���Np1a+"z9~=7
g�|��c�C�r���J =z8�7����|��R����3\{�~t�����������t�����\����_������UDwe�]D�VH�X�����v�6��n})�8a�	��F�7%��3IP�h��w�d�H�]���RT��`� a����vJ�U!�������R�V'3c�hq��j�D���A����_)>S�w��.r�����e�y��P�V����Y��#N
������r����S�E�Pu�x�$�4��g���~�9k�8}l�i+��c�-����q�R}�;�R�G��m��8M��7��}�����4�9��)��Qf
���g�d��2��P	�C6Bl
��$N�F���s���f�g�Bc�U�����h�q����Zv@��k�V79SE)�\O��%Af��|�����w8�f������z��o���*g/|r~�6c�%I�:$�z"�Z-f;Q���C�;��YDg5O�^���}�@�%�������^����D�����z��v$Rr�-�[�����pD�w|w��� �����l#�U�����=����{;�b.(W��Q�-�?*J#�QHg�������r���M��N��g��Q;2c���GCJ��$�������C�v�yd��������
��������l�������l��?�n G�[k
�	l��?IYv����e�N�w�E�������46�o1��c	75��������RN������F�8�7�e��K�R^���w�����Z�/�+�����,�M]���g�w846��=6no���C��#c�(`/?Z�1�z�5��.b3�Z�Z��0���Y��^���W[��]�/z����#L���v��-�d4#�V�[u�aHP&|����|BI�U+|��?���H������I���;�9�E�5e`B�W�������6���j��j�<CU%8�,J�S�
�z������t����$u��l����GvE��m��������}�G#j�|�:��][�*�pu������}�������'�������<�%�
�k��2������i��*���y���������9@o{�^��[��.Az;D�����������z��h�U�p�~�O1��3�`���V�K�I����a�8r#���;r����1t������2�������E>.�?�����QR�;v]��������hUE�QW!�@�E}�J�G�����uz����2�Do]�`z����-N�U������n�Z�g]7�_b��k�A�����F�����]k�-�ZjX�G� ����'�,��S�'��9��HH�*bV��:���0���@�s'���������'�#��,%�z�kb�(m���l�Np�^b.4\�R��fH����!�,��>���MA�t�w[�w��x����>��oC�&T����;�)���x	�&m7�����:��w��ar�'wz�"��`~����eH�(<��*���j�����J�n�t�m�l7k�1���f��
�3>���G�T]�����;����L�A���h����~��W�Kl�D������sv�!��]�!�i"�;r^��TJ����t~s���iO�]��EQv�����#����`��4�D-��A��[?)�Ai���~���z�����@��,�}]1E�n{��-��\CB4�]��0\
�7DbX�~��I�-���{���>����(rI�jU�����L���k�T$�S��^s����}����>�gY�u��}��I.a������\[���S��
x:J���aY
:I�sI^�#w��J�B�!��@&��]d��3$K c���D�_���u�,a-�Q�3U�y�"��1]�yx�~K�� ���U"�����O���SN`-���D��j6�^���9�d�����|���dO���4��g��|��L�u#�����#M�gw��A��b��A�Aq�x3����X
�����19���qX=�����<2p2a�|�@��_F��0{f�pcu�?������������d�&�%�2�@��I��@�uEK��&A�!�I&�IId7��S}4a��u���l��`�>���,���������!�/��b�=]�("[�.�w? 4��3��q6��~I��z������X<���{�h
f���FN;�f`W@�P��T��H�
���Q1ZT��.��	OpI��r�N�������-
�-p��}+]���P�H]d���#���}
�!�[�J�s.����c:y�C����#K)��o���D�a�|o"N���=$�� �A�� 	��T�h�����a�H�ie�"n����x):
�|~��#.�������<_��+����4�'g����un�=�\���`5�&�"������F+��KL0el��1b�����G��&�� ������� ���\s��F��l����8�X
7D(����t������K�BW�����!J�"��%3���8����Z���si�{���[`uTCj%f��{&G\���f����Q�*�����u�5�
�	�b.���:Q�g�r���4�	��]����U\#��a���0��4��^�uZ�B4�/^-�W����?���p�)T*�Y~~6[��V���V��Q+��R����B�a��Y2m�r�����H[R\�1Dy��&���q�+��S�8��2�qr?�Q8/�N����J�����N���� ��:ebuR�)@��@<���L��P�x%�����Y����N�H�K������,c	����k+
���X4�w���L��(�p�h��I�%���S�c�m'�����g���=��Pr��<�D�1A<1l���_<s�ID�q��n��I���p���WB���%'	����@t����R���S�s}����B��%,{�jT%���0��@R�BI��r�Ga`�s���i��9�M��D�@���������,��G��UE�]T(����?�Xx�W��f:��)�HL�?)��v�?�li��i7���n2�������(�(r�W���pW�R����6�[���r�6�}Yc�)��m�c,���U_b�\����v��g�g�NY��Y��G��e�d�X�Ci)��w�5�Jn����0i��|�������i�9�O���B0W+�uCY�pXt������ee�AsSM�����7�4!�L<����Hwh��cU�b%'�Y�����|!���8�Y-C8h������Hq&�]�[�3���m+����4���D�������M�!���a�I��������� �����P��S��F]�5J��WgUV��=X-������
Om�Z���Pz4F��/`[<�	����`��$�����h�hic�u4����`�)���l��N����`��n�nk�� ��0H~��p3Gv6/����SJ�����=q<�!�^9\������������������?��KH�%��A0q�v��Kt?+�!g�V3vkF��p3��V�s��O
j������v�y<���
��g�T3����������0sZ�\�1'N$���������u4�'�.y�g�9�3����h?Cn�n[3��-��{0lu�p�������i7:�ZC)G����*K��B�����	%����b)jg�f��g����!pe���D�p6GOE"��iO��c�e�k��Q�^����'+%�x�X�\+� ��I�b�T(��Z�`����d�,��k�Lj��K�~�'zdbn9{���������B�bmmuc��*����Ij�4'�$�<��p��j8����7}�1�1�<g����7�����9������1��^�A4����W��t���)�+�����c�^�D��z���z�<�UU����
��'��sE���A���"E�dO}�0�f=g,��HDl6`�c8����+P�V�����3U���P��W���z�&5�s0���!�ijr~����?��h��Z���t�����x�	aK�V���
}E���m�r������[1�b���ix�t���m��>�&|�9z�G�A�:j���mE� �~���?��	���(�1V�4c5T�S���|���]�X�����_c�{�b���D�'�-������i0(��NO����W&!�\y�[���m�u]S7��@,d8�x?��jh���N��X�/�������.$&�MHw���D|D�@��,#�
�x1����o;_��?�����$+!�����{� l�;A��
��zPk��gl���S6Ui�\e�G]X�"��p���^k���s��J��d`��4�B_����/@�x�5^��A������/#�
_FS�;]��v���]�����#s-W��	_{Z��s&^�LT��{0�����bJ����Va�L��#Eo��jnA��@��/9T�(�������u��j�9l����8<8�FZ����U�9������9W]��8�d�$�'���[�}D��`ua��]���������>S���f��2Z��
�+������6:������ �+R��\��X��7b�~&8�5 �(q9r��&4,�lv3���gc� ��A�(~PN�+NI������cM1.���t�1$8��nS�v�.@M�q����18.hT��r����[:�IU���s�3��;�3����T	���Ri�d��K%���DZ�^�����G����%���R}G73�y��"[�g�s���2f���6F���sw�$'�4T��b� �<��2��{-W���;�6;��Q�Zo6��A������-�����Af�D4����UrB��z��a���Fj���[e�(�e���%����U���X�H���`{%0�\������lo�/��@!-��<
��X�.�;�W8Z�R��%�Oe��|t�j���{���+�H�� t7�O���x2����Q��`>]D��JJf���1I���n��7�`�{��a�o7��^'l*Y������ �N1IM�$��A)�����U�������p:�U����9C��`%�79)�UM��6{�����_����UM�t!gcSw���A��������l�:>���Ds��n�8]K��I�Y��..���������*��4���� .ZB�;c{��������=�����,�[�w�e6g�m���n��w��F����c���Q��z����o�\K�&��6����k��������y�bI\�~?�T�QG/;���I68v�W��Wp�!�� ����h2
t�R&�U�d<Qd��WU�_��G���7R��8����y�
�����GT$��78��J>�����\G�M�����=���� �;�%=��P�WT��#�����YlxW��0��WjO��_�\~1��@��na�a^Q���V��uh6�Q���k�|^a����[�Z����c�]�9>==9���.�������t	��bS���V���4�9�K�%�W�^O��z{�4�������gxW��?����
���(�"S�l��u��� �N�Z�
��?l5���u�*�.��9~c�����0�p�T:�{f��sx�oO�a5h�<+����������f���-4���N������
��#
$ �-�2��j�'�
�����S�;3�k��b�bbv%��>�z�����#fs2r��$U���jR�3���fJ��\Fcs�h]����"�B���Wk������k�F���u��g5%iU��B��
:��p_��#?X����������g������
�u���Wo�U�Z���y��b���j�!���p���L���|C9Ul<�������$�^CF�no�j6;���x�k�����A����FaP�����w����p���^����k�j�T��;�&���e=U���Q�����b�a0�*�������s���e�����g�W�=o��7{^��n�
J������YR��5j��(�3(�c
-.�DA��	Z}o�JO��>�c�du�������:x5�<���>���$�"�{@s��z��]k���$ne
y������b�x�6�n�a���V�1��1� AF�T�h�_QSOs���6�,8MX:~����'��@/�q�{W�0���������7���^iKqv7����*��/���lf����m6��$�j���������8w�vSq���}�5jo�d`��!�������7j�}�5��$���~���
�	:��HI�c%K�z�
�Z�f���
�m�UL	~�k��
��-�i��x��������{��c�&��xK�
x.�y~���Y�*����W�G�\�g��{���.�q�
G����|#��[���?���@B��r�[���:	g;�^����@y��A"2����a���QF�.*.rH���v�N��7S����^�*��3nW�A�����D���:u���S�+i��f[�C�
��O�8n;�\�����z?�t~�LI��
��G��(�U�Sn�
�,�!@R}��>.�8F{4D����$�WZ,�>��X��V��f*�=]�U�p3��%���c���G��xx�����.i���4'�1���A��;���w���	KHvm�<�c��o��	����^b-��t-^I2�@U��s�E����{�?��{}>x}~��>1��.�.��*^+}����l�c9����Lj[U�\\���Tb��)�9<�>9�C�K��jO��\}8Q"��*�L��/�6�yq������j� �gri��TB��T{�$��L����WO ��M��,��($V�=�sH��g`BA��{�F��vH�L�"�P���)K��\��l&��uY4\�J�;�gr�{!o��A�:iQ���3$��������m�9��W����>$H�L�x�T�m�wj�x���u�xI]�!U�h!�\����L�F�Y{�����Mx�OF���w�9^<�  J��'o4���gfR���)�!��C�P�OO�K��}�t�����68h��G8�#)g'�y�E���x0!��K��}m������<��i'������#{7	C�,YO.r	g��;u�|�&�M��#�V��=�0sp���3�1`�W��}aA�$���M�h;%:
���f�B�_�2P�>��o��x�������x�C�W��w3��h*�Q��q�9�s���0�#
����lQ���m�K����@A�*��4�|}Lc7PN�L
�kL�5��%\��3;J8���hm� bz�s�?�!�i�!U~���v��t=���&��j�C��5����f"^ITM���No@5�����^%S��{�}�	�|���v<��>�~;Ws���\��c]�7�X��UqR��L�a�
U �M��w��I"U�
@
���ZC�#)�@�5H8X=�!g��_H�j�^������� ���Z��hV�������S9�_5���AUon��g0�JQ@�������TS���~�^���,{��*~X
���%���"w��_o�Rd��%.:�����f���y:L��gX�e"�\��''c"7�(�,��H�d~���@pbh�fj��*����!���p�s��4��Q���o%V<L*���bP���|�3k	�����0��M��B������g|<�2������H���hVU�f,�d�P�����QF���2���V���Q��k�!/��>��������A�6��jvj�Z�6�][����r
�3���9���Z��#�-��c��X� �e�--����,d|AN���^��QF(�zg�b��r��.����I��m��-U����T���H�����cq=E����O�-��T�z��&����&��wY����������-C��Z^i�A6D�U���f�����S`�'�|���ABDL�~��&V��|W��]�1!�KG&W�����E����pD/�_�	��is}~|����5�@���4)g~?���K�/B��;��"L��y�J<���Zb�.�b�x�����{��\98���f9�L	;���+F2��/7	�\r�X`I���
o��;C{�_��-��2���K_z������^J�D�&�ng��!�}�-OB�o�f��?8=���������+�2���e���U-Z��e6��t�����]{����w�z�kc�5�z�nu�M*���M�������|���|�FfC0��h�������b�t�?�A���J��V���7�HA�rh13���>�3m�mI��!AW)�����~�I�&�Bi��������]X_�2o��K5������p�|�H�KI@y�������]c)!a,�v�9��A�Z�t�V}8
k��N$���HU�G�u12��~��X��@:E:t
W�����B�0�9mc�c�bddmY�1���`���z.������)H3p��k��"Q�Z�kt�
=Bg7"��'}�-W�e/9x���u��v���-ND���
I�5���j>��i���V&�^�W����6	�@�����[ �\��zS7������s��p��������0�j�5ei6[��NW��G���6��&���S��.F$�P��u��*�-�I�,����Y��R�V��Q�&���Nt��1�C������T�%��4H��S�����Uj��R�g��RH��.���2����G�����J�bV�����kGEW�����|y�C&kh9�,�P� S�^ ����������2��[���S8�aA��!�����,��{{�=�9�K��R(R��W�G�|g!X}�������-�}���; ����x�$��|�X�sP�w����9��gTa���R
>� �7`�I��X�0�L�N�8]��R\D ��hI����O��-2�J}l��v
�PI�n�����K�a�6b�7
K�k��������Uu�;TUdwf_{O�gX��\����}Y�t;�n�����
���h��s�X�]�d���!JGQ*�T�J����zD��#�����B4���2����Uu�C���((�r�����8<�\
��/��^��S�|�I���p�Z���*vV�N�7W"h�*f�P�$�^q�O����-�K��0N����@��]<�������Ves�r1B�	7��dn�������rA���]+WV�
*-T��jtKQb	���������)��N����������?�w�$�>h���8l�����iw{���`����{�p�r��;����*��!���hxg�=���������.�0�z	����J�A"5��Z(Hb������$���
�*D!��L���@j�����-�P+��lx�����n$Uu���Y��t}�b�d%��S�"�pY��3�-��y��Z�x4!h�e6?�v�PxoU��2���%���0����y�, ��F��9iBrj��DT&�o:�
�������i�����UH��'�������6QD�`Y  �r+�b=YE�	�H7Z��~���P��������hs@LbG#�'���!�Y��}%�S����&�:	�����3�wL*�q~���0����YN"�V,^����
mj\TF[��P��T���V�����_8���q��{���"~�����]U���{RU��3�p_��gG����w���_����������y������������/f��O��u������������j�-L�7���|�9�,�����FH���������J;F�$�����#Ve�P���^�K}��>�����{�Q.����g�:D���b|�hw<�g7����2Q,:�bX�������zP��=�C�����~�7���[����)���F�7��:����Z��z~����;"<��6M�bV��8��0�0�����:���:�T�Z}&�_�^9�O�,���F�!���W�~�Dl��J�^�y��3V`J���jD7&=�Xu@M?=(���q�&�i~��3�z���Q�1zL��Q#�L���B"y�@�1cL�$8����� ����
���[�`�a�H�� 6J:�sftjx*3*�?�Fi �Mq�{�8�6D����N�`�sx����7�jf�2��'eJU��:CB+
fMW��)Z[O���9H5N
�1vV;����\�>i�q�������`�C�p��hY"����J���B����s�[R���e�:1_��H��0W��n����x�(*����'!&��������D�aAyI�h��S���`��g�\��g����6�*X�I�A��`c���3T�9�	�W�h/&3S�;)�r@�y�3�(�J��M��S�"q����Kd�����'��2I�P���i#��O���~���9��R������b}N�h�@���j���S�/NhfdK��z�������<��9�iw#f��{\�q��5y��;k~����U<�3���!=4�2� C2�+W����]%�!�{H����0[���-�z.��:�����~8�=�9O���R�j\�<NO�4@����X�}q]s�����~�����}���u��WV%P����S���(u�����b��-5���ex#^jF�����'G�;�>�"_wk�r.�����\;���P�:��b���QJ�����Q��
)�F6s����		f+�p��WflS��BngP�[9�����%a����dI�`vEyp1M�U	>�����v�����s��8*���>n�5���wLm�;��3v������H�P�q���BJ���wC���7� ����W�IU$��$��`=2+2���p���NBe'�@k���t�0�P����(e)���M��	��].P{c;�<��]�J���N����B���sT��0/�gjR�~����4d�~��t�;�d���8�c��}�w����D��W�Y\W~'�#	�g�n��av��~{�L^(�}��k�i %��4�'p�#{B���T�"B+��S. �G�rI�3������c�f��l~�6����.�fW�8g�����w=������A0q�o
�L7��l���bt�{�fs���:���o��Qo_.�:��,C�1j�Pb4
��h/#��yGNp�7����Y��/
��'��,�98p"4
�!��a��XG��M�:�������o�M4LL'uN^+�������M�J�R��"M�)l5`�m����vI%==�%4y��I��=���(�L�%L��"�����n�]x|1�?\5�4W��j�*=�������~@M�a����;pO�L��O��Zl^�hIr�� C��@����[|1�C"wa�d@^l
e:�}�L��+��I��tO���0��>:�����W����ek��&���Yx��3P+�gG���tC\PI`K�>q���A�)�[�9�("����&vS��<�2��G%�a�V���_�q4"�VR
CO��VP����z��9�|�B�V����wAU"\J����4_��x
)<uG$!���z�pG�n"�����q���h;�&���?<����������Y`�r�����������O"�������(�(�9<V�1HS���*-*�����+Z8�U�q��![q��QOZ��[,-*����
Y�,:5����O9N���Ugu6�'�z�,^k�gBeB`l
�����!1
r�&&`�a�mdQ��O��D�Z�u�D�I;,%�t9��������{1%���<��HjG���&H���!g%�(���
��9������8�i'{n�MH�_�v����`t���/.���4V�M�w}��WM������?�
������o��R%~��By<�6�g�|f��-n�b���y5��N�x3c� pb�Nfz�2�!M�^���-FT���$\ �=������15�l�������F]�!Y���WX��^n(&�ij@�t�dj9d@��XiR���4�it;����d;@n��=v��^�;��'�$��p�?���H��H��S �Z�P�'�%)����T�
�n�,xRdX��o�23��I��t�3��Wd�`f�$f�GB��������X��\��[��(��������e�x�=��0B�����Q����K���$>��6�pB-��y����C����\hCg)@\�%��'+w�	�&Rd��T9���$������V�@��	0/���>�i��9t��QX$(C����a��P�����_:T��y�<N�;�:�;�<;<�	�<����k`�����]�J��N��u�-@M�5}�[D�BI��P'y��TX1�L�E����\	a$��1Z?&q_�5������)���[��Q.F���������5�j�@��\R��7}K��M��a�}�+9a�t�fnY�������
�q�8����R�A^G�(�u�X��� d�����������bt
NG���j���v��^&�!J9)��n1x/��%'f����b�7�R�BY���"��eN��VP:����feb�
���9.M_j>Hc�r�v�
����\m�5tC�Q�	����Q~P�N�@����b{u��k���w�c<�7�
5vY���6�f� �?����X\2���U�a�;H�^��������V��Zd�.��
~��kQ��������$�x'y��'sur`��y��m s��*�n�A����w@'��c�������.� J�|g�:!�e����E7��p37����[���T���W��r`��Zf���gGm���
��4���4��4{���Ha�(1��������h�.JlVEDx��]c�Dr
`�apR	�@|B��Ri�Y�'o>��E�����>W'�h���4�&��Il����*� h&w��A�S!#S�=V����l��>{������+����<���Mn4(C	?S-��}�EH;�/����HH!b^,��y���$�A�G�BLBeL����M1�KS`��/��Jb=������'bN4[����c	�o������-�<�����$��136��qO`�i�d��e_'�G������1fr�8���B�#MI ����u&.3()NI^O��Ux�z�e4Y��g��J$*mDO��U/ u����6�xA�� y��0$��Hm����3�}�h����b����l��d*���zB����E"!Y��o4Y������s<0��� `�>I���4�@|����U��:�>�b����\{�@{��-���2�A���`�l,a�
	�8�l�F�G/�b>�d�W$9~��Jy��;K�:Y�D�mFR!8	I��9�����2x��_
.���h��\��=4�$I�n�Miop�+�BZ&;F�7�vXf�,k�'���@�n�>�OxV$I���H�%�L���JZ�K�����Y�[�R����|}s�����-ey������+] ���25���;�I
Q�N,���z�0k\�S�3]��s�Z�T��6�>��|��8^>-��6��K�a�g�|���G��b�-U��C��.�	�S$��i����3����g3�"m�$�68Ux���)�,o�I���,<����:��X����D�#�D���B�R�j�| ���������e���������W'��ZS��#-��������*��'s	��O*9��w������m�7�r����!���z�&�������h������:{C��������E��`�KX�|?L�������T�2��n�$C�@���o���mXt�ZB��|��s�6�w�e�)��3����S��h��f���p��}��F���R6H���&���z������+P���W������k���::7����Q��`r��-*�ie��z�Q��f�hsFwQDq�B������<�"pl������jR�9�4s���f����mv�%��n��LBH�����}V��B�=tN�/���<K� �~<���?����;����C���{;���%�3��
fi��h6��f���u���^����k"�.�{2���C�w����<�,{O|�Z�R4���'i+`'����=�H
L�x��'F��B�����������c�js7��<x��i�H�yOeK�fX���;�3���HP���D�u�U����f�l����O�����|����$I����Q��"�I[%T�?]�)��X�!F!��o���nx�)CG���<|��+6J����8.�8����d~�z��E!i7������&��)T���z&.���,@t�"H�p:�A�b�RD8of����"����������(������"1��S�O�/Q��z����p��b#�I1zr�c(�*�v�Q����f�P������6�n�������x�-��9��K���V.$����&�S�6x`e;�R��~�d�5e\��VxJ.�p�9@v+ z� !P�^c����_b�.��UI���hw����m(����0�������h��&��L�)�{`�����8lS����h[�Z�Z���� ��n=�����uk2��]���X+`)�[���[Q2��^���EI�Dfm3! ��SL�J
2�����jb�MA����e:���L�b�Q��9�{���J[�y,��p���1o������{%'�����t�]�c��Q�TW�>�B�n���,�%��}�,�J��S	�<N-����t
�s���O�'x����5,!TvxO�%��,�p
M�����g6�~A`�g�AN�+����:�)K�I�`v��0���+����p����^��/n+��)�7Z:'��P�N�=6P�uS������fs�@	L����Z����-��C�V�'������C�����S�xg�����i�$.����������1
�/�)a3���=B���>��-�:u���f�6w�L�2A���n������a�</5���o��Uy�����y�����N�e:%
 �I.��$�+��A����c��H_zjy�h����v��EkXc(���	�����Jd��_�y�����/U�D��:a��L��n��D�JFFz�
^o�0y��R���i��
gN�?�I�N�-#h�%G�q&�X;�:$~�v�^���m�K[P���|�"7�/�����)�\~�����L����U�\cT�5h�����eh��@W��'�^TB`;�H5t��O��r������,��9����_j���F�Pf��B�g4���S����_jI��<��F��V|��`8�cr��R�
��$se��?IB����Mdj.v�&{����_	I+z��a�;�G����c�A���!��<qMW	�%~�y�
i��`�bM�,}�yT��b���Z�n�LI�%7\M�Y�b�l.�Y�j�����:�P�N��ZJ����"��������hq#��%��@��i�2��pGy(�Z���������������I;���Mw�4����Ws����?z�[�V�@��A=i�v�U*B3jC/U�	���KUu�1������zj���2z��TMJ<E+D�uKKf��k�-���"�4����EF5k
mf�����U-vTR����e�*��P
+q�x?I�W^��q*5�P�,6	���	�mG�zsd��B�`�u�,��aK�-��p��%B�,���u�������TBp+�1������!�1�h%w�����k%�dM��y������8�&o�	��}!�W#9w�yw�|���I\��x�p$�]�g�3��ng^�X��/�s�)y���b�j;���J��@X�������������/�0������I�Qr�!����\�g6d�+�-�fJT�
����`
������w��
@���+�@�t���c*���?�E��'Yz���J�<���,<ni�Qrs��B�mwl/�����`L�L�
�A�&�����UA�K�hroT����lDF8b
��E�^�_���������q���K�r
����`������p�����^"����$��n���.������b���&��a��1�a�|����������T��������mY��_�S ���6R��(v�,���e�O�������$(��[�eM����.��������nK�
�������<'���+~�b@*��h�'�
+eR#��x��
�J>�����Fe������t��G�G������x4���9��{l<���YQ��{�[bu!�\v��n�����;4":
�	�C����h0R��t������H�������4lN�9C S��9z��!>_<*�l�LqUn,�V��[�
:�<��+���l%�����a�� �P�1��F�P��[�"c����^�TP��� 
��������D�}2dE��|���D4tCm�L������9	(�$�����4����q�*��o���G������lP:k]-�H��H�H$����H���=�Y#����:����/����
����������B�%�"���
�4U:[+9[b0<
=��xB#�����PG{2��/�SV�C>���Vx\F���K����9-�����M3��Z�hO+�����x$j(_���TQ*xzuy���@%������4'�T�}�L<���:����Uum
�|�������3a�_�����@cr����T�IN����s{��4�D���c�5�
�Um��/{�(�c�����<*��$P�`k�k�nD:L+LMc
����6���[����#���N�'__*�������1=�T�����:X�������K�3d8�hS�T �0qgI�Dk����VlWR�������U��!~��������Pr+����Dfq��e��n���QK��5���4���f�|�����o���p��%�~2�;l���E$� ����Z���/E|��g��<~7��K$o���DR��N�J���Z�Q�����G��� ��L�r�h��)���`B?,��&��E�'�T}1�B�VBe!"gE�6��~��S�Z[6��0"��6?��T�N����$W����-[t^q�kga��7���)Jt�3 ���q(�J'���7���s@f?
X����I������2��8^��m�A��������de����t�
���mm ;��F�7�8*�4D��������e�@JI��N*3�[\[����K��uD������RI�t�*EX�2�(�$��m��L�=����������f(�d�$f|O.���Q�������A����E�J�$���!� +)>�&��hx�OO\?�� K����)�60��D��B��'}0�g�I-$O�W���l+�K��s����[-�p�2����D������\�,��zA�g�i!�K�����s�2�U�T������i`I��@-������0=��1���an?��PAZ�
8$��(MI�m/��5T�k��M�g^���@����"�E��8�5Es�)X�N��
��)�@�9Y�����������I�d�#���?��Hj��t9��q���Oh��G?��d:a=�d���h:�C����pt�L���	���6�]E��:]
.FBF��XKXx�A��7�'�5no�=�$�K�����������X?C�EJ��D}�����kC(T�	lk|��Lth�E�r��t[���}M�%��9o]Y@��`�	�q	����L�J�Tm��tp9��P�iG\�I����lh��hV�i<D;$C��fF\!j��ICK&Rf��u:� iFl��������h
�a��!�ZH�v�I��]����<����>0 f��2q|V��6[�+������k�M�YT�CUj���O�Z�{�bRE��b���if�yBx��\,eB���eJ|��X)��b���JfO�G�a��>�sT��&z�������gc5Bq�Q�Z����;�x	�����+N��%��4�����-Cf
l�\%�!v���z����(�4��d��)�d�a�&��s2�
�{�D�K4�9g�F�cx1��ho���q�����t"�O�#��i��u���Z��[?{`W	�6q���M9a��^�hkX)Az�����G�x�<}�I��;o�0!�!�\�����f� Bp2�MZ�a������w�4$zQ���*S��#�)a�
Ek	���ZI6�����8�m�����
X� �*p��`Q��K���f�]��G����rv���R%w�HP���:[�%�f�sgI?�wq1Nup5��`wv�K<B��Yek {7��2�(�>^�k���d�&H��b�l�����h!<v�?���m'�{`���^��(�>����A���~��eg��� ��7b��]6��%
������:8h8�8TF���V_��(5���$����U!:�����*��hM���,������f7� {�>�2v���/�]���0�D�R�����\.�z_P��3����X�)�T��Q'A�2��KO'�������Y+!1��b:="�_?���^����5�&
��=�,Ko8�=�i�����	���/��cR�R	����y=�C�)�
��_�'�����"31��^#M��gA�r�A0����K���@	�%�#�����l�^|k���g�
%��2}������b�T��1yH#�&r�f�)�G��~�f��4:{���z-�xL!�����^�)�C�gp�-��3[uh��P�g%�@�K����k2�X;��qS��"���f��F�v<1�wH����3�DLQ	X?\%��h4R�&�zz%��>;��a,bnF��}�������r�[�<��=sTT&�cj��h��2d2.����!�����O�(`}���,(����1�J��v"�	�eqL�����L�e�s�z��`��2v*pv8���2�*)���0�W�VSTj��([����?}�QJ;��<���������Mo��/���T6J�>Q��P��������m/��b��Ml>����JB ��O��a"��,Q���D�
,���@w�zB��~�k�K(�l�T<��������IPp'D}��T	��1��UB���+�7����w��{���5���w���tS�N>����C0W?9�g�E	�)=���,]4���N�y�5�����
j�K�^ex����������������������}���F���g�vU
,��(�������U�F���1:D�S��R0Y#
7�����������������$��5�z�?	*�
�L�F�{An`40:��,���l�4��������#%}���	��JK����*��g�����	I�����1����N��!h���1�����^)��E��M�-�8C��ob#|�i��o�6t�.%��sw�_�g�<@��� Q�-SSt��i���DI���k>~��������I��
K��T�e�'�/�| B$!�#47�E%�3C��g
�c���2bg)B�0Z��jW0;n@�a �*1�s����P���S�{n���H���{�[N�M��1*�a�&%���?/6S;�gn=K���J�C��
�a�B������Z�4������?��<��)�5�>��W>u�W���|�5���� ��V%�G2H�\����LK�b��$�����kk1]�#3�����^�*���Z���bWI���|����vTy(y��9���QSx0	���Gz6J��{~�cpBF,�����X���8r�A����_:~�p�����K�R|&dg�eV�����p^b�%�l�@&))��O�$+��z��9�\ !�Y��H�����<��e�B~��<�<�rC�,���x]�H��)9������Y�������.UJ'�(�m���O`���;s8W�G���e'�����/�Z([��Mu�wU0$��"�z���\����v>k������pC�2H���rw��sCu��p
h_�����Ts����@O�l	�����y��:
�����XC%��m(Wl�>����1���4�;a�|�����`]ifs/�V���:}���g�9
�v���A����u�R	)~�kfY�?��K3x��r����aq��B�kQ<��������)�.<������[��	������D��A����T����L=sUJ�d��T@��C��h�78��4&���JaW��<%S��,P�M
v7�a�S�����8fH��K�J��n��OpEA_��6�XX����������o�m(�W�����2\(kc7r�%X�������������JX���J�����;u��
�qC�v5�(-k	'�6��B[�V���pTK�������z�����5O<�qy����g9[�
�ydX���d�w��!�	zm����Z�p�2�1��qe�I�*a�����~D��������|�J������+TB�w�k/Q�HUBtG,V�M��v%Q�r�x3hs���b��D���[���K��oK�����3��,����
��_�����n�9���� p^C��Q+�$#��3Lgst?��SIw�s[�{4��J�j��[�c3K�y�0 5�����������Q��~�b.M��`b�Q|v��C[w��h���-�mE�3�,���Mm��
:$��<T�H��E�����;S�C�Fygtk.,��;d���mS�6%��BI��@�bh��Xj�H�2i�MJ����������{T���"�Q����U0��s��s�e����$a=���~,J��zt�ZT��ZZ���K&.U�����Z=-E���7�.}���T�z�F�7Z�!)O�9{�1u�A's��F�L�2}!s��������k(���.�����-���M�FC���t�����5,z���aIdWW�A��K��Q:�}h
W�x	{���'E��3����14���[Y�����$�l�m�oC+�}�Wz�#\�����K"�4/v�6����>8�D���m��Ipk��m���\gd�h�J������`����;������2,�4ll5R_!�)��_����/�������]�����s�����s�.�Y��p~q!.��[��mz��W�������`z��!f>��.�?�F�4��@���<_FqY����t#�����@j���6�|DqQ:������$})� t������sJv2���;�J
�cT3,`!'~=�(%��&���4��.C��I��%�&�H�!���:-�\�[B�X�x�@&}C����@!���$`��]w"�)#���xg�_1�%5�bjx�T�[���L��1�R,u�c7�X���4o��^�M������N�'��Yk~���`�8����#�5����#�)�5���of�"H���!���so4��bji��w���&��~�t�Z&%�L���U�Q"�O��H�������F���a����-��B��RS�����\�T�2�i�2�W��c3�OJM*����	���z +)�*�R����_������ �\��u7!F��Px�]��_����"B��@��@���h�>@@��#��>DuRCC+t7M�����5�E����K$���E����#�ui:0��E	�8M�7��D����K�DZ �R�'@W�>��Ih��E���Xmt:vb�a*6��
D�^�xJK'�I� �u
�{���Z�g��W���-�:#H��0(b�'��r�a�dP4���W��P�vgra68��_C��vx������A.�L�WA�Z	+Xj�O�X��M��H��y��*K�zY�����OY�Gjr���)��Rz��3�i�2��dR5"4����U���]�7���Q"Qz^U���ar����R�{|�
&�t��#�'��_-���:����X�ms�~��a��)-�KI*cAV"F�y�{����u������c�0y�����6@��������wt��>{��<�o�?<�^��Ul<���&o�r�����I�\����v�5�ncP�.
�a?�t8Y����~����oA�tP
���v3���`���?
>b~|�q��0�Xl�~���jjU��tN��'���=<vz�A��n*�r�@��w����\�G��8�O�O�h:C�����r���`T������!�`.��	��L��a���krE�'��C ��*=
����������P������o����{��8��, ��;e��W�!����C�����q����z��`hX~�����%�=�����P��N�����Cxq,D�RhWt�m'm9
T(-	R4�����Vn��cDu.A�������)�	�D���xDG����� ��P(Nr���V��nwS�m%~Hq���������M��j���{t	�,�n��H0t�#9��!�a���R���^���i���K�������>�Gz��
��{��vu'
�m���9ZV)Ds����?.��Ou�?e����Gfmq�Hd�
�L�Z����~��#��2�9N�lAt6�����fH�\V\�y���v(���.�#����7^�_i�=9U<�N��{h'���6��r��*2p�1�aqY2��8��D�}�M�B^�lyO�kf��RY�5���������*��|6JG2��QD�x'XjU`RlVQ��jM���"���m,��(�8��fq�;�8R�s�9%��\�`Nw�IF��XJ��]�7��O�-62�*���P'�|;����~	j�hv�N������;�����O���"�B!������U�����S%�6.�p�tT���2.���}+�w�d����{��z��<��4Rc��������l
@���g�9���gV����g�g2��f��ad|�	�W�59�2����nue#���|�=xyu�����7�����A^���CM����@�m�bR���$�Qx��1h���=Z�D����@!�}�<)%�1�l0!C:�����pr8���7�������!h�Z���I!,")1��'���Z�dy����������g@q���(B[���(���]vn�GU��[O���G���?)�F�a?+�-�*����Bl�t5	�[���agLi�gZ�`����/����Iwy"�����d��#91���tyw�������������28�@�[d��F����MX 0I���:k�r�����V����������6X+W��X�uy��rR��5.���s�5�����oZ�r��~��Au�.3[�U3�
�������U{����@6]H��g�>C���YM%l����+�S��V������T��?�Z�@��Jr�
�1!D�XE[��t����{�)�'X=�|�|	����:0zI���[��D�����Y�F�����W/h��k�$�C�h����X7k�df�W��wg���������h��Za���t0_�pJ��g\6�����������h�+@�����3�bL��6���"s�c�mp|A55���E�#�P�HTi�:��A�-���.��E��i�'�v�l +���9�0,r���F�qNbok��--�=a���9z7��������������HE���DIt�T����T�)��\3�*k��K�3��vbQ��W_�\B�u����}��2 �zg5v�����'inB��oB�
��_o:�gkbF�~$�����C��f�
f5�����c��YC�����\�?A���
e��@����:�Qc�g��>�w�n�m��~"��!s[)��i��k~�50��~?����z���E�����o0��u����d�E2�����_:����y�M������L$_Gh
��
��Xr�tb��U\=���$���O�
�%H1	�b���i��������wD5��A�������F�|CA=�l�^��{���Q��H�2��=��3Q���;R��~�������@4H�-��!%�;���9B��Dz�6�Fv��(�q�a��r��#}�<bW#��'�E
xav�%�:� ����?��8E�	�Lz�@��h��\(��r����\'j��eO��(V�������8a��Y@����g )���U�^3�h�����IA�@w���a7j��G��q���v�H���A�42����t$C=��[���8<0��	�:�q92�/'�N��1�O%@F�su�^��]5��P����pL��^8��G��RG>�`�\��
�Z��8�O����1`�%���$�,JBR&USj5���v�	(��Y{9��#���m��\#/:���5l��;�q4��w�eP+�;�8��y�O�'�����h�������|\�El�y`��ou�Br,V����������Xlf(�w���jI���G���;�&��TA���h�=���4�N�-*g��;��uC	D�� '����������Z�q2�7Aq�@�r���H�/��W~���'�����<���EB~��V�A,��gRzq�����Xg���
I���E����^	����:�`x&�<��)I�]M�f�~��!=Lu"U!��ALT�g��^���� �|;n�bR2�Ba����m�\=���7�ohN�;��w���]��?��g~ss��xjj���j�W �]�w�J�]	����������a���y��+�Y���q1V�3rZ3����yW)��Y��C�x�V��K'��tk$Gh�GRD,T2lK�����n��=#%��5���@�_G<~�����[��_
�e�k���U.�z"'��������OHWFLh6U����h*��4EiG�����R���OK���6�)E���y5��#Q6�Q�������jQS�����X!�RA�@��� ��ds���&hGr��Gfq	��/� =�����
���������0�	��v
��
��&xG�c���������qdU�)u������y�=og��.lG��	�{9L(���6��	��QI#{����bG��~���De�ce d^�Z����B�X�����4�X:F0�r�J������� �=c��;6���3z<]�P#�]!�=&����pnA��������a���(`���)�%����1�_N��0G�~����r?}�K�!+,�$�����4�����!�����(F�
dD��(Ij3���n���
?F�R����S�:1�����$
`[�w�,��`����d3�����&�R���0Z9��r��:|Ab�pb�/�/�Y���$7������.%��<J�����n�����f9��P���x#��
�H����������5������4��w����2:���wke��x>�+9�090�c����R�����D�p&X��|LD?�!�Q���:�qw�a4�S~y1_ii��7!5�
@T��EAI\�o��/����p??w��#�!�2��y#t��U�)��� �_yR��nQ
�6����A�
��EA��C��qmF���3f�D)'9i\�g�����8��u��^��kV��R�k��F������Ba�w4��"�B_k4�}�/������I��0�J������z�����W+����[�+���@<��m|?���`6![0��o������F���?���_:�n�#{���b��;��F�dN��]�T�n�e����1�e�O���J<��p��'}��N�@������V�p��4�5K�N�P�*�z�[�4Z����cdM(���j��M�5�3�j�'Zyi�Ly�f��=��B&�������ON�nn:o�����NM��L���?���CnZ#�Bo����`��+�
�]y�K�L���d��BW��a;��p��D�>�/�����y��5D=�����e���$PU�������������grmh_Lx
�L����w�>b>fd���{�~��#"��N����!e�`�S�9!v�n����	wk7HMl���7R�'��[���\J��p�X'h�yh��������)2��M�r6�J����������4�
�����������!��D�)�!k!�M���g9�rS�=�������Q�@1��bt�m�y8^�
���^VO��d�Q��f"��>^b�����8(����
'-����Bm��`]��T
�.+�z�r��B0��G2)8��1%n5��U��Y���=�/2��t)&�ab��L�:�8y�@����Y�������h��:�n<@#���^����.�s�����t7�JH<9��������s�~h�B���l�Se�I/����]�q��D	�-�Of��_�A��h���:s���O���U������v��+���@8=��r�	��"��(������2k�D����t9����99���I��A�  ���
�8F��*�K��x�u�*d!fm��Y�F�]�D��?���Y���J%:5�����;��o������2G��5�s�a�K���7��s�3������I��
sD!shx1��b���_0�t[)��+D~��;�Kn�=+�62�	���f�k�y�8��1`�5�5�Y<�(������#��4������.]�q!�Ge��e�'"�S�AD�������2������NqS:uI�,6�t�M���2����V'�4����t��kJ��wx;����!����]��Fv���y������h��,�����a.�����3W�|�����<Y����E������3'��7���o�MH��5'�mF�y0�s>!xs��f����K��Nr�v�7����2�7hiq�����3��D����f�,|����LB�#3�b��.e�T)CIAI���n�N������������"��b��r~�r��2��J�D	i:�:��[C�'�M����
~�u���9�o���|��5C�
��7g���7p���PIi?]s$K��P�����jv����Y�14T�z�����5T���Y������N��O]}�~�[5
:Y_������w�� I}��F����R����v��.}�]��i��*����`'���`f�	���AR�?��]/��\���F��f]����YeG�r7A��O1������aOdsY��h@�!hS����3�n
"���>'�Q�E@�j��E�^�+Y�7���z|7%��%G���������oo�N��%��'�b�j����v:���#;������)�+���+w�,�����]���BW����'��R���yu~*�vdR�[S��'	R�p�	�|�	p����� R[�4�@/���
M:a�\zp��c��p1�QSJM�TQr�w�/P��8)������� .���/����,�K����c����_�;����B#�Y~rW'zU��u�u00l��g�`������_��������yqO�E8Zc�~N����o�4C����a��7g.�z=D+rF����O�J�oP��n�����������}��i'd���qpX!#�]������v��O�U5�E�\j�@O�>�ZSn[<��E��M�Bd
Z�9A'z)~SI������6m:�J�Ke_	�?U��/� ��|e��b-_1���X��+�+�e*%cw�j� ���S�`��B����+�$c�iL��pH���>��}:�M���E����n����f��F@��F���G�����}�%��T���H�q�M�b$Mf^�8,�i���[�
J���������5�OI\�*����'��|�3��8�;M�C)��3��l� �Q?<�;�8]1���; ��K_�*���T����g�9�3G�=A���/�T����#�>Y���H�r���F�]�B�~'/9j��D�����s��[	�r/a�F���S,�vo9�#�1~�-B?�O���^���3E���MH�n�$ i�q<���qO�bvNZEl{�A��Nf��*��V0��Fz�����9p��Fb��1���_�Hr�lrN�����b�����F��H#<*�3"B��c?0c�WyA����&$<�~�o���dY��A� P�3�B����0ic{�oa	�#L��:
Q�:X���)w�\��NEH�n<���P^�XW�����Z��J�����Q�lM��������������$�����Q��>[qRio�4�S�f;��L�1�1�Gma$o�������]��TG�0��������LV��!������]�F�J*�Y�#���h����/�[�
��00�j�����W�!����9W�R0���"�������U%	�*fW]R�h��� �42����	�)�S���>�����a���������P��!���o��$2�<��>��M�([`K�����b0�a6��-X�O����!�|�&C��q32�����>
�0����ht���#va��p����3pp�A|��D�����T	���q�J�A�G����������0�M�Z��K�_���SW! h1$��z��E��S����OR@�/eRB��xl�3t.^�OcS�4�Z�
��I�awY
ax����)D�a�<���06��SO���z�#�Y'��/S�7���c��R���p����s�\��~�:.�����4���?�^�����S��	���1M�tHfpy�@�)�jUv�L�E#68)h���,)y��G��D	]��8�"P��:D�/-�����)�%��w�7`z�&,�'p[b-=N�Tb0>{3����R��ZH���r ��Gn�
g#`iF��sY{j��������pd��E���*J�(s�^<�\�,#���Q���i�>E&��m  ���O�s�1Xf9�~p�dhE $���81���1��������4�\���n/5��~t������3i��W]�&s��x,���N�1<���Y����|��R������,m:����_6��t��><ljL����Gqi�n5M���&��$�fT���:_LW��SfBX����c0p�7%F
{Y��4���"�%^5���,k���c���
�x4�t7��.�/2��0c�<���F��%�	�8�2^���b^j\�i����x��&u]NU-��X�"
���2�)�b�w�M���=`�K�A��JIJ~!a�Q�#c��qe�+�J,{�!�����dE��!5Sj�<��BVi��_\M8N&D���]�Zb�Q����������v�����b�h�V�g��8;��u��%��*���ND?�$���s��]��h�	�����	���������gc�����5Z�n�fu���d��S�r
�s��:J,���B�])V���������c��C�K�r��T*5
��@���p�/D��p�����pR@���� bW���f\���|������R����K�R=(W��T�^i��R�^j��WP�M&����x9�O3��b�A�{L�~�I�����Y/��^�U������*Q�[+���V�Y����A�^�ou��r3(������������p4�_���]�(~�?�;���-
&���8��o�W��A��}9	��r��R��j3�/�����e��
R�6���18�����(��"P�
�����
Rq��>������[���O;������i���,������A=�1<mrjU1F��,����'�H������t���xwu	l���ZQ����\��J�f�1H[d�������Cv��>(��}�)�%�
�O�W��M2�!��1�s`�'����1�u�`a	�f����k��#���DZz���9c�[�$�}j��V�i1Y��n������/������>��������|#<80G+�=����k��hJ��oe0���c�_�$t�](���]
W��\�W���?5u�<yC1�oT��N�!�� 9r�z>/�h�v*?"�������]|���q����<�y�cX�-s\|��}r����g��d�<I1����������u�J{��'C�����
_!�����9>&���mT�����WY�����>�>�������)��[A>��^�����5<�m��|f�R;b�a+�n�)��~���I%�.cW4���:OX�y�1o�-(*]���5E'}W�;Pb8�J�xoy-���:;����1��:N�������H�_
~�5F��<�a���l�k�J�AcP���5�����^#lD�jk%��n(3>�� !���� D����zT��{^��b�@���o���^N���I��O�c�]�#[����� b�n0vy�r<�X��(��2q�g2]Q����x��������n��z"}
y_�'�U���`%}�tt3]+���(��4���8�:�C���!��W,U������]���������3�gh�=�Q kd��Y�q7K��}Pm��<��z���F�xd�U�}Q���_���H���G,�$�G	����B�=��00���N,�"���1[����a��}�d����t�`e���]��)(x��(x��(x?������]�I/My?�!���&)���)$������Rx+������x���p^ ����bs!�����bZ����J�l�_��%G��(�����-�C�_����z�r�<����FXnwK�j��k�Z�R�Z����j�J�w{?�q��2�n�qb�v7�����]FA������T�-��-UA�*yt��q�8!0"��I:"N�"���~�����F����,��;����OO�C�2�lOg��������8�{!���'+����p���L�C��������/�����J�(eT9�F�Z�r��2��x��~�QJ�J�Z^�d�����
e��lD�������`�k�^��LWF&[J*"�e��i!��8�*z<F<�@�~���\.�3'���hI����&&������i�� n����Y�;%�������vFUn7��O�}"����ws��!�U��v�@��+���u������&�!$�~g�w��;�K��z?�:����������������6������n$�@Rq)�0�>���n��YP����+7'B�/h�=$�F�?a�5u'���H��������?FsD�'�g����!�CB�4�>�����3�@�M��
E�[��'���.��aT���a�!����p8	|4��+�*2@��g�G��i��#!�#��XT��>dA[�1�q2}8`�#��Y^}h�3�L�Q��g�K�Ss����v)�.00[�K���#�-��I���d�;Q�2GW�_�Ck�W�����t���\���o����{�yy~y|���|�OX�IB%���4*�� ���}qru�R3,��Z����Zqvq)��M�A6Qg� Wl�����va���R��a��F�	���r8����Z���_�UH���}M�gb��_��gA��4�	3/:�``(4��d*|�����':�
Ow2��d*|��T`5��'S!��TpO&�mA����&J�?�w�d"^E�����y����W.�����	�6����mx��������*%q�6��Zm4�%�������6z�v�T��QX�5��j��nv��F�����J�Ri��R�C���8�X�+W����mU��WF�O�f@���
L�;�xli�����"��a=<�!�����n�r�M���#�w�k)�A�Z�*�@�7��T��1� ��������mK��4�f��8���>���y��3=������/��viD!���C�W/��!���p��>��O���6��r*��8��q?��J����q�{*z����@��P� ���n@�d��Z���x�"��m��C�&1/n�����Q�E����1����#Y���#�R�X�1�d�.c^aKD-��	=���X_���u�C�W��tC�`��~*��eC�6�_,f����E���sT����BJ�Ra�?r:<9�K������?N�J�����]�������O��?O&������?�{��T�kU?}z�K���Gm"R�;�\0q���c�/	��-���s $�}���L���8��j5�V,�*�B�X�jE1�D�f�:
QO��`��/�dC��/P�%>��M��"������"lg�
�Z������=d����H�6�	�������Eu�����Z�mO�;QK�?Q��U����'YQTk��z����=���eY�r?��N�D�D�Szz�3�''�>u������YEs�H��b�y�J���pf_'��<��Gb��������5��PW�QL+u
��>�p�������]{�hu�����4�9}�9��]W��m�	�F�^��y�N��1���<��LW�"����_M�����{6t����l4$�!L��)�_Sz����x�U�G�*�Y1��x���������-��$�����@�ZD�!^��4������fv����#(���\w#�(M�Q����|���<���8��j~��z�����_}_l0�z=��]v��������5������s� k�[�����'�S�������JLjb[�5�}_-������|�]EYgCl�Q���	��c��4+	#M�j[i�f��������G&G�R��d��|�N�������vS_�����[�n�������W��Ay��u�����5�5j][eT����+N����g��������"�n��	��Z��'(����u//���v��������_^�+1�r#;�X�����g������	�6@^�A���w�=H��~���!�-x����DBM�|s{��
p���A�n3��-o��&�\?��	,�U%�j��A3l5k�J�X�7a�R�����j����'���
��u�������Nw���J�����I<fe���������
�����=����DC�O<����>����}�pV)9��!��F��]��a	K���8z0�ca:���~u~qvzvs"F����(��Q��S����=���<�Tf���X��E��s�J.��p��	�J�����~�M�����d
MN���U��y�S���9�������L����Gp�3Q� ��`�	�>
������Z����E��i�>����w����;A�g��U����7_�v
�)���b�_�\��������������h���K���m��]hV�aJ:�
��]�����8�7�����Dk��Q8�D=�*��+���u@V���:��QT��j�z��a���U��~��N�������|P"���!����uj�]!��w���m��}��b�>��q_�����������u�����/.�.���WgG�{���4��
 �4����������wW�������^����m��;�	�	vf��
�.���`�a�ySH8$�\������6B=2�u�uQ�r���[`���b���� X�N��X�X��
'�@2"h�5�{��������:>n�7�<4��?��D���+G�U��M�
�����{����>�������}����~������&G�������<�C�^�Q
�v��a�F�A���op���8�T:��p
��?-��������D��1��"^k���f������#���pV������}������3^����!868���D�NGx���k��9gM��}�_o��nH	F�Q�j�^����5�P��5�c(hc�V������
�3h�����g7����q0��j�*����vE{f��E��.o�_�.
8�*�$'�gG�l6u���t��+� �1	�o�8���t���r�rP�K�T���������Q����
��R��mv��,~��S�f'b���*\x1	��"�U�s���A��U� �9^,��Y!(_�q~�b1���_�����d-t�,���8��6f�kt�[�>[r����b�j�V��^��f�`���&��G\���T����u�v����O�3nY�
k���i!H'�����,���k���:���B��N����UWw�����t	�E������j'�g-'t|3S�DpO�Tv��n���������wr�7�r�����`�����\x!Z����8Fb}e�����\��r�Fwf�+
n��>>���r��?�B���Q����J�x��B�10������6�=-���<�Rb��]Q_��
�(c�"��x�����!�����gju%�*��'�j$(5*�5W�j����=26|��aa����L��������~��|5�Ix�>B,��.�����@Y�vK�����^��D���~�.�3�e(w�Nv�}A��b9��XS&��9�
��%Z�5gk
B��#�/��}�$b�&w�`t�*�^U�6*)��S����t���^�r5�7���|U�������3!4j���!��$�$���^�����=�}�r:�6�����Fq��7��Ekd�5��s��x�#�N��S��v��O�A�������
�7��q`r�tb��/��!`���.&�d����*!�P�A=����~�XKa�U+�Z�Vh�j�T�J�]X���}�o�J�E��wpM/��a�9�!�b��O�TC/H���f���|��!U���;�O^@� ���[���4���]���T�N=�0�
���>���L+�,�����#x!�H1J�]���r�5�>�E�5��2�8}��=�2��3�)L�=�UP�	�S/l�[�I��-j�^X)�)���j�_�7�+���&J"{Fo�</���<�4gT��3���E #��w>�:��U\%�z���p�+S��~�r�����\�����>�`����x������"p�I���*H�e�g}����E�\Z�V�]��C�]�w+�J�"N�J��j�{�r+#���X*-��;�N���GA���+B#����{�0��(f����~�}H6�����C�Gg�y���?_D�rF���w��8���"���b~����ms@7����b�A+����?=����h�� �R�i��3����!�h�1��4�����l<#_�g*f�����_�:�Y������������>��@��PL%�A�W�_���?�/V�^`F����_�%E��)�eb�����n��o5��bw4�t��Tm7�A�vA����s�@��@����1F|"�O.���r�2���XG����Cv->�|u�K<}�$f��t2
{�����$���S8���,��:"w���-s��o�fo�f��t�(��3��*S$\���=�����6��>��r�<5�����O8;^���v���n�A��v�2|5%�^���������[��Sr�H��b�gx�f����i��Z��&�����\FxZ�
N�w������+@H��I�x��[JrK�n��n��%q=F�F�6����pU{�'I�$2���Zg/U���l`P��^���Q�q4�G�j����c�����;�Es�#(�!����������Dr(N3����uR���R�n{~�W�������h�_M"��B�������6L6�����p���{����	��B��)�)��eyn�$�{������Q<�C�m8���sP�X�}m�3&u#9�+�5��%pu���
���7��u��������\����������S�F��������suy��I����&�C���9��%']J���x���1��h������G7�����O��!&���`����=1�A*r���nbWn�R���������S&����C1�O�N�o}���z����f#�*a����q�W�Ze�.������W>���
�G����8�w:.E�O��X�o��������r����fGyF�[+�D|�@&
��0"��Nq�4K@2�7���/%���;�^?L���9����.�4.bgBL�~P�O����`B�
��Q��b^@�S�A4��9�ys�V�����R"�O]FQ?�p|�1�8e�3�1a��z{|~���l�*���w4�B�%l���[�����Y��?l6��F�u�b�5��f�[)WW���X:�[�`���J��i'^�i�|�$���J�T�?=���Z��b��.C \���� �>\�
�pt�b��tA���O�7�E �8�w1@� �&�r�x���ML�����?�Y�4DD��h����[y�y;&����'�9��	A�:��
'���pB��n ��?�I����^y��s�	�~-���qo����x���_Q.*�����5Y��I`&�Aj��T����EcRh~�@���=j���N�j���
7C;e����A�V���5�0X�J�\�`B�i�3E�f�s�d~�I�?n�+5�����,c��sM�:�
�0���7� #3^��	1�%fr��>���CR:�YA7O�Jr&��fr����9�9J�.�\��/.�����]���G�s�D��
�Op`R�fn
�R|j�n6����k~��:�p�{��������L��Aq�����d,d~e�4I��Fy-�i��m
���]���EF��
.�g������H�r�~W�=#���n8(�w�����d����1Y���5��%���y�Y^�z�����!��O��]��n�_����~oP,��Z�4�t��:���h�(D���O?�DD!�e��Mg�x�9lO0���d���	B�
6������9"J��!�D�SU1rM�H�����(:�f�x�(��v����>�V9��i��6+Q[����T�6��j#���-f��]MrUd���w���\T�'��-�n���s�OU���nIW��2�q]SA�U�2>����wt���������F�uX�����9��,o�{2���N�����k�d����Zo�2'+�l��+c��(
��M���yi�)$�^�Q��j�nK�~�z�_�6���J�����$sJ��d����B<����3'���U�O��c�<zNT��H�����m���^8���j������ne
���r��3��^!�������=��8�/o?�3c��O
 ��I�Ud�����cw�`��������sy��_&��L
�K��������
[gC��i�z��������&�s^�X��"�R��^���z���R�����9(G���D{9��r"m����F���������t��� ��M���=r������L��hl]c����U����:�`Os��)���jtz�N;]���=�u.�.�����0T��{�W+"�PV��vP�ae�iW�qn�w�%�I��V��k�RoP*[�r{�hU�9D*O��;�S�X�6Y�C������7������-$��?(���|�a2�����a��)��t�%u�V�@KO�2�w���k��V��x�������+���i?�����������1:�[�5��,��-��n��a�j���b�����AE����*�*Q�������3�G�>_d{�����|2��J�q�t�G���������k�;f�j`���C
�.9��U�mU���VB[q�����sr��6����f��Qs�
G#���X��4���?o���Z�m�~X�\�n�����C.6��������%����� r c���
����$�L��p�]�!�)��"���H�
�f��a�V�tr�^	�C���a�#��#��{s��+��6W��_x��o@��&�t���zX��0�[&!S��'�+����/[�.��k�X��e&J[��i@�����/�{��e�MWjU+��Ui	�R����Z�T������R�9��U!SlKU��W�]LX,n�h�{�Zw ��TN��6Bp�����wQf��V���r���h���h�gLN�S�~L��-9�MO_�����x�<
-r���{���4�'�� ���IW*o�UJ9/Z\vb���X���6�������3=��/��?�b���@@s
��^�V���
�a�_����P�Y��K��^Q�G��h��U������/��!����`�CnaA��p���������Z�3���;F�VJ���������T�gKp��m�����������z� �����'��$'���Y����������h�k�8���f����r�S�m�:�,�G
���7ftQ�_'��>"��0���#��)�����nmm
F�]����^�|����C������DxC�zi� <��}�9��uo^��>Hm���K����a����CG�����+�i_�|+�3HC��Ckp��C�	H��8od��Gw�5���!����l�G:����U��������#�!}f�L�b�}9����*Gt����|1~�#�Pp;��P����k�K��s���j.\/=�2�Cme������������NT�8r���NU���s��^�M(
c�w��p�U2v]���z�K'�\�����Y����?I)O��(n8q`w���J��2_�R���D>��UggX������g�K�$)H6����"C�$�v��
�����ox.=���� H��3���X�W��������$_���������D{�>����#>��UB4`��x�i\����r���@�lX/���;�������[������J���|���
�_��u5K����
xi�"a�i����d�������y�����\�K����)�f6�>u���=��GM�Q^��l��\znW������l�a%��3"w@�p��8�"0_�n������w//�N���9~{��>����� �X�Z�h�QL?w�;�G���q'��!#������M;tz���������4��Fy���1��\3��Y� �v�
S�����y#.)�F������	��7P���������$��)$x��P�X�*�u������+[�b��S��j�"u���p����>���zS��f�@*��-E�,���_�_�e�&Y���OQl����_V`Ok�)�o�<L���O��[s������-�*6��0�v���H����w��������� 9X�f��4B�c�J��`�>�4�J���/2#���
P��;>g�-������*��1MS��&���8�}����a�}�������"��z������"�n����&%��j����b'LX���C��^j�\o��-��� 7�k[.C.��].��&��TfN(�5/�;������/{F-�e���FC�r�����I��OnL9,b�����t:��*z���q�� VE3�y"����LP��!�S�!�!yi��������
�c�W�� ���t� ���;�3[	�A3���7�J�,)��[��9�J�J�Xl��z�����~�6si1Ui"�z�6U���N#Ht�����v��$nf/����q|������q8���#�!\���"��jO
�k��A��P�
�L�M�
����1����R��}Y�TK���1\�|�\����?K���	����w��ysv|zv�yu~vq�;#���j�d����5Ng ��?2�&q�E�iVk��J�����w7�x���5h����<�WI��F�����Sg��
��Zk6�~�Z��3��[<64�9� 5�� %E�2�$
V�[,�/d��
Z��{��+^m���T*���=����)V0\,�t[���Ld�>0��c�A���O�)&klLr=�w_�9��+��@�<���$w
�������A��JIi]��T����,�^�;�3kB{�������=��%�5(�I�a�S6� xo*t�U���J����O��&`B���!*���������TLmK]mw�������{���aW
�j7��b�WT��n)*7��ze��n^Y�1m�0T��d��d�r����EM��h��JO��>\��V4�tx
/��0��H��.	��Zq�������B �������<������Ag��i�����_����j�e
,��-�sqX�����11%z���k]�S�#{�d���/�"A-gm�}�����#���������w��o�w���� }4�s�sB�<��T[���/]J����i�)G�(R%}���fxJl��=����bD1��W����l�Ks�v���F�/�Hq=K�T����4&&#!l��7�)�3�A+�A�(.��y���
�,����L��M����u�Dw���
���>���p\A8�(�����6�������n�s�����z���6��3� &�q�K7�p��c}��b����(Z��8�{��4�
t��5*O3��;�f�����	8z��r���s�"��S0��#�UkU��0�X9��8S:�p�S��j>�����W*-��C���R��h�0���o
c��]*�	�|������s��"�����;�����x,�x�'��j�'�pu�?���?�x_��x����,�ff�E?)�{��-�+h^��S%���{�u���Z�As	��]/�{�v�����N����ej��V��dt��7T�GB�A�����++�+���:5:�F!r���x6�h���

7nB��ET\W�,����N��:v�<�q9R���F q���QgK5����	�GL�A��� � |�����*�]Q���b��q�A���&����	��t�,nd��_=��K2��Q��x����L�����M�L�D4
���3jr����A>1��SS�K�&("(��%K�-�c:��`:8��pq<S$eD>J���C1���82��[e=Y�9��g��a�b��������Ag�
�1��R��A�Y|Z�������-s�ZWR�i�X ���d�yT�r���~yr|{&��b���3���f�Fc�}�i���uo�=�����y[���I���xyw;	J��KJl�p��1Sio�Oj�|Ed��n�W���r�Z,���f��j����������#C-���E<�����YqY�m���������h����4�G�}T�&V/���R���j4h�*�b���JXj�3P2M�ffq��� $���;l����L��'���y���M��Q7��JX�N��A�X�+H�?]��Y:5�J*nf��V�=�Ja��BG�&�T�{���!��5}�Cp��K���������c�I��j���y�9&�����&�v������Eg��X�O6�~��U��tW&��&��.�{�uH�����
'7��6��u9�#1q��=4���N
)��U+�JX�V��f�n�v����2O��nl�����j��&�E>?''B:]�Q$��ys{���3����0����8�d�?�h�h����}�td�dp��8������i�����^8��;R��>tZ�V�%6��Z_0V���nz�Ek�W�,�L���S4B��C��)X���|�)��O�������q���{M�c��<^u+[�yd`�����n$3��wj!,�@���R�dAF����?D�*���f�Z�]!D�W\���Vk�J=#�MZ{���V��z��z��L���e��g��%�������h��^j�,3BN�� �x,�h:C��:�7 )ko�_��#���I�H�k�DU��0�'�ue)3TX<p=�����b+B|������g�C�W����M�h���J�e�����7Y�D�O�c�Z��"����u��M����I3M�k������f|s����b.�������]3^�~� G��o
Kaf�|fC>}�''E#=�8j��v�C}�N��^ex��J�T���������i�fKA8�m��m��T��5Z�A��'H&�����-���Qk�uQ�H�j�����nZ���IoGL������vN���������m������T�}1�������'p<��?L ����������P��
_x��#V�hz�R1:��X�~�����4��C�,;�������������_��T��cZ���w�-�-��NP��$Y�%�B����BB�O�AO{C`����
����i��3����	j��N�?a��[,������~�����G�� �u�8�3�Q�C�L��
���o7jMs�|���O�;���������|�u
AY���D�a>
��NY��Futs�(�1@�Vl,���n|+�|��F������Ln�6�j�'�0��K3pFM�c	NaW��pBW���J�>�Y�Gs�#��l]SU�kuP�$��h:~{}|r�ywus������\�U�yu���
��]���]b�]{Z�bXAT��vP.#2�8m! 	��X���DIQ`�{�P&I���p��8�o��d�(����F�R*u3*u	�ni�[D���
+����@�%��b�9"I��-K��/��'���BA��\B�;��������K]�R�Ve���8^Fl�ET}�Z���V��/$Z@�sH�,���sx0��#!�-"!����|�`�#>,>�8d���"4\2]��M�^A�[���vG�n�NU�X�B�E�`9�W	�������(lU�vE���F�U��� �8j��R 5��D��u/��#�zx^?{���n�X5�i�)���?��	T�O�~�*_ �����y/G,+�I8�s��w�.�#6)�,&&�@J������r2�
����e��-W��Z!��!��x6�.'}3��V�����X)�6��W�c.�����pT����������9�<��
63u�}t���J���P������9������n0����������`.7��A������f+�T���^��>�������Hl� �_)�\��AP��5n��Oud���^�0�89$?���:~rg��1�����!����m�v�>�QH[���n�&R�(���������5]fk�G��Y����@�8����g�����g<i��y4������+4����AsP���~�X���n������(R���Ui�Z����O�,�e/�/��&�T�!���]:�/g
��K��!.��&�&w7����QXw
�a|9yk�&�si���N�^�]���g0!�m2��������!��k�)Q�,M���L��/���;a��~*���2�S-�����V���^����a����tK����k;vI>�q{��h<Ek�J��O>/�������m��G������W��>].�N��-�9�/'���#��yx�e�G[�����������q�=����,�A7�?
���o
{�Y���%�^�?�/;qt7�z����/�3�A�F�Wh��grO�D� )E�rS��H�}p�������9�g���m�������������H����=�����=c��!x���2��m��Jq\�*q?mY���
���#�k!���Y�J�i��*�?�+����R���w�\x1�w@!
M�T�?p�)A��/��R�+FHQE4��)��K�"���_'�Q��K�1Pm�&6E=��F�Xz������U� 8������\\��g���/1�.8g(����6�`XH+U'=�1h`0����3����Cm*vN��e�kuX9P�_N���>�5H��Mw@���A!��BA�.@fJV����vNDG��� v�$B���@�j*� fq/U����]���Tn��ONR�����X���D	�7��X�"��T*�q�&}����E�����(��`���81;pbo�W���������l��p�����P)�	(~���	r�3U�E;����@�>N%D��k���N:(
	X�;�d�
���FB�4Q��D-Q���Pq 1x�%�;FV���;�n�a��z)�������5���5~���o������2�I�WB��)Qz�\cw�l��]����>�Q{�W�WW4������j�A`��X���brkf9��0�0��7'^���3�e&C�80���������p`�p�@�p�����	!
�}(\F��`J;�K���GL�s&�@��������V�c�����:��g��%d�n��U��A�-+�J�<��rF*_[+D�X�������N���..`Z��v����y7 ���XU���De������a�Ue��k%�'��#�q�}E�wX�:����o�,���v�$���O�x�I����`�������Q�����~.��a�PUT3��
������7�!���W�c<W�:�K52��:(Nv	��n��p�(Xw�������C�%\@���.�h�S��R��|�J8$$�f������i'��U��B�U�}���:��G��*%�'�g�Y:(�(�}�.�k���v��CXb�yc��h)zt�&4��+��^�Z���|p�wT8��+�W��nb�GA�;�(�a�q��sh�x	�/Q}���AU�wJ���V/X�)r���c0���B=��6��U
Dh;E/l��^��+��
>b0�Hg�z2�x��D4>Z����y~2Q��
���1b�v�?'@g�D��;��(��@��d�&�6L������N~w��,�����-�=���V34J�Z�,k�A�W+��e_ThjK�
�Z���5�8?�5I����S
�������+�S���]p�� �<�Y%��Zo�N��
�L$������/�r�7 c��i�W�X��:���<]�X?�f��RE�GbTI�9m��)>�F������_]*�������~��l��a������z���vS���V�R����	�F{X�S��t�F���c
O1*��p��� ���G�,��FI����&� �=i��t=�%>����_<�����h0��Mq�'^��#�h�aZ��Rn?���;H��]G?2�V2��l����<���Bl�V������8C���X��~�������xel"����!����o�+���KE*N��1�:�(}��m� �8�7�����'$�������q�K���q1�}��.�&���*��_�������~jo��;Ir)h��7��W�p�-��D�5�S����QB�C�Z����@K�Q�_���]�[cV[���*E�+4��b;�7.|�< av��_{�-)o�X(��,�*�2B�BU������Ts��`3&�U�#����b�M���C++�8���yD[���P���oR
Y�|�e�
�'�%��jb�Yu�{f�b�6�>m9�����Nb<����j��_�c���8V�TxNO���F��g�.�����G�C!���OCf4>Z�K@U{�0�Nt��IJ������wu�m�@|��_!l@�4�+��d�O�����K�>$K���N`9]�!��xw�%K	�&>$��!�"�����i�0��Z�L�w�yz�������ok��u����c�����tz�m����@��Y�1E[-X�n������6L�i`"�g"�$gy����1��t2-��4q0ij����Rd�FNS��dy�<vA�>)W��hH�>&1�5���e]�.rx�L\���
ZnR��G#.��TQ �a�_���B
`-cv�T�%�;L�SE�v����?*X��D�,ih�x�%(_�$U���QP4���"��r \�rl[�D^���"������������S��X����n��lL�N�s�2�E�,���k��T#��(`���D��<���?9�h��)�!��s�:(>K���	!�s����i����|�z=����6r�]SG��~������{�������m��������wO�2!�6�7��c�e���6��yi�8NM)�s��$�cP��~��b^$|�}�U�P]I��Q���>�$���-��������l��Kwy)���=�����$x�Dq(1�d�z���*�!�"_ b�+��` s�#
�v�
���Pt]�s���7�dyN,;P���p6�;|"6�M���B6�{��|�F��6z�C��"��)���[.AE,��
�.���rFN4X�J;t��+V�C���g�����2���@m	��F�����q�7��{]cI��M}�����jXA� P������^��X��_Yr�]
�C����]��W9W�q����X�64+���H�Y���5�J��A���U��(4O������8�P���g����.><����>�)��`l�*s����8������D�To��
�a������|nU�|8�9-b���#�������ON�K�5/�����W��K�������8��(n�5(��2r����wgh��'�X�h��
�,.]�Bww�������x����@q�%m��P���������4��`9��([f�$����
5r"*B�2+S�D�U������vf�I��xHj����(�J]9�rLz�����M���c���~.���%�F�*�d�ZO�����{����.�_��t��}��Z%�L����m;�Zhn�-��U��wc�_�E*�H���
�]���1�r��;p��&'q����a��_��^���n��e�S?��'��a��>�o�eY�?PC���5\�����#��o��F� �J7MB�Gh��_Zg�<	����t>�`<-������(��F#������| U
�� aT����c��~��x&���O"�A�:���`[�M�Wk]�2�zhi�n����~h���_�l�?J]����z��� `��d��h�����]��=J%y�:��%[6!���K�=���fq����;��:�ETa�JE���;�c���a����Q�3at������!\��&��xe013��mR
�T��hoy�f��N�S���5��������e�CSM���x���h�h����.)ZV���#@\��I���#wQ>
�,���W�����q����6�e3N���j�8A����������`�S*;l:�z	cjd�\[�W�ZSY�g-�%����=���y�x���
!���K��kT�:�>�D�����.�fb$"��������`��;v�_V�?��P*�!���!��g@A�[�_��?*SB�JlV
�Oc��r�8��ie���yS���n�[>�JJ�u�����z���CM%��yO���DM�8�(F�`�C����>��O}�S����>��O}�S����>�4�vM�
#45Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#44)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jun 14, 2019 at 8:26 AM Thomas Munro <thomas.munro@gmail.com> wrote:

* current versions of the record and worker code discussed upthread by
Amit and others

Thanks for posting the complete patchset.

Last time, I mentioned the remaining work in undo-processing patchset,
the status of which is as follows:
1. Enhance uur_progress so that it updates undo action apply progress
at regular intervals. This has been done. The idea is that we update
the transaction's undo apply progress at regular intervals so that
after a crash we can skip already applied undo. The undo apply
progress is updated in terms of the number of blocks processed.
I think it is better to change the name of uur_progress to something
like uur_apply_progress. Any suggestions?

2. Enhance to support oldestXidHavingUnappliedUndo, more on that
later. This has been done. The idea here is that we register all the
undo apply (transaction abort) requests in the hash table (referred to
as Rollback Hash Table in the patch) and we have a hard limit (after
that we won't allow new transactions to write undo) on how many such
requests can be pending. So scanning this table gives us the value of
oldestXidHavingUnappliedUndo (actually the value for this will be
smallest of 'xid having pending undo' and 'oldestXmin'). As this
rollback hash table is not persistent, after start, we need to take a
pass over undo logs to register all the pending abort requests in the
rollback hash table. There are two main purposes which this value
serves (a) Any Xid below this is all-visible, so it can help in
visibility checks, (b) it can help us implementing the rule that "No
aborted XID with an age >2^31 can have unapplied undo.". This part
helps us to decide to truncate the clog because we can't truncate the
clog for transactions having undo.

3. Split the patch. The patch is split into five patches. I will
give a brief description of each patch which to a good extent is
mentioned in the commit message for each patch as well:
0010-Extend-binary-heap-functionality - This patch adds the routines
to allocate binary heap in shared memory and to remove nth element
from binary heap. These routines will be used by a later patch that
will allow an efficient way to process the pending rollback requests.

0011-Infrastructure-to-register-and-fetch-undo-action-req - This patch
provides an infrastructure to register and fetch undo action requests.
This infrastructure provides a way to allow execution of undo actions.
One might think that we can always execute undo actions on error or
explicit rollback by the user, however, there are cases when that is
not possible. For example, (a) if the system crash while doing the
operation, then after startup, we need a way to perform undo actions;
(b) If we get an error while
performing undo actions.

Apart from this, when there are large rollback requests, then it is
quite inefficient to perform all the undo actions and then return
control to the user.

0012-Infrastructure-to-execute-pending-undo-actions - This provides an
infrastructure to execute pending undo actions. To apply the undo
actions, we collect the undo records in bulk and try to process them
together. We ensure to update the transaction's progress at regular
intervals so that after a crash we can skip already applied undo.
This needs some more work to generalize the processing of undo records
so that this infrastructure can be used by other AM's as well.

0013-Allow-foreground-transactions-to-perform-undo-action - This patch
allows foreground transactions to perform undo actions on abort. We
always perform rollback actions after cleaning up the current
(sub)transaction. This will ensure that we perform the actions
immediately after an error (and release the locks) rather than when
the user issues Rollback command at some later point of time. We are
releasing the locks after the undo actions are applied. The reason to
delay lock release is that if we release locks before applying undo
actions, then the parallel session can acquire the lock before us
which can lead to deadlock.

0014-Allow-execution-and-discard-of-undo-by-background-wo- - This
patch allows execution and discard of undo by background workers.
Undo launcher is responsible for launching the workers iff there is
some work available in one of the work queues and there are more
workers available. The worker is launched to handle requests for a
particular database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.
It also registers the request for aborted transactions in the work
queues. It iterates through all the active logs one-by-one and tries
to discard the transactions that are old enough to matter.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#46Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#43)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 13, 2019 at 3:13 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, May 27, 2019 at 5:59 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Apart from fixing the above comments, the patch is rebased on latest
undo patchset. As of now, I have split the binaryheap.c changes into
a separate patch. We are stilll enhancing the patch to compute
oldestXidHavingUnappliedUndo which touches various parts of patch, so
splitting further without completing that can make it a bit difficult
to work on that.

Some review comments around execute_undo_actions:

The 'nopartial' argument to execute_undo_actions is confusing. First,
it would probably be worth spelling it out instead of abbreviation:
not_partial_transaction rather than nopartial. Second, it is usually
better to phrase parameter names in terms of what they are rather than
in terms of what they are not: complete_transaction rather than
not_partial_transaction. Third, it's unclear from these comments why
we'd be undoing something other than a complete transaction. It looks
as though the answer is that this flag will be false when we're
undoing a subxact -- in which case, why not invert the sense of the
flag and call it 'bool subxact'? I might be wrong, but it seems like
that would be a whole lot clearer.

The idea was that it could be use for multiple purposes like 'rolling
back complete xact', 'rolling back subxact', 'rollback at page-level'
or any similar future need even though not all code paths use that
function. I am not wedded to any particular name here, but among your
suggestions complete_transaction sounds better to me. Are you okay
going with that?

Fourth, the block at the top of
this function, guarded by nopartial, seems like it must be vulnerable
to race conditions. If we're undoing the complete transaction, then
it checks whether UndoFetchRecord() still returns anything. But that
could change not just at the beginning of the function, but also any
time in the middle, or so it seems to me.

It won't change in between because we have ensured at top-level that
no two processes can start executing pending undo at the same time.
Basically, anyone wants to execute the undo actions will have an entry
in rollback hash table and that will be marked as in-progress. As
mentioned in comments, the race is only "after discard worker
fetches the record and found that this transaction need to be rolled
back, backend might concurrently execute the actions and remove the
request from rollback hash table."

I doubt that this is the
right level at which to deal with this sort of interlocking. I think
there should be some higher-level mechanism that prevents two
processes from trying to undo the same transaction at the same time,
like a heavyweight lock or some kind of flag in the shared memory data
structure that keeps track of pending undo, so that we never even
reach this code unless we know that this XID needs undo work

Introducing heavyweight lock can create different sort of problems
because we need to hold it till all the actions are applied to avoid
what I have mentioned above. The problem will be that discard worker
will be blocked till backend/undo worker applies the complete actions
unless we just take this lock conditionally in discard worker.

Another way could be that we re-fetch the undo record when we are
registering the undo request under RollbackRequestLock and check it's
status again becuase in that case backend or other undoworker won't be
able to remove the request from hash table concurrently. However, the
advantage of checking it in execute_undo_actions is that we can
optimize it in the future to avoid re-fetching this record when
actually fetching the records to apply undo actions.

and no
other process is already doing it.

This part is already ensured in the current code.

The 'blk_chain_complete' variable which is set in this function and
passed down to execute_undo_actions_page() and then to the rmgr's
rm_undo callback also looks problematic.

I agree this parameter should go away from the generic interface
considering the requirements from zedstore.

First, not every AM that
uses undo may even have the notion of a 'block chain'; zedstore for
example uses TIDs as a 48-bit integer, not a block + offset number, so
it's really not going to have a 'block chain.' Second, even in
zheap's usage, it seems to me that the block chain could be complete
even when this gets set to false. It gets set to true when we're
undoing a toplevel transaction (not a subxact) and we were able to
fetch all of the undo for that toplevel transaction. But even if
that's not true, the chain for an individual block could still be
complete, because all the remaining undo for the block at issue
might've been in the chunk of undo we already read; the remaining undo
could be for other blocks. For that reason, I can't see how the zheap
code that relies on this value can be correct; it uses this value to
decide whether to stick zeroes in the transaction slot, but if the
scenario described above happened, then I suppose the XID would not
get cleared from the slot during undo. Maybe zheap is just relying on
that being harmless, since if all of the undo actions have been
correctly executed for the page, the fact that the transaction slot is
still bogusly used by an aborted xact won't matter; nothing will refer
to it. However, it seems to me that it would be better for zheap to
set things up so that the first undo record for a particular txn/page
combination is flagged in some way (in the payload!) so that undo can
zero the slot if the action being undone is the one that claimed the
slot. That seems cleaner on principle, and it also avoids having
supposedly AM-independent code pass down details that are driven by
zheap's particular needs.

Yeah, we can do what you are suggesting for zheap or in many cases, we
should be able to detect it via uur_blkprev of the last record of
page. The invalid value will indicate that the chain for the page is
complete.

I think that the signature for rm_undo can be simplified considerably.
I think blk_chain_complete should go away for the reasons discussed
above. Also, based on our conversations with Heikki at PGCon, we
decided that we should not presume that the AM wants the records
grouped by block, so the blkno argument should go away. In addition,
I don't see much reason to have a first_idx argument. Instead of
passing a pointer to the caller's entire array and telling the
callback where to start looking, couldn't we just pass a pointer to
the first record the callback should examine, i.e. instead of passing
urp_array, pass urp_array + first_idx. Then instead of having a
last_idx argument, have an argument for the number of entries in the
array, computed as last_idx - first_idx + 1. With those changes,
rm_undo would look like this:

bool (*rm_undo) (UndoRecInfo *urp_array, int count, Oid reloid,
FullTransactionId full_xid);

I agree.

Now for the $10m question: why even pass reloid and full_xid? Aren't
those values going to be present inside every UnpackedUndoRecord? Why
not just let the callback get them from the first record (or however
it wants to do things)? Perhaps there is some documentation value
here in that it implies that the value will be the same for every
record, but we could also handle that by just documenting in the
appropriate places that undo is done by transaction and relation and
therefore the callback is entitled to assume that the same value will
be present in every record. Then again, I am not sure we really want
the callback to assume that reloid doesn't change. I don't see a
reason offhand not to just pass as many records as we have for a given
transaction and let the callback do what it likes. So maybe that's
another reason to get rid of the reloid argument, at least. And then
we could document that all the record will have the same full_xid
(unless we decide that we don't want to guarantee that either).

Additionally, it strikes me that urp_array is not the greatest name.
Generally, putting _array into the name of the variable to indicate
that it's an array doesn't seem all that great from a coding-style
perspective. I mean, sometimes it's the best you can do, but it's not
amazing. And urp seems like it's using an abbreviation without any
real reason. For contrast, consider this existing precedent:

extern SysScanDesc systable_beginscan_ordered(Relation heapRelation,
Relation indexRelation,
Snapshot snapshot,
int nkeys, ScanKey key);

Or this one:

extern TupleDesc CreateTupleDesc(int natts, Form_pg_attribute *attrs);

Notice that in each case the array parameter (which is the last one)
is named based on what data it contains rather than on the fact that
it is an array.

Agreed, will change accordingly.

Finally, I observe that rm_undo returns a Boolean, but it's not used
for anything. The only call to rm_undo in the current patch set is in
execute_undo_actions_page, which returns that value to the caller, but
the callers just discard it. I suppose maybe this was intended to
report success or failure, but I think the way that rm_undo will
report failure is to ERROR.

For Error case, it is fine to report failure, but there can be cases
where we don't need to apply undo actions like when the relation is
dropped/truncated, undo actions are already applied. The original
idea was to cover such cases by the return value. I agree that
currently, caller ignores this value, but there is some value in
keeping it. So, I am in favor of a signature with bool as the return
value.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#47Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#46)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jun 17, 2019 at 6:03 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

The idea was that it could be use for multiple purposes like 'rolling
back complete xact', 'rolling back subxact', 'rollback at page-level'
or any similar future need even though not all code paths use that
function. I am not wedded to any particular name here, but among your
suggestions complete_transaction sounds better to me. Are you okay
going with that?

Sure, let's try that for now and see how it looks. We can always
change it again if it seems to be a good idea later.

It won't change in between because we have ensured at top-level that
no two processes can start executing pending undo at the same time.
Basically, anyone wants to execute the undo actions will have an entry
in rollback hash table and that will be marked as in-progress. As
mentioned in comments, the race is only "after discard worker
fetches the record and found that this transaction need to be rolled
back, backend might concurrently execute the actions and remove the
request from rollback hash table."

[ discussion of alternatives ]

I'm not precisely sure what the best thing to do here is, but I'm
skeptical that the code in question belongs in this function. There
are two separate things going on here: one is this revalidation that
the undo hasn't been discarded, and the other is executing the undo
actions. Those are clearly separate tasks, and they are not tasks that
always get done together: sometimes we do only one, and sometimes we
do both. Any function that looks like this is inherently suspicious:

whatever(....., bool flag)
{
if (flag)
{
// lengthy block of code
}

// another lengthy block of code
}

There has to be a reason not to just split this into two functions and
let the caller decide whether to call one or both.

For Error case, it is fine to report failure, but there can be cases
where we don't need to apply undo actions like when the relation is
dropped/truncated, undo actions are already applied. The original
idea was to cover such cases by the return value. I agree that
currently, caller ignores this value, but there is some value in
keeping it. So, I am in favor of a signature with bool as the return
value.

OK. So then the callers can't keep ignoring it... and there should be
some test framework that verifies the behavior when the return value
is false.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#48Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#47)
14 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jun 17, 2019 at 7:30 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Jun 17, 2019 at 6:03 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I'm not precisely sure what the best thing to do here is, but I'm
skeptical that the code in question belongs in this function. There
are two separate things going on here: one is this revalidation that
the undo hasn't been discarded, and the other is executing the undo
actions. Those are clearly separate tasks, and they are not tasks that
always get done together: sometimes we do only one, and sometimes we
do both. Any function that looks like this is inherently suspicious:

whatever(....., bool flag)
{
if (flag)
{
// lengthy block of code
}

// another lengthy block of code
}

There has to be a reason not to just split this into two functions and
let the caller decide whether to call one or both.

Yeah, because some of the information required to perform the
necessary steps (in the code under the flag) is quite central to this
function (see undo apply progress update part) and it is used at more
than one place in this function. I have refactored the code in this
function, see if it makes sense now. You need to check patch
0012-Infrastructure-to-execute-pending-undo-actions.patch for these
changes.

For Error case, it is fine to report failure, but there can be cases
where we don't need to apply undo actions like when the relation is
dropped/truncated, undo actions are already applied. The original
idea was to cover such cases by the return value. I agree that
currently, caller ignores this value, but there is some value in
keeping it. So, I am in favor of a signature with bool as the return
value.

OK. So then the callers can't keep ignoring it...

I again thought about this but couldn't come up with anything
meaningful. The idea is to ignore some undo records if they belong to
the same relation which is already gone. I think we can do something
about it in zheap specific code and make the generic code return void.

I have fixed the other comments raised by you. See
0012-Infrastructure-to-execute-pending-undo-actions.patch

Apart from the changes related to the undo apply, this patch series
contains changes for making the transaction header at a location
immediately after
UndoRecordHeader which makes it easy to update the same. The changes
are in patches 0007-Provide-interfaces-to-store-and-fetch-undo-records.patch
and 0012-Infrastructure-to-execute-pending-undo-actions.patch.

There are no changes in undo log module patches.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0014-Allow-execution-and-discard-of-undo-by-background-wo.patchapplication/octet-stream; name=0014-Allow-execution-and-discard-of-undo-by-background-wo.patchDownload
From 8c7dfb9dc7e2516d1bcd7c871415e85c3ce104b3 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 16:03:49 +0530
Subject: [PATCH 14/14] Allow execution and discard of undo by background
 workers.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

We don't allow any transaction older than 2^31 to have pending undo actions.
Also, we have a hard limit on the number of transactions that can have
pending undo which is proportional to pending_undo_queue_size.

Amit Kapila, Dilip Kumar and Kuntal Ghosh with inputs from Andres Freund,
Robert Haas and Thomas Munro.
---
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/varsup.c           |  35 +-
 src/backend/access/transam/xact.c             |   5 +
 src/backend/access/transam/xlog.c             |  29 +
 src/backend/access/undo/Makefile              |   4 +-
 src/backend/access/undo/README.UndoProcessing |  78 +++
 src/backend/access/undo/discardworker.c       | 215 +++++++
 src/backend/access/undo/undoaccess.c          |  58 +-
 src/backend/access/undo/undodiscard.c         | 480 +++++++++++++++
 src/backend/access/undo/undolog.c             |   2 +
 src/backend/access/undo/undorequest.c         | 200 ++++++-
 src/backend/access/undo/undoworker.c          | 809 ++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c              |   5 +
 src/backend/postmaster/bgworker.c             |  11 +
 src/backend/postmaster/pgstat.c               |   9 +
 src/backend/postmaster/postmaster.c           |  11 +
 src/backend/storage/ipc/ipci.c                |   6 +
 src/backend/storage/lmgr/lwlocknames.txt      |   1 +
 src/backend/storage/lmgr/proc.c               |   2 +
 src/backend/utils/misc/guc.c                  |  22 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/access/discardworker.h            |  20 +
 src/include/access/transam.h                  |  10 +
 src/include/access/undodiscard.h              |  26 +
 src/include/access/undolog.h                  |  13 +
 src/include/access/undoworker.h               |  29 +
 src/include/catalog/pg_control.h              |   9 +
 src/include/nodes/primnodes.h                 |   3 +-
 src/include/pgstat.h                          |   5 +-
 src/include/postmaster/postmaster.h           |   1 +
 src/include/storage/lwlock.h                  |   1 +
 src/include/storage/proc.h                    |   4 +
 src/include/storage/procarray.h               |   7 +-
 src/test/regress/expected/sysviews.out        |   3 +-
 34 files changed, 2091 insertions(+), 27 deletions(-)
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..4b00d7d 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest full xid having unapplied undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUnappliedUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index fd01989..e74155d 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -127,14 +127,16 @@ GetNewTransactionId(bool isSubXact)
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
 		{
@@ -147,14 +149,16 @@ GetNewTransactionId(bool isSubXact)
 								oldest_datname,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(WARNING,
 						(errmsg("database with OID %u must be vacuumed within %u transactions",
 								oldest_datoid,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -334,10 +338,24 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUnapplied Undo because this is the oldest xid
+	 * whose undo is not yet discarded so this is still a valid xid in the
+	 * system.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -433,6 +451,9 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 		 * Note: it's also possible that get_database_name fails and returns
 		 * NULL, for example because the database just got dropped.  We'll
 		 * still warn, even though the warning might now be unnecessary.
+		 *
+		 * XXX Can we easily distinguish that the problem is due to unapplied
+		 * undo or some old open transactions?
 		 */
 		if (IsTransactionState())
 			oldest_datname = get_database_name(oldest_datoid);
@@ -445,14 +466,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 							oldest_datname,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 		else
 			ereport(WARNING,
 					(errmsg("database with OID %u must be vacuumed within %u transactions",
 							oldest_datoid,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 	}
 }
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 3253a0c..9d1a08e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2274,6 +2275,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4ef7fb0..f0fda49 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5160,6 +5160,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUnappliedUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6611,6 +6612,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6627,6 +6631,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7315,7 +7323,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8712,6 +8726,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUnappliedUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9624,6 +9642,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9681,12 +9702,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9726,6 +9752,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 68696bc..b4e7bab 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
-	   undorequest.o
+OBJS = discardworker.o undoaccess.o undoaction.o undoactionxlog.o undodiscard.o \
+	   undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
index e0caf9e..b7817cf 100644
--- a/src/backend/access/undo/README.UndoProcessing
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -37,3 +37,81 @@ tables are only accessible in the backend that has created them.  We can't
 postpone applying undo actions for subtransactions as the modifications
 made by aborted subtransaction must not be visible even if the main transaction
 commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue will allow us to
+process the requests of older transactions and help us to move
+oldesdXidHavingUnappliedUndo (this is a xid-horizon below which all the
+transactions are visible) forward.  A size-based queue which will help us to
+perform the rollbacks of larger aborts in a timely fashion, so that we don't get
+stuck while processing them during discard of the logs.  An error queue to hold
+the requests for transactions that failed to apply its undo.  The rollback hash
+table is used to avoid duplicate undo requests by backends and discard worker.
+The table must be able to accommodate all active undo requests.  The undo
+requests must appear in both xid and size requests queues or neither.  As of now,
+we process the requests from these queues in a round-robin fashion to give equal
+priority to all three types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.
+
+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+
+In a running system, scanning the rollback hash table will give us the value of
+oldestXidHavingUnappliedUndo, however, after startup, we need to once scan all
+the undo logs and populate the rollback hash table.  After startup, we allow
+connections, but don't allow transactions that want to write undo till the
+rollback hash table is initialized.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+To apply the undo actions, we collect the undo records in bulk and try to
+process them together.  We ensure to update the transaction's progress at
+regular intervals so that after a crash we can skip already applied undo.  The
+undo apply progress is updated in terms of the number of blocks processed.
+Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED indicates that all the
+undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED indicates that no undo action
+has been applied yet and any other value indicates that we have applied undo
+partially and after crash recovery, we need to start processing the undo from
+the same location.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..2080782
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,215 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index d70ab65..787f4f3 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -56,8 +56,16 @@
 #include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/proc.h"
 #include "miscadmin.h"
 
+/*
+ * Defines the number of times we try to wait for rollback hash table to get
+ * initialized.  After these many attempts it will return error and the user
+ * can retry the operation.
+ */
+#define ROLLBACK_HT_INIT_WAIT_TRY	60
+
 /* Prototypes for static functions. */
 static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 				 UndoRecPtr urp, RelFileNode rnode,
@@ -514,6 +522,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
 	PreparedUndoSpace *prepared_undo;
 
+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo
+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}
+
+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+
 	/* Already reached maximum prepared limit. */
 	if (context->nprepared_undo == context->max_prepared_undo)
 		elog(ERROR, "already reached the maximum prepared limit");
@@ -1610,12 +1662,12 @@ UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 	page = BufferGetPage(buffer);
-	phdr = (UndoPageHeader)page;
+	phdr = (UndoPageHeader) page;
 
 	/* Calculate the size of the partial record. */
 	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
-						phdr->tuple_len + phdr->payload_len -
-						phdr->record_offset;
+					   phdr->tuple_len + phdr->payload_len -
+					   phdr->record_offset;
 
 	/* calculate the offset in current log. */
 	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..ee99a0a
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,480 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard the undo for the given log
+ *
+ * Search the undo log, get the start record for each transaction until we get
+ * the transaction with xid >= xmin or an invalid xid.  Then call undolog
+ * routine to discard up to that point and update the memory structure for the
+ * log slot.  We set the hibernate flag if we do not have any undo data that
+ * can be discarded, this flag is passed back to the discard worker wherein it
+ * determines if the system is idle and it should sleep for some time.
+ *
+ * Return the oldest full_xid remaining in this undo log (which should be
+ * >= xmin, since we'll discard everything older).  Returns
+ * InvalidTransactionId, if the undo log is empty.
+ */
+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	bool	log_complete = false;
+	TransactionId	undoxid = InvalidTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	uint32	epoch = 0;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		bool pending_abort = false;
+
+		next_insert = UndoLogGetNextInsertPtr(logno, InvalidTransactionId);
+
+		if (next_insert == undo_recptr)
+		{
+			/*
+			 * The caller of this function must have ensured that there is
+			 * something to discard.
+			 */
+			Assert(undo_recptr != slot->oldest_data);
+
+			/* Indicate that we have processed all the log. */
+			log_complete = true;
+		}
+		else
+		{
+			/* Fetch the undo record for the given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+
+			if (uur != NULL)
+			{
+				/*
+				 * Add the aborted transaction to the rollback request queues.
+				 *
+				 * If the undo actions for the aborted transaction is already
+				 * applied then continue discarding the undo log, otherwise,
+				 * discard till current point and stop processing this undo
+				 * log.
+				 *
+				 * We can ignore the abort for transactions whose
+				 * corresponding database doesn't exist.
+				 *
+				 * XXX: We've added the transaction-in-progress check to avoid
+				 * xids of in-progress autovacuum as those are not computed
+				 * for oldestxmin calculation.  See DiscardWorkerMain.
+				 */
+				if (!TransactionIdDidCommit(uur->uur_xid) &&
+					!TransactionIdIsInProgress(uur->uur_xid) &&
+					TransactionIdPrecedes(uur->uur_xid, xmin) &&
+					!IsXactApplyProgressCompleted(uur->uur_progress) &&
+					dbid_exists(uur->uur_dbid))
+				{
+					FullTransactionId full_xid;
+
+					full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+																uur->uur_xid);
+					(void) RegisterRollbackReq(InvalidUndoRecPtr,
+											   undo_recptr,
+											   uur->uur_dbid,
+											   full_xid);
+
+					pending_abort = true;
+				}
+
+				next_urecptr = uur->uur_next;
+				undoxid = uur->uur_xid;
+				epoch = uur->uur_xidepoch;
+
+				UndoRecordRelease(uur);
+				uur = NULL;
+			}
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) the next transaction is not all-visible. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if ((TransactionIdIsValid(undoxid) &&
+			 TransactionIdFollowsOrEquals(undoxid, xmin)) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			log_complete ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If the transaction id is smaller than the xmin, it means this
+			 * must be the last transaction in this undo log, so we need to
+			 * get the last insert point in this undo log and discard till
+			 * that point.
+			 *
+			 * Also, if the transaction has pending abort, stop discarding
+			 * further.
+			 */
+			if (TransactionIdPrecedes(undoxid, xmin) && !pending_abort)
+			{
+				UndoRecPtr	next_insert = InvalidUndoRecPtr;
+
+				/*
+				 * If more undo has been inserted since we checked last, then
+				 * we can process that as well.
+				 */
+				next_insert = UndoLogGetNextInsertPtr(logno, undoxid);
+				if (!UndoRecPtrIsValid(next_insert))
+					continue;
+
+				undo_recptr = next_insert;
+				need_discard = true;
+				epoch = 0;
+				latest_discardxid = undoxid;
+				undoxid = InvalidTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/*
+			 * If no more pending undo logs then set the oldest transaction to
+			 * InvalidTransactionId.
+			 */
+			if (log_complete)
+			{
+				slot->oldest_xid = InvalidTransactionId;
+				slot->oldest_xidepoch = 0;
+			}
+			else
+			{
+				slot->oldest_xid = undoxid;
+				slot->oldest_xidepoch = epoch;
+			}
+
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = undoxid;
+
+		Assert(uur == NULL);
+
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+}
+
+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass
+ * over undo logs is complete, new undo can is allowed to be written in the
+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before start allowing any new transaction to write the
+ * undo we need to make sure that we know exact number of pending requests.
+ */
+void
+UndoLogProcess()
+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();
+
+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;
+
+			/* Fetch the undo record for given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+			Assert(uur != NULL);
+
+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(uur->uur_xid) &&
+				!TransactionIdIsInProgress(uur->uur_xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_progress))
+			{
+				FullTransactionId full_xid;
+
+				full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+															uur->uur_xid);
+				(void) RegisterRollbackReq(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_dbid, full_xid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are
+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) == UndoRecPtrGetLogNo(uur->uur_next))
+				undo_recptr = uur->uur_next;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+
+	CommitTransactionCommand();
+
+	/* Allow the transactions to start writting undo. */
+	ProcGlobal->rollbackHTInitialized = true;
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		/*
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.persistence == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin the try
+		 * to discard the undo log.
+		 */
+		if (!TransactionIdIsValid(slot->oldest_xid) ||
+			TransactionIdPrecedes(slot->oldest_xid, oldestXmin))
+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+	}
+
+	/* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+	oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+
+	/*
+	 * Update the oldestFullXidHavingUnappliedUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.persistence == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 014480f..ad06b6e 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -145,6 +145,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 		}
 	}
 	else
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
index e46cfc4..9cdd548 100644
--- a/src/backend/access/undo/undorequest.c
+++ b/src/backend/access/undo/undorequest.c
@@ -54,10 +54,12 @@
 #include "postgres.h"
 #include "miscadmin.h"
 
+#include "access/discardworker.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_database.h"
@@ -914,6 +916,139 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 }
 
 /*
+ * Fetch the start urec pointer for the transaction and the undo request size.
+ *
+ * start_urecptr_inout - This is an INOUT parameter.  If a transaction has
+ * overflowed to multiple undo logs, the caller can set start_urecptr_inout
+ * to a location of any of the undo logs where the transaction has written its
+ * first record for that particular log.  Given that, this function calculates
+ * the undo location where the transaction has inserted its first undo record.
+ * If a transaction hasn't overflowed to multiple undo logs, the value of this
+ * parameter remains unchanged.
+ *
+ * The first record of a transaction in each undo log contains a reference to
+ * the first record of this transaction in the previous log.  It finds the
+ * initial location by moving backward in the undo chain of this transaction
+ * across undo logs.  While doing the same, it also calculates the undo size
+ * between the input and output start undo record pointer value.
+ */
+static uint64
+FindUndoStartLocationAndSize(UndoRecPtr *start_urecptr_inout,
+							 FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+
+	Assert(start_urecptr_inout);
+
+	urecptr = *start_urecptr_inout;
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * A backend always set the start undo record pointer to the first undo
+	 * record inserted by this transaction.  Hence, we don't have to proceed
+	 * further.
+	 */
+	if (!IsDiscardProcess())
+		return sz;
+
+	/*
+	 * Since the discard worker processes the undo logs sequentially, it's
+	 * possible that start undo record pointer doesn't refer to the actual
+	 * start of the transaction.  Instead, it may refer to the start location
+	 * of the transaction in any of the subsequent logs.  In that case, we've
+	 * to find the actual start location of the transaction by going backwards
+	 * in the chain.
+	 */
+	while (true)
+	{
+		UndoLogOffset next_insert;
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * Since the rollback isn't completed for this transaction, this undo
+		 * record can't be discarded.
+		 */
+		Assert (uur != NULL);
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * If this is the first undo record of this transaction, return from
+		 * here.
+		 */
+		if ((uur->uur_info & UREC_INFO_LOGSWITCH) == 0)
+		{
+			UndoRecordRelease(uur);
+			break;
+		}
+
+		/*
+		 * This is a start of a overflowed transaction header, so it must have
+		 * a valid pointer to previous log's start transaction header.
+		 */
+		Assert(UndoRecPtrIsValid(uur->uur_prevlogstart));
+
+		/*
+		 * Find the previous log from which the transaction is overflowed
+		 * to current log.
+		 */
+		urecptr = uur->uur_prevlogstart;
+		slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		/*
+		 * When a transaction overflows to a new undo log, it's guaranteed
+		 * that this transaction will be the last transaction in the previous
+		 * log and we mark that log as full so that no other transaction can
+		 * write in that log further.  Check UndoLogAllocate for details.
+		 *
+		 * So, to find the undo size in the previous log, we've to find the
+		 * next insert location of the previous log and subtract current
+		 * transaction's start location in the previous log from it.
+		 */
+		next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+		Assert(UndoRecPtrIsValid(next_insert));
+
+		sz += (next_insert - urecptr);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	*start_urecptr_inout = urecptr;
+
+	return sz;
+}
+
+/*
  * Returns true, if we can push the rollback request to undo wrokers, false,
  * otherwise.
  */
@@ -929,9 +1064,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 
 	/*
 	 * We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
 	 */
-	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
 	{
 		if (GetXidQueueSize() >= pending_undo_queue_size ||
 			GetSizeQueueSize() >= pending_undo_queue_size)
@@ -957,12 +1107,7 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 		if ((GetXidQueueSize() < pending_undo_queue_size))
 		{
 			Assert(GetSizeQueueSize() < pending_undo_queue_size);
-
-			/*
-			 * XXX - Here, we should return true once we have background
-			 * worker facility.
-			 */
-			return false;
+			return true;
 		}
 	}
 
@@ -1403,6 +1548,12 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	Assert(dbid != InvalidOid);
 
 	/*
+	 * The discard worker can only send the start undo record pointer of a
+	 * transaction.  It doesn't set the end_urec_ptr.
+	 */
+	Assert(IsDiscardProcess() || UndoRecPtrIsValid(end_urec_ptr));
+
+	/*
 	 * Find the rollback request size and the end_urec_ptr (in case of discard
 	 * worker only).
 	 */
@@ -1417,6 +1568,14 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	if (!UndoRecPtrIsValid(end_urec_ptr))
 		return false;
 
+	/*
+	 * For registering a rollback request, we always store the full transaction
+	 * ID and the first undo record pointer inserted by this transaction.  This
+	 * ensures that backends and discard worker don't register the same request
+	 * twice.
+	 */
+	req_size += FindUndoStartLocationAndSize(&start_urec_ptr, full_xid);
+
 	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
 
 	/*
@@ -1432,7 +1591,13 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 										   HASH_ENTER_NULL, &found);
 
 	/*
-	 * It can only fail, if the value of pending_undo_queue_size or
+	 * Except the first pass over the undo logs by discard worker, the hash
+	 * table can never be full.
+	 */
+	Assert(!ProcGlobal->rollbackHTInitialized || (rh != NULL));
+
+	/*
+	 * It can only fail, if  the value of pending_undo_queue_size or
 	 * max_connections guc is reduced after restart of the server.
 	 */
 	if (rh == NULL)
@@ -1480,10 +1645,16 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 		}
 		/*
 		 * The request can't be pushed into the undo worker queue.  The
-		 * backends will try executing by itself.
+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with
+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
 		 */
-		else
+		else if (!IsDiscardProcess())
 			rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
 	}
 	else if (!UndoRequestIsValid(rh) && can_push)
 	{
@@ -1511,6 +1682,13 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 
 	LWLockRelease(RollbackRequestLock);
 
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
 	return pushed;
 }
 
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..a5bdbcb
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,809 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 4;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.
+ */
+#define UNDO_WORKER_LINGER_MS 10000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker * worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo * urinfo);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		CommitTransactionCommand();
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.  If we fail to add the request in
+		 * an error queue, then mark the entry status invalid.  This request
+		 * will be later added back to the queue by the discard worker.
+		 */
+		if (!InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTMarkEntryInvalid(urinfo->full_xid,
+									   urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	for (;;)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, die);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	for (;;)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4737c58..88703ad 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14516,6 +14517,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f5db5a8..0550d2c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d8dc0cc..4c906f6 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 688ad43..ddfad1b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher = true;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c3249..56b5d08 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 19e4f1f..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -51,3 +51,4 @@ LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
 RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 6126ed1..c4d05da 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -297,7 +297,9 @@ InitProcGlobal(void)
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
 
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo, 0);
 	ProcGlobal->xactsHavingPendingUndo = 0;
+	ProcGlobal->rollbackHTInitialized = false;
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 89ced6d..886d393 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1956,6 +1957,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		 },
+		 &enable_undo_launcher,
+		 true,
+		 NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3032,6 +3044,16 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"max_undo_workers", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
+			gettext_noop("Maximum number of undo worker processes."),
+			NULL,
+		},
+		&max_undo_workers,
+		4, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 51e4929..c9eb688 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -612,6 +612,7 @@
 					# requests are pushed to undo workers
 #pending_undo_queue_size = 1024	# size of queue used to register undo
 					# requests
+#max_undo_workers = 4	# maximum undo workers
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 1321d84..0b5dc59 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -72,6 +72,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..282afc0
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+extern void UndoLogProcess(void);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index d2b2c2a..a46c5fe 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -266,6 +266,19 @@ typedef struct UndoLogAllocContext
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogSlot
 {
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..1bdc31f
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+extern int max_undo_workers;
+
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..babcc6b 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,15 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest full transaction id which is having unapplied undo.  We include
+	 * this value in the checkpoint record so that whenever server re-starts
+	 * we can use this to initialize the server-wide value for same variable.
+	 * Any Xid prior to this should be all-visible, so if this is not set,
+	 * then the scans might try to fetch undo which can suck the performance.
+	 */
+	FullTransactionId		oldestFullXidHavingUnappliedUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 7c278c0..e792d56 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2fff673..f9dc0bb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -785,7 +785,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 8ccd2af..5025db3 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,7 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..b220a05 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -223,6 +223,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
 	LWTRANCHE_REWIND,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 6a74d20..25839f7 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,8 +272,12 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUnappliedUndo;
 	/* Number of aborted transactions with pending undo actions. */
 	int			xactsHavingPendingUndo;
+	/* Whether the rollback hash table is initialized after the startup? */
+	bool		rollbackHTInitialized;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672..1d12994 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb..6034c5e 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
1.8.3.1

0008-Test-module-for-undo-api.patchapplication/octet-stream; name=0008-Test-module-for-undo-api.patchDownload
From d0cc2abc20bac4f83b075c6b7c98fbe6bc7dd4ef Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 13 Jun 2019 09:19:47 +0530
Subject: [PATCH 08/14] Test module for undo api

Basic test routine to test undo interface API.

Dilip Kumar
---
 src/test/modules/test_undo_api/Makefile            |  21 +++
 .../test_undo_api/expected/test_undo_api.out       |  12 ++
 .../modules/test_undo_api/sql/test_undo_api.sql    |   8 +
 .../modules/test_undo_api/test_undo_api--1.0.sql   |   6 +
 src/test/modules/test_undo_api/test_undo_api.c     | 185 +++++++++++++++++++++
 .../modules/test_undo_api/test_undo_api.control    |   4 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/test/modules/test_undo_api/Makefile
 create mode 100644 src/test/modules/test_undo_api/expected/test_undo_api.out
 create mode 100644 src/test/modules/test_undo_api/sql/test_undo_api.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api--1.0.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.c
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.control

diff --git a/src/test/modules/test_undo_api/Makefile b/src/test/modules/test_undo_api/Makefile
new file mode 100644
index 0000000..deb3816
--- /dev/null
+++ b/src/test/modules/test_undo_api/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo/Makefile
+
+MODULE_big = test_undo_api
+OBJS = test_undo_api.o
+PGFILEDESC = "test_undo_api - a test module for the undo api layer"
+
+EXTENSION = test_undo_api
+DATA = test_undo_api--1.0.sql
+
+REGRESS = test_undo_api
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_api
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_api/expected/test_undo_api.out b/src/test/modules/test_undo_api/expected/test_undo_api.out
new file mode 100644
index 0000000..5496ddb
--- /dev/null
+++ b/src/test/modules/test_undo_api/expected/test_undo_api.out
@@ -0,0 +1,12 @@
+CREATE EXTENSION test_undo_api;
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
+ test_undo_api 
+---------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_undo_api/sql/test_undo_api.sql b/src/test/modules/test_undo_api/sql/test_undo_api.sql
new file mode 100644
index 0000000..fa6f789
--- /dev/null
+++ b/src/test/modules/test_undo_api/sql/test_undo_api.sql
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_undo_api;
+
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
diff --git a/src/test/modules/test_undo_api/test_undo_api--1.0.sql b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
new file mode 100644
index 0000000..1aa4e02
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
@@ -0,0 +1,6 @@
+\echo Use "CREATE EXTENSION test_undo_api" to load this file. \quit
+
+CREATE FUNCTION test_undo_api(persistence text)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_undo_api/test_undo_api.c b/src/test/modules/test_undo_api/test_undo_api.c
new file mode 100644
index 0000000..396714b
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.c
@@ -0,0 +1,185 @@
+#include "postgres.h"
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "catalog/pg_class.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_undo_api);
+
+static void
+compare_undo_record(UnpackedUndoRecord *urp1, UnpackedUndoRecord *urp2)
+{
+	int	header_size = offsetof(UnpackedUndoRecord, uur_next) + sizeof(uint64);
+
+	/* Compare undo record header. */
+	if (strncmp((char *) urp1, (char *) urp2, header_size) != 0)
+		elog(ERROR, "undo header did not match");
+
+	/* Compare payload and tuple length. */
+	if (urp1->uur_payload.len != urp2->uur_payload.len)
+		elog(ERROR, "payload data length did not match");
+
+	if (urp1->uur_tuple.len != urp2->uur_tuple.len)
+		elog(ERROR, "tuple data length did not match");
+
+	/* Compare undo record payload data. */
+	if (strncmp(urp1->uur_payload.data, urp2->uur_payload.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo payload data did not match");
+
+	/* Compare undo record tuple data. */
+	if (strncmp(urp1->uur_tuple.data, urp2->uur_tuple.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo tuple data did not match");
+}
+
+/*
+ * test_insert_and_fetch - test simple insert and fetch undo record API
+ */
+static void
+test_insert_and_fetch()
+{
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	char	data[5000];
+	int		 len = 5000;
+	UnpackedUndoRecord	undorecord = {0};
+	UnpackedUndoRecord *undorecord_out;
+	UndoRecPtr	undo_ptr;
+
+	/* Prepare dummy undo record*/
+	undorecord.uur_rmid = 1;
+	undorecord.uur_type = 2;
+	undorecord.uur_info = 0;
+	undorecord.uur_xid = 100;
+	undorecord.uur_cid = 1;
+	undorecord.uur_fork = MAIN_FORKNUM;
+	undorecord.uur_blkprev = 10;
+	undorecord.uur_block = 1;
+	undorecord.uur_offset = 10;
+
+	/* Insert large data so that record get split across pages. */
+	initStringInfo(&undorecord.uur_tuple);
+	memset(data, 'a', 5000);
+	appendBinaryStringInfo(&undorecord.uur_tuple,
+						   (char *) data,
+						   len);
+	initStringInfo(&undorecord.uur_payload);
+	appendBinaryStringInfo(&undorecord.uur_payload,
+						   (char *) data,
+						   len);
+	/* Prepare undo record. */
+	BeginUndoRecordInsert(&context, persistence, 2, NULL);
+	undo_ptr = PrepareUndoInsert(&context, &undorecord, InvalidFullTransactionId);
+
+	/* Insert prepared undo record under critical section. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	/* Fetch inserted undo record. */
+	undorecord_out = UndoFetchRecord(undo_ptr, InvalidBlockNumber,
+									 InvalidOffsetNumber,
+									 InvalidTransactionId, NULL,
+									 NULL);
+	/* compare undo records. */
+	compare_undo_record(&undorecord, undorecord_out);
+
+	UndoRecordRelease(undorecord_out);
+	pfree(undorecord.uur_tuple.data);
+}
+
+#define MAX_UNDO_RECORD 10
+/*
+ * test_bulk_fetch - test the bulk fetch API.
+ */
+static void
+test_bulk_fetch()
+{
+	int i;
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	UndoRecInfo	urp_in_array[MAX_UNDO_RECORD];
+	UndoRecInfo *urp_out_array;
+	UnpackedUndoRecord	uur[MAX_UNDO_RECORD] = {{0}};
+	UndoRecPtr	undo_ptr;
+	int			nrecords = 0;
+
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		uur[i].uur_rmid = 1;
+		uur[i].uur_type = 2;
+		uur[i].uur_info = 0;
+		uur[i].uur_xid = 100;
+		uur[i].uur_cid = 1;
+		uur[i].uur_fork = MAIN_FORKNUM;
+		uur[i].uur_blkprev = 10;
+		uur[i].uur_block = i;
+		uur[i].uur_offset = i + 1;
+		urp_in_array[i].uur = &uur[i];
+	}
+
+	/* Prepare multiple undo records. */
+	BeginUndoRecordInsert(&context, persistence, MAX_UNDO_RECORD, NULL);
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		undo_ptr = PrepareUndoInsert(&context, &uur[i], InvalidFullTransactionId);
+		urp_in_array[i].urp = undo_ptr;
+	}
+
+	/* Insert them all in one shot. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	undo_ptr = urp_in_array[MAX_UNDO_RECORD - 1].urp;
+
+	/*
+	 * Perform the bulk fetch. 2000 bytes are enough to hold 10 records.  Later
+	 * we can enhance this to test the fetch in multi batch by increasing the
+	 * record counts or reducing undo_apply_size to smaller value.
+	 */
+	urp_out_array = UndoBulkFetchRecord(&undo_ptr, urp_in_array[0].urp, 2000,
+										&nrecords, false);
+	/* Check whether we have got all the record we inserted. */
+	if (nrecords != MAX_UNDO_RECORD)
+		elog(ERROR, "undo record count did not match");
+
+	/* Compare all records we have fetch using bulk fetch API*/
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		if (urp_in_array[i].urp != urp_out_array[MAX_UNDO_RECORD - 1 - i].urp)
+			elog(ERROR, "undo record pointer did not match");
+		compare_undo_record(urp_in_array[i].uur, urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+		UndoRecordRelease(urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+	}
+}
+/*
+ * Undo API test module
+ */
+Datum
+test_undo_api(PG_FUNCTION_ARGS)
+{
+	/* Test simple insert and fetch record. */
+	test_insert_and_fetch();
+
+	/* Test undo record bulk fetch API*/
+	test_bulk_fetch();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_undo_api/test_undo_api.control b/src/test/modules/test_undo_api/test_undo_api.control
new file mode 100644
index 0000000..09df344
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.control
@@ -0,0 +1,4 @@
+comment = 'test_undo_api'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_api'
+relocatable = true
-- 
1.8.3.1

0007-Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=0007-Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From 78972741bd6e4af434090887903e51933866cb36 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 07/14] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoaccess.c         | 1536 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         |  797 +++++++++++++
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  118 ++
 src/include/access/undorecord.h              |  256 +++++
 7 files changed, 2738 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..049a416 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoaccess.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 0000000..4cb5cc8
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1536 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the next log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the first and last undo
+ * record of the transaction in the previous log.  The last undo record
+ * location is used find the previous undo record pointer during rollback.
+ * The first undo record location is used to find the previous transaction
+ * header which is required to update the undo apply progress.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoaccess.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+									UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress
+ *
+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */
+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	XactUndoRecordInfo *xact_info;
+
+	xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Is there some byte of the urec_next on the current block, if not then
+	 * start from the next block.
+	 */
+	if (remaining_bytes <= offset)
+	{
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+		starting_byte += (offset - remaining_bytes);
+	}
+	else
+		starting_byte += offset;
+
+	/*
+	 * Set the offset in the first block where we need to start writing, during
+	 * the prepare phase so that during update phase we need not to compute it
+	 * again.
+	 */
+	xact_info->offset = starting_byte;
+
+	/* Loop until we have fetched all the buffers in which we need to write. */
+	while (size > 0)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	xact_info->next = InvalidUndoRecPtr;
+	xact_info->progress = 0;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	return (context->nxact_urec_info - 1);
+}
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr xact_urp)
+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetPersistence(xact_urp) == UNDO_TEMP)
+		return;
+
+	slot = UndoLogGetSlot(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoLogIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_next);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the next pointer in xact_urec_info, this will be overwritten in
+	 * actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].next = urecptr;
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&slot->discard_update_lock);
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			i = 0;
+	int			write_bytes;
+	int			write_offset;
+	char	   *sourceptr;
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/* Whether to update the next or undo apply progress. */
+	if (UndoRecPtrIsValid(xact_info->next))
+	{
+		sourceptr = (char *) &xact_info->next;
+		write_bytes = sizeof(xact_info->next);
+	}
+	else
+	{
+		sourceptr = (char *) &xact_info->progress;
+		write_bytes = sizeof(xact_info->progress);
+	}
+
+	/* Where to start writing in the current block. */
+	write_offset = xact_info->offset;
+
+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */
+	while(write_bytes > 0)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+		int			can_write;
+		char	   *writeptr;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/* How may bytes can be written in the current page. */
+		can_write = Min((BLCKSZ - write_offset), write_bytes);
+
+		/*
+		 * If buffer is valid then write it otherwise just skip writing it but
+		 * compute the variable for writing into the next block.
+		 */
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+
+			/* Compute the write pointer. */
+			writeptr = (char *) page + write_offset;
+
+			/* Copy the bytes we can write. */
+			memcpy(writeptr, sourceptr, can_write);
+			MarkBufferDirty(buffer);
+		}
+
+		/* Update bookkeeping information. */
+		write_bytes -= can_write;
+		sourceptr += can_write;
+		write_offset = UndoLogBlockHeaderSize;
+		i++;
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = InvalidBlockNumber;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If we have a valid undo start pointer in the previous log then we need
+	 * a log switch header otherwise we don't.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		urec->uur_prevurp = prevlogurp;
+		urec->uur_prevlogstart = prevlog_xact_start;
+	}
+	else
+	{
+		/* We don't need a log transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_LOGSWITCH;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareUpdateNext(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareUpdateNext(context, urecptr, prevlog_xact_start);
+	}
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/* Free memory allocated for the prepare undo and prepared buffers. */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			ReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogSlot *slot;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			/*
+			 * The undo log number is unknown.  Presumably it has been
+			 * entirely discarded.
+			 */
+			urp = InvalidUndoRecPtr;
+			break;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 *
+		 * In Hot Standby mode log->oldest_data is never initialized because
+		 * it's get updated by undo discard worker whereas in HotStandby undo
+		 * logs are getting discarded using discard WAL.  So in HotStandby we
+		 * can directly check whether the undo record pointer is discarded or
+		 * not.  But, we can not do same for normal case because discard
+		 * worker can concurrently discard the undo logs.
+		 *
+		 * XXX We can avoid this check by always initializing log->oldest_data
+		 * in HotStandby mode as well whenever we apply discard WAL.  But, for
+		 * doing that we need to acquire discard lock just for setting this
+		 * variable?
+		 */
+		if (InHotStandby)
+		{
+			if (UndoLogIsDiscarded(urp))
+			{
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+		else
+		{
+			LWLockAcquire(&slot->discard_lock, LW_SHARED);
+			if (slot->logno != logno || urp < slot->oldest_data)
+			{
+				/*
+				 * The slot has been recycled because the undo log was
+				 * entirely discarded, or the pointer is before the oldest
+				 * data.
+				 */
+				LWLockRelease(&slot->discard_lock);
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+
+		/* Fetch the current undo record. */
+		UndoGetOneRecord(urec, urp, rnode, slot->meta.persistence, &buffer);
+
+		/* Release the discard lock after fetching the record. */
+		LWLockRelease(&slot->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	/*
+	 * If we have not found any valid undo record that means it might have
+	 * already got discarded so release the memory we allocated for unpacked
+	 * undo record and set urec to NULL.
+	 */
+	if (!UndoRecPtrIsValid(urp))
+	{
+		pfree(urec);
+		urec = NULL;
+	}
+	else if (urec_ptr_out != NULL)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogSlot *slot;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = slot->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoLogIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&slot->discard_lock, LW_SHARED);
+				if (slot->logno != logno || urecptr < slot->oldest_data)
+				{
+					/*
+					 * The undo log slot has been recycled because it was
+					 * entirely discarded, or the data has been discarded
+					 * already.
+					 */
+					LWLockRelease(&slot->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			LWLockRelease(&slot->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit or
+		 * undo log got switched then stop processing more records.  Remember to
+		 * set the from_urecptr so that on next call we can resume fetching undo
+		 * records where we left it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size ||
+			UndoRecPtrIsValid(uur->uur_prevurp))
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..08b3151
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,797 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		ucontext->urec_logswitch.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_logswitch.urec_prevlogstart = uur->uur_prevlogstart;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordLogSwitch)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordLogSwitch;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_logswitch,
+								   SizeOfUndoRecordLogSwitch,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		uur->uur_prevurp = ucontext->urec_logswitch.urec_prevurp;
+		uur->uur_prevlogstart = ucontext->urec_logswitch.urec_prevlogstart;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 6cbb0c8..0ac7f73 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 0000000..e12029b
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* Undo record pointer to be updated. */
+	uint32		offset;			/* offset in page where to start updating. */
+	UndoRecPtr	next;			/* first urp of the next transaction which is to
+								 * be updated in transaction header */
+	BlockNumber	progress;		/* undo apply action progress. */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..baededc
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,256 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_LOGSWITCH is set, an UndoRecordLogSwitch structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_TRANSACTION				0x01
+#define UREC_INFO_FORK						0x02
+#define UREC_INFO_BLOCK						0x04
+#define UREC_INFO_BLKPREV					0x08
+#define UREC_INFO_LOGSWITCH					0x10
+#define UREC_INFO_PAYLOAD					0x20
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * Undo block number where we need to start reading the undo for applying
+	 * the undo action.   InvalidBlockNumber means undo applying hasn't started
+	 * for the transaction and MaxBlockNumber mean undo completely applied. And,
+	 * any other block number means we have applied partial undo so next we can
+	 * start from this block.
+	 */
+	BlockNumber	urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 *
+	 * TODO: instead of keeping in transaction header we can have new log
+	 * switch header.
+	 */
+	UndoRecPtr	urec_prevurp;
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information of the transaction's undo in the previous log.  If a transaction
+ * is split across the undo logs then this header will be included in the first
+ * undo record of the transaction in next log.
+ */
+typedef struct UndoRecordLogSwitch
+{
+	UndoRecPtr	urec_prevurp;		/* Transaction's last undo record pointer in
+									 * the previous undo log. */
+	UndoRecPtr	urec_prevlogstart;	/* Transaction's first undo record pointer
+									 * in previous undo log. */
+} UndoRecordLogSwitch;
+
+#define SizeOfUndoRecordLogSwitch \
+	(offsetof(UndoRecordLogSwitch, urec_prevlogstart) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	BlockNumber	uur_progress;	/* undo applying progress, see detail comment in
+								 * UndoRecordTransaction */
+	Oid			uur_dbid;		/* database id */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	UndoRecPtr	uur_prevlogstart; /* urec pointer to the first record in the
+									* previous log. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_LOGSWITCH,	/* The next thing to be processed is the log
+								 * switch details. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordLogSwitch urec_logswitch; /* Log switch header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

0010-Extend-binary-heap-functionality.patchapplication/octet-stream; name=0010-Extend-binary-heap-functionality.patchDownload
From 46cee827ba2407accebfd5bcf1b73493552e3b41 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:02:23 +0530
Subject: [PATCH 10/14] Extend binary heap functionality.

Add the routines to allocate binary heap in shared memory and to remove
nth element from binray heap.  This routines will be used by latter commit
to add support for undo workers.

Author: Kuntal Ghosh and Amit Kapila
---
 src/backend/lib/binaryheap.c | 118 +++++++++++++++++++++++++++++++++++++++++++
 src/include/lib/binaryheap.h |  12 ++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..5f7454d 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -212,6 +243,79 @@ binaryheap_replace_first(binaryheap *heap, Datum d)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap in O(1).  The caller must ensure that this routine is not used on
+ * an empty heap and is not called with n greater than or equal to the heap
+ * size.
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer to it in
+ * O(1) without preserving the heap property.  This is a convenience routine
+ * to remove elements quickly.  To obtain a valid heap, one must call
+ * binaryheap_build() afterwards.  The caller must ensure that this routine is
+ * not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * Swap the contents of two nodes.
  */
 static inline void
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 21f96b9..ed9e8e8 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -38,8 +38,11 @@ typedef struct binaryheap
 } binaryheap;
 
 extern binaryheap *binaryheap_allocate(int capacity,
-									   binaryheap_comparator compare,
-									   void *arg);
+					binaryheap_comparator compare,
+					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
+extern Size binaryheap_shmem_size(int capacity);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
-- 
1.8.3.1

0009-undo-page-consistency-checker.patchapplication/octet-stream; name=0009-undo-page-consistency-checker.patchDownload
From aeb8733ec3a969b626212fcbb904dd2fad9d2904 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 13 Jun 2019 09:20:39 +0530
Subject: [PATCH 09/14] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar with help from Amit Khandekar and Rafia Sabih
---
 src/backend/access/undo/undoaccess.c |   5 +-
 src/backend/access/undo/undorecord.c | 145 ++++++++++++++++++++++++++++++++---
 src/backend/storage/page/bufpage.c   |  33 ++++++++
 src/include/access/undolog.h         |   2 +-
 src/include/access/undorecord.h      |   2 +-
 src/include/storage/bufpage.h        |  34 ++++++++
 6 files changed, 206 insertions(+), 15 deletions(-)

diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 4cb5cc8..32bb2e6 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -775,7 +775,10 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 				 * block header.
 				 */
 				if (starting_byte == UndoLogBlockHeaderSize)
-					PageInit(page, BLCKSZ, 0);
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
 
 				/*
 				 * Try to insert the record into the current page. If it
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index 08b3151..b323350 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 
+#include "access/bufmask.h"
 #include "access/subtrans.h"
 #include "access/undorecord.h"
 #include "catalog/pg_tablespace.h"
@@ -25,28 +26,45 @@ static bool ReadUndoBytes(char *destptr, int readlen,
 			  char **readptr, char *endptr,
 			  int *total_bytes_read, int *partial_read);
 
-/*
- * Compute and return the expected size of an undo record.
- */
-Size
-UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+ /*
+  * Compute the header size of the undo record.
+  */
+static inline Size
+UndoRecordHeaderSize(uint8 uur_info)
 {
-	Size		size;
+	Size	size;
 
 	size = SizeOfUndoRecordHeader + sizeof(uint16);
-	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+	if ((uur_info & UREC_INFO_FORK) != 0)
 		size += sizeof(ForkNumber);
-	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
 		size += SizeOfUndoRecordBlock;
-	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	if ((uur_info & UREC_INFO_BLKPREV) != 0)
 		size += sizeof(UndoRecPtr);
-	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
 		size += SizeOfUndoRecordTransaction;
-	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
 		size += SizeOfUndoRecordLogSwitch;
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
 	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
 	{
-		size += SizeOfUndoRecordPayload;
 		size += uur->uur_payload.len;
 		size += uur->uur_tuple.len;
 	}
@@ -55,6 +73,30 @@ UndoRecordExpectedSize(UnpackedUndoRecord *uur)
 }
 
 /*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint8           uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size            size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload  *payload = (UndoRecordPayload *) page_ptr + size;
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
  * Compute size of the Unpacked undo record in memory
  */
 Size
@@ -74,6 +116,85 @@ UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
 	return size;
 }
 
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset = SizeOfUndoRecordHeader - sizeof(CommandId);
+	UndoPageHeader	phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page has
+	 * a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size	partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						   phdr->tuple_len + phdr->payload_len -
+						   phdr->record_offset;
+
+		/*
+		 * We just want to mask the cid in the undo record header.  So only if
+		 * the partial record in the current page include the undo record header
+		 * then we need to mask the cid bytes in this page.  Otherwise, directly
+		 * jump to the next record.
+		 */
+		if (phdr->record_offset < SizeOfUndoRecordHeader)
+		{
+			char   *cid_data;
+			Size	mask_size;
+
+			mask_size = Min(SizeOfUndoRecordHeader -
+							phdr->record_offset, sizeof(CommandId));
+
+			cid_data = next_record + cid_offset - phdr->record_offset;
+			memset(&cid_data, MASK_MARKER, mask_size);
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/*
+		 * If this is not complete record then check whether cid is on
+		 * this page or not.  If not then we are done with this page.
+		 */
+		if (page_end - next_record < SizeOfUndoRecordHeader)
+		{
+			int		mask_size = page_end - next_record - cid_offset;
+
+			if (mask_size > 0)
+				memset(&header->urec_cid, MASK_MARKER, mask_size);
+			break;
+		}
+		else
+		{
+			/* Mask cid */
+			memset(&header->urec_cid, MASK_MARKER, sizeof(header->urec_cid));
+		}
+
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
+
 /*
  * Initiate inserting an undo record.
  *
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810..de609054 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,39 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint8 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	/*
+	 * TODO: We can update the value of the p->pd_lower whenever we insert
+	 * a record into an undo page.  By doing this we can avoid processing
+	 * complete undo page if there are no more records.
+	 */
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 994c6a6..d2b2c2a 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -128,7 +128,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index baededc..043518a 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -20,7 +20,6 @@
 #include "storage/buf.h"
 #include "storage/off.h"
 
-
 /*
  * Every undo record begins with an UndoRecordHeader structure, which is
  * followed by the additional structures indicated by the contents of
@@ -252,5 +251,6 @@ extern void InsertUndoData(UndoPackContext *ucontext, Page page,
 			   int starting_byte);
 extern void SkipInsertingUndoData(UndoPackContext *ucontext,
 					  int bytes_to_skip);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 34b68ad..bbe42f9 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -216,6 +216,37 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * FIXME:  It should be declared in undolog.h ?
+ *
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+	/* Fields required for undolog consistency checker */
+	uint8		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -419,6 +450,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint8 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

0012-Infrastructure-to-execute-pending-undo-actions.patchapplication/octet-stream; name=0012-Infrastructure-to-execute-pending-undo-actions.patchDownload
From 258f3ad508703aeac4cbb6068148ad878cebc8b7 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:27:58 +0530
Subject: [PATCH 12/14] Infrastructure to execute pending undo actions.

To apply the undo actions, we collect the undo records in bulk and try to
process them together.  We ensure to update the transaction's progress at
regular intervals so that after a crash we can skip already applied undo.

This provides a way for users to register a callback for processing the
undo records based on resource manager.

Dilip Kumar, Amit Kapila, Thomas Munro and Kuntal Ghosh with inputs from
Robert Haas
---
 src/backend/access/rmgrdesc/Makefile         |   3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c |  47 +++
 src/backend/access/transam/rmgr.c            |   5 +-
 src/backend/access/undo/Makefile             |   3 +-
 src/backend/access/undo/undoaccess.c         |  39 ++-
 src/backend/access/undo/undoaction.c         | 450 +++++++++++++++++++++++++++
 src/backend/access/undo/undoactionxlog.c     |  60 ++++
 src/backend/replication/logical/decode.c     |   1 +
 src/bin/pg_rewind/parsexlog.c                |   2 +-
 src/bin/pg_waldump/rmgrdesc.c                |   3 +-
 src/include/access/rmgr.h                    |   2 +-
 src/include/access/rmgrlist.h                |  47 +--
 src/include/access/undoaccess.h              |   4 +
 src/include/access/undoaction_xlog.h         |  39 +++
 src/include/access/undorequest.h             |   3 -
 src/include/access/xlog_internal.h           |   8 +-
 16 files changed, 679 insertions(+), 37 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/include/access/undoaction_xlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..6238240 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 7327502..68696bc 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
+OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
+	   undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 25b2968..62cdfbf 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -65,8 +65,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 				 Buffer *prevbuf);
 static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
 						   UndoRecPtr xact_urp, int size, int offset);
-static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
-						  int idx);
 static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 									UndoRecPtr urecptr, UndoRecPtr xact_urp);
 static int UndoGetBufferSlot(UndoRecordInsertContext *context,
@@ -237,6 +235,41 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 }
 
 /*
+ * Prepare to update the undo apply progress in the transaction header.
+ */
+void
+UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+							   UndoRecPtr xact_urp, BlockNumber progress)
+{
+	int			index = 0;
+	int			offset;
+
+	Assert(UndoRecPtrIsValid(xact_urp));
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetPersistence(xact_urp) == UNDO_TEMP)
+		return;
+
+	/* It shouldn't be discarded. */
+	Assert(!UndoLogIsDiscarded(xact_urp));
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_progress);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the undo action progress in xact_urec_info, this will be overwritten
+	 * in actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].progress = progress;
+}
+
+/*
  * Overwrite the first undo record of the previous transaction to update its
  * next pointer.
  *
@@ -244,7 +277,7 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
  * This must be called under the critical section.  This will just overwrite the
  * header of the undo record.
  */
-static void
+void
 UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..54bae60
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,450 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to
+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+static void UpdateUndoApplyProgress(UndoRecPtr last_log_start_urec_ptr,
+						  BlockNumber block_num);
+static bool UndoAlreadyApplied(FullTransactionId full_xid,
+						UndoRecPtr to_urecptr);
+static void ApplyUndo(UndoRecInfo *urecinfo, int nrecords);
+static void ProcessAndApplyUndo(FullTransactionId full_xid,
+				UndoRecPtr from_urecptr, UndoRecPtr to_urecptr,
+				UndoRecPtr last_log_start_urec_ptr, bool complete_xact);
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else if (luur->uur_block > ruur->uur_block)
+		return 1;
+	else if (luur->uur_offset < ruur->uur_offset)
+		return -1;
+	else if (luur->uur_offset > ruur->uur_offset)
+		return 1;
+	else if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+	{
+		/*
+		 * If records are for the same block and offset, then maintain their
+		 * existing order by comparing their index in the array.
+		 */
+		return -1;
+	}
+	else
+		return 1;
+}
+
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoPersistence persistence;
+	UndoRecordInsertContext context = {{0}};
+
+	persistence =
+		UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	BeginUndoRecordInsert(&context, persistence, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,
+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	TransactionId	xid PG_USED_FOR_ASSERTS_ONLY = XidFromFullTransactionId(full_xid);
+
+	uur = UndoFetchRecord(to_urecptr, InvalidBlockNumber, InvalidOffsetNumber,
+						  InvalidTransactionId, NULL, NULL);
+
+	/* already processed and discarded */
+	if (uur == NULL)
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+		return true;
+	}
+
+	/* already processed */
+	if (IsXactApplyProgressCompleted(uur->uur_progress))
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+		UndoRecordRelease(uur);
+		return true;
+	}
+
+	Assert(xid == uur->uur_xid);
+
+	UndoRecordRelease(uur);
+
+	return false;
+}
+
+/*
+ * ApplyUndo - Invode rmgr specific undo apply functions.
+ *
+ * urecinfo - An array of undo records sorted in the rmgr order.
+ * nrecords - number of records in this array.
+ */
+static void
+ApplyUndo(UndoRecInfo *urecinfo, int nrecords)
+{
+	int			rmgr_start_idx = 0;
+	int			rmgr_nrecords = 0;
+	int			prev_rmid = -1;
+	int			i;
+
+	/* Apply the undo action for each rmgr. */
+	for (i = 0; i < nrecords; i++)
+	{
+		UnpackedUndoRecord *uur = urecinfo[i].uur;
+
+		Assert(uur->uur_rmid >= 0);
+
+		/*
+		 * If this undo is not for the same rmgr then apply all undo
+		 * actions for the previous rmgr.
+		 */
+		if (prev_rmid >= 0 &&
+			prev_rmid != uur->uur_rmid)
+		{
+			Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+			RmgrTable[prev_rmid].rm_undo(rmgr_nrecords,
+										 &urecinfo[rmgr_start_idx]);
+
+			rmgr_start_idx = i;
+			rmgr_nrecords = 0;
+		}
+
+		rmgr_nrecords++;
+		prev_rmid = uur->uur_rmid;
+	}
+
+	/* Apply the last set of the actions. */
+	Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+	RmgrTable[prev_rmid].rm_undo(rmgr_nrecords, &urecinfo[rmgr_start_idx]);
+}
+
+/*
+ * ProcessAndApplyUndo - Fetch undo records and apply actions.
+ *
+ * We always process the undo of the last log when the undo for a transaction
+ * spans across multiple logs.  Then from there onwards the previous undo logs
+ * for the same transaction are processed.
+ *
+ * We also update the undo apply progress in the transaction header so that
+ * after recovery we don't need to process the records that are already
+ * processed.  As we update the progress only after one batch of records,
+ * the crash in-between can cause us to read/apply part of undo records
+ * again but this will never be more than one-batch.  We can further optimize
+ * it by marking the progress in each record, but that has its own downsides
+ * like it will generate more WAL and I/O corresponding to dirty undo buffers.
+ */
+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	TransactionId	xid PG_USED_FOR_ASSERTS_ONLY = XidFromFullTransactionId(full_xid);
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	do
+	{
+		BlockNumber	progress_block_num;
+		int			i;
+		int			nrecords;
+		bool		low_switched = false;
+		bool		update_progress = false;
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update
+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		if (complete_xact)
+		{
+			if (urecinfo[nrecords - 1].uur->uur_info & UREC_INFO_LOGSWITCH)
+			{
+				/*
+				 * We have crossed the log boundary.  The rest of the undo for
+				 * this transaction is in some other log, the location of which
+				 * can be found from this record.  See commets atop undoaccess.c.
+				 */
+				low_switched = true;
+				last_log_start_urec_ptr = urecinfo[nrecords - 1].uur->uur_prevlogstart;
+			}
+			else if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(urecinfo[nrecords - 1].urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(urecinfo[0].urp) > progress_block_num)
+					update_progress = true;
+			}
+		}
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(TransactionIdEquals(xid, urecinfo[0].uur->uur_xid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urecinfo, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		/* Call resource manager specific callbacks to apply actions. */
+		ApplyUndo(urecinfo, nrecords);
+
+		/*
+		 * Set undo action apply progress as completed in the transaction header
+		 * if this is a main transaction.
+		 */
+		if (complete_xact)
+		{
+			if (low_switched)
+			{
+				/*
+				 * We have crossed the log boundary.  So, mark current log
+				 * header as complete and set the next progress location in the
+				 * previous log.
+				 */
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else if (!UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else if (update_progress)
+			{
+				/*
+				 * Update the progress block number.  We increase the block
+				 * number by one since the current block might have some undo
+				 * records that are yet to be applied.  But, all undo records
+				 * from the next block must have been applied.
+				 */
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										progress_block_num + 1);
+			}
+		}
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+	} while (true);
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{
+	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	if (complete_xact)
+	{
+		/*
+		 * It is important here to fetch the latest undo record and validate if
+		 * the actions are already executed.  The reason is that it is possible
+		 * that discard worker or backend might try to execute the rollback
+		 * request which is already executed.  For ex., after discard worker
+		 * fetches the record and found that this transaction need to be
+		 * rolledback, backend might concurrently execute the actions and
+		 * remove the request from rollback hash table.
+		 *
+		 * The other case where this will be required is when the transactions
+		 * records span across multiple logs.  Say, we register the
+		 * transaction from the first log and then we encounter the same
+		 * transaction in the second log where its status is still not marked
+		 * as done.  Now, before we try to register the request for the second
+		 * log, the undo worker came along rolled back the previous request
+		 * and removed its hash entry.  In this case, we will successfully
+		 * register the request from the second log and it should be detected
+		 * here.
+		 */
+		if (UndoAlreadyApplied(full_xid, to_urecptr))
+			return;
+
+		last_log_start_urec_ptr =
+			RollbackHTGetLastLogStartUrp(full_xid, to_urecptr);
+	}
+
+	ProcessAndApplyUndo(full_xid, from_urecptr, to_urecptr,
+						last_log_start_urec_ptr, complete_xact);
+
+	/*
+	 * Undo actions are applied so delete the hash table entry.
+	 */
+	RollbackHTRemoveEntry(full_xid, to_urecptr);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..2b06d114
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoPersistence persistence;
+	UndoRecordInsertContext context = {{0}};
+
+	persistence =
+		UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, persistence, 1, record);
+
+	/* Update the undo apply progress in the transaction header. */
+	UndoRecordPrepareApplyProgress(&context, xlrec->urec_ptr,
+								   xlrec->progress);
+
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index f2ed561..d075aa9 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 44f4c41..5b49cd4 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..396193b 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..e1fb42a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..ef0f6ac 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,26 +25,27 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 139e2e8..4d5f292 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -13,6 +13,7 @@
 #ifndef UNDOACCESS_H
 #define UNDOACCESS_H
 
+#include "access/transam.h"
 #include "access/undolog.h"
 #include "access/undorecord.h"
 #include "access/xlogdefs.h"
@@ -91,6 +92,9 @@ typedef struct UndoRecordInsertContext
 	int			nxact_urec_info;	/* Number of previous xact info. */
 } UndoRecordInsertContext;
 
+extern void UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+					UndoRecPtr urecptr, BlockNumber progress);
+extern void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx);
 extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
 					  UndoPersistence persistence,
 					  int nprepared,
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
index 2c9b11d..099eaa5 100644
--- a/src/include/access/undorequest.h
+++ b/src/include/access/undorequest.h
@@ -218,8 +218,5 @@ extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin
 /* functions exposed from undoaction.c */
 extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool nopartial);
-extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
-						  int last_idx, Oid reloid, FullTransactionId full_xid,
-						  BlockNumber blkno, bool blk_chain_complete);
 
 #endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 3cc9c3d..e66d046 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -19,10 +19,14 @@
 #ifndef XLOG_INTERNAL_H
 #define XLOG_INTERNAL_H
 
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undorecord.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -295,9 +299,11 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	void		(*rm_undo) (int nrecords, UndoRecInfo *records);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
-- 
1.8.3.1

0011-Infrastructure-to-register-and-fetch-undo-action-req.patchapplication/octet-stream; name=0011-Infrastructure-to-register-and-fetch-undo-action-req.patchDownload
From 527d06e064b3a68c333ee10bf176a9c7ee5d7a35 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:10:06 +0530
Subject: [PATCH 11/14] Infrastructure to register and fetch undo action
 requests.

This infrasture provides a way to allow execution of undo actions.  One
might think that we can always execute undo actions on error or explicit
rollabck by user, however there are cases when that is not posssible.
For example, (a) if the system crash while doing operation, then after
startup, we need a way to perform undo actions; (b) If we get error while
performing undo actions.

Apart from this, when there are large rollback requests, then it is quite
inefficient to perform all the undo actions and then return control to
user.

To allow efficient execution of the undo actions, we create three queues
and a hash table for the rollback requests.  A Xid based priority queue
which will allow us to process the requests of older transactions and help
us to move oldesdXidHavingUndo (this is a xid-horizon below which all the
transactions are visible) forward.  A size-based queue which will help us
to perform the rollbacks of larger aborts in a timely fashion so that we
don't get stuck while processing them during discard of the logs.  An error
queue to hold the requests for transactions that failed to apply its undo.
The rollback hash table is used to avoid duplicate undo requests by backends
and discard worker.

Amit Kapila and Kuntal Ghosh, design idea by Andres Freund.
---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undoaccess.c          |   51 +-
 src/backend/access/undo/undorecord.c          |    2 +-
 src/backend/access/undo/undorequest.c         | 1629 +++++++++++++++++++++++++
 src/backend/storage/lmgr/lwlocknames.txt      |    1 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    8 +
 src/include/access/undoaccess.h               |    2 +
 src/include/access/undorecord.h               |    1 +
 src/include/access/undorequest.h              |  225 ++++
 src/include/miscadmin.h                       |    1 +
 src/include/storage/proc.h                    |    2 +
 14 files changed, 1959 insertions(+), 3 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 049a416..7327502 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o
+OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 32bb2e6..25b2968 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -46,6 +46,7 @@
 #include "access/undorecord.h"
 #include "access/undoaccess.h"
 #include "access/undolog_xlog.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -588,7 +589,7 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	}
 
 	/* Initialize transaction related members. */
-	urec->uur_progress = InvalidBlockNumber;
+	urec->uur_progress = XACT_APPLY_PROGRESS_NOT_STARTED;
 	if (need_xact_header)
 	{
 		/*
@@ -1537,3 +1538,51 @@ UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 	/* calculate the previous undo record pointer */
 	return MakeUndoRecPtr(logno, offset - prevlen);
 }
+
+/*
+ * Returns the undo record pointer corresponding to first record in the given
+ * block.
+ */
+UndoRecPtr
+UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+							UndoPersistence persistence)
+{
+	Buffer buffer;
+	Page page;
+	UndoPageHeader	phdr;
+	RelFileNode		rnode;
+	UndoLogOffset	log_cur_off;
+	Size			partial_rec_size;
+	int				offset_cur_page;
+
+	if (blkno < 0 || blkno > MaxBlockNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid undo block number")));
+
+	UndoRecPtrAssignRelFileNode(rnode, urec_ptr);
+
+	buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+									   rnode, UndoLogForkNum, blkno,
+									   RBM_NORMAL, NULL,
+									   RelPersistenceForUndoPersistence(persistence));
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader)page;
+
+	/* Calculate the size of the partial record. */
+	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						phdr->tuple_len + phdr->payload_len -
+						phdr->record_offset;
+
+	/* calculate the offset in current log. */
+	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
+	log_cur_off = (blkno * BLCKSZ) + offset_cur_page;
+
+	UnlockReleaseBuffer(buffer);
+
+	/* calculate the undo record pointer based on current offset in log. */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urec_ptr), log_cur_off);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index b323350..3d88c79 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -29,7 +29,7 @@ static bool ReadUndoBytes(char *destptr, int readlen,
  /*
   * Compute the header size of the undo record.
   */
-static inline Size
+Size
 UndoRecordHeaderSize(uint8 uur_info)
 {
 	Size	size;
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..e46cfc4
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1629 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To ensure that backend and discard worker don't register the same request
+ * in the hash table, we always register the request with full_xid and the
+ * start pointer for the transaction in the hash table as key.  Backends
+ * always remember the value of start pointer, but discard worker doesn't know
+ * the actual start value in case transaction's undo spans across multiple
+ * logs.  The reason for the same is that discard worker might encounter the
+ * log which has overflowed undo records of the transaction first.  In such
+ * cases, we need to compute the actual start position.  The first record of a
+ * transaction in each undo log contains a reference to the first record of
+ * this transaction in the previous log.  By following the previous log chain
+ * of this transaction, we find the initial location which is used to register
+ * the request.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+#include "storage/proc.h"
+
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+
+int			rollback_overflow_size = 64;
+int			pending_undo_queue_size = 1024;
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdEquals(xidQueueElem1->full_xid,
+									 xidQueueElem2->full_xid))
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size == sizeQueueElem2->request_size)
+		return 0;
+	return -1;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at == errQueueElem2->err_occurred_at)
+		return 0;
+	return -1;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * pending_undo_queue_size) + pending_undo_queue_size +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo * urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+		{
+			cur_undo_queue++;
+			return false;
+		}
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * last_log_start_urec_ptr_out - This is an OUT parameter.  If a transaction
+ * writes undo records in multiple undo logs, this is set to the start undo
+ * record pointer of this transaction in the last log.  If the transaction
+ * writes undo records only in single undo log, it is set to start_urec_ptr.
+ * This value is used to update the rollback progress of the transaction in
+ * the last log.  Once, we have start location in last log, the start location
+ * in all the previous logs can be computed.  See execute_undo_actions for
+ * more details.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+static uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   UndoRecPtr *last_log_start_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	UndoRecPtr	last_log_start_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoPersistence persistence;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				last_log_start_urecptr = urecptr;
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+		persistence = slot->meta.persistence;
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * If the corresponding undo record got rolled back and discarded as
+		 * well, we return from here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * Case 1: Check whether any undo records have been applied from this
+		 * log.  Else, we've to find the undo location till where the undo
+		 * actions have been applied.
+		 */
+		if (!IsXactApplyProgressNotStarted(uur->uur_progress))
+		{
+			/*
+			 * If all the undo records in this log corresponding to this
+			 * transaction, has been applied, we return from here.
+			 */
+			if (IsXactApplyProgressCompleted(uur->uur_progress))
+				break;
+
+			/*
+			 * Find the first undo record of uur_progress block number.  We'll
+			 * set end_urec_ptr to this undo record.
+			 */
+			end_urecptr = UndoBlockGetFirstUndoRecord(uur->uur_progress, urecptr,
+													  persistence);
+
+			/*
+			 * Since rollbacks from this undo log are in-progress, all undo
+			 * records from subsequent undo logs must have been applied.  Hence,
+			 * this is the last log.  So, we set last_log_start_urecptr as the
+			 * start undo record pointer of this transaction from current log.
+			 */
+			last_log_start_urecptr = urecptr;
+			sz += (end_urecptr - urecptr);
+			break;
+		}
+
+		next_urecptr = uur->uur_next;
+
+		/*
+		 * Case 2: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			/*
+			 * While fetching the next insert location if the new transaction
+			 * has already started in this log then lets re-fetch the undo
+			 * record.
+			 */
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			if (!UndoRecPtrIsValid(next_insert))
+			{
+				UndoRecordRelease(uur);
+				uur = NULL;
+				continue;
+			}
+
+			/*
+			 * If next_insert location points to the starting location of a
+			 * new page, we should subtract the page header size from the
+			 * insert location.
+			 */
+			if (UndoRecPtrGetPageOffset(next_insert) == UndoLogBlockHeaderSize)
+				next_insert -= UndoLogBlockHeaderSize;
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == slot->logno)
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoLogIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 5: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log but before that consider
+		 * this log for request size computation.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+	if (last_log_start_urecptr_out &&
+		(*last_log_start_urecptr_out == InvalidUndoRecPtr))
+		*last_log_start_urecptr_out = last_log_start_urecptr;
+
+	return sz;
+}
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	{
+		if (GetXidQueueSize() >= pending_undo_queue_size ||
+			GetSizeQueueSize() >= pending_undo_queue_size)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < pending_undo_queue_size))
+		{
+			Assert(GetSizeQueueSize() < pending_undo_queue_size);
+
+			/*
+			 * XXX - Here, we should return true once we have background
+			 * worker facility.
+			 */
+			return false;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(pending_undo_queue_size)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 pending_undo_queue_size,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 pending_undo_queue_size,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 pending_undo_queue_size,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Size Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= pending_undo_queue_size)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as UNDO_REQUEST_INQUEUE so that undo
+	 * launcher or other undo worker can process this request.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->status = UNDO_REQUEST_INQUEUE;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->status = UNDO_REQUEST_INPROGRESS;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || UndoRequestIsInProgress(rh))
+					continue;
+
+				/*
+				 * The request that is present in any queue must be a valid request
+				 * and its status must be in_queue.
+				 */
+				Assert(UndoRequestIsValid(rh));
+				Assert(UndoRequestIsInQueue(rh));
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->status = UNDO_REQUEST_INPROGRESS;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself if the request is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues.  The two reasons why request can't be pushed are (a) the size of
+ * request is smaller than a threshold and the request is not from discard
+ * worker, (b) the undo request queues are full.
+ *
+ * It is not advisable to apply the undo actions of a very large transaction
+ * in the foreground as that can lead to a delay in retruning the control back
+ * to user after abort.
+ */
+bool
+RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+	UndoRecPtr	last_log_start_urec_ptr = InvalidUndoRecPtr;
+	RollbackHashKey hkey;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * Find the rollback request size and the end_urec_ptr (in case of discard
+	 * worker only).
+	 */
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr,
+										  &last_log_start_urec_ptr, full_xid);
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+										   HASH_ENTER_NULL, &found);
+
+	/*
+	 * It can only fail, if the value of pending_undo_queue_size or
+	 * max_connections guc is reduced after restart of the server.
+	 */
+	if (rh == NULL)
+	{
+		Assert(RollbackHTIsFull());
+
+		ereport(PANIC,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("rollback hash table is full, try running with higher value of pending_undo_queue_size")));
+	}
+
+	/* We shouldn't try to add the same rollback request again. */
+	if (!found)
+	{
+		rh->start_urec_ptr = start_urec_ptr;
+		rh->end_urec_ptr = end_urec_ptr;
+		rh->last_log_start_urec_ptr = last_log_start_urec_ptr;
+		rh->dbid = dbid;
+		rh->full_xid = full_xid;
+
+		/* Increment the pending request counter. */
+		ProcGlobal->xactsHavingPendingUndo++;
+
+		if (can_push)
+		{
+			UndoRequestInfo urinfo;
+
+			ResetUndoRequestInfo(&urinfo);
+
+			urinfo.full_xid = rh->full_xid;
+			urinfo.start_urec_ptr = rh->start_urec_ptr;
+			urinfo.end_urec_ptr = rh->end_urec_ptr;
+			urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+			urinfo.dbid = rh->dbid;
+			urinfo.request_size = req_size;
+
+			InsertRequestIntoUndoQueues(&urinfo);
+
+			/*
+			 * Indicates that the request will be processed by undo
+			 * worker.
+			 */
+			rh->status = UNDO_REQUEST_INQUEUE;
+			pushed = true;
+		}
+		/*
+		 * The request can't be pushed into the undo worker queue.  The
+		 * backends will try executing by itself.
+		 */
+		else
+			rh->status = UNDO_REQUEST_INPROGRESS;
+	}
+	else if (!UndoRequestIsValid(rh) && can_push)
+	{
+		/*
+		 * If we found the request which is still not in queue or not in
+		 * progress then add it to the queue if there is a space in the queue.
+		 */
+		UndoRequestInfo urinfo;
+
+		ResetUndoRequestInfo(&urinfo);
+
+		urinfo.full_xid = rh->full_xid;
+		urinfo.start_urec_ptr = rh->start_urec_ptr;
+		urinfo.end_urec_ptr = rh->end_urec_ptr;
+		urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+		urinfo.dbid = rh->dbid;
+		urinfo.request_size = req_size;
+
+		InsertRequestIntoUndoQueues(&urinfo);
+
+		/* Indicates that the request will be processed by the undo worker */
+		rh->status = UNDO_REQUEST_INQUEUE;
+		pushed = true;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return pushed;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	/* Decrement the pending request counter. */
+	ProcGlobal->xactsHavingPendingUndo--;
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Mark the entry status as invalid in the rollback hash table.
+ */
+void
+RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+						   UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	rh->status = UNDO_REQUEST_INVALID;
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Returns the start undo record pointer for the last undo log in which
+ * transaction has spanned.  This will be different from start_urec_ptr only
+ * when the undo for a transaction has spanned across multiple undo logs.
+ */
+UndoRecPtr
+RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+							 UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+	UndoRecPtr	last_log_start_urecptr;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	last_log_start_urecptr = rh->last_log_start_urec_ptr;
+	LWLockRelease(RollbackRequestLock);
+
+	return last_log_start_urecptr;
+}
+
+/*
+ * Returns true, if the rollback hash table is full, false, otherwise.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
+
+/*
+ * Get the smallest of 'xid having pending undo' and 'oldestXmin'.
+ */
+FullTransactionId
+RollbackHTGetOldestFullXid(FullTransactionId oldestXmin)
+{
+	RollbackHashEntry   *rh;
+	FullTransactionId	oldestXid = oldestXmin;
+	HASH_SEQ_STATUS		status;
+
+	/* Fetch the pending undo requests */
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	Assert(hash_get_num_entries(RollbackHT) <= UndoRollbackHashTableSize());
+	hash_seq_init(&status, RollbackHT);
+	while (RollbackHT != NULL &&
+		   (rh = (RollbackHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (!FullTransactionIdIsValid(oldestXid) ||
+			FullTransactionIdPrecedes(rh->full_xid, oldestXid))
+			oldestXid = rh->full_xid;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return oldestXid;
+}
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..19e4f1f 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,4 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0da5b19..6126ed1 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	ProcGlobal->xactsHavingPendingUndo = 0;
 }
 
 /*
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index d88113f..3be16b8 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1086,6 +1086,20 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 }
 
 /*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
+/*
  * Process any command-line switches and any additional GUC variable
  * settings passed in the startup packet.
  */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b92645f..89ced6d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -3042,6 +3043,27 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"pending_undo_queue_size", PGC_POSTMASTER, RESOURCES_MEM,
+			gettext_noop("Sets the size of queue used to register undo requests"),
+			NULL,
+		},
+		&pending_undo_queue_size,
+		1024, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"old_snapshot_threshold", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
 			gettext_noop("Time before a snapshot is too old to read pages changed after the snapshot was taken."),
 			gettext_noop("A value of -1 disables this feature."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 5ee5e09..51e4929 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -604,6 +604,14 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# UNDO options
+#------------------------------------------------------------------------------
+
+#rollback_overflow_size = 64	# default size above which the undo
+					# requests are pushed to undo workers
+#pending_undo_queue_size = 1024	# size of queue used to register undo
+					# requests
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index e12029b..139e2e8 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -114,5 +114,7 @@ extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
 extern void UndoRecordRelease(UnpackedUndoRecord *urec);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 					  UndoPersistence upersistence);
+extern UndoRecPtr UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+					  UndoPersistence persistence);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 043518a..0647dfe 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -235,6 +235,7 @@ typedef struct UndoPackContext
 } UndoPackContext;
 
 extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordHeaderSize(uint8 uur_info);
 extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
 extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
 extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..2c9b11d
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,225 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+extern PGDLLIMPORT int	rollback_overflow_size;
+extern PGDLLIMPORT int	pending_undo_queue_size;
+
+/*
+ * Current status of the undo request in the hash table.
+ */
+typedef enum
+{
+	/*
+	 * Request is present in the rollback hash table, but not present in any
+	 * of the queues.  In this state, the undo actions can't be executed.
+	 *
+	 * The request will be marked with this status if a) discard worker finds
+	 * that there is no space in the undo worker queue for inserting the undo
+	 * request, b) there is an error while backend or undo worker is
+	 * executing undo actions and there is no space in the error queue.
+	 *
+	 * Later when the discard worker finds such entry and if there is a
+	 * sufficient space in the undo worker queues, then the request will be
+	 * added to them and the status will be changed to UNDO_REQUEST_INQUEUE.
+	 *
+	 * It is important to keep the request in hash table with this status
+	 * intsead of removing it to compute the value of
+	 * oldestXidHavingUnappliedUndo.  If we don't do that, then the
+	 * corresponding xid won't be considered for computation of
+	 * oldestXidHavingUnappliedUndo.
+	 */
+	UNDO_REQUEST_INVALID,
+
+	/*
+	 * When backend or discard worker push the request to undo worker queue the
+	 * status will be set to this.  Undo workers pulls such requests from the
+	 * queues, change the state as UNDO_REQUEST_INPROGRESS and process the undo
+	 * actions.
+	 */
+	UNDO_REQUEST_INQUEUE,
+
+	/*
+	 * Undo action execution is in progress either by backend or by undo worker.
+	 */
+	UNDO_REQUEST_INPROGRESS
+} UndoRequestStatus;
+
+/*
+ * UndoRequestIsValid
+ *		True iff undo request status is not invalid.
+ */
+#define UndoRequestIsValid(rh) \
+	((bool) ((rh->status) != UNDO_REQUEST_INVALID))
+
+ /*
+  * UndoRequestIsInProgress
+  *		True iff undo request status is in progress.
+  */
+#define UndoRequestIsInProgress(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INPROGRESS))
+
+/*
+ * UndoRequestIsInQueue
+ *		True iff undo request status is in queue.
+ */
+#define UndoRequestIsInQueue(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INQUEUE))
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	UndoRequestStatus	status;	/* current state of the entry. */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	TimestampTz err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->last_log_start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->last_log_start_urec_ptr = rh->last_log_start_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/*
+ * From an undo log if all the undo actions have been applied for a particular
+ * transaction, we set the uur_progress of the transaction's log in that undo
+ * log as MaxBlockNumber.  If none of the undo actions have yet been applied,
+ * we set it to InvalidBlockNumber.
+ */
+#define XACT_APPLY_PROGRESS_COMPLETED MaxBlockNumber
+#define XACT_APPLY_PROGRESS_NOT_STARTED InvalidBlockNumber
+
+#define IsXactApplyProgressCompleted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_COMPLETED)
+
+#define IsXactApplyProgressNotStarted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_NOT_STARTED)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+			UndoRequestInfo *urinfo, bool *in_other_db);
+
+/* Exposed functions for rollback hash table. */
+extern bool RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr);
+extern bool RollbackHTIsFull(void);
+extern int UndoRollbackHashTableSize(void);
+extern void RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+								UndoRecPtr start_urec_ptr);
+extern UndoRecPtr RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+											   UndoRecPtr start_urec_ptr);
+extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 61a24c2..1afc4d3 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -426,6 +426,7 @@ extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 						 Oid useroid, char *out_dbname, bool override_allow_connections);
+extern bool dbid_exists(Oid dboid);
 extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1cee7db..6a74d20 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Number of aborted transactions with pending undo actions. */
+	int			xactsHavingPendingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
-- 
1.8.3.1

0013-Allow-foreground-transactions-to-perform-undo-action.patchapplication/octet-stream; name=0013-Allow-foreground-transactions-to-perform-undo-action.patchDownload
From f67b77aeb23317b2f135df188d20b98decabfa0e Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:42:46 +0530
Subject: [PATCH 13/14] Allow foreground transactions to perform undo actions
 on abort.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

Amit Kapila and Dilip Kumar  with inputs from Robert Haas
---
 src/backend/access/transam/twophase.c         |  82 +++-
 src/backend/access/transam/varsup.c           |  24 ++
 src/backend/access/transam/xact.c             | 595 +++++++++++++++++++++++++-
 src/backend/access/undo/README.UndoProcessing |  39 ++
 src/backend/access/undo/undoaccess.c          |   7 +
 src/backend/utils/error/elog.c                |  36 +-
 src/backend/utils/init/globals.c              |   6 +
 src/backend/utils/resowner/resowner.c         |  11 +-
 src/include/access/transam.h                  |   1 +
 src/include/access/twophase.h                 |   3 +-
 src/include/access/xact.h                     |  10 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/elog.h                      |   3 +
 13 files changed, 798 insertions(+), 21 deletions(-)
 create mode 100644 src/backend/access/undo/README.UndoProcessing

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 5196d61..d3b221d 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1012,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1044,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1485,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
+	bool		undo_action_pushed[UndoPersistenceLevels];
+	uint32		epoch;
+	int			i;
+	FullTransactionId full_xid;
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1528,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1518,6 +1545,19 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * TransactionIdIsInProgress will stop saying the prepared xact is in
 	 * progress), then run the post-commit or post-abort callbacks. The
 	 * callbacks will release the locks the transaction held.
+	 *
+	 * XXX Note that, unlike non-two-phase transactions, we don't skip
+	 * releasing the locks when we have to perform the undo actions.  The
+	 * reason is that here the locks are not directly associated with current
+	 * transaction, so we need to keep the two phase records unprocessed (or
+	 * find some other way to associate it with current transaction's resource
+	 * owner) till the undo actions are completetly applied.  Also, we might
+	 * need to retain TwoPhaseStateLock till the locks are released which is
+	 * again not a good idea.  The downside is that we might face deadlock
+	 * while applying undo actions which ideally we should try to avoid but
+	 * not sure if that is worth complicating this subsytem.  Anyway,
+	 * currently such deadlock risks are there while executing undo actions
+	 * for toast relations as well.
 	 */
 	if (isCommit)
 		RecordTransactionCommitPrepared(xid,
@@ -1526,10 +1566,36 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 										hdr->ninvalmsgs, invalmsgs,
 										hdr->initfileinval, gid);
 	else
+	{
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id in
+		 * TwoPhaseFileHeader instead.)
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Register the rollback request to apply undo actions.  It is
+		 * important to do this before marking it aborted in clog, see
+		 * comments atop PushUndoRequest for further details.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (end_urec_ptr[i] != InvalidUndoRecPtr && i != UNDO_TEMP)
+			{
+				undo_action_pushed[i] = RegisterRollbackReq(end_urec_ptr[i],
+															start_urec_ptr[i],
+															hdr->database,
+															full_xid);
+			}
+		}
+
 		RecordTransactionAbortPrepared(xid,
 									   hdr->nsubxacts, children,
 									   hdr->nabortrels, abortrels,
 									   gid);
+	}
 
 	ProcArrayRemove(proc, latestXid);
 
@@ -1612,6 +1678,20 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	if (!isCommit)
+	{
+		/*
+		 * Perform undo actions, if there are undologs for this transaction.
+		 * We need to perform undo actions while we are still in a transaction.
+		 */
+		if (!PerformUndoActions(full_xid, hdr->database, end_urec_ptr,
+								start_urec_ptr, undo_action_pushed,
+								false))
+		{
+			FlushErrorState();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..fd01989 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -566,3 +566,27 @@ GetNewObjectId(void)
 
 	return result;
 }
+
+/*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 821652b..3253a0c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,15 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		undo_req_pushed[UndoPersistenceLevels]; /* undo request pushed
+														 * to worker? */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -339,6 +352,7 @@ static void ShowTransactionState(const char *str);
 static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
+static void PushUndoRequest(void);
 
 
 /* ----------------------------------------------------------------
@@ -362,9 +376,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +737,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +924,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -1885,6 +1904,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1968,6 +1988,15 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2264,6 +2293,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2280,7 +2311,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2433,7 +2464,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2622,7 +2653,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2780,6 +2813,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2880,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2943,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3032,14 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3008,7 +3056,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3052,6 +3100,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3065,7 +3131,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3087,7 +3153,10 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3176,10 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				UndoActionsRequired();
+				PushUndoRequest();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3160,6 +3232,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	UndoActionsRequired();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3175,7 +3255,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3190,7 +3274,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3287,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3215,7 +3305,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3226,7 +3318,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_END:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3255,7 +3349,9 @@ AbortCurrentTransaction(void)
 			 * Abort, cleanup, go to idle state.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3266,7 +3362,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_PREPARE:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3277,7 +3375,9 @@ AbortCurrentTransaction(void)
 			 * we get ROLLBACK.
 			 */
 		case TBLOCK_SUBINPROGRESS:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3291,7 +3391,9 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBCOMMIT:
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,10 +3406,173 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
 /*
+ * PushUndoRequest - Register the request for apllying undo actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+PushUndoRequest()
+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	volatile int per_level;
+
+	if (!s->performUndoActions)
+		return;
+	/*
+	 * We can't postpone applying undo actions for subtransactions as the
+	 * modifications made by aborted subtransaction must not be visible even if
+	 * the main transaction commits.
+	 */
+	if (IsSubTransaction())
+		return;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (per_level != UNDO_TEMP && s->latest_urec_ptr[per_level])
+		{
+			result = RegisterRollbackReq(s->latest_urec_ptr[per_level],
+										 s->start_urec_ptr[per_level],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undo_req_pushed[per_level] = result;
+		}
+	}
+}
+
+/*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	bool		ret;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	ret = PerformUndoActions(GetTopFullTransactionId(), MyDatabaseId,
+							 s->latest_urec_ptr, s->start_urec_ptr,
+							 s->undo_req_pushed,
+							 IsSubTransaction());
+
+	if (!ret)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
+	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
  *	PreventInTransactionBlock
  *
  *	This routine is to be called by statements that must not run inside
@@ -3633,6 +3898,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +4092,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4210,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4352,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4099,6 +4372,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4157,6 +4442,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4192,8 +4479,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4266,6 +4582,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4702,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4404,6 +4724,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4422,6 +4743,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4473,17 +4810,32 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	UndoActionsRequired();
+
+	/* Try to push rollback request to worker if possible. */
+	PushUndoRequest();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4514,6 +4866,14 @@ AbortOutOfAnyTransaction(void)
 	 */
 	do
 	{
+		/*
+		 * Here, we just detect whether there are any pending undo actions so that
+		 * we can skip releasing the locks during abort transaction.  We don't
+		 * release the locks till we execute undo actions otherwise, there is a
+		 * risk of deadlock.
+		 */
+		UndoActionsRequired();
+
 		switch (s->blockState)
 		{
 			case TBLOCK_DEFAULT:
@@ -4529,7 +4889,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4545,6 +4909,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_ABORT_PENDING:
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
+				PushUndoRequest();
+				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
@@ -4572,6 +4950,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBCOMMIT:
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
+				PushUndoRequest();
+				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
@@ -4666,6 +5058,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4705,6 +5099,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4722,6 +5117,15 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4909,7 +5313,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5336,6 +5741,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5761,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5788,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6388,155 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * UndoActionsRequired - Set the information required to perform undo actions.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing the locks if required.
+ */
+void
+UndoActionsRequired(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
+
+/*
+ * PerformUndoActions - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ */
+bool
+PerformUndoActions(FullTransactionId fxid, Oid dbid, UndoRecPtr *end_urec_ptr,
+				   UndoRecPtr *start_urec_ptr, bool *undo_req_pushed,
+				   bool isSubTrans)
+{
+	volatile	UndoRequestInfo urinfo;
+	uint32		save_holdoff;
+	int			per_level;
+	bool		success = true;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (end_urec_ptr[per_level] && !undo_req_pushed[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[per_level];
+
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(urinfo.full_xid,
+									 end_urec_ptr[per_level],
+									 start_urec_ptr[per_level],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
+
+/*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..e0caf9e
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,39 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 62cdfbf..d70ab65 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -835,6 +835,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.persistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de..4e751d0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -123,6 +123,12 @@ int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
 
 /*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
+
+/*
  * Primary determinants of sizes of shared-memory structures.
  *
  * MaxBackends is computed by PostmasterMain after modules have had a chance to
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c4..3b2a28b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 0ac7f73..1321d84 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -231,6 +231,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern uint32 GetEpochForXid(TransactionId xid);
 
 /*
  * Some frontend programs include this header.  For compilers that emit static
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..497b92f 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,6 +14,7 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undolog.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
@@ -41,7 +42,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726a..4e0099f 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/undolog.h"
 #include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
@@ -427,6 +428,15 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 int xactflags, TransactionId twophase_xid,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
+extern void ApplyUndoActions(void);
+extern void UndoActionsRequired(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+extern bool PerformUndoActions(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool *undo_req_pushed, bool isSubTrans);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+				UndoPersistence upersistence);
 
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1afc4d3..388dc97 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -246,6 +246,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
 
+extern bool applying_subxact_undo;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8ef..0b227ab 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
-- 
1.8.3.1

0005-Add-prefetch-support-for-the-undo-log.patchapplication/octet-stream; name=0005-Add-prefetch-support-for-the-undo-log.patchDownload
From 67845a7afa675e973bd0ea9481072effa1eb219d Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/14] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 103 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 88 insertions(+), 36 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c742861..d8dc0cc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index d679279..955e045 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -519,14 +519,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, smgr->smgr_which, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -549,43 +592,33 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_which,
-					   reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+							  ForkNumber forkNum, BlockNumber blockNum,
+							  char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 2aa4952..14ccc52 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -117,7 +117,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1936c5d..2fff673 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -937,6 +937,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 060215f..8e3d38c 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -166,6 +166,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 				   				 BlockNumber blockNum, ReadBufferMode mode,
-- 
1.8.3.1

0001-Add-SmgrId-to-smgropen-and-BufferTag.patchapplication/octet-stream; name=0001-Add-SmgrId-to-smgropen-and-BufferTag.patchDownload
From 7224c8d19185aba2833ca1b05d602893c6a22459 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 8 Mar 2019 12:03:00 +1300
Subject: [PATCH 01/14] Add SmgrId to smgropen() and BufferTag.

To use bufmgr.c for new kinds of data in addition to plain old
relations, add an SMGR argument to places that identify blocks
and the files that hold them (smgropen(), block references in
the WAL, BufferTag).

To avoid making BufferTag wider, take some space away from the
fork number for this new member, since there are just a few
values possible.

Add a "smgrid" column to the pg_buffercache extension.

Create a new callback for smgropen() calls so that some md.c-
specific stuff can move out of smgropen(), and future
implementations can also run their own initialization code.

Author: Thomas Munro
Discussion: https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BDE0mmiBZMtZyvwWtgv1sZCniSVhXYsXkvJ_Wo%2B83vvw%40mail.gmail.com
---
 contrib/bloom/blinsert.c                           |  2 +-
 contrib/pg_buffercache/Makefile                    |  4 +-
 contrib/pg_buffercache/pg_buffercache--1.2.sql     | 21 ----------
 .../pg_buffercache/pg_buffercache--1.3--1.4.sql    |  7 ++++
 contrib/pg_buffercache/pg_buffercache--1.4.sql     | 21 ++++++++++
 contrib/pg_buffercache/pg_buffercache.control      |  2 +-
 contrib/pg_buffercache/pg_buffercache_pages.c      | 49 ++++++++++++----------
 doc/src/sgml/pgbuffercache.sgml                    |  7 ++++
 src/backend/access/brin/brin_xlog.c                |  2 +-
 src/backend/access/gin/ginxlog.c                   |  3 +-
 src/backend/access/gist/gistxlog.c                 |  6 +--
 src/backend/access/hash/hash_xlog.c                | 12 +++---
 src/backend/access/hash/hashpage.c                 |  6 ++-
 src/backend/access/heap/heapam.c                   | 20 ++++-----
 src/backend/access/heap/heapam_handler.c           |  2 +-
 src/backend/access/heap/rewriteheap.c              |  6 ++-
 src/backend/access/nbtree/nbtree.c                 |  2 +-
 src/backend/access/nbtree/nbtsort.c                |  3 +-
 src/backend/access/nbtree/nbtxlog.c                |  8 ++--
 src/backend/access/spgist/spginsert.c              |  6 +--
 src/backend/access/spgist/spgxlog.c                | 12 +++---
 src/backend/access/transam/xlog.c                  |  6 ++-
 src/backend/access/transam/xloginsert.c            | 39 ++++++++++-------
 src/backend/access/transam/xlogreader.c            | 11 ++++-
 src/backend/access/transam/xlogutils.c             | 17 ++++----
 src/backend/catalog/storage.c                      | 11 ++---
 src/backend/commands/tablecmds.c                   |  2 +-
 src/backend/replication/logical/decode.c           | 10 ++---
 src/backend/replication/logical/reorderbuffer.c    |  3 +-
 src/backend/storage/buffer/bufmgr.c                | 29 ++++++++-----
 src/backend/storage/buffer/localbuf.c              |  8 ++--
 src/backend/storage/freespace/freespace.c          |  3 +-
 src/backend/storage/freespace/fsmpage.c            |  3 +-
 src/backend/storage/smgr/md.c                      | 29 +++++++++----
 src/backend/storage/smgr/smgr.c                    | 13 +++---
 src/bin/pg_rewind/parsexlog.c                      |  8 +++-
 src/bin/pg_waldump/pg_waldump.c                    | 16 ++++---
 src/include/access/xloginsert.h                    | 13 +++---
 src/include/access/xlogreader.h                    |  6 ++-
 src/include/access/xlogutils.h                     |  4 +-
 src/include/storage/buf_internals.h                | 11 ++++-
 src/include/storage/bufmgr.h                       | 11 ++---
 src/include/storage/md.h                           |  1 +
 src/include/storage/smgr.h                         |  8 +++-
 src/include/utils/rel.h                            |  6 ++-
 45 files changed, 289 insertions(+), 180 deletions(-)
 delete mode 100644 contrib/pg_buffercache/pg_buffercache--1.2.sql
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.4.sql

diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index 4b2186b..e39d21d 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -181,7 +181,7 @@ blbuildempty(Relation index)
 	PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
 			  (char *) metapage, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				BLOOM_METAPAGE_BLKNO, metapage, true);
 
 	/*
diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile
index 18f7a87..d76ac24 100644
--- a/contrib/pg_buffercache/Makefile
+++ b/contrib/pg_buffercache/Makefile
@@ -4,7 +4,9 @@ MODULE_big = pg_buffercache
 OBJS = pg_buffercache_pages.o $(WIN32RES)
 
 EXTENSION = pg_buffercache
-DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
+DATA = \
+	pg_buffercache--1.4.sql \
+	pg_buffercache--1.3--1.4.sql pg_buffercache--1.2--1.3.sql \
 	pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \
 	pg_buffercache--unpackaged--1.0.sql
 PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
diff --git a/contrib/pg_buffercache/pg_buffercache--1.2.sql b/contrib/pg_buffercache/pg_buffercache--1.2.sql
deleted file mode 100644
index 6ee5d84..0000000
--- a/contrib/pg_buffercache/pg_buffercache--1.2.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-/* contrib/pg_buffercache/pg_buffercache--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
-
--- Register the function.
-CREATE FUNCTION pg_buffercache_pages()
-RETURNS SETOF RECORD
-AS 'MODULE_PATHNAME', 'pg_buffercache_pages'
-LANGUAGE C PARALLEL SAFE;
-
--- Create a view for convenient access.
-CREATE VIEW pg_buffercache AS
-	SELECT P.* FROM pg_buffercache_pages() AS P
-	(bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
-	 relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,
-	 pinning_backends int4);
-
--- Don't want these to be available to public.
-REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
-REVOKE ALL ON pg_buffercache FROM PUBLIC;
diff --git a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
new file mode 100644
index 0000000..5277740
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
@@ -0,0 +1,7 @@
+/* contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.4'" to load this file. \quit
+
+GRANT EXECUTE ON FUNCTION pg_buffercache_pages() TO pg_monitor;
+GRANT SELECT ON pg_buffercache TO pg_monitor;
diff --git a/contrib/pg_buffercache/pg_buffercache--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.4.sql
new file mode 100644
index 0000000..4a36b42
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.4.sql
@@ -0,0 +1,21 @@
+/* contrib/pg_buffercache/pg_buffercache--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
+
+-- Register the function.
+CREATE FUNCTION pg_buffercache_pages()
+RETURNS SETOF RECORD
+AS 'MODULE_PATHNAME', 'pg_buffercache_pages'
+LANGUAGE C PARALLEL SAFE;
+
+-- Create a view for convenient access.
+CREATE VIEW pg_buffercache AS
+	SELECT P.* FROM pg_buffercache_pages() AS P
+	(bufferid integer, smgrid int2, relfilenode oid, reltablespace oid,
+	 reldatabase oid, relforknumber int2, relblocknumber int8, isdirty bool,
+	 usagecount int2, pinning_backends int4);
+
+-- Don't want these to be available to public.
+REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
+REVOKE ALL ON pg_buffercache FROM PUBLIC;
diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control
index 8c060ae..a82ae5f 100644
--- a/contrib/pg_buffercache/pg_buffercache.control
+++ b/contrib/pg_buffercache/pg_buffercache.control
@@ -1,5 +1,5 @@
 # pg_buffercache extension
 comment = 'examine the shared buffer cache'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/pg_buffercache'
 relocatable = true
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index 1bd579f..052764e 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -15,8 +15,8 @@
 #include "storage/bufmgr.h"
 
 
-#define NUM_BUFFERCACHE_PAGES_MIN_ELEM	8
-#define NUM_BUFFERCACHE_PAGES_ELEM	9
+#define NUM_BUFFERCACHE_PAGES_MIN_ELEM	10
+#define NUM_BUFFERCACHE_PAGES_ELEM	10
 
 PG_MODULE_MAGIC;
 
@@ -25,6 +25,7 @@ PG_MODULE_MAGIC;
  */
 typedef struct
 {
+	SmgrId		smgrid;
 	uint32		bufferid;
 	Oid			relfilenode;
 	Oid			reltablespace;
@@ -102,24 +103,24 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 		tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
 		TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
 						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 2, "smgrid",
+						   INT2OID, -1, 0);
+		TupleDescInitEntry(tupledesc, (AttrNumber) 3, "relfilenode",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reltablespace",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 5, "reldatabase",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relforknumber",
 						   INT2OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 7, "relblocknumber",
 						   INT8OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 8, "isdirty",
 						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 9, "usage_count",
 						   INT2OID, -1, 0);
-
-		if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
-			TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
-							   INT4OID, -1, 0);
+		TupleDescInitEntry(tupledesc, (AttrNumber) 10, "pinning_backends",
+						   INT4OID, -1, 0);
 
 		fctx->tupdesc = BlessTupleDesc(tupledesc);
 
@@ -153,6 +154,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 			buf_state = LockBufHdr(bufHdr);
 
 			fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
+			fctx->record[i].smgrid = bufHdr->tag.smgrid;
 			fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
 			fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
 			fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
@@ -204,28 +206,29 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 			nulls[5] = true;
 			nulls[6] = true;
 			nulls[7] = true;
-			/* unused for v1.0 callers, but the array is always long enough */
 			nulls[8] = true;
+			nulls[9] = true;
 		}
 		else
 		{
-			values[1] = ObjectIdGetDatum(fctx->record[i].relfilenode);
+			values[1] = Int16GetDatum(fctx->record[i].smgrid);
 			nulls[1] = false;
-			values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
+			values[2] = ObjectIdGetDatum(fctx->record[i].relfilenode);
 			nulls[2] = false;
-			values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
+			values[3] = ObjectIdGetDatum(fctx->record[i].reltablespace);
 			nulls[3] = false;
-			values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
+			values[4] = ObjectIdGetDatum(fctx->record[i].reldatabase);
 			nulls[4] = false;
-			values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
+			values[5] = ObjectIdGetDatum(fctx->record[i].forknum);
 			nulls[5] = false;
-			values[6] = BoolGetDatum(fctx->record[i].isdirty);
+			values[6] = Int64GetDatum((int64) fctx->record[i].blocknum);
 			nulls[6] = false;
-			values[7] = Int16GetDatum(fctx->record[i].usagecount);
+			values[7] = BoolGetDatum(fctx->record[i].isdirty);
 			nulls[7] = false;
-			/* unused for v1.0 callers, but the array is always long enough */
-			values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
+			values[8] = Int16GetDatum(fctx->record[i].usagecount);
 			nulls[8] = false;
+			values[9] = Int32GetDatum(fctx->record[i].pinning_backends);
+			nulls[9] = false;
 		}
 
 		/* Build and return the tuple. */
diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml
index faf5a31..a0a7be3 100644
--- a/doc/src/sgml/pgbuffercache.sgml
+++ b/doc/src/sgml/pgbuffercache.sgml
@@ -58,6 +58,13 @@
      </row>
 
      <row>
+      <entry><structfield>smgrid</structfield></entry>
+      <entry><type>smallint</type></entry>
+      <entry></entry>
+      <entry>Block storage manager ID.  0 for regular relation data.</entry>
+     </row>
+
+     <row>
       <entry><structfield>relfilenode</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal>pg_class.relfilenode</literal></entry>
diff --git a/src/backend/access/brin/brin_xlog.c b/src/backend/access/brin/brin_xlog.c
index db1f47c..a13b3cd 100644
--- a/src/backend/access/brin/brin_xlog.c
+++ b/src/backend/access/brin/brin_xlog.c
@@ -217,7 +217,7 @@ brin_xlog_revmap_extend(XLogReaderState *record)
 
 	xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
 
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &targetBlk);
 	Assert(xlrec->targetBlk == targetBlk);
 
 	/* Update the metapage */
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index c945b28..261881c 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -95,11 +95,12 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
 
 	if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
 	{
+		SmgrId		smgrid;
 		RelFileNode node;
 		ForkNumber	forknum;
 		BlockNumber blknum;
 
-		BufferGetTag(buffer, &node, &forknum, &blknum);
+		BufferGetTag(buffer, &smgrid, &node, &forknum, &blknum);
 		elog(ERROR, "failed to add item to index page in %u/%u/%u",
 			 node.spcNode, node.dbNode, node.relNode);
 	}
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index 503db34..bf945b9 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -193,7 +193,7 @@ gistRedoDeleteRecord(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 
 		ResolveRecoveryConflictWithSnapshot(xldata->latestRemovedXid, rnode);
 	}
@@ -278,7 +278,7 @@ gistRedoPageSplitRecord(XLogReaderState *record)
 		BlockNumber blkno;
 		IndexTuple *tuples;
 
-		XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno);
+		XLogRecGetBlockTag(record, i + 1, NULL, NULL, NULL, &blkno);
 		if (blkno == GIST_ROOT_BLKNO)
 		{
 			Assert(i == 0);
@@ -313,7 +313,7 @@ gistRedoPageSplitRecord(XLogReaderState *record)
 			{
 				BlockNumber nextblkno;
 
-				XLogRecGetBlockTag(record, i + 2, NULL, NULL, &nextblkno);
+				XLogRecGetBlockTag(record, i + 2, NULL, NULL, NULL, &nextblkno);
 				GistPageGetOpaque(page)->rightlink = nextblkno;
 			}
 			else
diff --git a/src/backend/access/hash/hash_xlog.c b/src/backend/access/hash/hash_xlog.c
index d7b7098..ec604a7 100644
--- a/src/backend/access/hash/hash_xlog.c
+++ b/src/backend/access/hash/hash_xlog.c
@@ -51,7 +51,7 @@ hash_xlog_init_meta_page(XLogReaderState *record)
 	 * special handling for init forks as create index operations don't log a
 	 * full page image of the metapage.
 	 */
-	XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, &forknum, NULL);
 	if (forknum == INIT_FORKNUM)
 		FlushOneBuffer(metabuf);
 
@@ -89,7 +89,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
 	 * special handling for init forks as create index operations don't log a
 	 * full page image of the metapage.
 	 */
-	XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, &forknum, NULL);
 	if (forknum == INIT_FORKNUM)
 		FlushOneBuffer(bitmapbuf);
 	UnlockReleaseBuffer(bitmapbuf);
@@ -113,7 +113,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
 		PageSetLSN(page, lsn);
 		MarkBufferDirty(metabuf);
 
-		XLogRecGetBlockTag(record, 1, NULL, &forknum, NULL);
+		XLogRecGetBlockTag(record, 1, NULL, NULL, &forknum, NULL);
 		if (forknum == INIT_FORKNUM)
 			FlushOneBuffer(metabuf);
 	}
@@ -190,8 +190,8 @@ hash_xlog_add_ovfl_page(XLogReaderState *record)
 	Size		datalen PG_USED_FOR_ASSERTS_ONLY;
 	bool		new_bmpage = false;
 
-	XLogRecGetBlockTag(record, 0, NULL, NULL, &rightblk);
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &leftblk);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &rightblk);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &leftblk);
 
 	ovflbuf = XLogInitBufferForRedo(record, 0);
 	Assert(BufferIsValid(ovflbuf));
@@ -1001,7 +1001,7 @@ hash_xlog_vacuum_one_page(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 		ResolveRecoveryConflictWithSnapshot(xldata->latestRemovedXid, rnode);
 	}
 
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 376ee2a..f6042e4 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -427,7 +427,8 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 		MarkBufferDirty(buf);
 
 		if (use_wal)
-			log_newpage(&rel->rd_node,
+			log_newpage(SMGR_MD,
+						&rel->rd_node,
 						forkNum,
 						blkno,
 						BufferGetPage(buf),
@@ -1021,7 +1022,8 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 	ovflopaque->hasho_page_id = HASHO_PAGE_ID;
 
 	if (RelationNeedsWAL(rel))
-		log_newpage(&rel->rd_node,
+		log_newpage(SMGR_MD,
+					&rel->rd_node,
 					MAIN_FORKNUM,
 					lastblock,
 					zerobuf.data,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8ac0f8a..7a63c09 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7684,7 +7684,7 @@ heap_xlog_clean(XLogReaderState *record)
 	BlockNumber blkno;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * We're about to remove tuples. In Hot Standby mode, ensure that there's
@@ -7779,7 +7779,7 @@ heap_xlog_visible(XLogReaderState *record)
 	BlockNumber blkno;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 1, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * If there are any Hot Standby transactions running that have an xmin
@@ -7927,7 +7927,7 @@ heap_xlog_freeze_page(XLogReaderState *record)
 
 		TransactionIdRetreat(latestRemovedXid);
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 		ResolveRecoveryConflictWithSnapshot(latestRemovedXid, rnode);
 	}
 
@@ -7999,7 +7999,7 @@ heap_xlog_delete(XLogReaderState *record)
 	RelFileNode target_node;
 	ItemPointerData target_tid;
 
-	XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &target_node, NULL, &blkno);
 	ItemPointerSetBlockNumber(&target_tid, blkno);
 	ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
 
@@ -8080,7 +8080,7 @@ heap_xlog_insert(XLogReaderState *record)
 	ItemPointerData target_tid;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &target_node, NULL, &blkno);
 	ItemPointerSetBlockNumber(&target_tid, blkno);
 	ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
 
@@ -8202,7 +8202,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	 */
 	xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * The visibility map may need to be fixed even if the heap page is
@@ -8348,8 +8348,8 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	oldtup.t_data = NULL;
 	oldtup.t_len = 0;
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &newblk);
-	if (XLogRecGetBlockTag(record, 1, NULL, NULL, &oldblk))
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &newblk);
+	if (XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &oldblk))
 	{
 		/* HOT updates are never done across pages */
 		Assert(!hot_update);
@@ -8644,7 +8644,7 @@ heap_xlog_lock(XLogReaderState *record)
 		BlockNumber block;
 		Relation	reln;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, &block);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &block);
 		reln = CreateFakeRelcacheEntry(rnode);
 
 		visibilitymap_pin(reln, block, &vmbuffer);
@@ -8717,7 +8717,7 @@ heap_xlog_lock_updated(XLogReaderState *record)
 		BlockNumber block;
 		Relation	reln;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, &block);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &block);
 		reln = CreateFakeRelcacheEntry(rnode);
 
 		visibilitymap_pin(reln, block, &vmbuffer);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index b7d2ddb..4f64c0f 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -637,7 +637,7 @@ heapam_relation_copy_data(Relation rel, const RelFileNode *newrnode)
 {
 	SMgrRelation dstrel;
 
-	dstrel = smgropen(*newrnode, rel->rd_backend);
+	dstrel = smgropen(SMGR_MD, *newrnode, rel->rd_backend);
 	RelationOpenSmgr(rel);
 
 	/*
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 369694f..67e00b6 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -331,7 +331,8 @@ end_heap_rewrite(RewriteState state)
 	if (state->rs_buffer_valid)
 	{
 		if (state->rs_use_wal)
-			log_newpage(&state->rs_new_rel->rd_node,
+			log_newpage(SMGR_MD,
+						&state->rs_new_rel->rd_node,
 						MAIN_FORKNUM,
 						state->rs_blockno,
 						state->rs_buffer,
@@ -696,7 +697,8 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 
 			/* XLOG stuff */
 			if (state->rs_use_wal)
-				log_newpage(&state->rs_new_rel->rd_node,
+				log_newpage(SMGR_MD,
+							&state->rs_new_rel->rd_node,
 							MAIN_FORKNUM,
 							state->rs_blockno,
 							page,
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 85e54ac..4255d4b 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -172,7 +172,7 @@ btbuildempty(Relation index)
 	PageSetChecksumInplace(metapage, BTREE_METAPAGE);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE,
 			  (char *) metapage, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				BTREE_METAPAGE, metapage, true);
 
 	/*
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index d0b9013..931d6cd 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -658,7 +658,8 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 	if (wstate->btws_use_wal)
 	{
 		/* We use the heap NEWPAGE record type for this */
-		log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page, true);
+		log_newpage(SMGR_MD, &wstate->index->rd_node, MAIN_FORKNUM, blkno,
+					page, true);
 	}
 
 	/*
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index 6532a25..1b0a1f7 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -216,9 +216,9 @@ btree_xlog_split(bool onleft, XLogReaderState *record)
 	BlockNumber rightsib;
 	BlockNumber rnext;
 
-	XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
-	if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
+	XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &leftsib);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &rightsib);
+	if (!XLogRecGetBlockTag(record, 2, NULL, NULL, NULL, &rnext))
 		rnext = P_NONE;
 
 	/*
@@ -524,7 +524,7 @@ btree_xlog_delete(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 
 		ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid, rnode);
 	}
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index b40bd44..8019f68 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -171,7 +171,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_METAPAGE_BLKNO, page, true);
 
 	/* Likewise for the root page. */
@@ -180,7 +180,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_ROOT_BLKNO, page, true);
 
 	/* Likewise for the null-tuples root page. */
@@ -189,7 +189,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_NULL_BLKNO, page, true);
 
 	/*
diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c
index ebe6ae8..3ce35fe 100644
--- a/src/backend/access/spgist/spgxlog.c
+++ b/src/backend/access/spgist/spgxlog.c
@@ -151,7 +151,7 @@ spgRedoAddLeaf(XLogReaderState *record)
 			SpGistInnerTuple tuple;
 			BlockNumber blknoLeaf;
 
-			XLogRecGetBlockTag(record, 0, NULL, NULL, &blknoLeaf);
+			XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &blknoLeaf);
 
 			page = BufferGetPage(buffer);
 
@@ -184,7 +184,7 @@ spgRedoMoveLeafs(XLogReaderState *record)
 	XLogRedoAction action;
 	BlockNumber blknoDst;
 
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoDst);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &blknoDst);
 
 	fillFakeState(&state, xldata->stateSrc);
 
@@ -328,8 +328,8 @@ spgRedoAddNode(XLogReaderState *record)
 		BlockNumber blkno;
 		BlockNumber blknoNew;
 
-		XLogRecGetBlockTag(record, 0, NULL, NULL, &blkno);
-		XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoNew);
+		XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &blkno);
+		XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &blknoNew);
 
 		/*
 		 * In normal operation we would have all three pages (source, dest,
@@ -549,7 +549,7 @@ spgRedoPickSplit(XLogReaderState *record)
 	BlockNumber blknoInner;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 2, NULL, NULL, &blknoInner);
+	XLogRecGetBlockTag(record, 2, NULL, NULL, NULL, &blknoInner);
 
 	fillFakeState(&state, xldata->stateSrc);
 
@@ -879,7 +879,7 @@ spgRedoVacuumRedirect(XLogReaderState *record)
 		{
 			RelFileNode node;
 
-			XLogRecGetBlockTag(record, 0, &node, NULL, NULL);
+			XLogRecGetBlockTag(record, 0, NULL, &node, NULL, NULL);
 			ResolveRecoveryConflictWithSnapshot(xldata->newestRedirectXid,
 												node);
 		}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index e08320e..254c724 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -1371,6 +1371,7 @@ checkXLogConsistency(XLogReaderState *record)
 	ForkNumber	forknum;
 	BlockNumber blkno;
 	int			block_id;
+	SmgrId		smgrid;
 
 	/* Records with no backup blocks have no need for consistency checks. */
 	if (!XLogRecHasAnyBlockRefs(record))
@@ -1383,7 +1384,8 @@ checkXLogConsistency(XLogReaderState *record)
 		Buffer		buf;
 		Page		page;
 
-		if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+		if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+								&blkno))
 		{
 			/*
 			 * WAL record doesn't contain a block reference with the given id.
@@ -1408,7 +1410,7 @@ checkXLogConsistency(XLogReaderState *record)
 		 * Read the contents from the current buffer and store it in a
 		 * temporary page.
 		 */
-		buf = XLogReadBufferExtended(rnode, forknum, blkno,
+		buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno,
 									 RBM_NORMAL_NO_LOG);
 		if (!BufferIsValid(buf))
 			continue;
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d4..1697797 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -43,7 +43,8 @@ typedef struct
 {
 	bool		in_use;			/* is this slot in use? */
 	uint8		flags;			/* REGBUF_* flags */
-	RelFileNode rnode;			/* identifies the relation and block */
+	SmgrId		smgrid;			/* identifies the SGMR, relation and block */
+	RelFileNode rnode;
 	ForkNumber	forkno;
 	BlockNumber block;
 	Page		page;			/* page content */
@@ -227,7 +228,8 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 
 	regbuf = &registered_buffers[block_id];
 
-	BufferGetTag(buffer, &regbuf->rnode, &regbuf->forkno, &regbuf->block);
+	BufferGetTag(buffer, &regbuf->smgrid, &regbuf->rnode, &regbuf->forkno,
+				 &regbuf->block);
 	regbuf->page = BufferGetPage(buffer);
 	regbuf->flags = flags;
 	regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
@@ -248,7 +250,8 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 			if (i == block_id || !regbuf_old->in_use)
 				continue;
 
-			Assert(!RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
+			Assert(regbuf_old->smgrid != regbuf->smgrid ||
+				   !RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
 				   regbuf_old->forkno != regbuf->forkno ||
 				   regbuf_old->block != regbuf->block);
 		}
@@ -263,8 +266,9 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
  * shared buffer pool (i.e. when you don't have a Buffer for it).
  */
 void
-XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
-				  BlockNumber blknum, Page page, uint8 flags)
+XLogRegisterBlock(uint8 block_id, SmgrId smgrid, RelFileNode *rnode,
+				  ForkNumber forknum, BlockNumber blknum, Page page,
+				  uint8 flags)
 {
 	registered_buffer *regbuf;
 
@@ -280,6 +284,7 @@ XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
 
 	regbuf = &registered_buffers[block_id];
 
+	regbuf->smgrid = smgrid;
 	regbuf->rnode = *rnode;
 	regbuf->forkno = forknum;
 	regbuf->block = blknum;
@@ -303,7 +308,8 @@ XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
 			if (i == block_id || !regbuf_old->in_use)
 				continue;
 
-			Assert(!RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
+			Assert(regbuf_old->smgrid != regbuf->smgrid ||
+				   !RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
 				   regbuf_old->forkno != regbuf->forkno ||
 				   regbuf_old->block != regbuf->block);
 		}
@@ -702,7 +708,8 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			rdt_datas_last = regbuf->rdata_tail;
 		}
 
-		if (prev_regbuf && RelFileNodeEquals(regbuf->rnode, prev_regbuf->rnode))
+		if (prev_regbuf && regbuf->smgrid == prev_regbuf->smgrid &&
+			RelFileNodeEquals(regbuf->rnode, prev_regbuf->rnode))
 		{
 			samerel = true;
 			bkpb.fork_flags |= BKPBLOCK_SAME_REL;
@@ -727,6 +734,8 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		}
 		if (!samerel)
 		{
+			memcpy(scratch, &regbuf->smgrid, sizeof(SmgrId));
+			scratch += sizeof(SmgrId);
 			memcpy(scratch, &regbuf->rnode, sizeof(RelFileNode));
 			scratch += sizeof(RelFileNode);
 		}
@@ -919,6 +928,7 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 		int			flags;
 		PGAlignedBlock copied_buffer;
 		char	   *origdata = (char *) BufferGetBlock(buffer);
+		SmgrId		smgrid;
 		RelFileNode rnode;
 		ForkNumber	forkno;
 		BlockNumber blkno;
@@ -947,8 +957,8 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 		if (buffer_std)
 			flags |= REGBUF_STANDARD;
 
-		BufferGetTag(buffer, &rnode, &forkno, &blkno);
-		XLogRegisterBlock(0, &rnode, forkno, blkno, copied_buffer.data, flags);
+		BufferGetTag(buffer, &smgrid, &rnode, &forkno, &blkno);
+		XLogRegisterBlock(0, smgrid, &rnode, forkno, blkno, copied_buffer.data, flags);
 
 		recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI_FOR_HINT);
 	}
@@ -969,8 +979,8 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
  * the unused space to be left out from the WAL record, making it smaller.
  */
 XLogRecPtr
-log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
-			Page page, bool page_std)
+log_newpage(SmgrId smgrid, RelFileNode *rnode, ForkNumber forkNum,
+			BlockNumber blkno, Page page, bool page_std)
 {
 	int			flags;
 	XLogRecPtr	recptr;
@@ -980,7 +990,7 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
 		flags |= REGBUF_STANDARD;
 
 	XLogBeginInsert();
-	XLogRegisterBlock(0, rnode, forkNum, blkno, page, flags);
+	XLogRegisterBlock(0, smgrid, rnode, forkNum, blkno, page, flags);
 	recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
 
 	/*
@@ -1009,6 +1019,7 @@ XLogRecPtr
 log_newpage_buffer(Buffer buffer, bool page_std)
 {
 	Page		page = BufferGetPage(buffer);
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 	BlockNumber blkno;
@@ -1016,9 +1027,9 @@ log_newpage_buffer(Buffer buffer, bool page_std)
 	/* Shared buffers should be modified in a critical section. */
 	Assert(CritSectionCount > 0);
 
-	BufferGetTag(buffer, &rnode, &forkNum, &blkno);
+	BufferGetTag(buffer, &smgrid, &rnode, &forkNum, &blkno);
 
-	return log_newpage(&rnode, forkNum, blkno, page, page_std);
+	return log_newpage(smgrid, &rnode, forkNum, blkno, page, page_std);
 }
 
 /*
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 88be7fe..8ac51be 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1056,6 +1056,7 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 	uint32		remaining;
 	uint32		datatotal;
 	RelFileNode *rnode = NULL;
+	SmgrId		smgrid = -1;
 	uint8		block_id;
 
 	ResetDecoder(state);
@@ -1229,8 +1230,10 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 			}
 			if (!(fork_flags & BKPBLOCK_SAME_REL))
 			{
+				COPY_HEADER_FIELD(&blk->smgrid, sizeof(SmgrId));
 				COPY_HEADER_FIELD(&blk->rnode, sizeof(RelFileNode));
 				rnode = &blk->rnode;
+				smgrid = blk->smgrid;
 			}
 			else
 			{
@@ -1242,6 +1245,7 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 					goto err;
 				}
 
+				blk->smgrid = smgrid;
 				blk->rnode = *rnode;
 			}
 			COPY_HEADER_FIELD(&blk->blkno, sizeof(BlockNumber));
@@ -1349,12 +1353,13 @@ err:
 /*
  * Returns information about the block that a block reference refers to.
  *
- * If the WAL record contains a block reference with the given ID, *rnode,
- * *forknum, and *blknum are filled in (if not NULL), and returns true.
+ * If the WAL record contains a block reference with the given ID, *smgrid,
+ * *rnode, *forknum and *blknum are filled in (if not NULL), and returns true.
  * Otherwise returns false.
  */
 bool
 XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
+				   SmgrId *smgrid,
 				   RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
 {
 	DecodedBkpBlock *bkpb;
@@ -1363,6 +1368,8 @@ XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
 		return false;
 
 	bkpb = &record->blocks[block_id];
+	if (smgrid)
+		*smgrid = bkpb->smgrid;
 	if (rnode)
 		*rnode = bkpb->rnode;
 	if (forknum)
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663b..c5f27fb 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -335,8 +335,9 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	Page		page;
 	bool		zeromode;
 	bool		willinit;
+	SmgrId		smgrid;
 
-	if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+	if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum, &blkno))
 	{
 		/* Caller specified a bogus block_id */
 		elog(PANIC, "failed to locate backup block with ID %d", block_id);
@@ -357,7 +358,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	if (XLogRecBlockImageApply(record, block_id))
 	{
 		Assert(XLogRecHasBlockImage(record, block_id));
-		*buf = XLogReadBufferExtended(rnode, forknum, blkno,
+		*buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno,
 									  get_cleanup_lock ? RBM_ZERO_AND_CLEANUP_LOCK : RBM_ZERO_AND_LOCK);
 		page = BufferGetPage(*buf);
 		if (!RestoreBlockImage(record, block_id, page))
@@ -387,7 +388,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	}
 	else
 	{
-		*buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
+		*buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno, mode);
 		if (BufferIsValid(*buf))
 		{
 			if (mode != RBM_ZERO_AND_LOCK && mode != RBM_ZERO_AND_CLEANUP_LOCK)
@@ -434,7 +435,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
  * modified.
  */
 Buffer
-XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
+XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 					   BlockNumber blkno, ReadBufferMode mode)
 {
 	BlockNumber lastblock;
@@ -444,7 +445,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	Assert(blkno != P_NEW);
 
 	/* Open the relation at smgr level */
-	smgr = smgropen(rnode, InvalidBackendId);
+	smgr = smgropen(smgrid, rnode, InvalidBackendId);
 
 	/*
 	 * Create the target file if it doesn't already exist.  This lets us cope
@@ -461,7 +462,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	if (blkno < lastblock)
 	{
 		/* page exists in file */
-		buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
+		buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
 										   mode, NULL);
 	}
 	else
@@ -486,7 +487,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 					LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 				ReleaseBuffer(buffer);
 			}
-			buffer = ReadBufferWithoutRelcache(rnode, forknum,
+			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum,
 											   P_NEW, mode, NULL);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
@@ -496,7 +497,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 			if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
-			buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
+			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
 											   mode, NULL);
 		}
 	}
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 3cc886f..9509c19 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -102,7 +102,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 			return NULL;		/* placate compiler */
 	}
 
-	srel = smgropen(rnode, backend);
+	srel = smgropen(SMGR_MD, rnode, backend);
 	smgrcreate(srel, MAIN_FORKNUM, false);
 
 	if (needs_wal)
@@ -353,7 +353,8 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
 		 * space.
 		 */
 		if (use_wal)
-			log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
+			log_newpage(SMGR_MD, &dst->smgr_rnode.node, forkNum, blkno, page,
+						false);
 
 		PageSetChecksumInplace(page, blkno);
 
@@ -428,7 +429,7 @@ smgrDoPendingDeletes(bool isCommit)
 			{
 				SMgrRelation srel;
 
-				srel = smgropen(pending->relnode, pending->backend);
+				srel = smgropen(SMGR_MD, pending->relnode, pending->backend);
 
 				/* allocate the initial array, or extend it, if needed */
 				if (maxrels == 0)
@@ -580,7 +581,7 @@ smgr_redo(XLogReaderState *record)
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
 		SMgrRelation reln;
 
-		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		reln = smgropen(SMGR_MD, xlrec->rnode, InvalidBackendId);
 		smgrcreate(reln, xlrec->forkNum, true);
 	}
 	else if (info == XLOG_SMGR_TRUNCATE)
@@ -589,7 +590,7 @@ smgr_redo(XLogReaderState *record)
 		SMgrRelation reln;
 		Relation	rel;
 
-		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		reln = smgropen(SMGR_MD, xlrec->rnode, InvalidBackendId);
 
 		/*
 		 * Forcibly create relation if it doesn't exist (which suggests that
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cb2c5e1..4737c58 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12590,7 +12590,7 @@ index_copy_data(Relation rel, RelFileNode newrnode)
 {
 	SMgrRelation dstrel;
 
-	dstrel = smgropen(newrnode, rel->rd_backend);
+	dstrel = smgropen(SMGR_MD, newrnode, rel->rd_backend);
 	RelationOpenSmgr(rel);
 
 	/*
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 151c3ef..3e96d2a 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -683,7 +683,7 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -731,7 +731,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_update *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -796,7 +796,7 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_delete *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -892,7 +892,7 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_multi_insert *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &rnode, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &rnode, NULL, NULL);
 	if (rnode.dbNode != ctx->slot->data.database)
 		return;
 
@@ -991,7 +991,7 @@ DecodeSpecConfirm(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	RelFileNode target_node;
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index e7c32f2..71dbbda 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -3496,6 +3496,7 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 	ReorderBufferTupleCidEnt *ent;
 	ForkNumber	forkno;
 	BlockNumber blockno;
+	SmgrId		smgrid;
 	bool		updated_mapping = false;
 
 	/* be careful about padding */
@@ -3507,7 +3508,7 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 	 * get relfilenode from the buffer, no convenient way to access it other
 	 * than that.
 	 */
-	BufferGetTag(buffer, &key.relnode, &forkno, &blockno);
+	BufferGetTag(buffer, &smgrid, &key.relnode, &forkno, &blockno);
 
 	/* tuples can only be in the main fork */
 	Assert(forkno == MAIN_FORKNUM);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 7332e6b..8046334 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -555,7 +555,8 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		int			buf_id;
 
 		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
+		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_which,
+					   reln->rd_smgr->smgr_rnode.node,
 					   forkNum, blockNum);
 
 		/* determine its hash code and partition lock ID */
@@ -680,13 +681,13 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
  * parameters.
  */
 Buffer
-ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
+ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
 						  BufferAccessStrategy strategy)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
+	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
 
 	Assert(InRecovery);
 
@@ -1009,7 +1010,8 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	uint32		buf_state;
 
 	/* create a tag so we can lookup the buffer */
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* determine its hash code and partition lock ID */
 	newHash = BufTableHashCode(&newTag);
@@ -1843,6 +1845,7 @@ BufferSync(int flags)
 			buf_state |= BM_CHECKPOINT_NEEDED;
 
 			item = &CkptBufferIds[num_to_scan++];
+			item->smgrid = bufHdr->tag.smgrid;
 			item->buf_id = buf_id;
 			item->tsId = bufHdr->tag.rnode.spcNode;
 			item->relNode = bufHdr->tag.rnode.relNode;
@@ -2626,12 +2629,12 @@ BufferGetBlockNumber(Buffer buffer)
 
 /*
  * BufferGetTag
- *		Returns the relfilenode, fork number and block number associated with
- *		a buffer.
+ *		Returns the SMGR ID, relfilenode, fork number and block number
+ *		associated with a buffer.
  */
 void
-BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum,
-			 BlockNumber *blknum)
+BufferGetTag(Buffer buffer, SmgrId *smgrid, RelFileNode *rnode,
+			 ForkNumber *forknum, BlockNumber *blknum)
 {
 	BufferDesc *bufHdr;
 
@@ -2644,6 +2647,7 @@ BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum,
 		bufHdr = GetBufferDescriptor(buffer - 1);
 
 	/* pinned, so OK to read tag without spinlock */
+	*smgrid = bufHdr->tag.smgrid;
 	*rnode = bufHdr->tag.rnode;
 	*forknum = bufHdr->tag.forkNum;
 	*blknum = bufHdr->tag.blockNum;
@@ -2695,7 +2699,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 
 	/* Find smgr relation for buffer */
 	if (reln == NULL)
-		reln = smgropen(buf->tag.rnode, InvalidBackendId);
+		reln = smgropen(buf->tag.smgrid, buf->tag.rnode, InvalidBackendId);
 
 	TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum,
 										buf->tag.blockNum,
@@ -4220,6 +4224,11 @@ ckpt_buforder_comparator(const void *pa, const void *pb)
 	const CkptSortItem *a = (const CkptSortItem *) pa;
 	const CkptSortItem *b = (const CkptSortItem *) pb;
 
+	/* compare smgr */
+	if (a->smgrid < b->smgrid)
+		return -1;
+	else if (a->smgrid > b->smgrid)
+		return 1;
 	/* compare tablespace */
 	if (a->tsId < b->tsId)
 		return -1;
@@ -4377,7 +4386,7 @@ IssuePendingWritebacks(WritebackContext *context)
 		i += ahead;
 
 		/* and finally tell the kernel to write the data to storage */
-		reln = smgropen(tag.rnode, InvalidBackendId);
+		reln = smgropen(tag.smgrid, tag.rnode, InvalidBackendId);
 		smgrwriteback(reln, tag.forkNum, tag.blockNum, nblocks);
 	}
 
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index c462ea8..896285a 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -68,7 +68,8 @@ LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
 	BufferTag	newTag;			/* identity of requested block */
 	LocalBufferLookupEnt *hresult;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -111,7 +112,8 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	bool		found;
 	uint32		buf_state;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -209,7 +211,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 		Page		localpage = (char *) LocalBufHdrGetBlock(bufHdr);
 
 		/* Find smgr relation for buffer */
-		oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
+		oreln = smgropen(bufHdr->tag.smgrid, bufHdr->tag.rnode, MyBackendId);
 
 		PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
 
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index c17b3f4..78a2274 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -210,7 +210,8 @@ XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
 	blkno = fsm_logical_to_physical(addr);
 
 	/* If the page doesn't exist already, extend */
-	buf = XLogReadBufferExtended(rnode, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR);
+	buf = XLogReadBufferExtended(SMGR_MD, rnode, FSM_FORKNUM, blkno,
+								 RBM_ZERO_ON_ERROR);
 	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
 	page = BufferGetPage(buf);
diff --git a/src/backend/storage/freespace/fsmpage.c b/src/backend/storage/freespace/fsmpage.c
index cf7f03f..da3b286 100644
--- a/src/backend/storage/freespace/fsmpage.c
+++ b/src/backend/storage/freespace/fsmpage.c
@@ -268,11 +268,12 @@ restart:
 			 *
 			 * Fix the corruption and restart.
 			 */
+			SmgrId		smgrid;
 			RelFileNode rnode;
 			ForkNumber	forknum;
 			BlockNumber blknum;
 
-			BufferGetTag(buf, &rnode, &forknum, &blknum);
+			BufferGetTag(buf, &smgrid, &rnode, &forknum, &blknum);
 			elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/%u",
 				 blknum, rnode.spcNode, rnode.dbNode, rnode.relNode);
 
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 64acc3f..6c576ed 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -120,7 +120,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 /* local routines */
 static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum,
 						 bool isRedo);
-static MdfdVec *mdopen(SMgrRelation reln, ForkNumber forknum, int behavior);
+static MdfdVec *mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior);
 static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum,
 								   MdfdVec *seg);
 static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum,
@@ -152,6 +152,17 @@ mdinit(void)
 }
 
 /*
+ *	mdopen() -- Initialize a newly-opened relation.
+ */
+void
+mdopen(SMgrRelation reln)
+{
+	/* mark it not open */
+	for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++)
+		reln->md_num_open_segs[forknum] = 0;
+}
+
+/*
  *	mdexists() -- Does the physical file exist?
  *
  * Note: this will return true for lingering files, with pending deletions
@@ -165,7 +176,7 @@ mdexists(SMgrRelation reln, ForkNumber forkNum)
 	 */
 	mdclose(reln, forkNum);
 
-	return (mdopen(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
+	return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
 }
 
 /*
@@ -425,7 +436,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 }
 
 /*
- *	mdopen() -- Open the specified relation.
+ *	mdopenfork() -- Open the specified relation.
  *
  * Note we only open the first segment, when there are multiple segments.
  *
@@ -435,7 +446,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
  * invent one out of whole cloth.
  */
 static MdfdVec *
-mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
+mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 {
 	MdfdVec    *mdfd;
 	char	   *path;
@@ -713,11 +724,11 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 BlockNumber
 mdnblocks(SMgrRelation reln, ForkNumber forknum)
 {
-	MdfdVec    *v = mdopen(reln, forknum, EXTENSION_FAIL);
+	MdfdVec    *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
 	BlockNumber nblocks;
 	BlockNumber segno = 0;
 
-	/* mdopen has opened the first segment */
+	/* mdopenfork has opened the first segment */
 	Assert(reln->md_num_open_segs[forknum] > 0);
 
 	/*
@@ -981,7 +992,7 @@ DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo)
 	srels = palloc(sizeof(SMgrRelation) * ndelrels);
 	for (i = 0; i < ndelrels; i++)
 	{
-		SMgrRelation srel = smgropen(delrels[i], InvalidBackendId);
+		SMgrRelation srel = smgropen(SMGR_MD, delrels[i], InvalidBackendId);
 
 		if (isRedo)
 		{
@@ -1137,7 +1148,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1];
 	else
 	{
-		v = mdopen(reln, forknum, behavior);
+		v = mdopenfork(reln, forknum, behavior);
 		if (!v)
 			return NULL;		/* if behavior & EXTENSION_RETURN_NULL */
 	}
@@ -1257,7 +1268,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 int
 mdsyncfiletag(const FileTag *ftag, char *path)
 {
-	SMgrRelation reln = smgropen(ftag->rnode, InvalidBackendId);
+	SMgrRelation reln = smgropen(SMGR_MD, ftag->rnode, InvalidBackendId);
 	MdfdVec    *v;
 	char	   *p;
 
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c39..26281fa 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -41,6 +41,7 @@ typedef struct f_smgr
 {
 	void		(*smgr_init) (void);	/* may be NULL */
 	void		(*smgr_shutdown) (void);	/* may be NULL */
+	void		(*smgr_open) (SMgrRelation reln);
 	void		(*smgr_close) (SMgrRelation reln, ForkNumber forknum);
 	void		(*smgr_create) (SMgrRelation reln, ForkNumber forknum,
 								bool isRedo);
@@ -68,6 +69,7 @@ static const f_smgr smgrsw[] = {
 	{
 		.smgr_init = mdinit,
 		.smgr_shutdown = NULL,
+		.smgr_open = mdopen,
 		.smgr_close = mdclose,
 		.smgr_create = mdcreate,
 		.smgr_exists = mdexists,
@@ -141,7 +143,7 @@ smgrshutdown(int code, Datum arg)
  *		This does not attempt to actually open the underlying file.
  */
 SMgrRelation
-smgropen(RelFileNode rnode, BackendId backend)
+smgropen(SmgrId smgrid, RelFileNode rnode, BackendId backend)
 {
 	RelFileNodeBackend brnode;
 	SMgrRelation reln;
@@ -170,18 +172,15 @@ smgropen(RelFileNode rnode, BackendId backend)
 	/* Initialize it if not present before */
 	if (!found)
 	{
-		int			forknum;
-
 		/* hash_search already filled in the lookup key */
 		reln->smgr_owner = NULL;
 		reln->smgr_targblock = InvalidBlockNumber;
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
-		reln->smgr_which = 0;	/* we only have md.c at present */
+		reln->smgr_which = smgrid;
 
-		/* mark it not open */
-		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
-			reln->md_num_open_segs[forknum] = 0;
+		/* implementation-specific initialization */
+		smgrsw[reln->smgr_which].smgr_open(reln);
 
 		/* it has no owner yet */
 		dlist_push_tail(&unowned_relns, &reln->node);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 287af60..44f4c41 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -394,8 +394,14 @@ extractPageInfo(XLogReaderState *record)
 		RelFileNode rnode;
 		ForkNumber	forknum;
 		BlockNumber blkno;
+		SmgrId		smgrid;
 
-		if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+		if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+								&blkno))
+			continue;
+
+		/* TODO: How should we handle other smgr IDs? */
+		if (smgrid != SMGR_MD)
 			continue;
 
 		/* We only care about the main fork; others are copied in toto */
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b95d467..9b9b450 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -524,6 +524,7 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 	const RmgrDescData *desc = &RmgrDescTable[XLogRecGetRmid(record)];
 	uint32		rec_len;
 	uint32		fpi_len;
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forknum;
 	BlockNumber blk;
@@ -556,16 +557,19 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 			if (!XLogRecHasBlockRef(record, block_id))
 				continue;
 
-			XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
+			XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+							   &blk);
 			if (forknum != MAIN_FORKNUM)
-				printf(", blkref #%u: rel %u/%u/%u fork %s blk %u",
+				printf(", blkref #%u: smgr %d rel %u/%u/%u fork %s blk %u",
 					   block_id,
+					   smgrid,
 					   rnode.spcNode, rnode.dbNode, rnode.relNode,
 					   forkNames[forknum],
 					   blk);
 			else
-				printf(", blkref #%u: rel %u/%u/%u blk %u",
+				printf(", blkref #%u: smgr %d rel %u/%u/%u blk %u",
 					   block_id,
+					   smgrid,
 					   rnode.spcNode, rnode.dbNode, rnode.relNode,
 					   blk);
 			if (XLogRecHasBlockImage(record, block_id))
@@ -587,9 +591,11 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 			if (!XLogRecHasBlockRef(record, block_id))
 				continue;
 
-			XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
-			printf("\tblkref #%u: rel %u/%u/%u fork %s blk %u",
+			XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+							   &blk);
+			printf("\tblkref #%u: smgr %d rel %u/%u/%u fork %s blk %u",
 				   block_id,
+				   smgrid,
 				   rnode.spcNode, rnode.dbNode, rnode.relNode,
 				   forkNames[forknum],
 				   blk);
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index df24089..3ae9d22 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -16,6 +16,7 @@
 #include "storage/block.h"
 #include "storage/buf.h"
 #include "storage/relfilenode.h"
+#include "storage/smgr.h"
 #include "utils/relcache.h"
 
 /*
@@ -45,15 +46,17 @@ extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info);
 extern void XLogEnsureRecordSpace(int nbuffers, int ndatas);
 extern void XLogRegisterData(char *data, int len);
 extern void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags);
-extern void XLogRegisterBlock(uint8 block_id, RelFileNode *rnode,
-							  ForkNumber forknum, BlockNumber blknum, char *page,
-							  uint8 flags);
+extern void XLogRegisterBlock(uint8 block_id, SmgrId smgrid,
+							  RelFileNode *rnode, ForkNumber forknum,
+							  BlockNumber blknum,
+							  char *page, uint8 flags);
 extern void XLogRegisterBufData(uint8 block_id, char *data, int len);
 extern void XLogResetInsertion(void);
 extern bool XLogCheckBufferNeedsBackup(Buffer buffer);
 
-extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum,
-							  BlockNumber blk, char *page, bool page_std);
+extern XLogRecPtr log_newpage(SmgrId smgrid, RelFileNode *rnode,
+							  ForkNumber forkNum, BlockNumber blk,
+							  char *page, bool page_std);
 extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std);
 extern void log_newpage_range(Relation rel, ForkNumber forkNum,
 							  BlockNumber startblk, BlockNumber endblk, bool page_std);
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 04228e2..3c545ff 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -26,6 +26,7 @@
 #define XLOGREADER_H
 
 #include "access/xlogrecord.h"
+#include "storage/smgr.h"
 
 typedef struct XLogReaderState XLogReaderState;
 
@@ -43,6 +44,7 @@ typedef struct
 	bool		in_use;
 
 	/* Identify the block this refers to */
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forknum;
 	BlockNumber blkno;
@@ -243,7 +245,7 @@ extern bool DecodeXLogRecord(XLogReaderState *state, XLogRecord *record,
 extern bool RestoreBlockImage(XLogReaderState *recoder, uint8 block_id, char *dst);
 extern char *XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len);
 extern bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
-							   RelFileNode *rnode, ForkNumber *forknum,
-							   BlockNumber *blknum);
+							   SmgrId *smgrid, RelFileNode *rnode,
+							   ForkNumber *forknum, BlockNumber *blknum);
 
 #endif							/* XLOGREADER_H */
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59..366b8d3 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -13,6 +13,7 @@
 
 #include "access/xlogreader.h"
 #include "storage/bufmgr.h"
+#include "storage/smgr.h"
 
 
 extern bool XLogHaveInvalidPages(void);
@@ -41,7 +42,8 @@ extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record,
 													ReadBufferMode mode, bool get_cleanup_lock,
 													Buffer *buf);
 
-extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
+extern Buffer XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode,
+									 ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index df2dda7..e0d7e8f 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -87,16 +87,20 @@
  *
  * Note: if there's any pad bytes in the struct, INIT_BUFFERTAG will have
  * to be fixed to zero them, since this struct is used as a hash key.
+ * Conceptually the SmgrId should go first, but we put it next to the
+ * ForkNumber so that it packs better with typical alignment rules.
  */
 typedef struct buftag
 {
 	RelFileNode rnode;			/* physical relation identifier */
-	ForkNumber	forkNum;
+	int16		smgrid;			/* SmgrId */
+	int16		forkNum;		/* ForkNumber */
 	BlockNumber blockNum;		/* blknum relative to begin of reln */
 } BufferTag;
 
 #define CLEAR_BUFFERTAG(a) \
 ( \
+	(a).smgrid = SMGR_INVALID, \
 	(a).rnode.spcNode = InvalidOid, \
 	(a).rnode.dbNode = InvalidOid, \
 	(a).rnode.relNode = InvalidOid, \
@@ -104,8 +108,9 @@ typedef struct buftag
 	(a).blockNum = InvalidBlockNumber \
 )
 
-#define INIT_BUFFERTAG(a,xx_rnode,xx_forkNum,xx_blockNum) \
+#define INIT_BUFFERTAG(a,xx_smgrid,xx_rnode,xx_forkNum,xx_blockNum) \
 ( \
+	(a).smgrid = (xx_smgrid), \
 	(a).rnode = (xx_rnode), \
 	(a).forkNum = (xx_forkNum), \
 	(a).blockNum = (xx_blockNum) \
@@ -113,6 +118,7 @@ typedef struct buftag
 
 #define BUFFERTAGS_EQUAL(a,b) \
 ( \
+	(a).smgrid == (b).smgrid && \
 	RelFileNodeEquals((a).rnode, (b).rnode) && \
 	(a).blockNum == (b).blockNum && \
 	(a).forkNum == (b).forkNum \
@@ -288,6 +294,7 @@ extern BufferDesc *LocalBufferDescriptors;
  */
 typedef struct CkptSortItem
 {
+	SmgrId		smgrid;
 	Oid			tsId;
 	Oid			relNode;
 	ForkNumber	forkNum;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 509f4b7..256dcd5 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -18,6 +18,7 @@
 #include "storage/buf.h"
 #include "storage/bufpage.h"
 #include "storage/relfilenode.h"
+#include "storage/smgr.h"
 #include "utils/relcache.h"
 #include "utils/snapmgr.h"
 
@@ -166,11 +167,11 @@ extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
-								 BlockNumber blockNum, ReadBufferMode mode,
+				   				 BlockNumber blockNum, ReadBufferMode mode,
 								 BufferAccessStrategy strategy);
-extern Buffer ReadBufferWithoutRelcache(RelFileNode rnode,
-										ForkNumber forkNum, BlockNumber blockNum,
-										ReadBufferMode mode, BufferAccessStrategy strategy);
+extern Buffer ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+						  ForkNumber forkNum, BlockNumber blockNum,
+						  ReadBufferMode mode, BufferAccessStrategy strategy);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -205,7 +206,7 @@ extern XLogRecPtr BufferGetLSNAtomic(Buffer buffer);
 extern void PrintPinnedBufs(void);
 #endif
 extern Size BufferShmemSize(void);
-extern void BufferGetTag(Buffer buffer, RelFileNode *rnode,
+extern void BufferGetTag(Buffer buffer, SmgrId *smgrid, RelFileNode *rnode,
 						 ForkNumber *forknum, BlockNumber *blknum);
 
 extern void MarkBufferDirtyHint(Buffer buffer, bool buffer_std);
diff --git a/src/include/storage/md.h b/src/include/storage/md.h
index df24b93..c0f05e2 100644
--- a/src/include/storage/md.h
+++ b/src/include/storage/md.h
@@ -21,6 +21,7 @@
 
 /* md storage manager functionality */
 extern void mdinit(void);
+extern void mdopen(SMgrRelation reln);
 extern void mdclose(SMgrRelation reln, ForkNumber forknum);
 extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
 extern bool mdexists(SMgrRelation reln, ForkNumber forknum);
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index d286c8c..243efc6 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -79,8 +79,14 @@ typedef SMgrRelationData *SMgrRelation;
 #define SmgrIsTemp(smgr) \
 	RelFileNodeBackendIsTemp((smgr)->smgr_rnode)
 
+typedef enum SmgrId
+{
+	SMGR_INVALID = -1,
+	SMGR_MD = 0,		/* md.c */
+} SmgrId;
+
 extern void smgrinit(void);
-extern SMgrRelation smgropen(RelFileNode rnode, BackendId backend);
+extern SMgrRelation smgropen(SmgrId which, RelFileNode rnode, BackendId backend);
 extern bool smgrexists(SMgrRelation reln, ForkNumber forknum);
 extern void smgrsetowner(SMgrRelation *owner, SMgrRelation reln);
 extern void smgrclearowner(SMgrRelation *owner, SMgrRelation reln);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index d7f33ab..c6e516d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -52,6 +52,7 @@ typedef LockInfoData *LockInfo;
 
 typedef struct RelationData
 {
+	SmgrId		rd_smgrid;		/* relation storage manager */
 	RelFileNode rd_node;		/* relation physical identifier */
 	/* use "struct" here to avoid needing to include smgr.h: */
 	struct SMgrRelationData *rd_smgr;	/* cached file handle, or NULL */
@@ -471,7 +472,10 @@ typedef struct ViewOptions
 #define RelationOpenSmgr(relation) \
 	do { \
 		if ((relation)->rd_smgr == NULL) \
-			smgrsetowner(&((relation)->rd_smgr), smgropen((relation)->rd_node, (relation)->rd_backend)); \
+			smgrsetowner(&((relation)->rd_smgr), \
+						 smgropen((relation)->rd_smgrid, \
+								  (relation)->rd_node, \
+								  (relation)->rd_backend)); \
 	} while (0)
 
 /*
-- 
1.8.3.1

0004-Allow-WAL-record-data-on-first-modification-after-a-.patchapplication/octet-stream; name=0004-Allow-WAL-record-data-on-first-modification-after-a-.patchDownload
From 8178766bddd837efc8608d3b05d3213e690c7250 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Sat, 12 Jan 2019 02:17:02 +1300
Subject: [PATCH 04/14] Allow WAL record data on first modification after a
 checkpoint.

Provide a way to attach data to WAL record conditionally, so that it is
included only if this turns out to the be first modification to a given
block after a checkpoint.

This will be used to record undo log meta-data, to avoid a data
synchronization problem with online checkpoints.

Author: Thomas Munro
Reviewed-by:
Discussion:
---
 src/backend/access/transam/xloginsert.c | 15 +++++++++++++++
 src/include/access/xloginsert.h         |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 1697797..36bf458 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -571,6 +571,21 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			needs_data = false;
 		else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
 			needs_data = true;
+		else if ((regbuf->flags & REGBUF_KEEP_DATA_AFTER_CP) != 0)
+		{
+			XLogRecPtr	page_lsn = PageGetLSN(regbuf->page);
+
+			needs_data = (page_lsn <= RedoRecPtr);
+			if (!needs_data)
+			{
+				/*
+				 * XLogInsertRecord() will detect if our view of the latest
+				 * checkpoint's RedoRecPtr is out of date.
+				 */
+				if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)
+					*fpw_lsn = page_lsn;
+			}
+		}
 		else
 			needs_data = !needs_backup;
 
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index 3ae9d22..616f553 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -38,6 +38,8 @@
 									 * will be skipped) */
 #define REGBUF_KEEP_DATA	0x10	/* include data even if a full-page image
 									 * is taken */
+#define REGBUF_KEEP_DATA_AFTER_CP 0x20 /* include data on the first
+										* modification after a checkpoint */
 
 /* prototypes for public functions in xloginsert.c: */
 extern void XLogBeginInsert(void);
-- 
1.8.3.1

0002-Move-tablespace-dir-creation-from-smgr.c-to-md.c.patchapplication/octet-stream; name=0002-Move-tablespace-dir-creation-from-smgr.c-to-md.c.patchDownload
From 6fa24d975e21435bda6f929e3d9ba5bdbe78fa5d Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 30 Apr 2019 22:11:03 +1200
Subject: [PATCH 02/14] Move tablespace dir creation from smgr.c to md.c.

For undo logs, we don't need to create tablespace directories when
opening a relation, because that is managed automatically by
undolog.c.

Author: Thomas Munro
---
 src/backend/storage/smgr/md.c   | 14 ++++++++++++++
 src/backend/storage/smgr/smgr.c | 14 --------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 6c576ed..4c9d448 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -28,6 +28,7 @@
 #include "miscadmin.h"
 #include "access/xlogutils.h"
 #include "access/xlog.h"
+#include "commands/tablespace.h"
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
@@ -196,6 +197,19 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 
 	Assert(reln->md_num_open_segs[forkNum] == 0);
 
+	/*
+	 * We may be using the target table space for the first time in this
+	 * database, so create a per-database subdirectory if needed.
+	 *
+	 * XXX this is a fairly ugly violation of module layering, but this seems
+	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
+	 * should be here and not in commands/tablespace.c?  But that would imply
+	 * importing a lot of stuff that smgr.c oughtn't know, either.
+	 */
+	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
+							reln->smgr_rnode.node.dbNode,
+							isRedo);
+
 	path = relpath(reln->smgr_rnode, forkNum);
 
 	fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 26281fa..4ba07a0 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,7 +17,6 @@
  */
 #include "postgres.h"
 
-#include "commands/tablespace.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -343,19 +342,6 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
 	if (isRedo && reln->md_num_open_segs[forknum] > 0)
 		return;
 
-	/*
-	 * We may be using the target table space for the first time in this
-	 * database, so create a per-database subdirectory if needed.
-	 *
-	 * XXX this is a fairly ugly violation of module layering, but this seems
-	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
-	 * should be here and not in commands/tablespace.c?  But that would imply
-	 * importing a lot of stuff that smgr.c oughtn't know, either.
-	 */
-	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
-							reln->smgr_rnode.node.dbNode,
-							isRedo);
-
 	smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
 }
 
-- 
1.8.3.1

0003-Add-undo-log-manager.patchapplication/octet-stream; name=0003-Add-undo-log-manager.patchDownload
From 7206c40e4cee3391c537cdb22c854889bb417d0e Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 6 Mar 2019 16:46:04 +1300
Subject: [PATCH 03/14] Add undo log manager.

Add a new subsystem to manage undo logs.  Undo logs allow data to be appended
efficiently, like logs.  They also allow data to be discarded efficiently from
the other end, like a queue.  Thirdly, they allow efficient buffered random
access, like a relation.

Undo logs physically consist of a set of 1MB segment files under
$PGDATA/base/undo (or per-tablespace equivalent) that are created, deleted or
renamed as required, similarly to the way that WAL segments are managed.
Meta-data about the set of undo logs is stored in shared memory, and written
to per-checkpoint files under $PGDATA/pg_undo.

Provide access to the undo files managed by undolog.c through bufmgr.c.
A new SMGR implementation allows bufmgr.c to access files created by
undolog.c.

Author: Thomas Munro, with contributions from Dilip Kumar, Rafia Sabih,
        Robert Haas and Amit Kapila
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ%40mail.gmail.com
---
 src/backend/access/Makefile               |    2 +-
 src/backend/access/rmgrdesc/Makefile      |    2 +-
 src/backend/access/rmgrdesc/undologdesc.c |   81 +
 src/backend/access/transam/rmgr.c         |    1 +
 src/backend/access/transam/xlog.c         |    5 +
 src/backend/access/transam/xlogutils.c    |   73 +-
 src/backend/access/undo/Makefile          |   17 +
 src/backend/access/undo/undolog.c         | 2599 +++++++++++++++++++++++++++++
 src/backend/bootstrap/bootstrap.c         |    3 +
 src/backend/catalog/system_views.sql      |    4 +
 src/backend/commands/tablespace.c         |   23 +
 src/backend/postmaster/pgstat.c           |   21 +
 src/backend/replication/basebackup.c      |   18 +-
 src/backend/replication/logical/decode.c  |    1 +
 src/backend/storage/buffer/bufmgr.c       |   81 +-
 src/backend/storage/buffer/localbuf.c     |   44 +
 src/backend/storage/file/fd.c             |    3 +-
 src/backend/storage/ipc/ipci.c            |    3 +
 src/backend/storage/lmgr/lwlock.c         |    2 +
 src/backend/storage/lmgr/lwlocknames.txt  |    1 +
 src/backend/storage/smgr/Makefile         |    2 +-
 src/backend/storage/smgr/smgr.c           |   21 +-
 src/backend/storage/smgr/undofile.c       |  420 +++++
 src/backend/storage/sync/sync.c           |    6 +
 src/backend/utils/init/postinit.c         |    2 +
 src/backend/utils/misc/guc.c              |   12 +
 src/bin/initdb/initdb.c                   |    2 +
 src/bin/pg_checksums/pg_checksums.c       |   23 +-
 src/bin/pg_resetwal/pg_resetwal.c         |   78 +
 src/bin/pg_upgrade/Makefile               |    2 +-
 src/bin/pg_upgrade/check.c                |   42 +
 src/bin/pg_upgrade/controldata.c          |   25 +
 src/bin/pg_upgrade/exec.c                 |    4 +
 src/bin/pg_upgrade/pg_upgrade.c           |    2 +
 src/bin/pg_upgrade/pg_upgrade.h           |    5 +
 src/bin/pg_upgrade/undo.c                 |  292 ++++
 src/bin/pg_waldump/rmgrdesc.c             |    1 +
 src/include/access/rmgrlist.h             |    1 +
 src/include/access/session.h              |    7 +
 src/include/access/undolog.h              |  455 +++++
 src/include/access/undolog_xlog.h         |   62 +
 src/include/access/xlogutils.h            |   16 +
 src/include/catalog/pg_proc.dat           |    7 +
 src/include/pgstat.h                      |    7 +
 src/include/storage/bufmgr.h              |   14 +-
 src/include/storage/fd.h                  |    1 +
 src/include/storage/lwlock.h              |    5 +-
 src/include/storage/smgr.h                |    4 +
 src/include/storage/sync.h                |    3 +-
 src/include/storage/undofile.h            |   59 +
 src/include/utils/guc.h                   |    2 +
 src/test/regress/expected/rules.out       |   10 +
 52 files changed, 4534 insertions(+), 42 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undologdesc.c
 create mode 100644 src/backend/access/undo/Makefile
 create mode 100644 src/backend/access/undo/undolog.c
 create mode 100644 src/backend/storage/smgr/undofile.c
 create mode 100644 src/bin/pg_upgrade/undo.c
 create mode 100644 src/include/access/undolog.h
 create mode 100644 src/include/access/undolog_xlog.h
 create mode 100644 src/include/storage/undofile.h

diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0880e0a..bf6d3fa 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -9,6 +9,6 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  table tablesample transam
+			  table tablesample transam undo
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 5514db1..91ad1ef 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,6 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undologdesc.c b/src/backend/access/rmgrdesc/undologdesc.c
new file mode 100644
index 0000000..f89fcb3
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undologdesc.c
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * undologdesc.c
+ *	  rmgr descriptor routines for access/undo/undolog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undologdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+
+void
+undolog_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDOLOG_CREATE)
+	{
+		xl_undolog_create *xlrec = (xl_undolog_create *) rec;
+
+		appendStringInfo(buf, "logno %u", xlrec->logno);
+	}
+	else if (info == XLOG_UNDOLOG_EXTEND)
+	{
+		xl_undolog_extend *xlrec = (xl_undolog_extend *) rec;
+
+		appendStringInfo(buf, "logno %u end " UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_DISCARD)
+	{
+		xl_undolog_discard *xlrec = (xl_undolog_discard *) rec;
+
+		appendStringInfo(buf, "logno %u discard " UndoLogOffsetFormat " end "
+						 UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->discard, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_SWITCH)
+	{
+		xl_undolog_switch *xlrec = (xl_undolog_switch *) rec;
+
+		appendStringInfo(buf, "logno %u start " UndoLogOffsetFormat " last " UndoLogOffsetFormat,
+						 xlrec->logno,
+						 xlrec->prevlog_xact_start,
+						 xlrec->prevlog_last_urp);
+	}
+
+}
+
+const char *
+undolog_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			id = "CREATE";
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			id = "EXTEND";
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			id = "DISCARD";
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			id = "SWITCH";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9368b56..8b05374 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 254c724..4ef7fb0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/tuptoaster.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "access/xloginsert.h"
@@ -6699,6 +6700,9 @@ StartupXLOG(void)
 	 */
 	restoreTwoPhaseData();
 
+	/* Recover undo log meta data corresponding to this checkpoint. */
+	StartupUndoLogs(ControlFile->checkPointCopy.redo);
+
 	lastFullPageWrites = checkPoint.fullPageWrites;
 
 	RedoRecPtr = XLogCtl->RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
@@ -8966,6 +8970,7 @@ static void
 CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointCLOG();
+	CheckPointUndoLogs(checkPointRedo, ControlFile->checkPointCopy.redo);
 	CheckPointCommitTs();
 	CheckPointSUBTRANS();
 	CheckPointMultiXact();
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index c5f27fb..b06dbd5 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -294,6 +294,68 @@ XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
 }
 
 /*
+ * Find the block ID of the first block that matches the given rnode forknum
+ * and blockno.  If blockno is InvalidBlockNumber, then match any block
+ * number.  Return true if found.
+ */
+bool
+XLogFindBlockId(XLogReaderState *record,
+				SmgrId smgrid,
+				RelFileNode rnode,
+				ForkNumber forknum,
+				BlockNumber blockno,
+				uint8 *block_id)
+{
+	uint8	i;
+
+	for (i = 0; i <= record->max_block_id; ++i)
+	{
+		DecodedBkpBlock *block = &record->blocks[i];
+
+		if (block->in_use &&
+			block->smgrid == smgrid &&
+			RelFileNodeEquals(block->rnode, rnode) &&
+			block->forknum == forknum &&
+			(block->blkno == blockno || blockno == InvalidBlockNumber))
+		{
+			*block_id = i;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * If the caller doesn't know the the block_id, but does know the RelFileNode,
+ * forknum and block number, then we try to find it.
+ */
+XLogRedoAction
+XLogReadBufferForRedoBlock(XLogReaderState *record,
+						   SmgrId smgrid,
+						   RelFileNode rnode,
+						   ForkNumber forknum,
+						   BlockNumber blockno,
+						   ReadBufferMode mode,
+						   bool get_cleanup_lock,
+						   Buffer *buf)
+{
+	uint8  	block_id;
+
+	if (XLogFindBlockId(record, smgrid, rnode, forknum, blockno, &block_id))
+		return XLogReadBufferForRedoExtended(record,
+											 block_id,
+											 mode,
+											 get_cleanup_lock,
+											 buf);
+
+	elog(ERROR, "failed to find block reference rel %u/%u/%u, forknum = %u, block = %u",
+		 rnode.spcNode, rnode.dbNode, rnode.relNode, forknum, blockno);
+
+	return BLK_NOTFOUND;	/* not reached */
+}
+
+/*
  * Pin and lock a buffer referenced by a WAL record, for the purpose of
  * re-initializing it.
  */
@@ -347,7 +409,8 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	 * Make sure that if the block is marked with WILL_INIT, the caller is
 	 * going to initialize it. And vice versa.
 	 */
-	zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
+	zeromode = (mode == RBM_ZERO || mode == RBM_ZERO_AND_LOCK ||
+				mode == RBM_ZERO_AND_CLEANUP_LOCK);
 	willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
 	if (willinit && !zeromode)
 		elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
@@ -463,7 +526,7 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 	{
 		/* page exists in file */
 		buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-										   mode, NULL);
+										   mode, NULL, RELPERSISTENCE_PERMANENT);
 	}
 	else
 	{
@@ -488,7 +551,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 				ReleaseBuffer(buffer);
 			}
 			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum,
-											   P_NEW, mode, NULL);
+											   P_NEW, mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
 		/* Handle the corner case that P_NEW returns non-consecutive pages */
@@ -498,7 +562,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
 			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-											   mode, NULL);
+											   mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 	}
 
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
new file mode 100644
index 0000000..219c696
--- /dev/null
+++ b/src/backend/access/undo/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/undo
+#
+# IDENTIFICATION
+#    src/backend/access/undo/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/undo
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = undolog.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
new file mode 100644
index 0000000..67b08e7
--- /dev/null
+++ b/src/backend/access/undo/undolog.c
@@ -0,0 +1,2599 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.c
+ *	  management of undo logs
+ *
+ * PostgreSQL undo log manager.  This module is responsible for managing the
+ * lifecycle of undo logs and their segment files, associating undo logs with
+ * backends, and allocating space within undo logs.
+ *
+ * For the code that reads and writes blocks of data, see undofile.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undolog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/session.h"
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogreader.h"
+#include "access/xlogutils.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablespace.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "pgstat.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "storage/standby.h"
+#include "storage/sync.h"
+#include "storage/undofile.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/varlena.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+/*
+ * Main control structure for undo log management in shared memory.
+ * UndoLogSlot objects are arranged in a fixed-size array, with no particular
+ * ordering.
+ */
+typedef struct UndoLogSharedData
+{
+	UndoLogNumber	free_lists[UndoPersistenceLevels];
+	UndoLogNumber	low_logno;
+	UndoLogNumber	next_logno;
+	UndoLogNumber	nslots;
+	UndoLogSlot		slots[FLEXIBLE_ARRAY_MEMBER];
+} UndoLogSharedData;
+
+/* The shared memory region that all backends are attach to. */
+UndoLogSharedData *UndoLogShared;
+
+undologtable_hash *undologtable_cache;
+
+/* GUC variables */
+char	   *undo_tablespaces = NULL;
+
+static UndoLogSlot *find_undo_log_slot(UndoLogNumber logno, bool locked);
+static UndoLogSlot *allocate_undo_log_slot(void);
+static void free_undo_log_slot(UndoLogSlot *log);
+static void attach_undo_log(UndoPersistence level, Oid tablespace);
+static void detach_current_undo_log(UndoPersistence level, bool full);
+static void extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end);
+static void undo_log_before_exit(int code, Datum value);
+static void forget_undo_buffers(int logno, UndoLogOffset old_discard,
+								UndoLogOffset new_discard,
+								bool drop_tail);
+static bool choose_undo_tablespace(bool force_detach, Oid *oid);
+
+PG_FUNCTION_INFO_V1(pg_stat_get_undo_logs);
+
+/*
+ * How many undo logs can be active at a time?  This creates a theoretical
+ * maximum amount of undo data that can exist, but if we set it to a multiple
+ * of the maximum number of backends it will be a very high limit.
+ * Alternative designs involving demand paging or dynamic shared memory could
+ * remove this limit but would be complicated.
+ */
+static inline size_t
+UndoLogNumSlots(void)
+{
+	return MaxBackends * 4;
+}
+
+/*
+ * Return the amount of traditional shmem required for undo log management.
+ */
+Size
+UndoLogShmemSize(void)
+{
+	return sizeof(UndoLogSharedData) +
+		UndoLogNumSlots() * sizeof(UndoLogSlot);
+}
+
+/*
+ * Initialize the undo log subsystem.  Called in each backend.
+ */
+void
+UndoLogShmemInit(void)
+{
+	bool found;
+
+	UndoLogShared = (UndoLogSharedData *)
+		ShmemInitStruct("UndoLogShared", UndoLogShmemSize(), &found);
+
+	/* The postmaster initialized the shared memory state. */
+	if (!IsUnderPostmaster)
+	{
+		int		i;
+
+		Assert(!found);
+
+		/*
+		 * We start with no active undo logs.  StartUpUndoLogs() will recreate
+		 * the undo logs that were known at the last checkpoint.
+		 */
+		memset(UndoLogShared, 0, sizeof(*UndoLogShared));
+		UndoLogShared->nslots = UndoLogNumSlots();
+		for (i = 0; i < UndoPersistenceLevels; ++i)
+			UndoLogShared->free_lists[i] = InvalidUndoLogNumber;
+		for (i = 0; i < UndoLogShared->nslots; ++i)
+		{
+			memset(&UndoLogShared->slots[i], 0, sizeof(UndoLogShared->slots[i]));
+			UndoLogShared->slots[i].logno = InvalidUndoLogNumber;
+			LWLockInitialize(&UndoLogShared->slots[i].mutex,
+							 LWTRANCHE_UNDOLOG);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
+							 LWTRANCHE_UNDODISCARD);
+		}
+	}
+	else
+		Assert(found);
+
+	/* All backends prepare their per-backend lookup table. */
+	undologtable_cache = undologtable_create(TopMemoryContext,
+											 UndoLogNumSlots(),
+											 NULL);
+}
+
+void
+UndoLogInit(void)
+{
+	before_shmem_exit(undo_log_before_exit, 0);
+}
+
+/*
+ * Figure out which directory holds an undo log based on tablespace.
+ */
+void
+UndoLogDirectory(Oid tablespace, char *dir)
+{
+	if (tablespace == DEFAULTTABLESPACE_OID ||
+		tablespace == InvalidOid)
+		snprintf(dir, MAXPGPATH, "base/undo");
+	else
+		snprintf(dir, MAXPGPATH, "pg_tblspc/%u/%s/undo",
+				 tablespace, TABLESPACE_VERSION_DIRECTORY);
+}
+
+/*
+ * Compute the pathname to use for an undo log segment file.
+ */
+void
+UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace, char *path)
+{
+	char		dir[MAXPGPATH];
+
+	/* Figure out which directory holds the segment, based on tablespace. */
+	UndoLogDirectory(tablespace, dir);
+
+	/*
+	 * Build the path from log number and offset.  The pathname is the
+	 * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+	 * period inserted between the components.
+	 */
+	snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+			 segno * UndoLogSegmentSize);
+}
+
+/*
+ * Iterate through the set of currently active logs.  Pass in NULL to get the
+ * first undo log.  NULL indicates the end of the set of logs.  The caller
+ * must lock the returned log before accessing its members, and must skip if
+ * logno is not valid.
+ */
+UndoLogSlot *
+UndoLogNextSlot(UndoLogSlot *slot)
+{
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+	for (;;)
+	{
+		/* Advance to the next log. */
+		if (slot == NULL)
+		{
+			/* Start at the beginning. */
+			slot = &UndoLogShared->slots[0];
+		}
+		else if (++slot == &UndoLogShared->slots[UndoLogShared->nslots])
+		{
+			/* Past the end. */
+			slot = NULL;
+			break;
+		}
+		/* Have we found a slot with a valid log? */
+		if (slot->logno != InvalidUndoLogNumber)
+			break;
+	}
+	LWLockRelease(UndoLogLock);
+
+	/* XXX: erm, which lock should the caller hold!? */
+	return slot;
+}
+
+/*
+ * Check if an undo log position has been discarded.  'point' must be an undo
+ * log pointer that was allocated at some point in the past, otherwise the
+ * result is undefined.
+ */
+bool
+UndoLogIsDiscarded(UndoRecPtr point)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(point);
+	UndoLogSlot *slot;
+	bool	result;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * If we couldn't find the undo log number, then it must be entirely
+	 * discarded.
+	 */
+	if (slot == NULL)
+		return true;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (unlikely(logno != slot->logno))
+	{
+		/*
+		 * The undo log has been entirely discarded since we looked it up, and
+		 * the UndoLogSlot is now unused or being used for some other undo
+		 * log.  That means that any pointer within it must be discarded.
+		 */
+		result = true;
+	}
+	else
+	{
+		/* Check if this point is before the discard pointer. */
+		result = UndoRecPtrGetOffset(point) < slot->meta.discard;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * Fetch the previous transaction's start undo record point.
+ */
+UndoRecPtr
+UndoLogGetLastXactStartPoint(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	uint64 last_xact_start = 0;
+
+	if (unlikely(slot == NULL))
+		return InvalidUndoRecPtr;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: review */
+	last_xact_start = slot->meta.unlogged.last_xact_start;
+	LWLockRelease(&slot->mutex);
+
+	if (last_xact_start == 0)
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, last_xact_start);
+}
+
+/*
+ * Detach from the undo log we are currently attached to, returning it to the
+ * appropriate free list if it still has space.
+ */
+static void
+detach_current_undo_log(UndoPersistence persistence, bool full)
+{
+	UndoLogSlot *slot;
+
+	slot = CurrentSession->attached_undo_slots[persistence];
+
+	Assert(slot != NULL);
+
+	CurrentSession->attached_undo_slots[persistence] = NULL;
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = InvalidPid;
+	slot->meta.unlogged.xid = InvalidTransactionId;
+	if (full)
+		slot->meta.status = UNDO_LOG_STATUS_FULL;
+	LWLockRelease(&slot->mutex);
+
+	/* Push back onto the appropriate free list, unless it's full. */
+	if (!full)
+	{
+		LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+		slot->next_free = UndoLogShared->free_lists[persistence];
+		UndoLogShared->free_lists[persistence] = slot->logno;
+		LWLockRelease(UndoLogLock);
+	}
+}
+
+/*
+ * Exit handler, detaching from all undo logs.
+ */
+static void
+undo_log_before_exit(int code, Datum arg)
+{
+	int		i;
+
+	if (!CurrentSession)
+		return;
+
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+	{
+		if (CurrentSession->attached_undo_slots[i] != NULL)
+			detach_current_undo_log(i, false);
+	}
+}
+
+/*
+ * Create a new empty segment file on disk for the byte starting at 'end'.
+ */
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+							UndoLogOffset end)
+{
+	struct stat	stat_buffer;
+	off_t	size;
+	char	path[MAXPGPATH];
+	void   *zeroes;
+	size_t	nzeroes = 8192;
+	int		fd;
+
+	UndoLogSegmentPath(logno, end / UndoLogSegmentSize, tablespace, path);
+
+	/*
+	 * Create and fully allocate a new file.  If we crashed and recovered
+	 * then the file might already exist, so use flags that tolerate that.
+	 * It's also possible that it exists but is too short, in which case
+	 * we'll write the rest.  We don't really care what's in the file, we
+	 * just want to make sure that the filesystem has allocated physical
+	 * blocks for it, so that non-COW filesystems will report ENOSPC now
+	 * rather than later when the space is needed and we'll avoid creating
+	 * files with holes.
+	 */
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0 && tablespace != 0)
+	{
+		char undo_path[MAXPGPATH];
+
+		/* Try creating the undo directory for this tablespace. */
+		UndoLogDirectory(tablespace, undo_path);
+		if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+		{
+			char	   *parentdir;
+
+			if (errno != ENOENT || !InRecovery)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+
+			/*
+			 * In recovery, it's possible that the tablespace directory
+			 * doesn't exist because a later WAL record removed the whole
+			 * tablespace.  In that case we create a regular directory to
+			 * stand in for it.  This is similar to the logic in
+			 * TablespaceCreateDbspace().
+			 */
+
+			/* create two parents up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			/* create one parent up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+		}
+
+		fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	}
+	if (fd < 0)
+		elog(ERROR, "could not create new file \"%s\": %m", path);
+	if (fstat(fd, &stat_buffer) < 0)
+		elog(ERROR, "could not stat \"%s\": %m", path);
+	size = stat_buffer.st_size;
+
+	/* A buffer full of zeroes we'll use to fill up new segment files. */
+	zeroes = palloc0(nzeroes);
+
+	while (size < UndoLogSegmentSize)
+	{
+		ssize_t written;
+
+		written = write(fd, zeroes, Min(nzeroes, UndoLogSegmentSize - size));
+		if (written < 0)
+			elog(ERROR, "cannot initialize undo log segment file \"%s\": %m",
+				 path);
+		size += written;
+	}
+
+	/* Flush the contents of the file to disk before the next checkpoint. */
+	undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
+
+	CloseTransientFile(fd);
+
+	pfree(zeroes);
+
+	elog(DEBUG1, "created undo segment \"%s\"", path);
+}
+
+/*
+ * Create a new undo segment, when it is unexpectedly not present.
+ */
+void
+UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno)
+{
+	Assert(InRecovery);
+	allocate_empty_undo_segment(logno, tablespace, segno * UndoLogSegmentSize);
+}
+
+/*
+ * Create and zero-fill a new segment for a given undo log number.
+ */
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+	UndoLogSlot *slot;
+	size_t		end;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/* TODO review interlocking */
+
+	Assert(slot != NULL);
+	Assert(slot->meta.end % UndoLogSegmentSize == 0);
+	Assert(new_end % UndoLogSegmentSize == 0);
+	Assert(CurrentSession->attached_undo_slots[slot->meta.persistence] == slot || InRecovery);
+
+	/*
+	 * Create all the segments needed to increase 'end' to the requested
+	 * size.  This is quite expensive, so we will try to avoid it completely
+	 * by renaming files into place in UndoLogDiscard() instead.
+	 */
+	end = slot->meta.end;
+	while (end < new_end)
+	{
+		allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/*
+	 * If we're not in recovery, we need to WAL-log the creation of the new
+	 * file(s).  We do that after the above filesystem modifications, in
+	 * violation of the data-before-WAL rule as exempted by
+	 * src/backend/access/transam/README.  This means that it's possible for
+	 * us to crash having made some or all of the filesystem changes but
+	 * before WAL logging, but in that case we'll eventually try to create the
+	 * same segment(s) again, which is tolerated.
+	 */
+	if (!InRecovery)
+	{
+		xl_undolog_extend xlrec;
+		XLogRecPtr	ptr;
+
+		xlrec.logno = logno;
+		xlrec.end = end;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+		XLogFlush(ptr);
+	}
+
+	/*
+	 * We didn't need to acquire the mutex to read 'end' above because only
+	 * we write to it.  But we need the mutex to update it, because the
+	 * checkpointer might read it concurrently.
+	 *
+	 * XXX It's possible for meta.end to be higher already during
+	 * recovery, because of the timing of a checkpoint; in that case we did
+	 * nothing above and we shouldn't update shmem here.  That interaction
+	 * needs more analysis.
+	 */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (slot->meta.end < end)
+		slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * This function must be called before all of the undo log activity that will
+ * be covered by a single WAL record.
+ */
+void
+UndoLogBeginInsert(UndoLogAllocContext *context,
+				   UndoPersistence persistence,
+				   XLogReaderState *xlog_record)
+{
+	context->try_location = InvalidUndoRecPtr;
+	context->persistence = persistence;
+
+	/*
+	 * Tell UndoLogAllocate() to capture undo log meta-data before-change
+	 * images, so that UndoLogRegister() can find them and they can be written
+	 * to the WAL once per checkpoint.
+	 */
+	context->num_meta_data_images = 0;
+
+	/*
+	 * Tell UndoLogAllocateInRecovery() that we don't know which undo log to
+	 * allocate in yet, and to start its search for registered blocks at
+	 * the lowest-numbered block_id.
+	 */
+	context->xlog_record = xlog_record;
+	context->recovery_logno = InvalidUndoLogNumber;
+	context->recovery_block_id = 0;
+}
+
+/*
+ * Get an insertion point that is guaranteed to be backed by enough space to
+ * hold 'size' bytes of data.  To actually write into the undo log, client
+ * code should call this first and then use bufmgr routines to access buffers
+ * and provide WAL logs and redo handlers.  In other words, while this module
+ * looks after making sure the undo log has sufficient space and the undo meta
+ * data is crash safe, the *contents* of the undo log and (indirectly) the
+ * insertion point are the responsibility of client code.
+ *
+ * A suggested insertion point can optionally be passed in as 'try_location',
+ * and will be returned if possible.  If not InvalidUndoRecPtr, it must fall
+ * with, or exactly one byte after, the most recent allocation for the same
+ * persistence level.  This interface allows for a series of allocation to be
+ * made without committing to using the space yet; call UndoLogAdvance() to
+ * actually advance the insert pointer.
+ *
+ * Return an undo log insertion point that can be converted to a buffer tag
+ * and an insertion point within a buffer page.
+ */
+UndoRecPtr
+UndoLogAllocate(UndoLogAllocContext *context,
+				uint16 size,
+				bool *need_xact_header,
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
+{
+	Session *session = CurrentSession;
+	UndoLogSlot *slot;
+	UndoLogOffset new_insert;
+	TransactionId logxid;
+
+	slot = CurrentSession->attached_undo_slots[context->persistence];
+
+	/*
+	 * We may need to attach to an undo log, either because this is the first
+	 * time this backend as needed to write to an undo log at all or because
+	 * the undo_tablespaces GUC was changed.  When doing that, we'll need
+	 * interlocking against tablespaces being concurrently dropped.
+	 */
+
+ retry:
+	/* See if we need to check the undo_tablespaces GUC. */
+	if (unlikely(session->need_to_choose_undo_tablespace || slot == NULL))
+	{
+		Oid		tablespace;
+		bool	need_to_unlock;
+
+		need_to_unlock =
+			choose_undo_tablespace(session->need_to_choose_undo_tablespace,
+								   &tablespace);
+		attach_undo_log(context->persistence, tablespace);
+		if (need_to_unlock)
+			LWLockRelease(TablespaceCreateLock);
+		slot = CurrentSession->attached_undo_slots[context->persistence];
+		session->need_to_choose_undo_tablespace = false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	logxid = slot->meta.unlogged.xid;
+
+	if (logxid != GetTopTransactionId())
+	{
+		/*
+		 * While we have the lock, check if we have been forcibly detached by
+		 * DROP TABLESPACE.  That can only happen between transactions (see
+		 * DropUndoLogsInsTablespace()).
+		 */
+		if (slot->pid == InvalidPid)
+		{
+			LWLockRelease(&slot->mutex);
+			slot = NULL;
+			goto retry;
+		}
+		slot->meta.unlogged.xid = GetTopTransactionId();
+		if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+		{
+			slot->meta.unlogged.last_xact_start =
+				slot->meta.unlogged.this_xact_start;
+			slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+		}
+		LWLockRelease(&slot->mutex);
+	}
+	else
+	{
+		LWLockRelease(&slot->mutex);
+	}
+
+	/*
+	 * 'size' is expressed in usable non-header bytes.  Figure out how far we
+	 * have to move insert to create space for 'size' usable bytes, stepping
+	 * over any intervening headers.
+	 */
+	Assert(slot->meta.unlogged.insert % BLCKSZ >= UndoLogBlockHeaderSize);
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+
+		Assert(UndoRecPtrGetLogNo(context->try_location) == slot->logno);
+		Assert(try_offset <= slot->meta.end);
+		new_insert = UndoLogOffsetPlusUsableBytes(try_offset, size);
+	}
+	else
+	{
+		new_insert = UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert,
+												  size);
+	}
+	Assert(new_insert % BLCKSZ >= UndoLogBlockHeaderSize);
+
+	/*
+	 * We don't need to acquire log->mutex to read log->meta.insert and
+	 * log->meta.end, because this backend is the only one that can
+	 * modify them.
+	 */
+	if (unlikely(new_insert > slot->meta.end))
+	{
+		if (new_insert > UndoLogMaxSize)
+		{
+			/* This undo log is entirely full.  Get a new one. */
+			if (logxid == GetTopTransactionId())
+			{
+				/*
+				 * If the same transaction is split over two undo logs then
+				 * store the previous log number in new log.  See detailed
+				 * comments in undorecord.c file header.
+				 */
+				*prevlog_xact_start =
+					MakeUndoRecPtr(slot->logno,
+								   slot->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+			}
+			elog(DEBUG1, "undo log %u is full, switching to a new one", slot->logno);
+			slot = NULL;
+			detach_current_undo_log(context->persistence, true);
+			context->try_location = InvalidUndoRecPtr;
+			goto retry;
+		}
+		/*
+		 * Extend the end of this undo log to cover new_insert (in other words
+		 * round up to the segment size).
+		 */
+		extend_undo_log(slot->logno,
+						new_insert + UndoLogSegmentSize -
+						new_insert % UndoLogSegmentSize);
+		Assert(new_insert <= slot->meta.end);
+	}
+
+	/*
+	 * Create a back-up image of the unlogged part of the undo log's
+	 * meta-data, if we haven't already done so since UndoLogBeginInsert() (ie
+	 * for the WAL record that this undo allocation will be replayed by).
+	 */
+	if (context->num_meta_data_images == 0 ||
+		context->meta_data_images[context->num_meta_data_images - 1].logno != slot->logno)
+	{
+		if (context->num_meta_data_images >= MAX_META_DATA_IMAGES)
+			elog(ERROR, "too many undo log meta data images");
+		context->meta_data_images[context->num_meta_data_images].logno = slot->logno;
+		context->meta_data_images[context->num_meta_data_images++].data = slot->meta.unlogged;
+	}
+
+	/*
+	 * If no try_location was passed in, or if we switched logs, then we'll
+	 * return the current insertion point.
+	 */
+	if (context->try_location == InvalidUndoRecPtr)
+		context->try_location = MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+
+	/* Is this location the first in this undo log for a transaction? */
+	*need_xact_header =
+		UndoRecPtrGetOffset(context->try_location) == slot->meta.unlogged.this_xact_start;
+	*last_xact_start =
+		MakeUndoRecPtr(slot->logno, slot->meta.unlogged.last_xact_start);
+
+	return context->try_location;
+}
+
+void
+UndoLogRegister(UndoLogAllocContext *context, uint8 block_id, UndoLogNumber logno)
+{
+	int		i;
+
+	for (i = 0; i < context->num_meta_data_images; ++i)
+	{
+		if (context->meta_data_images[i].logno == logno)
+		{
+			XLogRegisterBufData(block_id,
+								(char *) &context->meta_data_images[i].data,
+								sizeof(context->meta_data_images[i].data));
+			return;
+		}
+	}
+}
+
+/*
+ * In recovery, we expect exactly the same sequence of allocation sizes, but
+ * we also need the WAL record that is being replayed so we can figure out
+ * where the undo space was allocated.
+ */
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+						  TransactionId xid,
+						  uint16 size,
+						  bool *need_xact_header,
+						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	Assert(InRecovery);
+
+	/*
+	 * Just as in UndoLogAllocate(), the caller may be extending an existing
+	 * allocation before committing with UndoLogAdvance().
+	 */
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+		UndoLogNumber logno = UndoRecPtrGetLogNo(context->try_location);
+
+		/*
+		 * You can only have a try_location on your second or later allocation
+		 * for a given WAL record.  It had better be in the same log as the
+		 * previous allocation for this WAL record (though it may not turn out
+		 * to have enough space, below).
+		 */
+		Assert(logno == context->recovery_logno);
+
+		/*
+		 * Any log extension triggered by UndoLogAllocate() must have been
+		 * replayed by now, so we can just check if this log has enough space,
+		 * and if so, return.
+		 */
+		slot = find_undo_log_slot(logno, false);
+		if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+		{
+			*need_xact_header = false;
+			return try_offset;
+		}
+
+		/* Full.  Ignore try_location and find the next log that was used. */
+		Assert(slot->meta.status == UNDO_LOG_STATUS_FULL);
+	}
+	else
+	{
+		/*
+		 * For now we only support one allocation per WAL record that doesn't
+		 * have a try_location (ie the first one).  We'll have to find out
+		 * which log was used first.
+		 */
+		Assert(context->recovery_logno == InvalidUndoLogNumber);
+	}
+
+	/*
+	 * In order to find the undo log that was used by UndoLogAllocate(), we
+	 * consult the list of registered blocks to figure out which undo logs
+	 * should be written to by this WAL record.
+	 */
+	while (context->recovery_block_id <= context->xlog_record->max_block_id)
+	{
+		DecodedBkpBlock *block;
+
+		/* We're looking for the first block referencing a new undo log. */
+		block = &context->xlog_record->blocks[context->recovery_block_id];
+		if (block->smgrid == SMGR_UNDO &&
+			block->rnode.dbNode == UndoLogDatabaseOid &&
+			block->rnode.relNode != context->recovery_logno)
+		{
+			UndoLogNumber logno = block->rnode.relNode;
+			const void *backup;
+			size_t backup_size;
+
+			/* We found a reference to a different (or first) undo log. */
+			slot = find_undo_log_slot(logno, false);
+
+			/*
+			 * Since on-line checkpoints capture an inconsistent snapshot of
+			 * undo log meta-data, we'll restore the unlogged part of the
+			 * meta-data image if one was attached to the WAL record (that is,
+			 * the members that don't have WAL records for every change
+			 * already).
+			 */
+			backup =
+				XLogRecGetBlockData(context->xlog_record,
+									context->recovery_block_id,
+									&backup_size);
+			if (unlikely(backup))
+			{
+				Assert(backup_size == sizeof(UndoLogUnloggedMetaData));
+
+				/* Restore the unlogged members from the backup-imaged. */
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				memcpy(&slot->meta.unlogged, backup, sizeof(UndoLogUnloggedMetaData));
+				LWLockRelease(&slot->mutex);
+			}
+			else
+			{
+				/*
+				 * Otherwise we need to do our own transaction tracking
+				 * whenever we see a new xid, to match the logic in
+				 * UndoLogAllocate().
+				 */
+				if (xid != slot->meta.unlogged.xid)
+				{
+					slot->meta.unlogged.xid = xid;
+					if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+						slot->meta.unlogged.last_xact_start =
+							slot->meta.unlogged.this_xact_start;
+					slot->meta.unlogged.this_xact_start =
+						slot->meta.unlogged.insert;
+				}
+			}
+
+			/* TODO: check locking against undo log slot recycling? */
+
+			/*
+			 * At this stage we should have an undo log that can handle this
+			 * allocation.  If we don't, something is screwed up.
+			 */
+			if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) > slot->meta.end)
+				elog(ERROR,
+					 "cannot allocate %d bytes in undo log %d",
+					 (int) size, slot->logno);
+
+			*need_xact_header =
+				context->try_location == InvalidUndoRecPtr &&
+				slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+			*last_xact_start = slot->meta.unlogged.last_xact_start;
+			context->recovery_logno = slot->logno;
+
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+
+			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
+			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+		}
+		++context->recovery_block_id;
+	}
+
+	/*
+	 * If we've run out of blocks to inspect, then we must have replayed a
+	 * different sequence of allocation sizes, or screwed up the
+	 * XLOG_UNDOLOG_EXTEND records, indicating a bug somewhere.
+	 */
+	elog(ERROR, "cannot determine undo log to allocate from");
+
+	return 0;		/* not reached */
+}
+
+/*
+ * Advance the insertion pointer in this context by 'size' usable (non-header)
+ * bytes.  This is the next place we'll try to allocate a record, if it fits.
+ * This is not committed to shared memory until after we've WAL-logged the
+ * record and UndoLogAdvanceFinal() is called.
+ */
+void
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+	context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+														 size);
+}
+
+/*
+ * Advance the insertion pointer to 'size' usable (non-header) bytes past
+ * insertion_point.
+ */
+void
+UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(insertion_point) ;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(InRecovery ||
+		   AmAttachedToUndoLogSlot(slot) ||
+		   slot->meta.status == UNDO_LOG_STATUS_FULL);
+
+	/*
+	 * The caller has the current insertion point, as returned by
+	 * UndoLogAllocate[InRecovery]().
+	 */
+	Assert(UndoRecPtrGetOffset(insertion_point) == slot->meta.unlogged.insert);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.insert =
+		UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size);
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * Advance the discard pointer in one undo log, discarding all undo data
+ * relating to one or more whole transactions.  The passed in undo pointer is
+ * the address of the oldest data that the called would like to keep, and the
+ * affected undo log is implied by this pointer, ie
+ * UndoRecPtrGetLogNo(discard_pointer).
+ *
+ * The caller asserts that there will be no attempts to access the undo log
+ * region being discarded after this moment.  This operation will cause the
+ * relevant buffers to be dropped immediately, without writing any data out to
+ * disk.  Any attempt to read the buffers (except a partial buffer at the end
+ * of this range which will remain) may result in IO errors, because the
+ * underlying segment file may have been physically removed.
+ *
+ * Return true if the discard point was updated, and false if nothing was done
+ * because the log precending the given point was already discarded.
+ *
+ * TODO: The return value is not yet reliable and the code still doesn't work
+ * correctly if called for the same undo log in two backends; more
+ * interlocking work required here.
+ */
+bool
+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(discard_point);
+	UndoLogOffset discard = UndoRecPtrGetOffset(discard_point);
+	UndoLogOffset old_discard;
+	UndoLogOffset end;
+	UndoLogSlot *slot;
+	int			segno;
+	int			new_segno;
+	bool		need_to_flush_wal = false;
+	bool		entirely_discarded = false;
+
+	slot = find_undo_log_slot(logno, false);
+	if (unlikely(slot == NULL))
+	{
+		/* Already discarded (entirely). */
+		return false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (unlikely(slot->logno != logno || discard <= slot->meta.discard))
+	{
+		/*
+		 * Already discarded entirely and the slot has been recycled, or up
+		 * to this point).
+		 */
+		LWLockRelease(&slot->mutex);
+		return false;
+	}
+	if (discard > slot->meta.unlogged.insert)
+		elog(ERROR, "cannot move discard point past insert point");
+	old_discard = slot->meta.discard;
+	end = slot->meta.end;
+	/* Are we discarding the last remaining data in a log marked as full? */
+	if (slot->meta.status == UNDO_LOG_STATUS_FULL &&
+		discard == slot->meta.unlogged.insert)
+	{
+		/*
+		 * Adjust the discard and insert pointers so that the final segment is
+		 * deleted from disk, and remember not to recycle it.
+		 */
+		entirely_discarded = true;
+		/* TODO: Check if the following line is replayed correctly */
+		slot->meta.unlogged.insert = slot->meta.end;
+		discard = slot->meta.end;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * TODO: I think we need a new lock just for this phase, so that buffer
+	 * dropping and IO are done by only one backend if a superuser command and
+	 * a discard worker both run this!
+	 */
+
+	/*
+	 * Drop all buffers holding this undo data out of the buffer pool (except
+	 * the last one, if the new location is in the middle of it somewhere), so
+	 * that the contained data doesn't ever touch the disk.  The caller
+	 * promises that this data will not be needed again.  We have to drop the
+	 * buffers from the buffer pool before removing files, otherwise a
+	 * concurrent session might try to write the block to evict the buffer.
+	 */
+	forget_undo_buffers(logno, old_discard, discard, entirely_discarded);
+
+	/*
+	 * Check if we crossed a segment boundary and need to do some synchronous
+	 * filesystem operations.
+	 */
+	segno = old_discard / UndoLogSegmentSize;
+	new_segno = discard / UndoLogSegmentSize;
+	if (segno < new_segno)
+	{
+		int		recycle;
+		UndoLogOffset pointer;
+
+		/*
+		 * We always WAL-log discards, but we only need to flush the WAL if we
+		 * have performed a filesystem operation.
+		 */
+		need_to_flush_wal = true;
+
+		/*
+		 * XXX When we rename or unlink a file, it's possible that some
+		 * backend still has it open because it has recently read a page from
+		 * it.  smgr/undofile.c in any such backend will eventually close it,
+		 * because it considers that fd to belong to the file with the name
+		 * that we're unlinking or renaming and it doesn't like to keep more
+		 * than one open at a time.  No backend should ever try to read from
+		 * such a file descriptor; that is what it means when we say that the
+		 * caller of UndoLogDiscard() asserts that there will be no attempts
+		 * to access the discarded range of undo log.  In the case of a
+		 * rename, if a backend were to attempt to read undo data in the range
+		 * being discarded, it would read entirely the wrong data.
+		 */
+
+		/*
+		 * How many segments should we recycle (= rename from tail position to
+		 * head position)?  For now it's always 1 unless there is already a
+		 * spare one, but we could have an adaptive algorithm that recycles
+		 * multiple segments at a time and pays just one fsync().
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if ((slot->meta.end - slot->meta.unlogged.insert) < UndoLogSegmentSize &&
+			slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+			recycle = 1;
+		else
+			recycle = 0;
+		LWLockRelease(&slot->mutex);
+
+		/* Rewind to the start of the segment. */
+		pointer = segno * UndoLogSegmentSize;
+
+		while (pointer < new_segno * UndoLogSegmentSize)
+		{
+			char	discard_path[MAXPGPATH];
+
+			/* Tell the checkpointer that the file is going away. */
+			undofile_forget_sync(logno, pointer / UndoLogSegmentSize,
+								 slot->meta.tablespace);
+
+			UndoLogSegmentPath(logno, pointer / UndoLogSegmentSize,
+							   slot->meta.tablespace, discard_path);
+
+			/* Can we recycle the oldest segment? */
+			if (recycle > 0)
+			{
+				char	recycle_path[MAXPGPATH];
+
+				/*
+				 * End points one byte past the end of the current undo space,
+				 * ie to the first byte of the segment file we want to create.
+				 */
+				UndoLogSegmentPath(logno, end / UndoLogSegmentSize,
+								   slot->meta.tablespace, recycle_path);
+				if (rename(discard_path, recycle_path) == 0)
+				{
+					elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+						 discard_path, recycle_path);
+					end += UndoLogSegmentSize;
+					--recycle;
+				}
+				else
+				{
+					elog(ERROR, "could not rename \"%s\" to \"%s\": %m",
+						 discard_path, recycle_path);
+				}
+			}
+			else
+			{
+				if (unlink(discard_path) == 0)
+					elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+				else
+					elog(ERROR, "could not unlink \"%s\": %m", discard_path);
+			}
+			pointer += UndoLogSegmentSize;
+		}
+	}
+
+	/* WAL log the discard. */
+	{
+		xl_undolog_discard xlrec;
+		XLogRecPtr ptr;
+
+		xlrec.logno = logno;
+		xlrec.discard = discard;
+		xlrec.end = end;
+		xlrec.latestxid = xid;
+		xlrec.entirely_discarded = entirely_discarded;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_DISCARD);
+
+		if (need_to_flush_wal)
+			XLogFlush(ptr);
+	}
+
+	/* Update shmem to show the new discard and end pointers. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (entirely_discarded)
+		free_undo_log_slot(slot);
+
+	return true;
+}
+
+/*
+ * Return an UndoRecPtr to the oldest valid data in an undo log, or
+ * InvalidUndoRecPtr if it is empty.
+ */
+UndoRecPtr
+UndoLogGetOldestRecord(UndoLogNumber logno, bool *full)
+{
+	UndoLogSlot *slot;
+	UndoRecPtr	result;
+
+	/* Try to find the slot for this undo log number. */
+	slot = find_undo_log_slot(logno, false);
+	if (slot == NULL)
+	{
+		/* It's unknown to us, so we assume it's been entirely discarded. */
+		if (full)
+			*full = true;
+		return InvalidUndoRecPtr;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->logno != logno)
+	{
+		/* It's been recycled.  SO it must have been entirely discarded. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = true;
+	}
+	else if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		/* It's empty, so there is no oldest record pointer to return. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	else
+	{
+		/* There is a record here! */
+		result = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLogSlot(slot));
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&slot->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
+ * Return the Next insert location.  This will also validate the input xid
+ * if latest insert point is not for the same transaction id then this will
+ * return Invalid Undo pointer.
+ */
+UndoRecPtr
+UndoLogGetNextInsertPtr(UndoLogNumber logno, TransactionId xid)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	TransactionId	logxid;
+	UndoRecPtr	insert;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	insert = slot->meta.unlogged.insert;
+	logxid = slot->meta.unlogged.xid;
+	LWLockRelease(&slot->mutex);
+
+	if (TransactionIdIsValid(logxid) && !TransactionIdEquals(logxid, xid))
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, insert);
+}
+
+/*
+ * Delete unreachable files under pg_undo.  Any files corresponding to LSN
+ * positions before the previous checkpoint are no longer needed.
+ */
+static void
+CleanUpUndoCheckPointFiles(XLogRecPtr checkPointRedo)
+{
+	DIR	   *dir;
+	struct dirent *de;
+	char	path[MAXPGPATH];
+	char	oldest_path[MAXPGPATH];
+
+	/*
+	 * If a base backup is in progress, we can't delete any checkpoint
+	 * snapshot files because one of them corresponds to the backup label but
+	 * there could be any number of checkpoints during the backup.
+	 */
+	if (BackupInProgress())
+		return;
+
+	/* Otherwise keep only those >= the previous checkpoint's redo point. */
+	snprintf(oldest_path, MAXPGPATH, "%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	dir = AllocateDir("pg_undo");
+	while ((de = ReadDir(dir, "pg_undo")) != NULL)
+	{
+		/*
+		 * Assume that fixed width uppercase hex strings sort the same way as
+		 * the values they represent, so we can use strcmp to identify undo
+		 * log snapshot files corresponding to checkpoints that we don't need
+		 * anymore.  This assumption holds for ASCII.
+		 */
+		if (!(strlen(de->d_name) == UNDO_CHECKPOINT_FILENAME_LENGTH))
+			continue;
+
+		if (UndoCheckPointFilenamePrecedes(de->d_name, oldest_path))
+		{
+			snprintf(path, MAXPGPATH, "pg_undo/%s", de->d_name);
+			if (unlink(path) != 0)
+				elog(ERROR, "could not unlink file \"%s\": %m", path);
+		}
+	}
+	FreeDir(dir);
+}
+
+/*
+ * Write out the undo log meta data to the pg_undo directory.  The actual
+ * contents of undo logs is in shared buffers and therefore handled by
+ * CheckPointBuffers(), but here we record the table of undo logs and their
+ * properties.
+ */
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
+{
+	UndoLogMetaData *serialized = NULL;
+	size_t	serialized_size = 0;
+	char   *data;
+	char	path[MAXPGPATH];
+	UndoLogNumber num_logs;
+	int		fd;
+	int		i;
+	pg_crc32c crc;
+
+	/*
+	 * We acquire UndoLogLock to prevent any undo logs from being created or
+	 * discarded while we build a snapshot of them.  This isn't expected to
+	 * take long on a healthy system because the number of active logs should
+	 * be around the number of backends.  Holding this lock won't prevent
+	 * concurrent access to the undo log, except when segments need to be
+	 * added or removed.
+	 */
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+
+	/*
+	 * Rather than doing the file IO while we hold locks, we'll copy the
+	 * meta-data into a palloc'd buffer.
+	 */
+	serialized_size = sizeof(UndoLogMetaData) * UndoLogNumSlots();
+	serialized = (UndoLogMetaData *) palloc0(serialized_size);
+
+	/* Scan through all slots looking for non-empty ones. */
+	num_logs = 0;
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+
+		/* Skip empty slots. */
+		if (slot->logno == InvalidUndoLogNumber)
+			continue;
+
+		/* Capture snapshot while holding each mutex. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		serialized[num_logs++] = slot->meta;
+		LWLockRelease(&slot->mutex);
+	}
+
+	LWLockRelease(UndoLogLock);
+
+	/* Dump into a file under pg_undo. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE);
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not create file \"%s\": %m", path)));
+
+	/* Compute header checksum. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the number of active logs + crc. */
+	if ((write(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno)) ||
+		(write(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno)) != sizeof(UndoLogShared->next_logno)) ||
+		(write(fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+	/* Write out the meta data for all active undo logs. */
+	data = (char *) serialized;
+	INIT_CRC32C(crc);
+	serialized_size = num_logs * sizeof(UndoLogMetaData);
+	while (serialized_size > 0)
+	{
+		ssize_t written;
+
+		written = write(fd, data, serialized_size);
+		if (written < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write to file \"%s\": %m", path)));
+		COMP_CRC32C(crc, data, written);
+		serialized_size -= written;
+		data += written;
+	}
+	FIN_CRC32C(crc);
+
+	if (write(fd, &crc, sizeof(crc)) != sizeof(crc))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+
+	/* Flush file and directory entry. */
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC);
+	pg_fsync(fd);
+	if (CloseTransientFile(fd) < 0)
+		ereport(data_sync_elevel(ERROR),
+				(errcode_for_file_access(),
+				 errmsg("could not close file \"%s\": %m", path)));
+	fsync_fname("pg_undo", true);
+	pgstat_report_wait_end();
+
+	if (serialized)
+		pfree(serialized);
+
+	CleanUpUndoCheckPointFiles(priorCheckPointRedo);
+}
+
+void
+StartupUndoLogs(XLogRecPtr checkPointRedo)
+{
+	char	path[MAXPGPATH];
+	int		i;
+	int		fd;
+	int		nlogs;
+	pg_crc32c crc;
+	pg_crc32c new_crc;
+
+	/* If initdb is calling, there is no file to read yet. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/* Open the pg_undo file corresponding to the given checkpoint. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_READ);
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
+
+	/* Read the active log number range. */
+	if ((read(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno))
+		 != sizeof(UndoLogShared->low_logno)) ||
+		(read(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno))
+		 != sizeof(UndoLogShared->next_logno)) ||
+		(read(fd, &nlogs, sizeof(nlogs)) != sizeof(nlogs)) ||
+		(read(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+
+	/* Verify the header checksum. */
+	INIT_CRC32C(new_crc);
+	COMP_CRC32C(new_crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(new_crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(new_crc, &nlogs, sizeof(UndoLogShared->next_logno));
+	FIN_CRC32C(new_crc);
+
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	/*
+	 * We'll acquire UndoLogLock just because allocate_undo_log() asserts we
+	 * hold it (we don't actually expect concurrent access yet).
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* Initialize all the logs and set up the freelist. */
+	INIT_CRC32C(new_crc);
+	for (i = 0; i < nlogs; ++i)
+	{
+		ssize_t size;
+		UndoLogSlot *slot;
+
+		/*
+		 * Get a new UndoLogSlot.  If this checkpoint was created on a system
+		 * with a higher max_connections setting, it's theoretically possible
+		 * that we don't have enough space and cannot start up.
+		 */
+		slot = allocate_undo_log_slot();
+		if (!slot)
+			ereport(ERROR,
+					(errmsg("not enough undo log slots to recover from checkpoint: need at least %d, have %zu",
+							nlogs, UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections")));
+
+		/* Read in the meta data for this undo log. */
+		if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
+			elog(ERROR, "short read of pg_undo meta data in file \"%s\": %m (got %zu, wanted %zu)",
+				 path, size, sizeof(slot->meta));
+		COMP_CRC32C(new_crc, &slot->meta, sizeof(slot->meta));
+
+		/*
+		 * At normal start-up, or during recovery, all active undo logs start
+		 * out on the appropriate free list.
+		 */
+		slot->logno = slot->meta.logno;
+		slot->pid = InvalidPid;
+		slot->oldest_data = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+		{
+			slot->next_free = UndoLogShared->free_lists[slot->meta.persistence];
+			UndoLogShared->free_lists[slot->meta.persistence] = slot->logno;
+		}
+	}
+	FIN_CRC32C(new_crc);
+
+	LWLockRelease(UndoLogLock);
+
+	/* Verify body checksum. */
+	if (read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	CloseTransientFile(fd);
+	pgstat_report_wait_end();
+}
+
+/*
+ * Allocate a new UndoLogSlot object.
+ */
+static UndoLogSlot *
+allocate_undo_log_slot(void)
+{
+	UndoLogSlot *slot;
+	UndoLogNumber i;
+
+	Assert(LWLockHeldByMeInMode(UndoLogLock, LW_EXCLUSIVE));
+
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		slot = &UndoLogShared->slots[i];
+		if (slot->logno == InvalidUndoLogNumber)
+		{
+			memset(&slot->meta, 0, sizeof(slot->meta));
+			slot->pid = 0;
+			slot->oldest_xid = 0;
+			slot->oldest_xidepoch = 0;
+			slot->oldest_data =0;
+			slot->next_free = -1;
+			slot->logno = -1;
+			return slot;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Free an UndoLogSlot object in shared memory, so that it can be reused.
+ * This is a rare event, and has complications for all code paths that access
+ * slots.  Unless the current session is attached to the slot, it must be
+ * prepared for it to be freed and then potentially recycled for use by
+ * another log.  See UndoLogGetSlot().
+ */
+static void
+free_undo_log_slot(UndoLogSlot *slot)
+{
+	/*
+	 * When removing an undo log from a slot in shared memory, we acquire
+	 * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+	 * hold any one of those locks to prevent the slot from being recycled.
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno != InvalidUndoLogNumber);
+	slot->logno = InvalidUndoLogNumber;
+	memset(&slot->meta, 0, sizeof(slot->meta));
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * Find the UndoLogSlot object for a given log number.
+ *
+ * The caller may or may not already hold UndoLogLock, and should indicate
+ * this by passing 'locked'.  We'll acquire it in the slow path if necessary.
+ * If it is not held by the caller, the caller must deal with the possibility
+ * that the returned UndoLogSlot no longer contains the requested logno by the
+ * time it is accessed.
+ *
+ * To do that, one of the following approaches must be taken by the calling
+ * code:
+ *
+ * 1.  If the calling code knows that it is attached to this lock or is the
+ * recovery process, then there is no way for the slot to be recycled, so it's
+ * not necessary to check that the log number hasn't changed.  The slot cannot
+ * be recycled while a backend is attached.  It should probably assert that it
+ * is attached, however.
+ *
+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
+ *
+ * Return NULL if the undo log has been entirely discarded.  It is an error to
+ * ask for undo logs that have never been created.
+ */
+static UndoLogSlot *
+find_undo_log_slot(UndoLogNumber logno, bool locked)
+{
+	UndoLogSlot *result = NULL;
+	UndoLogTableEntry *entry;
+	bool	   found;
+
+	Assert(locked == LWLockHeldByMe(UndoLogLock));
+
+	/* First see if we already have it in our cache. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		result = entry->slot;
+	else
+	{
+		UndoLogNumber i;
+
+		/* Nope.  Linear search for the slot in shared memory. */
+		if (!locked)
+			LWLockAcquire(UndoLogLock, LW_SHARED);
+		for (i = 0; i < UndoLogNumSlots(); ++i)
+		{
+			if (UndoLogShared->slots[i].logno == logno)
+			{
+				/* Found it. */
+
+				/*
+				 * TODO: Should this function be usable in a critical section?
+				 * Would it make sense to detect that we are in a critical
+				 * section and just return the pointer to the log without
+				 * updating the cache, to avoid any chance of allocating
+				 * memory?
+				 */
+
+				entry = undologtable_insert(undologtable_cache, logno, &found);
+				entry->number = logno;
+				entry->slot = &UndoLogShared->slots[i];
+				entry->tablespace = entry->slot->meta.tablespace;
+				result = entry->slot;
+				break;
+			}
+		}
+
+		/*
+		 * If we didn't find it, then it must already have been entirely
+		 * discarded.  We create a negative cache entry so that we can answer
+		 * this question quickly next time.
+		 *
+		 * TODO: We could track the lowest known undo log number, to reduce
+		 * the negative cache entry bloat.
+		 */
+		if (result == NULL)
+		{
+			/*
+			 * Sanity check: the caller should not be asking about undo logs
+			 * that have never existed.
+			 */
+			if (logno >= UndoLogShared->next_logno)
+				elog(ERROR, "undo log %u hasn't been created yet", logno);
+			entry = undologtable_insert(undologtable_cache, logno, &found);
+			entry->number = logno;
+			entry->slot = NULL;
+			entry->tablespace = 0;
+		}
+		if (!locked)
+			LWLockRelease(UndoLogLock);
+	}
+
+	return result;
+}
+
+/*
+ * Get a pointer to an UndoLogSlot object corresponding to a given logno.
+ *
+ * In general, the caller must acquire the UndoLogSlot's mutex to access
+ * the contents, and at that time must consider that the logno might have
+ * changed because the undo log it contained has been entirely discarded.
+ *
+ * If the calling backend is currently attached to the undo log, that is not
+ * possible, because logs can only reach UNDO_LOG_STATUS_DISCARDED after first
+ * reaching UNDO_LOG_STATUS_FULL, and that only happens while detaching.
+ */
+UndoLogSlot *
+UndoLogGetSlot(UndoLogNumber logno, bool missing_ok)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL && !missing_ok)
+		elog(ERROR, "unknown undo log number %d", logno);
+
+	return slot;
+}
+
+/*
+ * Attach to a free undo log, creating a new one if required.
+ */
+static void
+attach_undo_log(UndoPersistence persistence, Oid tablespace)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber logno;
+	UndoLogNumber *place;
+
+	Assert(!InRecovery);
+	Assert(CurrentSession->attached_undo_slots[persistence] == NULL);
+
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/*
+	 * For now we have a simple linked list of unattached undo logs for each
+	 * persistence level.  We'll grovel though it to find something for the
+	 * tablespace you asked for.  If you're not using multiple tablespaces
+	 * it'll be able to pop one off the front.  We might need a hash table
+	 * keyed by tablespace if this simple scheme turns out to be too slow when
+	 * using many tablespaces and many undo logs, but that seems like an
+	 * unusual use case not worth optimizing for.
+	 */
+	place = &UndoLogShared->free_lists[persistence];
+	while (*place != InvalidUndoLogNumber)
+	{
+		UndoLogSlot *candidate = find_undo_log_slot(*place, true);
+
+		/*
+		 * There should never be an undo log on the freelist that has been
+		 * entirely discarded, or hasn't been created yet.  The persistence
+		 * level should match the freelist.
+		 */
+		if (unlikely(candidate == NULL))
+			elog(ERROR,
+				 "corrupted undo log freelist, no such undo log %u", *place);
+		if (unlikely(candidate->meta.persistence != persistence))
+			elog(ERROR,
+				 "corrupted undo log freelist, undo log %u with persistence %d found on freelist %d",
+				 *place, candidate->meta.persistence, persistence);
+
+		if (candidate->meta.tablespace == tablespace)
+		{
+			logno = *place;
+			slot = candidate;
+			*place = candidate->next_free;
+			break;
+		}
+		place = &candidate->next_free;
+	}
+
+	/*
+	 * If all existing undo logs for this tablespace and persistence level are
+	 * busy, we'll have to create a new one.
+	 */
+	if (slot == NULL)
+	{
+		if (UndoLogShared->next_logno > MaxUndoLogNumber)
+		{
+			/*
+			 * You've used up all 16 exabytes of undo log addressing space.
+			 * This is a difficult state to reach using only 16 exabytes of
+			 * WAL.
+			 */
+			elog(ERROR, "undo log address space exhausted");
+		}
+
+		/* Allocate a slot from the UndoLogSlot pool. */
+		slot = allocate_undo_log_slot();
+		if (unlikely(!slot))
+			ereport(ERROR,
+					(errmsg("could not create new undo log"),
+					 errdetail("The maximum number of active undo logs is %zu.",
+							   UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections.")));
+		slot->logno = logno = UndoLogShared->next_logno;
+
+		/*
+		 * The insert and discard pointers start after the first block's
+		 * header.  XXX That means that insert is > end for a short time in a
+		 * newly created undo log.  Is there any problem with that?
+		 */
+		slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+		slot->meta.discard = UndoLogBlockHeaderSize;
+
+		slot->meta.logno = logno;
+		slot->meta.tablespace = tablespace;
+		slot->meta.persistence = persistence;
+		slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+
+		/* Move the high log number pointer past this one. */
+		++UndoLogShared->next_logno;
+
+		/* WAL-log the creation of this new undo log. */
+		{
+			xl_undolog_create xlrec;
+
+			xlrec.logno = logno;
+			xlrec.tablespace = slot->meta.tablespace;
+			xlrec.persistence = slot->meta.persistence;
+
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+			XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_CREATE);
+		}
+
+		/*
+		 * This undo log has no segments.  UndoLogAllocate will create the
+		 * first one on demand.
+		 */
+	}
+	LWLockRelease(UndoLogLock);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = MyProcPid;
+	LWLockRelease(&slot->mutex);
+
+	CurrentSession->attached_undo_slots[persistence] = slot;
+}
+
+/* check_hook: validate new undo_tablespaces */
+bool
+check_undo_tablespaces(char **newval, void **extra, GucSource source)
+{
+	char	   *rawname;
+	List	   *namelist;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/*
+	 * Parse string into list of identifiers, just to check for
+	 * well-formedness (unfortunateley we can't validate the names in the
+	 * catalog yet).
+	 */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	/*
+	 * Make sure we aren't already in a transaction that has been assigned an
+	 * XID.  This ensures we don't detach from an undo log that we might have
+	 * started writing undo data into for this transaction.
+	 */
+	if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 (errmsg("undo_tablespaces cannot be changed while a transaction is in progress"))));
+	list_free(namelist);
+
+	return true;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_undo_tablespaces(const char *newval, void *extra)
+{
+	/*
+	 * This is normally called only when GetTopTransactionIdIfAny() ==
+	 * InvalidTransactionId (because you can't change undo_tablespaces in the
+	 * middle of a transaction that's been asigned an xid), but we can't
+	 * assert that because it's also called at the end of a transaction that's
+	 * rolling back, to reset the GUC if it was set inside the transaction.
+	 */
+
+	/* Tell UndoLogAllocate() to reexamine undo_tablespaces. */
+	if (CurrentSession)
+		CurrentSession->need_to_choose_undo_tablespace = true;
+}
+
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+	char   *rawname;
+	List   *namelist;
+	bool	need_to_unlock;
+	int		length;
+	int		i;
+
+	/* We need a modifiable copy of string. */
+	rawname = pstrdup(undo_tablespaces);
+
+	/* Break string into list of identifiers. */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+		elog(ERROR, "undo_tablespaces is unexpectedly malformed");
+
+	length = list_length(namelist);
+	if (length == 0 ||
+		(length == 1 && ((char *) linitial(namelist))[0] == '\0'))
+	{
+		/*
+		 * If it's an empty string, then we'll use the default tablespace.  No
+		 * locking is required because it can't be dropped.
+		 */
+		*tablespace = DEFAULTTABLESPACE_OID;
+		need_to_unlock = false;
+	}
+	else
+	{
+		/*
+		 * Choose an OID using our pid, so that if several backends have the
+		 * same multi-tablespace setting they'll spread out.  We could easily
+		 * do better than this if more serious load balancing is judged
+		 * useful.
+		 */
+		int		index = MyProcPid % length;
+		int		first_index = index;
+		Oid		oid = InvalidOid;
+
+		/*
+		 * Take the tablespace create/drop lock while we look the name up.
+		 * This prevents the tablespace from being dropped while we're trying
+		 * to resolve the name, or while the called is trying to create an
+		 * undo log in it.  The caller will have to release this lock.
+		 */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		for (;;)
+		{
+			const char *name = list_nth(namelist, index);
+
+			oid = get_tablespace_oid(name, true);
+			if (oid == InvalidOid)
+			{
+				/* Unknown tablespace, try the next one. */
+				index = (index + 1) % length;
+				/*
+				 * But if we've tried them all, it's time to complain.  We'll
+				 * arbitrarily complain about the last one we tried in the
+				 * error message.
+				 */
+				if (index == first_index)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", name),
+							 errhint("Create the tablespace or set undo_tablespaces to a valid or empty list.")));
+				continue;
+			}
+			if (oid == GLOBALTABLESPACE_OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("undo logs cannot be placed in pg_global tablespace")));
+			/* If we got here we succeeded in finding one. */
+			break;
+		}
+
+		Assert(oid != InvalidOid);
+		*tablespace = oid;
+		need_to_unlock = true;
+	}
+
+	/*
+	 * If we came here because the user changed undo_tablesaces, then detach
+	 * from any undo logs we happen to be attached to.
+	 */
+	if (force_detach)
+	{
+		for (i = 0; i < UndoPersistenceLevels; ++i)
+		{
+			UndoLogSlot *slot = CurrentSession->attached_undo_slots[i];
+
+			if (slot != NULL)
+			{
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				slot->pid = InvalidPid;
+				slot->meta.unlogged.xid = InvalidTransactionId;
+				LWLockRelease(&slot->mutex);
+
+				LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+				slot->next_free = UndoLogShared->free_lists[i];
+				UndoLogShared->free_lists[i] = slot->logno;
+				LWLockRelease(UndoLogLock);
+
+				CurrentSession->attached_undo_slots[i] = NULL;
+			}
+		}
+	}
+
+	return need_to_unlock;
+}
+
+bool
+DropUndoLogsInTablespace(Oid tablespace)
+{
+	DIR *dir;
+	char undo_path[MAXPGPATH];
+	UndoLogSlot *slot = NULL;
+	int		i;
+
+	Assert(LWLockHeldByMe(TablespaceCreateLock));
+	Assert(tablespace != DEFAULTTABLESPACE_OID);
+
+	/* First, try to kick everyone off any undo logs in this tablespace. */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		bool ok;
+		bool return_to_freelist = false;
+
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/* Check if this undo log can be forcibly detached. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		if (slot->meta.discard == slot->meta.unlogged.insert &&
+			(slot->meta.unlogged.xid == InvalidTransactionId ||
+			 !TransactionIdIsInProgress(slot->meta.unlogged.xid)))
+		{
+			slot->meta.unlogged.xid = InvalidTransactionId;
+			if (slot->pid != InvalidPid)
+			{
+				slot->pid = InvalidPid;
+				return_to_freelist = true;
+			}
+			ok = true;
+		}
+		else
+		{
+			/*
+			 * There is data we need in this undo log.  We can't force it to
+			 * be detached.
+			 */
+			ok = false;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* If we failed, then give up now and report failure. */
+		if (!ok)
+			return false;
+
+		/*
+		 * Put this undo log back on the appropriate free-list.  No one can
+		 * attach to it while we hold TablespaceCreateLock, but if we return
+		 * earlier in a future go around this loop, we need the undo log to
+		 * remain usable.  We'll remove all appropriate logs from the
+		 * free-lists in a separate step below.
+		 */
+		if (return_to_freelist)
+		{
+			LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+			slot->next_free = UndoLogShared->free_lists[slot->meta.persistence];
+			UndoLogShared->free_lists[slot->meta.persistence] = slot->logno;
+			LWLockRelease(UndoLogLock);
+		}
+	}
+
+	/*
+	 * We detached all backends from undo logs in this tablespace, and no one
+	 * can attach to any non-default-tablespace undo logs while we hold
+	 * TablespaceCreateLock.  We can now drop the undo logs.
+	 */
+	slot = NULL;
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/*
+		 * Make sure no buffers remain.  When that is done by
+		 * UndoLogDiscard(), the final page is left in shared_buffers because
+		 * it may contain data, or at least be needed again very soon.  Here
+		 * we need to drop even that page from the buffer pool.
+		 */
+		forget_undo_buffers(slot->logno, slot->meta.discard, slot->meta.discard, true);
+
+		/*
+		 * TODO: For now we drop the undo log, meaning that it will never be
+		 * used again.  That wastes the rest of its address space.  Instead,
+		 * we should put it onto a special list of 'offline' undo logs, ready
+		 * to be reactivated in some other tablespace.  Then we can keep the
+		 * unused portion of its address space.
+		 */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		slot->meta.status = UNDO_LOG_STATUS_DISCARDED;
+		LWLockRelease(&slot->mutex);
+	}
+
+	/* Forget about all sync requests relating to this tablespace. */
+	undofile_forget_sync_tablespace(tablespace);
+
+	/* Unlink all undo segment files in this tablespace. */
+	UndoLogDirectory(tablespace, undo_path);
+
+	dir = AllocateDir(undo_path);
+	if (dir != NULL)
+	{
+		struct dirent *de;
+
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strcmp(de->d_name, ".") == 0 ||
+				strcmp(de->d_name, "..") == 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+	}
+
+	/* Remove all dropped undo logs from the free-lists. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+	{
+		UndoLogSlot *slot;
+		UndoLogNumber *place;
+
+		place = &UndoLogShared->free_lists[i];
+		while (*place != InvalidUndoLogNumber)
+		{
+			slot = find_undo_log_slot(*place, true);
+			if (!slot)
+				elog(ERROR,
+					 "corrupted undo log freelist, unknown log %u", *place);
+			if (slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+				*place = slot->next_free;
+			else
+				place = &slot->next_free;
+		}
+	}
+	LWLockRelease(UndoLogLock);
+
+	return true;
+}
+
+void
+ResetUndoLogs(UndoPersistence persistence)
+{
+	UndoLogSlot *slot = NULL;
+
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		DIR	   *dir;
+		struct dirent *de;
+		char	undo_path[MAXPGPATH];
+		char	segment_prefix[MAXPGPATH];
+		size_t	segment_prefix_size;
+
+		if (slot->meta.persistence != persistence)
+			continue;
+
+		/* Scan the directory for files belonging to this undo log. */
+		snprintf(segment_prefix, sizeof(segment_prefix), "%06X.", slot->logno);
+		segment_prefix_size = strlen(segment_prefix);
+		UndoLogDirectory(slot->meta.tablespace, undo_path);
+		dir = AllocateDir(undo_path);
+		if (dir == NULL)
+			continue;
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+
+		/*
+		 * We have no segment files.  Set the pointers to indicate that there
+		 * is no data.  The discard and insert pointers point to the first
+		 * usable byte in the segment we will create when we next try to
+		 * allocate.  This is a bit strange, because it means that they are
+		 * past the end pointer.  That's the same as when new undo logs are
+		 * created.
+		 *
+		 * TODO: Should we rewind to zero instead, so we can reuse that (now)
+		 * unreferenced address space?
+		 */
+		slot->meta.unlogged.insert = slot->meta.discard = slot->meta.end +
+			UndoLogBlockHeaderSize;
+	}
+}
+
+Datum
+pg_stat_get_undo_logs(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_UNDO_LOGS_COLS 9
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	char *tablespace_name = NULL;
+	Oid last_tablespace = InvalidOid;
+	int			i;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Scan all undo logs to build the results. */
+	for (i = 0; i < UndoLogShared->nslots; ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+		char buffer[17];
+		Datum values[PG_STAT_GET_UNDO_LOGS_COLS];
+		bool nulls[PG_STAT_GET_UNDO_LOGS_COLS] = { false };
+		Oid tablespace;
+
+		/*
+		 * This won't be a consistent result overall, but the values for each
+		 * log will be consistent because we'll take the per-log lock while
+		 * copying them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+
+		/* Skip unused slots and entirely discarded undo logs. */
+		if (slot->logno == InvalidUndoLogNumber ||
+			slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+
+		values[0] = ObjectIdGetDatum((Oid) slot->logno);
+		values[1] = CStringGetTextDatum(
+			slot->meta.persistence == UNDO_PERMANENT ? "permanent" :
+			slot->meta.persistence == UNDO_UNLOGGED ? "unlogged" :
+			slot->meta.persistence == UNDO_TEMP ? "temporary" : "<uknown>");
+		tablespace = slot->meta.tablespace;
+
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.discard));
+		values[3] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert));
+		values[4] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.end));
+		values[5] = CStringGetTextDatum(buffer);
+		if (slot->meta.unlogged.xid == InvalidTransactionId)
+			nulls[6] = true;
+		else
+			values[6] = TransactionIdGetDatum(slot->meta.unlogged.xid);
+		if (slot->pid == InvalidPid)
+			nulls[7] = true;
+		else
+			values[7] = Int32GetDatum((int32) slot->pid);
+		switch (slot->meta.status)
+		{
+		case UNDO_LOG_STATUS_ACTIVE:
+			values[8] = CStringGetTextDatum("ACTIVE"); break;
+		case UNDO_LOG_STATUS_FULL:
+			values[8] = CStringGetTextDatum("FULL"); break;
+		default:
+			nulls[8] = true;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/*
+		 * Deal with potentially slow tablespace name lookup without the lock.
+		 * Avoid making multiple calls to that expensive function for the
+		 * common case of repeating tablespace.
+		 */
+		if (tablespace != last_tablespace)
+		{
+			if (tablespace_name)
+				pfree(tablespace_name);
+			tablespace_name = get_tablespace_name(tablespace);
+			last_tablespace = tablespace;
+		}
+		if (tablespace_name)
+		{
+			values[2] = CStringGetTextDatum(tablespace_name);
+			nulls[2] = false;
+		}
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	if (tablespace_name)
+		pfree(tablespace_name);
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * replay the creation of a new undo log
+ */
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+	xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	/* Create meta-data space in shared memory. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* TODO: assert that it doesn't exist already? */
+
+	slot = allocate_undo_log_slot();
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->logno = xlrec->logno;
+	slot->meta.logno = xlrec->logno;
+	slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+	slot->meta.persistence = xlrec->persistence;
+	slot->meta.tablespace = xlrec->tablespace;
+	slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+	slot->meta.discard = UndoLogBlockHeaderSize;
+	UndoLogShared->next_logno = Max(xlrec->logno + 1, UndoLogShared->next_logno);
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * replay the addition of a new segment to an undo log
+ */
+static void
+undolog_xlog_extend(XLogReaderState *record)
+{
+	xl_undolog_extend *xlrec = (xl_undolog_extend *) XLogRecGetData(record);
+
+	/* Extend exactly as we would during DO phase. */
+	extend_undo_log(xlrec->logno, xlrec->end);
+}
+
+/*
+ * Drop all buffers for the given undo log, from the old_discard to up
+ * new_discard.  If drop_tail is true, also drop the buffer that holds
+ * new_discard; this is used when discarding undo logs completely, for example
+ * via DROP TABLESPACE.  If it is false, then the final buffer is not dropped
+ * because it may contain data.
+ *
+ */
+static void
+forget_undo_buffers(int logno, UndoLogOffset old_discard,
+					UndoLogOffset new_discard, bool drop_tail)
+{
+	BlockNumber old_blockno;
+	BlockNumber new_blockno;
+	RelFileNode	rnode;
+
+	UndoRecPtrAssignRelFileNode(rnode, MakeUndoRecPtr(logno, old_discard));
+	old_blockno = old_discard / BLCKSZ;
+	new_blockno = new_discard / BLCKSZ;
+	if (drop_tail)
+		++new_blockno;
+	while (old_blockno < new_blockno)
+	{
+		ForgetBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno);
+		ForgetLocalBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno++);
+	}
+}
+/*
+ * replay an undo segment discard record
+ */
+static void
+undolog_xlog_discard(XLogReaderState *record)
+{
+	xl_undolog_discard *xlrec = (xl_undolog_discard *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	UndoLogOffset old_segment_begin;
+	UndoLogOffset new_segment_begin;
+	RelFileNode rnode = {0};
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+	if (slot == NULL)
+		elog(ERROR, "unknown undo log %d", xlrec->logno);
+
+	/*
+	 * We're about to discard undologs. In Hot Standby mode, ensure that
+	 * there's no queries running which need to get tuple from discarded undo.
+	 *
+	 * XXX we are passing empty rnode to the conflict function so that it can
+	 * check conflict in all the backend regardless of which database the
+	 * backend is connected.
+	 */
+	if (InHotStandby && TransactionIdIsValid(xlrec->latestxid))
+		ResolveRecoveryConflictWithSnapshot(xlrec->latestxid, rnode);
+
+	/*
+	 * See if we need to unlink or rename any files, but don't consider it an
+	 * error if we find that files are missing.  Since UndoLogDiscard()
+	 * performs filesystem operations before WAL logging or updating shmem
+	 * which could be checkpointed, a crash could have left files already
+	 * deleted, but we could replay WAL that expects the files to be there.
+	 */
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno == xlrec->logno);
+	discard = slot->meta.discard;
+	end = slot->meta.end;
+	LWLockRelease(&slot->mutex);
+
+	/* Drop buffers before we remove/recycle any files. */
+	forget_undo_buffers(xlrec->logno, discard, xlrec->discard,
+						xlrec->entirely_discarded);
+
+	/* Rewind to the start of the segment. */
+	old_segment_begin = discard - discard % UndoLogSegmentSize;
+	new_segment_begin = xlrec->discard - xlrec->discard % UndoLogSegmentSize;
+
+	/* Unlink or rename segments that are no longer in range. */
+	while (old_segment_begin < new_segment_begin)
+	{
+		char	discard_path[MAXPGPATH];
+
+		/* Tell the checkpointer that the file is going away. */
+		undofile_forget_sync(slot->logno,
+							 old_segment_begin / UndoLogSegmentSize,
+							 slot->meta.tablespace);
+
+		UndoLogSegmentPath(xlrec->logno, old_segment_begin / UndoLogSegmentSize,
+						   slot->meta.tablespace, discard_path);
+
+		/* Can we recycle the oldest segment? */
+		if (end < xlrec->end)
+		{
+			char	recycle_path[MAXPGPATH];
+
+			UndoLogSegmentPath(xlrec->logno, end / UndoLogSegmentSize,
+							   slot->meta.tablespace, recycle_path);
+			if (rename(discard_path, recycle_path) == 0)
+			{
+				elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+					 discard_path, recycle_path);
+				end += UndoLogSegmentSize;
+			}
+			else
+			{
+				elog(LOG, "could not rename \"%s\" to \"%s\": %m",
+					 discard_path, recycle_path);
+			}
+		}
+		else
+		{
+			if (unlink(discard_path) == 0)
+				elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+			else
+				elog(LOG, "could not unlink \"%s\": %m", discard_path);
+		}
+		old_segment_begin += UndoLogSegmentSize;
+	}
+
+	/* Create any further new segments that are needed the slow way. */
+	while (end < xlrec->end)
+	{
+		allocate_empty_undo_segment(xlrec->logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/* Update shmem. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = xlrec->discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (xlrec->entirely_discarded)
+		free_undo_log_slot(slot);
+}
+
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
+void
+undolog_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			undolog_xlog_create(record);
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			undolog_xlog_extend(record);
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			undolog_xlog_discard(record);
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
+		default:
+			elog(PANIC, "undo_redo: unknown op code %u", info);
+	}
+}
+
+/*
+ * For assertions only.
+ */
+bool
+AmAttachedToUndoLogSlot(UndoLogSlot *slot)
+{
+	/*
+	 * In general, we can't access log's members without locking.  But this
+	 * function is intended only for asserting that you are attached, and
+	 * while you're attached the slot can't be recycled, so don't bother
+	 * locking.
+	 */
+	return CurrentSession->attached_undo_slots[slot->meta.persistence] == slot;
+}
+
+/*
+ * For testing use only.  This function is only used by the test_undo module.
+ */
+void
+UndoLogDetachFull(void)
+{
+	int		i;
+
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+		if (CurrentSession->attached_undo_slots[i])
+			detach_current_undo_log(i, true);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 43627ab..8b3a28b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -20,6 +20,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/session.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -519,6 +520,8 @@ BootstrapModeMain(void)
 
 	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
 
+	InitializeSession();
+
 	/* Initialize stuff for bootstrap-file processing */
 	for (i = 0; i < MAXATTR; i++)
 	{
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 78a103c..8134f6a 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1022,6 +1022,10 @@ GRANT SELECT (subdbid, subname, subowner, subenabled, subslotname, subpublicatio
     ON pg_subscription TO public;
 
 
+CREATE VIEW pg_stat_undo_logs AS
+    SELECT *
+    FROM pg_stat_get_undo_logs();
+
 --
 -- We have a few function definitions in here, too.
 -- At some point there might be enough to justify breaking them out into
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 5e43867..f1c03e8 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -55,6 +55,7 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -488,6 +489,20 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 	LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
 
 	/*
+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));
+
+	/*
 	 * Try to remove the physical infrastructure.
 	 */
 	if (!destroy_tablespace_directories(tablespaceoid, false))
@@ -1499,6 +1514,14 @@ tblspc_redo(XLogReaderState *record)
 	{
 		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
 
+		/* This shouldn't be able to fail in recovery. */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		if (!DropUndoLogsInTablespace(xlrec->ts_id))
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("tablespace cannot be dropped because it contains non-empty undo logs")));
+		LWLockRelease(TablespaceCreateLock);
+
 		/*
 		 * If we issued a WAL record for a drop tablespace it implies that
 		 * there were no files in it at all when the DROP was done. That means
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b4f2b28..c742861 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4070,6 +4070,27 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_TWOPHASE_FILE_WRITE:
 			event_name = "TwophaseFileWrite";
 			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_READ:
+			event_name = "UndoCheckpointRead";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_WRITE:
+			event_name = "UndoCheckpointWrite";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
+			event_name = "UndoCheckpointSync";
+			break;
+		case WAIT_EVENT_UNDO_FILE_READ:
+			event_name = "UndoFileRead";
+			break;
+		case WAIT_EVENT_UNDO_FILE_WRITE:
+			event_name = "UndoFileWrite";
+			break;
+		case WAIT_EVENT_UNDO_FILE_FLUSH:
+			event_name = "UndoFileFlush";
+			break;
+		case WAIT_EVENT_UNDO_FILE_SYNC:
+			event_name = "UndoFileSync";
+			break;
 		case WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ:
 			event_name = "WALSenderTimelineHistoryRead";
 			break;
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index c2978a9..a188939 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1380,7 +1380,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	char	   *page;
 	size_t		pad;
 	PageHeader	phdr;
-	int			segmentno = 0;
+	BlockNumber	first_blkno = 0;
 	char	   *segmentpath;
 	bool		verify_checksum = false;
 
@@ -1418,12 +1418,18 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 			segmentpath = strstr(filename, ".");
 			if (segmentpath != NULL)
 			{
-				segmentno = atoi(segmentpath + 1);
-				if (segmentno == 0)
+				char	   *end;
+				if (strstr(readfilename, "undo"))
+					first_blkno = strtol(segmentpath + 1, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath + 1, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 					ereport(ERROR,
-							(errmsg("invalid segment number %d in file \"%s\"",
-									segmentno, filename)));
+							(errmsg("invalid segment number in file \"%s\"",
+									filename)));
 			}
+			else
+				first_blkno = 0;
 		}
 	}
 
@@ -1463,7 +1469,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + first_blkno);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 3e96d2a..f2ed561 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -154,6 +154,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_COMMIT_TS_ID:
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
+		case RM_UNDOLOG_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 8046334..d679279 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -177,6 +177,7 @@ static PrivateRefCountEntry *NewPrivateRefCountEntry(Buffer buffer);
 static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move);
 static inline int32 GetPrivateRefCount(Buffer buffer);
 static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref);
+static void InvalidateBuffer(BufferDesc *buf);
 
 /*
  * Ensure that the PrivateRefCountArray has sufficient space to store one more
@@ -620,10 +621,12 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
  * valid, the page is zeroed instead of throwing an error. This is intended
  * for non-critical data, where the caller is prepared to repair errors.
  *
- * In RBM_ZERO_AND_LOCK mode, if the page isn't in buffer cache already, it's
+ * In RBM_ZERO mode, if the page isn't in buffer cache already, it's
  * filled with zeros instead of reading it from disk.  Useful when the caller
  * is going to fill the page from scratch, since this saves I/O and avoids
  * unnecessary failure if the page-on-disk has corrupt page headers.
+ *
+ * In RBM_ZERO_AND_LOCK mode, the page is zeroed and also locked.
  * The page is returned locked to ensure that the caller has a chance to
  * initialize the page before it's made visible to others.
  * Caution: do not use this mode to read a page that is beyond the relation's
@@ -674,24 +677,20 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 /*
  * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
  *		a relcache entry for the relation.
- *
- * NB: At present, this function may only be used on permanent relations, which
- * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary or unlogged relations, we could pass additional
- * parameters.
  */
 Buffer
 ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
-						  BufferAccessStrategy strategy)
+						  BufferAccessStrategy strategy,
+						  char relpersistence)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
-
-	Assert(InRecovery);
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+	return ReadBuffer_common(smgr, relpersistence, forkNum, blockNum,
 							 mode, strategy, &hit);
 }
 
@@ -885,7 +884,9 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * Read in the page, unless the caller intends to overwrite it and
 		 * just wants us to allocate a buffer.
 		 */
-		if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
+		if (mode == RBM_ZERO ||
+			mode == RBM_ZERO_AND_LOCK ||
+			mode == RBM_ZERO_AND_CLEANUP_LOCK)
 			MemSet((char *) bufBlock, 0, BLCKSZ);
 		else
 		{
@@ -1341,6 +1342,62 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 }
 
 /*
+ * ForgetBuffer -- drop a buffer from shared buffers
+ *
+ * If the buffer isn't present in shared buffers, nothing happens.  If it is
+ * present, it is discarded without making any attempt to write it back out to
+ * the operating system.  The caller must therefore somehow be sure that the
+ * data won't be needed for anything now or in the future.  It assumes that
+ * there is no concurrent access to the block, except that it might be being
+ * concurrently written.
+ */
+void
+ForgetBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+			 BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
+	BufferTag	tag;			/* identity of target block */
+	uint32		hash;			/* hash value for tag */
+	LWLock	   *partitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgrid, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	hash = BufTableHashCode(&tag);
+	partitionLock = BufMappingPartitionLock(hash);
+
+	/* see if the block is in the buffer pool */
+	LWLockAcquire(partitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&tag, hash);
+	LWLockRelease(partitionLock);
+
+	/* didn't find it, so nothing to do */
+	if (buf_id < 0)
+		return;
+
+	/* take the buffer header lock */
+	bufHdr = GetBufferDescriptor(buf_id);
+	buf_state = LockBufHdr(bufHdr);
+
+	/*
+	 * The buffer might been evicted after we released the partition lock and
+	 * before we acquired the buffer header lock.  If so, the buffer we've
+	 * locked might contain some other data which we shouldn't touch. If the
+	 * buffer hasn't been recycled, we proceed to invalidate it.
+	 */
+	if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+		bufHdr->tag.blockNum == blockNum &&
+		bufHdr->tag.forkNum == forkNum)
+		InvalidateBuffer(bufHdr);		/* releases spinlock */
+	else
+		UnlockBufHdr(bufHdr, buf_state);
+}
+
+/*
  * InvalidateBuffer -- mark a shared buffer invalid and return it to the
  * freelist.
  *
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 896285a..4183e8d 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -275,6 +275,50 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 }
 
 /*
+ * ForgetLocalBuffer - drop a buffer from local buffers
+ *
+ * This is similar to bufmgr.c's ForgetBuffer, except that we do not need
+ * to do any locking since this is all local.  As with that function, this
+ * must be used very carefully, since we'll cheerfully throw away dirty
+ * buffers without any attempt to write them.
+ */
+void
+ForgetLocalBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+				  BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, BackendIdForTempRelations());
+	BufferTag	tag;					/* identity of target block */
+	LocalBufferLookupEnt *hresult;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/*
+	 * If somehow this is the first request in the session, there's nothing to
+	 * do.  (This probably shouldn't happen, though.)
+	 */
+	if (LocalBufHash == NULL)
+		return;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgrid, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* see if the block is in the local buffer pool */
+	hresult = (LocalBufferLookupEnt *)
+	hash_search(LocalBufHash, (void *) &tag, HASH_REMOVE, NULL);
+
+	/* didn't find it, so nothing to do */
+	if (!hresult)
+		return;
+
+	/* mark buffer invalid */
+	bufHdr = GetLocalBufferDescriptor(hresult->id);
+	CLEAR_BUFFERTAG(bufHdr->tag);
+	buf_state = pg_atomic_read_u32(&bufHdr->state);
+	buf_state &= ~(BM_VALID | BM_TAG_VALID | BM_DIRTY);
+	pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
+}
+
+/*
  * MarkLocalBufferDirty -
  *	  mark a local buffer dirty
  */
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 73c455e..25e36b8 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -322,7 +322,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
 static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
 static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
 
-static int	fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
 static int	fsync_parent_path(const char *fname, int elevel);
 
 
@@ -3345,7 +3344,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
  *
  * Returns 0 if the operation succeeded, -1 otherwise.
  */
-static int
+int
 fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
 {
 	int			fd;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index d7d7335..12c3249 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/subtrans.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -125,6 +126,7 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, ProcGlobalShmemSize());
 		size = add_size(size, XLOGShmemSize());
 		size = add_size(size, CLOGShmemSize());
+		size = add_size(size, UndoLogShmemSize());
 		size = add_size(size, CommitTsShmemSize());
 		size = add_size(size, SUBTRANSShmemSize());
 		size = add_size(size, TwoPhaseShmemSize());
@@ -213,6 +215,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	 */
 	XLOGShmemInit();
 	CLOGShmemInit();
+	UndoLogShmemInit();
 	CommitTsShmemInit();
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index bc1aa88..5df658d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -522,6 +522,8 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append");
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_HASH_JOIN, "parallel_hash_join");
 	LWLockRegisterTranche(LWTRANCHE_SXACT, "serializable_xact");
+	LWLockRegisterTranche(LWTRANCHE_UNDOLOG, "undo_log");
+	LWLockRegisterTranche(LWTRANCHE_UNDODISCARD, "undo_discard");
 
 	/* Register named tranches. */
 	for (i = 0; i < NamedLWLockTrancheRequests; i++)
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index db47843..4b42a1c 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -49,3 +49,4 @@ MultiXactTruncationLock				41
 OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
+UndoLogLock                                      45
diff --git a/src/backend/storage/smgr/Makefile b/src/backend/storage/smgr/Makefile
index e486b7c..ff2e5e2 100644
--- a/src/backend/storage/smgr/Makefile
+++ b/src/backend/storage/smgr/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/storage/smgr
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = md.o smgr.o
+OBJS = md.o smgr.o undofile.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 4ba07a0..8cd1f7e 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -22,6 +22,7 @@
 #include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/smgr.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
 
@@ -81,6 +82,24 @@ static const f_smgr smgrsw[] = {
 		.smgr_nblocks = mdnblocks,
 		.smgr_truncate = mdtruncate,
 		.smgr_immedsync = mdimmedsync,
+	},
+	/* undo logs */
+	{
+		.smgr_init = undofile_init,
+		.smgr_shutdown = undofile_shutdown,
+		.smgr_open = undofile_open,
+		.smgr_close = undofile_close,
+		.smgr_create = undofile_create,
+		.smgr_exists = undofile_exists,
+		.smgr_unlink = undofile_unlink,
+		.smgr_extend = undofile_extend,
+		.smgr_prefetch = undofile_prefetch,
+		.smgr_read = undofile_read,
+		.smgr_write = undofile_write,
+		.smgr_writeback = undofile_writeback,
+		.smgr_nblocks = undofile_nblocks,
+		.smgr_truncate = undofile_truncate,
+		.smgr_immedsync = undofile_immedsync,
 	}
 };
 
@@ -97,7 +116,6 @@ static dlist_head unowned_relns;
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
 
-
 /*
  *	smgrinit(), smgrshutdown() -- Initialize or shut down storage
  *								  managers.
@@ -177,6 +195,7 @@ smgropen(SmgrId smgrid, RelFileNode rnode, BackendId backend)
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = smgrid;
+		reln->private_data = NULL;
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
new file mode 100644
index 0000000..2aa4952
--- /dev/null
+++ b/src/backend/storage/smgr/undofile.c
@@ -0,0 +1,420 @@
+/*
+ * undofile.h
+ *
+ * PostgreSQL undo file manager.  This module provides SMGR-compatible
+ * interface to the files that back undo logs on the filesystem, so that undo
+ * log data can use the shared buffer pool.  Other aspects of undo log
+ * management are provided by undolog.c, so the SMGR interfaces not directly
+ * concerned with reading, writing and flushing data are unimplemented.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/storage/smgr/undofile.c
+ */
+
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/xlog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgwriter.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "storage/undofile.h"
+#include "utils/memutils.h"
+
+/* Populate a file tag describing an undo segment file. */
+#define INIT_UNDOFILETAG(a,xx_logno,xx_tbspc,xx_segno) \
+( \
+	memset(&(a), 0, sizeof(FileTag)), \
+	(a).handler = SYNC_HANDLER_UNDO, \
+	(a).rnode.spcNode = (xx_tbspc), \
+	(a).rnode.relNode = (xx_logno), \
+	(a).segno = (xx_segno) \
+)
+
+/*
+ * While md.c expects random access and has a small number of huge
+ * segments, undofile.c manages a potentially very large number of smaller
+ * segments and has a less random access pattern.  Therefore, instead of
+ * keeping a potentially huge array of vfds we'll just keep the most
+ * recently accessed N.
+ *
+ * For now, N == 1, so we just need to hold onto one 'File' handle.
+ */
+typedef struct UndoFileState
+{
+	int		mru_segno;
+	File	mru_file;
+} UndoFileState;
+
+static MemoryContext UndoFileCxt;
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok);
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno);
+
+void
+undofile_init(void)
+{
+	UndoFileCxt = AllocSetContextCreate(TopMemoryContext,
+										"UndoFileSmgr",
+										ALLOCSET_DEFAULT_SIZES);
+}
+
+void
+undofile_shutdown(void)
+{
+}
+
+void
+undofile_open(SMgrRelation reln)
+{
+	UndoFileState *state;
+
+	state = MemoryContextAllocZero(UndoFileCxt, sizeof(UndoFileState));
+	reln->private_data = state;
+}
+
+void
+undofile_close(SMgrRelation reln, ForkNumber forknum)
+{
+}
+
+void
+undofile_create(SMgrRelation reln, ForkNumber forknum, bool isRedo)
+{
+	/*
+	 * File creation is managed by undolog.c, but xlogutils.c likes to call
+	 * this just in case.  Ignore.
+	 */
+}
+
+bool
+undofile_exists(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_exists is not supported");
+
+	return false;		/* not reached */
+}
+
+void
+undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo)
+{
+	elog(ERROR, "undofile_unlink is not supported");
+}
+
+void
+undofile_extend(SMgrRelation reln, ForkNumber forknum,
+				BlockNumber blocknum, char *buffer,
+				bool skipFsync)
+{
+	elog(ERROR, "undofile_extend is not supported");
+}
+
+void
+undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
+{
+	elog(ERROR, "undofile_prefetch is not supported");
+}
+
+void
+undofile_read(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+			  char *buffer)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileRead(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_READ);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+}
+
+void
+undofile_write(SMgrRelation reln, ForkNumber forknum,
+			   BlockNumber blocknum, char *buffer,
+			   bool skipFsync)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileWrite(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_WRITE);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		/*
+		 * short write: unexpected, because this should be overwriting an
+		 * entirely pre-allocated segment file
+		 */
+		ereport(ERROR,
+				(errcode(ERRCODE_DISK_FULL),
+				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+
+	/* Tell checkpointer this file is dirty. */
+	if (!skipFsync && !SmgrIsTemp(reln))
+	{
+		undofile_request_sync(reln->smgr_rnode.node.relNode,
+							  blocknum / UNDOSEG_SIZE,
+							  reln->smgr_rnode.node.spcNode);
+	}
+}
+
+void
+undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+				   BlockNumber blocknum, BlockNumber nblocks)
+{
+	while (nblocks > 0)
+	{
+		File	file;
+		int		nflush;
+
+		file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+
+		/* compute number of desired writes within the current segment */
+		nflush = Min(nblocks,
+					 1 + UNDOSEG_SIZE - (blocknum % UNDOSEG_SIZE));
+
+		FileWriteback(file,
+					  (blocknum % UNDOSEG_SIZE) * BLCKSZ,
+					  nflush * BLCKSZ, WAIT_EVENT_UNDO_FILE_FLUSH);
+
+		nblocks -= nflush;
+		blocknum += nflush;
+	}
+}
+
+BlockNumber
+undofile_nblocks(SMgrRelation reln, ForkNumber forknum)
+{
+	/*
+	 * xlogutils.c likes to call this to decide whether to read or extend; for
+	 * now we lie and say the relation is big as possible.
+	 */
+	return UndoLogMaxSize / BLCKSZ;
+}
+
+void
+undofile_truncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
+{
+	elog(ERROR, "undofile_truncate is not supported");
+}
+
+void
+undofile_immedsync(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_immedsync is not supported");
+}
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok)
+{
+	File		file;
+	char		path[MAXPGPATH];
+
+	UndoLogSegmentPath(relNode, segno, spcNode, path);
+	file = PathNameOpenFile(path, O_RDWR | PG_BINARY);
+
+	if (file <= 0 && (!missing_ok || errno != ENOENT))
+		elog(ERROR, "cannot open undo segment file '%s': %m", path);
+
+	return file;
+}
+
+/*
+ * Get a File for a particular segment of a SMgrRelation representing an undo
+ * log.
+ */
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno)
+{
+	UndoFileState *state = (UndoFileState *) reln->private_data;
+
+	/* If we have a file open already, check if we need to close it. */
+	if (state->mru_file > 0 && state->mru_segno != segno)
+	{
+		/* These are not the blocks we're looking for. */
+		FileClose(state->mru_file);
+		state->mru_file = 0;
+	}
+
+	/* Check if we need to open a new file. */
+	if (state->mru_file <= 0)
+	{
+		state->mru_file =
+			undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+									   reln->smgr_rnode.node.spcNode,
+									   segno, InRecovery);
+		if (InRecovery && state->mru_file <= 0)
+		{
+			/*
+			 * If in recovery, we may be trying to access a file that will
+			 * later be unlinked.  Tolerate missing files, creating a new
+			 * zero-filled file as required.
+			 */
+			UndoLogNewSegment(reln->smgr_rnode.node.relNode,
+							  reln->smgr_rnode.node.spcNode,
+							  segno);
+			state->mru_file =
+				undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+										   reln->smgr_rnode.node.spcNode,
+										   segno, false);
+			Assert(state->mru_file > 0);
+		}
+		state->mru_segno = segno;
+	}
+
+	return state->mru_file;
+}
+
+/*
+ * Callback to handle a queued sync request.
+ */
+int
+undofile_syncfiletag(const FileTag *tag, char *path)
+{
+	SMgrRelation reln = smgropen(SMGR_UNDO, tag->rnode, InvalidBackendId);
+	File		file;
+
+	if (tag->rnode.relNode == (Oid) InvalidUndoLogNumber)
+	{
+		/* Sync parent directory for this tablespace. */
+		UndoLogDirectory(tag->rnode.spcNode, path);
+
+		/* The caller (sync.c) will do appropriate error reporting. */
+		return fsync_fname_ext(path, true, false, WARNING);
+	}
+	else
+	{
+		/* Sync a segment file. */
+		UndoLogSegmentPath(tag->rnode.relNode, tag->segno, tag->rnode.spcNode,
+						   path);
+
+		file = undofile_get_segment_file(reln, tag->segno);
+		if (file <= 0)
+		{
+			/* errno set by undofile_get_segment_file() */
+			return -1;
+		}
+
+		return FileSync(file, WAIT_EVENT_UNDO_FILE_SYNC);
+	}
+}
+
+/*
+ * Filtering callback used by SYNC_FILTER_REQUEST to forget some requests.
+ */
+bool
+undofile_filetagmatches(const FileTag *tag, const FileTag *candidate)
+{
+	/*
+	 * We use SYNC_FILTER_REQUEST to forget requests for a given tablespace,
+	 * before removing all undo files in the tablespace.
+	 */
+	return tag->rnode.spcNode == candidate->rnode.spcNode;
+}
+
+/*
+ * Tell the checkpointer to sync a segment file.
+ */
+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync file \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about any sync requests for a given segment
+ * file, because it's about to go away.
+ */
+void
+undofile_forget_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Send, and keep retrying if out of space. */
+	(void) RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
+}
+
+/*
+ * Tell the checkpointer to fsync the undo directory in a given tablespace,
+ * because we have created or renamed files inside it.
+ */
+void
+undofile_request_sync_dir(Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	/* We use a special logno and segno to mean "the directory". */
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync directory \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about all sync requests for a given
+ * tablespace, because it's about to go away.
+ */
+void
+undofile_forget_sync_tablespace(Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/*
+	 * Tell checkpointer to forget about any request for this tag, and keep
+	 * waiting if there is not enough space.
+	 */
+	(void) RegisterSyncRequest(&tag, SYNC_FILTER_REQUEST, true);
+}
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index 705f229..02f8e31 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -28,6 +28,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/inval.h"
@@ -96,6 +97,11 @@ static const SyncOps syncsw[] = {
 		.sync_syncfiletag = mdsyncfiletag,
 		.sync_unlinkfiletag = mdunlinkfiletag,
 		.sync_filetagmatches = mdfiletagmatches
+	},
+	/* undo log segment files */
+	{
+		.sync_syncfiletag = undofile_syncfiletag,
+		.sync_filetagmatches = undofile_filetagmatches
 	}
 };
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index e9f72b5..d88113f 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -25,6 +25,7 @@
 #include "access/session.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -561,6 +562,7 @@ BaseInit(void)
 	InitSync();
 	smgrinit();
 	InitBufferPoolAccess();
+	UndoLogInit();
 }
 
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1208eb9..b92645f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -121,6 +121,7 @@ extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
+extern char *undo_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
 
@@ -3669,6 +3670,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"undo_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the tablespace(s) to use for undo logs."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&undo_tablespaces,
+		"",
+		check_undo_tablespaces, assign_undo_tablespaces, NULL
+	},
+
+	{
 		{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
 			gettext_noop("Sets the path for dynamically loadable modules."),
 			gettext_noop("If a dynamically loadable module needs to be opened and "
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ad5cd41..40f7671 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -210,11 +210,13 @@ static const char *const subdirs[] = {
 	"pg_snapshots",
 	"pg_subtrans",
 	"pg_twophase",
+	"pg_undo",
 	"pg_multixact",
 	"pg_multixact/members",
 	"pg_multixact/offsets",
 	"base",
 	"base/1",
+	"base/undo",
 	"pg_replslot",
 	"pg_tblspc",
 	"pg_stat",
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index b8e7c32..825b11c 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -167,7 +167,7 @@ skipfile(const char *fn)
 }
 
 static void
-scan_file(const char *fn, BlockNumber segmentno)
+scan_file(const char *fn, BlockNumber first_blkno)
 {
 	PGAlignedBlock buf;
 	PageHeader	header = (PageHeader) buf.data;
@@ -208,7 +208,7 @@ scan_file(const char *fn, BlockNumber segmentno)
 		if (PageIsNew(header))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + first_blkno);
 		current_size += r;
 		if (mode == PG_MODE_CHECK)
 		{
@@ -310,7 +310,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			char		fnonly[MAXPGPATH];
 			char	   *forkpath,
 					   *segmentpath;
-			BlockNumber segmentno = 0;
+			BlockNumber first_blkno;
 
 			if (skipfile(de->d_name))
 				continue;
@@ -325,15 +325,22 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			segmentpath = strchr(fnonly, '.');
 			if (segmentpath != NULL)
 			{
+				char	   *end;
+
 				*segmentpath++ = '\0';
-				segmentno = atoi(segmentpath);
-				if (segmentno == 0)
+				if (strstr(segmentpath, "undo"))
+					first_blkno = strtol(segmentpath, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 				{
-					pg_log_error("invalid segment number %d in file name \"%s\"",
-								 segmentno, fn);
+					pg_log_error("invalid segment number in file name \"%s\"",
+								 fn);
 					exit(1);
 				}
 			}
+			else
+				first_blkno = 0;
 
 			forkpath = strchr(fnonly, '_');
 			if (forkpath != NULL)
@@ -350,7 +357,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			 * the items in the data folder.
 			 */
 			if (!sizeonly)
-				scan_file(fn, segmentno);
+				scan_file(fn, first_blkno);
 		}
 #ifndef WIN32
 		else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 2734f87..1580380 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -80,6 +80,7 @@ static bool ReadControlFile(void);
 static void GuessControlValues(void);
 static void PrintControlValues(bool guessed);
 static void PrintNewControlValues(void);
+static void AdjustRedoLocation(const char *DataDir);
 static void RewriteControlFile(void);
 static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
@@ -510,6 +511,7 @@ main(int argc, char *argv[])
 	/*
 	 * Else, do the dirty deed.
 	 */
+	AdjustRedoLocation(DataDir);
 	RewriteControlFile();
 	KillExistingXLOG();
 	KillExistingArchiveStatus();
@@ -899,6 +901,82 @@ PrintNewControlValues(void)
 
 
 /*
+ * Compute the new redo, and move the pg_undo file to match if necessary.
+ * Rather than renaming it, we'll create a new copy, so that a failure that
+ * occurs before the controlfile is rewritten won't be fatal.
+ */
+static void
+AdjustRedoLocation(const char *DataDir)
+{
+	uint64		old_redo = ControlFile.checkPointCopy.redo;
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	int			old_fd;
+	int			new_fd;
+	ssize_t		nread;
+	ssize_t		nwritten;
+	char		buffer[1024];
+
+	/*
+	 * Adjust fields as needed to force an empty XLOG starting at
+	 * newXlogSegNo.
+	 */
+	XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
+							ControlFile.checkPointCopy.redo);
+
+	/* If the redo location didn't move, we don't need to do anything. */
+	if (old_redo == ControlFile.checkPointCopy.redo)
+		return;
+
+	/*
+	 * Otherwise we copy the pg_undo file, because its name must match the redo
+	 * location.
+	 */
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 DataDir,
+			 old_redo);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 DataDir,
+			 ControlFile.checkPointCopy.redo);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	new_fd = open(new_pg_undo_path, O_RDWR | O_CREAT, 0644);
+	if (new_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	while ((nread = read(old_fd, buffer, sizeof(buffer))) > 0)
+	{
+		do
+		{
+			nwritten = write(new_fd, buffer, nread);
+			if (nwritten < 0)
+			{
+				pg_log_error("could not write to \"%s\": %m", new_pg_undo_path);
+				exit(1);
+			}
+			nread -= nwritten;
+		} while (nread > 0);
+	}
+	if (nread < 0)
+	{
+		pg_log_error("could not read from \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	close(old_fd);
+	close(new_fd);
+}
+
+/*
  * Write out the new pg_control file.
  */
 static void
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 2cb829a..82c8033 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -9,7 +9,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = check.o controldata.o dump.o exec.o file.o function.o info.o \
        option.o parallel.o pg_upgrade.o relfilenode.o server.o \
-       tablespace.o util.o version.o $(WIN32RES)
+       tablespace.o undo.o util.o version.o $(WIN32RES)
 
 override CPPFLAGS := -DDLSUFFIX=\"$(DLSUFFIX)\" -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 617270f..02e94a0 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -21,6 +21,7 @@ static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb);
 static bool equivalent_locale(int category, const char *loca, const char *locb);
 static void check_is_install_user(ClusterInfo *cluster);
 static void check_proper_datallowconn(ClusterInfo *cluster);
+static void check_for_undo_data(ClusterInfo *cluster);
 static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
@@ -97,6 +98,7 @@ check_and_dump_old_cluster(bool live_check)
 	 */
 	check_is_install_user(&old_cluster);
 	check_proper_datallowconn(&old_cluster);
+	check_for_undo_data(&old_cluster);
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
@@ -171,6 +173,7 @@ check_new_cluster(void)
 
 	check_is_install_user(&new_cluster);
 
+	check_for_undo_data(&new_cluster);
 	check_for_prepared_transactions(&new_cluster);
 }
 
@@ -801,6 +804,45 @@ check_for_prepared_transactions(ClusterInfo *cluster)
 
 
 /*
+ *	check_for_live_undo_data()
+ *
+ *	Make sure there are no live undo records (aborted transactions that have
+ *	not been rolled back, or committed transactions whose undo data has not
+ *	yet been discarded).
+ */
+static void
+check_for_undo_data(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1200)
+		return;
+
+	prep_status("Checking for undo data");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_stat_undo_logs "
+							"WHERE discard != insert");
+
+	if (PQntuples(res) != 0)
+	{
+		if (cluster == &old_cluster)
+			pg_fatal("The source cluster contains live undo data\n");
+		else
+			pg_fatal("The target cluster contains live undo data\n");
+	}
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
+
+
+/*
  *	check_for_isn_and_int8_passing_mismatch()
  *
  *	contrib/isn relies on data type int8, and in 8.4 int8 can now be passed
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 3823641..523e86e 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -59,6 +59,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	bool		got_redo_location = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -485,6 +486,23 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "Latest checkpoint's REDO location:")) != NULL)
+		{
+			uint32		hi;
+			uint32		lo;
+
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			p++;				/* remove ':' char */
+
+			if (sscanf(p, "%X/%X", &hi, &lo) != 2)
+				pg_fatal("%d: controldata cannot parse REDO location\n", __LINE__);
+			cluster->controldata.redo_location = (((uint64) hi) << 32) | lo;
+			got_redo_location = true;
+		}
 	}
 
 	pclose(output);
@@ -528,6 +546,13 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		}
 	}
 
+	/*
+	 * If we used pg_resetwal instead of pg_controldata, there is no REDO
+	 * location.
+	 */
+	if (!got_redo_location)
+		cluster->controldata.redo_location = 0;
+
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi ||
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 043373f..90de62e 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -351,6 +351,10 @@ check_data_dir(ClusterInfo *cluster)
 		check_single_dir(pg_data, "pg_clog");
 	else
 		check_single_dir(pg_data, "pg_xact");
+
+	/* pg_undo is new in v13 */
+	if (GET_MAJOR_VERSION(cluster->major_version) >= 1200)
+		check_single_dir(pg_data, "pg_undo");
 }
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index ff78929..a51da36 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -139,6 +139,8 @@ main(int argc, char **argv)
 
 	/* New now using xids of the old system */
 
+	merge_undo_logs();
+
 	/* -- NEW -- */
 	start_postmaster(&new_cluster, true);
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 5d31750..d0d93c0 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -227,6 +227,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	uint64		redo_location;
 } ControlData;
 
 /*
@@ -465,3 +466,7 @@ void		parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr
 										  char *old_pgdata, char *new_pgdata,
 										  char *old_tablespace);
 bool		reap_child(bool wait_for_child);
+
+/* undo.c */
+
+void		merge_undo_logs(void);
diff --git a/src/bin/pg_upgrade/undo.c b/src/bin/pg_upgrade/undo.c
new file mode 100644
index 0000000..6efc99b
--- /dev/null
+++ b/src/bin/pg_upgrade/undo.c
@@ -0,0 +1,292 @@
+/*
+ *	undo.c
+ *
+ *	Support for upgrading undo logs.\
+ *	Copyright (c) 2019, PostgreSQL Global Development Group
+ *	src/bin/pg_upgrade/undo.c
+ */
+
+
+#include "postgres_fe.h"
+#include "pg_upgrade.h"
+#include "access/undolog.h"
+
+/*
+ * The relevant parts of UndoLogMetaDataData, in a version-independent format.
+ */
+typedef struct
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogStatus status;
+	UndoPersistence persistence;
+	Oid			tablespace;
+} UndoLogInfo;
+
+/*
+ * Read the header of a pg_undo file and extract basic information.  If the
+ * format of the header changes in later versions, this may need to change
+ * depending on "cluster".
+ */
+static void
+read_pg_undo_header(int fd, ClusterInfo *cluster, UndoLogNumber *low_logno,
+					UndoLogNumber *next_logno, UndoLogNumber *num_logs)
+{
+	pg_crc32c crc;
+
+	/* Read the header, much like StartupUndoLogs(). */
+	if (read(fd, low_logno, sizeof(*low_logno)) != sizeof(*low_logno) ||
+		read(fd, next_logno, sizeof(*next_logno)) != sizeof(*next_logno) ||
+		read(fd, num_logs, sizeof(*num_logs)) != sizeof(*num_logs) ||
+		read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("pg_undo file is corrupted or cannot be read\n");
+}
+
+/*
+ * Read a single UndoLogMetaData object.  If the format changes in later
+ * versions, this may need to change to be able to read different structs
+ * depending on "cluster".
+ */
+static void
+read_one_undo_log(int fd, ClusterInfo *cluster, UndoLogInfo *info)
+{
+	UndoLogMetaData meta_data;
+	int		rc;
+
+	rc = read(fd, &meta_data, sizeof(meta_data));
+	if (rc < 0)
+		pg_fatal("could not read undo log meta-data: %m");
+	else if (rc != sizeof(meta_data))
+		pg_fatal("could not read undo log meta-data: expect %zu bytes but read only %d bytes",
+				 sizeof(meta_data), rc);
+
+	info->logno = meta_data.logno;
+	info->persistence = meta_data.persistence;
+	info->tablespace = meta_data.tablespace;
+	info->discard = meta_data.discard;
+	info->status = meta_data.status;
+}
+
+static void
+merge_undo_log(UndoLogInfo *logs, UndoLogNumber *num_logs,
+			   const UndoLogInfo *info)
+{
+	UndoLogNumber i;
+
+	/* Do we already have an entry for this logno? */
+	for (i = 0; i < *num_logs; ++i)
+	{
+		if (logs[i].logno == info->logno)
+		{
+			/*
+			 * Take the highest discard offset, so that any pointers that
+			 * originated in either cluster appear to be discarded.
+			 */
+			if (logs[i].discard < info->discard)
+				logs[i].discard = info->discard;
+
+			/*
+			 * Take the highest status so that entirely discarded logs trump
+			 * active logs.
+			 */
+			StaticAssertStmt(UNDO_LOG_STATUS_ACTIVE < UNDO_LOG_STATUS_FULL,
+							 "undo log status out of order");
+			StaticAssertStmt(UNDO_LOG_STATUS_FULL < UNDO_LOG_STATUS_DISCARDED,
+							 "undo log status out of order");
+			if (logs[i].status < info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the most persistent persistence level.  While we could
+			 * just convert them all to permanent and it wouldn't hurt, it's
+			 * probably a better idea to keep about the same number of each
+			 * persistence level, so that a system that has stabilized with
+			 * those numbers will continue to be stable after the upgrade (ie
+			 * not suddenly need to create more undo logs of different
+			 * levels).  The most permanent is the best choice, because TEMP
+			 * undo logs might be rewound in future.
+			 */
+			StaticAssertStmt(UNDO_PERMANENT < UNDO_UNLOGGED,
+							 "undo log persistent out of order");
+			StaticAssertStmt(UNDO_UNLOGGED < UNDO_TEMP,
+							 "undo log persistent out of order");
+			if (logs[i].status > info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the highest tablespace OID.  The choice of 'highest' is
+			 * arbitrary (we don't really expect the new cluster to have more
+			 * than one log), but it seems useful to preserve the distribution
+			 * of tablespaces from the old cluster for stability, as above.
+			 */
+			if (logs[i].tablespace < info->tablespace)
+				logs[i].tablespace = info->tablespace;
+			break;
+		}
+	}
+
+	/* Otherwise create a new entry. */
+	logs[*num_logs++] = *info;
+}
+
+/*
+ * We need to merge the old undo logs and the new undo logs.  We know that
+ * there is no live undo data (see check_for_live_undo_data()), but we need to
+ * make sure that any undo record pointers that exist in the old OR new
+ * cluster appear as discarded.  That is, any log numbers that are entirely
+ * discard in either cluster appear as entirely discarded, and and we retain
+ * the higher of the discard pointers in any log that is active.  This is
+ * mostly a theoretical concern for now, but perhaps a future release will be
+ * able to create higher undo record pointers during initdb than the old
+ * cluster had, so let's use an algorithm that doesn't make any assumptions
+ * about that.
+ */
+void
+merge_undo_logs(void)
+{
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	UndoLogInfo *logs;
+	UndoLogNumber num_logs;
+	UndoLogNumber num_old_logs;
+	UndoLogNumber old_low_logno;
+	UndoLogNumber old_next_logno;
+	UndoLogNumber num_new_logs;
+	UndoLogNumber new_low_logno;
+	UndoLogNumber new_next_logno;
+	UndoLogNumber i;
+	int			old_fd;
+	int			new_fd;
+	pg_crc32c	crc;
+
+	/* If the old cluster has no undo logs, there is nothing to do */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1200)
+		return;
+
+	/*
+	 * Open the pg_undo files corresponding to the old and new redo locations.
+	 * First, we'll reload pg_controldata output, so that we have up-to-date
+	 * redo locations.
+	 */
+	get_control_data(&old_cluster, true);
+	get_control_data(&new_cluster, true);
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 old_cluster.pgdata,
+			 old_cluster.controldata.redo_location);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 new_cluster.pgdata,
+			 new_cluster.controldata.redo_location);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", old_pg_undo_path);
+	new_fd = open(new_pg_undo_path, O_RDWR, 0);
+	if (new_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", new_pg_undo_path);
+
+	/* Read the headers */
+	read_pg_undo_header(old_fd, &old_cluster, &old_low_logno, &old_next_logno, &num_old_logs);
+	read_pg_undo_header(new_fd, &new_cluster, &new_low_logno, &new_next_logno, &num_new_logs);
+
+	/* Allocate workspace that is sure to be enough for the merged set */
+	logs = malloc(sizeof(*logs) * (num_old_logs + num_new_logs));
+	if (logs == NULL)
+	{
+		pg_fatal("out of memory\n");
+		exit(1);
+	}
+	num_logs = 0;
+
+	/*
+	 * Anything below the "low" logno has been entirely discarded, so we'll
+	 * take the higher of the two values.  Likewise, the "next" log number to
+	 * allocate should be the higher of the two.
+	 */
+	new_low_logno = Max(old_low_logno, new_low_logno);
+	new_next_logno = Max(old_next_logno, new_next_logno);
+
+	/* Merge in the old logs */
+	while (num_old_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(old_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_old_logs;
+	}
+
+	/* Merge in the new logs */
+	while (num_new_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(new_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_new_logs;
+	}
+
+	close(old_fd);
+
+	/* Now write out the new file, much like CheckPointUndoLogs() */
+	if (ftruncate(new_fd, 0) < 0)
+		pg_fatal("could not truncate file \"%s\": %m", new_pg_undo_path);
+	if (lseek(new_fd, SEEK_SET, 0) < 0)
+		pg_fatal("could not seek to start of file \"%s\": %m", new_pg_undo_path);
+
+	/* Compute header checksum */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &new_low_logno, sizeof(new_low_logno));
+	COMP_CRC32C(crc, &new_next_logno, sizeof(new_next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the header */
+	if ((write(new_fd, &new_low_logno, sizeof(new_low_logno)) != sizeof(new_low_logno)) ||
+		(write(new_fd, &new_next_logno, sizeof(new_next_logno)) != sizeof(new_next_logno)) ||
+		(write(new_fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(new_fd, &crc, sizeof(crc)) != sizeof(crc)))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	/* Write out the undo logs */
+	INIT_CRC32C(crc);
+	for (i = 0; i < num_logs; ++i)
+	{
+		UndoLogMetaData	meta_data;
+		UndoLogInfo	*info = &logs[i];
+		UndoLogOffset end;
+
+		memset(&meta_data, 0, sizeof(meta_data));
+		meta_data.logno = info->logno;
+
+		/*
+		 * Round the discard offset up so that it points to the first byte in
+		 * a segment, and assign that to all three offsets.  That means there
+		 * is no logical data, and there are no physical files.
+		 */
+		end = ((info->discard + UndoLogSegmentSize - 1) / UndoLogSegmentSize)
+			* UndoLogSegmentSize;
+		meta_data.unlogged.insert = meta_data.discard = meta_data.end = end;
+
+		/*
+		 * We have whatever was the highest status (though it probably
+		 * wouldn't hurt if we set them all to ACTIVE).
+		 */
+		meta_data.status = info->status;
+
+
+		if (write(new_fd, &meta_data, sizeof(meta_data)) != sizeof(meta_data))
+			pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+		COMP_CRC32C(crc, &meta_data, sizeof(meta_data));
+	}
+	FIN_CRC32C(crc);
+
+	if (write(new_fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	close(new_fd);
+	free(logs);
+}
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca..938150d 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 3c0db2c..6945e3e 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
diff --git a/src/include/access/session.h b/src/include/access/session.h
index 8fba568..650b181 100644
--- a/src/include/access/session.h
+++ b/src/include/access/session.h
@@ -17,6 +17,9 @@
 /* Avoid including typcache.h */
 struct SharedRecordTypmodRegistry;
 
+/* Avoid including undolog.h */
+struct UndoLogSlot;
+
 /*
  * A struct encapsulating some elements of a user's session.  For now this
  * manages state that applies to parallel query, but it principle it could
@@ -27,6 +30,10 @@ typedef struct Session
 	dsm_segment *segment;		/* The session-scoped DSM segment. */
 	dsa_area   *area;			/* The session-scoped DSA area. */
 
+	/* State managed by undolog.c. */
+	struct UndoLogSlot *attached_undo_slots[3];
+	bool		need_to_choose_undo_tablespace;
+
 	/* State managed by typcache.c. */
 	struct SharedRecordTypmodRegistry *shared_typmod_registry;
 	dshash_table *shared_record_table;
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
new file mode 100644
index 0000000..f8f6f44
--- /dev/null
+++ b/src/include/access/undolog.h
@@ -0,0 +1,455 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.h
+ *
+ * PostgreSQL undo log manager.  This module is responsible for lifecycle
+ * management of undo logs and backing files, associating undo logs with
+ * backends, allocating and managing space within undo logs.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_H
+#define UNDOLOG_H
+
+#include "access/xlogreader.h"
+#include "catalog/pg_class.h"
+#include "common/relpath.h"
+#include "storage/bufpage.h"
+
+#ifndef FRONTEND
+#include "storage/lwlock.h"
+#endif
+
+/* The type used to identify an undo log and position within it. */
+typedef uint64 UndoRecPtr;
+
+/* The type used for undo record lengths. */
+typedef uint16 UndoRecordSize;
+
+/* Undo log statuses. */
+typedef enum
+{
+	UNDO_LOG_STATUS_UNUSED = 0,
+	UNDO_LOG_STATUS_ACTIVE,
+	UNDO_LOG_STATUS_FULL,
+	UNDO_LOG_STATUS_DISCARDED
+} UndoLogStatus;
+
+/*
+ * Undo log persistence levels.  These have a one-to-one correspondence with
+ * relpersistence values, but are small integers so that we can use them as an
+ * index into the "logs" and "lognos" arrays.
+ */
+typedef enum
+{
+	UNDO_PERMANENT = 0,
+	UNDO_UNLOGGED = 1,
+	UNDO_TEMP = 2
+} UndoPersistence;
+
+#define UndoPersistenceLevels 3
+
+/*
+ * Convert from relpersistence ('p', 'u', 't') to an UndoPersistence
+ * enumerator.
+ */
+#define UndoPersistenceForRelPersistence(rp)						\
+	((rp) == RELPERSISTENCE_PERMANENT ? UNDO_PERMANENT :			\
+	 (rp) == RELPERSISTENCE_UNLOGGED ? UNDO_UNLOGGED : UNDO_TEMP)
+
+/*
+ * Convert from UndoPersistence to a relpersistence value.
+ */
+#define RelPersistenceForUndoPersistence(up)				\
+	((up) == UNDO_PERMANENT ? RELPERSISTENCE_PERMANENT :	\
+	 (up) == UNDO_UNLOGGED ? RELPERSISTENCE_UNLOGGED :		\
+	 RELPERSISTENCE_TEMP)
+
+/*
+ * Get the appropriate UndoPersistence value from a Relation.
+ */
+#define UndoPersistenceForRelation(rel)									\
+	(UndoPersistenceForRelPersistence((rel)->rd_rel->relpersistence))
+
+/* Type for offsets within undo logs */
+typedef uint64 UndoLogOffset;
+
+/* printf-family format string for UndoRecPtr. */
+#define UndoRecPtrFormat "%016" INT64_MODIFIER "X"
+
+/* printf-family format string for UndoLogOffset. */
+#define UndoLogOffsetFormat UINT64_FORMAT
+
+/* Number of blocks of BLCKSZ in an undo log segment file.  128 = 1MB. */
+#define UNDOSEG_SIZE 128
+
+/* Size of an undo log segment file in bytes. */
+#define UndoLogSegmentSize ((size_t) BLCKSZ * UNDOSEG_SIZE)
+
+/* The width of an undo log number in bits.  24 allows for 16.7m logs. */
+#define UndoLogNumberBits 24
+
+/* The maximum valid undo log number. */
+#define MaxUndoLogNumber ((1 << UndoLogNumberBits) - 1)
+
+/* The width of an undo log offset in bits.  40 allows for 1TB per log.*/
+#define UndoLogOffsetBits (64 - UndoLogNumberBits)
+
+/* Special value for undo record pointer which indicates that it is invalid. */
+#define	InvalidUndoRecPtr	((UndoRecPtr) 0)
+
+/* End-of-list value when building linked lists of undo logs. */
+#define InvalidUndoLogNumber -1
+
+/*
+ * The maximum amount of data that can be stored in an undo log.  Can be set
+ * artificially low to test full log behavior.
+ */
+#define UndoLogMaxSize ((UndoLogOffset) 1 << UndoLogOffsetBits)
+
+/* Type for numbering undo logs. */
+typedef int UndoLogNumber;
+
+/* Extract the undo log number from an UndoRecPtr. */
+#define UndoRecPtrGetLogNo(urp)					\
+	((urp) >> UndoLogOffsetBits)
+
+/* Extract the offset from an UndoRecPtr. */
+#define UndoRecPtrGetOffset(urp)				\
+	((urp) & ((UINT64CONST(1) << UndoLogOffsetBits) - 1))
+
+/* Make an UndoRecPtr from an log number and offset. */
+#define MakeUndoRecPtr(logno, offset)			\
+	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
+
+/* The number of unusable bytes in the header of each block. */
+#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+
+/* The number of usable bytes we can store per block. */
+#define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
+
+/* The database OID is not used in undo buffer tags. */
+#define UndoLogDatabaseOid InvalidOid
+
+/* Length of undo checkpoint filename */
+#define UNDO_CHECKPOINT_FILENAME_LENGTH	16
+
+/*
+ * UndoRecPtrIsValid
+ *		True iff undoRecPtr is valid.
+ */
+#define UndoRecPtrIsValid(undoRecPtr) \
+	((bool) ((UndoRecPtr) (undoRecPtr) != InvalidUndoRecPtr))
+
+/* Extract the relnode for an undo log. */
+#define UndoRecPtrGetRelNode(urp)				\
+	UndoRecPtrGetLogNo(urp)
+
+/* The only valid fork number for undo log buffers. */
+#define UndoLogForkNum MAIN_FORKNUM
+
+/* Compute the block number that holds a given UndoRecPtr. */
+#define UndoRecPtrGetBlockNum(urp)				\
+	(UndoRecPtrGetOffset(urp) / BLCKSZ)
+
+/* Compute the offset of a given UndoRecPtr in the page that holds it. */
+#define UndoRecPtrGetPageOffset(urp)			\
+	(UndoRecPtrGetOffset(urp) % BLCKSZ)
+
+/* Compare two undo checkpoint files to find the oldest file. */
+#define UndoCheckPointFilenamePrecedes(file1, file2)	\
+	(strcmp(file1, file2) < 0)
+
+/* What is the offset of the i'th non-header byte? */
+#define UndoLogOffsetFromUsableByteNo(i)								\
+	(((i) / UndoLogUsableBytesPerPage) * BLCKSZ +						\
+	 UndoLogBlockHeaderSize +											\
+	 ((i) % UndoLogUsableBytesPerPage))
+
+/* How many non-header bytes are there before a given offset? */
+#define UndoLogOffsetToUsableByteNo(offset)				\
+	(((offset) % BLCKSZ - UndoLogBlockHeaderSize) +		\
+	 ((offset) / BLCKSZ) * UndoLogUsableBytesPerPage)
+
+/* Add 'n' usable bytes to offset stepping over headers to find new offset. */
+#define UndoLogOffsetPlusUsableBytes(offset, n)							\
+	UndoLogOffsetFromUsableByteNo(UndoLogOffsetToUsableByteNo(offset) + (n))
+
+/* Populate a RelFileNode from an UndoRecPtr. */
+#define UndoRecPtrAssignRelFileNode(rfn, urp)								 \
+	do																		 \
+	{																		 \
+		(rfn).spcNode = UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp)); \
+		(rfn).dbNode = UndoLogDatabaseOid;									 \
+		(rfn).relNode = UndoRecPtrGetRelNode(urp);							 \
+	} while (false);
+
+/*
+ * Properties of an undo log that don't have explicit WAL records logging
+ * their changes, to reduce WAL volume.  Instead, they change incrementally
+ * whenever data is inserted as a result of other WAL records.  Since the
+ * values recorded in an online checkpoint may be out of the sync (ie not the
+ * correct values as at the redo LSN), these are backed up in buffer data on
+ * first change after each checkpoint.
+ */
+typedef struct UndoLogUnloggedMetaData
+{
+	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
+	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
+	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
+} UndoLogUnloggedMetaData;
+
+/*
+ * Control metadata for an active undo log.  Lives in shared memory inside an
+ * UndoLogSlot object, but also written to disk during checkpoints.
+ */
+typedef struct UndoLogMetaData
+{
+	/* Members that are not managed by explicit WAL logs. */
+	UndoLogUnloggedMetaData unlogged;
+
+	/* Members that are fixed for the lifetime of the undo log. */
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoPersistence persistence;	/* permanent, unlogged, temp? */
+
+	/* Members that are changed by explicit WAL records. */
+	UndoLogStatus status;
+	UndoLogOffset end;				/* one past end of highest segment */
+	UndoLogOffset discard;			/* oldest data needed (tail) */
+} UndoLogMetaData;
+
+/*
+ * Context used to hold undo log state across all the undo log insertions
+ * corresponding to a single WAL record.
+ */
+typedef struct UndoLogAllocContext
+{
+	UndoPersistence persistence;
+	UndoRecPtr	try_location;
+	XLogReaderState *xlog_record;
+	UndoLogNumber recovery_logno;
+	uint8 		recovery_block_id;
+
+	/*
+	 * The maximum number of undo logs that a single WAL record could insert
+	 * into, modifying its unlogged meta data.  Typically the number is 1, but
+	 * it might touch a couple or more in rare cases where space runs out.
+	 */
+#define MAX_META_DATA_IMAGES 4
+	int			num_meta_data_images;
+	struct
+	{
+		UndoLogNumber logno;
+		UndoLogUnloggedMetaData data;
+	} meta_data_images[MAX_META_DATA_IMAGES];
+} UndoLogAllocContext;
+
+#ifndef FRONTEND
+
+/*
+ * The in-memory control object for an undo log.  We have a fixed-sized array
+ * of these.
+ */
+typedef struct UndoLogSlot
+{
+	/*
+	 * Protected by UndoLogLock and 'mutex'.  Both must be held to steal this
+	 * slot for another undolog.  Either may be held to prevent that from
+	 * happening.
+	 */
+	UndoLogNumber logno;			/* InvalidUndoLogNumber for unused slots */
+
+	/* Protected by UndoLogLock. */
+	UndoLogNumber next_free;		/* link for active unattached undo logs */
+
+	/* Protected by 'mutex'. */
+	LWLock		mutex;
+	UndoLogMetaData meta;			/* current meta-data */
+	pid_t		pid;				/* InvalidPid for unattached */
+
+	/* Protected by 'discard_lock'.  State used by undo workers. */
+	TransactionId	oldest_xid;		/* cache of oldest transaction's xid */
+	uint32		oldest_xidepoch;
+	UndoRecPtr	oldest_data;
+	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
+} UndoLogSlot;
+
+extern UndoLogSlot *UndoLogGetSlot(UndoLogNumber logno, bool missing_ok);
+extern UndoLogSlot *UndoLogNextSlot(UndoLogSlot *slot);
+extern bool AmAttachedToUndoLogSlot(UndoLogSlot *slot);
+extern UndoRecPtr UndoLogGetOldestRecord(UndoLogNumber logno, bool *full);
+
+/*
+ * Each backend maintains a small hash table mapping undo log numbers to
+ * UndoLogSlot objects in shared memory.
+ *
+ * We also cache the tablespace and persistence here, since we need fast
+ * access to those in a couple of places.  We could also reach those via
+ * slot->meta, but they can't be accessed without locking (since the
+ * UndoLogSlot object might be recycled).  Since these properties are constant
+ * for lifetime of the undo log number, there is no cache invalidation problem to
+ * worry about.
+ */
+typedef struct UndoLogTableEntry
+{
+	UndoLogNumber	number;
+	UndoLogSlot	   *slot;
+	Oid				tablespace;
+	UndoPersistence	persistence;
+	char			status;
+} UndoLogTableEntry;
+
+/*
+ * Instantiate fast inline hash table access functions.  We use an identity
+ * hash function for speed, since we already have integers and don't expect
+ * many collisions.
+ */
+#define SH_PREFIX undologtable
+#define SH_ELEMENT_TYPE UndoLogTableEntry
+#define SH_KEY_TYPE UndoLogNumber
+#define SH_KEY number
+#define SH_HASH_KEY(tb, key) (key)
+#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_SCOPE static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+extern PGDLLIMPORT undologtable_hash *undologtable_cache;
+
+/*
+ * Find or create an UndoLogTableGetEntry for this log number.  This is used
+ * only for fast look-ups of tablespace and persistence.
+ */
+static pg_attribute_always_inline UndoLogTableEntry *
+UndoLogGetTableEntry(UndoLogNumber logno)
+{
+	UndoLogTableEntry  *entry;
+
+	/* Fast path. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		return entry;
+
+	/* Slow path: force cache entry to be created. */
+	UndoLogGetSlot(logno, false);
+	entry = undologtable_lookup(undologtable_cache, logno);
+
+	return entry;
+}
+
+/*
+ * Look up the tablespace for an undo log in our cache.
+ */
+static inline Oid
+UndoLogNumberGetTablespace(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->tablespace;
+}
+
+static inline Oid
+UndoRecPtrGetTablespace(UndoRecPtr urp)
+{
+	return UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp));
+}
+
+/*
+ * Look up the persistence level for an undo log in our cache.
+ */
+static inline UndoPersistence
+UndoLogNumberGetPersistence(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->persistence;
+}
+
+static inline UndoPersistence
+UndoRecPtrGetPersistence(UndoRecPtr urp)
+{
+	return UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(urp));
+}
+
+#endif
+
+/* Space management. */
+extern void UndoLogBeginInsert(UndoLogAllocContext *context,
+							   UndoPersistence persistence,
+							   XLogReaderState *xlog_record);
+extern void UndoLogRegister(UndoLogAllocContext *context,
+							uint8 block_id,
+							UndoLogNumber logno);
+extern UndoRecPtr UndoLogAllocate(UndoLogAllocContext *context,
+								  uint16 size,
+								  bool *need_xact_header,
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
+extern UndoRecPtr UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+											TransactionId xid,
+											uint16 size,
+											bool *need_xact_header,
+											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp);
+extern void UndoLogAdvance(UndoLogAllocContext *context, size_t size);
+extern void UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size);
+extern bool UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
+extern bool UndoLogIsDiscarded(UndoRecPtr point);
+
+/* Initialization interfaces. */
+extern void StartupUndoLogs(XLogRecPtr checkPointRedo);
+extern void UndoLogShmemInit(void);
+extern Size UndoLogShmemSize(void);
+extern void UndoLogInit(void);
+extern void UndoLogDirectory(Oid tablespace, char *path);
+extern void UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace,
+							   char *path);
+extern void ResetUndoLogs(UndoPersistence persistence);
+
+/* Interface use by tablespace.c. */
+extern bool DropUndoLogsInTablespace(Oid tablespace);
+
+/* GUC interfaces. */
+extern void assign_undo_tablespaces(const char *newval, void *extra);
+
+/* Checkpointing interfaces. */
+extern void CheckPointUndoLogs(XLogRecPtr checkPointRedo,
+							   XLogRecPtr priorCheckPointRedo);
+
+/* File sync request management. */
+
+
+extern UndoRecPtr UndoLogGetLastXactStartPoint(UndoLogNumber logno);
+extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno,
+										  TransactionId xid);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
+extern void UndoLogSetLSN(XLogRecPtr lsn);
+void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
+/* Redo interface. */
+extern void undolog_redo(XLogReaderState *record);
+/* Discard the undo logs for temp tables */
+extern void TempUndoDiscard(UndoLogNumber);
+
+/* Test-only interfacing. */
+extern void UndoLogDetachFull(void);
+
+#endif
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
new file mode 100644
index 0000000..7b83d27
--- /dev/null
+++ b/src/include/access/undolog_xlog.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog_xlog.h
+ *	  undo log access XLOG definitions.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_XLOG_H
+#define UNDOLOG_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+
+/* XLOG records */
+#define XLOG_UNDOLOG_CREATE		0x00
+#define XLOG_UNDOLOG_EXTEND		0x10
+#define XLOG_UNDOLOG_DISCARD	0x20
+#define XLOG_UNDOLOG_SWITCH		0x30
+
+/* Create a new undo log. */
+typedef struct xl_undolog_create
+{
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoPersistence persistence;
+} xl_undolog_create;
+
+/* Extend an undo log by adding a new segment. */
+typedef struct xl_undolog_extend
+{
+	UndoLogNumber logno;
+	UndoLogOffset end;
+} xl_undolog_extend;
+
+/* Discard space, and possibly destroy or recycle undo log segments. */
+typedef struct xl_undolog_discard
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	TransactionId latestxid;	/* latest xid whose undolog are discarded. */
+	bool		  entirely_discarded;
+} xl_undolog_discard;
+
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
+extern void undolog_desc(StringInfo buf,XLogReaderState *record);
+extern const char *undolog_identify(uint8 info);
+
+#endif
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 366b8d3..05b2239 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -46,6 +46,22 @@ extern Buffer XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode,
 									 ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
+extern bool XLogFindBlockId(XLogReaderState *record,
+							SmgrId smgrid,
+							RelFileNode rnode,
+							ForkNumber forknum,
+							BlockNumber blockno,
+							uint8 *block_id);
+
+extern XLogRedoAction XLogReadBufferForRedoBlock(XLogReaderState *record,
+												 SmgrId smgrid,
+												 RelFileNode rnode,
+												 ForkNumber forknum,
+												 BlockNumber blockno,
+												 ReadBufferMode mode,
+												 bool get_cleanup_lock,
+												 Buffer *buf);
+
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 8733524..1841ab4 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10677,4 +10677,11 @@
   proname => 'pg_partition_root', prorettype => 'regclass',
   proargtypes => 'regclass', prosrc => 'pg_partition_root' },
 
+# undo logs
+{ oid => '5032', descr => 'list undo logs',
+  proname => 'pg_stat_get_undo_logs', procost => '1', prorows => '10', proretset => 't',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,text,text,text,text,text,xid,int4,text}', proargmodes => '{o,o,o,o,o,o,o,o,o}',
+  proargnames => '{logno,persistence,tablespace,discard,insert,end,xid,pid,status}', prosrc => 'pg_stat_get_undo_logs' },
+
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0a3ad3a..1936c5d 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -934,6 +934,13 @@ typedef enum
 	WAIT_EVENT_TWOPHASE_FILE_READ,
 	WAIT_EVENT_TWOPHASE_FILE_SYNC,
 	WAIT_EVENT_TWOPHASE_FILE_WRITE,
+	WAIT_EVENT_UNDO_CHECKPOINT_READ,
+	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
+	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_READ,
+	WAIT_EVENT_UNDO_FILE_WRITE,
+	WAIT_EVENT_UNDO_FILE_FLUSH,
+	WAIT_EVENT_UNDO_FILE_SYNC,
 	WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ,
 	WAIT_EVENT_WAL_BOOTSTRAP_SYNC,
 	WAIT_EVENT_WAL_BOOTSTRAP_WRITE,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 256dcd5..060215f 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -38,8 +38,9 @@ typedef enum BufferAccessStrategyType
 typedef enum
 {
 	RBM_NORMAL,					/* Normal read */
-	RBM_ZERO_AND_LOCK,			/* Don't read from disk, caller will
-								 * initialize. Also locks the page. */
+	RBM_ZERO,					/* Don't read from disk, caller will
+								 * initialize. */
+	RBM_ZERO_AND_LOCK,			/* Like RBM_ZERO, but also locks the page. */
 	RBM_ZERO_AND_CLEANUP_LOCK,	/* Like RBM_ZERO_AND_LOCK, but locks the page
 								 * in "cleanup" mode */
 	RBM_ZERO_ON_ERROR,			/* Read, but return an all-zeros page on error */
@@ -171,7 +172,10 @@ extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BufferAccessStrategy strategy);
 extern Buffer ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
 						  ForkNumber forkNum, BlockNumber blockNum,
-						  ReadBufferMode mode, BufferAccessStrategy strategy);
+						  ReadBufferMode mode, BufferAccessStrategy strategy,
+						  char relpersistence);
+extern void ForgetBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+			 BlockNumber blockNum);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -228,6 +232,10 @@ extern void AtProcExit_LocalBuffers(void);
 
 extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation);
 
+/* in localbuf.c */
+extern void ForgetLocalBuffer(SmgrId smgrid, RelFileNode rnode,
+							  ForkNumber forkNum, BlockNumber blockNum);
+
 /* in freelist.c */
 extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
 extern void FreeAccessStrategy(BufferAccessStrategy strategy);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 9959258..2782bca 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -143,6 +143,7 @@ extern int	pg_fsync_writethrough(int fd);
 extern int	pg_fdatasync(int fd);
 extern void pg_flush_data(int fd, off_t offset, off_t amount);
 extern void fsync_fname(const char *fname, bool isdir);
+extern int	fsync_fname_ext(const char *fname, bool isdir, bool perm, int elevel);
 extern int	durable_rename(const char *oldfile, const char *newfile, int loglevel);
 extern int	durable_unlink(const char *fname, int loglevel);
 extern int	durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 08e0dc8..4abb344 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -220,7 +220,10 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_SXACT,
-	LWTRANCHE_FIRST_USER_DEFINED
+	LWTRANCHE_UNDOLOG,
+	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_REWIND,
+	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
 /*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 243efc6..04bdd17 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -70,6 +70,9 @@ typedef struct SMgrRelationData
 	int			md_num_open_segs[MAX_FORKNUM + 1];
 	struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
 
+	/* For use by implementations. */
+	void	   *private_data;
+
 	/* if unowned, list link in list of all unowned SMgrRelations */
 	dlist_node	node;
 } SMgrRelationData;
@@ -83,6 +86,7 @@ typedef enum SmgrId
 {
 	SMGR_INVALID = -1,
 	SMGR_MD = 0,		/* md.c */
+	SMGR_UNDO			/* undofile.c */
 } SmgrId;
 
 extern void smgrinit(void);
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index 16428c5..59f1da9 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -34,7 +34,8 @@ typedef enum SyncRequestType
  */
 typedef enum SyncRequestHandler
 {
-	SYNC_HANDLER_MD = 0			/* md smgr */
+	SYNC_HANDLER_MD = 0,		/* md smgr */
+	SYNC_HANDLER_UNDO = 1		/* undo smgr */
 } SyncRequestHandler;
 
 /*
diff --git a/src/include/storage/undofile.h b/src/include/storage/undofile.h
new file mode 100644
index 0000000..a5ec30f
--- /dev/null
+++ b/src/include/storage/undofile.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * undofile.h
+ *	  undo storage manager public interface declarations.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/undofile.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOFILE_H
+#define UNDOFILE_H
+
+#include "access/undolog.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "storage/smgr.h"
+#include "storage/sync.h"
+
+extern void undofile_init(void);
+extern void undofile_shutdown(void);
+extern void undofile_open(SMgrRelation reln);
+extern void undofile_close(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_create(SMgrRelation reln, ForkNumber forknum,
+							bool isRedo);
+extern bool undofile_exists(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum,
+							bool isRedo);
+extern void undofile_extend(SMgrRelation reln, ForkNumber forknum,
+		 BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_prefetch(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber blocknum);
+extern void undofile_read(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, char *buffer);
+extern void undofile_write(SMgrRelation reln, ForkNumber forknum,
+		BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+			BlockNumber blocknum, BlockNumber nblocks);
+extern BlockNumber undofile_nblocks(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_truncate(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber nblocks);
+extern void undofile_immedsync(SMgrRelation reln, ForkNumber forknum);
+
+/* Callbacks used by sync.c. */
+extern int undofile_syncfiletag(const FileTag *tag, char *path);
+extern bool undofile_filetagmatches(const FileTag *tag, const FileTag *candidate);
+
+/* Management of checkpointer requests. */
+extern void undofile_request_sync(UndoLogNumber logno, BlockNumber segno,
+								  Oid tablespace);
+extern void undofile_forget_sync(UndoLogNumber logno, BlockNumber segno,
+								 Oid tablespace);
+extern void undofile_forget_sync_tablespace(Oid tablespace);
+extern void undofile_request_sync_dir(Oid tablespace);
+
+#endif							/* UNDOFILE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e709177..84639ee 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -431,6 +431,8 @@ extern void GUC_check_errcode(int sqlerrcode);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
+extern bool check_undo_tablespaces(char **newval, void **extra, GucSource source);
+extern void assign_undo_tablespaces(const char *newval, void *extra);
 
 /* in catalog/namespace.c */
 extern bool check_search_path(char **newval, void **extra, GucSource source);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7d365c4..189920b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2010,6 +2010,16 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text));
+pg_stat_undo_logs| SELECT pg_stat_get_undo_logs.logno,
+    pg_stat_get_undo_logs.persistence,
+    pg_stat_get_undo_logs.tablespace,
+    pg_stat_get_undo_logs.discard,
+    pg_stat_get_undo_logs.insert,
+    pg_stat_get_undo_logs."end",
+    pg_stat_get_undo_logs.xid,
+    pg_stat_get_undo_logs.pid,
+    pg_stat_get_undo_logs.status
+   FROM pg_stat_get_undo_logs() pg_stat_get_undo_logs(logno, persistence, tablespace, discard, insert, "end", xid, pid, status);
 pg_stat_user_functions| SELECT p.oid AS funcid,
     n.nspname AS schemaname,
     p.proname AS funcname,
-- 
1.8.3.1

0006-Defect-and-enhancement-in-multi-log-support.patchapplication/octet-stream; name=0006-Defect-and-enhancement-in-multi-log-support.patchDownload
From 880f25a543783f8dc3784a51ab1c29b72f6b5b27 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Fri, 7 Jun 2019 15:03:37 +0530
Subject: [PATCH 06/14] Defect and enhancement in multi-log support

---
 src/backend/access/undo/undolog.c | 18 +++++++++---------
 src/include/access/undolog.h      | 20 ++++++++++----------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 67b08e7..014480f 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -909,11 +909,11 @@ UndoLogAllocateInRecovery(UndoLogAllocContext *context,
 			context->recovery_logno = slot->logno;
 
 			/* Read log switch information from meta and reset it. */
-			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
-			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+			*prevlog_xact_start = slot->meta.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.prevlog_last_urp;
 
-			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
-			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+			slot->meta.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.prevlog_last_urp = InvalidUndoRecPtr;
 
 			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
 		}
@@ -1253,8 +1253,8 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 	Assert(AmAttachedToUndoLogSlot(slot));
 
 	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
-	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
-	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	slot->meta.prevlog_xact_start = prevlog_xact_start;
+	slot->meta.prevlog_last_urp = prevlog_last_urp;
 	LWLockRelease(&slot->mutex);
 
 	/* Wal log the log switch. */
@@ -1262,7 +1262,7 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 		xl_undolog_switch xlrec;
 
 		xlrec.logno = logno;
-		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_xact_start = prevlog_xact_start;
 		xlrec.prevlog_last_urp = prevlog_xact_start;
 
 		XLogBeginInsert();
@@ -2543,8 +2543,8 @@ undolog_xlog_switch(XLogReaderState *record)
 	 * Restore the log switch information in the MyUndoLogState this will be
 	 * reset by following UndoLogAllocateDuringRecovery.
 	 */
-	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
-	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+	slot->meta.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.prevlog_last_urp = xlrec->prevlog_last_urp;
 }
 
 void
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index f8f6f44..994c6a6 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -203,16 +203,6 @@ typedef struct UndoLogUnloggedMetaData
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
-
-	/*
-	 * Below two variable are used during recovery when transaction's undo
-	 * records are split across undo logs.  Replay of switch will restore
-	 * these two undo record pointers which will be reset on next allocation
-	 * during recovery. */
-	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
-									 * in the previous log. */
-	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
-									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -233,6 +223,16 @@ typedef struct UndoLogMetaData
 	UndoLogStatus status;
 	UndoLogOffset end;				/* one past end of highest segment */
 	UndoLogOffset discard;			/* oldest data needed (tail) */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogMetaData;
 
 /*
-- 
1.8.3.1

#49Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#48)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code that throws an error from an undo log
handler and the results were not good. It appears that the code will
retry in a tight loop:

2019-06-18 13:58:53.262 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.264 EDT [42803] ERROR: robert_undo

It seems clear that the error-handling aspect of this patch has not
been given enough thought. It's debatable what strategy should be
used when undo fails, but retrying 40 times per millisecond isn't the
right answer. I assume we want some kind of cool-down between retries.
10 seconds? A minute? Some kind of back-off algorithm that gradually
increases the retry time up to some maximum? Should there be one or
more GUCs?

Another thing that is not very nice is that when I tried to shut down
the server via 'pg_ctl stop' while the above was happening, it did not
shut down. I had to use an immediate shutdown. That's clearly not
OK.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#50Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#49)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code that throws an error from an undo log
handler and the results were not good.

I discovered another bothersome thing here: if you have a single
transaction that generates a bunch of undo records, the first one has
uur_dbid set correctly and the remaining records have uur_dbid set to
0. That means if you try to write a sanity check like if
(record->uur_dbid != MyDatabaseId) elog(ERROR, "the undo system messed
up") it fails.

The original idea of UnpackedUndoRecord was this: you would put a
bunch of data into an UnpackedUndoRecord that you wanted written to
undo, and the undo system would find ways to compress stuff out of the
on-disk representation by e.g. omitting the fork number if it's
MAIN_FORKNUM. Then, when you read an undo record, it would decompress
so that you ended up with the same UnpackedUndoRecord that you had at
the beginning. However, the inclusion of transaction headers has made
this a bit confusing: that stuff isn't being added by the user but by
the undo system itself. It's not very clear from the comments what the
contract is around these things: do you need to set uur_dbid to
MyDatabaseId when preparing to insert an undo record? Or can you just
leave it unset and then it'll possibly be set at decoding time? The
comments for the UnpackedUndoRecord structure don't explain this.

I'd really like to see this draw a cleaner distinction between the
stuff that the user is expected to set and the other stuff we deal
with internally to the undo subsystem. For example, suppose that
UnpackedUndoRecord didn't include any of the fields that are only
present in the transaction header. Maybe there's another structure,
like UndoTransactionHeader, that includes those fields. The client of
the undo subsystem creates a bunch of UnpackedUndoRecords and inserts
them. At undo time, the callback gets back an identical set of
UnpackedUndoRecords. And maybe it also gets a pointer to the
UndoTransactionHeader which contains all of the system-generated
stuff. Under this scheme, uur_xid, uur_xidepoch (which still need to
be combined into uur_fxid), uur_progress, uur_dbid, uur_next,
uur_prevlogstart, and uur_prevurp would all move out of the
UnpackedUndoRecord and into the UndoTransactionHeader. The user would
supply none of those things when inserting undo records, but the
rm_undo callback could examine those values if it wished.

A weaker approach would be to at least clean up the structure
definition so that the transaction-header fields set by the system are
clearly segregated from the per-record fields set by the
undo-inserter, with comments explaining that those fields don't need
to be set but will (or may?) be set at undo time. That would be better
than what we have right now - because it would hopefully make it much
more clear which fields need to be set on insert and which fields can
be expected to be set when decoding - but I think it's probably not
going far enough.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#51Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#49)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 18, 2019 at 11:37 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code that throws an error from an undo log
handler and the results were not good. It appears that the code will
retry in a tight loop:

2019-06-18 13:58:53.262 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo
2019-06-18 13:58:53.263 EDT [42803] ERROR: robert_undo

..

It seems clear that the error-handling aspect of this patch has not
been given enough thought. It's debatable what strategy should be
used when undo fails, but retrying 40 times per millisecond isn't the
right answer.

The reason for the same is that currently, the undo worker keep on
executing the requests if there are any. I think this is good when
there are different requests, but getting the same request from error
queue and doing it, again and again, doesn't seem to be good and I
think it will not help either.

I assume we want some kind of cool-down between retries.
10 seconds? A minute? Some kind of back-off algorithm that gradually
increases the retry time up to some maximum?

Yeah, something on these lines would be good. How about if we add
failure_count with each request in error queue? Now, it will get
incremented on each retry and we can wait in proportion to that, say
10s after the first retry, 20s after second and so on and maximum up
to 10 failure_count (100s) will be allowed after which worker will
exit considering it has no more work to do.

Actually, we also need to think about what we should with such
requests because even if undo worker exits after retrying for some
threshold number of times, undo launcher will again launch a new
worker for this request unless we have some special handling for the
same.

We can issue some WARNING once any particular request reached the
maximum number of retries but not sure if that is enough because the
user might not notice the same or didn't take any action. Do we want
to PANIC at some point of time, if so, when or the other alternative
is we can try at regular intervals till we succeed?

Should there be one or
more GUCs?

Yeah, we can do that, something like undo_apply_error_retry_count, but
I am not completely sure about this, maybe some pre-defined number say
10 or 20 should be enough. However, I am fine if you or others think
that a guc can help users in this case.

Another thing that is not very nice is that when I tried to shut down
the server via 'pg_ctl stop' while the above was happening, it did not
shut down. I had to use an immediate shutdown. That's clearly not
OK.

CHECK_FOR_INTERRUPTS is missing at one place, will fix.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#52Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#50)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 2:40 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code that throws an error from an undo log
handler and the results were not good.

I discovered another bothersome thing here: if you have a single
transaction that generates a bunch of undo records, the first one has
uur_dbid set correctly and the remaining records have uur_dbid set to
0. That means if you try to write a sanity check like if
(record->uur_dbid != MyDatabaseId) elog(ERROR, "the undo system messed
up") it fails.

The original idea of UnpackedUndoRecord was this: you would put a
bunch of data into an UnpackedUndoRecord that you wanted written to
undo, and the undo system would find ways to compress stuff out of the
on-disk representation by e.g. omitting the fork number if it's
MAIN_FORKNUM. Then, when you read an undo record, it would decompress
so that you ended up with the same UnpackedUndoRecord that you had at
the beginning. However, the inclusion of transaction headers has made
this a bit confusing: that stuff isn't being added by the user but by
the undo system itself. It's not very clear from the comments what the
contract is around these things: do you need to set uur_dbid to
MyDatabaseId when preparing to insert an undo record? Or can you just
leave it unset and then it'll possibly be set at decoding time? The
comments for the UnpackedUndoRecord structure don't explain this.

I'd really like to see this draw a cleaner distinction between the
stuff that the user is expected to set and the other stuff we deal
with internally to the undo subsystem. For example, suppose that
UnpackedUndoRecord didn't include any of the fields that are only
present in the transaction header. Maybe there's another structure,
like UndoTransactionHeader, that includes those fields. The client of
the undo subsystem creates a bunch of UnpackedUndoRecords and inserts
them. At undo time, the callback gets back an identical set of
UnpackedUndoRecords. And maybe it also gets a pointer to the
UndoTransactionHeader which contains all of the system-generated
stuff. Under this scheme, uur_xid, uur_xidepoch (which still need to
be combined into uur_fxid), uur_progress, uur_dbid, uur_next,
uur_prevlogstart, and uur_prevurp would all move out of the
UnpackedUndoRecord and into the UndoTransactionHeader. The user would
supply none of those things when inserting undo records, but the
rm_undo callback could examine those values if it wished.

A weaker approach would be to at least clean up the structure
definition so that the transaction-header fields set by the system are
clearly segregated from the per-record fields set by the
undo-inserter, with comments explaining that those fields don't need
to be set but will (or may?) be set at undo time. That would be better
than what we have right now - because it would hopefully make it much
more clear which fields need to be set on insert and which fields can
be expected to be set when decoding - but I think it's probably not
going far enough.

I think it's a fair point. We can keep pointer to
UndoRecordTransaction(urec_progress, dbid, uur_next) and
UndoRecordLogSwitch(urec_prevurp, urec_prevlogstart) in
UnpackedUndoRecord and include them whenever undo record contain these
headers. Transaction header in the first record of the transaction
and log-switch header in the first record after undo-log switch during
a transaction. IMHO uur_fxid, we can keep as part of the main
UnpackedUndoRecord, because as part of the other work "Compression
for undo records to consider rmgrid, xid,cid,reloid for each record",
the FullTransactionId, will be present in every UnpackedUndoRecord
(although it will not be stored in every undo record).

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#53Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#51)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 2:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

The reason for the same is that currently, the undo worker keep on
executing the requests if there are any. I think this is good when
there are different requests, but getting the same request from error
queue and doing it, again and again, doesn't seem to be good and I
think it will not help either.

Even if there are multiple requests involved, you don't want a tight
loop like this.

I assume we want some kind of cool-down between retries.
10 seconds? A minute? Some kind of back-off algorithm that gradually
increases the retry time up to some maximum?

Yeah, something on these lines would be good. How about if we add
failure_count with each request in error queue? Now, it will get
incremented on each retry and we can wait in proportion to that, say
10s after the first retry, 20s after second and so on and maximum up
to 10 failure_count (100s) will be allowed after which worker will
exit considering it has no more work to do.

Actually, we also need to think about what we should with such
requests because even if undo worker exits after retrying for some
threshold number of times, undo launcher will again launch a new
worker for this request unless we have some special handling for the
same.

We can issue some WARNING once any particular request reached the
maximum number of retries but not sure if that is enough because the
user might not notice the same or didn't take any action. Do we want
to PANIC at some point of time, if so, when or the other alternative
is we can try at regular intervals till we succeed?

PANIC is a terrible idea. How would that fix anything? You'll very
possibly still have the same problem after restarting, and so you'll
just keep on hitting the PANIC. That will mean that in addition to
whatever problem with undo you already had, you now have a system that
you can't use for anything at all, because it keeps restarting.

The design goal here should be that if undo for a transaction fails,
we keep retrying periodically, but with minimal adverse impact on the
rest of the system. That means you can't retry in a loop. It also
means that the system needs to provide fairness: that is, it shouldn't
be possible to create a system where one or more transactions for
which undo keeps failing cause other transactions that could have been
undone to get starved.

It seems to me that thinking of this in terms of what the undo worker
does and what the undo launcher does is probably not the right
approach. We need to think of it more as an integrated system. Instead
of storing a failure_count with each request in the error queue, how
about storing a next retry time? I think the error queue needs to be
ordered by database_id, then by next_retry_time, and then by order of
insertion. (The last part is important because next_retry_time is
going to be prone to having ties, and we need to break those ties in
the right way.) So, when a per-database worker starts up, it's pulling
from the queues in alternation, ignoring items that are not for the
current database. When it pulls from the error queue, it looks at the
item for the current database that has the lowest retry time - if
that's still in the future, then it ignores the queue until something
new (perhaps with a lower retry_time) is added, or until the first
next_retry_time arrives. If the item that it pulls again fails, it
gets inserted back into the error queue but with a higher next retry
time.

This might not be exactly right, but the point is that there should
probably be NO logic that causes a worker to retry the same
transaction immediately afterward, with or without a delay. It should
be all be driven off what gets pulled out of the error queue. In the
above sketch, if a worker gets to the point where there's nothing in
the error queue for the current database with a timestamp that is <=
the current time, then it can't pull anything else from that queue; if
there's no other work to do, it exits. If there is other work to do,
it does that and then maybe enough time will have passed to allow
something to be pulled from the error queue, or maybe not. Meanwhile
some other worker running in the same database might pull the item
before the original worker gets back to it. Meanwhile if the worker
exits because there's nothing more to do in that database, the
launcher can also see the error queue. When enough time has passed,
it can notice that there is an item (or items) that could be pulled
from the error queue for that database and launch a worker for that
database if necessary (or else let an existing worker take care of
it).

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#54Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#49)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code [ to use these patches ].

I spent some more time experimenting with this patch set today and I
think that the UndoFetchRecord interface is far too zheap-centric. I
expected that I would be able to do this:

UnpackedUndoRecord *uur = UndoFetchRecord(urp);
// do stuff with uur
UndoRecordRelease(uur);

But I can't, because the UndoFetchRecord API requires me to pass not
only an undo record but also a block number, an offset number, an XID,
and a callback. I think I could get the effect that I want by
defining a callback that always returns true. Then I could do:

UndoRecPtr junk;
UnpackedUndoRecord *uur = UndoFetchRecord(urp, InvalidBlockNumber,
InvalidOffsetNumber, &junk, always_returns_true);
// do stuff with uur
UndoRecordRelease(uur);

That seems ridiculously baroque. I think the most common thing that
an AM will want to do with an UndoRecPtr is look up that exact record;
that is, for example, what zedstore will want to do. However, even if
some AMs, like zheap, want to search backward through a chain of
records, there's no real reason to suppose that all of them will want
to search by block number + offset. They might want to search by some
bit of data buried in the payload, for example.

I think the basic question here is whether we really need anything
more complicated than:

extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp);

I mean, if you had that, the caller can implement looping easily
enough, and insert any test they want:

for (;;)
{
UnpackedUndoRecord *uur = UndoFetchRecord(urp);
if (i like this one)
break;
urp = uur->uur_blkprev; // should be renamed, since zedstore +
probably others will have tuple chains not block chains
UndoRecordRelease(uur);
}

The question in my mind is whether there's some performance advantage
of having the undo layer manage the looping rather than the caller do
it. If there is, then there's a lot of zheap code that ought to be
changed to use it, because it's just using the same satisfies-callback
everywhere. If there's not, we should just simplify the undo record
lookup along the lines mentioned above and put all the looping into
the callers. zheap could provide a wrapper around UndoFetchRecord
that does a search by block and offset, so that we don't have to
repeat that logic in multiple places.

BTW, an actually generic iterator interface would probably look more like this:

typedef bool (*SatisfyUndoRecordCallback)(void *callback_data,
UnpackedUndoRecord *uur);
extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp, UndoRecPtr
*found, SatisfyUndoRecordCallback callback, void *callback_data);

Now we're not assuming anything about what parts of the record the
callback wants to examine. It can do whatever it likes. Typically
with this sort of interface a caller will define a file-private struct
that is known to both the callback and the caller of UndoFetchRecord,
but not elsewhere.

If we decide we need an iterator within the undo machinery itself,
then I think it should look like the above, and I think it should
accept NULL for found, callback, and callback_data, so that somebody
who wants to just look up a record, full stop, can do just:

UnpackedUndoRecord *uur = UndoFetchRecord(urp, NULL, NULL, NULL);

which seems tolerable.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#55Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#52)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 9:13 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I think it's a fair point. We can keep pointer to
UndoRecordTransaction(urec_progress, dbid, uur_next) and
UndoRecordLogSwitch(urec_prevurp, urec_prevlogstart) in
UnpackedUndoRecord and include them whenever undo record contain these
headers. Transaction header in the first record of the transaction
and log-switch header in the first record after undo-log switch during
a transaction. IMHO uur_fxid, we can keep as part of the main
UnpackedUndoRecord, because as part of the other work "Compression
for undo records to consider rmgrid, xid,cid,reloid for each record",
the FullTransactionId, will be present in every UnpackedUndoRecord
(although it will not be stored in every undo record).

I agree that fxid needs to be set all the time.

I'm not sure I'm entirely following the rest of what you are saying
here, but let me say again that I don't think UnpackedUndoRecord
should include a bunch of stuff that callers (1) don't need to set
when inserting and (2) can't count on having set when fetching. Stuff
of that type should be handled in some way that spares clients of the
undo system from having to worry about it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#56Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#53)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 8:25 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Jun 19, 2019 at 2:45 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

The reason for the same is that currently, the undo worker keep on
executing the requests if there are any. I think this is good when
there are different requests, but getting the same request from error
queue and doing it, again and again, doesn't seem to be good and I
think it will not help either.

Even if there are multiple requests involved, you don't want a tight
loop like this.

Okay, one reason that comes to mind is we don't want to choke the
system as applying undo can consume CPU and generate a lot of I/O. Is
that you have in mind or something else?

I see an advantage in having some sort of throttling here, so we can
have some wait time (say 100ms) between processing requests. Do we
see any need of guc here?

I think on one side it seems a good idea to have multiple guc's for
tuning undo worker machinery because whatever default values we pick
might not be good for some of the users. OTOH, giving too many guc's
can also make the system difficult to understand and can confuse users
or at the least, they won't know how exactly to use those. It seems
to me that we should first complete the entire patch and then we can
decide which all things need separate guc.

I assume we want some kind of cool-down between retries.
10 seconds? A minute? Some kind of back-off algorithm that gradually
increases the retry time up to some maximum?

Yeah, something on these lines would be good. How about if we add
failure_count with each request in error queue? Now, it will get
incremented on each retry and we can wait in proportion to that, say
10s after the first retry, 20s after second and so on and maximum up
to 10 failure_count (100s) will be allowed after which worker will
exit considering it has no more work to do.

Actually, we also need to think about what we should with such
requests because even if undo worker exits after retrying for some
threshold number of times, undo launcher will again launch a new
worker for this request unless we have some special handling for the
same.

We can issue some WARNING once any particular request reached the
maximum number of retries but not sure if that is enough because the
user might not notice the same or didn't take any action. Do we want
to PANIC at some point of time, if so, when or the other alternative
is we can try at regular intervals till we succeed?

PANIC is a terrible idea. How would that fix anything? You'll very
possibly still have the same problem after restarting, and so you'll
just keep on hitting the PANIC. That will mean that in addition to
whatever problem with undo you already had, you now have a system that
you can't use for anything at all, because it keeps restarting.

The design goal here should be that if undo for a transaction fails,
we keep retrying periodically, but with minimal adverse impact on the
rest of the system. That means you can't retry in a loop. It also
means that the system needs to provide fairness: that is, it shouldn't
be possible to create a system where one or more transactions for
which undo keeps failing cause other transactions that could have been
undone to get starved.

Agreed.

It seems to me that thinking of this in terms of what the undo worker
does and what the undo launcher does is probably not the right
approach. We need to think of it more as an integrated system. Instead
of storing a failure_count with each request in the error queue, how
about storing a next retry time?

I think both failure_count and next_retry_time can work in a similar way.

I think incrementing next retry time in multiples will be a bit
tricky. Say first-time error occurs at X hours. We can say that
next_retry_time will X+10s=Y and error_occured_at will be X. The
second time it again failed, how will we know that we need set
next_retry_time as Y+20s, maybe we can do something like Y-X and then
add 10s to it and add the result to the current time. Now whenever
the worker or launcher finds this request, they can check if the
current_time is greater than or equal to next_retry_time, if so they
can pick that request, otherwise, they check request in next queue.

The failure_count can also work in a somewhat similar fashion.
Basically, we can use error_occurred at and failure_count to compute
the required time. So, if error is occurred at say X hours and
failure count is 3, then we can check if current_time is greater than
X+(3 * 10s), then we will allow the entry to be processed, otherwise,
it will check other queues for work.

I think the error queue needs to be
ordered by database_id, then by next_retry_time, and then by order of
insertion. (The last part is important because next_retry_time is
going to be prone to having ties, and we need to break those ties in
the right way.)

I think it makes sense to order requests by next_retry_time,
error_occurred_at (this will ensure the order of insertion). However,
I am not sure if there is a need to club the requests w.r.t database
id. It can starve the error requests from other databases. Moreover,
we already have a functionality wherein if the undo worker doesn't
encounter the next request from the same database on which it is
operating for a certain amount of time, then it will peek ahead (few
entries) in each queue to get the request for the same database. We
don't sort by db_id in other queues as well, so it will be consistent
for this queue if we just sort by next_retry_time and
error_occurred_at.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#57Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#54)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 11:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code [ to use these patches ].

I spent some more time experimenting with this patch set today and I
think that the UndoFetchRecord interface is far too zheap-centric. I
expected that I would be able to do this:

UnpackedUndoRecord *uur = UndoFetchRecord(urp);
// do stuff with uur
UndoRecordRelease(uur);

But I can't, because the UndoFetchRecord API requires me to pass not
only an undo record but also a block number, an offset number, an XID,
and a callback. I think I could get the effect that I want by
defining a callback that always returns true. Then I could do:

UndoRecPtr junk;
UnpackedUndoRecord *uur = UndoFetchRecord(urp, InvalidBlockNumber,
InvalidOffsetNumber, &junk, always_returns_true);
// do stuff with uur
UndoRecordRelease(uur);

That seems ridiculously baroque. I think the most common thing that
an AM will want to do with an UndoRecPtr is look up that exact record;
that is, for example, what zedstore will want to do. However, even if
some AMs, like zheap, want to search backward through a chain of
records, there's no real reason to suppose that all of them will want
to search by block number + offset. They might want to search by some
bit of data buried in the payload, for example.

I think the basic question here is whether we really need anything
more complicated than:

extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp);

I mean, if you had that, the caller can implement looping easily
enough, and insert any test they want:

for (;;)
{
UnpackedUndoRecord *uur = UndoFetchRecord(urp);
if (i like this one)
break;
urp = uur->uur_blkprev; // should be renamed, since zedstore +
probably others will have tuple chains not block chains
UndoRecordRelease(uur);
}

The idea behind having the loop inside the undo machinery was that
while traversing the blkprev chain, we can read all the undo records
on the same undo page under one buffer lock.

The question in my mind is whether there's some performance advantage
of having the undo layer manage the looping rather than the caller do
it. If there is, then there's a lot of zheap code that ought to be
changed to use it, because it's just using the same satisfies-callback
everywhere. If there's not, we should just simplify the undo record
lookup along the lines mentioned above and put all the looping into
the callers. zheap could provide a wrapper around UndoFetchRecord
that does a search by block and offset, so that we don't have to
repeat that logic in multiple places.

BTW, an actually generic iterator interface would probably look more like this:

typedef bool (*SatisfyUndoRecordCallback)(void *callback_data,
UnpackedUndoRecord *uur);

Right, it should be this way.

extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp, UndoRecPtr
*found, SatisfyUndoRecordCallback callback, void *callback_data);

Now we're not assuming anything about what parts of the record the
callback wants to examine. It can do whatever it likes. Typically
with this sort of interface a caller will define a file-private struct
that is known to both the callback and the caller of UndoFetchRecord,
but not elsewhere.

If we decide we need an iterator within the undo machinery itself,
then I think it should look like the above, and I think it should
accept NULL for found, callback, and callback_data, so that somebody
who wants to just look up a record, full stop, can do just:

UnpackedUndoRecord *uur = UndoFetchRecord(urp, NULL, NULL, NULL);

which seems tolerable.

I agree with this.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#58Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#57)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 2:24 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Wed, Jun 19, 2019 at 11:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code [ to use these patches ].

I spent some more time experimenting with this patch set today and I
think that the UndoFetchRecord interface is far too zheap-centric. I
expected that I would be able to do this:

UnpackedUndoRecord *uur = UndoFetchRecord(urp);
// do stuff with uur
UndoRecordRelease(uur);

But I can't, because the UndoFetchRecord API requires me to pass not
only an undo record but also a block number, an offset number, an XID,
and a callback. I think I could get the effect that I want by
defining a callback that always returns true. Then I could do:

UndoRecPtr junk;
UnpackedUndoRecord *uur = UndoFetchRecord(urp, InvalidBlockNumber,
InvalidOffsetNumber, &junk, always_returns_true);
// do stuff with uur
UndoRecordRelease(uur);

That seems ridiculously baroque. I think the most common thing that
an AM will want to do with an UndoRecPtr is look up that exact record;
that is, for example, what zedstore will want to do. However, even if
some AMs, like zheap, want to search backward through a chain of
records, there's no real reason to suppose that all of them will want
to search by block number + offset. They might want to search by some
bit of data buried in the payload, for example.

I think the basic question here is whether we really need anything
more complicated than:

extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp);

I mean, if you had that, the caller can implement looping easily
enough, and insert any test they want:

for (;;)
{
UnpackedUndoRecord *uur = UndoFetchRecord(urp);
if (i like this one)
break;
urp = uur->uur_blkprev; // should be renamed, since zedstore +
probably others will have tuple chains not block chains
UndoRecordRelease(uur);
}

The idea behind having the loop inside the undo machinery was that
while traversing the blkprev chain, we can read all the undo records
on the same undo page under one buffer lock.

I think if we want we can hold this buffer and allow it to be released
in UndoRecordRelease. However, this buffer needs to be stored in some
common structure which can be then handed over to UndoRecordRelease.
Another thing is that as of now the API allocates the memory just once
for UnpackedUndoRecord whereas in the new scheme it needs to be
allocated again and again. I think this is a relatively minor thing,
but it might be better if we can avoid palloc again and again.

BTW, while looking at the code of UndoFetchRecord, I see some problem.
There is a coding pattern like
if()
{
}
else
{
LWLockAcquire()
..
..
}

LWLockRelease().

I think this is not correct.

BTW, an actually generic iterator interface would probably look more like this:

typedef bool (*SatisfyUndoRecordCallback)(void *callback_data,
UnpackedUndoRecord *uur);

Right, it should be this way.

extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp, UndoRecPtr
*found, SatisfyUndoRecordCallback callback, void *callback_data);

Now we're not assuming anything about what parts of the record the
callback wants to examine. It can do whatever it likes. Typically
with this sort of interface a caller will define a file-private struct
that is known to both the callback and the caller of UndoFetchRecord,
but not elsewhere.

If we decide we need an iterator within the undo machinery itself,
then I think it should look like the above, and I think it should
accept NULL for found, callback, and callback_data, so that somebody
who wants to just look up a record, full stop, can do just:

UnpackedUndoRecord *uur = UndoFetchRecord(urp, NULL, NULL, NULL);

which seems tolerable.

I agree with this.

+1.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#59Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#54)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 19, 2019 at 11:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 2:07 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 18, 2019 at 7:31 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I tried writing some code [ to use these patches ].

for (;;)
{
UnpackedUndoRecord *uur = UndoFetchRecord(urp);
if (i like this one)
break;
urp = uur->uur_blkprev; // should be renamed, since zedstore +
probably others will have tuple chains not block chains

..

+1 for renaming this variable. How about uur_prev_ver or uur_prevver
or uur_verprev? Any other suggestions?

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#60Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#56)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 2:42 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Okay, one reason that comes to mind is we don't want to choke the
system as applying undo can consume CPU and generate a lot of I/O. Is
that you have in mind or something else?

Yeah, mainly that, but also things like log spam, and even pressure on
the lock table. If we are trying over and over again to take useless
locks, it can affect other things on the system. The main thing,
however, is the CPU and I/O consumption.

I see an advantage in having some sort of throttling here, so we can
have some wait time (say 100ms) between processing requests. Do we
see any need of guc here?

I don't think that is the right approach. As I said in my previous
reply, we need a way of holding off the retry of the same error for a
certain amount of time, probably measured in seconds or tens of
seconds. Introducing a delay before processing every request is an
inferior alternative: if there are a lot of rollbacks, it can cause
the system to lag; and in the case where there's just one rollback
that's failing, it will still be way too much log spam (and probably
CPU time too). Nobody wants 10 failure messages per second in the
log.

It seems to me that thinking of this in terms of what the undo worker
does and what the undo launcher does is probably not the right
approach. We need to think of it more as an integrated system. Instead
of storing a failure_count with each request in the error queue, how
about storing a next retry time?

I think both failure_count and next_retry_time can work in a similar way.

I think incrementing next retry time in multiples will be a bit
tricky. Say first-time error occurs at X hours. We can say that
next_retry_time will X+10s=Y and error_occured_at will be X. The
second time it again failed, how will we know that we need set
next_retry_time as Y+20s, maybe we can do something like Y-X and then
add 10s to it and add the result to the current time. Now whenever
the worker or launcher finds this request, they can check if the
current_time is greater than or equal to next_retry_time, if so they
can pick that request, otherwise, they check request in next queue.

The failure_count can also work in a somewhat similar fashion.
Basically, we can use error_occurred at and failure_count to compute
the required time. So, if error is occurred at say X hours and
failure count is 3, then we can check if current_time is greater than
X+(3 * 10s), then we will allow the entry to be processed, otherwise,
it will check other queues for work.

Meh. Don't get stuck on one particular method of calculating the next
retry time. We want to be able to change that easily if whatever we
try first doesn't work out well. I am not convinced that we need
anything more complex than a fixed retry time, probably controlled by
a GUC (undo_failure_retry_time = 10s?). An escalating time between
retries would be important and advantageous if we expected the sizes
of these queues to grow into the millions, but the current design
seems to be contemplating something more in the tends-of-thousands
range and I am not sure we're going to need it at that level. We
should try simple things first and then see where we need to make it
more complex.

At some basic level, the queue needs to be ordered by increasing retry
time. You can do that with your design, but you have to recompute the
next retry time from the error_occurred_at and failure_count values
every time you examine an entry. It's almost certainly better to
store the next_retry_time explicitly. That way, if for example we
change the logic for computing the next_retry_time to something really
complicated, it doesn't have any effect on the code that keeps the
queue in order -- it just looks at the computed value. If we end up
with something very simple, like error_occurred_at + constant, it may
end up seeming a little silly, but I think that's a price well worth
paying for code maintainability. If we end up with error_occurred_at
+ Min(failure_count * 10, 100) or something of that sort, then we can
also store failure_count in each record, but it will just be part of
the payload, not the sort key, so adding it or removing it won't
affect the code that maintains the queue ordering.

I think the error queue needs to be
ordered by database_id, then by next_retry_time, and then by order of
insertion. (The last part is important because next_retry_time is
going to be prone to having ties, and we need to break those ties in
the right way.)

I think it makes sense to order requests by next_retry_time,
error_occurred_at (this will ensure the order of insertion). However,
I am not sure if there is a need to club the requests w.r.t database
id. It can starve the error requests from other databases. Moreover,
we already have a functionality wherein if the undo worker doesn't
encounter the next request from the same database on which it is
operating for a certain amount of time, then it will peek ahead (few
entries) in each queue to get the request for the same database. We
don't sort by db_id in other queues as well, so it will be consistent
for this queue if we just sort by next_retry_time and
error_occurred_at.

You're misunderstanding my point. We certainly do not wish to always
pick the request from the database with the lowest OID, or anything
like that. However, we do need a worker for a particular database to
find the work pending for that database efficiently. Peeking ahead a
few requests is a version of that, but I'm not sure it's going to be
good enough. Suppose we look ahead 3 requests but there are 10
databases. Then, if all 10 databases have requests pending, it is
likely that we won't find the next request for our particular database
even though it exists -- the first 3 may easily be for all other
databases. If you look ahead more requests, that doesn't really fix
it - it just means you need more databases for the problem to become
likely. And note that this problem happens even if every database
contains a worker. Some of those workers will erroneously think that
they should exit.

I'm not sure exactly what to do about this. My first thought was that
for all of the queues we might need to have a queue per database (or
something equivalent) rather than just one big queue. But that has
problems too: it will mean that a database worker will never exit as
long as there is any work at all to be done in that database, even if
some other database is getting starved. Somehow we need to balance the
efficiency of having a worker for a particular database process many
requests before exiting against the need to ensure fairness across
databases, and it doesn't sound to me like we quite know what exactly
we ought to do there.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#61Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#59)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 6:48 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

for (;;)
{
UnpackedUndoRecord *uur = UndoFetchRecord(urp);
if (i like this one)
break;
urp = uur->uur_blkprev; // should be renamed, since zedstore +
probably others will have tuple chains not block chains

..

+1 for renaming this variable. How about uur_prev_ver or uur_prevver
or uur_verprev? Any other suggestions?

Maybe just uur_previous or uur_prevundo or something like that. We've
already got a uur_prevurp, but that's really pretty misnamed and IMHO
it doesn't belong in this structure anyway. (uur_next is also a bad
name and also doesn't belong in this structure.)

I don't think we want to use 'ver' because that supposes that undo is
being used to track tuple versions, which is a likely use but perhaps
not the only one.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#62Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#58)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 6:44 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

BTW, while looking at the code of UndoFetchRecord, I see some problem.
There is a coding pattern like
if()
{
}
else
{
LWLockAcquire()
..
..
}

LWLockRelease().

I think this is not correct.

Independently of that problem, I think it's probably bad that we're
not maintaining the same shared memory state on the master and the
standby. Doing the same check in one way on the master and in a
different way on the standby is a recipe for surprising and probably
bad behavior differences between master and standby servers. Those
could be simple things like lock acquire/release not matching, but
they could also be things like performance or correctness differences
that only materialize under certain scenarios.

This is not the only place in the patch set where we have this kind of
thing, and I hate them all. I don't exactly know what the solution
is, either, but I suspect it will involve either having the recovery
process do a more thorough job updating the shared memory state when
it does undo-related stuff, or running some of the undo-specific
processes on the standby just for the purpose of getting these updates
done.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#63Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#60)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 8:01 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Jun 20, 2019 at 2:42 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Okay, one reason that comes to mind is we don't want to choke the
system as applying undo can consume CPU and generate a lot of I/O. Is
that you have in mind or something else?

Yeah, mainly that, but also things like log spam, and even pressure on
the lock table. If we are trying over and over again to take useless
locks, it can affect other things on the system. The main thing,
however, is the CPU and I/O consumption.

I see an advantage in having some sort of throttling here, so we can
have some wait time (say 100ms) between processing requests. Do we
see any need of guc here?

I don't think that is the right approach. As I said in my previous
reply, we need a way of holding off the retry of the same error for a
certain amount of time, probably measured in seconds or tens of
seconds. Introducing a delay before processing every request is an
inferior alternative:

This delay is for *not* choking the system by constantly performing
undo requests that consume a lot of CPU and I/O as discussed in above
point. For holding off the same error request to be re-tried, we need
next_retry_time type of method as discussed below.

if there are a lot of rollbacks, it can cause

the system to lag; and in the case where there's just one rollback
that's failing, it will still be way too much log spam (and probably
CPU time too). Nobody wants 10 failure messages per second in the
log.

It seems to me that thinking of this in terms of what the undo worker
does and what the undo launcher does is probably not the right
approach. We need to think of it more as an integrated system. Instead
of storing a failure_count with each request in the error queue, how
about storing a next retry time?

I think both failure_count and next_retry_time can work in a similar way.

I think incrementing next retry time in multiples will be a bit
tricky. Say first-time error occurs at X hours. We can say that
next_retry_time will X+10s=Y and error_occured_at will be X. The
second time it again failed, how will we know that we need set
next_retry_time as Y+20s, maybe we can do something like Y-X and then
add 10s to it and add the result to the current time. Now whenever
the worker or launcher finds this request, they can check if the
current_time is greater than or equal to next_retry_time, if so they
can pick that request, otherwise, they check request in next queue.

The failure_count can also work in a somewhat similar fashion.
Basically, we can use error_occurred at and failure_count to compute
the required time. So, if error is occurred at say X hours and
failure count is 3, then we can check if current_time is greater than
X+(3 * 10s), then we will allow the entry to be processed, otherwise,
it will check other queues for work.

Meh. Don't get stuck on one particular method of calculating the next
retry time. We want to be able to change that easily if whatever we
try first doesn't work out well. I am not convinced that we need
anything more complex than a fixed retry time, probably controlled by
a GUC (undo_failure_retry_time = 10s?).

IIRC, then you only seem to have suggested that we need a kind of
back-off algorithm that gradually increases the retry time up to some
maximum [1]/messages/by-id/CA+TgmoYHBkm7M8tNk6Z9G_aEOiw3Bjdux7v9+UzmdNTdFmFzjA@mail.gmail.com. I think that is a good way to de-prioritize requests
that are repeatedly failing. Say, there is a request that has already
failed for 5 times and the worker queues it to get executed after 10s.
Immediately after that, another new request has failed for the first
time for the same database and it also got queued to get executed
after 10s. In this scheme the request that has already failed for 5
times will get a chance before the request that has failed for the
first time.

[1]: /messages/by-id/CA+TgmoYHBkm7M8tNk6Z9G_aEOiw3Bjdux7v9+UzmdNTdFmFzjA@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#64Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#63)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 11:35 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

This delay is for *not* choking the system by constantly performing
undo requests that consume a lot of CPU and I/O as discussed in above
point. For holding off the same error request to be re-tried, we need
next_retry_time type of method as discussed below.

Oh. That's not what I thought we were talking about. It's not
unreasonable to think about trying to rate limit undo application just
like we do for vacuum, but a fixed delay between requests would be a
completely inadequate way of attacking that problem. If the
individual requests are short, it will create too much delay, and if
they are long, it will not create enough. We would need delays within
a transaction, not just between transactions, similar to how the
vacuum cost delay stuff works.

I suggest that we leave that to one side for now. It seems like
something that could be added later, maybe in a more general way, and
not something that needs to be or should be closely connected to the
logic for deciding the order in which we're going to process different
transactions in undo.

Meh. Don't get stuck on one particular method of calculating the next
retry time. We want to be able to change that easily if whatever we
try first doesn't work out well. I am not convinced that we need
anything more complex than a fixed retry time, probably controlled by
a GUC (undo_failure_retry_time = 10s?).

IIRC, then you only seem to have suggested that we need a kind of
back-off algorithm that gradually increases the retry time up to some
maximum [1]. I think that is a good way to de-prioritize requests
that are repeatedly failing. Say, there is a request that has already
failed for 5 times and the worker queues it to get executed after 10s.
Immediately after that, another new request has failed for the first
time for the same database and it also got queued to get executed
after 10s. In this scheme the request that has already failed for 5
times will get a chance before the request that has failed for the
first time.

Sure, that's an advantage of increasing back-off times -- you can keep
the stuff that looks hopeless from interfering too much with the stuff
that is more likely to work out. However, I don't think we've actually
done enough testing to know for sure what algorithm will work out
best. Do we want linear back-off (10s, 20s, 30s, ...)? Exponential
back-off (1s, 2s, 4s, 8s, ...)? No back-off (10s, 10s, 10s, 10s)?
Some algorithm that depends on the size of the failed transaction, so
that big things get retried less often? I think it's important to
design the code in such a way that the algorithm can be changed easily
later, because I don't think we can be confident that whatever we pick
for the first attempt will prove to be best. I'm pretty sure that
storing the failure count INSTEAD OF the next retry time is going to
make it harder to experiment with different algorithms later.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#65Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#57)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 4:54 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

The idea behind having the loop inside the undo machinery was that
while traversing the blkprev chain, we can read all the undo records
on the same undo page under one buffer lock.

That's not a bad goal, although invoking a user-supplied callback
while holding a buffer lock is a little scary. If we stick with that,
it had better be clearly documented. Perhaps worth noting:
ReadBuffer() is noticeably more expensive in terms of CPU consumption
than LockBuffer(). So it may work out that we keep a pin to avoid
redoing that and worry less about retaking the buffer locks. But I'm
not sure: avoiding buffer locks is clearly good, too.

I have a couple of observations after poking at this some more. One
is that it's not necessarily adequate to have an interface that
iterates backward through the undo chain until the callback returns
true. Here's an example: suppose you want to walk backward through
the undo chain until you find the first undo record that corresponds
to a change you can see, and then return the undo record immediately
prior to that. zheap doesn't really need this, because it (mostly?)
stores the XID of the next record we're going to look up in the
current record, and the XID of the first record we're going to look up
in the chain -- so it can tell from the record it's found so far
whether it should bother looking up the next record. That, however,
would not necessarily be true for some other AM.

Suppose you just store an undo pointer in the tuple header, as Heikki
proposed to do for zedstore. Suppose further that each record has the
undo pointer for the previous record that modified that TID but not
necessarily the XID. Then imagine a TID where we do an insert and a
bunch of in-place updates. Then, a scan comes along with an old
snapshot. It seems to me that what you need to do is walk backward
through the chain of undo records until you see one that has an XID
that is visible to your snapshot, and then the version of the tuple
that you want is in the payload of the next-newer undo record. So what
you want to do is something like this:

look up the undo pointer in the tuple. call that the current undo record.
loop:
- look up the undo pointer in the current undo record. call that the
previous undo record.
- if the XID from the previous undo record is visible, then stop; use
the tuple version from the current undo record.
- release the current undo record and let the new current undo record
be the previous undo record.

I'm not sure if this is actually a reasonable design from a
performance point of view, but it doesn't sound totally crazy, and
it's just to illustrate the point that there might be cases that are
too complicated for a loop-until-true model. In this kind of loop, at
any given time you are holding onto two undo records, working your way
back through the undo log, and you just can't make this work with the
UndoFetchRecord callback interface. Possibly you could have a context
object that could hold onto one or a few buffer pins:

BeginUndoFetch(&cxt);
uur = UndoFetchRecord(&cxt, urp); // maybe do this a bunch of times
FinishUndoFetch(&cxt);

...but I'm not sure if that's exactly what we want either. Still, if
there is a significant savings from avoiding repinning and relocking
the buffer, we want to make it easy for people to get that advantage
as often as possible.

Another point that has occurred to me is that it is probably
impossible to avoid a fairly large number of duplicate undo fetches.
For instance, suppose somebody runs an UPDATE on a tuple that has been
recently updated. The tuple_update method just gets a TID + snapshot,
so the AM basically has to go look up the tuple all over again,
including checking whether the latest version of the tuple is the one
visible to our snapshot. So that means repinning and relocking the
same buffers and decoding the same undo record all over again. I'm
not exactly sure what to do about this, but it seems like a potential
performance problem. I wonder if it's feasible to cache undo lookups
so that in common cases we can just reuse the result of a previous
lookup instead of doing a new one, and I wonder whether it's possible
to make that fast enough that it actually helps...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#66Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#65)
Re: POC: Cleaning up orphaned files using undo logs

Robert Haas <robertmhaas@gmail.com> writes:

On Thu, Jun 20, 2019 at 4:54 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

The idea behind having the loop inside the undo machinery was that
while traversing the blkprev chain, we can read all the undo records
on the same undo page under one buffer lock.

That's not a bad goal, although invoking a user-supplied callback
while holding a buffer lock is a little scary.

I nominate Robert for Understater of the Year. I think there's pretty
much 0 chance of that working reliably.

regards, tom lane

#67Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#66)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jun 21, 2019 at 6:54 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

That's not a bad goal, although invoking a user-supplied callback
while holding a buffer lock is a little scary.

I nominate Robert for Understater of the Year. I think there's pretty
much 0 chance of that working reliably.

It's an honor to be nominated, although I am pretty sure this is not
my best work in category, even for 2019. There are certainly useful
things that could be done by such a callback without doing anything
that touches shared memory and without doing anything that consumes
more than a handful of CPU cycles, so it doesn't seem utterly crazy to
think that such a design might survive. However, the constraints we'd
have to impose might chafe.

I am more inclined to ditch the callback model altogether in favor of
putting any necessary looping logic on the caller side. That seems a
lot more flexible, and the only trick is figuring out how to keep it
cheap. Providing some kind of context object that can hold onto one or
more pins seems like the most reasonable approach. Last week it seemed
to me that we would need several, but at the moment I can't think of a
reason why we would need more than one. I think we just want to
optimize the case where several undo lookups in quick succession are
actually reading from the same page, and we don't want to go to the
expense of looking that page up multiple times. It doesn't seem at all
likely that we would have a chain of undo records that leaves a
certain page and then comes back to it later, because this is a log
that grows forward, not some kind of random-access thing. So a cache
of size >1 probably wouldn't help.

Unless I'm still confused.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#68Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#64)
14 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jun 20, 2019 at 9:56 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Jun 20, 2019 at 11:35 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

IIRC, then you only seem to have suggested that we need a kind of
back-off algorithm that gradually increases the retry time up to some
maximum [1]. I think that is a good way to de-prioritize requests
that are repeatedly failing. Say, there is a request that has already
failed for 5 times and the worker queues it to get executed after 10s.
Immediately after that, another new request has failed for the first
time for the same database and it also got queued to get executed
after 10s. In this scheme the request that has already failed for 5
times will get a chance before the request that has failed for the
first time.

Sure, that's an advantage of increasing back-off times -- you can keep
the stuff that looks hopeless from interfering too much with the stuff
that is more likely to work out. However, I don't think we've actually
done enough testing to know for sure what algorithm will work out
best. Do we want linear back-off (10s, 20s, 30s, ...)? Exponential
back-off (1s, 2s, 4s, 8s, ...)? No back-off (10s, 10s, 10s, 10s)?
Some algorithm that depends on the size of the failed transaction, so
that big things get retried less often? I think it's important to
design the code in such a way that the algorithm can be changed easily
later, because I don't think we can be confident that whatever we pick
for the first attempt will prove to be best. I'm pretty sure that
storing the failure count INSTEAD OF the next retry time is going to
make it harder to experiment with different algorithms later.

Fair enough. I have implemented it based on next_retry_at and use
constant time 10s for the next retry. I have used define instead of a
GUC as all the other constants for similar things are defined as of
now. One thing to note is that we want the linger time (defined as
UNDO_WORKER_LINGER_MS) for a undo worker to be more than failure retry
time (defined as UNDO_FAILURE_RETRY_DELAY_MS) as, otherwise, the undo
worker can exit before retrying the failed requests. The changes for
this are in patches
0011-Infrastructure-to-register-and-fetch-undo-action-req.patch and
0014-Allow-execution-and-discard-of-undo-by-background-wo.patch.

Apart from these, there are few other changes in the patch series:

0014-Allow-execution-and-discard-of-undo-by-background-wo.patch:
1. Allow the undo workers to respond to cancel command by the user.
CHECK_FOR_INTERRUPTS was missing while the worker was checking for the
next undo request in a loop.
2. Change the value of UNDO_WORKER_LINGER_MS to 20s, so that it is
more than UNDO_FAILURE_RETRY_DELAY_MS.
3. Handled Sigterm signal for undo launcher and workers
4. Fixed the code bug to avoid having CommitTransaction when one of
the workers fails to register. There is no StartTransaction to match
the same. This was leftover from the previous approach.

0012-Infrastructure-to-execute-pending-undo-actions.patch
1 Fix compiler warning

0007-Provide-interfaces-to-store-and-fetch-undo-records.patch
1. Fixed a bug to unlock the buffer while resetting the undo unpacked record
2. Fixed the spurious release of the lock in UndoFetchRecord.
3. Remove the pointer to previous undo in a different log from
UndoRecordTransaction structure. Now, a separate low_switch header
contains the same.

0007-Provide-interfaces-to-store-and-fetch-undo-records.patch is
Dilip's patch and he has modified it, but changes were small so there
was not much sense in posting it separately.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Add-SmgrId-to-smgropen-and-BufferTag.patchapplication/octet-stream; name=0001-Add-SmgrId-to-smgropen-and-BufferTag.patchDownload
From 7224c8d19185aba2833ca1b05d602893c6a22459 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 8 Mar 2019 12:03:00 +1300
Subject: [PATCH 01/14] Add SmgrId to smgropen() and BufferTag.

To use bufmgr.c for new kinds of data in addition to plain old
relations, add an SMGR argument to places that identify blocks
and the files that hold them (smgropen(), block references in
the WAL, BufferTag).

To avoid making BufferTag wider, take some space away from the
fork number for this new member, since there are just a few
values possible.

Add a "smgrid" column to the pg_buffercache extension.

Create a new callback for smgropen() calls so that some md.c-
specific stuff can move out of smgropen(), and future
implementations can also run their own initialization code.

Author: Thomas Munro
Discussion: https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BDE0mmiBZMtZyvwWtgv1sZCniSVhXYsXkvJ_Wo%2B83vvw%40mail.gmail.com
---
 contrib/bloom/blinsert.c                           |  2 +-
 contrib/pg_buffercache/Makefile                    |  4 +-
 contrib/pg_buffercache/pg_buffercache--1.2.sql     | 21 ----------
 .../pg_buffercache/pg_buffercache--1.3--1.4.sql    |  7 ++++
 contrib/pg_buffercache/pg_buffercache--1.4.sql     | 21 ++++++++++
 contrib/pg_buffercache/pg_buffercache.control      |  2 +-
 contrib/pg_buffercache/pg_buffercache_pages.c      | 49 ++++++++++++----------
 doc/src/sgml/pgbuffercache.sgml                    |  7 ++++
 src/backend/access/brin/brin_xlog.c                |  2 +-
 src/backend/access/gin/ginxlog.c                   |  3 +-
 src/backend/access/gist/gistxlog.c                 |  6 +--
 src/backend/access/hash/hash_xlog.c                | 12 +++---
 src/backend/access/hash/hashpage.c                 |  6 ++-
 src/backend/access/heap/heapam.c                   | 20 ++++-----
 src/backend/access/heap/heapam_handler.c           |  2 +-
 src/backend/access/heap/rewriteheap.c              |  6 ++-
 src/backend/access/nbtree/nbtree.c                 |  2 +-
 src/backend/access/nbtree/nbtsort.c                |  3 +-
 src/backend/access/nbtree/nbtxlog.c                |  8 ++--
 src/backend/access/spgist/spginsert.c              |  6 +--
 src/backend/access/spgist/spgxlog.c                | 12 +++---
 src/backend/access/transam/xlog.c                  |  6 ++-
 src/backend/access/transam/xloginsert.c            | 39 ++++++++++-------
 src/backend/access/transam/xlogreader.c            | 11 ++++-
 src/backend/access/transam/xlogutils.c             | 17 ++++----
 src/backend/catalog/storage.c                      | 11 ++---
 src/backend/commands/tablecmds.c                   |  2 +-
 src/backend/replication/logical/decode.c           | 10 ++---
 src/backend/replication/logical/reorderbuffer.c    |  3 +-
 src/backend/storage/buffer/bufmgr.c                | 29 ++++++++-----
 src/backend/storage/buffer/localbuf.c              |  8 ++--
 src/backend/storage/freespace/freespace.c          |  3 +-
 src/backend/storage/freespace/fsmpage.c            |  3 +-
 src/backend/storage/smgr/md.c                      | 29 +++++++++----
 src/backend/storage/smgr/smgr.c                    | 13 +++---
 src/bin/pg_rewind/parsexlog.c                      |  8 +++-
 src/bin/pg_waldump/pg_waldump.c                    | 16 ++++---
 src/include/access/xloginsert.h                    | 13 +++---
 src/include/access/xlogreader.h                    |  6 ++-
 src/include/access/xlogutils.h                     |  4 +-
 src/include/storage/buf_internals.h                | 11 ++++-
 src/include/storage/bufmgr.h                       | 11 ++---
 src/include/storage/md.h                           |  1 +
 src/include/storage/smgr.h                         |  8 +++-
 src/include/utils/rel.h                            |  6 ++-
 45 files changed, 289 insertions(+), 180 deletions(-)
 delete mode 100644 contrib/pg_buffercache/pg_buffercache--1.2.sql
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.4.sql

diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index 4b2186b..e39d21d 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -181,7 +181,7 @@ blbuildempty(Relation index)
 	PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
 			  (char *) metapage, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				BLOOM_METAPAGE_BLKNO, metapage, true);
 
 	/*
diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile
index 18f7a87..d76ac24 100644
--- a/contrib/pg_buffercache/Makefile
+++ b/contrib/pg_buffercache/Makefile
@@ -4,7 +4,9 @@ MODULE_big = pg_buffercache
 OBJS = pg_buffercache_pages.o $(WIN32RES)
 
 EXTENSION = pg_buffercache
-DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
+DATA = \
+	pg_buffercache--1.4.sql \
+	pg_buffercache--1.3--1.4.sql pg_buffercache--1.2--1.3.sql \
 	pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \
 	pg_buffercache--unpackaged--1.0.sql
 PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
diff --git a/contrib/pg_buffercache/pg_buffercache--1.2.sql b/contrib/pg_buffercache/pg_buffercache--1.2.sql
deleted file mode 100644
index 6ee5d84..0000000
--- a/contrib/pg_buffercache/pg_buffercache--1.2.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-/* contrib/pg_buffercache/pg_buffercache--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
-
--- Register the function.
-CREATE FUNCTION pg_buffercache_pages()
-RETURNS SETOF RECORD
-AS 'MODULE_PATHNAME', 'pg_buffercache_pages'
-LANGUAGE C PARALLEL SAFE;
-
--- Create a view for convenient access.
-CREATE VIEW pg_buffercache AS
-	SELECT P.* FROM pg_buffercache_pages() AS P
-	(bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
-	 relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,
-	 pinning_backends int4);
-
--- Don't want these to be available to public.
-REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
-REVOKE ALL ON pg_buffercache FROM PUBLIC;
diff --git a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
new file mode 100644
index 0000000..5277740
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql
@@ -0,0 +1,7 @@
+/* contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.4'" to load this file. \quit
+
+GRANT EXECUTE ON FUNCTION pg_buffercache_pages() TO pg_monitor;
+GRANT SELECT ON pg_buffercache TO pg_monitor;
diff --git a/contrib/pg_buffercache/pg_buffercache--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.4.sql
new file mode 100644
index 0000000..4a36b42
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.4.sql
@@ -0,0 +1,21 @@
+/* contrib/pg_buffercache/pg_buffercache--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
+
+-- Register the function.
+CREATE FUNCTION pg_buffercache_pages()
+RETURNS SETOF RECORD
+AS 'MODULE_PATHNAME', 'pg_buffercache_pages'
+LANGUAGE C PARALLEL SAFE;
+
+-- Create a view for convenient access.
+CREATE VIEW pg_buffercache AS
+	SELECT P.* FROM pg_buffercache_pages() AS P
+	(bufferid integer, smgrid int2, relfilenode oid, reltablespace oid,
+	 reldatabase oid, relforknumber int2, relblocknumber int8, isdirty bool,
+	 usagecount int2, pinning_backends int4);
+
+-- Don't want these to be available to public.
+REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
+REVOKE ALL ON pg_buffercache FROM PUBLIC;
diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control
index 8c060ae..a82ae5f 100644
--- a/contrib/pg_buffercache/pg_buffercache.control
+++ b/contrib/pg_buffercache/pg_buffercache.control
@@ -1,5 +1,5 @@
 # pg_buffercache extension
 comment = 'examine the shared buffer cache'
-default_version = '1.3'
+default_version = '1.4'
 module_pathname = '$libdir/pg_buffercache'
 relocatable = true
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index 1bd579f..052764e 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -15,8 +15,8 @@
 #include "storage/bufmgr.h"
 
 
-#define NUM_BUFFERCACHE_PAGES_MIN_ELEM	8
-#define NUM_BUFFERCACHE_PAGES_ELEM	9
+#define NUM_BUFFERCACHE_PAGES_MIN_ELEM	10
+#define NUM_BUFFERCACHE_PAGES_ELEM	10
 
 PG_MODULE_MAGIC;
 
@@ -25,6 +25,7 @@ PG_MODULE_MAGIC;
  */
 typedef struct
 {
+	SmgrId		smgrid;
 	uint32		bufferid;
 	Oid			relfilenode;
 	Oid			reltablespace;
@@ -102,24 +103,24 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 		tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
 		TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
 						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 2, "smgrid",
+						   INT2OID, -1, 0);
+		TupleDescInitEntry(tupledesc, (AttrNumber) 3, "relfilenode",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reltablespace",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 5, "reldatabase",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relforknumber",
 						   INT2OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 7, "relblocknumber",
 						   INT8OID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 8, "isdirty",
 						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
+		TupleDescInitEntry(tupledesc, (AttrNumber) 9, "usage_count",
 						   INT2OID, -1, 0);
-
-		if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
-			TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
-							   INT4OID, -1, 0);
+		TupleDescInitEntry(tupledesc, (AttrNumber) 10, "pinning_backends",
+						   INT4OID, -1, 0);
 
 		fctx->tupdesc = BlessTupleDesc(tupledesc);
 
@@ -153,6 +154,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 			buf_state = LockBufHdr(bufHdr);
 
 			fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
+			fctx->record[i].smgrid = bufHdr->tag.smgrid;
 			fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
 			fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
 			fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
@@ -204,28 +206,29 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 			nulls[5] = true;
 			nulls[6] = true;
 			nulls[7] = true;
-			/* unused for v1.0 callers, but the array is always long enough */
 			nulls[8] = true;
+			nulls[9] = true;
 		}
 		else
 		{
-			values[1] = ObjectIdGetDatum(fctx->record[i].relfilenode);
+			values[1] = Int16GetDatum(fctx->record[i].smgrid);
 			nulls[1] = false;
-			values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
+			values[2] = ObjectIdGetDatum(fctx->record[i].relfilenode);
 			nulls[2] = false;
-			values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
+			values[3] = ObjectIdGetDatum(fctx->record[i].reltablespace);
 			nulls[3] = false;
-			values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
+			values[4] = ObjectIdGetDatum(fctx->record[i].reldatabase);
 			nulls[4] = false;
-			values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
+			values[5] = ObjectIdGetDatum(fctx->record[i].forknum);
 			nulls[5] = false;
-			values[6] = BoolGetDatum(fctx->record[i].isdirty);
+			values[6] = Int64GetDatum((int64) fctx->record[i].blocknum);
 			nulls[6] = false;
-			values[7] = Int16GetDatum(fctx->record[i].usagecount);
+			values[7] = BoolGetDatum(fctx->record[i].isdirty);
 			nulls[7] = false;
-			/* unused for v1.0 callers, but the array is always long enough */
-			values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
+			values[8] = Int16GetDatum(fctx->record[i].usagecount);
 			nulls[8] = false;
+			values[9] = Int32GetDatum(fctx->record[i].pinning_backends);
+			nulls[9] = false;
 		}
 
 		/* Build and return the tuple. */
diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml
index faf5a31..a0a7be3 100644
--- a/doc/src/sgml/pgbuffercache.sgml
+++ b/doc/src/sgml/pgbuffercache.sgml
@@ -58,6 +58,13 @@
      </row>
 
      <row>
+      <entry><structfield>smgrid</structfield></entry>
+      <entry><type>smallint</type></entry>
+      <entry></entry>
+      <entry>Block storage manager ID.  0 for regular relation data.</entry>
+     </row>
+
+     <row>
       <entry><structfield>relfilenode</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal>pg_class.relfilenode</literal></entry>
diff --git a/src/backend/access/brin/brin_xlog.c b/src/backend/access/brin/brin_xlog.c
index db1f47c..a13b3cd 100644
--- a/src/backend/access/brin/brin_xlog.c
+++ b/src/backend/access/brin/brin_xlog.c
@@ -217,7 +217,7 @@ brin_xlog_revmap_extend(XLogReaderState *record)
 
 	xlrec = (xl_brin_revmap_extend *) XLogRecGetData(record);
 
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &targetBlk);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &targetBlk);
 	Assert(xlrec->targetBlk == targetBlk);
 
 	/* Update the metapage */
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index c945b28..261881c 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -95,11 +95,12 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
 
 	if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
 	{
+		SmgrId		smgrid;
 		RelFileNode node;
 		ForkNumber	forknum;
 		BlockNumber blknum;
 
-		BufferGetTag(buffer, &node, &forknum, &blknum);
+		BufferGetTag(buffer, &smgrid, &node, &forknum, &blknum);
 		elog(ERROR, "failed to add item to index page in %u/%u/%u",
 			 node.spcNode, node.dbNode, node.relNode);
 	}
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index 503db34..bf945b9 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -193,7 +193,7 @@ gistRedoDeleteRecord(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 
 		ResolveRecoveryConflictWithSnapshot(xldata->latestRemovedXid, rnode);
 	}
@@ -278,7 +278,7 @@ gistRedoPageSplitRecord(XLogReaderState *record)
 		BlockNumber blkno;
 		IndexTuple *tuples;
 
-		XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno);
+		XLogRecGetBlockTag(record, i + 1, NULL, NULL, NULL, &blkno);
 		if (blkno == GIST_ROOT_BLKNO)
 		{
 			Assert(i == 0);
@@ -313,7 +313,7 @@ gistRedoPageSplitRecord(XLogReaderState *record)
 			{
 				BlockNumber nextblkno;
 
-				XLogRecGetBlockTag(record, i + 2, NULL, NULL, &nextblkno);
+				XLogRecGetBlockTag(record, i + 2, NULL, NULL, NULL, &nextblkno);
 				GistPageGetOpaque(page)->rightlink = nextblkno;
 			}
 			else
diff --git a/src/backend/access/hash/hash_xlog.c b/src/backend/access/hash/hash_xlog.c
index d7b7098..ec604a7 100644
--- a/src/backend/access/hash/hash_xlog.c
+++ b/src/backend/access/hash/hash_xlog.c
@@ -51,7 +51,7 @@ hash_xlog_init_meta_page(XLogReaderState *record)
 	 * special handling for init forks as create index operations don't log a
 	 * full page image of the metapage.
 	 */
-	XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, &forknum, NULL);
 	if (forknum == INIT_FORKNUM)
 		FlushOneBuffer(metabuf);
 
@@ -89,7 +89,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
 	 * special handling for init forks as create index operations don't log a
 	 * full page image of the metapage.
 	 */
-	XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, &forknum, NULL);
 	if (forknum == INIT_FORKNUM)
 		FlushOneBuffer(bitmapbuf);
 	UnlockReleaseBuffer(bitmapbuf);
@@ -113,7 +113,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
 		PageSetLSN(page, lsn);
 		MarkBufferDirty(metabuf);
 
-		XLogRecGetBlockTag(record, 1, NULL, &forknum, NULL);
+		XLogRecGetBlockTag(record, 1, NULL, NULL, &forknum, NULL);
 		if (forknum == INIT_FORKNUM)
 			FlushOneBuffer(metabuf);
 	}
@@ -190,8 +190,8 @@ hash_xlog_add_ovfl_page(XLogReaderState *record)
 	Size		datalen PG_USED_FOR_ASSERTS_ONLY;
 	bool		new_bmpage = false;
 
-	XLogRecGetBlockTag(record, 0, NULL, NULL, &rightblk);
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &leftblk);
+	XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &rightblk);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &leftblk);
 
 	ovflbuf = XLogInitBufferForRedo(record, 0);
 	Assert(BufferIsValid(ovflbuf));
@@ -1001,7 +1001,7 @@ hash_xlog_vacuum_one_page(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 		ResolveRecoveryConflictWithSnapshot(xldata->latestRemovedXid, rnode);
 	}
 
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 376ee2a..f6042e4 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -427,7 +427,8 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 		MarkBufferDirty(buf);
 
 		if (use_wal)
-			log_newpage(&rel->rd_node,
+			log_newpage(SMGR_MD,
+						&rel->rd_node,
 						forkNum,
 						blkno,
 						BufferGetPage(buf),
@@ -1021,7 +1022,8 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 	ovflopaque->hasho_page_id = HASHO_PAGE_ID;
 
 	if (RelationNeedsWAL(rel))
-		log_newpage(&rel->rd_node,
+		log_newpage(SMGR_MD,
+					&rel->rd_node,
 					MAIN_FORKNUM,
 					lastblock,
 					zerobuf.data,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8ac0f8a..7a63c09 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7684,7 +7684,7 @@ heap_xlog_clean(XLogReaderState *record)
 	BlockNumber blkno;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * We're about to remove tuples. In Hot Standby mode, ensure that there's
@@ -7779,7 +7779,7 @@ heap_xlog_visible(XLogReaderState *record)
 	BlockNumber blkno;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 1, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 1, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * If there are any Hot Standby transactions running that have an xmin
@@ -7927,7 +7927,7 @@ heap_xlog_freeze_page(XLogReaderState *record)
 
 		TransactionIdRetreat(latestRemovedXid);
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 		ResolveRecoveryConflictWithSnapshot(latestRemovedXid, rnode);
 	}
 
@@ -7999,7 +7999,7 @@ heap_xlog_delete(XLogReaderState *record)
 	RelFileNode target_node;
 	ItemPointerData target_tid;
 
-	XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &target_node, NULL, &blkno);
 	ItemPointerSetBlockNumber(&target_tid, blkno);
 	ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
 
@@ -8080,7 +8080,7 @@ heap_xlog_insert(XLogReaderState *record)
 	ItemPointerData target_tid;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &target_node, NULL, &blkno);
 	ItemPointerSetBlockNumber(&target_tid, blkno);
 	ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
 
@@ -8202,7 +8202,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	 */
 	xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &blkno);
 
 	/*
 	 * The visibility map may need to be fixed even if the heap page is
@@ -8348,8 +8348,8 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	oldtup.t_data = NULL;
 	oldtup.t_len = 0;
 
-	XLogRecGetBlockTag(record, 0, &rnode, NULL, &newblk);
-	if (XLogRecGetBlockTag(record, 1, NULL, NULL, &oldblk))
+	XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &newblk);
+	if (XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &oldblk))
 	{
 		/* HOT updates are never done across pages */
 		Assert(!hot_update);
@@ -8644,7 +8644,7 @@ heap_xlog_lock(XLogReaderState *record)
 		BlockNumber block;
 		Relation	reln;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, &block);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &block);
 		reln = CreateFakeRelcacheEntry(rnode);
 
 		visibilitymap_pin(reln, block, &vmbuffer);
@@ -8717,7 +8717,7 @@ heap_xlog_lock_updated(XLogReaderState *record)
 		BlockNumber block;
 		Relation	reln;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, &block);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, &block);
 		reln = CreateFakeRelcacheEntry(rnode);
 
 		visibilitymap_pin(reln, block, &vmbuffer);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index b7d2ddb..4f64c0f 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -637,7 +637,7 @@ heapam_relation_copy_data(Relation rel, const RelFileNode *newrnode)
 {
 	SMgrRelation dstrel;
 
-	dstrel = smgropen(*newrnode, rel->rd_backend);
+	dstrel = smgropen(SMGR_MD, *newrnode, rel->rd_backend);
 	RelationOpenSmgr(rel);
 
 	/*
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 369694f..67e00b6 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -331,7 +331,8 @@ end_heap_rewrite(RewriteState state)
 	if (state->rs_buffer_valid)
 	{
 		if (state->rs_use_wal)
-			log_newpage(&state->rs_new_rel->rd_node,
+			log_newpage(SMGR_MD,
+						&state->rs_new_rel->rd_node,
 						MAIN_FORKNUM,
 						state->rs_blockno,
 						state->rs_buffer,
@@ -696,7 +697,8 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 
 			/* XLOG stuff */
 			if (state->rs_use_wal)
-				log_newpage(&state->rs_new_rel->rd_node,
+				log_newpage(SMGR_MD,
+							&state->rs_new_rel->rd_node,
 							MAIN_FORKNUM,
 							state->rs_blockno,
 							page,
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 85e54ac..4255d4b 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -172,7 +172,7 @@ btbuildempty(Relation index)
 	PageSetChecksumInplace(metapage, BTREE_METAPAGE);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE,
 			  (char *) metapage, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				BTREE_METAPAGE, metapage, true);
 
 	/*
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index d0b9013..931d6cd 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -658,7 +658,8 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 	if (wstate->btws_use_wal)
 	{
 		/* We use the heap NEWPAGE record type for this */
-		log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page, true);
+		log_newpage(SMGR_MD, &wstate->index->rd_node, MAIN_FORKNUM, blkno,
+					page, true);
 	}
 
 	/*
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index 6532a25..1b0a1f7 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -216,9 +216,9 @@ btree_xlog_split(bool onleft, XLogReaderState *record)
 	BlockNumber rightsib;
 	BlockNumber rnext;
 
-	XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
-	if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
+	XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &leftsib);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &rightsib);
+	if (!XLogRecGetBlockTag(record, 2, NULL, NULL, NULL, &rnext))
 		rnext = P_NONE;
 
 	/*
@@ -524,7 +524,7 @@ btree_xlog_delete(XLogReaderState *record)
 	{
 		RelFileNode rnode;
 
-		XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
+		XLogRecGetBlockTag(record, 0, NULL, &rnode, NULL, NULL);
 
 		ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid, rnode);
 	}
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index b40bd44..8019f68 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -171,7 +171,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_METAPAGE_BLKNO, page, true);
 
 	/* Likewise for the root page. */
@@ -180,7 +180,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_ROOT_BLKNO, page, true);
 
 	/* Likewise for the null-tuples root page. */
@@ -189,7 +189,7 @@ spgbuildempty(Relation index)
 	PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
 			  (char *) page, true);
-	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+	log_newpage(SMGR_MD, &index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
 				SPGIST_NULL_BLKNO, page, true);
 
 	/*
diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c
index ebe6ae8..3ce35fe 100644
--- a/src/backend/access/spgist/spgxlog.c
+++ b/src/backend/access/spgist/spgxlog.c
@@ -151,7 +151,7 @@ spgRedoAddLeaf(XLogReaderState *record)
 			SpGistInnerTuple tuple;
 			BlockNumber blknoLeaf;
 
-			XLogRecGetBlockTag(record, 0, NULL, NULL, &blknoLeaf);
+			XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &blknoLeaf);
 
 			page = BufferGetPage(buffer);
 
@@ -184,7 +184,7 @@ spgRedoMoveLeafs(XLogReaderState *record)
 	XLogRedoAction action;
 	BlockNumber blknoDst;
 
-	XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoDst);
+	XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &blknoDst);
 
 	fillFakeState(&state, xldata->stateSrc);
 
@@ -328,8 +328,8 @@ spgRedoAddNode(XLogReaderState *record)
 		BlockNumber blkno;
 		BlockNumber blknoNew;
 
-		XLogRecGetBlockTag(record, 0, NULL, NULL, &blkno);
-		XLogRecGetBlockTag(record, 1, NULL, NULL, &blknoNew);
+		XLogRecGetBlockTag(record, 0, NULL, NULL, NULL, &blkno);
+		XLogRecGetBlockTag(record, 1, NULL, NULL, NULL, &blknoNew);
 
 		/*
 		 * In normal operation we would have all three pages (source, dest,
@@ -549,7 +549,7 @@ spgRedoPickSplit(XLogReaderState *record)
 	BlockNumber blknoInner;
 	XLogRedoAction action;
 
-	XLogRecGetBlockTag(record, 2, NULL, NULL, &blknoInner);
+	XLogRecGetBlockTag(record, 2, NULL, NULL, NULL, &blknoInner);
 
 	fillFakeState(&state, xldata->stateSrc);
 
@@ -879,7 +879,7 @@ spgRedoVacuumRedirect(XLogReaderState *record)
 		{
 			RelFileNode node;
 
-			XLogRecGetBlockTag(record, 0, &node, NULL, NULL);
+			XLogRecGetBlockTag(record, 0, NULL, &node, NULL, NULL);
 			ResolveRecoveryConflictWithSnapshot(xldata->newestRedirectXid,
 												node);
 		}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index e08320e..254c724 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -1371,6 +1371,7 @@ checkXLogConsistency(XLogReaderState *record)
 	ForkNumber	forknum;
 	BlockNumber blkno;
 	int			block_id;
+	SmgrId		smgrid;
 
 	/* Records with no backup blocks have no need for consistency checks. */
 	if (!XLogRecHasAnyBlockRefs(record))
@@ -1383,7 +1384,8 @@ checkXLogConsistency(XLogReaderState *record)
 		Buffer		buf;
 		Page		page;
 
-		if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+		if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+								&blkno))
 		{
 			/*
 			 * WAL record doesn't contain a block reference with the given id.
@@ -1408,7 +1410,7 @@ checkXLogConsistency(XLogReaderState *record)
 		 * Read the contents from the current buffer and store it in a
 		 * temporary page.
 		 */
-		buf = XLogReadBufferExtended(rnode, forknum, blkno,
+		buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno,
 									 RBM_NORMAL_NO_LOG);
 		if (!BufferIsValid(buf))
 			continue;
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d4..1697797 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -43,7 +43,8 @@ typedef struct
 {
 	bool		in_use;			/* is this slot in use? */
 	uint8		flags;			/* REGBUF_* flags */
-	RelFileNode rnode;			/* identifies the relation and block */
+	SmgrId		smgrid;			/* identifies the SGMR, relation and block */
+	RelFileNode rnode;
 	ForkNumber	forkno;
 	BlockNumber block;
 	Page		page;			/* page content */
@@ -227,7 +228,8 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 
 	regbuf = &registered_buffers[block_id];
 
-	BufferGetTag(buffer, &regbuf->rnode, &regbuf->forkno, &regbuf->block);
+	BufferGetTag(buffer, &regbuf->smgrid, &regbuf->rnode, &regbuf->forkno,
+				 &regbuf->block);
 	regbuf->page = BufferGetPage(buffer);
 	regbuf->flags = flags;
 	regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
@@ -248,7 +250,8 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 			if (i == block_id || !regbuf_old->in_use)
 				continue;
 
-			Assert(!RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
+			Assert(regbuf_old->smgrid != regbuf->smgrid ||
+				   !RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
 				   regbuf_old->forkno != regbuf->forkno ||
 				   regbuf_old->block != regbuf->block);
 		}
@@ -263,8 +266,9 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
  * shared buffer pool (i.e. when you don't have a Buffer for it).
  */
 void
-XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
-				  BlockNumber blknum, Page page, uint8 flags)
+XLogRegisterBlock(uint8 block_id, SmgrId smgrid, RelFileNode *rnode,
+				  ForkNumber forknum, BlockNumber blknum, Page page,
+				  uint8 flags)
 {
 	registered_buffer *regbuf;
 
@@ -280,6 +284,7 @@ XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
 
 	regbuf = &registered_buffers[block_id];
 
+	regbuf->smgrid = smgrid;
 	regbuf->rnode = *rnode;
 	regbuf->forkno = forknum;
 	regbuf->block = blknum;
@@ -303,7 +308,8 @@ XLogRegisterBlock(uint8 block_id, RelFileNode *rnode, ForkNumber forknum,
 			if (i == block_id || !regbuf_old->in_use)
 				continue;
 
-			Assert(!RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
+			Assert(regbuf_old->smgrid != regbuf->smgrid ||
+				   !RelFileNodeEquals(regbuf_old->rnode, regbuf->rnode) ||
 				   regbuf_old->forkno != regbuf->forkno ||
 				   regbuf_old->block != regbuf->block);
 		}
@@ -702,7 +708,8 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			rdt_datas_last = regbuf->rdata_tail;
 		}
 
-		if (prev_regbuf && RelFileNodeEquals(regbuf->rnode, prev_regbuf->rnode))
+		if (prev_regbuf && regbuf->smgrid == prev_regbuf->smgrid &&
+			RelFileNodeEquals(regbuf->rnode, prev_regbuf->rnode))
 		{
 			samerel = true;
 			bkpb.fork_flags |= BKPBLOCK_SAME_REL;
@@ -727,6 +734,8 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 		}
 		if (!samerel)
 		{
+			memcpy(scratch, &regbuf->smgrid, sizeof(SmgrId));
+			scratch += sizeof(SmgrId);
 			memcpy(scratch, &regbuf->rnode, sizeof(RelFileNode));
 			scratch += sizeof(RelFileNode);
 		}
@@ -919,6 +928,7 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 		int			flags;
 		PGAlignedBlock copied_buffer;
 		char	   *origdata = (char *) BufferGetBlock(buffer);
+		SmgrId		smgrid;
 		RelFileNode rnode;
 		ForkNumber	forkno;
 		BlockNumber blkno;
@@ -947,8 +957,8 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 		if (buffer_std)
 			flags |= REGBUF_STANDARD;
 
-		BufferGetTag(buffer, &rnode, &forkno, &blkno);
-		XLogRegisterBlock(0, &rnode, forkno, blkno, copied_buffer.data, flags);
+		BufferGetTag(buffer, &smgrid, &rnode, &forkno, &blkno);
+		XLogRegisterBlock(0, smgrid, &rnode, forkno, blkno, copied_buffer.data, flags);
 
 		recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI_FOR_HINT);
 	}
@@ -969,8 +979,8 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
  * the unused space to be left out from the WAL record, making it smaller.
  */
 XLogRecPtr
-log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
-			Page page, bool page_std)
+log_newpage(SmgrId smgrid, RelFileNode *rnode, ForkNumber forkNum,
+			BlockNumber blkno, Page page, bool page_std)
 {
 	int			flags;
 	XLogRecPtr	recptr;
@@ -980,7 +990,7 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
 		flags |= REGBUF_STANDARD;
 
 	XLogBeginInsert();
-	XLogRegisterBlock(0, rnode, forkNum, blkno, page, flags);
+	XLogRegisterBlock(0, smgrid, rnode, forkNum, blkno, page, flags);
 	recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
 
 	/*
@@ -1009,6 +1019,7 @@ XLogRecPtr
 log_newpage_buffer(Buffer buffer, bool page_std)
 {
 	Page		page = BufferGetPage(buffer);
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 	BlockNumber blkno;
@@ -1016,9 +1027,9 @@ log_newpage_buffer(Buffer buffer, bool page_std)
 	/* Shared buffers should be modified in a critical section. */
 	Assert(CritSectionCount > 0);
 
-	BufferGetTag(buffer, &rnode, &forkNum, &blkno);
+	BufferGetTag(buffer, &smgrid, &rnode, &forkNum, &blkno);
 
-	return log_newpage(&rnode, forkNum, blkno, page, page_std);
+	return log_newpage(smgrid, &rnode, forkNum, blkno, page, page_std);
 }
 
 /*
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 88be7fe..8ac51be 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1056,6 +1056,7 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 	uint32		remaining;
 	uint32		datatotal;
 	RelFileNode *rnode = NULL;
+	SmgrId		smgrid = -1;
 	uint8		block_id;
 
 	ResetDecoder(state);
@@ -1229,8 +1230,10 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 			}
 			if (!(fork_flags & BKPBLOCK_SAME_REL))
 			{
+				COPY_HEADER_FIELD(&blk->smgrid, sizeof(SmgrId));
 				COPY_HEADER_FIELD(&blk->rnode, sizeof(RelFileNode));
 				rnode = &blk->rnode;
+				smgrid = blk->smgrid;
 			}
 			else
 			{
@@ -1242,6 +1245,7 @@ DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)
 					goto err;
 				}
 
+				blk->smgrid = smgrid;
 				blk->rnode = *rnode;
 			}
 			COPY_HEADER_FIELD(&blk->blkno, sizeof(BlockNumber));
@@ -1349,12 +1353,13 @@ err:
 /*
  * Returns information about the block that a block reference refers to.
  *
- * If the WAL record contains a block reference with the given ID, *rnode,
- * *forknum, and *blknum are filled in (if not NULL), and returns true.
+ * If the WAL record contains a block reference with the given ID, *smgrid,
+ * *rnode, *forknum and *blknum are filled in (if not NULL), and returns true.
  * Otherwise returns false.
  */
 bool
 XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
+				   SmgrId *smgrid,
 				   RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
 {
 	DecodedBkpBlock *bkpb;
@@ -1363,6 +1368,8 @@ XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
 		return false;
 
 	bkpb = &record->blocks[block_id];
+	if (smgrid)
+		*smgrid = bkpb->smgrid;
 	if (rnode)
 		*rnode = bkpb->rnode;
 	if (forknum)
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663b..c5f27fb 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -335,8 +335,9 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	Page		page;
 	bool		zeromode;
 	bool		willinit;
+	SmgrId		smgrid;
 
-	if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+	if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum, &blkno))
 	{
 		/* Caller specified a bogus block_id */
 		elog(PANIC, "failed to locate backup block with ID %d", block_id);
@@ -357,7 +358,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	if (XLogRecBlockImageApply(record, block_id))
 	{
 		Assert(XLogRecHasBlockImage(record, block_id));
-		*buf = XLogReadBufferExtended(rnode, forknum, blkno,
+		*buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno,
 									  get_cleanup_lock ? RBM_ZERO_AND_CLEANUP_LOCK : RBM_ZERO_AND_LOCK);
 		page = BufferGetPage(*buf);
 		if (!RestoreBlockImage(record, block_id, page))
@@ -387,7 +388,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	}
 	else
 	{
-		*buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
+		*buf = XLogReadBufferExtended(smgrid, rnode, forknum, blkno, mode);
 		if (BufferIsValid(*buf))
 		{
 			if (mode != RBM_ZERO_AND_LOCK && mode != RBM_ZERO_AND_CLEANUP_LOCK)
@@ -434,7 +435,7 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
  * modified.
  */
 Buffer
-XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
+XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 					   BlockNumber blkno, ReadBufferMode mode)
 {
 	BlockNumber lastblock;
@@ -444,7 +445,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	Assert(blkno != P_NEW);
 
 	/* Open the relation at smgr level */
-	smgr = smgropen(rnode, InvalidBackendId);
+	smgr = smgropen(smgrid, rnode, InvalidBackendId);
 
 	/*
 	 * Create the target file if it doesn't already exist.  This lets us cope
@@ -461,7 +462,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	if (blkno < lastblock)
 	{
 		/* page exists in file */
-		buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
+		buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
 										   mode, NULL);
 	}
 	else
@@ -486,7 +487,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 					LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 				ReleaseBuffer(buffer);
 			}
-			buffer = ReadBufferWithoutRelcache(rnode, forknum,
+			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum,
 											   P_NEW, mode, NULL);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
@@ -496,7 +497,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 			if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
-			buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
+			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
 											   mode, NULL);
 		}
 	}
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 3cc886f..9509c19 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -102,7 +102,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 			return NULL;		/* placate compiler */
 	}
 
-	srel = smgropen(rnode, backend);
+	srel = smgropen(SMGR_MD, rnode, backend);
 	smgrcreate(srel, MAIN_FORKNUM, false);
 
 	if (needs_wal)
@@ -353,7 +353,8 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
 		 * space.
 		 */
 		if (use_wal)
-			log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
+			log_newpage(SMGR_MD, &dst->smgr_rnode.node, forkNum, blkno, page,
+						false);
 
 		PageSetChecksumInplace(page, blkno);
 
@@ -428,7 +429,7 @@ smgrDoPendingDeletes(bool isCommit)
 			{
 				SMgrRelation srel;
 
-				srel = smgropen(pending->relnode, pending->backend);
+				srel = smgropen(SMGR_MD, pending->relnode, pending->backend);
 
 				/* allocate the initial array, or extend it, if needed */
 				if (maxrels == 0)
@@ -580,7 +581,7 @@ smgr_redo(XLogReaderState *record)
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
 		SMgrRelation reln;
 
-		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		reln = smgropen(SMGR_MD, xlrec->rnode, InvalidBackendId);
 		smgrcreate(reln, xlrec->forkNum, true);
 	}
 	else if (info == XLOG_SMGR_TRUNCATE)
@@ -589,7 +590,7 @@ smgr_redo(XLogReaderState *record)
 		SMgrRelation reln;
 		Relation	rel;
 
-		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		reln = smgropen(SMGR_MD, xlrec->rnode, InvalidBackendId);
 
 		/*
 		 * Forcibly create relation if it doesn't exist (which suggests that
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cb2c5e1..4737c58 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12590,7 +12590,7 @@ index_copy_data(Relation rel, RelFileNode newrnode)
 {
 	SMgrRelation dstrel;
 
-	dstrel = smgropen(newrnode, rel->rd_backend);
+	dstrel = smgropen(SMGR_MD, newrnode, rel->rd_backend);
 	RelationOpenSmgr(rel);
 
 	/*
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 151c3ef..3e96d2a 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -683,7 +683,7 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -731,7 +731,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_update *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -796,7 +796,7 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_delete *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
@@ -892,7 +892,7 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	xlrec = (xl_heap_multi_insert *) XLogRecGetData(r);
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &rnode, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &rnode, NULL, NULL);
 	if (rnode.dbNode != ctx->slot->data.database)
 		return;
 
@@ -991,7 +991,7 @@ DecodeSpecConfirm(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 	RelFileNode target_node;
 
 	/* only interested in our database */
-	XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
+	XLogRecGetBlockTag(r, 0, NULL, &target_node, NULL, NULL);
 	if (target_node.dbNode != ctx->slot->data.database)
 		return;
 
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index e7c32f2..71dbbda 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -3496,6 +3496,7 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 	ReorderBufferTupleCidEnt *ent;
 	ForkNumber	forkno;
 	BlockNumber blockno;
+	SmgrId		smgrid;
 	bool		updated_mapping = false;
 
 	/* be careful about padding */
@@ -3507,7 +3508,7 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 	 * get relfilenode from the buffer, no convenient way to access it other
 	 * than that.
 	 */
-	BufferGetTag(buffer, &key.relnode, &forkno, &blockno);
+	BufferGetTag(buffer, &smgrid, &key.relnode, &forkno, &blockno);
 
 	/* tuples can only be in the main fork */
 	Assert(forkno == MAIN_FORKNUM);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 7332e6b..8046334 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -555,7 +555,8 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		int			buf_id;
 
 		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
+		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_which,
+					   reln->rd_smgr->smgr_rnode.node,
 					   forkNum, blockNum);
 
 		/* determine its hash code and partition lock ID */
@@ -680,13 +681,13 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
  * parameters.
  */
 Buffer
-ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
+ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
 						  BufferAccessStrategy strategy)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
+	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
 
 	Assert(InRecovery);
 
@@ -1009,7 +1010,8 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	uint32		buf_state;
 
 	/* create a tag so we can lookup the buffer */
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* determine its hash code and partition lock ID */
 	newHash = BufTableHashCode(&newTag);
@@ -1843,6 +1845,7 @@ BufferSync(int flags)
 			buf_state |= BM_CHECKPOINT_NEEDED;
 
 			item = &CkptBufferIds[num_to_scan++];
+			item->smgrid = bufHdr->tag.smgrid;
 			item->buf_id = buf_id;
 			item->tsId = bufHdr->tag.rnode.spcNode;
 			item->relNode = bufHdr->tag.rnode.relNode;
@@ -2626,12 +2629,12 @@ BufferGetBlockNumber(Buffer buffer)
 
 /*
  * BufferGetTag
- *		Returns the relfilenode, fork number and block number associated with
- *		a buffer.
+ *		Returns the SMGR ID, relfilenode, fork number and block number
+ *		associated with a buffer.
  */
 void
-BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum,
-			 BlockNumber *blknum)
+BufferGetTag(Buffer buffer, SmgrId *smgrid, RelFileNode *rnode,
+			 ForkNumber *forknum, BlockNumber *blknum)
 {
 	BufferDesc *bufHdr;
 
@@ -2644,6 +2647,7 @@ BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum,
 		bufHdr = GetBufferDescriptor(buffer - 1);
 
 	/* pinned, so OK to read tag without spinlock */
+	*smgrid = bufHdr->tag.smgrid;
 	*rnode = bufHdr->tag.rnode;
 	*forknum = bufHdr->tag.forkNum;
 	*blknum = bufHdr->tag.blockNum;
@@ -2695,7 +2699,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 
 	/* Find smgr relation for buffer */
 	if (reln == NULL)
-		reln = smgropen(buf->tag.rnode, InvalidBackendId);
+		reln = smgropen(buf->tag.smgrid, buf->tag.rnode, InvalidBackendId);
 
 	TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum,
 										buf->tag.blockNum,
@@ -4220,6 +4224,11 @@ ckpt_buforder_comparator(const void *pa, const void *pb)
 	const CkptSortItem *a = (const CkptSortItem *) pa;
 	const CkptSortItem *b = (const CkptSortItem *) pb;
 
+	/* compare smgr */
+	if (a->smgrid < b->smgrid)
+		return -1;
+	else if (a->smgrid > b->smgrid)
+		return 1;
 	/* compare tablespace */
 	if (a->tsId < b->tsId)
 		return -1;
@@ -4377,7 +4386,7 @@ IssuePendingWritebacks(WritebackContext *context)
 		i += ahead;
 
 		/* and finally tell the kernel to write the data to storage */
-		reln = smgropen(tag.rnode, InvalidBackendId);
+		reln = smgropen(tag.smgrid, tag.rnode, InvalidBackendId);
 		smgrwriteback(reln, tag.forkNum, tag.blockNum, nblocks);
 	}
 
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index c462ea8..896285a 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -68,7 +68,8 @@ LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
 	BufferTag	newTag;			/* identity of requested block */
 	LocalBufferLookupEnt *hresult;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -111,7 +112,8 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	bool		found;
 	uint32		buf_state;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_which,
+				   smgr->smgr_rnode.node, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -209,7 +211,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 		Page		localpage = (char *) LocalBufHdrGetBlock(bufHdr);
 
 		/* Find smgr relation for buffer */
-		oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
+		oreln = smgropen(bufHdr->tag.smgrid, bufHdr->tag.rnode, MyBackendId);
 
 		PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
 
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index c17b3f4..78a2274 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -210,7 +210,8 @@ XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
 	blkno = fsm_logical_to_physical(addr);
 
 	/* If the page doesn't exist already, extend */
-	buf = XLogReadBufferExtended(rnode, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR);
+	buf = XLogReadBufferExtended(SMGR_MD, rnode, FSM_FORKNUM, blkno,
+								 RBM_ZERO_ON_ERROR);
 	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
 	page = BufferGetPage(buf);
diff --git a/src/backend/storage/freespace/fsmpage.c b/src/backend/storage/freespace/fsmpage.c
index cf7f03f..da3b286 100644
--- a/src/backend/storage/freespace/fsmpage.c
+++ b/src/backend/storage/freespace/fsmpage.c
@@ -268,11 +268,12 @@ restart:
 			 *
 			 * Fix the corruption and restart.
 			 */
+			SmgrId		smgrid;
 			RelFileNode rnode;
 			ForkNumber	forknum;
 			BlockNumber blknum;
 
-			BufferGetTag(buf, &rnode, &forknum, &blknum);
+			BufferGetTag(buf, &smgrid, &rnode, &forknum, &blknum);
 			elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/%u",
 				 blknum, rnode.spcNode, rnode.dbNode, rnode.relNode);
 
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 64acc3f..6c576ed 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -120,7 +120,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 /* local routines */
 static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum,
 						 bool isRedo);
-static MdfdVec *mdopen(SMgrRelation reln, ForkNumber forknum, int behavior);
+static MdfdVec *mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior);
 static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum,
 								   MdfdVec *seg);
 static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum,
@@ -152,6 +152,17 @@ mdinit(void)
 }
 
 /*
+ *	mdopen() -- Initialize a newly-opened relation.
+ */
+void
+mdopen(SMgrRelation reln)
+{
+	/* mark it not open */
+	for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++)
+		reln->md_num_open_segs[forknum] = 0;
+}
+
+/*
  *	mdexists() -- Does the physical file exist?
  *
  * Note: this will return true for lingering files, with pending deletions
@@ -165,7 +176,7 @@ mdexists(SMgrRelation reln, ForkNumber forkNum)
 	 */
 	mdclose(reln, forkNum);
 
-	return (mdopen(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
+	return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
 }
 
 /*
@@ -425,7 +436,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 }
 
 /*
- *	mdopen() -- Open the specified relation.
+ *	mdopenfork() -- Open the specified relation.
  *
  * Note we only open the first segment, when there are multiple segments.
  *
@@ -435,7 +446,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
  * invent one out of whole cloth.
  */
 static MdfdVec *
-mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
+mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 {
 	MdfdVec    *mdfd;
 	char	   *path;
@@ -713,11 +724,11 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 BlockNumber
 mdnblocks(SMgrRelation reln, ForkNumber forknum)
 {
-	MdfdVec    *v = mdopen(reln, forknum, EXTENSION_FAIL);
+	MdfdVec    *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
 	BlockNumber nblocks;
 	BlockNumber segno = 0;
 
-	/* mdopen has opened the first segment */
+	/* mdopenfork has opened the first segment */
 	Assert(reln->md_num_open_segs[forknum] > 0);
 
 	/*
@@ -981,7 +992,7 @@ DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo)
 	srels = palloc(sizeof(SMgrRelation) * ndelrels);
 	for (i = 0; i < ndelrels; i++)
 	{
-		SMgrRelation srel = smgropen(delrels[i], InvalidBackendId);
+		SMgrRelation srel = smgropen(SMGR_MD, delrels[i], InvalidBackendId);
 
 		if (isRedo)
 		{
@@ -1137,7 +1148,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1];
 	else
 	{
-		v = mdopen(reln, forknum, behavior);
+		v = mdopenfork(reln, forknum, behavior);
 		if (!v)
 			return NULL;		/* if behavior & EXTENSION_RETURN_NULL */
 	}
@@ -1257,7 +1268,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 int
 mdsyncfiletag(const FileTag *ftag, char *path)
 {
-	SMgrRelation reln = smgropen(ftag->rnode, InvalidBackendId);
+	SMgrRelation reln = smgropen(SMGR_MD, ftag->rnode, InvalidBackendId);
 	MdfdVec    *v;
 	char	   *p;
 
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c39..26281fa 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -41,6 +41,7 @@ typedef struct f_smgr
 {
 	void		(*smgr_init) (void);	/* may be NULL */
 	void		(*smgr_shutdown) (void);	/* may be NULL */
+	void		(*smgr_open) (SMgrRelation reln);
 	void		(*smgr_close) (SMgrRelation reln, ForkNumber forknum);
 	void		(*smgr_create) (SMgrRelation reln, ForkNumber forknum,
 								bool isRedo);
@@ -68,6 +69,7 @@ static const f_smgr smgrsw[] = {
 	{
 		.smgr_init = mdinit,
 		.smgr_shutdown = NULL,
+		.smgr_open = mdopen,
 		.smgr_close = mdclose,
 		.smgr_create = mdcreate,
 		.smgr_exists = mdexists,
@@ -141,7 +143,7 @@ smgrshutdown(int code, Datum arg)
  *		This does not attempt to actually open the underlying file.
  */
 SMgrRelation
-smgropen(RelFileNode rnode, BackendId backend)
+smgropen(SmgrId smgrid, RelFileNode rnode, BackendId backend)
 {
 	RelFileNodeBackend brnode;
 	SMgrRelation reln;
@@ -170,18 +172,15 @@ smgropen(RelFileNode rnode, BackendId backend)
 	/* Initialize it if not present before */
 	if (!found)
 	{
-		int			forknum;
-
 		/* hash_search already filled in the lookup key */
 		reln->smgr_owner = NULL;
 		reln->smgr_targblock = InvalidBlockNumber;
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
-		reln->smgr_which = 0;	/* we only have md.c at present */
+		reln->smgr_which = smgrid;
 
-		/* mark it not open */
-		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
-			reln->md_num_open_segs[forknum] = 0;
+		/* implementation-specific initialization */
+		smgrsw[reln->smgr_which].smgr_open(reln);
 
 		/* it has no owner yet */
 		dlist_push_tail(&unowned_relns, &reln->node);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 287af60..44f4c41 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -394,8 +394,14 @@ extractPageInfo(XLogReaderState *record)
 		RelFileNode rnode;
 		ForkNumber	forknum;
 		BlockNumber blkno;
+		SmgrId		smgrid;
 
-		if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+		if (!XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+								&blkno))
+			continue;
+
+		/* TODO: How should we handle other smgr IDs? */
+		if (smgrid != SMGR_MD)
 			continue;
 
 		/* We only care about the main fork; others are copied in toto */
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b95d467..9b9b450 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -524,6 +524,7 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 	const RmgrDescData *desc = &RmgrDescTable[XLogRecGetRmid(record)];
 	uint32		rec_len;
 	uint32		fpi_len;
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forknum;
 	BlockNumber blk;
@@ -556,16 +557,19 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 			if (!XLogRecHasBlockRef(record, block_id))
 				continue;
 
-			XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
+			XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+							   &blk);
 			if (forknum != MAIN_FORKNUM)
-				printf(", blkref #%u: rel %u/%u/%u fork %s blk %u",
+				printf(", blkref #%u: smgr %d rel %u/%u/%u fork %s blk %u",
 					   block_id,
+					   smgrid,
 					   rnode.spcNode, rnode.dbNode, rnode.relNode,
 					   forkNames[forknum],
 					   blk);
 			else
-				printf(", blkref #%u: rel %u/%u/%u blk %u",
+				printf(", blkref #%u: smgr %d rel %u/%u/%u blk %u",
 					   block_id,
+					   smgrid,
 					   rnode.spcNode, rnode.dbNode, rnode.relNode,
 					   blk);
 			if (XLogRecHasBlockImage(record, block_id))
@@ -587,9 +591,11 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
 			if (!XLogRecHasBlockRef(record, block_id))
 				continue;
 
-			XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
-			printf("\tblkref #%u: rel %u/%u/%u fork %s blk %u",
+			XLogRecGetBlockTag(record, block_id, &smgrid, &rnode, &forknum,
+							   &blk);
+			printf("\tblkref #%u: smgr %d rel %u/%u/%u fork %s blk %u",
 				   block_id,
+				   smgrid,
 				   rnode.spcNode, rnode.dbNode, rnode.relNode,
 				   forkNames[forknum],
 				   blk);
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index df24089..3ae9d22 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -16,6 +16,7 @@
 #include "storage/block.h"
 #include "storage/buf.h"
 #include "storage/relfilenode.h"
+#include "storage/smgr.h"
 #include "utils/relcache.h"
 
 /*
@@ -45,15 +46,17 @@ extern XLogRecPtr XLogInsert(RmgrId rmid, uint8 info);
 extern void XLogEnsureRecordSpace(int nbuffers, int ndatas);
 extern void XLogRegisterData(char *data, int len);
 extern void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags);
-extern void XLogRegisterBlock(uint8 block_id, RelFileNode *rnode,
-							  ForkNumber forknum, BlockNumber blknum, char *page,
-							  uint8 flags);
+extern void XLogRegisterBlock(uint8 block_id, SmgrId smgrid,
+							  RelFileNode *rnode, ForkNumber forknum,
+							  BlockNumber blknum,
+							  char *page, uint8 flags);
 extern void XLogRegisterBufData(uint8 block_id, char *data, int len);
 extern void XLogResetInsertion(void);
 extern bool XLogCheckBufferNeedsBackup(Buffer buffer);
 
-extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum,
-							  BlockNumber blk, char *page, bool page_std);
+extern XLogRecPtr log_newpage(SmgrId smgrid, RelFileNode *rnode,
+							  ForkNumber forkNum, BlockNumber blk,
+							  char *page, bool page_std);
 extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std);
 extern void log_newpage_range(Relation rel, ForkNumber forkNum,
 							  BlockNumber startblk, BlockNumber endblk, bool page_std);
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 04228e2..3c545ff 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -26,6 +26,7 @@
 #define XLOGREADER_H
 
 #include "access/xlogrecord.h"
+#include "storage/smgr.h"
 
 typedef struct XLogReaderState XLogReaderState;
 
@@ -43,6 +44,7 @@ typedef struct
 	bool		in_use;
 
 	/* Identify the block this refers to */
+	SmgrId		smgrid;
 	RelFileNode rnode;
 	ForkNumber	forknum;
 	BlockNumber blkno;
@@ -243,7 +245,7 @@ extern bool DecodeXLogRecord(XLogReaderState *state, XLogRecord *record,
 extern bool RestoreBlockImage(XLogReaderState *recoder, uint8 block_id, char *dst);
 extern char *XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len);
 extern bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
-							   RelFileNode *rnode, ForkNumber *forknum,
-							   BlockNumber *blknum);
+							   SmgrId *smgrid, RelFileNode *rnode,
+							   ForkNumber *forknum, BlockNumber *blknum);
 
 #endif							/* XLOGREADER_H */
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59..366b8d3 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -13,6 +13,7 @@
 
 #include "access/xlogreader.h"
 #include "storage/bufmgr.h"
+#include "storage/smgr.h"
 
 
 extern bool XLogHaveInvalidPages(void);
@@ -41,7 +42,8 @@ extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record,
 													ReadBufferMode mode, bool get_cleanup_lock,
 													Buffer *buf);
 
-extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
+extern Buffer XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode,
+									 ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index df2dda7..e0d7e8f 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -87,16 +87,20 @@
  *
  * Note: if there's any pad bytes in the struct, INIT_BUFFERTAG will have
  * to be fixed to zero them, since this struct is used as a hash key.
+ * Conceptually the SmgrId should go first, but we put it next to the
+ * ForkNumber so that it packs better with typical alignment rules.
  */
 typedef struct buftag
 {
 	RelFileNode rnode;			/* physical relation identifier */
-	ForkNumber	forkNum;
+	int16		smgrid;			/* SmgrId */
+	int16		forkNum;		/* ForkNumber */
 	BlockNumber blockNum;		/* blknum relative to begin of reln */
 } BufferTag;
 
 #define CLEAR_BUFFERTAG(a) \
 ( \
+	(a).smgrid = SMGR_INVALID, \
 	(a).rnode.spcNode = InvalidOid, \
 	(a).rnode.dbNode = InvalidOid, \
 	(a).rnode.relNode = InvalidOid, \
@@ -104,8 +108,9 @@ typedef struct buftag
 	(a).blockNum = InvalidBlockNumber \
 )
 
-#define INIT_BUFFERTAG(a,xx_rnode,xx_forkNum,xx_blockNum) \
+#define INIT_BUFFERTAG(a,xx_smgrid,xx_rnode,xx_forkNum,xx_blockNum) \
 ( \
+	(a).smgrid = (xx_smgrid), \
 	(a).rnode = (xx_rnode), \
 	(a).forkNum = (xx_forkNum), \
 	(a).blockNum = (xx_blockNum) \
@@ -113,6 +118,7 @@ typedef struct buftag
 
 #define BUFFERTAGS_EQUAL(a,b) \
 ( \
+	(a).smgrid == (b).smgrid && \
 	RelFileNodeEquals((a).rnode, (b).rnode) && \
 	(a).blockNum == (b).blockNum && \
 	(a).forkNum == (b).forkNum \
@@ -288,6 +294,7 @@ extern BufferDesc *LocalBufferDescriptors;
  */
 typedef struct CkptSortItem
 {
+	SmgrId		smgrid;
 	Oid			tsId;
 	Oid			relNode;
 	ForkNumber	forkNum;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 509f4b7..256dcd5 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -18,6 +18,7 @@
 #include "storage/buf.h"
 #include "storage/bufpage.h"
 #include "storage/relfilenode.h"
+#include "storage/smgr.h"
 #include "utils/relcache.h"
 #include "utils/snapmgr.h"
 
@@ -166,11 +167,11 @@ extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
-								 BlockNumber blockNum, ReadBufferMode mode,
+				   				 BlockNumber blockNum, ReadBufferMode mode,
 								 BufferAccessStrategy strategy);
-extern Buffer ReadBufferWithoutRelcache(RelFileNode rnode,
-										ForkNumber forkNum, BlockNumber blockNum,
-										ReadBufferMode mode, BufferAccessStrategy strategy);
+extern Buffer ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+						  ForkNumber forkNum, BlockNumber blockNum,
+						  ReadBufferMode mode, BufferAccessStrategy strategy);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -205,7 +206,7 @@ extern XLogRecPtr BufferGetLSNAtomic(Buffer buffer);
 extern void PrintPinnedBufs(void);
 #endif
 extern Size BufferShmemSize(void);
-extern void BufferGetTag(Buffer buffer, RelFileNode *rnode,
+extern void BufferGetTag(Buffer buffer, SmgrId *smgrid, RelFileNode *rnode,
 						 ForkNumber *forknum, BlockNumber *blknum);
 
 extern void MarkBufferDirtyHint(Buffer buffer, bool buffer_std);
diff --git a/src/include/storage/md.h b/src/include/storage/md.h
index df24b93..c0f05e2 100644
--- a/src/include/storage/md.h
+++ b/src/include/storage/md.h
@@ -21,6 +21,7 @@
 
 /* md storage manager functionality */
 extern void mdinit(void);
+extern void mdopen(SMgrRelation reln);
 extern void mdclose(SMgrRelation reln, ForkNumber forknum);
 extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
 extern bool mdexists(SMgrRelation reln, ForkNumber forknum);
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index d286c8c..243efc6 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -79,8 +79,14 @@ typedef SMgrRelationData *SMgrRelation;
 #define SmgrIsTemp(smgr) \
 	RelFileNodeBackendIsTemp((smgr)->smgr_rnode)
 
+typedef enum SmgrId
+{
+	SMGR_INVALID = -1,
+	SMGR_MD = 0,		/* md.c */
+} SmgrId;
+
 extern void smgrinit(void);
-extern SMgrRelation smgropen(RelFileNode rnode, BackendId backend);
+extern SMgrRelation smgropen(SmgrId which, RelFileNode rnode, BackendId backend);
 extern bool smgrexists(SMgrRelation reln, ForkNumber forknum);
 extern void smgrsetowner(SMgrRelation *owner, SMgrRelation reln);
 extern void smgrclearowner(SMgrRelation *owner, SMgrRelation reln);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index d7f33ab..c6e516d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -52,6 +52,7 @@ typedef LockInfoData *LockInfo;
 
 typedef struct RelationData
 {
+	SmgrId		rd_smgrid;		/* relation storage manager */
 	RelFileNode rd_node;		/* relation physical identifier */
 	/* use "struct" here to avoid needing to include smgr.h: */
 	struct SMgrRelationData *rd_smgr;	/* cached file handle, or NULL */
@@ -471,7 +472,10 @@ typedef struct ViewOptions
 #define RelationOpenSmgr(relation) \
 	do { \
 		if ((relation)->rd_smgr == NULL) \
-			smgrsetowner(&((relation)->rd_smgr), smgropen((relation)->rd_node, (relation)->rd_backend)); \
+			smgrsetowner(&((relation)->rd_smgr), \
+						 smgropen((relation)->rd_smgrid, \
+								  (relation)->rd_node, \
+								  (relation)->rd_backend)); \
 	} while (0)
 
 /*
-- 
1.8.3.1

0002-Move-tablespace-dir-creation-from-smgr.c-to-md.c.patchapplication/octet-stream; name=0002-Move-tablespace-dir-creation-from-smgr.c-to-md.c.patchDownload
From 6fa24d975e21435bda6f929e3d9ba5bdbe78fa5d Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 30 Apr 2019 22:11:03 +1200
Subject: [PATCH 02/14] Move tablespace dir creation from smgr.c to md.c.

For undo logs, we don't need to create tablespace directories when
opening a relation, because that is managed automatically by
undolog.c.

Author: Thomas Munro
---
 src/backend/storage/smgr/md.c   | 14 ++++++++++++++
 src/backend/storage/smgr/smgr.c | 14 --------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 6c576ed..4c9d448 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -28,6 +28,7 @@
 #include "miscadmin.h"
 #include "access/xlogutils.h"
 #include "access/xlog.h"
+#include "commands/tablespace.h"
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
@@ -196,6 +197,19 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 
 	Assert(reln->md_num_open_segs[forkNum] == 0);
 
+	/*
+	 * We may be using the target table space for the first time in this
+	 * database, so create a per-database subdirectory if needed.
+	 *
+	 * XXX this is a fairly ugly violation of module layering, but this seems
+	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
+	 * should be here and not in commands/tablespace.c?  But that would imply
+	 * importing a lot of stuff that smgr.c oughtn't know, either.
+	 */
+	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
+							reln->smgr_rnode.node.dbNode,
+							isRedo);
+
 	path = relpath(reln->smgr_rnode, forkNum);
 
 	fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 26281fa..4ba07a0 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,7 +17,6 @@
  */
 #include "postgres.h"
 
-#include "commands/tablespace.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -343,19 +342,6 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
 	if (isRedo && reln->md_num_open_segs[forknum] > 0)
 		return;
 
-	/*
-	 * We may be using the target table space for the first time in this
-	 * database, so create a per-database subdirectory if needed.
-	 *
-	 * XXX this is a fairly ugly violation of module layering, but this seems
-	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
-	 * should be here and not in commands/tablespace.c?  But that would imply
-	 * importing a lot of stuff that smgr.c oughtn't know, either.
-	 */
-	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
-							reln->smgr_rnode.node.dbNode,
-							isRedo);
-
 	smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
 }
 
-- 
1.8.3.1

0004-Allow-WAL-record-data-on-first-modification-after-a-.patchapplication/octet-stream; name=0004-Allow-WAL-record-data-on-first-modification-after-a-.patchDownload
From 8178766bddd837efc8608d3b05d3213e690c7250 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Sat, 12 Jan 2019 02:17:02 +1300
Subject: [PATCH 04/14] Allow WAL record data on first modification after a
 checkpoint.

Provide a way to attach data to WAL record conditionally, so that it is
included only if this turns out to the be first modification to a given
block after a checkpoint.

This will be used to record undo log meta-data, to avoid a data
synchronization problem with online checkpoints.

Author: Thomas Munro
Reviewed-by:
Discussion:
---
 src/backend/access/transam/xloginsert.c | 15 +++++++++++++++
 src/include/access/xloginsert.h         |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 1697797..36bf458 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -571,6 +571,21 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			needs_data = false;
 		else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
 			needs_data = true;
+		else if ((regbuf->flags & REGBUF_KEEP_DATA_AFTER_CP) != 0)
+		{
+			XLogRecPtr	page_lsn = PageGetLSN(regbuf->page);
+
+			needs_data = (page_lsn <= RedoRecPtr);
+			if (!needs_data)
+			{
+				/*
+				 * XLogInsertRecord() will detect if our view of the latest
+				 * checkpoint's RedoRecPtr is out of date.
+				 */
+				if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)
+					*fpw_lsn = page_lsn;
+			}
+		}
 		else
 			needs_data = !needs_backup;
 
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index 3ae9d22..616f553 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -38,6 +38,8 @@
 									 * will be skipped) */
 #define REGBUF_KEEP_DATA	0x10	/* include data even if a full-page image
 									 * is taken */
+#define REGBUF_KEEP_DATA_AFTER_CP 0x20 /* include data on the first
+										* modification after a checkpoint */
 
 /* prototypes for public functions in xloginsert.c: */
 extern void XLogBeginInsert(void);
-- 
1.8.3.1

0005-Add-prefetch-support-for-the-undo-log.patchapplication/octet-stream; name=0005-Add-prefetch-support-for-the-undo-log.patchDownload
From 67845a7afa675e973bd0ea9481072effa1eb219d Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/14] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 103 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 88 insertions(+), 36 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c742861..d8dc0cc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index d679279..955e045 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -519,14 +519,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, smgr->smgr_which, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -549,43 +592,33 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_which,
-					   reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+							  ForkNumber forkNum, BlockNumber blockNum,
+							  char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 2aa4952..14ccc52 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -117,7 +117,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1936c5d..2fff673 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -937,6 +937,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 060215f..8e3d38c 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -166,6 +166,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 				   				 BlockNumber blockNum, ReadBufferMode mode,
-- 
1.8.3.1

0003-Add-undo-log-manager.patchapplication/octet-stream; name=0003-Add-undo-log-manager.patchDownload
From 7206c40e4cee3391c537cdb22c854889bb417d0e Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 6 Mar 2019 16:46:04 +1300
Subject: [PATCH 03/14] Add undo log manager.

Add a new subsystem to manage undo logs.  Undo logs allow data to be appended
efficiently, like logs.  They also allow data to be discarded efficiently from
the other end, like a queue.  Thirdly, they allow efficient buffered random
access, like a relation.

Undo logs physically consist of a set of 1MB segment files under
$PGDATA/base/undo (or per-tablespace equivalent) that are created, deleted or
renamed as required, similarly to the way that WAL segments are managed.
Meta-data about the set of undo logs is stored in shared memory, and written
to per-checkpoint files under $PGDATA/pg_undo.

Provide access to the undo files managed by undolog.c through bufmgr.c.
A new SMGR implementation allows bufmgr.c to access files created by
undolog.c.

Author: Thomas Munro, with contributions from Dilip Kumar, Rafia Sabih,
        Robert Haas and Amit Kapila
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ%40mail.gmail.com
---
 src/backend/access/Makefile               |    2 +-
 src/backend/access/rmgrdesc/Makefile      |    2 +-
 src/backend/access/rmgrdesc/undologdesc.c |   81 +
 src/backend/access/transam/rmgr.c         |    1 +
 src/backend/access/transam/xlog.c         |    5 +
 src/backend/access/transam/xlogutils.c    |   73 +-
 src/backend/access/undo/Makefile          |   17 +
 src/backend/access/undo/undolog.c         | 2599 +++++++++++++++++++++++++++++
 src/backend/bootstrap/bootstrap.c         |    3 +
 src/backend/catalog/system_views.sql      |    4 +
 src/backend/commands/tablespace.c         |   23 +
 src/backend/postmaster/pgstat.c           |   21 +
 src/backend/replication/basebackup.c      |   18 +-
 src/backend/replication/logical/decode.c  |    1 +
 src/backend/storage/buffer/bufmgr.c       |   81 +-
 src/backend/storage/buffer/localbuf.c     |   44 +
 src/backend/storage/file/fd.c             |    3 +-
 src/backend/storage/ipc/ipci.c            |    3 +
 src/backend/storage/lmgr/lwlock.c         |    2 +
 src/backend/storage/lmgr/lwlocknames.txt  |    1 +
 src/backend/storage/smgr/Makefile         |    2 +-
 src/backend/storage/smgr/smgr.c           |   21 +-
 src/backend/storage/smgr/undofile.c       |  420 +++++
 src/backend/storage/sync/sync.c           |    6 +
 src/backend/utils/init/postinit.c         |    2 +
 src/backend/utils/misc/guc.c              |   12 +
 src/bin/initdb/initdb.c                   |    2 +
 src/bin/pg_checksums/pg_checksums.c       |   23 +-
 src/bin/pg_resetwal/pg_resetwal.c         |   78 +
 src/bin/pg_upgrade/Makefile               |    2 +-
 src/bin/pg_upgrade/check.c                |   42 +
 src/bin/pg_upgrade/controldata.c          |   25 +
 src/bin/pg_upgrade/exec.c                 |    4 +
 src/bin/pg_upgrade/pg_upgrade.c           |    2 +
 src/bin/pg_upgrade/pg_upgrade.h           |    5 +
 src/bin/pg_upgrade/undo.c                 |  292 ++++
 src/bin/pg_waldump/rmgrdesc.c             |    1 +
 src/include/access/rmgrlist.h             |    1 +
 src/include/access/session.h              |    7 +
 src/include/access/undolog.h              |  455 +++++
 src/include/access/undolog_xlog.h         |   62 +
 src/include/access/xlogutils.h            |   16 +
 src/include/catalog/pg_proc.dat           |    7 +
 src/include/pgstat.h                      |    7 +
 src/include/storage/bufmgr.h              |   14 +-
 src/include/storage/fd.h                  |    1 +
 src/include/storage/lwlock.h              |    5 +-
 src/include/storage/smgr.h                |    4 +
 src/include/storage/sync.h                |    3 +-
 src/include/storage/undofile.h            |   59 +
 src/include/utils/guc.h                   |    2 +
 src/test/regress/expected/rules.out       |   10 +
 52 files changed, 4534 insertions(+), 42 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undologdesc.c
 create mode 100644 src/backend/access/undo/Makefile
 create mode 100644 src/backend/access/undo/undolog.c
 create mode 100644 src/backend/storage/smgr/undofile.c
 create mode 100644 src/bin/pg_upgrade/undo.c
 create mode 100644 src/include/access/undolog.h
 create mode 100644 src/include/access/undolog_xlog.h
 create mode 100644 src/include/storage/undofile.h

diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0880e0a..bf6d3fa 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -9,6 +9,6 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  table tablesample transam
+			  table tablesample transam undo
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 5514db1..91ad1ef 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,6 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undologdesc.c b/src/backend/access/rmgrdesc/undologdesc.c
new file mode 100644
index 0000000..f89fcb3
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undologdesc.c
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * undologdesc.c
+ *	  rmgr descriptor routines for access/undo/undolog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undologdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+
+void
+undolog_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDOLOG_CREATE)
+	{
+		xl_undolog_create *xlrec = (xl_undolog_create *) rec;
+
+		appendStringInfo(buf, "logno %u", xlrec->logno);
+	}
+	else if (info == XLOG_UNDOLOG_EXTEND)
+	{
+		xl_undolog_extend *xlrec = (xl_undolog_extend *) rec;
+
+		appendStringInfo(buf, "logno %u end " UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_DISCARD)
+	{
+		xl_undolog_discard *xlrec = (xl_undolog_discard *) rec;
+
+		appendStringInfo(buf, "logno %u discard " UndoLogOffsetFormat " end "
+						 UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->discard, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_SWITCH)
+	{
+		xl_undolog_switch *xlrec = (xl_undolog_switch *) rec;
+
+		appendStringInfo(buf, "logno %u start " UndoLogOffsetFormat " last " UndoLogOffsetFormat,
+						 xlrec->logno,
+						 xlrec->prevlog_xact_start,
+						 xlrec->prevlog_last_urp);
+	}
+
+}
+
+const char *
+undolog_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			id = "CREATE";
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			id = "EXTEND";
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			id = "DISCARD";
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			id = "SWITCH";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9368b56..8b05374 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 254c724..4ef7fb0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/tuptoaster.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "access/xloginsert.h"
@@ -6699,6 +6700,9 @@ StartupXLOG(void)
 	 */
 	restoreTwoPhaseData();
 
+	/* Recover undo log meta data corresponding to this checkpoint. */
+	StartupUndoLogs(ControlFile->checkPointCopy.redo);
+
 	lastFullPageWrites = checkPoint.fullPageWrites;
 
 	RedoRecPtr = XLogCtl->RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
@@ -8966,6 +8970,7 @@ static void
 CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointCLOG();
+	CheckPointUndoLogs(checkPointRedo, ControlFile->checkPointCopy.redo);
 	CheckPointCommitTs();
 	CheckPointSUBTRANS();
 	CheckPointMultiXact();
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index c5f27fb..b06dbd5 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -294,6 +294,68 @@ XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
 }
 
 /*
+ * Find the block ID of the first block that matches the given rnode forknum
+ * and blockno.  If blockno is InvalidBlockNumber, then match any block
+ * number.  Return true if found.
+ */
+bool
+XLogFindBlockId(XLogReaderState *record,
+				SmgrId smgrid,
+				RelFileNode rnode,
+				ForkNumber forknum,
+				BlockNumber blockno,
+				uint8 *block_id)
+{
+	uint8	i;
+
+	for (i = 0; i <= record->max_block_id; ++i)
+	{
+		DecodedBkpBlock *block = &record->blocks[i];
+
+		if (block->in_use &&
+			block->smgrid == smgrid &&
+			RelFileNodeEquals(block->rnode, rnode) &&
+			block->forknum == forknum &&
+			(block->blkno == blockno || blockno == InvalidBlockNumber))
+		{
+			*block_id = i;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * If the caller doesn't know the the block_id, but does know the RelFileNode,
+ * forknum and block number, then we try to find it.
+ */
+XLogRedoAction
+XLogReadBufferForRedoBlock(XLogReaderState *record,
+						   SmgrId smgrid,
+						   RelFileNode rnode,
+						   ForkNumber forknum,
+						   BlockNumber blockno,
+						   ReadBufferMode mode,
+						   bool get_cleanup_lock,
+						   Buffer *buf)
+{
+	uint8  	block_id;
+
+	if (XLogFindBlockId(record, smgrid, rnode, forknum, blockno, &block_id))
+		return XLogReadBufferForRedoExtended(record,
+											 block_id,
+											 mode,
+											 get_cleanup_lock,
+											 buf);
+
+	elog(ERROR, "failed to find block reference rel %u/%u/%u, forknum = %u, block = %u",
+		 rnode.spcNode, rnode.dbNode, rnode.relNode, forknum, blockno);
+
+	return BLK_NOTFOUND;	/* not reached */
+}
+
+/*
  * Pin and lock a buffer referenced by a WAL record, for the purpose of
  * re-initializing it.
  */
@@ -347,7 +409,8 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	 * Make sure that if the block is marked with WILL_INIT, the caller is
 	 * going to initialize it. And vice versa.
 	 */
-	zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
+	zeromode = (mode == RBM_ZERO || mode == RBM_ZERO_AND_LOCK ||
+				mode == RBM_ZERO_AND_CLEANUP_LOCK);
 	willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
 	if (willinit && !zeromode)
 		elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
@@ -463,7 +526,7 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 	{
 		/* page exists in file */
 		buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-										   mode, NULL);
+										   mode, NULL, RELPERSISTENCE_PERMANENT);
 	}
 	else
 	{
@@ -488,7 +551,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 				ReleaseBuffer(buffer);
 			}
 			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum,
-											   P_NEW, mode, NULL);
+											   P_NEW, mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
 		/* Handle the corner case that P_NEW returns non-consecutive pages */
@@ -498,7 +562,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
 			buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-											   mode, NULL);
+											   mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 	}
 
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
new file mode 100644
index 0000000..219c696
--- /dev/null
+++ b/src/backend/access/undo/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/undo
+#
+# IDENTIFICATION
+#    src/backend/access/undo/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/undo
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = undolog.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
new file mode 100644
index 0000000..67b08e7
--- /dev/null
+++ b/src/backend/access/undo/undolog.c
@@ -0,0 +1,2599 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.c
+ *	  management of undo logs
+ *
+ * PostgreSQL undo log manager.  This module is responsible for managing the
+ * lifecycle of undo logs and their segment files, associating undo logs with
+ * backends, and allocating space within undo logs.
+ *
+ * For the code that reads and writes blocks of data, see undofile.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undolog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/session.h"
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogreader.h"
+#include "access/xlogutils.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablespace.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "pgstat.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "storage/standby.h"
+#include "storage/sync.h"
+#include "storage/undofile.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/varlena.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+/*
+ * Main control structure for undo log management in shared memory.
+ * UndoLogSlot objects are arranged in a fixed-size array, with no particular
+ * ordering.
+ */
+typedef struct UndoLogSharedData
+{
+	UndoLogNumber	free_lists[UndoPersistenceLevels];
+	UndoLogNumber	low_logno;
+	UndoLogNumber	next_logno;
+	UndoLogNumber	nslots;
+	UndoLogSlot		slots[FLEXIBLE_ARRAY_MEMBER];
+} UndoLogSharedData;
+
+/* The shared memory region that all backends are attach to. */
+UndoLogSharedData *UndoLogShared;
+
+undologtable_hash *undologtable_cache;
+
+/* GUC variables */
+char	   *undo_tablespaces = NULL;
+
+static UndoLogSlot *find_undo_log_slot(UndoLogNumber logno, bool locked);
+static UndoLogSlot *allocate_undo_log_slot(void);
+static void free_undo_log_slot(UndoLogSlot *log);
+static void attach_undo_log(UndoPersistence level, Oid tablespace);
+static void detach_current_undo_log(UndoPersistence level, bool full);
+static void extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end);
+static void undo_log_before_exit(int code, Datum value);
+static void forget_undo_buffers(int logno, UndoLogOffset old_discard,
+								UndoLogOffset new_discard,
+								bool drop_tail);
+static bool choose_undo_tablespace(bool force_detach, Oid *oid);
+
+PG_FUNCTION_INFO_V1(pg_stat_get_undo_logs);
+
+/*
+ * How many undo logs can be active at a time?  This creates a theoretical
+ * maximum amount of undo data that can exist, but if we set it to a multiple
+ * of the maximum number of backends it will be a very high limit.
+ * Alternative designs involving demand paging or dynamic shared memory could
+ * remove this limit but would be complicated.
+ */
+static inline size_t
+UndoLogNumSlots(void)
+{
+	return MaxBackends * 4;
+}
+
+/*
+ * Return the amount of traditional shmem required for undo log management.
+ */
+Size
+UndoLogShmemSize(void)
+{
+	return sizeof(UndoLogSharedData) +
+		UndoLogNumSlots() * sizeof(UndoLogSlot);
+}
+
+/*
+ * Initialize the undo log subsystem.  Called in each backend.
+ */
+void
+UndoLogShmemInit(void)
+{
+	bool found;
+
+	UndoLogShared = (UndoLogSharedData *)
+		ShmemInitStruct("UndoLogShared", UndoLogShmemSize(), &found);
+
+	/* The postmaster initialized the shared memory state. */
+	if (!IsUnderPostmaster)
+	{
+		int		i;
+
+		Assert(!found);
+
+		/*
+		 * We start with no active undo logs.  StartUpUndoLogs() will recreate
+		 * the undo logs that were known at the last checkpoint.
+		 */
+		memset(UndoLogShared, 0, sizeof(*UndoLogShared));
+		UndoLogShared->nslots = UndoLogNumSlots();
+		for (i = 0; i < UndoPersistenceLevels; ++i)
+			UndoLogShared->free_lists[i] = InvalidUndoLogNumber;
+		for (i = 0; i < UndoLogShared->nslots; ++i)
+		{
+			memset(&UndoLogShared->slots[i], 0, sizeof(UndoLogShared->slots[i]));
+			UndoLogShared->slots[i].logno = InvalidUndoLogNumber;
+			LWLockInitialize(&UndoLogShared->slots[i].mutex,
+							 LWTRANCHE_UNDOLOG);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
+							 LWTRANCHE_UNDODISCARD);
+		}
+	}
+	else
+		Assert(found);
+
+	/* All backends prepare their per-backend lookup table. */
+	undologtable_cache = undologtable_create(TopMemoryContext,
+											 UndoLogNumSlots(),
+											 NULL);
+}
+
+void
+UndoLogInit(void)
+{
+	before_shmem_exit(undo_log_before_exit, 0);
+}
+
+/*
+ * Figure out which directory holds an undo log based on tablespace.
+ */
+void
+UndoLogDirectory(Oid tablespace, char *dir)
+{
+	if (tablespace == DEFAULTTABLESPACE_OID ||
+		tablespace == InvalidOid)
+		snprintf(dir, MAXPGPATH, "base/undo");
+	else
+		snprintf(dir, MAXPGPATH, "pg_tblspc/%u/%s/undo",
+				 tablespace, TABLESPACE_VERSION_DIRECTORY);
+}
+
+/*
+ * Compute the pathname to use for an undo log segment file.
+ */
+void
+UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace, char *path)
+{
+	char		dir[MAXPGPATH];
+
+	/* Figure out which directory holds the segment, based on tablespace. */
+	UndoLogDirectory(tablespace, dir);
+
+	/*
+	 * Build the path from log number and offset.  The pathname is the
+	 * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+	 * period inserted between the components.
+	 */
+	snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+			 segno * UndoLogSegmentSize);
+}
+
+/*
+ * Iterate through the set of currently active logs.  Pass in NULL to get the
+ * first undo log.  NULL indicates the end of the set of logs.  The caller
+ * must lock the returned log before accessing its members, and must skip if
+ * logno is not valid.
+ */
+UndoLogSlot *
+UndoLogNextSlot(UndoLogSlot *slot)
+{
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+	for (;;)
+	{
+		/* Advance to the next log. */
+		if (slot == NULL)
+		{
+			/* Start at the beginning. */
+			slot = &UndoLogShared->slots[0];
+		}
+		else if (++slot == &UndoLogShared->slots[UndoLogShared->nslots])
+		{
+			/* Past the end. */
+			slot = NULL;
+			break;
+		}
+		/* Have we found a slot with a valid log? */
+		if (slot->logno != InvalidUndoLogNumber)
+			break;
+	}
+	LWLockRelease(UndoLogLock);
+
+	/* XXX: erm, which lock should the caller hold!? */
+	return slot;
+}
+
+/*
+ * Check if an undo log position has been discarded.  'point' must be an undo
+ * log pointer that was allocated at some point in the past, otherwise the
+ * result is undefined.
+ */
+bool
+UndoLogIsDiscarded(UndoRecPtr point)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(point);
+	UndoLogSlot *slot;
+	bool	result;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * If we couldn't find the undo log number, then it must be entirely
+	 * discarded.
+	 */
+	if (slot == NULL)
+		return true;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (unlikely(logno != slot->logno))
+	{
+		/*
+		 * The undo log has been entirely discarded since we looked it up, and
+		 * the UndoLogSlot is now unused or being used for some other undo
+		 * log.  That means that any pointer within it must be discarded.
+		 */
+		result = true;
+	}
+	else
+	{
+		/* Check if this point is before the discard pointer. */
+		result = UndoRecPtrGetOffset(point) < slot->meta.discard;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * Fetch the previous transaction's start undo record point.
+ */
+UndoRecPtr
+UndoLogGetLastXactStartPoint(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	uint64 last_xact_start = 0;
+
+	if (unlikely(slot == NULL))
+		return InvalidUndoRecPtr;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: review */
+	last_xact_start = slot->meta.unlogged.last_xact_start;
+	LWLockRelease(&slot->mutex);
+
+	if (last_xact_start == 0)
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, last_xact_start);
+}
+
+/*
+ * Detach from the undo log we are currently attached to, returning it to the
+ * appropriate free list if it still has space.
+ */
+static void
+detach_current_undo_log(UndoPersistence persistence, bool full)
+{
+	UndoLogSlot *slot;
+
+	slot = CurrentSession->attached_undo_slots[persistence];
+
+	Assert(slot != NULL);
+
+	CurrentSession->attached_undo_slots[persistence] = NULL;
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = InvalidPid;
+	slot->meta.unlogged.xid = InvalidTransactionId;
+	if (full)
+		slot->meta.status = UNDO_LOG_STATUS_FULL;
+	LWLockRelease(&slot->mutex);
+
+	/* Push back onto the appropriate free list, unless it's full. */
+	if (!full)
+	{
+		LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+		slot->next_free = UndoLogShared->free_lists[persistence];
+		UndoLogShared->free_lists[persistence] = slot->logno;
+		LWLockRelease(UndoLogLock);
+	}
+}
+
+/*
+ * Exit handler, detaching from all undo logs.
+ */
+static void
+undo_log_before_exit(int code, Datum arg)
+{
+	int		i;
+
+	if (!CurrentSession)
+		return;
+
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+	{
+		if (CurrentSession->attached_undo_slots[i] != NULL)
+			detach_current_undo_log(i, false);
+	}
+}
+
+/*
+ * Create a new empty segment file on disk for the byte starting at 'end'.
+ */
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+							UndoLogOffset end)
+{
+	struct stat	stat_buffer;
+	off_t	size;
+	char	path[MAXPGPATH];
+	void   *zeroes;
+	size_t	nzeroes = 8192;
+	int		fd;
+
+	UndoLogSegmentPath(logno, end / UndoLogSegmentSize, tablespace, path);
+
+	/*
+	 * Create and fully allocate a new file.  If we crashed and recovered
+	 * then the file might already exist, so use flags that tolerate that.
+	 * It's also possible that it exists but is too short, in which case
+	 * we'll write the rest.  We don't really care what's in the file, we
+	 * just want to make sure that the filesystem has allocated physical
+	 * blocks for it, so that non-COW filesystems will report ENOSPC now
+	 * rather than later when the space is needed and we'll avoid creating
+	 * files with holes.
+	 */
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0 && tablespace != 0)
+	{
+		char undo_path[MAXPGPATH];
+
+		/* Try creating the undo directory for this tablespace. */
+		UndoLogDirectory(tablespace, undo_path);
+		if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+		{
+			char	   *parentdir;
+
+			if (errno != ENOENT || !InRecovery)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+
+			/*
+			 * In recovery, it's possible that the tablespace directory
+			 * doesn't exist because a later WAL record removed the whole
+			 * tablespace.  In that case we create a regular directory to
+			 * stand in for it.  This is similar to the logic in
+			 * TablespaceCreateDbspace().
+			 */
+
+			/* create two parents up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			/* create one parent up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+		}
+
+		fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	}
+	if (fd < 0)
+		elog(ERROR, "could not create new file \"%s\": %m", path);
+	if (fstat(fd, &stat_buffer) < 0)
+		elog(ERROR, "could not stat \"%s\": %m", path);
+	size = stat_buffer.st_size;
+
+	/* A buffer full of zeroes we'll use to fill up new segment files. */
+	zeroes = palloc0(nzeroes);
+
+	while (size < UndoLogSegmentSize)
+	{
+		ssize_t written;
+
+		written = write(fd, zeroes, Min(nzeroes, UndoLogSegmentSize - size));
+		if (written < 0)
+			elog(ERROR, "cannot initialize undo log segment file \"%s\": %m",
+				 path);
+		size += written;
+	}
+
+	/* Flush the contents of the file to disk before the next checkpoint. */
+	undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
+
+	CloseTransientFile(fd);
+
+	pfree(zeroes);
+
+	elog(DEBUG1, "created undo segment \"%s\"", path);
+}
+
+/*
+ * Create a new undo segment, when it is unexpectedly not present.
+ */
+void
+UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno)
+{
+	Assert(InRecovery);
+	allocate_empty_undo_segment(logno, tablespace, segno * UndoLogSegmentSize);
+}
+
+/*
+ * Create and zero-fill a new segment for a given undo log number.
+ */
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+	UndoLogSlot *slot;
+	size_t		end;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/* TODO review interlocking */
+
+	Assert(slot != NULL);
+	Assert(slot->meta.end % UndoLogSegmentSize == 0);
+	Assert(new_end % UndoLogSegmentSize == 0);
+	Assert(CurrentSession->attached_undo_slots[slot->meta.persistence] == slot || InRecovery);
+
+	/*
+	 * Create all the segments needed to increase 'end' to the requested
+	 * size.  This is quite expensive, so we will try to avoid it completely
+	 * by renaming files into place in UndoLogDiscard() instead.
+	 */
+	end = slot->meta.end;
+	while (end < new_end)
+	{
+		allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/*
+	 * If we're not in recovery, we need to WAL-log the creation of the new
+	 * file(s).  We do that after the above filesystem modifications, in
+	 * violation of the data-before-WAL rule as exempted by
+	 * src/backend/access/transam/README.  This means that it's possible for
+	 * us to crash having made some or all of the filesystem changes but
+	 * before WAL logging, but in that case we'll eventually try to create the
+	 * same segment(s) again, which is tolerated.
+	 */
+	if (!InRecovery)
+	{
+		xl_undolog_extend xlrec;
+		XLogRecPtr	ptr;
+
+		xlrec.logno = logno;
+		xlrec.end = end;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+		XLogFlush(ptr);
+	}
+
+	/*
+	 * We didn't need to acquire the mutex to read 'end' above because only
+	 * we write to it.  But we need the mutex to update it, because the
+	 * checkpointer might read it concurrently.
+	 *
+	 * XXX It's possible for meta.end to be higher already during
+	 * recovery, because of the timing of a checkpoint; in that case we did
+	 * nothing above and we shouldn't update shmem here.  That interaction
+	 * needs more analysis.
+	 */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (slot->meta.end < end)
+		slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * This function must be called before all of the undo log activity that will
+ * be covered by a single WAL record.
+ */
+void
+UndoLogBeginInsert(UndoLogAllocContext *context,
+				   UndoPersistence persistence,
+				   XLogReaderState *xlog_record)
+{
+	context->try_location = InvalidUndoRecPtr;
+	context->persistence = persistence;
+
+	/*
+	 * Tell UndoLogAllocate() to capture undo log meta-data before-change
+	 * images, so that UndoLogRegister() can find them and they can be written
+	 * to the WAL once per checkpoint.
+	 */
+	context->num_meta_data_images = 0;
+
+	/*
+	 * Tell UndoLogAllocateInRecovery() that we don't know which undo log to
+	 * allocate in yet, and to start its search for registered blocks at
+	 * the lowest-numbered block_id.
+	 */
+	context->xlog_record = xlog_record;
+	context->recovery_logno = InvalidUndoLogNumber;
+	context->recovery_block_id = 0;
+}
+
+/*
+ * Get an insertion point that is guaranteed to be backed by enough space to
+ * hold 'size' bytes of data.  To actually write into the undo log, client
+ * code should call this first and then use bufmgr routines to access buffers
+ * and provide WAL logs and redo handlers.  In other words, while this module
+ * looks after making sure the undo log has sufficient space and the undo meta
+ * data is crash safe, the *contents* of the undo log and (indirectly) the
+ * insertion point are the responsibility of client code.
+ *
+ * A suggested insertion point can optionally be passed in as 'try_location',
+ * and will be returned if possible.  If not InvalidUndoRecPtr, it must fall
+ * with, or exactly one byte after, the most recent allocation for the same
+ * persistence level.  This interface allows for a series of allocation to be
+ * made without committing to using the space yet; call UndoLogAdvance() to
+ * actually advance the insert pointer.
+ *
+ * Return an undo log insertion point that can be converted to a buffer tag
+ * and an insertion point within a buffer page.
+ */
+UndoRecPtr
+UndoLogAllocate(UndoLogAllocContext *context,
+				uint16 size,
+				bool *need_xact_header,
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
+{
+	Session *session = CurrentSession;
+	UndoLogSlot *slot;
+	UndoLogOffset new_insert;
+	TransactionId logxid;
+
+	slot = CurrentSession->attached_undo_slots[context->persistence];
+
+	/*
+	 * We may need to attach to an undo log, either because this is the first
+	 * time this backend as needed to write to an undo log at all or because
+	 * the undo_tablespaces GUC was changed.  When doing that, we'll need
+	 * interlocking against tablespaces being concurrently dropped.
+	 */
+
+ retry:
+	/* See if we need to check the undo_tablespaces GUC. */
+	if (unlikely(session->need_to_choose_undo_tablespace || slot == NULL))
+	{
+		Oid		tablespace;
+		bool	need_to_unlock;
+
+		need_to_unlock =
+			choose_undo_tablespace(session->need_to_choose_undo_tablespace,
+								   &tablespace);
+		attach_undo_log(context->persistence, tablespace);
+		if (need_to_unlock)
+			LWLockRelease(TablespaceCreateLock);
+		slot = CurrentSession->attached_undo_slots[context->persistence];
+		session->need_to_choose_undo_tablespace = false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	logxid = slot->meta.unlogged.xid;
+
+	if (logxid != GetTopTransactionId())
+	{
+		/*
+		 * While we have the lock, check if we have been forcibly detached by
+		 * DROP TABLESPACE.  That can only happen between transactions (see
+		 * DropUndoLogsInsTablespace()).
+		 */
+		if (slot->pid == InvalidPid)
+		{
+			LWLockRelease(&slot->mutex);
+			slot = NULL;
+			goto retry;
+		}
+		slot->meta.unlogged.xid = GetTopTransactionId();
+		if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+		{
+			slot->meta.unlogged.last_xact_start =
+				slot->meta.unlogged.this_xact_start;
+			slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+		}
+		LWLockRelease(&slot->mutex);
+	}
+	else
+	{
+		LWLockRelease(&slot->mutex);
+	}
+
+	/*
+	 * 'size' is expressed in usable non-header bytes.  Figure out how far we
+	 * have to move insert to create space for 'size' usable bytes, stepping
+	 * over any intervening headers.
+	 */
+	Assert(slot->meta.unlogged.insert % BLCKSZ >= UndoLogBlockHeaderSize);
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+
+		Assert(UndoRecPtrGetLogNo(context->try_location) == slot->logno);
+		Assert(try_offset <= slot->meta.end);
+		new_insert = UndoLogOffsetPlusUsableBytes(try_offset, size);
+	}
+	else
+	{
+		new_insert = UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert,
+												  size);
+	}
+	Assert(new_insert % BLCKSZ >= UndoLogBlockHeaderSize);
+
+	/*
+	 * We don't need to acquire log->mutex to read log->meta.insert and
+	 * log->meta.end, because this backend is the only one that can
+	 * modify them.
+	 */
+	if (unlikely(new_insert > slot->meta.end))
+	{
+		if (new_insert > UndoLogMaxSize)
+		{
+			/* This undo log is entirely full.  Get a new one. */
+			if (logxid == GetTopTransactionId())
+			{
+				/*
+				 * If the same transaction is split over two undo logs then
+				 * store the previous log number in new log.  See detailed
+				 * comments in undorecord.c file header.
+				 */
+				*prevlog_xact_start =
+					MakeUndoRecPtr(slot->logno,
+								   slot->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+			}
+			elog(DEBUG1, "undo log %u is full, switching to a new one", slot->logno);
+			slot = NULL;
+			detach_current_undo_log(context->persistence, true);
+			context->try_location = InvalidUndoRecPtr;
+			goto retry;
+		}
+		/*
+		 * Extend the end of this undo log to cover new_insert (in other words
+		 * round up to the segment size).
+		 */
+		extend_undo_log(slot->logno,
+						new_insert + UndoLogSegmentSize -
+						new_insert % UndoLogSegmentSize);
+		Assert(new_insert <= slot->meta.end);
+	}
+
+	/*
+	 * Create a back-up image of the unlogged part of the undo log's
+	 * meta-data, if we haven't already done so since UndoLogBeginInsert() (ie
+	 * for the WAL record that this undo allocation will be replayed by).
+	 */
+	if (context->num_meta_data_images == 0 ||
+		context->meta_data_images[context->num_meta_data_images - 1].logno != slot->logno)
+	{
+		if (context->num_meta_data_images >= MAX_META_DATA_IMAGES)
+			elog(ERROR, "too many undo log meta data images");
+		context->meta_data_images[context->num_meta_data_images].logno = slot->logno;
+		context->meta_data_images[context->num_meta_data_images++].data = slot->meta.unlogged;
+	}
+
+	/*
+	 * If no try_location was passed in, or if we switched logs, then we'll
+	 * return the current insertion point.
+	 */
+	if (context->try_location == InvalidUndoRecPtr)
+		context->try_location = MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+
+	/* Is this location the first in this undo log for a transaction? */
+	*need_xact_header =
+		UndoRecPtrGetOffset(context->try_location) == slot->meta.unlogged.this_xact_start;
+	*last_xact_start =
+		MakeUndoRecPtr(slot->logno, slot->meta.unlogged.last_xact_start);
+
+	return context->try_location;
+}
+
+void
+UndoLogRegister(UndoLogAllocContext *context, uint8 block_id, UndoLogNumber logno)
+{
+	int		i;
+
+	for (i = 0; i < context->num_meta_data_images; ++i)
+	{
+		if (context->meta_data_images[i].logno == logno)
+		{
+			XLogRegisterBufData(block_id,
+								(char *) &context->meta_data_images[i].data,
+								sizeof(context->meta_data_images[i].data));
+			return;
+		}
+	}
+}
+
+/*
+ * In recovery, we expect exactly the same sequence of allocation sizes, but
+ * we also need the WAL record that is being replayed so we can figure out
+ * where the undo space was allocated.
+ */
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+						  TransactionId xid,
+						  uint16 size,
+						  bool *need_xact_header,
+						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	Assert(InRecovery);
+
+	/*
+	 * Just as in UndoLogAllocate(), the caller may be extending an existing
+	 * allocation before committing with UndoLogAdvance().
+	 */
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+		UndoLogNumber logno = UndoRecPtrGetLogNo(context->try_location);
+
+		/*
+		 * You can only have a try_location on your second or later allocation
+		 * for a given WAL record.  It had better be in the same log as the
+		 * previous allocation for this WAL record (though it may not turn out
+		 * to have enough space, below).
+		 */
+		Assert(logno == context->recovery_logno);
+
+		/*
+		 * Any log extension triggered by UndoLogAllocate() must have been
+		 * replayed by now, so we can just check if this log has enough space,
+		 * and if so, return.
+		 */
+		slot = find_undo_log_slot(logno, false);
+		if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+		{
+			*need_xact_header = false;
+			return try_offset;
+		}
+
+		/* Full.  Ignore try_location and find the next log that was used. */
+		Assert(slot->meta.status == UNDO_LOG_STATUS_FULL);
+	}
+	else
+	{
+		/*
+		 * For now we only support one allocation per WAL record that doesn't
+		 * have a try_location (ie the first one).  We'll have to find out
+		 * which log was used first.
+		 */
+		Assert(context->recovery_logno == InvalidUndoLogNumber);
+	}
+
+	/*
+	 * In order to find the undo log that was used by UndoLogAllocate(), we
+	 * consult the list of registered blocks to figure out which undo logs
+	 * should be written to by this WAL record.
+	 */
+	while (context->recovery_block_id <= context->xlog_record->max_block_id)
+	{
+		DecodedBkpBlock *block;
+
+		/* We're looking for the first block referencing a new undo log. */
+		block = &context->xlog_record->blocks[context->recovery_block_id];
+		if (block->smgrid == SMGR_UNDO &&
+			block->rnode.dbNode == UndoLogDatabaseOid &&
+			block->rnode.relNode != context->recovery_logno)
+		{
+			UndoLogNumber logno = block->rnode.relNode;
+			const void *backup;
+			size_t backup_size;
+
+			/* We found a reference to a different (or first) undo log. */
+			slot = find_undo_log_slot(logno, false);
+
+			/*
+			 * Since on-line checkpoints capture an inconsistent snapshot of
+			 * undo log meta-data, we'll restore the unlogged part of the
+			 * meta-data image if one was attached to the WAL record (that is,
+			 * the members that don't have WAL records for every change
+			 * already).
+			 */
+			backup =
+				XLogRecGetBlockData(context->xlog_record,
+									context->recovery_block_id,
+									&backup_size);
+			if (unlikely(backup))
+			{
+				Assert(backup_size == sizeof(UndoLogUnloggedMetaData));
+
+				/* Restore the unlogged members from the backup-imaged. */
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				memcpy(&slot->meta.unlogged, backup, sizeof(UndoLogUnloggedMetaData));
+				LWLockRelease(&slot->mutex);
+			}
+			else
+			{
+				/*
+				 * Otherwise we need to do our own transaction tracking
+				 * whenever we see a new xid, to match the logic in
+				 * UndoLogAllocate().
+				 */
+				if (xid != slot->meta.unlogged.xid)
+				{
+					slot->meta.unlogged.xid = xid;
+					if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+						slot->meta.unlogged.last_xact_start =
+							slot->meta.unlogged.this_xact_start;
+					slot->meta.unlogged.this_xact_start =
+						slot->meta.unlogged.insert;
+				}
+			}
+
+			/* TODO: check locking against undo log slot recycling? */
+
+			/*
+			 * At this stage we should have an undo log that can handle this
+			 * allocation.  If we don't, something is screwed up.
+			 */
+			if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) > slot->meta.end)
+				elog(ERROR,
+					 "cannot allocate %d bytes in undo log %d",
+					 (int) size, slot->logno);
+
+			*need_xact_header =
+				context->try_location == InvalidUndoRecPtr &&
+				slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+			*last_xact_start = slot->meta.unlogged.last_xact_start;
+			context->recovery_logno = slot->logno;
+
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+
+			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
+			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+		}
+		++context->recovery_block_id;
+	}
+
+	/*
+	 * If we've run out of blocks to inspect, then we must have replayed a
+	 * different sequence of allocation sizes, or screwed up the
+	 * XLOG_UNDOLOG_EXTEND records, indicating a bug somewhere.
+	 */
+	elog(ERROR, "cannot determine undo log to allocate from");
+
+	return 0;		/* not reached */
+}
+
+/*
+ * Advance the insertion pointer in this context by 'size' usable (non-header)
+ * bytes.  This is the next place we'll try to allocate a record, if it fits.
+ * This is not committed to shared memory until after we've WAL-logged the
+ * record and UndoLogAdvanceFinal() is called.
+ */
+void
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+	context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+														 size);
+}
+
+/*
+ * Advance the insertion pointer to 'size' usable (non-header) bytes past
+ * insertion_point.
+ */
+void
+UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(insertion_point) ;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(InRecovery ||
+		   AmAttachedToUndoLogSlot(slot) ||
+		   slot->meta.status == UNDO_LOG_STATUS_FULL);
+
+	/*
+	 * The caller has the current insertion point, as returned by
+	 * UndoLogAllocate[InRecovery]().
+	 */
+	Assert(UndoRecPtrGetOffset(insertion_point) == slot->meta.unlogged.insert);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.insert =
+		UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size);
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * Advance the discard pointer in one undo log, discarding all undo data
+ * relating to one or more whole transactions.  The passed in undo pointer is
+ * the address of the oldest data that the called would like to keep, and the
+ * affected undo log is implied by this pointer, ie
+ * UndoRecPtrGetLogNo(discard_pointer).
+ *
+ * The caller asserts that there will be no attempts to access the undo log
+ * region being discarded after this moment.  This operation will cause the
+ * relevant buffers to be dropped immediately, without writing any data out to
+ * disk.  Any attempt to read the buffers (except a partial buffer at the end
+ * of this range which will remain) may result in IO errors, because the
+ * underlying segment file may have been physically removed.
+ *
+ * Return true if the discard point was updated, and false if nothing was done
+ * because the log precending the given point was already discarded.
+ *
+ * TODO: The return value is not yet reliable and the code still doesn't work
+ * correctly if called for the same undo log in two backends; more
+ * interlocking work required here.
+ */
+bool
+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(discard_point);
+	UndoLogOffset discard = UndoRecPtrGetOffset(discard_point);
+	UndoLogOffset old_discard;
+	UndoLogOffset end;
+	UndoLogSlot *slot;
+	int			segno;
+	int			new_segno;
+	bool		need_to_flush_wal = false;
+	bool		entirely_discarded = false;
+
+	slot = find_undo_log_slot(logno, false);
+	if (unlikely(slot == NULL))
+	{
+		/* Already discarded (entirely). */
+		return false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (unlikely(slot->logno != logno || discard <= slot->meta.discard))
+	{
+		/*
+		 * Already discarded entirely and the slot has been recycled, or up
+		 * to this point).
+		 */
+		LWLockRelease(&slot->mutex);
+		return false;
+	}
+	if (discard > slot->meta.unlogged.insert)
+		elog(ERROR, "cannot move discard point past insert point");
+	old_discard = slot->meta.discard;
+	end = slot->meta.end;
+	/* Are we discarding the last remaining data in a log marked as full? */
+	if (slot->meta.status == UNDO_LOG_STATUS_FULL &&
+		discard == slot->meta.unlogged.insert)
+	{
+		/*
+		 * Adjust the discard and insert pointers so that the final segment is
+		 * deleted from disk, and remember not to recycle it.
+		 */
+		entirely_discarded = true;
+		/* TODO: Check if the following line is replayed correctly */
+		slot->meta.unlogged.insert = slot->meta.end;
+		discard = slot->meta.end;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * TODO: I think we need a new lock just for this phase, so that buffer
+	 * dropping and IO are done by only one backend if a superuser command and
+	 * a discard worker both run this!
+	 */
+
+	/*
+	 * Drop all buffers holding this undo data out of the buffer pool (except
+	 * the last one, if the new location is in the middle of it somewhere), so
+	 * that the contained data doesn't ever touch the disk.  The caller
+	 * promises that this data will not be needed again.  We have to drop the
+	 * buffers from the buffer pool before removing files, otherwise a
+	 * concurrent session might try to write the block to evict the buffer.
+	 */
+	forget_undo_buffers(logno, old_discard, discard, entirely_discarded);
+
+	/*
+	 * Check if we crossed a segment boundary and need to do some synchronous
+	 * filesystem operations.
+	 */
+	segno = old_discard / UndoLogSegmentSize;
+	new_segno = discard / UndoLogSegmentSize;
+	if (segno < new_segno)
+	{
+		int		recycle;
+		UndoLogOffset pointer;
+
+		/*
+		 * We always WAL-log discards, but we only need to flush the WAL if we
+		 * have performed a filesystem operation.
+		 */
+		need_to_flush_wal = true;
+
+		/*
+		 * XXX When we rename or unlink a file, it's possible that some
+		 * backend still has it open because it has recently read a page from
+		 * it.  smgr/undofile.c in any such backend will eventually close it,
+		 * because it considers that fd to belong to the file with the name
+		 * that we're unlinking or renaming and it doesn't like to keep more
+		 * than one open at a time.  No backend should ever try to read from
+		 * such a file descriptor; that is what it means when we say that the
+		 * caller of UndoLogDiscard() asserts that there will be no attempts
+		 * to access the discarded range of undo log.  In the case of a
+		 * rename, if a backend were to attempt to read undo data in the range
+		 * being discarded, it would read entirely the wrong data.
+		 */
+
+		/*
+		 * How many segments should we recycle (= rename from tail position to
+		 * head position)?  For now it's always 1 unless there is already a
+		 * spare one, but we could have an adaptive algorithm that recycles
+		 * multiple segments at a time and pays just one fsync().
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if ((slot->meta.end - slot->meta.unlogged.insert) < UndoLogSegmentSize &&
+			slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+			recycle = 1;
+		else
+			recycle = 0;
+		LWLockRelease(&slot->mutex);
+
+		/* Rewind to the start of the segment. */
+		pointer = segno * UndoLogSegmentSize;
+
+		while (pointer < new_segno * UndoLogSegmentSize)
+		{
+			char	discard_path[MAXPGPATH];
+
+			/* Tell the checkpointer that the file is going away. */
+			undofile_forget_sync(logno, pointer / UndoLogSegmentSize,
+								 slot->meta.tablespace);
+
+			UndoLogSegmentPath(logno, pointer / UndoLogSegmentSize,
+							   slot->meta.tablespace, discard_path);
+
+			/* Can we recycle the oldest segment? */
+			if (recycle > 0)
+			{
+				char	recycle_path[MAXPGPATH];
+
+				/*
+				 * End points one byte past the end of the current undo space,
+				 * ie to the first byte of the segment file we want to create.
+				 */
+				UndoLogSegmentPath(logno, end / UndoLogSegmentSize,
+								   slot->meta.tablespace, recycle_path);
+				if (rename(discard_path, recycle_path) == 0)
+				{
+					elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+						 discard_path, recycle_path);
+					end += UndoLogSegmentSize;
+					--recycle;
+				}
+				else
+				{
+					elog(ERROR, "could not rename \"%s\" to \"%s\": %m",
+						 discard_path, recycle_path);
+				}
+			}
+			else
+			{
+				if (unlink(discard_path) == 0)
+					elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+				else
+					elog(ERROR, "could not unlink \"%s\": %m", discard_path);
+			}
+			pointer += UndoLogSegmentSize;
+		}
+	}
+
+	/* WAL log the discard. */
+	{
+		xl_undolog_discard xlrec;
+		XLogRecPtr ptr;
+
+		xlrec.logno = logno;
+		xlrec.discard = discard;
+		xlrec.end = end;
+		xlrec.latestxid = xid;
+		xlrec.entirely_discarded = entirely_discarded;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_DISCARD);
+
+		if (need_to_flush_wal)
+			XLogFlush(ptr);
+	}
+
+	/* Update shmem to show the new discard and end pointers. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (entirely_discarded)
+		free_undo_log_slot(slot);
+
+	return true;
+}
+
+/*
+ * Return an UndoRecPtr to the oldest valid data in an undo log, or
+ * InvalidUndoRecPtr if it is empty.
+ */
+UndoRecPtr
+UndoLogGetOldestRecord(UndoLogNumber logno, bool *full)
+{
+	UndoLogSlot *slot;
+	UndoRecPtr	result;
+
+	/* Try to find the slot for this undo log number. */
+	slot = find_undo_log_slot(logno, false);
+	if (slot == NULL)
+	{
+		/* It's unknown to us, so we assume it's been entirely discarded. */
+		if (full)
+			*full = true;
+		return InvalidUndoRecPtr;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->logno != logno)
+	{
+		/* It's been recycled.  SO it must have been entirely discarded. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = true;
+	}
+	else if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		/* It's empty, so there is no oldest record pointer to return. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	else
+	{
+		/* There is a record here! */
+		result = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLogSlot(slot));
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&slot->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
+ * Return the Next insert location.  This will also validate the input xid
+ * if latest insert point is not for the same transaction id then this will
+ * return Invalid Undo pointer.
+ */
+UndoRecPtr
+UndoLogGetNextInsertPtr(UndoLogNumber logno, TransactionId xid)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	TransactionId	logxid;
+	UndoRecPtr	insert;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	insert = slot->meta.unlogged.insert;
+	logxid = slot->meta.unlogged.xid;
+	LWLockRelease(&slot->mutex);
+
+	if (TransactionIdIsValid(logxid) && !TransactionIdEquals(logxid, xid))
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, insert);
+}
+
+/*
+ * Delete unreachable files under pg_undo.  Any files corresponding to LSN
+ * positions before the previous checkpoint are no longer needed.
+ */
+static void
+CleanUpUndoCheckPointFiles(XLogRecPtr checkPointRedo)
+{
+	DIR	   *dir;
+	struct dirent *de;
+	char	path[MAXPGPATH];
+	char	oldest_path[MAXPGPATH];
+
+	/*
+	 * If a base backup is in progress, we can't delete any checkpoint
+	 * snapshot files because one of them corresponds to the backup label but
+	 * there could be any number of checkpoints during the backup.
+	 */
+	if (BackupInProgress())
+		return;
+
+	/* Otherwise keep only those >= the previous checkpoint's redo point. */
+	snprintf(oldest_path, MAXPGPATH, "%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	dir = AllocateDir("pg_undo");
+	while ((de = ReadDir(dir, "pg_undo")) != NULL)
+	{
+		/*
+		 * Assume that fixed width uppercase hex strings sort the same way as
+		 * the values they represent, so we can use strcmp to identify undo
+		 * log snapshot files corresponding to checkpoints that we don't need
+		 * anymore.  This assumption holds for ASCII.
+		 */
+		if (!(strlen(de->d_name) == UNDO_CHECKPOINT_FILENAME_LENGTH))
+			continue;
+
+		if (UndoCheckPointFilenamePrecedes(de->d_name, oldest_path))
+		{
+			snprintf(path, MAXPGPATH, "pg_undo/%s", de->d_name);
+			if (unlink(path) != 0)
+				elog(ERROR, "could not unlink file \"%s\": %m", path);
+		}
+	}
+	FreeDir(dir);
+}
+
+/*
+ * Write out the undo log meta data to the pg_undo directory.  The actual
+ * contents of undo logs is in shared buffers and therefore handled by
+ * CheckPointBuffers(), but here we record the table of undo logs and their
+ * properties.
+ */
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
+{
+	UndoLogMetaData *serialized = NULL;
+	size_t	serialized_size = 0;
+	char   *data;
+	char	path[MAXPGPATH];
+	UndoLogNumber num_logs;
+	int		fd;
+	int		i;
+	pg_crc32c crc;
+
+	/*
+	 * We acquire UndoLogLock to prevent any undo logs from being created or
+	 * discarded while we build a snapshot of them.  This isn't expected to
+	 * take long on a healthy system because the number of active logs should
+	 * be around the number of backends.  Holding this lock won't prevent
+	 * concurrent access to the undo log, except when segments need to be
+	 * added or removed.
+	 */
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+
+	/*
+	 * Rather than doing the file IO while we hold locks, we'll copy the
+	 * meta-data into a palloc'd buffer.
+	 */
+	serialized_size = sizeof(UndoLogMetaData) * UndoLogNumSlots();
+	serialized = (UndoLogMetaData *) palloc0(serialized_size);
+
+	/* Scan through all slots looking for non-empty ones. */
+	num_logs = 0;
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+
+		/* Skip empty slots. */
+		if (slot->logno == InvalidUndoLogNumber)
+			continue;
+
+		/* Capture snapshot while holding each mutex. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		serialized[num_logs++] = slot->meta;
+		LWLockRelease(&slot->mutex);
+	}
+
+	LWLockRelease(UndoLogLock);
+
+	/* Dump into a file under pg_undo. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE);
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not create file \"%s\": %m", path)));
+
+	/* Compute header checksum. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the number of active logs + crc. */
+	if ((write(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno)) ||
+		(write(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno)) != sizeof(UndoLogShared->next_logno)) ||
+		(write(fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+	/* Write out the meta data for all active undo logs. */
+	data = (char *) serialized;
+	INIT_CRC32C(crc);
+	serialized_size = num_logs * sizeof(UndoLogMetaData);
+	while (serialized_size > 0)
+	{
+		ssize_t written;
+
+		written = write(fd, data, serialized_size);
+		if (written < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write to file \"%s\": %m", path)));
+		COMP_CRC32C(crc, data, written);
+		serialized_size -= written;
+		data += written;
+	}
+	FIN_CRC32C(crc);
+
+	if (write(fd, &crc, sizeof(crc)) != sizeof(crc))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+
+	/* Flush file and directory entry. */
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC);
+	pg_fsync(fd);
+	if (CloseTransientFile(fd) < 0)
+		ereport(data_sync_elevel(ERROR),
+				(errcode_for_file_access(),
+				 errmsg("could not close file \"%s\": %m", path)));
+	fsync_fname("pg_undo", true);
+	pgstat_report_wait_end();
+
+	if (serialized)
+		pfree(serialized);
+
+	CleanUpUndoCheckPointFiles(priorCheckPointRedo);
+}
+
+void
+StartupUndoLogs(XLogRecPtr checkPointRedo)
+{
+	char	path[MAXPGPATH];
+	int		i;
+	int		fd;
+	int		nlogs;
+	pg_crc32c crc;
+	pg_crc32c new_crc;
+
+	/* If initdb is calling, there is no file to read yet. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/* Open the pg_undo file corresponding to the given checkpoint. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_READ);
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
+
+	/* Read the active log number range. */
+	if ((read(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno))
+		 != sizeof(UndoLogShared->low_logno)) ||
+		(read(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno))
+		 != sizeof(UndoLogShared->next_logno)) ||
+		(read(fd, &nlogs, sizeof(nlogs)) != sizeof(nlogs)) ||
+		(read(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+
+	/* Verify the header checksum. */
+	INIT_CRC32C(new_crc);
+	COMP_CRC32C(new_crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(new_crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(new_crc, &nlogs, sizeof(UndoLogShared->next_logno));
+	FIN_CRC32C(new_crc);
+
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	/*
+	 * We'll acquire UndoLogLock just because allocate_undo_log() asserts we
+	 * hold it (we don't actually expect concurrent access yet).
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* Initialize all the logs and set up the freelist. */
+	INIT_CRC32C(new_crc);
+	for (i = 0; i < nlogs; ++i)
+	{
+		ssize_t size;
+		UndoLogSlot *slot;
+
+		/*
+		 * Get a new UndoLogSlot.  If this checkpoint was created on a system
+		 * with a higher max_connections setting, it's theoretically possible
+		 * that we don't have enough space and cannot start up.
+		 */
+		slot = allocate_undo_log_slot();
+		if (!slot)
+			ereport(ERROR,
+					(errmsg("not enough undo log slots to recover from checkpoint: need at least %d, have %zu",
+							nlogs, UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections")));
+
+		/* Read in the meta data for this undo log. */
+		if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
+			elog(ERROR, "short read of pg_undo meta data in file \"%s\": %m (got %zu, wanted %zu)",
+				 path, size, sizeof(slot->meta));
+		COMP_CRC32C(new_crc, &slot->meta, sizeof(slot->meta));
+
+		/*
+		 * At normal start-up, or during recovery, all active undo logs start
+		 * out on the appropriate free list.
+		 */
+		slot->logno = slot->meta.logno;
+		slot->pid = InvalidPid;
+		slot->oldest_data = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+		{
+			slot->next_free = UndoLogShared->free_lists[slot->meta.persistence];
+			UndoLogShared->free_lists[slot->meta.persistence] = slot->logno;
+		}
+	}
+	FIN_CRC32C(new_crc);
+
+	LWLockRelease(UndoLogLock);
+
+	/* Verify body checksum. */
+	if (read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	CloseTransientFile(fd);
+	pgstat_report_wait_end();
+}
+
+/*
+ * Allocate a new UndoLogSlot object.
+ */
+static UndoLogSlot *
+allocate_undo_log_slot(void)
+{
+	UndoLogSlot *slot;
+	UndoLogNumber i;
+
+	Assert(LWLockHeldByMeInMode(UndoLogLock, LW_EXCLUSIVE));
+
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		slot = &UndoLogShared->slots[i];
+		if (slot->logno == InvalidUndoLogNumber)
+		{
+			memset(&slot->meta, 0, sizeof(slot->meta));
+			slot->pid = 0;
+			slot->oldest_xid = 0;
+			slot->oldest_xidepoch = 0;
+			slot->oldest_data =0;
+			slot->next_free = -1;
+			slot->logno = -1;
+			return slot;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Free an UndoLogSlot object in shared memory, so that it can be reused.
+ * This is a rare event, and has complications for all code paths that access
+ * slots.  Unless the current session is attached to the slot, it must be
+ * prepared for it to be freed and then potentially recycled for use by
+ * another log.  See UndoLogGetSlot().
+ */
+static void
+free_undo_log_slot(UndoLogSlot *slot)
+{
+	/*
+	 * When removing an undo log from a slot in shared memory, we acquire
+	 * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+	 * hold any one of those locks to prevent the slot from being recycled.
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno != InvalidUndoLogNumber);
+	slot->logno = InvalidUndoLogNumber;
+	memset(&slot->meta, 0, sizeof(slot->meta));
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * Find the UndoLogSlot object for a given log number.
+ *
+ * The caller may or may not already hold UndoLogLock, and should indicate
+ * this by passing 'locked'.  We'll acquire it in the slow path if necessary.
+ * If it is not held by the caller, the caller must deal with the possibility
+ * that the returned UndoLogSlot no longer contains the requested logno by the
+ * time it is accessed.
+ *
+ * To do that, one of the following approaches must be taken by the calling
+ * code:
+ *
+ * 1.  If the calling code knows that it is attached to this lock or is the
+ * recovery process, then there is no way for the slot to be recycled, so it's
+ * not necessary to check that the log number hasn't changed.  The slot cannot
+ * be recycled while a backend is attached.  It should probably assert that it
+ * is attached, however.
+ *
+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
+ *
+ * Return NULL if the undo log has been entirely discarded.  It is an error to
+ * ask for undo logs that have never been created.
+ */
+static UndoLogSlot *
+find_undo_log_slot(UndoLogNumber logno, bool locked)
+{
+	UndoLogSlot *result = NULL;
+	UndoLogTableEntry *entry;
+	bool	   found;
+
+	Assert(locked == LWLockHeldByMe(UndoLogLock));
+
+	/* First see if we already have it in our cache. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		result = entry->slot;
+	else
+	{
+		UndoLogNumber i;
+
+		/* Nope.  Linear search for the slot in shared memory. */
+		if (!locked)
+			LWLockAcquire(UndoLogLock, LW_SHARED);
+		for (i = 0; i < UndoLogNumSlots(); ++i)
+		{
+			if (UndoLogShared->slots[i].logno == logno)
+			{
+				/* Found it. */
+
+				/*
+				 * TODO: Should this function be usable in a critical section?
+				 * Would it make sense to detect that we are in a critical
+				 * section and just return the pointer to the log without
+				 * updating the cache, to avoid any chance of allocating
+				 * memory?
+				 */
+
+				entry = undologtable_insert(undologtable_cache, logno, &found);
+				entry->number = logno;
+				entry->slot = &UndoLogShared->slots[i];
+				entry->tablespace = entry->slot->meta.tablespace;
+				result = entry->slot;
+				break;
+			}
+		}
+
+		/*
+		 * If we didn't find it, then it must already have been entirely
+		 * discarded.  We create a negative cache entry so that we can answer
+		 * this question quickly next time.
+		 *
+		 * TODO: We could track the lowest known undo log number, to reduce
+		 * the negative cache entry bloat.
+		 */
+		if (result == NULL)
+		{
+			/*
+			 * Sanity check: the caller should not be asking about undo logs
+			 * that have never existed.
+			 */
+			if (logno >= UndoLogShared->next_logno)
+				elog(ERROR, "undo log %u hasn't been created yet", logno);
+			entry = undologtable_insert(undologtable_cache, logno, &found);
+			entry->number = logno;
+			entry->slot = NULL;
+			entry->tablespace = 0;
+		}
+		if (!locked)
+			LWLockRelease(UndoLogLock);
+	}
+
+	return result;
+}
+
+/*
+ * Get a pointer to an UndoLogSlot object corresponding to a given logno.
+ *
+ * In general, the caller must acquire the UndoLogSlot's mutex to access
+ * the contents, and at that time must consider that the logno might have
+ * changed because the undo log it contained has been entirely discarded.
+ *
+ * If the calling backend is currently attached to the undo log, that is not
+ * possible, because logs can only reach UNDO_LOG_STATUS_DISCARDED after first
+ * reaching UNDO_LOG_STATUS_FULL, and that only happens while detaching.
+ */
+UndoLogSlot *
+UndoLogGetSlot(UndoLogNumber logno, bool missing_ok)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL && !missing_ok)
+		elog(ERROR, "unknown undo log number %d", logno);
+
+	return slot;
+}
+
+/*
+ * Attach to a free undo log, creating a new one if required.
+ */
+static void
+attach_undo_log(UndoPersistence persistence, Oid tablespace)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber logno;
+	UndoLogNumber *place;
+
+	Assert(!InRecovery);
+	Assert(CurrentSession->attached_undo_slots[persistence] == NULL);
+
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/*
+	 * For now we have a simple linked list of unattached undo logs for each
+	 * persistence level.  We'll grovel though it to find something for the
+	 * tablespace you asked for.  If you're not using multiple tablespaces
+	 * it'll be able to pop one off the front.  We might need a hash table
+	 * keyed by tablespace if this simple scheme turns out to be too slow when
+	 * using many tablespaces and many undo logs, but that seems like an
+	 * unusual use case not worth optimizing for.
+	 */
+	place = &UndoLogShared->free_lists[persistence];
+	while (*place != InvalidUndoLogNumber)
+	{
+		UndoLogSlot *candidate = find_undo_log_slot(*place, true);
+
+		/*
+		 * There should never be an undo log on the freelist that has been
+		 * entirely discarded, or hasn't been created yet.  The persistence
+		 * level should match the freelist.
+		 */
+		if (unlikely(candidate == NULL))
+			elog(ERROR,
+				 "corrupted undo log freelist, no such undo log %u", *place);
+		if (unlikely(candidate->meta.persistence != persistence))
+			elog(ERROR,
+				 "corrupted undo log freelist, undo log %u with persistence %d found on freelist %d",
+				 *place, candidate->meta.persistence, persistence);
+
+		if (candidate->meta.tablespace == tablespace)
+		{
+			logno = *place;
+			slot = candidate;
+			*place = candidate->next_free;
+			break;
+		}
+		place = &candidate->next_free;
+	}
+
+	/*
+	 * If all existing undo logs for this tablespace and persistence level are
+	 * busy, we'll have to create a new one.
+	 */
+	if (slot == NULL)
+	{
+		if (UndoLogShared->next_logno > MaxUndoLogNumber)
+		{
+			/*
+			 * You've used up all 16 exabytes of undo log addressing space.
+			 * This is a difficult state to reach using only 16 exabytes of
+			 * WAL.
+			 */
+			elog(ERROR, "undo log address space exhausted");
+		}
+
+		/* Allocate a slot from the UndoLogSlot pool. */
+		slot = allocate_undo_log_slot();
+		if (unlikely(!slot))
+			ereport(ERROR,
+					(errmsg("could not create new undo log"),
+					 errdetail("The maximum number of active undo logs is %zu.",
+							   UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections.")));
+		slot->logno = logno = UndoLogShared->next_logno;
+
+		/*
+		 * The insert and discard pointers start after the first block's
+		 * header.  XXX That means that insert is > end for a short time in a
+		 * newly created undo log.  Is there any problem with that?
+		 */
+		slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+		slot->meta.discard = UndoLogBlockHeaderSize;
+
+		slot->meta.logno = logno;
+		slot->meta.tablespace = tablespace;
+		slot->meta.persistence = persistence;
+		slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+
+		/* Move the high log number pointer past this one. */
+		++UndoLogShared->next_logno;
+
+		/* WAL-log the creation of this new undo log. */
+		{
+			xl_undolog_create xlrec;
+
+			xlrec.logno = logno;
+			xlrec.tablespace = slot->meta.tablespace;
+			xlrec.persistence = slot->meta.persistence;
+
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+			XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_CREATE);
+		}
+
+		/*
+		 * This undo log has no segments.  UndoLogAllocate will create the
+		 * first one on demand.
+		 */
+	}
+	LWLockRelease(UndoLogLock);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = MyProcPid;
+	LWLockRelease(&slot->mutex);
+
+	CurrentSession->attached_undo_slots[persistence] = slot;
+}
+
+/* check_hook: validate new undo_tablespaces */
+bool
+check_undo_tablespaces(char **newval, void **extra, GucSource source)
+{
+	char	   *rawname;
+	List	   *namelist;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/*
+	 * Parse string into list of identifiers, just to check for
+	 * well-formedness (unfortunateley we can't validate the names in the
+	 * catalog yet).
+	 */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	/*
+	 * Make sure we aren't already in a transaction that has been assigned an
+	 * XID.  This ensures we don't detach from an undo log that we might have
+	 * started writing undo data into for this transaction.
+	 */
+	if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 (errmsg("undo_tablespaces cannot be changed while a transaction is in progress"))));
+	list_free(namelist);
+
+	return true;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_undo_tablespaces(const char *newval, void *extra)
+{
+	/*
+	 * This is normally called only when GetTopTransactionIdIfAny() ==
+	 * InvalidTransactionId (because you can't change undo_tablespaces in the
+	 * middle of a transaction that's been asigned an xid), but we can't
+	 * assert that because it's also called at the end of a transaction that's
+	 * rolling back, to reset the GUC if it was set inside the transaction.
+	 */
+
+	/* Tell UndoLogAllocate() to reexamine undo_tablespaces. */
+	if (CurrentSession)
+		CurrentSession->need_to_choose_undo_tablespace = true;
+}
+
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+	char   *rawname;
+	List   *namelist;
+	bool	need_to_unlock;
+	int		length;
+	int		i;
+
+	/* We need a modifiable copy of string. */
+	rawname = pstrdup(undo_tablespaces);
+
+	/* Break string into list of identifiers. */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+		elog(ERROR, "undo_tablespaces is unexpectedly malformed");
+
+	length = list_length(namelist);
+	if (length == 0 ||
+		(length == 1 && ((char *) linitial(namelist))[0] == '\0'))
+	{
+		/*
+		 * If it's an empty string, then we'll use the default tablespace.  No
+		 * locking is required because it can't be dropped.
+		 */
+		*tablespace = DEFAULTTABLESPACE_OID;
+		need_to_unlock = false;
+	}
+	else
+	{
+		/*
+		 * Choose an OID using our pid, so that if several backends have the
+		 * same multi-tablespace setting they'll spread out.  We could easily
+		 * do better than this if more serious load balancing is judged
+		 * useful.
+		 */
+		int		index = MyProcPid % length;
+		int		first_index = index;
+		Oid		oid = InvalidOid;
+
+		/*
+		 * Take the tablespace create/drop lock while we look the name up.
+		 * This prevents the tablespace from being dropped while we're trying
+		 * to resolve the name, or while the called is trying to create an
+		 * undo log in it.  The caller will have to release this lock.
+		 */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		for (;;)
+		{
+			const char *name = list_nth(namelist, index);
+
+			oid = get_tablespace_oid(name, true);
+			if (oid == InvalidOid)
+			{
+				/* Unknown tablespace, try the next one. */
+				index = (index + 1) % length;
+				/*
+				 * But if we've tried them all, it's time to complain.  We'll
+				 * arbitrarily complain about the last one we tried in the
+				 * error message.
+				 */
+				if (index == first_index)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", name),
+							 errhint("Create the tablespace or set undo_tablespaces to a valid or empty list.")));
+				continue;
+			}
+			if (oid == GLOBALTABLESPACE_OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("undo logs cannot be placed in pg_global tablespace")));
+			/* If we got here we succeeded in finding one. */
+			break;
+		}
+
+		Assert(oid != InvalidOid);
+		*tablespace = oid;
+		need_to_unlock = true;
+	}
+
+	/*
+	 * If we came here because the user changed undo_tablesaces, then detach
+	 * from any undo logs we happen to be attached to.
+	 */
+	if (force_detach)
+	{
+		for (i = 0; i < UndoPersistenceLevels; ++i)
+		{
+			UndoLogSlot *slot = CurrentSession->attached_undo_slots[i];
+
+			if (slot != NULL)
+			{
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				slot->pid = InvalidPid;
+				slot->meta.unlogged.xid = InvalidTransactionId;
+				LWLockRelease(&slot->mutex);
+
+				LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+				slot->next_free = UndoLogShared->free_lists[i];
+				UndoLogShared->free_lists[i] = slot->logno;
+				LWLockRelease(UndoLogLock);
+
+				CurrentSession->attached_undo_slots[i] = NULL;
+			}
+		}
+	}
+
+	return need_to_unlock;
+}
+
+bool
+DropUndoLogsInTablespace(Oid tablespace)
+{
+	DIR *dir;
+	char undo_path[MAXPGPATH];
+	UndoLogSlot *slot = NULL;
+	int		i;
+
+	Assert(LWLockHeldByMe(TablespaceCreateLock));
+	Assert(tablespace != DEFAULTTABLESPACE_OID);
+
+	/* First, try to kick everyone off any undo logs in this tablespace. */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		bool ok;
+		bool return_to_freelist = false;
+
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/* Check if this undo log can be forcibly detached. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		if (slot->meta.discard == slot->meta.unlogged.insert &&
+			(slot->meta.unlogged.xid == InvalidTransactionId ||
+			 !TransactionIdIsInProgress(slot->meta.unlogged.xid)))
+		{
+			slot->meta.unlogged.xid = InvalidTransactionId;
+			if (slot->pid != InvalidPid)
+			{
+				slot->pid = InvalidPid;
+				return_to_freelist = true;
+			}
+			ok = true;
+		}
+		else
+		{
+			/*
+			 * There is data we need in this undo log.  We can't force it to
+			 * be detached.
+			 */
+			ok = false;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* If we failed, then give up now and report failure. */
+		if (!ok)
+			return false;
+
+		/*
+		 * Put this undo log back on the appropriate free-list.  No one can
+		 * attach to it while we hold TablespaceCreateLock, but if we return
+		 * earlier in a future go around this loop, we need the undo log to
+		 * remain usable.  We'll remove all appropriate logs from the
+		 * free-lists in a separate step below.
+		 */
+		if (return_to_freelist)
+		{
+			LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+			slot->next_free = UndoLogShared->free_lists[slot->meta.persistence];
+			UndoLogShared->free_lists[slot->meta.persistence] = slot->logno;
+			LWLockRelease(UndoLogLock);
+		}
+	}
+
+	/*
+	 * We detached all backends from undo logs in this tablespace, and no one
+	 * can attach to any non-default-tablespace undo logs while we hold
+	 * TablespaceCreateLock.  We can now drop the undo logs.
+	 */
+	slot = NULL;
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/*
+		 * Make sure no buffers remain.  When that is done by
+		 * UndoLogDiscard(), the final page is left in shared_buffers because
+		 * it may contain data, or at least be needed again very soon.  Here
+		 * we need to drop even that page from the buffer pool.
+		 */
+		forget_undo_buffers(slot->logno, slot->meta.discard, slot->meta.discard, true);
+
+		/*
+		 * TODO: For now we drop the undo log, meaning that it will never be
+		 * used again.  That wastes the rest of its address space.  Instead,
+		 * we should put it onto a special list of 'offline' undo logs, ready
+		 * to be reactivated in some other tablespace.  Then we can keep the
+		 * unused portion of its address space.
+		 */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		slot->meta.status = UNDO_LOG_STATUS_DISCARDED;
+		LWLockRelease(&slot->mutex);
+	}
+
+	/* Forget about all sync requests relating to this tablespace. */
+	undofile_forget_sync_tablespace(tablespace);
+
+	/* Unlink all undo segment files in this tablespace. */
+	UndoLogDirectory(tablespace, undo_path);
+
+	dir = AllocateDir(undo_path);
+	if (dir != NULL)
+	{
+		struct dirent *de;
+
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strcmp(de->d_name, ".") == 0 ||
+				strcmp(de->d_name, "..") == 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+	}
+
+	/* Remove all dropped undo logs from the free-lists. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+	{
+		UndoLogSlot *slot;
+		UndoLogNumber *place;
+
+		place = &UndoLogShared->free_lists[i];
+		while (*place != InvalidUndoLogNumber)
+		{
+			slot = find_undo_log_slot(*place, true);
+			if (!slot)
+				elog(ERROR,
+					 "corrupted undo log freelist, unknown log %u", *place);
+			if (slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+				*place = slot->next_free;
+			else
+				place = &slot->next_free;
+		}
+	}
+	LWLockRelease(UndoLogLock);
+
+	return true;
+}
+
+void
+ResetUndoLogs(UndoPersistence persistence)
+{
+	UndoLogSlot *slot = NULL;
+
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		DIR	   *dir;
+		struct dirent *de;
+		char	undo_path[MAXPGPATH];
+		char	segment_prefix[MAXPGPATH];
+		size_t	segment_prefix_size;
+
+		if (slot->meta.persistence != persistence)
+			continue;
+
+		/* Scan the directory for files belonging to this undo log. */
+		snprintf(segment_prefix, sizeof(segment_prefix), "%06X.", slot->logno);
+		segment_prefix_size = strlen(segment_prefix);
+		UndoLogDirectory(slot->meta.tablespace, undo_path);
+		dir = AllocateDir(undo_path);
+		if (dir == NULL)
+			continue;
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+
+		/*
+		 * We have no segment files.  Set the pointers to indicate that there
+		 * is no data.  The discard and insert pointers point to the first
+		 * usable byte in the segment we will create when we next try to
+		 * allocate.  This is a bit strange, because it means that they are
+		 * past the end pointer.  That's the same as when new undo logs are
+		 * created.
+		 *
+		 * TODO: Should we rewind to zero instead, so we can reuse that (now)
+		 * unreferenced address space?
+		 */
+		slot->meta.unlogged.insert = slot->meta.discard = slot->meta.end +
+			UndoLogBlockHeaderSize;
+	}
+}
+
+Datum
+pg_stat_get_undo_logs(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_UNDO_LOGS_COLS 9
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	char *tablespace_name = NULL;
+	Oid last_tablespace = InvalidOid;
+	int			i;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Scan all undo logs to build the results. */
+	for (i = 0; i < UndoLogShared->nslots; ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+		char buffer[17];
+		Datum values[PG_STAT_GET_UNDO_LOGS_COLS];
+		bool nulls[PG_STAT_GET_UNDO_LOGS_COLS] = { false };
+		Oid tablespace;
+
+		/*
+		 * This won't be a consistent result overall, but the values for each
+		 * log will be consistent because we'll take the per-log lock while
+		 * copying them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+
+		/* Skip unused slots and entirely discarded undo logs. */
+		if (slot->logno == InvalidUndoLogNumber ||
+			slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+
+		values[0] = ObjectIdGetDatum((Oid) slot->logno);
+		values[1] = CStringGetTextDatum(
+			slot->meta.persistence == UNDO_PERMANENT ? "permanent" :
+			slot->meta.persistence == UNDO_UNLOGGED ? "unlogged" :
+			slot->meta.persistence == UNDO_TEMP ? "temporary" : "<uknown>");
+		tablespace = slot->meta.tablespace;
+
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.discard));
+		values[3] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert));
+		values[4] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.end));
+		values[5] = CStringGetTextDatum(buffer);
+		if (slot->meta.unlogged.xid == InvalidTransactionId)
+			nulls[6] = true;
+		else
+			values[6] = TransactionIdGetDatum(slot->meta.unlogged.xid);
+		if (slot->pid == InvalidPid)
+			nulls[7] = true;
+		else
+			values[7] = Int32GetDatum((int32) slot->pid);
+		switch (slot->meta.status)
+		{
+		case UNDO_LOG_STATUS_ACTIVE:
+			values[8] = CStringGetTextDatum("ACTIVE"); break;
+		case UNDO_LOG_STATUS_FULL:
+			values[8] = CStringGetTextDatum("FULL"); break;
+		default:
+			nulls[8] = true;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/*
+		 * Deal with potentially slow tablespace name lookup without the lock.
+		 * Avoid making multiple calls to that expensive function for the
+		 * common case of repeating tablespace.
+		 */
+		if (tablespace != last_tablespace)
+		{
+			if (tablespace_name)
+				pfree(tablespace_name);
+			tablespace_name = get_tablespace_name(tablespace);
+			last_tablespace = tablespace;
+		}
+		if (tablespace_name)
+		{
+			values[2] = CStringGetTextDatum(tablespace_name);
+			nulls[2] = false;
+		}
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	if (tablespace_name)
+		pfree(tablespace_name);
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * replay the creation of a new undo log
+ */
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+	xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	/* Create meta-data space in shared memory. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* TODO: assert that it doesn't exist already? */
+
+	slot = allocate_undo_log_slot();
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->logno = xlrec->logno;
+	slot->meta.logno = xlrec->logno;
+	slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+	slot->meta.persistence = xlrec->persistence;
+	slot->meta.tablespace = xlrec->tablespace;
+	slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+	slot->meta.discard = UndoLogBlockHeaderSize;
+	UndoLogShared->next_logno = Max(xlrec->logno + 1, UndoLogShared->next_logno);
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * replay the addition of a new segment to an undo log
+ */
+static void
+undolog_xlog_extend(XLogReaderState *record)
+{
+	xl_undolog_extend *xlrec = (xl_undolog_extend *) XLogRecGetData(record);
+
+	/* Extend exactly as we would during DO phase. */
+	extend_undo_log(xlrec->logno, xlrec->end);
+}
+
+/*
+ * Drop all buffers for the given undo log, from the old_discard to up
+ * new_discard.  If drop_tail is true, also drop the buffer that holds
+ * new_discard; this is used when discarding undo logs completely, for example
+ * via DROP TABLESPACE.  If it is false, then the final buffer is not dropped
+ * because it may contain data.
+ *
+ */
+static void
+forget_undo_buffers(int logno, UndoLogOffset old_discard,
+					UndoLogOffset new_discard, bool drop_tail)
+{
+	BlockNumber old_blockno;
+	BlockNumber new_blockno;
+	RelFileNode	rnode;
+
+	UndoRecPtrAssignRelFileNode(rnode, MakeUndoRecPtr(logno, old_discard));
+	old_blockno = old_discard / BLCKSZ;
+	new_blockno = new_discard / BLCKSZ;
+	if (drop_tail)
+		++new_blockno;
+	while (old_blockno < new_blockno)
+	{
+		ForgetBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno);
+		ForgetLocalBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno++);
+	}
+}
+/*
+ * replay an undo segment discard record
+ */
+static void
+undolog_xlog_discard(XLogReaderState *record)
+{
+	xl_undolog_discard *xlrec = (xl_undolog_discard *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	UndoLogOffset old_segment_begin;
+	UndoLogOffset new_segment_begin;
+	RelFileNode rnode = {0};
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+	if (slot == NULL)
+		elog(ERROR, "unknown undo log %d", xlrec->logno);
+
+	/*
+	 * We're about to discard undologs. In Hot Standby mode, ensure that
+	 * there's no queries running which need to get tuple from discarded undo.
+	 *
+	 * XXX we are passing empty rnode to the conflict function so that it can
+	 * check conflict in all the backend regardless of which database the
+	 * backend is connected.
+	 */
+	if (InHotStandby && TransactionIdIsValid(xlrec->latestxid))
+		ResolveRecoveryConflictWithSnapshot(xlrec->latestxid, rnode);
+
+	/*
+	 * See if we need to unlink or rename any files, but don't consider it an
+	 * error if we find that files are missing.  Since UndoLogDiscard()
+	 * performs filesystem operations before WAL logging or updating shmem
+	 * which could be checkpointed, a crash could have left files already
+	 * deleted, but we could replay WAL that expects the files to be there.
+	 */
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno == xlrec->logno);
+	discard = slot->meta.discard;
+	end = slot->meta.end;
+	LWLockRelease(&slot->mutex);
+
+	/* Drop buffers before we remove/recycle any files. */
+	forget_undo_buffers(xlrec->logno, discard, xlrec->discard,
+						xlrec->entirely_discarded);
+
+	/* Rewind to the start of the segment. */
+	old_segment_begin = discard - discard % UndoLogSegmentSize;
+	new_segment_begin = xlrec->discard - xlrec->discard % UndoLogSegmentSize;
+
+	/* Unlink or rename segments that are no longer in range. */
+	while (old_segment_begin < new_segment_begin)
+	{
+		char	discard_path[MAXPGPATH];
+
+		/* Tell the checkpointer that the file is going away. */
+		undofile_forget_sync(slot->logno,
+							 old_segment_begin / UndoLogSegmentSize,
+							 slot->meta.tablespace);
+
+		UndoLogSegmentPath(xlrec->logno, old_segment_begin / UndoLogSegmentSize,
+						   slot->meta.tablespace, discard_path);
+
+		/* Can we recycle the oldest segment? */
+		if (end < xlrec->end)
+		{
+			char	recycle_path[MAXPGPATH];
+
+			UndoLogSegmentPath(xlrec->logno, end / UndoLogSegmentSize,
+							   slot->meta.tablespace, recycle_path);
+			if (rename(discard_path, recycle_path) == 0)
+			{
+				elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+					 discard_path, recycle_path);
+				end += UndoLogSegmentSize;
+			}
+			else
+			{
+				elog(LOG, "could not rename \"%s\" to \"%s\": %m",
+					 discard_path, recycle_path);
+			}
+		}
+		else
+		{
+			if (unlink(discard_path) == 0)
+				elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+			else
+				elog(LOG, "could not unlink \"%s\": %m", discard_path);
+		}
+		old_segment_begin += UndoLogSegmentSize;
+	}
+
+	/* Create any further new segments that are needed the slow way. */
+	while (end < xlrec->end)
+	{
+		allocate_empty_undo_segment(xlrec->logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/* Update shmem. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = xlrec->discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (xlrec->entirely_discarded)
+		free_undo_log_slot(slot);
+}
+
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
+void
+undolog_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			undolog_xlog_create(record);
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			undolog_xlog_extend(record);
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			undolog_xlog_discard(record);
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
+		default:
+			elog(PANIC, "undo_redo: unknown op code %u", info);
+	}
+}
+
+/*
+ * For assertions only.
+ */
+bool
+AmAttachedToUndoLogSlot(UndoLogSlot *slot)
+{
+	/*
+	 * In general, we can't access log's members without locking.  But this
+	 * function is intended only for asserting that you are attached, and
+	 * while you're attached the slot can't be recycled, so don't bother
+	 * locking.
+	 */
+	return CurrentSession->attached_undo_slots[slot->meta.persistence] == slot;
+}
+
+/*
+ * For testing use only.  This function is only used by the test_undo module.
+ */
+void
+UndoLogDetachFull(void)
+{
+	int		i;
+
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+		if (CurrentSession->attached_undo_slots[i])
+			detach_current_undo_log(i, true);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 43627ab..8b3a28b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -20,6 +20,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/session.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -519,6 +520,8 @@ BootstrapModeMain(void)
 
 	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
 
+	InitializeSession();
+
 	/* Initialize stuff for bootstrap-file processing */
 	for (i = 0; i < MAXATTR; i++)
 	{
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 78a103c..8134f6a 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1022,6 +1022,10 @@ GRANT SELECT (subdbid, subname, subowner, subenabled, subslotname, subpublicatio
     ON pg_subscription TO public;
 
 
+CREATE VIEW pg_stat_undo_logs AS
+    SELECT *
+    FROM pg_stat_get_undo_logs();
+
 --
 -- We have a few function definitions in here, too.
 -- At some point there might be enough to justify breaking them out into
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 5e43867..f1c03e8 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -55,6 +55,7 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -488,6 +489,20 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 	LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
 
 	/*
+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));
+
+	/*
 	 * Try to remove the physical infrastructure.
 	 */
 	if (!destroy_tablespace_directories(tablespaceoid, false))
@@ -1499,6 +1514,14 @@ tblspc_redo(XLogReaderState *record)
 	{
 		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
 
+		/* This shouldn't be able to fail in recovery. */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		if (!DropUndoLogsInTablespace(xlrec->ts_id))
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("tablespace cannot be dropped because it contains non-empty undo logs")));
+		LWLockRelease(TablespaceCreateLock);
+
 		/*
 		 * If we issued a WAL record for a drop tablespace it implies that
 		 * there were no files in it at all when the DROP was done. That means
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b4f2b28..c742861 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4070,6 +4070,27 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_TWOPHASE_FILE_WRITE:
 			event_name = "TwophaseFileWrite";
 			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_READ:
+			event_name = "UndoCheckpointRead";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_WRITE:
+			event_name = "UndoCheckpointWrite";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
+			event_name = "UndoCheckpointSync";
+			break;
+		case WAIT_EVENT_UNDO_FILE_READ:
+			event_name = "UndoFileRead";
+			break;
+		case WAIT_EVENT_UNDO_FILE_WRITE:
+			event_name = "UndoFileWrite";
+			break;
+		case WAIT_EVENT_UNDO_FILE_FLUSH:
+			event_name = "UndoFileFlush";
+			break;
+		case WAIT_EVENT_UNDO_FILE_SYNC:
+			event_name = "UndoFileSync";
+			break;
 		case WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ:
 			event_name = "WALSenderTimelineHistoryRead";
 			break;
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index c2978a9..a188939 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1380,7 +1380,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	char	   *page;
 	size_t		pad;
 	PageHeader	phdr;
-	int			segmentno = 0;
+	BlockNumber	first_blkno = 0;
 	char	   *segmentpath;
 	bool		verify_checksum = false;
 
@@ -1418,12 +1418,18 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 			segmentpath = strstr(filename, ".");
 			if (segmentpath != NULL)
 			{
-				segmentno = atoi(segmentpath + 1);
-				if (segmentno == 0)
+				char	   *end;
+				if (strstr(readfilename, "undo"))
+					first_blkno = strtol(segmentpath + 1, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath + 1, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 					ereport(ERROR,
-							(errmsg("invalid segment number %d in file \"%s\"",
-									segmentno, filename)));
+							(errmsg("invalid segment number in file \"%s\"",
+									filename)));
 			}
+			else
+				first_blkno = 0;
 		}
 	}
 
@@ -1463,7 +1469,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + first_blkno);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 3e96d2a..f2ed561 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -154,6 +154,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_COMMIT_TS_ID:
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
+		case RM_UNDOLOG_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 8046334..d679279 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -177,6 +177,7 @@ static PrivateRefCountEntry *NewPrivateRefCountEntry(Buffer buffer);
 static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move);
 static inline int32 GetPrivateRefCount(Buffer buffer);
 static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref);
+static void InvalidateBuffer(BufferDesc *buf);
 
 /*
  * Ensure that the PrivateRefCountArray has sufficient space to store one more
@@ -620,10 +621,12 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
  * valid, the page is zeroed instead of throwing an error. This is intended
  * for non-critical data, where the caller is prepared to repair errors.
  *
- * In RBM_ZERO_AND_LOCK mode, if the page isn't in buffer cache already, it's
+ * In RBM_ZERO mode, if the page isn't in buffer cache already, it's
  * filled with zeros instead of reading it from disk.  Useful when the caller
  * is going to fill the page from scratch, since this saves I/O and avoids
  * unnecessary failure if the page-on-disk has corrupt page headers.
+ *
+ * In RBM_ZERO_AND_LOCK mode, the page is zeroed and also locked.
  * The page is returned locked to ensure that the caller has a chance to
  * initialize the page before it's made visible to others.
  * Caution: do not use this mode to read a page that is beyond the relation's
@@ -674,24 +677,20 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 /*
  * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
  *		a relcache entry for the relation.
- *
- * NB: At present, this function may only be used on permanent relations, which
- * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary or unlogged relations, we could pass additional
- * parameters.
  */
 Buffer
 ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
-						  BufferAccessStrategy strategy)
+						  BufferAccessStrategy strategy,
+						  char relpersistence)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
-
-	Assert(InRecovery);
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+	return ReadBuffer_common(smgr, relpersistence, forkNum, blockNum,
 							 mode, strategy, &hit);
 }
 
@@ -885,7 +884,9 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * Read in the page, unless the caller intends to overwrite it and
 		 * just wants us to allocate a buffer.
 		 */
-		if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
+		if (mode == RBM_ZERO ||
+			mode == RBM_ZERO_AND_LOCK ||
+			mode == RBM_ZERO_AND_CLEANUP_LOCK)
 			MemSet((char *) bufBlock, 0, BLCKSZ);
 		else
 		{
@@ -1341,6 +1342,62 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 }
 
 /*
+ * ForgetBuffer -- drop a buffer from shared buffers
+ *
+ * If the buffer isn't present in shared buffers, nothing happens.  If it is
+ * present, it is discarded without making any attempt to write it back out to
+ * the operating system.  The caller must therefore somehow be sure that the
+ * data won't be needed for anything now or in the future.  It assumes that
+ * there is no concurrent access to the block, except that it might be being
+ * concurrently written.
+ */
+void
+ForgetBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+			 BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
+	BufferTag	tag;			/* identity of target block */
+	uint32		hash;			/* hash value for tag */
+	LWLock	   *partitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgrid, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	hash = BufTableHashCode(&tag);
+	partitionLock = BufMappingPartitionLock(hash);
+
+	/* see if the block is in the buffer pool */
+	LWLockAcquire(partitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&tag, hash);
+	LWLockRelease(partitionLock);
+
+	/* didn't find it, so nothing to do */
+	if (buf_id < 0)
+		return;
+
+	/* take the buffer header lock */
+	bufHdr = GetBufferDescriptor(buf_id);
+	buf_state = LockBufHdr(bufHdr);
+
+	/*
+	 * The buffer might been evicted after we released the partition lock and
+	 * before we acquired the buffer header lock.  If so, the buffer we've
+	 * locked might contain some other data which we shouldn't touch. If the
+	 * buffer hasn't been recycled, we proceed to invalidate it.
+	 */
+	if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+		bufHdr->tag.blockNum == blockNum &&
+		bufHdr->tag.forkNum == forkNum)
+		InvalidateBuffer(bufHdr);		/* releases spinlock */
+	else
+		UnlockBufHdr(bufHdr, buf_state);
+}
+
+/*
  * InvalidateBuffer -- mark a shared buffer invalid and return it to the
  * freelist.
  *
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 896285a..4183e8d 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -275,6 +275,50 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 }
 
 /*
+ * ForgetLocalBuffer - drop a buffer from local buffers
+ *
+ * This is similar to bufmgr.c's ForgetBuffer, except that we do not need
+ * to do any locking since this is all local.  As with that function, this
+ * must be used very carefully, since we'll cheerfully throw away dirty
+ * buffers without any attempt to write them.
+ */
+void
+ForgetLocalBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+				  BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, BackendIdForTempRelations());
+	BufferTag	tag;					/* identity of target block */
+	LocalBufferLookupEnt *hresult;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/*
+	 * If somehow this is the first request in the session, there's nothing to
+	 * do.  (This probably shouldn't happen, though.)
+	 */
+	if (LocalBufHash == NULL)
+		return;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgrid, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* see if the block is in the local buffer pool */
+	hresult = (LocalBufferLookupEnt *)
+	hash_search(LocalBufHash, (void *) &tag, HASH_REMOVE, NULL);
+
+	/* didn't find it, so nothing to do */
+	if (!hresult)
+		return;
+
+	/* mark buffer invalid */
+	bufHdr = GetLocalBufferDescriptor(hresult->id);
+	CLEAR_BUFFERTAG(bufHdr->tag);
+	buf_state = pg_atomic_read_u32(&bufHdr->state);
+	buf_state &= ~(BM_VALID | BM_TAG_VALID | BM_DIRTY);
+	pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
+}
+
+/*
  * MarkLocalBufferDirty -
  *	  mark a local buffer dirty
  */
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 73c455e..25e36b8 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -322,7 +322,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
 static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
 static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
 
-static int	fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
 static int	fsync_parent_path(const char *fname, int elevel);
 
 
@@ -3345,7 +3344,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
  *
  * Returns 0 if the operation succeeded, -1 otherwise.
  */
-static int
+int
 fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
 {
 	int			fd;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index d7d7335..12c3249 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/subtrans.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -125,6 +126,7 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, ProcGlobalShmemSize());
 		size = add_size(size, XLOGShmemSize());
 		size = add_size(size, CLOGShmemSize());
+		size = add_size(size, UndoLogShmemSize());
 		size = add_size(size, CommitTsShmemSize());
 		size = add_size(size, SUBTRANSShmemSize());
 		size = add_size(size, TwoPhaseShmemSize());
@@ -213,6 +215,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	 */
 	XLOGShmemInit();
 	CLOGShmemInit();
+	UndoLogShmemInit();
 	CommitTsShmemInit();
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index bc1aa88..5df658d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -522,6 +522,8 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append");
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_HASH_JOIN, "parallel_hash_join");
 	LWLockRegisterTranche(LWTRANCHE_SXACT, "serializable_xact");
+	LWLockRegisterTranche(LWTRANCHE_UNDOLOG, "undo_log");
+	LWLockRegisterTranche(LWTRANCHE_UNDODISCARD, "undo_discard");
 
 	/* Register named tranches. */
 	for (i = 0; i < NamedLWLockTrancheRequests; i++)
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index db47843..4b42a1c 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -49,3 +49,4 @@ MultiXactTruncationLock				41
 OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
+UndoLogLock                                      45
diff --git a/src/backend/storage/smgr/Makefile b/src/backend/storage/smgr/Makefile
index e486b7c..ff2e5e2 100644
--- a/src/backend/storage/smgr/Makefile
+++ b/src/backend/storage/smgr/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/storage/smgr
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = md.o smgr.o
+OBJS = md.o smgr.o undofile.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 4ba07a0..8cd1f7e 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -22,6 +22,7 @@
 #include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/smgr.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
 
@@ -81,6 +82,24 @@ static const f_smgr smgrsw[] = {
 		.smgr_nblocks = mdnblocks,
 		.smgr_truncate = mdtruncate,
 		.smgr_immedsync = mdimmedsync,
+	},
+	/* undo logs */
+	{
+		.smgr_init = undofile_init,
+		.smgr_shutdown = undofile_shutdown,
+		.smgr_open = undofile_open,
+		.smgr_close = undofile_close,
+		.smgr_create = undofile_create,
+		.smgr_exists = undofile_exists,
+		.smgr_unlink = undofile_unlink,
+		.smgr_extend = undofile_extend,
+		.smgr_prefetch = undofile_prefetch,
+		.smgr_read = undofile_read,
+		.smgr_write = undofile_write,
+		.smgr_writeback = undofile_writeback,
+		.smgr_nblocks = undofile_nblocks,
+		.smgr_truncate = undofile_truncate,
+		.smgr_immedsync = undofile_immedsync,
 	}
 };
 
@@ -97,7 +116,6 @@ static dlist_head unowned_relns;
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
 
-
 /*
  *	smgrinit(), smgrshutdown() -- Initialize or shut down storage
  *								  managers.
@@ -177,6 +195,7 @@ smgropen(SmgrId smgrid, RelFileNode rnode, BackendId backend)
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = smgrid;
+		reln->private_data = NULL;
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
new file mode 100644
index 0000000..2aa4952
--- /dev/null
+++ b/src/backend/storage/smgr/undofile.c
@@ -0,0 +1,420 @@
+/*
+ * undofile.h
+ *
+ * PostgreSQL undo file manager.  This module provides SMGR-compatible
+ * interface to the files that back undo logs on the filesystem, so that undo
+ * log data can use the shared buffer pool.  Other aspects of undo log
+ * management are provided by undolog.c, so the SMGR interfaces not directly
+ * concerned with reading, writing and flushing data are unimplemented.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/storage/smgr/undofile.c
+ */
+
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/xlog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgwriter.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "storage/undofile.h"
+#include "utils/memutils.h"
+
+/* Populate a file tag describing an undo segment file. */
+#define INIT_UNDOFILETAG(a,xx_logno,xx_tbspc,xx_segno) \
+( \
+	memset(&(a), 0, sizeof(FileTag)), \
+	(a).handler = SYNC_HANDLER_UNDO, \
+	(a).rnode.spcNode = (xx_tbspc), \
+	(a).rnode.relNode = (xx_logno), \
+	(a).segno = (xx_segno) \
+)
+
+/*
+ * While md.c expects random access and has a small number of huge
+ * segments, undofile.c manages a potentially very large number of smaller
+ * segments and has a less random access pattern.  Therefore, instead of
+ * keeping a potentially huge array of vfds we'll just keep the most
+ * recently accessed N.
+ *
+ * For now, N == 1, so we just need to hold onto one 'File' handle.
+ */
+typedef struct UndoFileState
+{
+	int		mru_segno;
+	File	mru_file;
+} UndoFileState;
+
+static MemoryContext UndoFileCxt;
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok);
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno);
+
+void
+undofile_init(void)
+{
+	UndoFileCxt = AllocSetContextCreate(TopMemoryContext,
+										"UndoFileSmgr",
+										ALLOCSET_DEFAULT_SIZES);
+}
+
+void
+undofile_shutdown(void)
+{
+}
+
+void
+undofile_open(SMgrRelation reln)
+{
+	UndoFileState *state;
+
+	state = MemoryContextAllocZero(UndoFileCxt, sizeof(UndoFileState));
+	reln->private_data = state;
+}
+
+void
+undofile_close(SMgrRelation reln, ForkNumber forknum)
+{
+}
+
+void
+undofile_create(SMgrRelation reln, ForkNumber forknum, bool isRedo)
+{
+	/*
+	 * File creation is managed by undolog.c, but xlogutils.c likes to call
+	 * this just in case.  Ignore.
+	 */
+}
+
+bool
+undofile_exists(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_exists is not supported");
+
+	return false;		/* not reached */
+}
+
+void
+undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo)
+{
+	elog(ERROR, "undofile_unlink is not supported");
+}
+
+void
+undofile_extend(SMgrRelation reln, ForkNumber forknum,
+				BlockNumber blocknum, char *buffer,
+				bool skipFsync)
+{
+	elog(ERROR, "undofile_extend is not supported");
+}
+
+void
+undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
+{
+	elog(ERROR, "undofile_prefetch is not supported");
+}
+
+void
+undofile_read(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+			  char *buffer)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileRead(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_READ);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+}
+
+void
+undofile_write(SMgrRelation reln, ForkNumber forknum,
+			   BlockNumber blocknum, char *buffer,
+			   bool skipFsync)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileWrite(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_WRITE);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		/*
+		 * short write: unexpected, because this should be overwriting an
+		 * entirely pre-allocated segment file
+		 */
+		ereport(ERROR,
+				(errcode(ERRCODE_DISK_FULL),
+				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+
+	/* Tell checkpointer this file is dirty. */
+	if (!skipFsync && !SmgrIsTemp(reln))
+	{
+		undofile_request_sync(reln->smgr_rnode.node.relNode,
+							  blocknum / UNDOSEG_SIZE,
+							  reln->smgr_rnode.node.spcNode);
+	}
+}
+
+void
+undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+				   BlockNumber blocknum, BlockNumber nblocks)
+{
+	while (nblocks > 0)
+	{
+		File	file;
+		int		nflush;
+
+		file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+
+		/* compute number of desired writes within the current segment */
+		nflush = Min(nblocks,
+					 1 + UNDOSEG_SIZE - (blocknum % UNDOSEG_SIZE));
+
+		FileWriteback(file,
+					  (blocknum % UNDOSEG_SIZE) * BLCKSZ,
+					  nflush * BLCKSZ, WAIT_EVENT_UNDO_FILE_FLUSH);
+
+		nblocks -= nflush;
+		blocknum += nflush;
+	}
+}
+
+BlockNumber
+undofile_nblocks(SMgrRelation reln, ForkNumber forknum)
+{
+	/*
+	 * xlogutils.c likes to call this to decide whether to read or extend; for
+	 * now we lie and say the relation is big as possible.
+	 */
+	return UndoLogMaxSize / BLCKSZ;
+}
+
+void
+undofile_truncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
+{
+	elog(ERROR, "undofile_truncate is not supported");
+}
+
+void
+undofile_immedsync(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_immedsync is not supported");
+}
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok)
+{
+	File		file;
+	char		path[MAXPGPATH];
+
+	UndoLogSegmentPath(relNode, segno, spcNode, path);
+	file = PathNameOpenFile(path, O_RDWR | PG_BINARY);
+
+	if (file <= 0 && (!missing_ok || errno != ENOENT))
+		elog(ERROR, "cannot open undo segment file '%s': %m", path);
+
+	return file;
+}
+
+/*
+ * Get a File for a particular segment of a SMgrRelation representing an undo
+ * log.
+ */
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno)
+{
+	UndoFileState *state = (UndoFileState *) reln->private_data;
+
+	/* If we have a file open already, check if we need to close it. */
+	if (state->mru_file > 0 && state->mru_segno != segno)
+	{
+		/* These are not the blocks we're looking for. */
+		FileClose(state->mru_file);
+		state->mru_file = 0;
+	}
+
+	/* Check if we need to open a new file. */
+	if (state->mru_file <= 0)
+	{
+		state->mru_file =
+			undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+									   reln->smgr_rnode.node.spcNode,
+									   segno, InRecovery);
+		if (InRecovery && state->mru_file <= 0)
+		{
+			/*
+			 * If in recovery, we may be trying to access a file that will
+			 * later be unlinked.  Tolerate missing files, creating a new
+			 * zero-filled file as required.
+			 */
+			UndoLogNewSegment(reln->smgr_rnode.node.relNode,
+							  reln->smgr_rnode.node.spcNode,
+							  segno);
+			state->mru_file =
+				undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+										   reln->smgr_rnode.node.spcNode,
+										   segno, false);
+			Assert(state->mru_file > 0);
+		}
+		state->mru_segno = segno;
+	}
+
+	return state->mru_file;
+}
+
+/*
+ * Callback to handle a queued sync request.
+ */
+int
+undofile_syncfiletag(const FileTag *tag, char *path)
+{
+	SMgrRelation reln = smgropen(SMGR_UNDO, tag->rnode, InvalidBackendId);
+	File		file;
+
+	if (tag->rnode.relNode == (Oid) InvalidUndoLogNumber)
+	{
+		/* Sync parent directory for this tablespace. */
+		UndoLogDirectory(tag->rnode.spcNode, path);
+
+		/* The caller (sync.c) will do appropriate error reporting. */
+		return fsync_fname_ext(path, true, false, WARNING);
+	}
+	else
+	{
+		/* Sync a segment file. */
+		UndoLogSegmentPath(tag->rnode.relNode, tag->segno, tag->rnode.spcNode,
+						   path);
+
+		file = undofile_get_segment_file(reln, tag->segno);
+		if (file <= 0)
+		{
+			/* errno set by undofile_get_segment_file() */
+			return -1;
+		}
+
+		return FileSync(file, WAIT_EVENT_UNDO_FILE_SYNC);
+	}
+}
+
+/*
+ * Filtering callback used by SYNC_FILTER_REQUEST to forget some requests.
+ */
+bool
+undofile_filetagmatches(const FileTag *tag, const FileTag *candidate)
+{
+	/*
+	 * We use SYNC_FILTER_REQUEST to forget requests for a given tablespace,
+	 * before removing all undo files in the tablespace.
+	 */
+	return tag->rnode.spcNode == candidate->rnode.spcNode;
+}
+
+/*
+ * Tell the checkpointer to sync a segment file.
+ */
+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync file \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about any sync requests for a given segment
+ * file, because it's about to go away.
+ */
+void
+undofile_forget_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Send, and keep retrying if out of space. */
+	(void) RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
+}
+
+/*
+ * Tell the checkpointer to fsync the undo directory in a given tablespace,
+ * because we have created or renamed files inside it.
+ */
+void
+undofile_request_sync_dir(Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	/* We use a special logno and segno to mean "the directory". */
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync directory \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about all sync requests for a given
+ * tablespace, because it's about to go away.
+ */
+void
+undofile_forget_sync_tablespace(Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/*
+	 * Tell checkpointer to forget about any request for this tag, and keep
+	 * waiting if there is not enough space.
+	 */
+	(void) RegisterSyncRequest(&tag, SYNC_FILTER_REQUEST, true);
+}
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index 705f229..02f8e31 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -28,6 +28,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/inval.h"
@@ -96,6 +97,11 @@ static const SyncOps syncsw[] = {
 		.sync_syncfiletag = mdsyncfiletag,
 		.sync_unlinkfiletag = mdunlinkfiletag,
 		.sync_filetagmatches = mdfiletagmatches
+	},
+	/* undo log segment files */
+	{
+		.sync_syncfiletag = undofile_syncfiletag,
+		.sync_filetagmatches = undofile_filetagmatches
 	}
 };
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index e9f72b5..d88113f 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -25,6 +25,7 @@
 #include "access/session.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -561,6 +562,7 @@ BaseInit(void)
 	InitSync();
 	smgrinit();
 	InitBufferPoolAccess();
+	UndoLogInit();
 }
 
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1208eb9..b92645f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -121,6 +121,7 @@ extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
+extern char *undo_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
 
@@ -3669,6 +3670,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"undo_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the tablespace(s) to use for undo logs."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&undo_tablespaces,
+		"",
+		check_undo_tablespaces, assign_undo_tablespaces, NULL
+	},
+
+	{
 		{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
 			gettext_noop("Sets the path for dynamically loadable modules."),
 			gettext_noop("If a dynamically loadable module needs to be opened and "
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ad5cd41..40f7671 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -210,11 +210,13 @@ static const char *const subdirs[] = {
 	"pg_snapshots",
 	"pg_subtrans",
 	"pg_twophase",
+	"pg_undo",
 	"pg_multixact",
 	"pg_multixact/members",
 	"pg_multixact/offsets",
 	"base",
 	"base/1",
+	"base/undo",
 	"pg_replslot",
 	"pg_tblspc",
 	"pg_stat",
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index b8e7c32..825b11c 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -167,7 +167,7 @@ skipfile(const char *fn)
 }
 
 static void
-scan_file(const char *fn, BlockNumber segmentno)
+scan_file(const char *fn, BlockNumber first_blkno)
 {
 	PGAlignedBlock buf;
 	PageHeader	header = (PageHeader) buf.data;
@@ -208,7 +208,7 @@ scan_file(const char *fn, BlockNumber segmentno)
 		if (PageIsNew(header))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + first_blkno);
 		current_size += r;
 		if (mode == PG_MODE_CHECK)
 		{
@@ -310,7 +310,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			char		fnonly[MAXPGPATH];
 			char	   *forkpath,
 					   *segmentpath;
-			BlockNumber segmentno = 0;
+			BlockNumber first_blkno;
 
 			if (skipfile(de->d_name))
 				continue;
@@ -325,15 +325,22 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			segmentpath = strchr(fnonly, '.');
 			if (segmentpath != NULL)
 			{
+				char	   *end;
+
 				*segmentpath++ = '\0';
-				segmentno = atoi(segmentpath);
-				if (segmentno == 0)
+				if (strstr(segmentpath, "undo"))
+					first_blkno = strtol(segmentpath, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 				{
-					pg_log_error("invalid segment number %d in file name \"%s\"",
-								 segmentno, fn);
+					pg_log_error("invalid segment number in file name \"%s\"",
+								 fn);
 					exit(1);
 				}
 			}
+			else
+				first_blkno = 0;
 
 			forkpath = strchr(fnonly, '_');
 			if (forkpath != NULL)
@@ -350,7 +357,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			 * the items in the data folder.
 			 */
 			if (!sizeonly)
-				scan_file(fn, segmentno);
+				scan_file(fn, first_blkno);
 		}
 #ifndef WIN32
 		else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 2734f87..1580380 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -80,6 +80,7 @@ static bool ReadControlFile(void);
 static void GuessControlValues(void);
 static void PrintControlValues(bool guessed);
 static void PrintNewControlValues(void);
+static void AdjustRedoLocation(const char *DataDir);
 static void RewriteControlFile(void);
 static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
@@ -510,6 +511,7 @@ main(int argc, char *argv[])
 	/*
 	 * Else, do the dirty deed.
 	 */
+	AdjustRedoLocation(DataDir);
 	RewriteControlFile();
 	KillExistingXLOG();
 	KillExistingArchiveStatus();
@@ -899,6 +901,82 @@ PrintNewControlValues(void)
 
 
 /*
+ * Compute the new redo, and move the pg_undo file to match if necessary.
+ * Rather than renaming it, we'll create a new copy, so that a failure that
+ * occurs before the controlfile is rewritten won't be fatal.
+ */
+static void
+AdjustRedoLocation(const char *DataDir)
+{
+	uint64		old_redo = ControlFile.checkPointCopy.redo;
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	int			old_fd;
+	int			new_fd;
+	ssize_t		nread;
+	ssize_t		nwritten;
+	char		buffer[1024];
+
+	/*
+	 * Adjust fields as needed to force an empty XLOG starting at
+	 * newXlogSegNo.
+	 */
+	XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
+							ControlFile.checkPointCopy.redo);
+
+	/* If the redo location didn't move, we don't need to do anything. */
+	if (old_redo == ControlFile.checkPointCopy.redo)
+		return;
+
+	/*
+	 * Otherwise we copy the pg_undo file, because its name must match the redo
+	 * location.
+	 */
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 DataDir,
+			 old_redo);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 DataDir,
+			 ControlFile.checkPointCopy.redo);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	new_fd = open(new_pg_undo_path, O_RDWR | O_CREAT, 0644);
+	if (new_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	while ((nread = read(old_fd, buffer, sizeof(buffer))) > 0)
+	{
+		do
+		{
+			nwritten = write(new_fd, buffer, nread);
+			if (nwritten < 0)
+			{
+				pg_log_error("could not write to \"%s\": %m", new_pg_undo_path);
+				exit(1);
+			}
+			nread -= nwritten;
+		} while (nread > 0);
+	}
+	if (nread < 0)
+	{
+		pg_log_error("could not read from \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	close(old_fd);
+	close(new_fd);
+}
+
+/*
  * Write out the new pg_control file.
  */
 static void
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 2cb829a..82c8033 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -9,7 +9,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = check.o controldata.o dump.o exec.o file.o function.o info.o \
        option.o parallel.o pg_upgrade.o relfilenode.o server.o \
-       tablespace.o util.o version.o $(WIN32RES)
+       tablespace.o undo.o util.o version.o $(WIN32RES)
 
 override CPPFLAGS := -DDLSUFFIX=\"$(DLSUFFIX)\" -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 617270f..02e94a0 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -21,6 +21,7 @@ static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb);
 static bool equivalent_locale(int category, const char *loca, const char *locb);
 static void check_is_install_user(ClusterInfo *cluster);
 static void check_proper_datallowconn(ClusterInfo *cluster);
+static void check_for_undo_data(ClusterInfo *cluster);
 static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
@@ -97,6 +98,7 @@ check_and_dump_old_cluster(bool live_check)
 	 */
 	check_is_install_user(&old_cluster);
 	check_proper_datallowconn(&old_cluster);
+	check_for_undo_data(&old_cluster);
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
@@ -171,6 +173,7 @@ check_new_cluster(void)
 
 	check_is_install_user(&new_cluster);
 
+	check_for_undo_data(&new_cluster);
 	check_for_prepared_transactions(&new_cluster);
 }
 
@@ -801,6 +804,45 @@ check_for_prepared_transactions(ClusterInfo *cluster)
 
 
 /*
+ *	check_for_live_undo_data()
+ *
+ *	Make sure there are no live undo records (aborted transactions that have
+ *	not been rolled back, or committed transactions whose undo data has not
+ *	yet been discarded).
+ */
+static void
+check_for_undo_data(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1200)
+		return;
+
+	prep_status("Checking for undo data");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_stat_undo_logs "
+							"WHERE discard != insert");
+
+	if (PQntuples(res) != 0)
+	{
+		if (cluster == &old_cluster)
+			pg_fatal("The source cluster contains live undo data\n");
+		else
+			pg_fatal("The target cluster contains live undo data\n");
+	}
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
+
+
+/*
  *	check_for_isn_and_int8_passing_mismatch()
  *
  *	contrib/isn relies on data type int8, and in 8.4 int8 can now be passed
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 3823641..523e86e 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -59,6 +59,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	bool		got_redo_location = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -485,6 +486,23 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "Latest checkpoint's REDO location:")) != NULL)
+		{
+			uint32		hi;
+			uint32		lo;
+
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			p++;				/* remove ':' char */
+
+			if (sscanf(p, "%X/%X", &hi, &lo) != 2)
+				pg_fatal("%d: controldata cannot parse REDO location\n", __LINE__);
+			cluster->controldata.redo_location = (((uint64) hi) << 32) | lo;
+			got_redo_location = true;
+		}
 	}
 
 	pclose(output);
@@ -528,6 +546,13 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		}
 	}
 
+	/*
+	 * If we used pg_resetwal instead of pg_controldata, there is no REDO
+	 * location.
+	 */
+	if (!got_redo_location)
+		cluster->controldata.redo_location = 0;
+
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi ||
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 043373f..90de62e 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -351,6 +351,10 @@ check_data_dir(ClusterInfo *cluster)
 		check_single_dir(pg_data, "pg_clog");
 	else
 		check_single_dir(pg_data, "pg_xact");
+
+	/* pg_undo is new in v13 */
+	if (GET_MAJOR_VERSION(cluster->major_version) >= 1200)
+		check_single_dir(pg_data, "pg_undo");
 }
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index ff78929..a51da36 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -139,6 +139,8 @@ main(int argc, char **argv)
 
 	/* New now using xids of the old system */
 
+	merge_undo_logs();
+
 	/* -- NEW -- */
 	start_postmaster(&new_cluster, true);
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 5d31750..d0d93c0 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -227,6 +227,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	uint64		redo_location;
 } ControlData;
 
 /*
@@ -465,3 +466,7 @@ void		parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr
 										  char *old_pgdata, char *new_pgdata,
 										  char *old_tablespace);
 bool		reap_child(bool wait_for_child);
+
+/* undo.c */
+
+void		merge_undo_logs(void);
diff --git a/src/bin/pg_upgrade/undo.c b/src/bin/pg_upgrade/undo.c
new file mode 100644
index 0000000..6efc99b
--- /dev/null
+++ b/src/bin/pg_upgrade/undo.c
@@ -0,0 +1,292 @@
+/*
+ *	undo.c
+ *
+ *	Support for upgrading undo logs.\
+ *	Copyright (c) 2019, PostgreSQL Global Development Group
+ *	src/bin/pg_upgrade/undo.c
+ */
+
+
+#include "postgres_fe.h"
+#include "pg_upgrade.h"
+#include "access/undolog.h"
+
+/*
+ * The relevant parts of UndoLogMetaDataData, in a version-independent format.
+ */
+typedef struct
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogStatus status;
+	UndoPersistence persistence;
+	Oid			tablespace;
+} UndoLogInfo;
+
+/*
+ * Read the header of a pg_undo file and extract basic information.  If the
+ * format of the header changes in later versions, this may need to change
+ * depending on "cluster".
+ */
+static void
+read_pg_undo_header(int fd, ClusterInfo *cluster, UndoLogNumber *low_logno,
+					UndoLogNumber *next_logno, UndoLogNumber *num_logs)
+{
+	pg_crc32c crc;
+
+	/* Read the header, much like StartupUndoLogs(). */
+	if (read(fd, low_logno, sizeof(*low_logno)) != sizeof(*low_logno) ||
+		read(fd, next_logno, sizeof(*next_logno)) != sizeof(*next_logno) ||
+		read(fd, num_logs, sizeof(*num_logs)) != sizeof(*num_logs) ||
+		read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("pg_undo file is corrupted or cannot be read\n");
+}
+
+/*
+ * Read a single UndoLogMetaData object.  If the format changes in later
+ * versions, this may need to change to be able to read different structs
+ * depending on "cluster".
+ */
+static void
+read_one_undo_log(int fd, ClusterInfo *cluster, UndoLogInfo *info)
+{
+	UndoLogMetaData meta_data;
+	int		rc;
+
+	rc = read(fd, &meta_data, sizeof(meta_data));
+	if (rc < 0)
+		pg_fatal("could not read undo log meta-data: %m");
+	else if (rc != sizeof(meta_data))
+		pg_fatal("could not read undo log meta-data: expect %zu bytes but read only %d bytes",
+				 sizeof(meta_data), rc);
+
+	info->logno = meta_data.logno;
+	info->persistence = meta_data.persistence;
+	info->tablespace = meta_data.tablespace;
+	info->discard = meta_data.discard;
+	info->status = meta_data.status;
+}
+
+static void
+merge_undo_log(UndoLogInfo *logs, UndoLogNumber *num_logs,
+			   const UndoLogInfo *info)
+{
+	UndoLogNumber i;
+
+	/* Do we already have an entry for this logno? */
+	for (i = 0; i < *num_logs; ++i)
+	{
+		if (logs[i].logno == info->logno)
+		{
+			/*
+			 * Take the highest discard offset, so that any pointers that
+			 * originated in either cluster appear to be discarded.
+			 */
+			if (logs[i].discard < info->discard)
+				logs[i].discard = info->discard;
+
+			/*
+			 * Take the highest status so that entirely discarded logs trump
+			 * active logs.
+			 */
+			StaticAssertStmt(UNDO_LOG_STATUS_ACTIVE < UNDO_LOG_STATUS_FULL,
+							 "undo log status out of order");
+			StaticAssertStmt(UNDO_LOG_STATUS_FULL < UNDO_LOG_STATUS_DISCARDED,
+							 "undo log status out of order");
+			if (logs[i].status < info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the most persistent persistence level.  While we could
+			 * just convert them all to permanent and it wouldn't hurt, it's
+			 * probably a better idea to keep about the same number of each
+			 * persistence level, so that a system that has stabilized with
+			 * those numbers will continue to be stable after the upgrade (ie
+			 * not suddenly need to create more undo logs of different
+			 * levels).  The most permanent is the best choice, because TEMP
+			 * undo logs might be rewound in future.
+			 */
+			StaticAssertStmt(UNDO_PERMANENT < UNDO_UNLOGGED,
+							 "undo log persistent out of order");
+			StaticAssertStmt(UNDO_UNLOGGED < UNDO_TEMP,
+							 "undo log persistent out of order");
+			if (logs[i].status > info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the highest tablespace OID.  The choice of 'highest' is
+			 * arbitrary (we don't really expect the new cluster to have more
+			 * than one log), but it seems useful to preserve the distribution
+			 * of tablespaces from the old cluster for stability, as above.
+			 */
+			if (logs[i].tablespace < info->tablespace)
+				logs[i].tablespace = info->tablespace;
+			break;
+		}
+	}
+
+	/* Otherwise create a new entry. */
+	logs[*num_logs++] = *info;
+}
+
+/*
+ * We need to merge the old undo logs and the new undo logs.  We know that
+ * there is no live undo data (see check_for_live_undo_data()), but we need to
+ * make sure that any undo record pointers that exist in the old OR new
+ * cluster appear as discarded.  That is, any log numbers that are entirely
+ * discard in either cluster appear as entirely discarded, and and we retain
+ * the higher of the discard pointers in any log that is active.  This is
+ * mostly a theoretical concern for now, but perhaps a future release will be
+ * able to create higher undo record pointers during initdb than the old
+ * cluster had, so let's use an algorithm that doesn't make any assumptions
+ * about that.
+ */
+void
+merge_undo_logs(void)
+{
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	UndoLogInfo *logs;
+	UndoLogNumber num_logs;
+	UndoLogNumber num_old_logs;
+	UndoLogNumber old_low_logno;
+	UndoLogNumber old_next_logno;
+	UndoLogNumber num_new_logs;
+	UndoLogNumber new_low_logno;
+	UndoLogNumber new_next_logno;
+	UndoLogNumber i;
+	int			old_fd;
+	int			new_fd;
+	pg_crc32c	crc;
+
+	/* If the old cluster has no undo logs, there is nothing to do */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1200)
+		return;
+
+	/*
+	 * Open the pg_undo files corresponding to the old and new redo locations.
+	 * First, we'll reload pg_controldata output, so that we have up-to-date
+	 * redo locations.
+	 */
+	get_control_data(&old_cluster, true);
+	get_control_data(&new_cluster, true);
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 old_cluster.pgdata,
+			 old_cluster.controldata.redo_location);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 new_cluster.pgdata,
+			 new_cluster.controldata.redo_location);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", old_pg_undo_path);
+	new_fd = open(new_pg_undo_path, O_RDWR, 0);
+	if (new_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", new_pg_undo_path);
+
+	/* Read the headers */
+	read_pg_undo_header(old_fd, &old_cluster, &old_low_logno, &old_next_logno, &num_old_logs);
+	read_pg_undo_header(new_fd, &new_cluster, &new_low_logno, &new_next_logno, &num_new_logs);
+
+	/* Allocate workspace that is sure to be enough for the merged set */
+	logs = malloc(sizeof(*logs) * (num_old_logs + num_new_logs));
+	if (logs == NULL)
+	{
+		pg_fatal("out of memory\n");
+		exit(1);
+	}
+	num_logs = 0;
+
+	/*
+	 * Anything below the "low" logno has been entirely discarded, so we'll
+	 * take the higher of the two values.  Likewise, the "next" log number to
+	 * allocate should be the higher of the two.
+	 */
+	new_low_logno = Max(old_low_logno, new_low_logno);
+	new_next_logno = Max(old_next_logno, new_next_logno);
+
+	/* Merge in the old logs */
+	while (num_old_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(old_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_old_logs;
+	}
+
+	/* Merge in the new logs */
+	while (num_new_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(new_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_new_logs;
+	}
+
+	close(old_fd);
+
+	/* Now write out the new file, much like CheckPointUndoLogs() */
+	if (ftruncate(new_fd, 0) < 0)
+		pg_fatal("could not truncate file \"%s\": %m", new_pg_undo_path);
+	if (lseek(new_fd, SEEK_SET, 0) < 0)
+		pg_fatal("could not seek to start of file \"%s\": %m", new_pg_undo_path);
+
+	/* Compute header checksum */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &new_low_logno, sizeof(new_low_logno));
+	COMP_CRC32C(crc, &new_next_logno, sizeof(new_next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the header */
+	if ((write(new_fd, &new_low_logno, sizeof(new_low_logno)) != sizeof(new_low_logno)) ||
+		(write(new_fd, &new_next_logno, sizeof(new_next_logno)) != sizeof(new_next_logno)) ||
+		(write(new_fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(new_fd, &crc, sizeof(crc)) != sizeof(crc)))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	/* Write out the undo logs */
+	INIT_CRC32C(crc);
+	for (i = 0; i < num_logs; ++i)
+	{
+		UndoLogMetaData	meta_data;
+		UndoLogInfo	*info = &logs[i];
+		UndoLogOffset end;
+
+		memset(&meta_data, 0, sizeof(meta_data));
+		meta_data.logno = info->logno;
+
+		/*
+		 * Round the discard offset up so that it points to the first byte in
+		 * a segment, and assign that to all three offsets.  That means there
+		 * is no logical data, and there are no physical files.
+		 */
+		end = ((info->discard + UndoLogSegmentSize - 1) / UndoLogSegmentSize)
+			* UndoLogSegmentSize;
+		meta_data.unlogged.insert = meta_data.discard = meta_data.end = end;
+
+		/*
+		 * We have whatever was the highest status (though it probably
+		 * wouldn't hurt if we set them all to ACTIVE).
+		 */
+		meta_data.status = info->status;
+
+
+		if (write(new_fd, &meta_data, sizeof(meta_data)) != sizeof(meta_data))
+			pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+		COMP_CRC32C(crc, &meta_data, sizeof(meta_data));
+	}
+	FIN_CRC32C(crc);
+
+	if (write(new_fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	close(new_fd);
+	free(logs);
+}
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca..938150d 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 3c0db2c..6945e3e 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
diff --git a/src/include/access/session.h b/src/include/access/session.h
index 8fba568..650b181 100644
--- a/src/include/access/session.h
+++ b/src/include/access/session.h
@@ -17,6 +17,9 @@
 /* Avoid including typcache.h */
 struct SharedRecordTypmodRegistry;
 
+/* Avoid including undolog.h */
+struct UndoLogSlot;
+
 /*
  * A struct encapsulating some elements of a user's session.  For now this
  * manages state that applies to parallel query, but it principle it could
@@ -27,6 +30,10 @@ typedef struct Session
 	dsm_segment *segment;		/* The session-scoped DSM segment. */
 	dsa_area   *area;			/* The session-scoped DSA area. */
 
+	/* State managed by undolog.c. */
+	struct UndoLogSlot *attached_undo_slots[3];
+	bool		need_to_choose_undo_tablespace;
+
 	/* State managed by typcache.c. */
 	struct SharedRecordTypmodRegistry *shared_typmod_registry;
 	dshash_table *shared_record_table;
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
new file mode 100644
index 0000000..f8f6f44
--- /dev/null
+++ b/src/include/access/undolog.h
@@ -0,0 +1,455 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.h
+ *
+ * PostgreSQL undo log manager.  This module is responsible for lifecycle
+ * management of undo logs and backing files, associating undo logs with
+ * backends, allocating and managing space within undo logs.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_H
+#define UNDOLOG_H
+
+#include "access/xlogreader.h"
+#include "catalog/pg_class.h"
+#include "common/relpath.h"
+#include "storage/bufpage.h"
+
+#ifndef FRONTEND
+#include "storage/lwlock.h"
+#endif
+
+/* The type used to identify an undo log and position within it. */
+typedef uint64 UndoRecPtr;
+
+/* The type used for undo record lengths. */
+typedef uint16 UndoRecordSize;
+
+/* Undo log statuses. */
+typedef enum
+{
+	UNDO_LOG_STATUS_UNUSED = 0,
+	UNDO_LOG_STATUS_ACTIVE,
+	UNDO_LOG_STATUS_FULL,
+	UNDO_LOG_STATUS_DISCARDED
+} UndoLogStatus;
+
+/*
+ * Undo log persistence levels.  These have a one-to-one correspondence with
+ * relpersistence values, but are small integers so that we can use them as an
+ * index into the "logs" and "lognos" arrays.
+ */
+typedef enum
+{
+	UNDO_PERMANENT = 0,
+	UNDO_UNLOGGED = 1,
+	UNDO_TEMP = 2
+} UndoPersistence;
+
+#define UndoPersistenceLevels 3
+
+/*
+ * Convert from relpersistence ('p', 'u', 't') to an UndoPersistence
+ * enumerator.
+ */
+#define UndoPersistenceForRelPersistence(rp)						\
+	((rp) == RELPERSISTENCE_PERMANENT ? UNDO_PERMANENT :			\
+	 (rp) == RELPERSISTENCE_UNLOGGED ? UNDO_UNLOGGED : UNDO_TEMP)
+
+/*
+ * Convert from UndoPersistence to a relpersistence value.
+ */
+#define RelPersistenceForUndoPersistence(up)				\
+	((up) == UNDO_PERMANENT ? RELPERSISTENCE_PERMANENT :	\
+	 (up) == UNDO_UNLOGGED ? RELPERSISTENCE_UNLOGGED :		\
+	 RELPERSISTENCE_TEMP)
+
+/*
+ * Get the appropriate UndoPersistence value from a Relation.
+ */
+#define UndoPersistenceForRelation(rel)									\
+	(UndoPersistenceForRelPersistence((rel)->rd_rel->relpersistence))
+
+/* Type for offsets within undo logs */
+typedef uint64 UndoLogOffset;
+
+/* printf-family format string for UndoRecPtr. */
+#define UndoRecPtrFormat "%016" INT64_MODIFIER "X"
+
+/* printf-family format string for UndoLogOffset. */
+#define UndoLogOffsetFormat UINT64_FORMAT
+
+/* Number of blocks of BLCKSZ in an undo log segment file.  128 = 1MB. */
+#define UNDOSEG_SIZE 128
+
+/* Size of an undo log segment file in bytes. */
+#define UndoLogSegmentSize ((size_t) BLCKSZ * UNDOSEG_SIZE)
+
+/* The width of an undo log number in bits.  24 allows for 16.7m logs. */
+#define UndoLogNumberBits 24
+
+/* The maximum valid undo log number. */
+#define MaxUndoLogNumber ((1 << UndoLogNumberBits) - 1)
+
+/* The width of an undo log offset in bits.  40 allows for 1TB per log.*/
+#define UndoLogOffsetBits (64 - UndoLogNumberBits)
+
+/* Special value for undo record pointer which indicates that it is invalid. */
+#define	InvalidUndoRecPtr	((UndoRecPtr) 0)
+
+/* End-of-list value when building linked lists of undo logs. */
+#define InvalidUndoLogNumber -1
+
+/*
+ * The maximum amount of data that can be stored in an undo log.  Can be set
+ * artificially low to test full log behavior.
+ */
+#define UndoLogMaxSize ((UndoLogOffset) 1 << UndoLogOffsetBits)
+
+/* Type for numbering undo logs. */
+typedef int UndoLogNumber;
+
+/* Extract the undo log number from an UndoRecPtr. */
+#define UndoRecPtrGetLogNo(urp)					\
+	((urp) >> UndoLogOffsetBits)
+
+/* Extract the offset from an UndoRecPtr. */
+#define UndoRecPtrGetOffset(urp)				\
+	((urp) & ((UINT64CONST(1) << UndoLogOffsetBits) - 1))
+
+/* Make an UndoRecPtr from an log number and offset. */
+#define MakeUndoRecPtr(logno, offset)			\
+	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
+
+/* The number of unusable bytes in the header of each block. */
+#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+
+/* The number of usable bytes we can store per block. */
+#define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
+
+/* The database OID is not used in undo buffer tags. */
+#define UndoLogDatabaseOid InvalidOid
+
+/* Length of undo checkpoint filename */
+#define UNDO_CHECKPOINT_FILENAME_LENGTH	16
+
+/*
+ * UndoRecPtrIsValid
+ *		True iff undoRecPtr is valid.
+ */
+#define UndoRecPtrIsValid(undoRecPtr) \
+	((bool) ((UndoRecPtr) (undoRecPtr) != InvalidUndoRecPtr))
+
+/* Extract the relnode for an undo log. */
+#define UndoRecPtrGetRelNode(urp)				\
+	UndoRecPtrGetLogNo(urp)
+
+/* The only valid fork number for undo log buffers. */
+#define UndoLogForkNum MAIN_FORKNUM
+
+/* Compute the block number that holds a given UndoRecPtr. */
+#define UndoRecPtrGetBlockNum(urp)				\
+	(UndoRecPtrGetOffset(urp) / BLCKSZ)
+
+/* Compute the offset of a given UndoRecPtr in the page that holds it. */
+#define UndoRecPtrGetPageOffset(urp)			\
+	(UndoRecPtrGetOffset(urp) % BLCKSZ)
+
+/* Compare two undo checkpoint files to find the oldest file. */
+#define UndoCheckPointFilenamePrecedes(file1, file2)	\
+	(strcmp(file1, file2) < 0)
+
+/* What is the offset of the i'th non-header byte? */
+#define UndoLogOffsetFromUsableByteNo(i)								\
+	(((i) / UndoLogUsableBytesPerPage) * BLCKSZ +						\
+	 UndoLogBlockHeaderSize +											\
+	 ((i) % UndoLogUsableBytesPerPage))
+
+/* How many non-header bytes are there before a given offset? */
+#define UndoLogOffsetToUsableByteNo(offset)				\
+	(((offset) % BLCKSZ - UndoLogBlockHeaderSize) +		\
+	 ((offset) / BLCKSZ) * UndoLogUsableBytesPerPage)
+
+/* Add 'n' usable bytes to offset stepping over headers to find new offset. */
+#define UndoLogOffsetPlusUsableBytes(offset, n)							\
+	UndoLogOffsetFromUsableByteNo(UndoLogOffsetToUsableByteNo(offset) + (n))
+
+/* Populate a RelFileNode from an UndoRecPtr. */
+#define UndoRecPtrAssignRelFileNode(rfn, urp)								 \
+	do																		 \
+	{																		 \
+		(rfn).spcNode = UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp)); \
+		(rfn).dbNode = UndoLogDatabaseOid;									 \
+		(rfn).relNode = UndoRecPtrGetRelNode(urp);							 \
+	} while (false);
+
+/*
+ * Properties of an undo log that don't have explicit WAL records logging
+ * their changes, to reduce WAL volume.  Instead, they change incrementally
+ * whenever data is inserted as a result of other WAL records.  Since the
+ * values recorded in an online checkpoint may be out of the sync (ie not the
+ * correct values as at the redo LSN), these are backed up in buffer data on
+ * first change after each checkpoint.
+ */
+typedef struct UndoLogUnloggedMetaData
+{
+	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
+	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
+	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
+} UndoLogUnloggedMetaData;
+
+/*
+ * Control metadata for an active undo log.  Lives in shared memory inside an
+ * UndoLogSlot object, but also written to disk during checkpoints.
+ */
+typedef struct UndoLogMetaData
+{
+	/* Members that are not managed by explicit WAL logs. */
+	UndoLogUnloggedMetaData unlogged;
+
+	/* Members that are fixed for the lifetime of the undo log. */
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoPersistence persistence;	/* permanent, unlogged, temp? */
+
+	/* Members that are changed by explicit WAL records. */
+	UndoLogStatus status;
+	UndoLogOffset end;				/* one past end of highest segment */
+	UndoLogOffset discard;			/* oldest data needed (tail) */
+} UndoLogMetaData;
+
+/*
+ * Context used to hold undo log state across all the undo log insertions
+ * corresponding to a single WAL record.
+ */
+typedef struct UndoLogAllocContext
+{
+	UndoPersistence persistence;
+	UndoRecPtr	try_location;
+	XLogReaderState *xlog_record;
+	UndoLogNumber recovery_logno;
+	uint8 		recovery_block_id;
+
+	/*
+	 * The maximum number of undo logs that a single WAL record could insert
+	 * into, modifying its unlogged meta data.  Typically the number is 1, but
+	 * it might touch a couple or more in rare cases where space runs out.
+	 */
+#define MAX_META_DATA_IMAGES 4
+	int			num_meta_data_images;
+	struct
+	{
+		UndoLogNumber logno;
+		UndoLogUnloggedMetaData data;
+	} meta_data_images[MAX_META_DATA_IMAGES];
+} UndoLogAllocContext;
+
+#ifndef FRONTEND
+
+/*
+ * The in-memory control object for an undo log.  We have a fixed-sized array
+ * of these.
+ */
+typedef struct UndoLogSlot
+{
+	/*
+	 * Protected by UndoLogLock and 'mutex'.  Both must be held to steal this
+	 * slot for another undolog.  Either may be held to prevent that from
+	 * happening.
+	 */
+	UndoLogNumber logno;			/* InvalidUndoLogNumber for unused slots */
+
+	/* Protected by UndoLogLock. */
+	UndoLogNumber next_free;		/* link for active unattached undo logs */
+
+	/* Protected by 'mutex'. */
+	LWLock		mutex;
+	UndoLogMetaData meta;			/* current meta-data */
+	pid_t		pid;				/* InvalidPid for unattached */
+
+	/* Protected by 'discard_lock'.  State used by undo workers. */
+	TransactionId	oldest_xid;		/* cache of oldest transaction's xid */
+	uint32		oldest_xidepoch;
+	UndoRecPtr	oldest_data;
+	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
+} UndoLogSlot;
+
+extern UndoLogSlot *UndoLogGetSlot(UndoLogNumber logno, bool missing_ok);
+extern UndoLogSlot *UndoLogNextSlot(UndoLogSlot *slot);
+extern bool AmAttachedToUndoLogSlot(UndoLogSlot *slot);
+extern UndoRecPtr UndoLogGetOldestRecord(UndoLogNumber logno, bool *full);
+
+/*
+ * Each backend maintains a small hash table mapping undo log numbers to
+ * UndoLogSlot objects in shared memory.
+ *
+ * We also cache the tablespace and persistence here, since we need fast
+ * access to those in a couple of places.  We could also reach those via
+ * slot->meta, but they can't be accessed without locking (since the
+ * UndoLogSlot object might be recycled).  Since these properties are constant
+ * for lifetime of the undo log number, there is no cache invalidation problem to
+ * worry about.
+ */
+typedef struct UndoLogTableEntry
+{
+	UndoLogNumber	number;
+	UndoLogSlot	   *slot;
+	Oid				tablespace;
+	UndoPersistence	persistence;
+	char			status;
+} UndoLogTableEntry;
+
+/*
+ * Instantiate fast inline hash table access functions.  We use an identity
+ * hash function for speed, since we already have integers and don't expect
+ * many collisions.
+ */
+#define SH_PREFIX undologtable
+#define SH_ELEMENT_TYPE UndoLogTableEntry
+#define SH_KEY_TYPE UndoLogNumber
+#define SH_KEY number
+#define SH_HASH_KEY(tb, key) (key)
+#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_SCOPE static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+extern PGDLLIMPORT undologtable_hash *undologtable_cache;
+
+/*
+ * Find or create an UndoLogTableGetEntry for this log number.  This is used
+ * only for fast look-ups of tablespace and persistence.
+ */
+static pg_attribute_always_inline UndoLogTableEntry *
+UndoLogGetTableEntry(UndoLogNumber logno)
+{
+	UndoLogTableEntry  *entry;
+
+	/* Fast path. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		return entry;
+
+	/* Slow path: force cache entry to be created. */
+	UndoLogGetSlot(logno, false);
+	entry = undologtable_lookup(undologtable_cache, logno);
+
+	return entry;
+}
+
+/*
+ * Look up the tablespace for an undo log in our cache.
+ */
+static inline Oid
+UndoLogNumberGetTablespace(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->tablespace;
+}
+
+static inline Oid
+UndoRecPtrGetTablespace(UndoRecPtr urp)
+{
+	return UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp));
+}
+
+/*
+ * Look up the persistence level for an undo log in our cache.
+ */
+static inline UndoPersistence
+UndoLogNumberGetPersistence(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->persistence;
+}
+
+static inline UndoPersistence
+UndoRecPtrGetPersistence(UndoRecPtr urp)
+{
+	return UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(urp));
+}
+
+#endif
+
+/* Space management. */
+extern void UndoLogBeginInsert(UndoLogAllocContext *context,
+							   UndoPersistence persistence,
+							   XLogReaderState *xlog_record);
+extern void UndoLogRegister(UndoLogAllocContext *context,
+							uint8 block_id,
+							UndoLogNumber logno);
+extern UndoRecPtr UndoLogAllocate(UndoLogAllocContext *context,
+								  uint16 size,
+								  bool *need_xact_header,
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
+extern UndoRecPtr UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+											TransactionId xid,
+											uint16 size,
+											bool *need_xact_header,
+											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp);
+extern void UndoLogAdvance(UndoLogAllocContext *context, size_t size);
+extern void UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size);
+extern bool UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
+extern bool UndoLogIsDiscarded(UndoRecPtr point);
+
+/* Initialization interfaces. */
+extern void StartupUndoLogs(XLogRecPtr checkPointRedo);
+extern void UndoLogShmemInit(void);
+extern Size UndoLogShmemSize(void);
+extern void UndoLogInit(void);
+extern void UndoLogDirectory(Oid tablespace, char *path);
+extern void UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace,
+							   char *path);
+extern void ResetUndoLogs(UndoPersistence persistence);
+
+/* Interface use by tablespace.c. */
+extern bool DropUndoLogsInTablespace(Oid tablespace);
+
+/* GUC interfaces. */
+extern void assign_undo_tablespaces(const char *newval, void *extra);
+
+/* Checkpointing interfaces. */
+extern void CheckPointUndoLogs(XLogRecPtr checkPointRedo,
+							   XLogRecPtr priorCheckPointRedo);
+
+/* File sync request management. */
+
+
+extern UndoRecPtr UndoLogGetLastXactStartPoint(UndoLogNumber logno);
+extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno,
+										  TransactionId xid);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
+extern void UndoLogSetLSN(XLogRecPtr lsn);
+void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
+/* Redo interface. */
+extern void undolog_redo(XLogReaderState *record);
+/* Discard the undo logs for temp tables */
+extern void TempUndoDiscard(UndoLogNumber);
+
+/* Test-only interfacing. */
+extern void UndoLogDetachFull(void);
+
+#endif
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
new file mode 100644
index 0000000..7b83d27
--- /dev/null
+++ b/src/include/access/undolog_xlog.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog_xlog.h
+ *	  undo log access XLOG definitions.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_XLOG_H
+#define UNDOLOG_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+
+/* XLOG records */
+#define XLOG_UNDOLOG_CREATE		0x00
+#define XLOG_UNDOLOG_EXTEND		0x10
+#define XLOG_UNDOLOG_DISCARD	0x20
+#define XLOG_UNDOLOG_SWITCH		0x30
+
+/* Create a new undo log. */
+typedef struct xl_undolog_create
+{
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoPersistence persistence;
+} xl_undolog_create;
+
+/* Extend an undo log by adding a new segment. */
+typedef struct xl_undolog_extend
+{
+	UndoLogNumber logno;
+	UndoLogOffset end;
+} xl_undolog_extend;
+
+/* Discard space, and possibly destroy or recycle undo log segments. */
+typedef struct xl_undolog_discard
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	TransactionId latestxid;	/* latest xid whose undolog are discarded. */
+	bool		  entirely_discarded;
+} xl_undolog_discard;
+
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
+extern void undolog_desc(StringInfo buf,XLogReaderState *record);
+extern const char *undolog_identify(uint8 info);
+
+#endif
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 366b8d3..05b2239 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -46,6 +46,22 @@ extern Buffer XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode,
 									 ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
+extern bool XLogFindBlockId(XLogReaderState *record,
+							SmgrId smgrid,
+							RelFileNode rnode,
+							ForkNumber forknum,
+							BlockNumber blockno,
+							uint8 *block_id);
+
+extern XLogRedoAction XLogReadBufferForRedoBlock(XLogReaderState *record,
+												 SmgrId smgrid,
+												 RelFileNode rnode,
+												 ForkNumber forknum,
+												 BlockNumber blockno,
+												 ReadBufferMode mode,
+												 bool get_cleanup_lock,
+												 Buffer *buf);
+
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 8733524..1841ab4 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10677,4 +10677,11 @@
   proname => 'pg_partition_root', prorettype => 'regclass',
   proargtypes => 'regclass', prosrc => 'pg_partition_root' },
 
+# undo logs
+{ oid => '5032', descr => 'list undo logs',
+  proname => 'pg_stat_get_undo_logs', procost => '1', prorows => '10', proretset => 't',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,text,text,text,text,text,xid,int4,text}', proargmodes => '{o,o,o,o,o,o,o,o,o}',
+  proargnames => '{logno,persistence,tablespace,discard,insert,end,xid,pid,status}', prosrc => 'pg_stat_get_undo_logs' },
+
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0a3ad3a..1936c5d 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -934,6 +934,13 @@ typedef enum
 	WAIT_EVENT_TWOPHASE_FILE_READ,
 	WAIT_EVENT_TWOPHASE_FILE_SYNC,
 	WAIT_EVENT_TWOPHASE_FILE_WRITE,
+	WAIT_EVENT_UNDO_CHECKPOINT_READ,
+	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
+	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_READ,
+	WAIT_EVENT_UNDO_FILE_WRITE,
+	WAIT_EVENT_UNDO_FILE_FLUSH,
+	WAIT_EVENT_UNDO_FILE_SYNC,
 	WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ,
 	WAIT_EVENT_WAL_BOOTSTRAP_SYNC,
 	WAIT_EVENT_WAL_BOOTSTRAP_WRITE,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 256dcd5..060215f 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -38,8 +38,9 @@ typedef enum BufferAccessStrategyType
 typedef enum
 {
 	RBM_NORMAL,					/* Normal read */
-	RBM_ZERO_AND_LOCK,			/* Don't read from disk, caller will
-								 * initialize. Also locks the page. */
+	RBM_ZERO,					/* Don't read from disk, caller will
+								 * initialize. */
+	RBM_ZERO_AND_LOCK,			/* Like RBM_ZERO, but also locks the page. */
 	RBM_ZERO_AND_CLEANUP_LOCK,	/* Like RBM_ZERO_AND_LOCK, but locks the page
 								 * in "cleanup" mode */
 	RBM_ZERO_ON_ERROR,			/* Read, but return an all-zeros page on error */
@@ -171,7 +172,10 @@ extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BufferAccessStrategy strategy);
 extern Buffer ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode,
 						  ForkNumber forkNum, BlockNumber blockNum,
-						  ReadBufferMode mode, BufferAccessStrategy strategy);
+						  ReadBufferMode mode, BufferAccessStrategy strategy,
+						  char relpersistence);
+extern void ForgetBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+			 BlockNumber blockNum);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -228,6 +232,10 @@ extern void AtProcExit_LocalBuffers(void);
 
 extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation);
 
+/* in localbuf.c */
+extern void ForgetLocalBuffer(SmgrId smgrid, RelFileNode rnode,
+							  ForkNumber forkNum, BlockNumber blockNum);
+
 /* in freelist.c */
 extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
 extern void FreeAccessStrategy(BufferAccessStrategy strategy);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 9959258..2782bca 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -143,6 +143,7 @@ extern int	pg_fsync_writethrough(int fd);
 extern int	pg_fdatasync(int fd);
 extern void pg_flush_data(int fd, off_t offset, off_t amount);
 extern void fsync_fname(const char *fname, bool isdir);
+extern int	fsync_fname_ext(const char *fname, bool isdir, bool perm, int elevel);
 extern int	durable_rename(const char *oldfile, const char *newfile, int loglevel);
 extern int	durable_unlink(const char *fname, int loglevel);
 extern int	durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 08e0dc8..4abb344 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -220,7 +220,10 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_SXACT,
-	LWTRANCHE_FIRST_USER_DEFINED
+	LWTRANCHE_UNDOLOG,
+	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_REWIND,
+	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
 /*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 243efc6..04bdd17 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -70,6 +70,9 @@ typedef struct SMgrRelationData
 	int			md_num_open_segs[MAX_FORKNUM + 1];
 	struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
 
+	/* For use by implementations. */
+	void	   *private_data;
+
 	/* if unowned, list link in list of all unowned SMgrRelations */
 	dlist_node	node;
 } SMgrRelationData;
@@ -83,6 +86,7 @@ typedef enum SmgrId
 {
 	SMGR_INVALID = -1,
 	SMGR_MD = 0,		/* md.c */
+	SMGR_UNDO			/* undofile.c */
 } SmgrId;
 
 extern void smgrinit(void);
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index 16428c5..59f1da9 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -34,7 +34,8 @@ typedef enum SyncRequestType
  */
 typedef enum SyncRequestHandler
 {
-	SYNC_HANDLER_MD = 0			/* md smgr */
+	SYNC_HANDLER_MD = 0,		/* md smgr */
+	SYNC_HANDLER_UNDO = 1		/* undo smgr */
 } SyncRequestHandler;
 
 /*
diff --git a/src/include/storage/undofile.h b/src/include/storage/undofile.h
new file mode 100644
index 0000000..a5ec30f
--- /dev/null
+++ b/src/include/storage/undofile.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * undofile.h
+ *	  undo storage manager public interface declarations.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/undofile.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOFILE_H
+#define UNDOFILE_H
+
+#include "access/undolog.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "storage/smgr.h"
+#include "storage/sync.h"
+
+extern void undofile_init(void);
+extern void undofile_shutdown(void);
+extern void undofile_open(SMgrRelation reln);
+extern void undofile_close(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_create(SMgrRelation reln, ForkNumber forknum,
+							bool isRedo);
+extern bool undofile_exists(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum,
+							bool isRedo);
+extern void undofile_extend(SMgrRelation reln, ForkNumber forknum,
+		 BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_prefetch(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber blocknum);
+extern void undofile_read(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, char *buffer);
+extern void undofile_write(SMgrRelation reln, ForkNumber forknum,
+		BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+			BlockNumber blocknum, BlockNumber nblocks);
+extern BlockNumber undofile_nblocks(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_truncate(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber nblocks);
+extern void undofile_immedsync(SMgrRelation reln, ForkNumber forknum);
+
+/* Callbacks used by sync.c. */
+extern int undofile_syncfiletag(const FileTag *tag, char *path);
+extern bool undofile_filetagmatches(const FileTag *tag, const FileTag *candidate);
+
+/* Management of checkpointer requests. */
+extern void undofile_request_sync(UndoLogNumber logno, BlockNumber segno,
+								  Oid tablespace);
+extern void undofile_forget_sync(UndoLogNumber logno, BlockNumber segno,
+								 Oid tablespace);
+extern void undofile_forget_sync_tablespace(Oid tablespace);
+extern void undofile_request_sync_dir(Oid tablespace);
+
+#endif							/* UNDOFILE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e709177..84639ee 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -431,6 +431,8 @@ extern void GUC_check_errcode(int sqlerrcode);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
+extern bool check_undo_tablespaces(char **newval, void **extra, GucSource source);
+extern void assign_undo_tablespaces(const char *newval, void *extra);
 
 /* in catalog/namespace.c */
 extern bool check_search_path(char **newval, void **extra, GucSource source);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7d365c4..189920b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2010,6 +2010,16 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text));
+pg_stat_undo_logs| SELECT pg_stat_get_undo_logs.logno,
+    pg_stat_get_undo_logs.persistence,
+    pg_stat_get_undo_logs.tablespace,
+    pg_stat_get_undo_logs.discard,
+    pg_stat_get_undo_logs.insert,
+    pg_stat_get_undo_logs."end",
+    pg_stat_get_undo_logs.xid,
+    pg_stat_get_undo_logs.pid,
+    pg_stat_get_undo_logs.status
+   FROM pg_stat_get_undo_logs() pg_stat_get_undo_logs(logno, persistence, tablespace, discard, insert, "end", xid, pid, status);
 pg_stat_user_functions| SELECT p.oid AS funcid,
     n.nspname AS schemaname,
     p.proname AS funcname,
-- 
1.8.3.1

0006-Defect-and-enhancement-in-multi-log-support.patchapplication/octet-stream; name=0006-Defect-and-enhancement-in-multi-log-support.patchDownload
From 880f25a543783f8dc3784a51ab1c29b72f6b5b27 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Fri, 7 Jun 2019 15:03:37 +0530
Subject: [PATCH 06/14] Defect and enhancement in multi-log support

---
 src/backend/access/undo/undolog.c | 18 +++++++++---------
 src/include/access/undolog.h      | 20 ++++++++++----------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 67b08e7..014480f 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -909,11 +909,11 @@ UndoLogAllocateInRecovery(UndoLogAllocContext *context,
 			context->recovery_logno = slot->logno;
 
 			/* Read log switch information from meta and reset it. */
-			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
-			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+			*prevlog_xact_start = slot->meta.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.prevlog_last_urp;
 
-			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
-			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+			slot->meta.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.prevlog_last_urp = InvalidUndoRecPtr;
 
 			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
 		}
@@ -1253,8 +1253,8 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 	Assert(AmAttachedToUndoLogSlot(slot));
 
 	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
-	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
-	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	slot->meta.prevlog_xact_start = prevlog_xact_start;
+	slot->meta.prevlog_last_urp = prevlog_last_urp;
 	LWLockRelease(&slot->mutex);
 
 	/* Wal log the log switch. */
@@ -1262,7 +1262,7 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 		xl_undolog_switch xlrec;
 
 		xlrec.logno = logno;
-		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_xact_start = prevlog_xact_start;
 		xlrec.prevlog_last_urp = prevlog_xact_start;
 
 		XLogBeginInsert();
@@ -2543,8 +2543,8 @@ undolog_xlog_switch(XLogReaderState *record)
 	 * Restore the log switch information in the MyUndoLogState this will be
 	 * reset by following UndoLogAllocateDuringRecovery.
 	 */
-	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
-	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+	slot->meta.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.prevlog_last_urp = xlrec->prevlog_last_urp;
 }
 
 void
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index f8f6f44..994c6a6 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -203,16 +203,6 @@ typedef struct UndoLogUnloggedMetaData
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
-
-	/*
-	 * Below two variable are used during recovery when transaction's undo
-	 * records are split across undo logs.  Replay of switch will restore
-	 * these two undo record pointers which will be reset on next allocation
-	 * during recovery. */
-	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
-									 * in the previous log. */
-	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
-									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -233,6 +223,16 @@ typedef struct UndoLogMetaData
 	UndoLogStatus status;
 	UndoLogOffset end;				/* one past end of highest segment */
 	UndoLogOffset discard;			/* oldest data needed (tail) */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogMetaData;
 
 /*
-- 
1.8.3.1

0008-Test-module-for-undo-api.patchapplication/octet-stream; name=0008-Test-module-for-undo-api.patchDownload
From 26aaed974a81ec61cb9510e6ca7266f8b53bd1fa Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 13 Jun 2019 09:19:47 +0530
Subject: [PATCH 08/14] Test module for undo api

Basic test routine to test undo interface API.

Dilip Kumar
---
 src/test/modules/test_undo_api/Makefile            |  21 +++
 .../test_undo_api/expected/test_undo_api.out       |  12 ++
 .../modules/test_undo_api/sql/test_undo_api.sql    |   8 +
 .../modules/test_undo_api/test_undo_api--1.0.sql   |   6 +
 src/test/modules/test_undo_api/test_undo_api.c     | 185 +++++++++++++++++++++
 .../modules/test_undo_api/test_undo_api.control    |   4 +
 6 files changed, 236 insertions(+)
 create mode 100644 src/test/modules/test_undo_api/Makefile
 create mode 100644 src/test/modules/test_undo_api/expected/test_undo_api.out
 create mode 100644 src/test/modules/test_undo_api/sql/test_undo_api.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api--1.0.sql
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.c
 create mode 100644 src/test/modules/test_undo_api/test_undo_api.control

diff --git a/src/test/modules/test_undo_api/Makefile b/src/test/modules/test_undo_api/Makefile
new file mode 100644
index 0000000..deb3816
--- /dev/null
+++ b/src/test/modules/test_undo_api/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo/Makefile
+
+MODULE_big = test_undo_api
+OBJS = test_undo_api.o
+PGFILEDESC = "test_undo_api - a test module for the undo api layer"
+
+EXTENSION = test_undo_api
+DATA = test_undo_api--1.0.sql
+
+REGRESS = test_undo_api
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_api
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_api/expected/test_undo_api.out b/src/test/modules/test_undo_api/expected/test_undo_api.out
new file mode 100644
index 0000000..5496ddb
--- /dev/null
+++ b/src/test/modules/test_undo_api/expected/test_undo_api.out
@@ -0,0 +1,12 @@
+CREATE EXTENSION test_undo_api;
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
+ test_undo_api 
+---------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_undo_api/sql/test_undo_api.sql b/src/test/modules/test_undo_api/sql/test_undo_api.sql
new file mode 100644
index 0000000..fa6f789
--- /dev/null
+++ b/src/test/modules/test_undo_api/sql/test_undo_api.sql
@@ -0,0 +1,8 @@
+CREATE EXTENSION test_undo_api;
+
+--
+-- This test will insert the data in the undo using undo api and after that
+-- it will fetch the data and verify that whether we have got the same data
+-- back or not.
+--
+SELECT test_undo_api('permanent');
diff --git a/src/test/modules/test_undo_api/test_undo_api--1.0.sql b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
new file mode 100644
index 0000000..1aa4e02
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api--1.0.sql
@@ -0,0 +1,6 @@
+\echo Use "CREATE EXTENSION test_undo_api" to load this file. \quit
+
+CREATE FUNCTION test_undo_api(persistence text)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_undo_api/test_undo_api.c b/src/test/modules/test_undo_api/test_undo_api.c
new file mode 100644
index 0000000..396714b
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.c
@@ -0,0 +1,185 @@
+#include "postgres.h"
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "catalog/pg_class.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_undo_api);
+
+static void
+compare_undo_record(UnpackedUndoRecord *urp1, UnpackedUndoRecord *urp2)
+{
+	int	header_size = offsetof(UnpackedUndoRecord, uur_next) + sizeof(uint64);
+
+	/* Compare undo record header. */
+	if (strncmp((char *) urp1, (char *) urp2, header_size) != 0)
+		elog(ERROR, "undo header did not match");
+
+	/* Compare payload and tuple length. */
+	if (urp1->uur_payload.len != urp2->uur_payload.len)
+		elog(ERROR, "payload data length did not match");
+
+	if (urp1->uur_tuple.len != urp2->uur_tuple.len)
+		elog(ERROR, "tuple data length did not match");
+
+	/* Compare undo record payload data. */
+	if (strncmp(urp1->uur_payload.data, urp2->uur_payload.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo payload data did not match");
+
+	/* Compare undo record tuple data. */
+	if (strncmp(urp1->uur_tuple.data, urp2->uur_tuple.data, urp1->uur_tuple.len) != 0)
+		elog(ERROR, "undo tuple data did not match");
+}
+
+/*
+ * test_insert_and_fetch - test simple insert and fetch undo record API
+ */
+static void
+test_insert_and_fetch()
+{
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	char	data[5000];
+	int		 len = 5000;
+	UnpackedUndoRecord	undorecord = {0};
+	UnpackedUndoRecord *undorecord_out;
+	UndoRecPtr	undo_ptr;
+
+	/* Prepare dummy undo record*/
+	undorecord.uur_rmid = 1;
+	undorecord.uur_type = 2;
+	undorecord.uur_info = 0;
+	undorecord.uur_xid = 100;
+	undorecord.uur_cid = 1;
+	undorecord.uur_fork = MAIN_FORKNUM;
+	undorecord.uur_blkprev = 10;
+	undorecord.uur_block = 1;
+	undorecord.uur_offset = 10;
+
+	/* Insert large data so that record get split across pages. */
+	initStringInfo(&undorecord.uur_tuple);
+	memset(data, 'a', 5000);
+	appendBinaryStringInfo(&undorecord.uur_tuple,
+						   (char *) data,
+						   len);
+	initStringInfo(&undorecord.uur_payload);
+	appendBinaryStringInfo(&undorecord.uur_payload,
+						   (char *) data,
+						   len);
+	/* Prepare undo record. */
+	BeginUndoRecordInsert(&context, persistence, 2, NULL);
+	undo_ptr = PrepareUndoInsert(&context, &undorecord, InvalidFullTransactionId);
+
+	/* Insert prepared undo record under critical section. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	/* Fetch inserted undo record. */
+	undorecord_out = UndoFetchRecord(undo_ptr, InvalidBlockNumber,
+									 InvalidOffsetNumber,
+									 InvalidTransactionId, NULL,
+									 NULL);
+	/* compare undo records. */
+	compare_undo_record(&undorecord, undorecord_out);
+
+	UndoRecordRelease(undorecord_out);
+	pfree(undorecord.uur_tuple.data);
+}
+
+#define MAX_UNDO_RECORD 10
+/*
+ * test_bulk_fetch - test the bulk fetch API.
+ */
+static void
+test_bulk_fetch()
+{
+	int i;
+	UndoRecordInsertContext context = {{0}};
+	UndoPersistence persistence = UNDO_PERMANENT;
+	UndoRecInfo	urp_in_array[MAX_UNDO_RECORD];
+	UndoRecInfo *urp_out_array;
+	UnpackedUndoRecord	uur[MAX_UNDO_RECORD] = {{0}};
+	UndoRecPtr	undo_ptr;
+	int			nrecords = 0;
+
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		uur[i].uur_rmid = 1;
+		uur[i].uur_type = 2;
+		uur[i].uur_info = 0;
+		uur[i].uur_xid = 100;
+		uur[i].uur_cid = 1;
+		uur[i].uur_fork = MAIN_FORKNUM;
+		uur[i].uur_blkprev = 10;
+		uur[i].uur_block = i;
+		uur[i].uur_offset = i + 1;
+		urp_in_array[i].uur = &uur[i];
+	}
+
+	/* Prepare multiple undo records. */
+	BeginUndoRecordInsert(&context, persistence, MAX_UNDO_RECORD, NULL);
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		undo_ptr = PrepareUndoInsert(&context, &uur[i], InvalidFullTransactionId);
+		urp_in_array[i].urp = undo_ptr;
+	}
+
+	/* Insert them all in one shot. */
+	START_CRIT_SECTION();
+	InsertPreparedUndo(&context);
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+
+	undo_ptr = urp_in_array[MAX_UNDO_RECORD - 1].urp;
+
+	/*
+	 * Perform the bulk fetch. 2000 bytes are enough to hold 10 records.  Later
+	 * we can enhance this to test the fetch in multi batch by increasing the
+	 * record counts or reducing undo_apply_size to smaller value.
+	 */
+	urp_out_array = UndoBulkFetchRecord(&undo_ptr, urp_in_array[0].urp, 2000,
+										&nrecords, false);
+	/* Check whether we have got all the record we inserted. */
+	if (nrecords != MAX_UNDO_RECORD)
+		elog(ERROR, "undo record count did not match");
+
+	/* Compare all records we have fetch using bulk fetch API*/
+	for (i = 0; i < MAX_UNDO_RECORD; i++)
+	{
+		if (urp_in_array[i].urp != urp_out_array[MAX_UNDO_RECORD - 1 - i].urp)
+			elog(ERROR, "undo record pointer did not match");
+		compare_undo_record(urp_in_array[i].uur, urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+		UndoRecordRelease(urp_out_array[MAX_UNDO_RECORD - 1 - i].uur);
+	}
+}
+/*
+ * Undo API test module
+ */
+Datum
+test_undo_api(PG_FUNCTION_ARGS)
+{
+	/* Test simple insert and fetch record. */
+	test_insert_and_fetch();
+
+	/* Test undo record bulk fetch API*/
+	test_bulk_fetch();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_undo_api/test_undo_api.control b/src/test/modules/test_undo_api/test_undo_api.control
new file mode 100644
index 0000000..09df344
--- /dev/null
+++ b/src/test/modules/test_undo_api/test_undo_api.control
@@ -0,0 +1,4 @@
+comment = 'test_undo_api'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_api'
+relocatable = true
-- 
1.8.3.1

0009-undo-page-consistency-checker.patchapplication/octet-stream; name=0009-undo-page-consistency-checker.patchDownload
From 3fe97e7f7804240454e80b6c6027f9ff9bc84eef Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 13 Jun 2019 09:20:39 +0530
Subject: [PATCH 09/14] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar with help from Amit Khandekar and Rafia Sabih
---
 src/backend/access/undo/undoaccess.c |   5 +-
 src/backend/access/undo/undorecord.c | 145 ++++++++++++++++++++++++++++++++---
 src/backend/storage/page/bufpage.c   |  33 ++++++++
 src/include/access/undolog.h         |   2 +-
 src/include/access/undorecord.h      |   2 +-
 src/include/storage/bufpage.h        |  34 ++++++++
 6 files changed, 206 insertions(+), 15 deletions(-)

diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 8d668e4..7e7c313 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -775,7 +775,10 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 				 * block header.
 				 */
 				if (starting_byte == UndoLogBlockHeaderSize)
-					PageInit(page, BLCKSZ, 0);
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
 
 				/*
 				 * Try to insert the record into the current page. If it
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index 08b3151..b323350 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 
+#include "access/bufmask.h"
 #include "access/subtrans.h"
 #include "access/undorecord.h"
 #include "catalog/pg_tablespace.h"
@@ -25,28 +26,45 @@ static bool ReadUndoBytes(char *destptr, int readlen,
 			  char **readptr, char *endptr,
 			  int *total_bytes_read, int *partial_read);
 
-/*
- * Compute and return the expected size of an undo record.
- */
-Size
-UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+ /*
+  * Compute the header size of the undo record.
+  */
+static inline Size
+UndoRecordHeaderSize(uint8 uur_info)
 {
-	Size		size;
+	Size	size;
 
 	size = SizeOfUndoRecordHeader + sizeof(uint16);
-	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+	if ((uur_info & UREC_INFO_FORK) != 0)
 		size += sizeof(ForkNumber);
-	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
 		size += SizeOfUndoRecordBlock;
-	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	if ((uur_info & UREC_INFO_BLKPREV) != 0)
 		size += sizeof(UndoRecPtr);
-	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
 		size += SizeOfUndoRecordTransaction;
-	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
 		size += SizeOfUndoRecordLogSwitch;
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
 	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
 	{
-		size += SizeOfUndoRecordPayload;
 		size += uur->uur_payload.len;
 		size += uur->uur_tuple.len;
 	}
@@ -55,6 +73,30 @@ UndoRecordExpectedSize(UnpackedUndoRecord *uur)
 }
 
 /*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint8           uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size            size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload  *payload = (UndoRecordPayload *) page_ptr + size;
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
  * Compute size of the Unpacked undo record in memory
  */
 Size
@@ -74,6 +116,85 @@ UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
 	return size;
 }
 
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset = SizeOfUndoRecordHeader - sizeof(CommandId);
+	UndoPageHeader	phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page has
+	 * a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size	partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						   phdr->tuple_len + phdr->payload_len -
+						   phdr->record_offset;
+
+		/*
+		 * We just want to mask the cid in the undo record header.  So only if
+		 * the partial record in the current page include the undo record header
+		 * then we need to mask the cid bytes in this page.  Otherwise, directly
+		 * jump to the next record.
+		 */
+		if (phdr->record_offset < SizeOfUndoRecordHeader)
+		{
+			char   *cid_data;
+			Size	mask_size;
+
+			mask_size = Min(SizeOfUndoRecordHeader -
+							phdr->record_offset, sizeof(CommandId));
+
+			cid_data = next_record + cid_offset - phdr->record_offset;
+			memset(&cid_data, MASK_MARKER, mask_size);
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/*
+		 * If this is not complete record then check whether cid is on
+		 * this page or not.  If not then we are done with this page.
+		 */
+		if (page_end - next_record < SizeOfUndoRecordHeader)
+		{
+			int		mask_size = page_end - next_record - cid_offset;
+
+			if (mask_size > 0)
+				memset(&header->urec_cid, MASK_MARKER, mask_size);
+			break;
+		}
+		else
+		{
+			/* Mask cid */
+			memset(&header->urec_cid, MASK_MARKER, sizeof(header->urec_cid));
+		}
+
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
+
 /*
  * Initiate inserting an undo record.
  *
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810..de609054 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,39 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint8 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	/*
+	 * TODO: We can update the value of the p->pd_lower whenever we insert
+	 * a record into an undo page.  By doing this we can avoid processing
+	 * complete undo page if there are no more records.
+	 */
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 994c6a6..d2b2c2a 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -128,7 +128,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 97b23d1..3e50570 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -20,7 +20,6 @@
 #include "storage/buf.h"
 #include "storage/off.h"
 
-
 /*
  * Every undo record begins with an UndoRecordHeader structure, which is
  * followed by the additional structures indicated by the contents of
@@ -251,5 +250,6 @@ extern void InsertUndoData(UndoPackContext *ucontext, Page page,
 			   int starting_byte);
 extern void SkipInsertingUndoData(UndoPackContext *ucontext,
 					  int bytes_to_skip);
+extern void mask_undo_page(char *pagedata);
 
 #endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 34b68ad..bbe42f9 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -216,6 +216,37 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * FIXME:  It should be declared in undolog.h ?
+ *
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+	/* Fields required for undolog consistency checker */
+	uint8		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -419,6 +450,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint8 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

0007-Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=0007-Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From 809d030fe8a99833e47a7b593bdad7aadecfe773 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 07/14] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoaccess.c         | 1537 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         |  797 +++++++++++++
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  118 ++
 src/include/access/undorecord.h              |  255 +++++
 7 files changed, 2738 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100644 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..049a416 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoaccess.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 0000000..8d668e4
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1537 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the next log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the first and last undo
+ * record of the transaction in the previous log.  The last undo record
+ * location is used find the previous undo record pointer during rollback.
+ * The first undo record location is used to find the previous transaction
+ * header which is required to update the undo apply progress.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoaccess.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+									UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress
+ *
+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */
+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	XactUndoRecordInfo *xact_info;
+
+	xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Is there some byte of the urec_next on the current block, if not then
+	 * start from the next block.
+	 */
+	if (remaining_bytes <= offset)
+	{
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+		starting_byte += (offset - remaining_bytes);
+	}
+	else
+		starting_byte += offset;
+
+	/*
+	 * Set the offset in the first block where we need to start writing, during
+	 * the prepare phase so that during update phase we need not to compute it
+	 * again.
+	 */
+	xact_info->offset = starting_byte;
+
+	/* Loop until we have fetched all the buffers in which we need to write. */
+	while (size > 0)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	xact_info->next = InvalidUndoRecPtr;
+	xact_info->progress = 0;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	return (context->nxact_urec_info - 1);
+}
+
+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr xact_urp)
+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetPersistence(xact_urp) == UNDO_TEMP)
+		return;
+
+	slot = UndoLogGetSlot(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoLogIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_next);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the next pointer in xact_urec_info, this will be overwritten in
+	 * actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].next = urecptr;
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&slot->discard_update_lock);
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			i = 0;
+	int			write_bytes;
+	int			write_offset;
+	char	   *sourceptr;
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/* Whether to update the next or undo apply progress. */
+	if (UndoRecPtrIsValid(xact_info->next))
+	{
+		sourceptr = (char *) &xact_info->next;
+		write_bytes = sizeof(xact_info->next);
+	}
+	else
+	{
+		sourceptr = (char *) &xact_info->progress;
+		write_bytes = sizeof(xact_info->progress);
+	}
+
+	/* Where to start writing in the current block. */
+	write_offset = xact_info->offset;
+
+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */
+	while(write_bytes > 0)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+		int			can_write;
+		char	   *writeptr;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/* How may bytes can be written in the current page. */
+		can_write = Min((BLCKSZ - write_offset), write_bytes);
+
+		/*
+		 * If buffer is valid then write it otherwise just skip writing it but
+		 * compute the variable for writing into the next block.
+		 */
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+
+			/* Compute the write pointer. */
+			writeptr = (char *) page + write_offset;
+
+			/* Copy the bytes we can write. */
+			memcpy(writeptr, sourceptr, can_write);
+			MarkBufferDirty(buffer);
+		}
+
+		/* Update bookkeeping information. */
+		write_bytes -= can_write;
+		sourceptr += can_write;
+		write_offset = UndoLogBlockHeaderSize;
+		i++;
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+										   rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoPersistence(persistence));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, persistence, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ *
+ * In recovery, 'fxid' refers to the full transaction id stored in WAL,
+ * otherwise, it refers to the top full transaction id.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  FullTransactionId fxid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId txid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	if (!FullTransactionIdIsValid(fxid))
+	{
+		/* During recovery, we must have a valid transaction id. */
+		Assert(!InRecovery);
+		txid = GetTopFullTransactionId();
+	}
+	else
+	{
+		/*
+		 * Assign the top transaction id because undo log only stores mapping
+		 * for the top most transactions.
+		 */
+		Assert(InRecovery ||
+			   FullTransactionIdEquals(fxid, GetTopFullTransactionId()));
+		txid = fxid;
+	}
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */
+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.persistence);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	/* Initialize transaction related members. */
+	urec->uur_progress = InvalidBlockNumber;
+	if (need_xact_header)
+	{
+		/*
+		 * TODO: Should we set urec->uur_dbid automatically?  How can you do
+		 * that, in recovery -- can we extract it from xlog_record?  For now
+		 * assume that the caller set it explicitly.
+		 */
+	}
+	else
+	{
+		urec->uur_dbid = 0;
+
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	/*
+	 * If we have a valid undo start pointer in the previous log then we need
+	 * a log switch header otherwise we don't.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		urec->uur_prevurp = prevlogurp;
+		urec->uur_prevlogstart = prevlog_xact_start;
+	}
+	else
+	{
+		/* We don't need a log transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_LOGSWITCH;
+		size = UndoRecordExpectedSize(urec);
+	}
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareUpdateNext(context, urecptr, last_xact_start);
+
+	/*
+	 * If prevlog_xact_start is valid that means the transaction's undo are
+	 * split across the undo logs.  So we need to  update our own transaction
+	 * header in the previous log as well.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareUpdateNext(context, urecptr, prevlog_xact_start);
+	}
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					PageInit(page, BLCKSZ, 0);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/* Free memory allocated for the prepare undo and prepared buffers. */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+											   rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoPersistence(persistence));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+
+			/* Acquire shared lock on the buffer before reading undo from it. */
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		}
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * Helper function for UndoFetchRecord to reset the unpacked undo record.
+ */
+static void
+ResetUndoRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode *rnode,
+				RelFileNode *prevrec_rnode, Buffer *buffer)
+{
+	/*
+	 * If we have a valid buffer pinned then just ensure that we want to find
+	 * the next tuple from the same block.  Otherwise release the buffer and
+	 * set it invalid
+	 */
+	if (BufferIsValid(*buffer))
+	{
+		/*
+		 * Undo buffer will be changed if the next undo record belongs to a
+		 * different block or undo log.
+		 */
+		if ((UndoRecPtrGetBlockNum(urp) !=
+			 BufferGetBlockNumber(*buffer)) ||
+			(prevrec_rnode->relNode != rnode->relNode))
+		{
+			UnlockReleaseBuffer(*buffer);
+			*buffer = InvalidBuffer;
+		}
+	}
+
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Reset the urec before fetching the tuple */
+	urec->uur_tuple.data = NULL;
+	urec->uur_tuple.len = 0;
+	urec->uur_payload.data = NULL;
+	urec->uur_payload.len = 0;
+}
+
+/*
+ * Fetch undo record for given urp
+ *
+ * Fetch the next undo record for given blkno, offset and transaction id (if
+ * valid).  The same tuple can be modified by multiple transactions, so during
+ * undo chain traversal sometimes we need to distinguish based on transaction
+ * id.  Callers that don't have any such requirement can pass
+ * InvalidTransactionId.
+ *
+ * Start the search from urp.  Caller need to call UndoRecordRelease to release the
+ * resources allocated by this function.
+ *
+ * urec_ptr_out is undo record pointer of the qualified undo record if valid
+ * pointer is passed.
+ *
+ * callback function decides whether particular undo record satisfies the
+ * condition of caller.
+ *
+ * Returns the required undo record if found, otherwise, return NULL which
+ * means either the record is already discarded or there is no such record
+ * in the undo chain.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecPtr urp, BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback)
+{
+	RelFileNode rnode,
+				prevrec_rnode = {0};
+	UnpackedUndoRecord *urec = NULL;
+	Buffer		buffer = InvalidBuffer;
+	int			logno;
+
+	if (urec_ptr_out)
+		*urec_ptr_out = InvalidUndoRecPtr;
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory.
+	 */
+	urec = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/* Find the undo record pointer we are interested in. */
+	while (true)
+	{
+		UndoLogSlot *slot;
+
+		logno = UndoRecPtrGetLogNo(urp);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			/*
+			 * The undo log number is unknown.  Presumably it has been
+			 * entirely discarded.
+			 */
+			urp = InvalidUndoRecPtr;
+			break;
+		}
+
+		/*
+		 * Prevent UndoDiscardOneLog() from discarding data while we try to
+		 * read it.  Usually we would acquire log->mutex to read log->meta
+		 * members, but in this case we know that discard can't move without
+		 * also holding log->discard_lock.
+		 *
+		 * In Hot Standby mode log->oldest_data is never initialized because
+		 * it's get updated by undo discard worker whereas in HotStandby undo
+		 * logs are getting discarded using discard WAL.  So in HotStandby we
+		 * can directly check whether the undo record pointer is discarded or
+		 * not.  But, we can not do same for normal case because discard
+		 * worker can concurrently discard the undo logs.
+		 *
+		 * XXX We can avoid this check by always initializing log->oldest_data
+		 * in HotStandby mode as well whenever we apply discard WAL.  But, for
+		 * doing that we need to acquire discard lock just for setting this
+		 * variable?
+		 */
+		if (InHotStandby)
+		{
+			if (UndoLogIsDiscarded(urp))
+			{
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+		else
+		{
+			LWLockAcquire(&slot->discard_lock, LW_SHARED);
+			if (slot->logno != logno || urp < slot->oldest_data)
+			{
+				/*
+				 * The slot has been recycled because the undo log was
+				 * entirely discarded, or the pointer is before the oldest
+				 * data.
+				 */
+				LWLockRelease(&slot->discard_lock);
+				urp = InvalidUndoRecPtr;
+				break;
+			}
+		}
+
+		/* Fetch the current undo record. */
+		UndoGetOneRecord(urec, urp, rnode, slot->meta.persistence, &buffer);
+
+		/* Release the discard lock after fetching the record. */
+		if (!InHotStandby)
+			LWLockRelease(&slot->discard_lock);
+
+		if (blkno == InvalidBlockNumber)
+			break;
+
+		/* Check whether the undo record satisfies conditions */
+		if (callback(urec, blkno, offset, xid))
+			break;
+
+		urp = urec->uur_blkprev;
+		prevrec_rnode = rnode;
+
+		/* Get rnode for the current undo record pointer. */
+		UndoRecPtrAssignRelFileNode(rnode, urp);
+
+		/* Reset the current undo record before fetching the next. */
+		ResetUndoRecord(urec, urp, &rnode, &prevrec_rnode, &buffer);
+	}
+
+	/*
+	 * If we have not found any valid undo record that means it might have
+	 * already got discarded so release the memory we allocated for unpacked
+	 * undo record and set urec to NULL.
+	 */
+	if (!UndoRecPtrIsValid(urp))
+	{
+		pfree(urec);
+		urec = NULL;
+	}
+	else if (urec_ptr_out != NULL)
+		*urec_ptr_out = urp;
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	return urec;
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  char persistence)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(SMGR_UNDO, rnode, MAIN_FORKNUM,
+									  startblock++,
+									  RelPersistenceForUndoPersistence(persistence));
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	TransactionId xid = InvalidTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.
+	 */
+	if (!one_page)
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogSlot *slot;
+		UndoPersistence persistence;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			if (BufferIsValid(buffer))
+				UnlockReleaseBuffer(buffer);
+			return NULL;
+		}
+		persistence = slot->meta.persistence;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				UnlockReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, persistence);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoLogIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&slot->discard_lock, LW_SHARED);
+				if (slot->logno != logno || urecptr < slot->oldest_data)
+				{
+					/*
+					 * The undo log slot has been recycled because it was
+					 * entirely discarded, or the data has been discarded
+					 * already.
+					 */
+					LWLockRelease(&slot->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			LWLockRelease(&slot->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, persistence, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!TransactionIdIsValid(xid))
+				xid = uur->uur_xid;
+			else if (xid != uur->uur_xid)
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_blkprev;
+		else if (UndoRecPtrIsValid(uur->uur_prevurp))
+			urecptr = uur->uur_prevurp;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, persistence);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/*
+		 * Including current record, if we have crossed the memory limit or
+		 * undo log got switched then stop processing more records.  Remember to
+		 * set the from_urecptr so that on next call we can resume fetching undo
+		 * records where we left it.
+		 */
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		if (total_size >= undo_apply_size ||
+			UndoRecPtrIsValid(uur->uur_prevurp))
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		UnlockReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Release the resources allocated by UndoFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	pfree(urec);
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoPersistence(upersistence);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(SMGR_UNDO, rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, upersistence);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100644
index 0000000..08b3151
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,797 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+				char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+			  char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read);
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = SizeOfUndoRecordHeader + sizeof(uint16);
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+		size += sizeof(UndoRecPtr);
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += SizeOfUndoRecordPayload;
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_rmid = uur->uur_rmid;
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+	ucontext->urec_hd.urec_reloid = uur->uur_reloid;
+	ucontext->urec_hd.urec_xid = uur->uur_xid;
+	ucontext->urec_hd.urec_cid = uur->uur_cid;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		ucontext->urec_txn.urec_progress = uur->uur_progress;
+		ucontext->urec_txn.urec_xidepoch = uur->uur_xidepoch;
+		ucontext->urec_txn.urec_dbid = uur->uur_dbid;
+		ucontext->urec_txn.urec_next = uur->uur_next;
+	}
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record block prev if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		ucontext->urec_blkprev = uur->uur_blkprev;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		ucontext->urec_logswitch.urec_prevurp = uur->uur_prevurp;
+		ucontext->urec_logswitch.urec_prevlogstart = uur->uur_prevlogstart;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blkprev,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordLogSwitch)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordLogSwitch;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCKPREV;
+			/* fall through */
+		case UNDO_PACK_STAGE_BLOCKPREV:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLKPREV) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blkprev,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_logswitch,
+								   SizeOfUndoRecordLogSwitch,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_rmid = ucontext->urec_hd.urec_rmid;
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+	uur->uur_reloid = ucontext->urec_hd.urec_reloid;
+	uur->uur_xid = ucontext->urec_hd.urec_xid;
+	uur->uur_cid = ucontext->urec_hd.urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_progress = ucontext->urec_txn.urec_progress;
+		uur->uur_xidepoch = ucontext->urec_txn.urec_xidepoch;
+		uur->uur_dbid = ucontext->urec_txn.urec_dbid;
+		uur->uur_next = ucontext->urec_txn.urec_next;
+	}
+
+	/* Copy undo record block prev header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLKPREV) != 0)
+	{
+		uur->uur_blkprev = ucontext->urec_blkprev;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		uur->uur_prevurp = ucontext->urec_logswitch.urec_prevurp;
+		uur->uur_prevlogstart = ucontext->urec_logswitch.urec_prevlogstart;
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * other fields are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+	if (uur->uur_blkprev != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_BLKPREV;
+	if (uur->uur_next != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_TRANSACTION;
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 6cbb0c8..0ac7f73 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 0000000..e12029b
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/* undo record information */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * Typedef for callback function for UndoFetchRecord.
+ *
+ * This checks whether an undorecord satisfies the given conditions.
+ */
+typedef bool (*SatisfyUndoRecordCallback) (UnpackedUndoRecord *urec,
+										   BlockNumber blkno,
+										   OffsetNumber offset,
+										   TransactionId xid);
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* Undo record pointer to be updated. */
+	uint32		offset;			/* offset in page where to start updating. */
+	UndoRecPtr	next;			/* first urp of the next transaction which is to
+								 * be updated in transaction header */
+	BlockNumber	progress;		/* undo apply action progress. */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec, FullTransactionId fxid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+					   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+					 XLogRecPtr recptr);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecPtr urp,
+				BlockNumber blkno, OffsetNumber offset,
+				TransactionId xid, UndoRecPtr *urec_ptr_out,
+				SatisfyUndoRecordCallback callback);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+					UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords,
+					bool one_page);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoPersistence upersistence);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..97b23d1
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,255 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	RmgrId		urec_rmid;		/* RMGR [XXX:TODO: this creates an alignment
+								 * hole?] */
+	uint8		urec_type;		/* record type code */
+	uint8		urec_info;		/* flag bits */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	TransactionId urec_xid;		/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_cid) + sizeof(CommandId))
+
+/*
+ * If UREC_INFO_RELATION_DETAILS is set, an UndoRecordRelationDetails structure
+ * follows.
+ *
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ *
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ *
+ * If UREC_INFO_BLKPREV is set, an UndoRecordBlockPrev structure follows.
+ *
+ * If UREC_INFO_LOGSWITCH is set, an UndoRecordLogSwitch structure follows.
+ *
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordRelationDetails appears first.
+ */
+#define UREC_INFO_TRANSACTION				0x01
+#define UREC_INFO_FORK						0x02
+#define UREC_INFO_BLOCK						0x04
+#define UREC_INFO_BLKPREV					0x08
+#define UREC_INFO_LOGSWITCH					0x10
+#define UREC_INFO_PAYLOAD					0x20
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * Undo block number where we need to start reading the undo for applying
+	 * the undo action.   InvalidBlockNumber means undo applying hasn't started
+	 * for the transaction and MaxBlockNumber mean undo completely applied. And,
+	 * any other block number means we have applied partial undo so next we can
+	 * start from this block.
+	 */
+	BlockNumber	urec_progress;
+	uint32		urec_xidepoch;	/* epoch of the current transaction */
+	Oid			urec_dbid;		/* database id */
+
+	/*
+	 * Transaction's previous undo record pointer when a transaction spans
+	 * across undo logs.  The first undo record in the new log stores the
+	 * previous undo record pointer in the previous log as we can't calculate
+	 * that directly using prevlen during rollback.
+	 *
+	 * TODO: instead of keeping in transaction header we can have new log
+	 * switch header.
+	 */
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information of the transaction's undo in the previous log.  If a transaction
+ * is split across the undo logs then this header will be included in the first
+ * undo record of the transaction in next log.
+ */
+typedef struct UndoRecordLogSwitch
+{
+	UndoRecPtr	urec_prevurp;		/* Transaction's last undo record pointer in
+									 * the previous undo log. */
+	UndoRecPtr	urec_prevlogstart;	/* Transaction's first undo record pointer
+									 * in previous undo log. */
+} UndoRecordLogSwitch;
+
+#define SizeOfUndoRecordLogSwitch \
+	(offsetof(UndoRecordLogSwitch, urec_prevlogstart) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint8		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	TransactionId uur_xid;		/* transaction id */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_blkprev;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	uint32		uur_xidepoch;	/* epoch of the inserting transaction. */
+	BlockNumber	uur_progress;	/* undo applying progress, see detail comment in
+								 * UndoRecordTransaction */
+	Oid			uur_dbid;		/* database id */
+	UndoRecPtr	uur_next;		/* urec pointer of the next transaction */
+	UndoRecPtr	uur_prevlogstart; /* urec pointer to the first record in the
+									* previous log. */
+	UndoRecPtr	uur_prevurp;	/* urec pointer to the previous record in the
+								 * different log */
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+} UnpackedUndoRecord;
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_BLOCKPREV,	/* The next thing to be processed is the block
+								 * prev info. */
+	UNDO_PACK_STAGE_LOGSWITCH,	/* The next thing to be processed is the log
+								 * switch details. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecPtr	urec_blkprev;	/* Block prev */
+	UndoRecordLogSwitch urec_logswitch; /* Log switch header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
+				 int starting_byte, int *already_written,
+				 int remaining_bytes, uint16 undo_len, bool header_only);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+				 UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+				UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+			   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+					  int bytes_to_skip);
+
+#endif							/* UNDORECORD_H */
-- 
1.8.3.1

0010-Extend-binary-heap-functionality.patchapplication/octet-stream; name=0010-Extend-binary-heap-functionality.patchDownload
From 10e22330dfd84582cf5669a0e174ad0b68e112b9 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:02:23 +0530
Subject: [PATCH 10/14] Extend binary heap functionality.

Add the routines to allocate binary heap in shared memory and to remove
nth element from binray heap.  This routines will be used by latter commit
to add support for undo workers.

Author: Kuntal Ghosh and Amit Kapila
---
 src/backend/lib/binaryheap.c | 118 +++++++++++++++++++++++++++++++++++++++++++
 src/include/lib/binaryheap.h |  12 ++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..5f7454d 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -212,6 +243,79 @@ binaryheap_replace_first(binaryheap *heap, Datum d)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap in O(1).  The caller must ensure that this routine is not used on
+ * an empty heap and is not called with n greater than or equal to the heap
+ * size.
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer to it in
+ * O(1) without preserving the heap property.  This is a convenience routine
+ * to remove elements quickly.  To obtain a valid heap, one must call
+ * binaryheap_build() afterwards.  The caller must ensure that this routine is
+ * not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * Swap the contents of two nodes.
  */
 static inline void
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 21f96b9..ed9e8e8 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -38,8 +38,11 @@ typedef struct binaryheap
 } binaryheap;
 
 extern binaryheap *binaryheap_allocate(int capacity,
-									   binaryheap_comparator compare,
-									   void *arg);
+					binaryheap_comparator compare,
+					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
+extern Size binaryheap_shmem_size(int capacity);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
-- 
1.8.3.1

0013-Allow-foreground-transactions-to-perform-undo-action.patchapplication/octet-stream; name=0013-Allow-foreground-transactions-to-perform-undo-action.patchDownload
From 66725e4994e5999ddf845f24f2ea2afb90c892c8 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:42:46 +0530
Subject: [PATCH 13/14] Allow foreground transactions to perform undo actions
 on abort.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

Amit Kapila and Dilip Kumar  with inputs from Robert Haas
---
 src/backend/access/transam/twophase.c         |  82 +++-
 src/backend/access/transam/varsup.c           |  24 ++
 src/backend/access/transam/xact.c             | 595 +++++++++++++++++++++++++-
 src/backend/access/undo/README.UndoProcessing |  39 ++
 src/backend/access/undo/undoaccess.c          |   7 +
 src/backend/utils/error/elog.c                |  36 +-
 src/backend/utils/init/globals.c              |   6 +
 src/backend/utils/resowner/resowner.c         |  11 +-
 src/include/access/transam.h                  |   1 +
 src/include/access/twophase.h                 |   3 +-
 src/include/access/xact.h                     |  10 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/elog.h                      |   3 +
 13 files changed, 798 insertions(+), 21 deletions(-)
 create mode 100644 src/backend/access/undo/README.UndoProcessing

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 5196d61..d3b221d 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1012,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1044,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1485,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
+	bool		undo_action_pushed[UndoPersistenceLevels];
+	uint32		epoch;
+	int			i;
+	FullTransactionId full_xid;
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1528,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1518,6 +1545,19 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * TransactionIdIsInProgress will stop saying the prepared xact is in
 	 * progress), then run the post-commit or post-abort callbacks. The
 	 * callbacks will release the locks the transaction held.
+	 *
+	 * XXX Note that, unlike non-two-phase transactions, we don't skip
+	 * releasing the locks when we have to perform the undo actions.  The
+	 * reason is that here the locks are not directly associated with current
+	 * transaction, so we need to keep the two phase records unprocessed (or
+	 * find some other way to associate it with current transaction's resource
+	 * owner) till the undo actions are completetly applied.  Also, we might
+	 * need to retain TwoPhaseStateLock till the locks are released which is
+	 * again not a good idea.  The downside is that we might face deadlock
+	 * while applying undo actions which ideally we should try to avoid but
+	 * not sure if that is worth complicating this subsytem.  Anyway,
+	 * currently such deadlock risks are there while executing undo actions
+	 * for toast relations as well.
 	 */
 	if (isCommit)
 		RecordTransactionCommitPrepared(xid,
@@ -1526,10 +1566,36 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 										hdr->ninvalmsgs, invalmsgs,
 										hdr->initfileinval, gid);
 	else
+	{
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id in
+		 * TwoPhaseFileHeader instead.)
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Register the rollback request to apply undo actions.  It is
+		 * important to do this before marking it aborted in clog, see
+		 * comments atop PushUndoRequest for further details.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (end_urec_ptr[i] != InvalidUndoRecPtr && i != UNDO_TEMP)
+			{
+				undo_action_pushed[i] = RegisterRollbackReq(end_urec_ptr[i],
+															start_urec_ptr[i],
+															hdr->database,
+															full_xid);
+			}
+		}
+
 		RecordTransactionAbortPrepared(xid,
 									   hdr->nsubxacts, children,
 									   hdr->nabortrels, abortrels,
 									   gid);
+	}
 
 	ProcArrayRemove(proc, latestXid);
 
@@ -1612,6 +1678,20 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	if (!isCommit)
+	{
+		/*
+		 * Perform undo actions, if there are undologs for this transaction.
+		 * We need to perform undo actions while we are still in a transaction.
+		 */
+		if (!PerformUndoActions(full_xid, hdr->database, end_urec_ptr,
+								start_urec_ptr, undo_action_pushed,
+								false))
+		{
+			FlushErrorState();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..fd01989 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -566,3 +566,27 @@ GetNewObjectId(void)
 
 	return result;
 }
+
+/*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 821652b..3253a0c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,15 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		undo_req_pushed[UndoPersistenceLevels]; /* undo request pushed
+														 * to worker? */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -339,6 +352,7 @@ static void ShowTransactionState(const char *str);
 static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
+static void PushUndoRequest(void);
 
 
 /* ----------------------------------------------------------------
@@ -362,9 +376,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +737,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +924,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -1885,6 +1904,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1968,6 +1988,15 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2264,6 +2293,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2280,7 +2311,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2433,7 +2464,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2622,7 +2653,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2780,6 +2813,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2880,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2943,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3032,14 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3008,7 +3056,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3052,6 +3100,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3065,7 +3131,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3087,7 +3153,10 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3176,10 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				UndoActionsRequired();
+				PushUndoRequest();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3160,6 +3232,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	UndoActionsRequired();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3175,7 +3255,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3190,7 +3274,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3287,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3215,7 +3305,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3226,7 +3318,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_END:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3255,7 +3349,9 @@ AbortCurrentTransaction(void)
 			 * Abort, cleanup, go to idle state.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3266,7 +3362,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_PREPARE:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3277,7 +3375,9 @@ AbortCurrentTransaction(void)
 			 * we get ROLLBACK.
 			 */
 		case TBLOCK_SUBINPROGRESS:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3291,7 +3391,9 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBCOMMIT:
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,10 +3406,173 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
 /*
+ * PushUndoRequest - Register the request for apllying undo actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+PushUndoRequest()
+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	volatile int per_level;
+
+	if (!s->performUndoActions)
+		return;
+	/*
+	 * We can't postpone applying undo actions for subtransactions as the
+	 * modifications made by aborted subtransaction must not be visible even if
+	 * the main transaction commits.
+	 */
+	if (IsSubTransaction())
+		return;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (per_level != UNDO_TEMP && s->latest_urec_ptr[per_level])
+		{
+			result = RegisterRollbackReq(s->latest_urec_ptr[per_level],
+										 s->start_urec_ptr[per_level],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undo_req_pushed[per_level] = result;
+		}
+	}
+}
+
+/*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	bool		ret;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	ret = PerformUndoActions(GetTopFullTransactionId(), MyDatabaseId,
+							 s->latest_urec_ptr, s->start_urec_ptr,
+							 s->undo_req_pushed,
+							 IsSubTransaction());
+
+	if (!ret)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
+	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
  *	PreventInTransactionBlock
  *
  *	This routine is to be called by statements that must not run inside
@@ -3633,6 +3898,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +4092,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4210,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4352,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4099,6 +4372,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4157,6 +4442,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4192,8 +4479,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4266,6 +4582,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4702,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4404,6 +4724,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4422,6 +4743,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4473,17 +4810,32 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	UndoActionsRequired();
+
+	/* Try to push rollback request to worker if possible. */
+	PushUndoRequest();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4514,6 +4866,14 @@ AbortOutOfAnyTransaction(void)
 	 */
 	do
 	{
+		/*
+		 * Here, we just detect whether there are any pending undo actions so that
+		 * we can skip releasing the locks during abort transaction.  We don't
+		 * release the locks till we execute undo actions otherwise, there is a
+		 * risk of deadlock.
+		 */
+		UndoActionsRequired();
+
 		switch (s->blockState)
 		{
 			case TBLOCK_DEFAULT:
@@ -4529,7 +4889,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4545,6 +4909,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_ABORT_PENDING:
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
+				PushUndoRequest();
+				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
@@ -4572,6 +4950,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBCOMMIT:
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
+				PushUndoRequest();
+				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
@@ -4666,6 +5058,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4705,6 +5099,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4722,6 +5117,15 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4909,7 +5313,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5336,6 +5741,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5761,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5788,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6388,155 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * UndoActionsRequired - Set the information required to perform undo actions.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing the locks if required.
+ */
+void
+UndoActionsRequired(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
+
+/*
+ * PerformUndoActions - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ */
+bool
+PerformUndoActions(FullTransactionId fxid, Oid dbid, UndoRecPtr *end_urec_ptr,
+				   UndoRecPtr *start_urec_ptr, bool *undo_req_pushed,
+				   bool isSubTrans)
+{
+	volatile	UndoRequestInfo urinfo;
+	uint32		save_holdoff;
+	int			per_level;
+	bool		success = true;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (end_urec_ptr[per_level] && !undo_req_pushed[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[per_level];
+
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(urinfo.full_xid,
+									 end_urec_ptr[per_level],
+									 start_urec_ptr[per_level],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
+
+/*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..e0caf9e
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,39 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 2894cc7..8334291 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -835,6 +835,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.persistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de..4e751d0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -123,6 +123,12 @@ int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
 
 /*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
+
+/*
  * Primary determinants of sizes of shared-memory structures.
  *
  * MaxBackends is computed by PostmasterMain after modules have had a chance to
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c4..3b2a28b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 3c5dcfb..90f39ee 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -232,6 +232,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern uint32 GetEpochForXid(TransactionId xid);
 
 /*
  * Some frontend programs include this header.  For compilers that emit static
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..497b92f 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,6 +14,7 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undolog.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
@@ -41,7 +42,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726a..4e0099f 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/undolog.h"
 #include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
@@ -427,6 +428,15 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 int xactflags, TransactionId twophase_xid,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
+extern void ApplyUndoActions(void);
+extern void UndoActionsRequired(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+extern bool PerformUndoActions(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool *undo_req_pushed, bool isSubTrans);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+				UndoPersistence upersistence);
 
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1afc4d3..388dc97 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -246,6 +246,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
 
+extern bool applying_subxact_undo;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8ef..0b227ab 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
-- 
1.8.3.1

0011-Infrastructure-to-register-and-fetch-undo-action-req.patchapplication/octet-stream; name=0011-Infrastructure-to-register-and-fetch-undo-action-req.patchDownload
From c830f9d7023f228fd069c09408c76f131ff96aae Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:10:06 +0530
Subject: [PATCH 11/14] Infrastructure to register and fetch undo action
 requests.

This infrasture provides a way to allow execution of undo actions.  One
might think that we can always execute undo actions on error or explicit
rollabck by user, however there are cases when that is not posssible.
For example, (a) if the system crash while doing operation, then after
startup, we need a way to perform undo actions; (b) If we get error while
performing undo actions.

Apart from this, when there are large rollback requests, then it is quite
inefficient to perform all the undo actions and then return control to
user.

To allow efficient execution of the undo actions, we create three queues
and a hash table for the rollback requests.  A Xid based priority queue
which will allow us to process the requests of older transactions and help
us to move oldesdXidHavingUnappliedUndo (this is a xid-horizon below which
all the transactions are visible) forward.  A size-based queue which will
help us to perform the rollbacks of larger aborts in a timely fashion so
that we don't get stuck while processing them during discard of the logs.
An error queue to hold the requests for transactions that failed to apply
its undo.  The rollback hash table is used to avoid duplicate undo requests
by backends and discard worker.

Amit Kapila and Kuntal Ghosh, design idea by Andres Freund.
---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undoaccess.c          |   51 +-
 src/backend/access/undo/undorecord.c          |    2 +-
 src/backend/access/undo/undorequest.c         | 1659 +++++++++++++++++++++++++
 src/backend/storage/lmgr/lwlocknames.txt      |    1 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    8 +
 src/include/access/transam.h                  |    1 +
 src/include/access/undoaccess.h               |    2 +
 src/include/access/undorecord.h               |    1 +
 src/include/access/undorequest.h              |  226 ++++
 src/include/miscadmin.h                       |    1 +
 src/include/storage/proc.h                    |    2 +
 15 files changed, 1991 insertions(+), 3 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 049a416..7327502 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o
+OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 7e7c313..2d413f7 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -46,6 +46,7 @@
 #include "access/undorecord.h"
 #include "access/undoaccess.h"
 #include "access/undolog_xlog.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -588,7 +589,7 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	}
 
 	/* Initialize transaction related members. */
-	urec->uur_progress = InvalidBlockNumber;
+	urec->uur_progress = XACT_APPLY_PROGRESS_NOT_STARTED;
 	if (need_xact_header)
 	{
 		/*
@@ -1538,3 +1539,51 @@ UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 	/* calculate the previous undo record pointer */
 	return MakeUndoRecPtr(logno, offset - prevlen);
 }
+
+/*
+ * Returns the undo record pointer corresponding to first record in the given
+ * block.
+ */
+UndoRecPtr
+UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+							UndoPersistence persistence)
+{
+	Buffer buffer;
+	Page page;
+	UndoPageHeader	phdr;
+	RelFileNode		rnode;
+	UndoLogOffset	log_cur_off;
+	Size			partial_rec_size;
+	int				offset_cur_page;
+
+	if (!BlockNumberIsValid(blkno))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid undo block number")));
+
+	UndoRecPtrAssignRelFileNode(rnode, urec_ptr);
+
+	buffer = ReadBufferWithoutRelcache(SMGR_UNDO,
+									   rnode, UndoLogForkNum, blkno,
+									   RBM_NORMAL, NULL,
+									   RelPersistenceForUndoPersistence(persistence));
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader)page;
+
+	/* Calculate the size of the partial record. */
+	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						phdr->tuple_len + phdr->payload_len -
+						phdr->record_offset;
+
+	/* calculate the offset in current log. */
+	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
+	log_cur_off = (blkno * BLCKSZ) + offset_cur_page;
+
+	UnlockReleaseBuffer(buffer);
+
+	/* calculate the undo record pointer based on current offset in log. */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urec_ptr), log_cur_off);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index b323350..3d88c79 100644
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -29,7 +29,7 @@ static bool ReadUndoBytes(char *destptr, int readlen,
  /*
   * Compute the header size of the undo record.
   */
-static inline Size
+Size
 UndoRecordHeaderSize(uint8 uur_info)
 {
 	Size	size;
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..28e844e
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1659 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To ensure that backend and discard worker don't register the same request
+ * in the hash table, we always register the request with full_xid and the
+ * start pointer for the transaction in the hash table as key.  Backends
+ * always remember the value of start pointer, but discard worker doesn't know
+ * the actual start value in case transaction's undo spans across multiple
+ * logs.  The reason for the same is that discard worker might encounter the
+ * log which has overflowed undo records of the transaction first.  In such
+ * cases, we need to compute the actual start position.  The first record of a
+ * transaction in each undo log contains a reference to the first record of
+ * this transaction in the previous log.  By following the previous log chain
+ * of this transaction, we find the initial location which is used to register
+ * the request.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+#include "storage/proc.h"
+
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+#define UNDO_FAILURE_RETRY_DELAY_MS 10000
+
+int			rollback_overflow_size = 64;
+int			pending_undo_queue_size = 1024;
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_retry_at, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).next_retry_at = e_retry_at, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdFollows(xidQueueElem1->full_xid,
+									  xidQueueElem2->full_xid))
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size < sizeQueueElem2->request_size)
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->next_retry_at < errQueueElem2->next_retry_at)
+		return 1;
+	else if (errQueueElem1->next_retry_at > errQueueElem2->next_retry_at)
+		return -1;
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at > errQueueElem2->err_occurred_at)
+		return -1;
+	return 0;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * pending_undo_queue_size) + pending_undo_queue_size +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo *urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+	TimestampTz next_retry;
+
+	/*
+	 * We want to retry this error request after some constant amount of time,
+	 * rather than retrying immediately, otherwise, in some cases (ex. when
+	 * all the pending requests are failed requests) worker will keep retrying
+	 * such errors constantly.
+	 *
+	 * In future, we might want some more sophisticated back-off algorithm
+	 * to delay the execution of such requests.
+	 */
+	next_retry = TimestampTzPlusMilliseconds(now, UNDO_FAILURE_RETRY_DELAY_MS);
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid, next_retry, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->next_retry_at = 0;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->next_retry_at = 0;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+			return false;
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+
+		/*
+		 * If it is too early to try the error request again, then check the
+		 * work in some other queue.
+		 */
+		if (GetCurrentTimestamp() < elem->next_retry_at)
+			return false;
+
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * last_log_start_urec_ptr_out - This is an OUT parameter.  If a transaction
+ * writes undo records in multiple undo logs, this is set to the start undo
+ * record pointer of this transaction in the last log.  If the transaction
+ * writes undo records only in single undo log, it is set to start_urec_ptr.
+ * This value is used to update the rollback progress of the transaction in
+ * the last log.  Once, we have start location in last log, the start location
+ * in all the previous logs can be computed.  See execute_undo_actions for
+ * more details.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+static uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   UndoRecPtr *last_log_start_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	UndoRecPtr	last_log_start_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoPersistence persistence;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				last_log_start_urecptr = urecptr;
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+		persistence = slot->meta.persistence;
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * If the corresponding undo record got rolled back and discarded as
+		 * well, we return from here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * Case 1: Check whether any undo records have been applied from this
+		 * log.  Else, we've to find the undo location till where the undo
+		 * actions have been applied.
+		 */
+		if (!IsXactApplyProgressNotStarted(uur->uur_progress))
+		{
+			/*
+			 * If all the undo records in this log corresponding to this
+			 * transaction, has been applied, we return from here.
+			 */
+			if (IsXactApplyProgressCompleted(uur->uur_progress))
+				break;
+
+			/*
+			 * Find the first undo record of uur_progress block number.  We'll
+			 * set end_urec_ptr to this undo record.
+			 */
+			end_urecptr = UndoBlockGetFirstUndoRecord(uur->uur_progress, urecptr,
+													  persistence);
+
+			/*
+			 * Since rollbacks from this undo log are in-progress, all undo
+			 * records from subsequent undo logs must have been applied.  Hence,
+			 * this is the last log.  So, we set last_log_start_urecptr as the
+			 * start undo record pointer of this transaction from current log.
+			 */
+			last_log_start_urecptr = urecptr;
+			sz += (end_urecptr - urecptr);
+			break;
+		}
+
+		next_urecptr = uur->uur_next;
+
+		/*
+		 * Case 2: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			/*
+			 * While fetching the next insert location if the new transaction
+			 * has already started in this log then lets re-fetch the undo
+			 * record.
+			 */
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			if (!UndoRecPtrIsValid(next_insert))
+			{
+				UndoRecordRelease(uur);
+				uur = NULL;
+				continue;
+			}
+
+			/*
+			 * If next_insert location points to the starting location of a
+			 * new page, we should subtract the page header size from the
+			 * insert location.
+			 */
+			if (UndoRecPtrGetPageOffset(next_insert) == UndoLogBlockHeaderSize)
+				next_insert -= UndoLogBlockHeaderSize;
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == slot->logno)
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidBuffer, persistence);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoLogIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 5: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log but before that consider
+		 * this log for request size computation.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, persistence);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+	if (last_log_start_urecptr_out &&
+		(*last_log_start_urecptr_out == InvalidUndoRecPtr))
+		*last_log_start_urecptr_out = last_log_start_urecptr;
+
+	return sz;
+}
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	{
+		if (GetXidQueueSize() >= pending_undo_queue_size ||
+			GetSizeQueueSize() >= pending_undo_queue_size)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < pending_undo_queue_size))
+		{
+			Assert(GetSizeQueueSize() < pending_undo_queue_size);
+
+			/*
+			 * XXX - Here, we should return true once we have background
+			 * worker facility.
+			 */
+			return false;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(pending_undo_queue_size)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 pending_undo_queue_size,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 pending_undo_queue_size,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 pending_undo_queue_size,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Error Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= pending_undo_queue_size)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as UNDO_REQUEST_INQUEUE so that undo
+	 * launcher or other undo worker can process this request.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->status = UNDO_REQUEST_INQUEUE;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->status = UNDO_REQUEST_INPROGRESS;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || UndoRequestIsInProgress(rh))
+					continue;
+
+				/*
+				 * The request that is present in any queue must be a valid request
+				 * and its status must be in_queue.
+				 */
+				Assert(UndoRequestIsValid(rh));
+				Assert(UndoRequestIsInQueue(rh));
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->status = UNDO_REQUEST_INPROGRESS;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself if the request is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues.  The two reasons why request can't be pushed are (a) the size of
+ * request is smaller than a threshold and the request is not from discard
+ * worker, (b) the undo request queues are full.
+ *
+ * It is not advisable to apply the undo actions of a very large transaction
+ * in the foreground as that can lead to a delay in retruning the control back
+ * to user after abort.
+ */
+bool
+RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+	UndoRecPtr	last_log_start_urec_ptr = InvalidUndoRecPtr;
+	RollbackHashKey hkey;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * Find the rollback request size and the end_urec_ptr (in case of discard
+	 * worker only).
+	 */
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr,
+										  &last_log_start_urec_ptr, full_xid);
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+										   HASH_ENTER_NULL, &found);
+
+	/*
+	 * It can only fail, if the value of pending_undo_queue_size or
+	 * max_connections guc is reduced after restart of the server.
+	 */
+	if (rh == NULL)
+	{
+		Assert(RollbackHTIsFull());
+
+		ereport(PANIC,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("rollback hash table is full, try running with higher value of pending_undo_queue_size")));
+	}
+
+	/* We shouldn't try to add the same rollback request again. */
+	if (!found)
+	{
+		rh->start_urec_ptr = start_urec_ptr;
+		rh->end_urec_ptr = end_urec_ptr;
+		rh->last_log_start_urec_ptr = last_log_start_urec_ptr;
+		rh->dbid = dbid;
+		rh->full_xid = full_xid;
+
+		/* Increment the pending request counter. */
+		ProcGlobal->xactsHavingPendingUndo++;
+
+		if (can_push)
+		{
+			UndoRequestInfo urinfo;
+
+			ResetUndoRequestInfo(&urinfo);
+
+			urinfo.full_xid = rh->full_xid;
+			urinfo.start_urec_ptr = rh->start_urec_ptr;
+			urinfo.end_urec_ptr = rh->end_urec_ptr;
+			urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+			urinfo.dbid = rh->dbid;
+			urinfo.request_size = req_size;
+
+			InsertRequestIntoUndoQueues(&urinfo);
+
+			/*
+			 * Indicates that the request will be processed by undo
+			 * worker.
+			 */
+			rh->status = UNDO_REQUEST_INQUEUE;
+			pushed = true;
+		}
+		/*
+		 * The request can't be pushed into the undo worker queue.  The
+		 * backends will try executing by itself.
+		 */
+		else
+			rh->status = UNDO_REQUEST_INPROGRESS;
+	}
+	else if (!UndoRequestIsValid(rh) && can_push)
+	{
+		/*
+		 * If we found the request which is still not in queue or not in
+		 * progress then add it to the queue if there is a space in the queue.
+		 */
+		UndoRequestInfo urinfo;
+
+		ResetUndoRequestInfo(&urinfo);
+
+		urinfo.full_xid = rh->full_xid;
+		urinfo.start_urec_ptr = rh->start_urec_ptr;
+		urinfo.end_urec_ptr = rh->end_urec_ptr;
+		urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+		urinfo.dbid = rh->dbid;
+		urinfo.request_size = req_size;
+
+		InsertRequestIntoUndoQueues(&urinfo);
+
+		/* Indicates that the request will be processed by the undo worker */
+		rh->status = UNDO_REQUEST_INQUEUE;
+		pushed = true;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return pushed;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	/* Decrement the pending request counter. */
+	ProcGlobal->xactsHavingPendingUndo--;
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Mark the entry status as invalid in the rollback hash table.
+ */
+void
+RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+						   UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	rh->status = UNDO_REQUEST_INVALID;
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Returns the start undo record pointer for the last undo log in which
+ * transaction has spanned.  This will be different from start_urec_ptr only
+ * when the undo for a transaction has spanned across multiple undo logs.
+ */
+UndoRecPtr
+RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+							 UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+	UndoRecPtr	last_log_start_urecptr;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	last_log_start_urecptr = rh->last_log_start_urec_ptr;
+	LWLockRelease(RollbackRequestLock);
+
+	return last_log_start_urecptr;
+}
+
+/*
+ * Returns true, if the rollback hash table is full, false, otherwise.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
+
+/*
+ * Get the smallest of 'xid having pending undo' and 'oldestXmin'.
+ */
+FullTransactionId
+RollbackHTGetOldestFullXid(FullTransactionId oldestXmin)
+{
+	RollbackHashEntry   *rh;
+	FullTransactionId	oldestXid = oldestXmin;
+	HASH_SEQ_STATUS		status;
+
+	/* Fetch the pending undo requests */
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	Assert(hash_get_num_entries(RollbackHT) <= UndoRollbackHashTableSize());
+	hash_seq_init(&status, RollbackHT);
+	while (RollbackHT != NULL &&
+		   (rh = (RollbackHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (!FullTransactionIdIsValid(oldestXid) ||
+			FullTransactionIdPrecedes(rh->full_xid, oldestXid))
+			oldestXid = rh->full_xid;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return oldestXid;
+}
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..19e4f1f 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,4 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 0da5b19..6126ed1 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	ProcGlobal->xactsHavingPendingUndo = 0;
 }
 
 /*
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index d88113f..3be16b8 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1086,6 +1086,20 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 }
 
 /*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
+/*
  * Process any command-line switches and any additional GUC variable
  * settings passed in the startup packet.
  */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b92645f..89ced6d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -3042,6 +3043,27 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"pending_undo_queue_size", PGC_POSTMASTER, RESOURCES_MEM,
+			gettext_noop("Sets the size of queue used to register undo requests"),
+			NULL,
+		},
+		&pending_undo_queue_size,
+		1024, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"old_snapshot_threshold", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
 			gettext_noop("Time before a snapshot is too old to read pages changed after the snapshot was taken."),
 			gettext_noop("A value of -1 disables this feature."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 5ee5e09..51e4929 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -604,6 +604,14 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# UNDO options
+#------------------------------------------------------------------------------
+
+#rollback_overflow_size = 64	# default size above which the undo
+					# requests are pushed to undo workers
+#pending_undo_queue_size = 1024	# size of queue used to register undo
+					# requests
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 0ac7f73..3c5dcfb 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index e12029b..139e2e8 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -114,5 +114,7 @@ extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
 extern void UndoRecordRelease(UnpackedUndoRecord *urec);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 					  UndoPersistence upersistence);
+extern UndoRecPtr UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+					  UndoPersistence persistence);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 3e50570..41029d0 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -234,6 +234,7 @@ typedef struct UndoPackContext
 } UndoPackContext;
 
 extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+extern Size UndoRecordHeaderSize(uint8 uur_info);
 extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
 extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
 extern bool InsertUndoRecord(UnpackedUndoRecord *uur, Page page,
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..81fc3d5
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,226 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+extern PGDLLIMPORT int	rollback_overflow_size;
+extern PGDLLIMPORT int	pending_undo_queue_size;
+
+/*
+ * Current status of the undo request in the hash table.
+ */
+typedef enum
+{
+	/*
+	 * Request is present in the rollback hash table, but not present in any
+	 * of the queues.  In this state, the undo actions can't be executed.
+	 *
+	 * The request will be marked with this status if a) discard worker finds
+	 * that there is no space in the undo worker queue for inserting the undo
+	 * request, b) there is an error while backend or undo worker is
+	 * executing undo actions and there is no space in the error queue.
+	 *
+	 * Later when the discard worker finds such entry and if there is a
+	 * sufficient space in the undo worker queues, then the request will be
+	 * added to them and the status will be changed to UNDO_REQUEST_INQUEUE.
+	 *
+	 * It is important to keep the request in hash table with this status
+	 * intsead of removing it to compute the value of
+	 * oldestXidHavingUnappliedUndo.  If we don't do that, then the
+	 * corresponding xid won't be considered for computation of
+	 * oldestXidHavingUnappliedUndo.
+	 */
+	UNDO_REQUEST_INVALID,
+
+	/*
+	 * When backend or discard worker push the request to undo worker queue the
+	 * status will be set to this.  Undo workers pulls such requests from the
+	 * queues, change the state as UNDO_REQUEST_INPROGRESS and process the undo
+	 * actions.
+	 */
+	UNDO_REQUEST_INQUEUE,
+
+	/*
+	 * Undo action execution is in progress either by backend or by undo worker.
+	 */
+	UNDO_REQUEST_INPROGRESS
+} UndoRequestStatus;
+
+/*
+ * UndoRequestIsValid
+ *		True iff undo request status is not invalid.
+ */
+#define UndoRequestIsValid(rh) \
+	((bool) ((rh->status) != UNDO_REQUEST_INVALID))
+
+ /*
+  * UndoRequestIsInProgress
+  *		True iff undo request status is in progress.
+  */
+#define UndoRequestIsInProgress(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INPROGRESS))
+
+/*
+ * UndoRequestIsInQueue
+ *		True iff undo request status is in queue.
+ */
+#define UndoRequestIsInQueue(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INQUEUE))
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	UndoRequestStatus	status;	/* current state of the entry. */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	TimestampTz next_retry_at;
+	TimestampTz err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->last_log_start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->last_log_start_urec_ptr = rh->last_log_start_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/*
+ * From an undo log if all the undo actions have been applied for a particular
+ * transaction, we set the uur_progress of the transaction's log in that undo
+ * log as MaxBlockNumber.  If none of the undo actions have yet been applied,
+ * we set it to InvalidBlockNumber.
+ */
+#define XACT_APPLY_PROGRESS_COMPLETED MaxBlockNumber
+#define XACT_APPLY_PROGRESS_NOT_STARTED InvalidBlockNumber
+
+#define IsXactApplyProgressCompleted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_COMPLETED)
+
+#define IsXactApplyProgressNotStarted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_NOT_STARTED)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+			UndoRequestInfo *urinfo, bool *in_other_db);
+
+/* Exposed functions for rollback hash table. */
+extern bool RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr);
+extern bool RollbackHTIsFull(void);
+extern int UndoRollbackHashTableSize(void);
+extern void RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+								UndoRecPtr start_urec_ptr);
+extern UndoRecPtr RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+											   UndoRecPtr start_urec_ptr);
+extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 61a24c2..1afc4d3 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -426,6 +426,7 @@ extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 						 Oid useroid, char *out_dbname, bool override_allow_connections);
+extern bool dbid_exists(Oid dboid);
 extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1cee7db..6a74d20 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Number of aborted transactions with pending undo actions. */
+	int			xactsHavingPendingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
-- 
1.8.3.1

0012-Infrastructure-to-execute-pending-undo-actions.patchapplication/octet-stream; name=0012-Infrastructure-to-execute-pending-undo-actions.patchDownload
From a9aba2a6ed57b592385c8c35fda5909d580f35ae Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:27:58 +0530
Subject: [PATCH 12/14] Infrastructure to execute pending undo actions.

To apply the undo actions, we collect the undo records in bulk and try to
process them together.  We ensure to update the transaction's progress at
regular intervals so that after a crash we can skip already applied undo.

This provides a way for users to register a callback for processing the
undo records based on resource manager.

Dilip Kumar, Amit Kapila, Thomas Munro and Kuntal Ghosh with inputs from
Robert Haas
---
 src/backend/access/rmgrdesc/Makefile         |   3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c |  47 +++
 src/backend/access/transam/rmgr.c            |   5 +-
 src/backend/access/undo/Makefile             |   3 +-
 src/backend/access/undo/undoaccess.c         |  39 ++-
 src/backend/access/undo/undoaction.c         | 450 +++++++++++++++++++++++++++
 src/backend/access/undo/undoactionxlog.c     |  60 ++++
 src/backend/replication/logical/decode.c     |   1 +
 src/bin/pg_rewind/parsexlog.c                |   2 +-
 src/bin/pg_waldump/rmgrdesc.c                |   3 +-
 src/include/access/rmgr.h                    |   2 +-
 src/include/access/rmgrlist.h                |  47 +--
 src/include/access/undoaccess.h              |   4 +
 src/include/access/undoaction_xlog.h         |  39 +++
 src/include/access/undorequest.h             |   3 -
 src/include/access/xlog_internal.h           |   8 +-
 16 files changed, 679 insertions(+), 37 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/include/access/undoaction_xlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..6238240 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 7327502..68696bc 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
+OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
+	   undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 2d413f7..2894cc7 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -65,8 +65,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 				 Buffer *prevbuf);
 static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
 						   UndoRecPtr xact_urp, int size, int offset);
-static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
-						  int idx);
 static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 									UndoRecPtr urecptr, UndoRecPtr xact_urp);
 static int UndoGetBufferSlot(UndoRecordInsertContext *context,
@@ -237,6 +235,41 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 }
 
 /*
+ * Prepare to update the undo apply progress in the transaction header.
+ */
+void
+UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+							   UndoRecPtr xact_urp, BlockNumber progress)
+{
+	int			index = 0;
+	int			offset;
+
+	Assert(UndoRecPtrIsValid(xact_urp));
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetPersistence(xact_urp) == UNDO_TEMP)
+		return;
+
+	/* It shouldn't be discarded. */
+	Assert(!UndoLogIsDiscarded(xact_urp));
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_progress);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the undo action progress in xact_urec_info, this will be overwritten
+	 * in actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].progress = progress;
+}
+
+/*
  * Overwrite the first undo record of the previous transaction to update its
  * next pointer.
  *
@@ -244,7 +277,7 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
  * This must be called under the critical section.  This will just overwrite the
  * header of the undo record.
  */
-static void
+void
 UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..22360f3
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,450 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to
+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+static void UpdateUndoApplyProgress(UndoRecPtr last_log_start_urec_ptr,
+						  BlockNumber block_num);
+static bool UndoAlreadyApplied(FullTransactionId full_xid,
+						UndoRecPtr to_urecptr);
+static void ApplyUndo(UndoRecInfo *urecinfo, int nrecords);
+static void ProcessAndApplyUndo(FullTransactionId full_xid,
+				UndoRecPtr from_urecptr, UndoRecPtr to_urecptr,
+				UndoRecPtr last_log_start_urec_ptr, bool complete_xact);
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else if (luur->uur_block > ruur->uur_block)
+		return 1;
+	else if (luur->uur_offset < ruur->uur_offset)
+		return -1;
+	else if (luur->uur_offset > ruur->uur_offset)
+		return 1;
+	else if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+	{
+		/*
+		 * If records are for the same block and offset, then maintain their
+		 * existing order by comparing their index in the array.
+		 */
+		return -1;
+	}
+	else
+		return 1;
+}
+
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoPersistence persistence;
+	UndoRecordInsertContext context = {{0}};
+
+	persistence =
+		UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	BeginUndoRecordInsert(&context, persistence, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,
+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	TransactionId	xid PG_USED_FOR_ASSERTS_ONLY = XidFromFullTransactionId(full_xid);
+
+	uur = UndoFetchRecord(to_urecptr, InvalidBlockNumber, InvalidOffsetNumber,
+						  InvalidTransactionId, NULL, NULL);
+
+	/* already processed and discarded */
+	if (uur == NULL)
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+		return true;
+	}
+
+	/* already processed */
+	if (IsXactApplyProgressCompleted(uur->uur_progress))
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr);
+		UndoRecordRelease(uur);
+		return true;
+	}
+
+	Assert(xid == uur->uur_xid);
+
+	UndoRecordRelease(uur);
+
+	return false;
+}
+
+/*
+ * ApplyUndo - Invode rmgr specific undo apply functions.
+ *
+ * urecinfo - An array of undo records sorted in the rmgr order.
+ * nrecords - number of records in this array.
+ */
+static void
+ApplyUndo(UndoRecInfo *urecinfo, int nrecords)
+{
+	int			rmgr_start_idx = 0;
+	int			rmgr_nrecords = 0;
+	int			prev_rmid = -1;
+	int			i;
+
+	/* Apply the undo action for each rmgr. */
+	for (i = 0; i < nrecords; i++)
+	{
+		UnpackedUndoRecord *uur = urecinfo[i].uur;
+
+		Assert(uur->uur_rmid >= 0);
+
+		/*
+		 * If this undo is not for the same rmgr then apply all undo
+		 * actions for the previous rmgr.
+		 */
+		if (prev_rmid >= 0 &&
+			prev_rmid != uur->uur_rmid)
+		{
+			Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+			RmgrTable[prev_rmid].rm_undo(rmgr_nrecords,
+										 &urecinfo[rmgr_start_idx]);
+
+			rmgr_start_idx = i;
+			rmgr_nrecords = 0;
+		}
+
+		rmgr_nrecords++;
+		prev_rmid = uur->uur_rmid;
+	}
+
+	/* Apply the last set of the actions. */
+	Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+	RmgrTable[prev_rmid].rm_undo(rmgr_nrecords, &urecinfo[rmgr_start_idx]);
+}
+
+/*
+ * ProcessAndApplyUndo - Fetch undo records and apply actions.
+ *
+ * We always process the undo of the last log when the undo for a transaction
+ * spans across multiple logs.  Then from there onwards the previous undo logs
+ * for the same transaction are processed.
+ *
+ * We also update the undo apply progress in the transaction header so that
+ * after recovery we don't need to process the records that are already
+ * processed.  As we update the progress only after one batch of records,
+ * the crash in-between can cause us to read/apply part of undo records
+ * again but this will never be more than one-batch.  We can further optimize
+ * it by marking the progress in each record, but that has its own downsides
+ * like it will generate more WAL and I/O corresponding to dirty undo buffers.
+ */
+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	TransactionId	xid PG_USED_FOR_ASSERTS_ONLY = XidFromFullTransactionId(full_xid);
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	do
+	{
+		BlockNumber	progress_block_num = InvalidBlockNumber;
+		int			i;
+		int			nrecords;
+		bool		low_switched = false;
+		bool		update_progress = false;
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update
+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		if (complete_xact)
+		{
+			if (urecinfo[nrecords - 1].uur->uur_info & UREC_INFO_LOGSWITCH)
+			{
+				/*
+				 * We have crossed the log boundary.  The rest of the undo for
+				 * this transaction is in some other log, the location of which
+				 * can be found from this record.  See commets atop undoaccess.c.
+				 */
+				low_switched = true;
+				last_log_start_urec_ptr = urecinfo[nrecords - 1].uur->uur_prevlogstart;
+			}
+			else if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(urecinfo[nrecords - 1].urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(urecinfo[0].urp) > progress_block_num)
+					update_progress = true;
+			}
+		}
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(TransactionIdEquals(xid, urecinfo[0].uur->uur_xid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urecinfo, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		/* Call resource manager specific callbacks to apply actions. */
+		ApplyUndo(urecinfo, nrecords);
+
+		/*
+		 * Set undo action apply progress as completed in the transaction header
+		 * if this is a main transaction.
+		 */
+		if (complete_xact)
+		{
+			if (low_switched)
+			{
+				/*
+				 * We have crossed the log boundary.  So, mark current log
+				 * header as complete and set the next progress location in the
+				 * previous log.
+				 */
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else if (!UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else if (update_progress)
+			{
+				/*
+				 * Update the progress block number.  We increase the block
+				 * number by one since the current block might have some undo
+				 * records that are yet to be applied.  But, all undo records
+				 * from the next block must have been applied.
+				 */
+				UpdateUndoApplyProgress(last_log_start_urec_ptr,
+										progress_block_num + 1);
+			}
+		}
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+	} while (true);
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{
+	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	if (complete_xact)
+	{
+		/*
+		 * It is important here to fetch the latest undo record and validate if
+		 * the actions are already executed.  The reason is that it is possible
+		 * that discard worker or backend might try to execute the rollback
+		 * request which is already executed.  For ex., after discard worker
+		 * fetches the record and found that this transaction need to be
+		 * rolledback, backend might concurrently execute the actions and
+		 * remove the request from rollback hash table.
+		 *
+		 * The other case where this will be required is when the transactions
+		 * records span across multiple logs.  Say, we register the
+		 * transaction from the first log and then we encounter the same
+		 * transaction in the second log where its status is still not marked
+		 * as done.  Now, before we try to register the request for the second
+		 * log, the undo worker came along rolled back the previous request
+		 * and removed its hash entry.  In this case, we will successfully
+		 * register the request from the second log and it should be detected
+		 * here.
+		 */
+		if (UndoAlreadyApplied(full_xid, to_urecptr))
+			return;
+
+		last_log_start_urec_ptr =
+			RollbackHTGetLastLogStartUrp(full_xid, to_urecptr);
+	}
+
+	ProcessAndApplyUndo(full_xid, from_urecptr, to_urecptr,
+						last_log_start_urec_ptr, complete_xact);
+
+	/*
+	 * Undo actions are applied so delete the hash table entry.
+	 */
+	RollbackHTRemoveEntry(full_xid, to_urecptr);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..2b06d114
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoPersistence persistence;
+	UndoRecordInsertContext context = {{0}};
+
+	persistence =
+		UndoLogNumberGetPersistence(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, persistence, 1, record);
+
+	/* Update the undo apply progress in the transaction header. */
+	UndoRecordPrepareApplyProgress(&context, xlrec->urec_ptr,
+								   xlrec->progress);
+
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index f2ed561..d075aa9 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 44f4c41..5b49cd4 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..396193b 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..e1fb42a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..ef0f6ac 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -25,26 +25,27 @@
  */
 
 /* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 139e2e8..4d5f292 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -13,6 +13,7 @@
 #ifndef UNDOACCESS_H
 #define UNDOACCESS_H
 
+#include "access/transam.h"
 #include "access/undolog.h"
 #include "access/undorecord.h"
 #include "access/xlogdefs.h"
@@ -91,6 +92,9 @@ typedef struct UndoRecordInsertContext
 	int			nxact_urec_info;	/* Number of previous xact info. */
 } UndoRecordInsertContext;
 
+extern void UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+					UndoRecPtr urecptr, BlockNumber progress);
+extern void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx);
 extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
 					  UndoPersistence persistence,
 					  int nprepared,
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
index 81fc3d5..1973a1f 100644
--- a/src/include/access/undorequest.h
+++ b/src/include/access/undorequest.h
@@ -219,8 +219,5 @@ extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin
 /* functions exposed from undoaction.c */
 extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool nopartial);
-extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
-						  int last_idx, Oid reloid, FullTransactionId full_xid,
-						  BlockNumber blkno, bool blk_chain_complete);
 
 #endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 3cc9c3d..e66d046 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -19,10 +19,14 @@
 #ifndef XLOG_INTERNAL_H
 #define XLOG_INTERNAL_H
 
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undorecord.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -295,9 +299,11 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	void		(*rm_undo) (int nrecords, UndoRecInfo *records);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
-- 
1.8.3.1

0014-Allow-execution-and-discard-of-undo-by-background-wo.patchapplication/octet-stream; name=0014-Allow-execution-and-discard-of-undo-by-background-wo.patchDownload
From 09cb7624ce1eba916a423fffa91caf132225ef90 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 16:03:49 +0530
Subject: [PATCH 14/14] Allow execution and discard of undo by background
 workers.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

We don't allow any transaction older than 2^31 to have pending undo actions.
Also, we have a hard limit on the number of transactions that can have
pending undo which is proportional to pending_undo_queue_size.

Amit Kapila, Dilip Kumar and Kuntal Ghosh with inputs from Andres Freund,
Robert Haas and Thomas Munro.
---
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/varsup.c           |  35 +-
 src/backend/access/transam/xact.c             |   5 +
 src/backend/access/transam/xlog.c             |  29 +
 src/backend/access/undo/Makefile              |   4 +-
 src/backend/access/undo/README.UndoProcessing |  78 +++
 src/backend/access/undo/discardworker.c       | 215 +++++++
 src/backend/access/undo/undoaccess.c          |  58 +-
 src/backend/access/undo/undodiscard.c         | 480 +++++++++++++++
 src/backend/access/undo/undolog.c             |   2 +
 src/backend/access/undo/undorequest.c         | 200 ++++++-
 src/backend/access/undo/undoworker.c          | 827 ++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c              |   5 +
 src/backend/postmaster/bgworker.c             |  11 +
 src/backend/postmaster/pgstat.c               |   9 +
 src/backend/postmaster/postmaster.c           |  11 +
 src/backend/storage/ipc/ipci.c                |   6 +
 src/backend/storage/lmgr/lwlocknames.txt      |   1 +
 src/backend/storage/lmgr/proc.c               |   2 +
 src/backend/utils/misc/guc.c                  |  22 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/access/discardworker.h            |  20 +
 src/include/access/transam.h                  |  10 +
 src/include/access/undodiscard.h              |  26 +
 src/include/access/undolog.h                  |  13 +
 src/include/access/undoworker.h               |  29 +
 src/include/catalog/pg_control.h              |   9 +
 src/include/nodes/primnodes.h                 |   3 +-
 src/include/pgstat.h                          |   5 +-
 src/include/postmaster/postmaster.h           |   1 +
 src/include/storage/lwlock.h                  |   1 +
 src/include/storage/proc.h                    |   4 +
 src/include/storage/procarray.h               |   7 +-
 src/test/regress/expected/sysviews.out        |   3 +-
 34 files changed, 2109 insertions(+), 27 deletions(-)
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..4b00d7d 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest full xid having unapplied undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUnappliedUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index fd01989..e74155d 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -127,14 +127,16 @@ GetNewTransactionId(bool isSubXact)
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
 		{
@@ -147,14 +149,16 @@ GetNewTransactionId(bool isSubXact)
 								oldest_datname,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(WARNING,
 						(errmsg("database with OID %u must be vacuumed within %u transactions",
 								oldest_datoid,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -334,10 +338,24 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUnapplied Undo because this is the oldest xid
+	 * whose undo is not yet discarded so this is still a valid xid in the
+	 * system.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -433,6 +451,9 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 		 * Note: it's also possible that get_database_name fails and returns
 		 * NULL, for example because the database just got dropped.  We'll
 		 * still warn, even though the warning might now be unnecessary.
+		 *
+		 * XXX Can we easily distinguish that the problem is due to unapplied
+		 * undo or some old open transactions?
 		 */
 		if (IsTransactionState())
 			oldest_datname = get_database_name(oldest_datoid);
@@ -445,14 +466,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 							oldest_datname,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 		else
 			ereport(WARNING,
 					(errmsg("database with OID %u must be vacuumed within %u transactions",
 							oldest_datoid,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 	}
 }
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 3253a0c..9d1a08e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2274,6 +2275,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4ef7fb0..f0fda49 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5160,6 +5160,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUnappliedUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6611,6 +6612,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6627,6 +6631,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7315,7 +7323,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8712,6 +8726,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUnappliedUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9624,6 +9642,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9681,12 +9702,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9726,6 +9752,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 68696bc..b4e7bab 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
-	   undorequest.o
+OBJS = discardworker.o undoaccess.o undoaction.o undoactionxlog.o undodiscard.o \
+	   undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
index e0caf9e..b7817cf 100644
--- a/src/backend/access/undo/README.UndoProcessing
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -37,3 +37,81 @@ tables are only accessible in the backend that has created them.  We can't
 postpone applying undo actions for subtransactions as the modifications
 made by aborted subtransaction must not be visible even if the main transaction
 commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue will allow us to
+process the requests of older transactions and help us to move
+oldesdXidHavingUnappliedUndo (this is a xid-horizon below which all the
+transactions are visible) forward.  A size-based queue which will help us to
+perform the rollbacks of larger aborts in a timely fashion, so that we don't get
+stuck while processing them during discard of the logs.  An error queue to hold
+the requests for transactions that failed to apply its undo.  The rollback hash
+table is used to avoid duplicate undo requests by backends and discard worker.
+The table must be able to accommodate all active undo requests.  The undo
+requests must appear in both xid and size requests queues or neither.  As of now,
+we process the requests from these queues in a round-robin fashion to give equal
+priority to all three types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.
+
+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+
+In a running system, scanning the rollback hash table will give us the value of
+oldestXidHavingUnappliedUndo, however, after startup, we need to once scan all
+the undo logs and populate the rollback hash table.  After startup, we allow
+connections, but don't allow transactions that want to write undo till the
+rollback hash table is initialized.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+To apply the undo actions, we collect the undo records in bulk and try to
+process them together.  We ensure to update the transaction's progress at
+regular intervals so that after a crash we can skip already applied undo.  The
+undo apply progress is updated in terms of the number of blocks processed.
+Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED indicates that all the
+undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED indicates that no undo action
+has been applied yet and any other value indicates that we have applied undo
+partially and after crash recovery, we need to start processing the undo from
+the same location.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..2080782
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,215 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 8334291..5ced039 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -56,8 +56,16 @@
 #include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/proc.h"
 #include "miscadmin.h"
 
+/*
+ * Defines the number of times we try to wait for rollback hash table to get
+ * initialized.  After these many attempts it will return error and the user
+ * can retry the operation.
+ */
+#define ROLLBACK_HT_INIT_WAIT_TRY	60
+
 /* Prototypes for static functions. */
 static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 				 UndoRecPtr urp, RelFileNode rnode,
@@ -514,6 +522,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
 	PreparedUndoSpace *prepared_undo;
 
+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo
+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}
+
+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+
 	/* Already reached maximum prepared limit. */
 	if (context->nprepared_undo == context->max_prepared_undo)
 		elog(ERROR, "already reached the maximum prepared limit");
@@ -1611,12 +1663,12 @@ UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 	page = BufferGetPage(buffer);
-	phdr = (UndoPageHeader)page;
+	phdr = (UndoPageHeader) page;
 
 	/* Calculate the size of the partial record. */
 	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
-						phdr->tuple_len + phdr->payload_len -
-						phdr->record_offset;
+					   phdr->tuple_len + phdr->payload_len -
+					   phdr->record_offset;
 
 	/* calculate the offset in current log. */
 	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..ee99a0a
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,480 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard the undo for the given log
+ *
+ * Search the undo log, get the start record for each transaction until we get
+ * the transaction with xid >= xmin or an invalid xid.  Then call undolog
+ * routine to discard up to that point and update the memory structure for the
+ * log slot.  We set the hibernate flag if we do not have any undo data that
+ * can be discarded, this flag is passed back to the discard worker wherein it
+ * determines if the system is idle and it should sleep for some time.
+ *
+ * Return the oldest full_xid remaining in this undo log (which should be
+ * >= xmin, since we'll discard everything older).  Returns
+ * InvalidTransactionId, if the undo log is empty.
+ */
+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	bool	log_complete = false;
+	TransactionId	undoxid = InvalidTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	uint32	epoch = 0;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		bool pending_abort = false;
+
+		next_insert = UndoLogGetNextInsertPtr(logno, InvalidTransactionId);
+
+		if (next_insert == undo_recptr)
+		{
+			/*
+			 * The caller of this function must have ensured that there is
+			 * something to discard.
+			 */
+			Assert(undo_recptr != slot->oldest_data);
+
+			/* Indicate that we have processed all the log. */
+			log_complete = true;
+		}
+		else
+		{
+			/* Fetch the undo record for the given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+
+			if (uur != NULL)
+			{
+				/*
+				 * Add the aborted transaction to the rollback request queues.
+				 *
+				 * If the undo actions for the aborted transaction is already
+				 * applied then continue discarding the undo log, otherwise,
+				 * discard till current point and stop processing this undo
+				 * log.
+				 *
+				 * We can ignore the abort for transactions whose
+				 * corresponding database doesn't exist.
+				 *
+				 * XXX: We've added the transaction-in-progress check to avoid
+				 * xids of in-progress autovacuum as those are not computed
+				 * for oldestxmin calculation.  See DiscardWorkerMain.
+				 */
+				if (!TransactionIdDidCommit(uur->uur_xid) &&
+					!TransactionIdIsInProgress(uur->uur_xid) &&
+					TransactionIdPrecedes(uur->uur_xid, xmin) &&
+					!IsXactApplyProgressCompleted(uur->uur_progress) &&
+					dbid_exists(uur->uur_dbid))
+				{
+					FullTransactionId full_xid;
+
+					full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+																uur->uur_xid);
+					(void) RegisterRollbackReq(InvalidUndoRecPtr,
+											   undo_recptr,
+											   uur->uur_dbid,
+											   full_xid);
+
+					pending_abort = true;
+				}
+
+				next_urecptr = uur->uur_next;
+				undoxid = uur->uur_xid;
+				epoch = uur->uur_xidepoch;
+
+				UndoRecordRelease(uur);
+				uur = NULL;
+			}
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) the next transaction is not all-visible. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if ((TransactionIdIsValid(undoxid) &&
+			 TransactionIdFollowsOrEquals(undoxid, xmin)) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			log_complete ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If the transaction id is smaller than the xmin, it means this
+			 * must be the last transaction in this undo log, so we need to
+			 * get the last insert point in this undo log and discard till
+			 * that point.
+			 *
+			 * Also, if the transaction has pending abort, stop discarding
+			 * further.
+			 */
+			if (TransactionIdPrecedes(undoxid, xmin) && !pending_abort)
+			{
+				UndoRecPtr	next_insert = InvalidUndoRecPtr;
+
+				/*
+				 * If more undo has been inserted since we checked last, then
+				 * we can process that as well.
+				 */
+				next_insert = UndoLogGetNextInsertPtr(logno, undoxid);
+				if (!UndoRecPtrIsValid(next_insert))
+					continue;
+
+				undo_recptr = next_insert;
+				need_discard = true;
+				epoch = 0;
+				latest_discardxid = undoxid;
+				undoxid = InvalidTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/*
+			 * If no more pending undo logs then set the oldest transaction to
+			 * InvalidTransactionId.
+			 */
+			if (log_complete)
+			{
+				slot->oldest_xid = InvalidTransactionId;
+				slot->oldest_xidepoch = 0;
+			}
+			else
+			{
+				slot->oldest_xid = undoxid;
+				slot->oldest_xidepoch = epoch;
+			}
+
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = undoxid;
+
+		Assert(uur == NULL);
+
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+}
+
+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass
+ * over undo logs is complete, new undo can is allowed to be written in the
+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before start allowing any new transaction to write the
+ * undo we need to make sure that we know exact number of pending requests.
+ */
+void
+UndoLogProcess()
+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();
+
+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;
+
+			/* Fetch the undo record for given undo_recptr. */
+			uur = UndoFetchRecord(undo_recptr, InvalidBlockNumber,
+								  InvalidOffsetNumber, InvalidTransactionId,
+								  NULL, NULL);
+			Assert(uur != NULL);
+
+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(uur->uur_xid) &&
+				!TransactionIdIsInProgress(uur->uur_xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_progress))
+			{
+				FullTransactionId full_xid;
+
+				full_xid = FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+															uur->uur_xid);
+				(void) RegisterRollbackReq(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_dbid, full_xid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are
+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) == UndoRecPtrGetLogNo(uur->uur_next))
+				undo_recptr = uur->uur_next;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+
+	CommitTransactionCommand();
+
+	/* Allow the transactions to start writting undo. */
+	ProcGlobal->rollbackHTInitialized = true;
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		/*
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.persistence == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin the try
+		 * to discard the undo log.
+		 */
+		if (!TransactionIdIsValid(slot->oldest_xid) ||
+			TransactionIdPrecedes(slot->oldest_xid, oldestXmin))
+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+	}
+
+	/* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+	oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+
+	/*
+	 * Update the oldestFullXidHavingUnappliedUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.persistence == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 014480f..ad06b6e 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -145,6 +145,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 		}
 	}
 	else
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
index 28e844e..50b0cb5 100644
--- a/src/backend/access/undo/undorequest.c
+++ b/src/backend/access/undo/undorequest.c
@@ -54,10 +54,12 @@
 #include "postgres.h"
 #include "miscadmin.h"
 
+#include "access/discardworker.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_database.h"
@@ -944,6 +946,139 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 }
 
 /*
+ * Fetch the start urec pointer for the transaction and the undo request size.
+ *
+ * start_urecptr_inout - This is an INOUT parameter.  If a transaction has
+ * overflowed to multiple undo logs, the caller can set start_urecptr_inout
+ * to a location of any of the undo logs where the transaction has written its
+ * first record for that particular log.  Given that, this function calculates
+ * the undo location where the transaction has inserted its first undo record.
+ * If a transaction hasn't overflowed to multiple undo logs, the value of this
+ * parameter remains unchanged.
+ *
+ * The first record of a transaction in each undo log contains a reference to
+ * the first record of this transaction in the previous log.  It finds the
+ * initial location by moving backward in the undo chain of this transaction
+ * across undo logs.  While doing the same, it also calculates the undo size
+ * between the input and output start undo record pointer value.
+ */
+static uint64
+FindUndoStartLocationAndSize(UndoRecPtr *start_urecptr_inout,
+							 FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+
+	Assert(start_urecptr_inout);
+
+	urecptr = *start_urecptr_inout;
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * A backend always set the start undo record pointer to the first undo
+	 * record inserted by this transaction.  Hence, we don't have to proceed
+	 * further.
+	 */
+	if (!IsDiscardProcess())
+		return sz;
+
+	/*
+	 * Since the discard worker processes the undo logs sequentially, it's
+	 * possible that start undo record pointer doesn't refer to the actual
+	 * start of the transaction.  Instead, it may refer to the start location
+	 * of the transaction in any of the subsequent logs.  In that case, we've
+	 * to find the actual start location of the transaction by going backwards
+	 * in the chain.
+	 */
+	while (true)
+	{
+		UndoLogOffset next_insert;
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		uur = UndoFetchRecord(urecptr,
+							  InvalidBlockNumber,
+							  InvalidOffsetNumber,
+							  InvalidTransactionId,
+							  NULL, NULL);
+
+		/*
+		 * Since the rollback isn't completed for this transaction, this undo
+		 * record can't be discarded.
+		 */
+		Assert (uur != NULL);
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid,
+			   FullTransactionIdFromEpochAndXid(uur->uur_xidepoch,
+												uur->uur_xid)));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * If this is the first undo record of this transaction, return from
+		 * here.
+		 */
+		if ((uur->uur_info & UREC_INFO_LOGSWITCH) == 0)
+		{
+			UndoRecordRelease(uur);
+			break;
+		}
+
+		/*
+		 * This is a start of a overflowed transaction header, so it must have
+		 * a valid pointer to previous log's start transaction header.
+		 */
+		Assert(UndoRecPtrIsValid(uur->uur_prevlogstart));
+
+		/*
+		 * Find the previous log from which the transaction is overflowed
+		 * to current log.
+		 */
+		urecptr = uur->uur_prevlogstart;
+		slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		/*
+		 * When a transaction overflows to a new undo log, it's guaranteed
+		 * that this transaction will be the last transaction in the previous
+		 * log and we mark that log as full so that no other transaction can
+		 * write in that log further.  Check UndoLogAllocate for details.
+		 *
+		 * So, to find the undo size in the previous log, we've to find the
+		 * next insert location of the previous log and subtract current
+		 * transaction's start location in the previous log from it.
+		 */
+		next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+		Assert(UndoRecPtrIsValid(next_insert));
+
+		sz += (next_insert - urecptr);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	*start_urecptr_inout = urecptr;
+
+	return sz;
+}
+
+/*
  * Returns true, if we can push the rollback request to undo wrokers, false,
  * otherwise.
  */
@@ -959,9 +1094,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 
 	/*
 	 * We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
 	 */
-	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
 	{
 		if (GetXidQueueSize() >= pending_undo_queue_size ||
 			GetSizeQueueSize() >= pending_undo_queue_size)
@@ -987,12 +1137,7 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 		if ((GetXidQueueSize() < pending_undo_queue_size))
 		{
 			Assert(GetSizeQueueSize() < pending_undo_queue_size);
-
-			/*
-			 * XXX - Here, we should return true once we have background
-			 * worker facility.
-			 */
-			return false;
+			return true;
 		}
 	}
 
@@ -1433,6 +1578,12 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	Assert(dbid != InvalidOid);
 
 	/*
+	 * The discard worker can only send the start undo record pointer of a
+	 * transaction.  It doesn't set the end_urec_ptr.
+	 */
+	Assert(IsDiscardProcess() || UndoRecPtrIsValid(end_urec_ptr));
+
+	/*
 	 * Find the rollback request size and the end_urec_ptr (in case of discard
 	 * worker only).
 	 */
@@ -1447,6 +1598,14 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	if (!UndoRecPtrIsValid(end_urec_ptr))
 		return false;
 
+	/*
+	 * For registering a rollback request, we always store the full transaction
+	 * ID and the first undo record pointer inserted by this transaction.  This
+	 * ensures that backends and discard worker don't register the same request
+	 * twice.
+	 */
+	req_size += FindUndoStartLocationAndSize(&start_urec_ptr, full_xid);
+
 	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
 
 	/*
@@ -1462,7 +1621,13 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 										   HASH_ENTER_NULL, &found);
 
 	/*
-	 * It can only fail, if the value of pending_undo_queue_size or
+	 * Except the first pass over the undo logs by discard worker, the hash
+	 * table can never be full.
+	 */
+	Assert(!ProcGlobal->rollbackHTInitialized || (rh != NULL));
+
+	/*
+	 * It can only fail, if  the value of pending_undo_queue_size or
 	 * max_connections guc is reduced after restart of the server.
 	 */
 	if (rh == NULL)
@@ -1510,10 +1675,16 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 		}
 		/*
 		 * The request can't be pushed into the undo worker queue.  The
-		 * backends will try executing by itself.
+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with
+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
 		 */
-		else
+		else if (!IsDiscardProcess())
 			rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
 	}
 	else if (!UndoRequestIsValid(rh) && can_push)
 	{
@@ -1541,6 +1712,13 @@ RegisterRollbackReq(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 
 	LWLockRelease(RollbackRequestLock);
 
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
 	return pushed;
 }
 
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..9382771
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,827 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 4;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.  This has to be more than UNDO_FAILURE_RETRY_DELAY_MS,
+ * otherwise, worker can exit before retrying the failed requests.
+ */
+#define UNDO_WORKER_LINGER_MS 20000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t got_SIGTERM = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker *worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo);
+static void UndoworkerSigtermHandler(SIGNAL_ARGS);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+UndoworkerSigtermHandler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.  If we fail to add the request in
+		 * an error queue, then mark the entry status invalid.  This request
+		 * will be later added back to the queue by the discard worker.
+		 */
+		if (!InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTMarkEntryInvalid(urinfo->full_xid,
+									   urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+
+	/* we're done */
+	ereport(LOG,
+			(errmsg("undo launcher shutting down")));
+	proc_exit(0);
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	while (!got_SIGTERM)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		CHECK_FOR_INTERRUPTS();
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4737c58..88703ad 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14516,6 +14517,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index f5db5a8..0550d2c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d8dc0cc..4c906f6 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 688ad43..ddfad1b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher = true;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c3249..56b5d08 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 19e4f1f..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -51,3 +51,4 @@ LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
 RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 6126ed1..c4d05da 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -297,7 +297,9 @@ InitProcGlobal(void)
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
 
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo, 0);
 	ProcGlobal->xactsHavingPendingUndo = 0;
+	ProcGlobal->rollbackHTInitialized = false;
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 89ced6d..886d393 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1956,6 +1957,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		 },
+		 &enable_undo_launcher,
+		 true,
+		 NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3032,6 +3044,16 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"max_undo_workers", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
+			gettext_noop("Maximum number of undo worker processes."),
+			NULL,
+		},
+		&max_undo_workers,
+		4, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 51e4929..c9eb688 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -612,6 +612,7 @@
 					# requests are pushed to undo workers
 #pending_undo_queue_size = 1024	# size of queue used to register undo
 					# requests
+#max_undo_workers = 4	# maximum undo workers
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 90f39ee..2cc8e11 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -73,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..282afc0
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+extern void UndoLogProcess(void);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index d2b2c2a..a46c5fe 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -266,6 +266,19 @@ typedef struct UndoLogAllocContext
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogSlot
 {
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..1bdc31f
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+extern int max_undo_workers;
+
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..babcc6b 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,15 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest full transaction id which is having unapplied undo.  We include
+	 * this value in the checkpoint record so that whenever server re-starts
+	 * we can use this to initialize the server-wide value for same variable.
+	 * Any Xid prior to this should be all-visible, so if this is not set,
+	 * then the scans might try to fetch undo which can suck the performance.
+	 */
+	FullTransactionId		oldestFullXidHavingUnappliedUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 7c278c0..e792d56 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2fff673..f9dc0bb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -785,7 +785,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 8ccd2af..5025db3 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,7 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..b220a05 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -223,6 +223,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
 	LWTRANCHE_REWIND,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 6a74d20..25839f7 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,8 +272,12 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUnappliedUndo;
 	/* Number of aborted transactions with pending undo actions. */
 	int			xactsHavingPendingUndo;
+	/* Whether the rollback hash table is initialized after the startup? */
+	bool		rollbackHTInitialized;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672..1d12994 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb..6034c5e 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
1.8.3.1

#69Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#68)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 25, 2019 at 4:00 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

[ new patches ]

I happened to open up 0001 from this series, which is from Thomas, and
I do not think that the pg_buffercache changes are correct. The idea
here is that the customer might install version 1.3 or any prior
version on an old release, then upgrade to PostgreSQL 13. When they
do, they will be running with the old SQL definitions and the new
binaries. At that point, it sure looks to me like the code in
pg_buffercache_pages.c is going to do the Wrong Thing. There's some
existing code in there to omit the pinning_backends column if the SQL
definitions don't know about it; instead of adding similar code for
the newly-added smgrid column, this patch rips out the existing
backward-compatibility code. I think that's a double fail.

At some later point, the customer is going to run ALTER EXTENSION
pg_buffercache UPDATE. At that point they are going to be expecting
that their SQL definitions now match the state that would have been
created had they installed on pg_buffercache for the first time on
PG13, starting with pg_buffercache v1.4. Since the view is now going
to have a new column, that would seem to required dropping and
recreating the view, but pg_buffercache--1.3--1.4.sql doesn't do that.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#70Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#65)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jun 22, 2019 at 2:51 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Jun 20, 2019 at 4:54 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

The idea behind having the loop inside the undo machinery was that
while traversing the blkprev chain, we can read all the undo records
on the same undo page under one buffer lock.

That's not a bad goal, although invoking a user-supplied callback
while holding a buffer lock is a little scary. If we stick with that,
it had better be clearly documented. Perhaps worth noting:
ReadBuffer() is noticeably more expensive in terms of CPU consumption
than LockBuffer(). So it may work out that we keep a pin to avoid
redoing that and worry less about retaking the buffer locks. But I'm
not sure: avoiding buffer locks is clearly good, too.

I have a couple of observations after poking at this some more. One
is that it's not necessarily adequate to have an interface that
iterates backward through the undo chain until the callback returns
true. Here's an example: suppose you want to walk backward through
the undo chain until you find the first undo record that corresponds
to a change you can see, and then return the undo record immediately
prior to that. zheap doesn't really need this, because it (mostly?)
stores the XID of the next record we're going to look up in the
current record, and the XID of the first record we're going to look up
in the chain -- so it can tell from the record it's found so far
whether it should bother looking up the next record. That, however,
would not necessarily be true for some other AM.

Suppose you just store an undo pointer in the tuple header, as Heikki
proposed to do for zedstore. Suppose further that each record has the
undo pointer for the previous record that modified that TID but not
necessarily the XID. Then imagine a TID where we do an insert and a
bunch of in-place updates. Then, a scan comes along with an old
snapshot. It seems to me that what you need to do is walk backward
through the chain of undo records until you see one that has an XID
that is visible to your snapshot, and then the version of the tuple
that you want is in the payload of the next-newer undo record. So what
you want to do is something like this:

look up the undo pointer in the tuple. call that the current undo record.
loop:
- look up the undo pointer in the current undo record. call that the
previous undo record.
- if the XID from the previous undo record is visible, then stop; use
the tuple version from the current undo record.
- release the current undo record and let the new current undo record
be the previous undo record.

I'm not sure if this is actually a reasonable design from a
performance point of view, but it doesn't sound totally crazy, and
it's just to illustrate the point that there might be cases that are
too complicated for a loop-until-true model. In this kind of loop, at
any given time you are holding onto two undo records, working your way
back through the undo log, and you just can't make this work with the
UndoFetchRecord callback interface. Possibly you could have a context
object that could hold onto one or a few buffer pins:

BeginUndoFetch(&cxt);
uur = UndoFetchRecord(&cxt, urp); // maybe do this a bunch of times
FinishUndoFetch(&cxt);

...but I'm not sure if that's exactly what we want either. Still, if
there is a significant savings from avoiding repinning and relocking
the buffer, we want to make it easy for people to get that advantage
as often as possible.

IIUC, you are proposing to retain the pin and then lock/unlock the
buffer each time in this API. I think there is no harm in trying out
something on these lines to see if there is any impact on some of the
common scenarios.

Another point that has occurred to me is that it is probably
impossible to avoid a fairly large number of duplicate undo fetches.
For instance, suppose somebody runs an UPDATE on a tuple that has been
recently updated. The tuple_update method just gets a TID + snapshot,
so the AM basically has to go look up the tuple all over again,
including checking whether the latest version of the tuple is the one
visible to our snapshot. So that means repinning and relocking the
same buffers and decoding the same undo record all over again. I'm
not exactly sure what to do about this, but it seems like a potential
performance problem. I wonder if it's feasible to cache undo lookups
so that in common cases we can just reuse the result of a previous
lookup instead of doing a new one, and I wonder whether it's possible
to make that fast enough that it actually helps...

I think it will be helpful if we can have such a cache, but OTOH, we
can try out such optimizations after the first version as well by
analyzing its benefit. For zheap, in many cases, the version in heap
itself is all-visible and or is visible as per current snapshot and
the same can be detected by looking at the transaction slot, however,
it might be tricky for zedstore.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#71Thomas Munro
thomas.munro@gmail.com
In reply to: Robert Haas (#69)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jun 28, 2019 at 6:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

I happened to open up 0001 from this series, which is from Thomas, and
I do not think that the pg_buffercache changes are correct. The idea
here is that the customer might install version 1.3 or any prior
version on an old release, then upgrade to PostgreSQL 13. When they
do, they will be running with the old SQL definitions and the new
binaries. At that point, it sure looks to me like the code in
pg_buffercache_pages.c is going to do the Wrong Thing. [...]

Yep, that was completely wrong. Here's a new version. I tested that
I can install 1.3 in an older release, then pg_upgrade to master, then
look at the view without the new column, then UPGRADE the extension to
1.4, and then the new column appears.

Other new stuff in this tarball (and also at
https://github.com/EnterpriseDB/zheap/tree/undo):

Based on hallway track discussions at PGCon, I have made a few
modifications to the undo log storage and record layer to support
"shared" record sets. They are groups of records can be used for
temporary storage space for anything that needs to outlive a whole set
of transactions. The intended usage is extra transaction slots for
updaters and lockers when there isn't enough space on a zheap (or
other AM) page. The idea is to avoid the need to have in-heap
overflow pages for transient transaction management data, and instead
put that stuff on the conveyor belt of perfectly timed doom[1]https://speakerdeck.com/macdice/transactions-in-postgresql-and-other-animals?slide=23 along
with old tuple versions.

"Shared" undo records are never executed (that is, they don't really
represent rollback actions), they are just used for storage space that
is eventually discarded. (I experimented with a way to use these also
to perform rollback actions to clean up stuff like the junk left
behind by aborted CREATE INDEX CONCURRENTLY commands, which seemed
promising, but it turned out to be quite tricky so I abandoned that
for now).

Details:

1. Renamed UndoPersistence to UndoLogCategory everywhere, and add a
fourth category UNDO_SHARED where transactions can write 'out of band'
data that relates to more than one transaction.

2. Introduced a new RMGR callback rm_undo_status. It is used to
decide when record sets in the UNDO_SHARED category should be
discarded (instead of the usual single xid-based rules). The possible
answers are "discard me now!", "ask me again when a given XID is all
visible", and "ask me again when a given XID is no longer running".

3. Recognise UNDO_SHARED record set boundaries differently. Whereas
undolog.c recognises transaction boundaries automatically for the
other categories (UNDO_PERMANENT, UNDO_UNLOGGED, UNDO_TEMP), for
UNDO_SHARED the

4. Add some quick-and-dirty throw-away test stuff to demonstrate
that. SELECT test_multixact([1234, 2345]) will create a new record
set that will survive until the given array of transactions is no
longer running, and then it'll be discarded. You can see that with
SELECT * FROM undoinspect('shared'). Or look at SELECT
pg_stat_undo_logs. This test simply writes all the xids into its
payload, and then has an rm_undo_status function that returns the
first xid it finds in the list that is still running, or if none are
running returns UNDO_STATUS_DISCARD.

Currently you can only return UNDO_STATUS_WAIT_XMIN so wait for an xid
to be older than the oldest xmin; presumably it'd be useful to be able
to discard as soon as an xid is no longer active, which could be a bit
sooner.

Another small change: several people commented that
UndoLogIsDiscarded(ptr) ought to have some kind of fast path that
doesn't acquire locks since it'll surely be hammered. Here's an
attempt at that that provides an inlined function that uses a
per-backend recent_discard to avoid doing more work in the (hopefully)
common case that you mostly encounter discarded undo pointers. I hope
this change will show up in profilers in some zheap workloads but this
hasn't been tested yet.

Another small change/review: the function UndoLogGetNextInsertPtr()
previously took a transaction ID, but I'm not sure if that made sense,
I need to think about it some more.

I pulled the latest patches pulled in from the "undoprocessing" branch
as of late last week, and most of the above is implemented as fixup
commits on top of that.

Next I'm working on DBA facilities for forcing undo records to be
discarded (which consists mostly of sorting out the interlocking to
make that work safely). And also testing facilities for simulating
undo log switching (when you fill up each log and move to another one,
which are rare code paths run, so we need a good way to make them not
rare).

[1]: https://speakerdeck.com/macdice/transactions-in-postgresql-and-other-animals?slide=23

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-20190701.tgzapplication/x-gzip; name=undo-20190701.tgzDownload
���]��i{��0����h+odPAl�DK7EI<����7���s`` �I|����YP�����L3�VWWW�:���f��W��76��������-E��o���������v��no�U��j����~���?�dLa(��x/(���e������[�z�Q�D�������$�9f�M<��������;�$��j�`��]���v�]����v��?���e���;U�e��?����f��Nt;;���A�t{A���~+���A��k4��z;�4��p�;�^N�T����<W���(H�)�R}7�o5�oFA4������k@���TU5���
�O5�����a���b�j����7{��zqp}�N5������s�BN�B�rycc���io�l���|&�^��T%�����g�*7��a�z���&�WU[E�$���x�T���A?��}c�\�G������f*�|`��V(G�~�Y���w�n���[������Vk���p�!<�2@������F{�����7|��2�z�S���>�N0�w!��ZY���J���a/�����a<���g�'_���������
*zN�(���^�&�����������������~y�������-�J���R?����#>���RI
�1T���4�I�����K�)}���a���]����� (��R��P������������g�bN�i��G�]���M���&�AYy*@�����fU��?9YC-��a��R4��d6�	����L��4Uu<�����px=
�I���s��yz\_M���;M�NUoZC��T�f��n4�n�����?��+���!��q�qi>��t��4�������<:<�|�c��z������a�|�i�N���j��O>�J?����7�g�d��a�%����eQ�4����|��*R��K���u�"��
 �~\��@v��Z�Q�����~�O��kovM�d6��f�i�1�7��ao>7&���o6��7Y�\��5����V�����n����?�����v�]�^���v����^���3�n��w{���n�>h/��F��|�0P�������1��3�L�$�w]&��vL`���p�[��;����o�r��6q�>��Y�C�`(�%����5|�L��(������/��Rp|
)���Z������i������M�5�~U8Nd4�	�!�7������f���Yy���p�G %�Jb��8�'*��.Hni��X%��
��0���d"9qz���)��������9��}!��F������ �`�eo��VK��������Q0n`����h��~>
�Uw������|<����m�s�f�0��|���L�2����.����`7�ns:����z������r���.K���x�+1��
G�zn5^�U�����U�v���AR5 ~w:�j����Vko����Y�����F�������C`���&FNl��m=�:�Z�xsr���wp_��#5	�.R0j��r��`���&f!��Y�F��p�������d{��6����*h�_�Y��lom
��Z������_\m�43�d��{��C�����5�����{���^�������@�B�wpU����
�j����������Q�U)���TL��{����A�_����o�Z;A+{�_����/-����6���* �P��2�'p'���~4]��t���0���:�_W�:w��j�z-�1�!1K�k���|��i��o�
�8 ��i�-�x����(Th���M�>����qW�������,�F��$��4���$������8�q�{/�f�a2��G\��vF��<���6�l ��J�pw�W����F\!�R���#w�\]�h�]?�X��l@��f�V���������#������]+`w�S��zN�F��H^^�|��X?�u��O/W�r��>�F��_S��Gc����-�U������:�'����3U������6���P"��y������z~
��dL�z]N�������%�h���������g�����0�q�aQ��������\�����6���.�fy����L6pD?)��/�H=#����[����!ILw>��'��%0���j���3�������J�E����$��
gp
*R�s8�wK��u�aG��z���'����7�������q��h�*\+��eQ�����_:��o/���`8%O�����t+��k���C`���^����e�p����Gr�?�������*V"�&���R����4A������Q�t�X6�-\v��hp_!�)����S%B�,J�x�	\Az��4
tzp>��9���}����>��s�Q���!\U�*��������%$���e�p�v�����o{�:4[��v=h����C��B��*GL�.1
,�.���"a!M�����p��-�G�W�����\P�xh)��	M�����j�o�@��@�����5�,��gj4l���c8��L!���*|	PD
�� G8,���Q��{*����������~4Fa�~M�~\E�T����b��|R�
�`�V�q����+���GUe�)]O��
k��a�>�������12_2�*�����!�y������;�zy�A���k��$�Yq{�{�t��W�����-��j�l���������v���v�nQ�����
��
*�d���_P��\n��j��_��\r'(o����&Y>�K�K-p�E5���~�l[�i���m���g�8�K��z���^}o� J�5A����*b��\�^QD��-$f�{���f�H�~6�|��J^!<�Eq�^�����4���c�yT��Z�k!
qE��gZ�hF��pH�;�)Pl?����x0H�*e����{��>��
�=��������z$��*�>9�q&��%F)g�9���b�_
�U�DLh���J4[�* <��i��p�>^�@����4��l��GI/��;,!����p����}9:�I�oxJ8)S"v�QB��?s���`3��
s���^�4���n85�N�J��Q
i1Z2Oz�����}��U���k\!T]��I<
�� z�B�	tYl����/�`��R���74�,a;���'_�7�r��c�rt�vhx� .����hau���1�{��}�� 3�w#��F����k] g�p[M��C}I����1.��E�Db]`�p��|`�a�Z���r��A
G�[U|oq�*�����Q!�1�E��4�v
 _�Z�e�*���X����a�x��B�(ns8D��p���F3XDn
*@�y0�l-��)i�x�Mna[��yl��{�+�����3�}��/\�+�:�A�*�)0ln���xrD�<�0�R�h�Pk��������vM�;;��|%%�
�0�l<j�Bm�����p��`���1���������z�;���}��e�*���n�Ri����,�����"�_U����>h�����b�lg�xo��+���lOk�]�����n5�.�x:tl���������i�E�F"#��NG�c>��[����#�t�w���@���ppx�!u�O/N���^S;�YP�&2%�*�0&�@5���������eA[��;G����(�p���w�b\"t^S���S��S��E0�{n�����������]�S�	�Z��$������K��"5��xae	U~��/��7���^��A���=��Sd�:A%�������)�0�Czn��������{i�ixz��F�$�Hrc�6�#�#��h\�ir;
G������8n������ �u|��#�6n�N@0��@>Nz���������5��9������ow�1�;r5t��I������
{�"C0"��|���`���,�3,����;�o�RLTp��C�2"�3��i��O����il���81�}�$���o��8�l�zL(<��B��io�������I��a8@�U�	��r[����^�
�����������uU��a7��3���SS��s�S
�1&��gk�\P��"0:�N��j�r��kU����LOV����\T���\E�~���J�R����~�"�aF��b,�F1F�P�7����>�df������;(��vI���h*������=M!����*�<�P.�d�^���P�,3NA�g�Zw�!ou�}xI��0;�CV�j�<��3���mg
1N�l-�t����+��Q ��3�4
��1
G���-���:�
=3��!��CO�7{��v��:Oh_���0�Py����������]a�����,=�g�a���["���R�z���YfQ|v�"��sxy7��C4��Xy�{;���\Za�U�]�pp���i����;�����x��P���>�����,L�m1G:e�$�`����$u�!��4k7���bU����F���"��xw��<LS�<���S�98������r�;[@:�T�:s����5�>:{]�"���������Qr[�=��$6��u�g��eysT�0
�$�����XeWy(N7�����.�B�����s������(� )y��>#��su������spuuty}�9?;�.k�G���h*z4~�;}�>W�as�e�l�Z����Xyh/ ����	W�|�q�0�K0�&�"��w�(�H-'
d�h�i
�r*�8�p<��K#�1�{�_������2���#�P��_�����f1?w�ff���Gc���������goQW��`�#��dX[3W���
Z�&��)*`n���Q�K���n����Q0��������Ej���3��f<�f'����N\$K���Z���#�$���'���.���"����R��;32��;�
�B�YQ��z�'~%�;��#�,������Q<{�����_�k���Q%u�{a�z�v@��"�q<���Xt+`@�&�����:FDstvn^8�z����}�����FJ03�������L��eZ�U��E������X���������u�A���h�>Oc��e���:�r����G-n�F9zU�E�ik�b��b��&G\���R�`���-Mo~@�.�O\��8\���C��Bi��2kV&�5�i�$�[�t���0X�x|��<L7�}j��<.c����3��I���B�O�1�k��1!0{b����|]F!r�`C���G<��>�1\]�����U�����h��
gwx�E�N/�#+n�AS��J�����	HE5s����dQe���[hF�A#a�4�OI#Of�����cL?j��l����0��%B��� {7N����GTt���S��`Tx�B|>�<���z�������tv���yg���.���x���z�ZK��9�^+D����/����/����L_���-�� ���q��+�^����;#Rz+M(��6���-:'3K��7�y�DE$f@�m��|}3��y�]���1�bB7;a"�#	K<AtcY7��.��}Q�ZT(:��s((T���{B�"�]8Z�F
T�\�<S�z�}�C�I9N;7��"�����X��.���i�I�d��I�s�O�F��/�I0��v��Y4DX��Z/�,b��vC���lp����bK�[^��2�� ��
�Io�E���}�wW�4��:�"G�Yw�Z�1�.�1x�"��P�aM�M�
3<%#�����������VT���SZS�$���z�
��z�6��>�s�a���[���r������W��G�"�����)�,���
Y���#�p_E�c	�4I��w�/�ShC/nU�1��7p9!�:~�"c�!�<XK>� �e������!��X�
�e�}����p��r��
��,�*��R{/}EH�)��'���2�c�ad�S����!{
���������5$-��JB�I�N�_�gT���T8��3�qf�4��~I�3o�`����!a57�����iDh����0���6�TWaH��!�x�Z��j��M��"Z��o
��s�,�-�*�q�7�3�6j+{�k�X+�/��5�4��	�Fd�FD	�B����j� ��<������K�=-�&��8G����)�u�.�0���!/A�a��Us��hD���v��)�*$N��`$����fbM�6�"hr;l]4!�/=k4 �WGG���4x3��Yh��@s�k�&$6{�YL���Jbu�>\�t3��>�i�<���������y��(|��mob�d�RT@�i��y�kU�� �8�4����6�RaC�5ee[������_c�P����*���d�-����r
s+�-{�3O.���C�O�~���Jb	��%>5��+..����h�)Uo7�1�!L}��;��3�C���5�a� 0��F�6��+�
����Z7����o���G���S`�(V�}�m4��b�9�_o�	�
i0�n/<-_dd��:�S���yJ���H
,��T�X�^�D ����*1��,76"�fZ7s��&2�&�H���#���jd�V�#M{R�"�'�<��G�9l����N �f���k��T����%��������C�^��h1\�P&�l����vw.��)����X�|1�� �,�x$��`C����H_���^�,|�0p����IM��{�����qc�\�3]$�9��Bn������O�9cH���Z��
M��1�/����HP�e4��3T�%%�$�@�52���<�RO��@Z�\Bm�s�b������,�V���
���(�bc�:vD�����hy=�!I��F�B8`gkL���!nM�������j�Q��8�F67� �+,�?
`�N�ZyL`$
ZR	#�)r����q���fA�V��
jL?���d�;�v����4|���fn��q�~\55�^<�:��fc�9��	����KpnD������
���f"�u����*��%f�o��"��U@�6��0����=rY������5D,�I����0�	�
�C����C 9B��o�K*^��e58�?C�8��,���0	��r�o��o���d�/�Mc{���j�����[��9��i��dRC#^1\A/-]2'
��{�Dy�6�2T�]�=r�Eof���L�9V��'|������!*jm[�D���7WX��tRl���Q���V�=2n���9=�{�H$z'����=V��0c��q����;W����b+;gv���F����s�t�8hn���6~ZI����{Ka9H���F������j������VP������!�0�_X�K^v}I� �)��%9����8��V+�	��dB�
����vv����,�h.��<g/�J�E�+?�����31.������nH��D�+� �:7�R?�5>�S|S�88;>����T��>��&�(�-M��-pd���qa����E�4���vc�>��j���V���[B3-l4{Z.,N����(|��h'\�5����A�jN��q f��BTb�!Z������������c�<x��y{tvty|�~���H��������*�� �Z?�S����C��(���.CRY�;���C���6^N�e��o|'���NBWc���TW
�w��h��^�����������^��n�s/���Ba�fBi��:�"�e���\4X?�XGgt-V��1<���t�zB)���)��T�Q���Q#
�R��RS�MNl7���N^��"L��5�]�^��n�6����^c�Vk�m7�Z��� (����2��S�0�N��E�6F������G���|���������I���(���8mb`��/31-���k�_
c[f0=?=B���`xo������=�uac�m7���ANH��V,v���3�R�5$u��~��������Z���5���d�l������c�[��A[,5	�D[K�k�=������
�+��34�BT#1���^R\.���ko��po����A/l��WBii!H"r[�&R9���M����C�'��`���D����X��Y�@���*����#7�$��j}����o������x"A��i�(M��U�������Ah	H�������6{h�p�P�Y(�,z���WWG�(�+��
S�i�?s��y���_�\]
���%@Gh�3&H���;�/��p�����.�)���A�0�����{��W��������{���i0yR�<��|����8s}p���_4������SMh��o�<�m�.�\4��wa0iB����)��gn�?�7��:D=3����m�i���5����HP��l"u������q���:�����;w������7~�q���q��l�|9�v�FHWn������Q�d��o�����yb"j������u_��:Kf��df�Kf^�5��~����]^�Ip�c��I��FT�$H
����or������<�d��~��'.��o�T�3|��^�z��"���}��zk�a�4wQ�OO��;�WB�)��u��*Q^;�Do�](���:mr�D��������Yt�24�2V����8E��((��[~�m�p���^�!("�S�%C�6����<����N�
N�K��>���������o��Xt�fA����w�h\Oq������Pq?�gT��/�%w�����%������Z��#qU��<:��`>��mw��r^�����{��8�}����4}<Nm���rm�h����-��_��+������G��py��s�Q��-K)Y���Tn�e�/5�v��?���<���A���o�j��~����t���Rq��V���+E��)X[F�0�L��y�n����s��y��9C�BMI�K��,T�h��{����5�{������������J�����QA{f�$cE,E�w�aP?�������t\�����8����/7��~Q�_�+��6�o��|"J�Kd��hn�����[�BJM\�����������Z�u����~c��Z�-����k��
�_��s=��d���H�����=�K�H!y�;���y����8�)r���`e%���=|#?���6v����6��}�PQ�$�����=����u��C���<;0i�4\RA7��r-������L;���6�b��jZW24�o�,���l�#f
41.UL���Fl��5��=�yh���U����=(��m����d�,��0{!4AL�3��*$��g(����N���Z����V�Vk��������������Ra[���@�v�:��BnA�����l����3,�vV��1��0����5q��I�����`W��#.�]��s�R����$$�|�2
z�7��GJG�t����rb��t����\���y�����k�A�T�Ci1�����6\����'�$����������}z%N'��E�T!�����<���k�p{�_oo�v���h�n����X�Q'k�F�c�Y�}v}tyvp����������?�s�kOA�=0��8)��I���c2����8��&S39\r�9�X�5���7���,H#�&�s���_*U�MGZd��(�"��V�7o��Fq�.E@U�{����z�Lkh�<s"\x�	.��(8��)7��>�������u�;�r��h�����ON�O/�/��W&����	G�E�����vG��7�D=�������
{T���f�V/�����b6�q�;��f��hmjO��pzn��
�]�����A�� ��?�&���:�
��lo��z���8+Z
�hm��[�6���������T�W"�h>���A��`�{�3+����C]��n�����n�?�n������n���7vv�v�j�w�{uu
���p�;�^N�T����<W�d��`
�]_j��C$5�i,a���2���9\��UXz�_��B$T���
hu[���Z�����?ao�\�������ln��|��v�I�F����&>f�\���0����0��
�]@>��pw8f	%���$���8,�G:���&�F��0�y� 1-/���:J��Y��n�#�C�'��p��6%�V@a���8is	tmFzr�����=@���*������\F:�!�����pG�~%=9���&E_i
T�8Z�z�W���'��29��,����`(��GW5��2����2N���<�������0�����K������(��>0<B1�,�i�C���������O���P�g�u���9a d�8�P��A'�x�!j��A�z��a��x���������O��
��2��Pti�C���O�:����L���
.&�0��Q�R���j����}��p���&���
�MJ�I�qdzN�t.�hR�S��Lg��aD1~(�W&��z�q\j�D��f�w?��Hg7�qtD]dP>�;����^>�i5��S�� 2�E��2:�#���x
�P4�:t��Y;g��q�e��/���9$�n)��b����Y0Too�������?Hd��)P�7�z��-�\�?||IT���5��������W�j,�(w���{�B��S����������������E�|�j��3g0����1{����
J(7�_?�jb=��������7�lgR�Y�����?
�$j	�<�]]/u
7����,\���%W~�����g�q�zMt,XO�t��f'��G�����U�[�nIP:9��|0�������$�Z_������ke}�8:��P���B!�j�&9(���h�R{/h7���F���j�l����<�����0�H>�$u.�������0fSA�2P�I�;��}.W�m�e������Z���Nk7������������c���O��qy}���]�/���`0fPz�h5�k���!"�zY8.��w�������n�������v�����5�J()�����u������r�Po�W����u{,~cX�<����s}k���q�OGv�:��oPB|�j�`�1��x
�pH��G!��8d�i���W���s�.II�r=�0�7
����k����g���W[��nT�H��s�p���L2���*�S�����a�2z�Sx��n`�TL<rJ��nw�|V	����u���c��yqG���M&�C&u��R��&�r^������?��L�`'*9:��Y�_��hXq�/�(T(�5�J��;Z����Oe.�� �gp*�JS� ��'�
g�+��������������`�:��X���TG�� x�8��A Y(La������������L��?^������:�g>89~��8�<8=�>�����Gk:(6�%7�'��o9N%k}���G�$��;��,�f!���=�?D��x>�����B����I���o��p����E/_����<=��]�����h)�7d�'0oE��z�����������w�GR�f��q7���+�Q����fW��L;��r���N����� ,i\�1����XW��m�5���/fpS;�����'��~}z��� ���f�>B;��.,cN!9���-XH5���6�����@M^�~�Q�������%����k���p|�����L��i��h��MB��zR��f����
]L8�n��jm���=�t������Vk//����s:�(k�(���;�O�6�����(�P%O*o�s�^0�>���cfKoRg�rv���5�|��r?�v$�u���w���������h9#�J_��AK�}Vh��������������6�2���m�W�l#962x�#��X�d�0������-_]�:�#�k!n�^wh��E�[�JQq�Zx�S�`��G�BM	�\ ���1��]Y Lh������&�Q���:��d���������/��r/���L_��]�)�]��F��J�#�x��1�i`�t �B�s=����� 4@iF�Z���k�O���H�C�&���BKO9�6�q7�;�	�&*��%v�M�Z�Pc�*Mz��6�
?c�-D�@��BK8����r^��[J������ ��^bT'�D�\ ^eKR�q2��:3�9����^^�s�c�""�;"2$����&���F��n"��d������?�U���/����/�T	n3s��n8cef�a�SG�W��������1�k�2�!~+w��\�����K�!��<�:�xjH���BI���
b�M�e:����$��Z��i�������B�������$N"� rm"_��6�(Q(�GmNy��!��t+��C��d
�������o�
�<��dS�����
��M�H3D��*b��M.c��k������>����C��Z���k�1e�x���a0����P.�c�u���<���U���i�T�WD$��!�/��@h���X)�E�6���Fq��P���1�42���]�����"C���L�H���Y��AFY=o�������0�XP�{���F�������*~�����p"����H�a.Ls���ac�4R�����3�����a6��O���4]Jz=J������G��7}�/6�}����c&�\d��~�E����:���^;o`{@6�BX��u%�A�_0���]����$Xa�����w�Nd��z��Ol���#J�*�'�+E	��������w������U����t.�����>��~W*5���o�O�_u.��/��N��9��+���|S�F}\i��v{_��t�l�J[D�T����r�'�P��������k9�),��+��uoI�?�L�+/p���X�N8�-�����_^��Ro���������?��~��n��"]���4�����I1<�'��6;�����������+���?��7�n��������4:i��eP�,�n�)��t���$�v>���r=�P��A^��V���V�W)3Dh��c�{=c�e5�kfM�"���k�o��qr4���+4�R�bN'���������}����m"<����#�K'$M?��?�}O�ygQ8��)���o^���l>BQ93�.4�\�Lf��1��ni���Ag�y�����%c��q���r�	�����3M��Aa*��3����a���VqAs�z�uX\!�)*=63�E{����lzCKW����j��-iP�����o�����-�GM����6�t�/�I��7�/#Y�}�O�8o����n�/X�.��U���R��)������>����WCx	�s�A.��8D��������V#�_�NxE��p�BJa�`R�_fK��L,Voz)�X
�|�a{�z�!3�/�(���H��R����_HTR#+�*9WDV��lz�AiC��{����W���!Ba�-����V���fpT��tQ=@9wT��n�II^�;��D��Z��[O��7oX��h_R	�`!%�ur�Wh�UPU��[e�g���I���%�����^Si�	L���F��4�urif��/�F��2���m����NJ!�k[�M�����#6�3��!	�*����u(_����Dc7�
.��`dH	���X�E�4Wj�E	h��0/�����x�6����>����W:�j JY�1��a}�����������W>aC�r�d��:�	R�F��WC-�'�h�d�1��������'�>�\C��wk�����;	�	���w5��e%
*���_Z���{�J�pY�m��
�oP%z;"Hy���l��G����`4�dx_��X8������-�����SQC��r��7����"���S�����F_��(����Glf��+�4�pf�.����������(������wru;"��B20NI_�����YtUG
r�\[[2��]@F��?f���X�6�.�C����S���muu��JH���C
4��*������4@_���2�q�iJ�dN�-����=�!xzqvcFM*�tvsc��k�e���&����1�
�������pauW}s4�4f����r�P���\k�\��U�f%�~�4� ����>����5���y���g,����9��+�&�T��h�\��b�����6kq������������f�	��c��BR��]��=�����5\�JD���P0vx��nRn���G��I%-m��}���gN�/5��l�M�\c�t���2XG����(*2�	7_�+nh�������S���,�V��B���Cv����3�b��8�*qx������.v'NyK,S��Fps/�����WF?OI-����;�~��DO�F���G�����(��'jTN�����a����'i��U����<M��3C�,��dE�w�	I�z_I3������u�@&�������!����4@���F)�D��o�h`K8��6���������WlH�D}25����9_��Q������G��]�.it��>1~3���=�b���/g������k�.�_�����S�&���>,QU'?t�>���:�����b��
�]F�n����X��d���>�W��{c�a��l�����:G2�6��K��%���HV�B��T|�:�J�d�8�����f!�[����z�]g^S�7�g�%2���xed��, &T�p��7�������]��~�����p�)�y��<sI�Mj����$�h������	
[��2I d(1|E�-4�\V����-�!2\\#���$
�#�@	)�s��dT{J�;�1���|���v�����M�rbn��;�)w��>y���'�p4�d�,���0��"p�����2H!Z/���i���,3��I
���W�����9f�/��y(��{vZ,OO�f^�rS[����r{!g��^�5[�)�5�fe�J���m6��em���BQ<A�'7`�{���@���k�gE]��[�<��X��$����j�_��	M���w��-���e���;3I����J��YW��1����s�W�^x��>�(��*��-f����0�Z�=�����@���y�S�h�4��$��!TwG
���<u��5�VF�IHv��H�����(��5�F���px_U$��������$�q���k�����N���sb�f�lM$c���97F~[4��L�4�%%P8��k�Jv�$��G��O23���I����C�2�0���
�J�p|}���u��t�b8ONa>*r?��ZWy����x-�8�QE�Z��O[�-d����V��W��i;��Y�����<���s�9�yY��7X*�����;F��t�>m0����g�0��r����������i����2k���W�E�������^r	��D��8#'����YR��\8p����@^`d�����	�N���e�H`y��I]�O8	���g!n|�^�PFNj��������<]F�2T�}z�2�A����k�K��z���X�.;Fh�������=Yf� 'O��:<���I�.�HA#��S�^�(B����{��}�/�$����0����Jo���B���P,jf)���}�X��9Mp��m:u|���B��zTu
�=�U���Y��c6QJ���<���L�V�Jvy�H�v���
"c���0��W^�(�V-�c��$� bf�4w�G��:Ss�go��Eyp����U�s?a#����"�VBB� �<@K<^3�����b�1\�/z��r��8 K�������_jE�������I�����m�S�+��q\�+�f�ywf�gq��
R|4?�i��
�4����5�v������h.k�+"C��,%.��/�&��D��b#�P�J	0�Xsax3	������rhG���`:�
'0�����5��
F�%��L�1��;�����l.�6��SpiJ�	����ao3��,��"��IP*Q�t�C4;��tp>�1�R�w�����	�f"�Q\(��
g���pfXFBcyq~s��������c���j����U���u�V��$��R�#x/q��1����7m,'��D?KM�U�����=���R���L��a������h�q�#�a<��073-�j����p`��3>|x��nJ��g�Q�$���&a�1�������Q�h@���DB������;�~u�;�L�d���1�((j"qH�{
WK��^���b	16�M?���h������Z:��2Zg&I�����RR�d��`t�����U4#�'y�v����p=�����}f	#�7�Lr��������q[�P S�`&9fP=`CPjM�8�]1��	����b0��vy�M4���G���`������%����n����R���X5��Q�h�x�eS���xz�o�l�\
��%���$e�c�P��39{s��S���XT���TJ��oK�� {�'N�
���/3����{e��K,�fJ����92��l�!�A����Ion"4��s�67aw���OD�Q�	uxI��0H"����&`G�����#/^����)�z��W��
�hY�_��oC��f���#�9O�i�+��������Z
W>��q�����-���������
/)���E6�l<����E��=>%3�f�ya�����10IA-�1J�,�����2W���A����r83���j��S�5�����$!R�m��I�4-��D<9o9�J�k�P��)H�Z#+�u��y,���}�T_iB�yj��x<��F�EeU����j��,�)���^j���A|���C�Tz��wg���%_�
���-*��MZ�����:����k!�i�
��~A��L����!G�`�Zs�T��<:���9�\_�]^��e�~����S�#C�D����6��sy��S�������g�5o�>�Ji�&�_Fv�|��+^��������YQ}KX�=����&�u.V��9C`y�%?�s���c.����3�C	x[893z�!���^�/7�]&�%O\>w1#�\$�y�)F����R���V�)UU�@jJ��(�N�7�M|`p���FC/h2��F���
K]�4$r71�i��Y�T��jP�����}~��Z�8��Lxxw���i�n�!oMV�T�Qq0��XE���h������s>zR0��8�7"E��0V�����1�IO"C>�-�q��h��D��Z��N��������H�Y+ �����<3=8�[��7�L�[Ab���	S5�,��F�{�����Y��Q������s�@�����o���"�	s���v�KJ*$����oi���8�j��0%�H<�s��B�eU�N-2��6��rs,����B��%������AI_@`�\�LLB���n��8��~�R�l2F����U%#\�&�FA<�g�JkE��'%��X�!��MBl�9b�Mw���#&>J����Gp�����_�v���>#�������%�^�b�mX%\��J+���$+�D��L��@'2������;�{Eg�%���}�|6�[�&;N^��|���8��p�<��/B<�����V�,D<"��hLCf�X�ol�4�[�A��
����MP
!�y�(��`m@��,�!���@���H�_�`���2��B�9���*<���GzQ8)��=}�#|�'@���E]U��tR,J�����i����*��xf���2�){��b
�|��Y,��i��d��O� �0�k�e��ul
p���4�UO�w]�y�D]����q��(Z�5E� ��J������x��${���7��p2z$r�!�i�7�u�x�r��fOG0��U��������6Y=j��hT������X���R�Q��y�em����0S���h��Nq�"qQ&w������](�S@8>uV�{��y��<����`�6����,��[��6V}N��A�0m[�*�����u+��-����'���r�vqMU�kn#��M� �"5�����PRb���N�u�AdE����K�G���u7��(�������x>�$�|���q�ix��<����,��,Sk&k��z���LHS�^D�Y���(�����wT^����{z�v*���}g�
��3�+���|����w���8vP���O���@�n(��n@g�z�0��T�)�-V�y�V���P{G�M��	����6�������.d��	��-�{I�?������p��h�B�� ���w���an_
�}n��|�(�yI��h����[}smQ����sY�����j�������.&s�FlX�6a�I�J!����*��g�b�6@�:�`l���q8�(8Q�����v�x���/+�('�1~a�ymm`��;��n�Ln3��Kg�2�E��/���A�S�����VUu������\���ZK	��|_��C�/���4Q�@�o:�����o�����<�	�������D�v|������j9�@q��o����������?#���~���uM��������e=",�#�
f��0����w�E�u������}�(���i�v�+J������7A�$��Z@V�%�z;f����7�?���9N��p�Y�G�iun�9/��&��U�<]�v��;����\}�p�_�|�i�_�n�\�����-Y@���g?�	��]D;��[E;��2���u��.����Jr���]��8����O�c����<��kZZxg
?M�E�5u��u���y�UM'������X�8e\�	��j!LV��+ma_T�C�F�	g�L>}������/���b9����������
�7Of$7�9,��h2 nF�������$����,�����w�%��a|Sy}����FU��A<
���s�D�?>��nw��_�\�'UZ��Op�����FB�eT3,s4�������o��V�1������U�cR+��m
W`��%Q��1��'����
&.��cdQ�x��qMWA-!D� ���3?S���A*�����mk����|`c>�h��	�Af��{�y��;��������8��c��U��CQ��s�(��{M5N1�}�*��>�8:�Gp�������W%1�������������w�#�ey���'e��n���cm8�5��vzw?�tHc)����A����" �g^4#+!m�����p+��
��O���$�W^�x:
{/u9I+�DK�f8�x��/��X��G@)�����}MT��ab#D3��;S051;������6�WZk��@���9Ou&a�QmF�~GOX��1�BL�#D�!�T����>1�@b�1>@M�<x[���}Gh@�D�����]���{����J��L�Bg��vq�2b/���'�d���i�������96C������CT�E����"Y!������C������FQ���v��M@����$��7���F�������l����8�a�3����,���_�'�UIE�E�j��`�3�J�R@�tt�t�s�2<�	�%S��0qi���8$�T 5N��Dz�G��m���J
����O�^<6Is���bN�h	xw���<�D+���IG�1���Ww:�f&*f����Y��8/����0���Lp�r�w�o�p�]���p����E��~f���PT���N!G�jJU����L��
���B�����8+�����9R�]v}t���
k����F���:��hc����pjy|�KC'.A
#����L���Q2K�^��+��<�G#��sa���VAF�K��Q��Zx�qm�@-WY;������du3c���Sr�&�@��4�5��������3�W"�E��L��J`P*�X������k���]e�S���;�00���Yd�'\�����
��5���y�V+n�\~�}X�aoj�bR�[���	�������f����2d�gO�_K���cZw8����2��eP�n���)KT�����Y�
�'�������V�O@{��L����Bo`�iX������SVD��o/�����}`Au��ANE-�)�n�Jh\���X���2��#E��y����|c,j[��3]N�"dk4:�<d�n�����9�Q�������b'����(=���K5�����t?�����:�7	�9KfR�M\�q�Fv2����p��}�0R���������������'�����+�R��`��{���]U�u=*����x��\�,���>/�`����
D�k����������G������BRU�^������)!m�s�!^������+/2K�2�.3�\}���T��%W��b�^V,�{��%��d����u���
W��/����T��w2�
��0���OsQ���R��C}%e6��8�E��rn���sT��b&�����}��Y�1Xf�����!����m�k��Bf9�E�_o����,{%��b0Y%"H�UXE�����������N���j6
o��k��J��Xwy�(�������L��AP�{�P6D �PH��&�K������H�z�*��4���-�p`�r}���Q`KSA��N�<�h|��s@�%���x��G`���,e��*?~��
���qwk�F��M?<'��G���'Lit��c�{��H�U�{�%��'H�"/�<:hO��x�0��I�T�\�����0��d�W�����t��;�1d��R�������
yX#eL��O*A<d�n<��Z�KAjg��.j�������
������,
��hi�
�lW�><pJ*t;���D�+��Y��"S^/����h�i�&=�!�gb�d��4��^< �L5�R;>u�1���Yz1�z���(����V������6����g���;7�O�����Q�����j1�X{�����b��PN$�����/���:��V�KM�+�$0*6��}X�c��
���7���(�aM��O%�w*O0��������'���E6A�[�q8�>��pT:6�M-�8!R�`L�f��lm,2�����A����2����{�C��=c���0�����.y��������/�T����E�,�}�����P�A=4�<��<<}����7o���b_���<<��F�h�4Jn*O�N|�0�R�q8}�����
b�2(=�'��������.��-K��
o��Y^F
��&#���GG_x�l�oLM���������/8zS6y��f2��	?[c��0�o�q7n�������w\�H�Y������!��_���'����g����)���;�}�L|������&�\^��5Z��^�Vz��w��-��i,2�N��Q*�2�;�RF/���E�q�\];�
K3��su�iN���gL����&p��J%<����p���������?��7��-���8�wo���f�=K��I�*�d|A��6�t���H�"s8������U�J����1
X�W��+l���l������J���p���/���d�m���{��D�Mne?7iZ.���n���	k��@�]n��0���:=_�eL�b��������?y������������U��]r#����#q-���<`��IZm��.Zt�R�����4��L���[F�[�0���E|���������IC�'�m� h�N~a�p`�0-xd����
�,�����Y�F��C����W'�u�\��v����4N��2k-������
����NV����#����^�m���������Ks���R���4s�v�JYR	� ���}[m���\�v$�"7���^`b6�,
���/� r*�U�>���~��	K��a|��|Kg�'���D����>�0���j�f�ij[�S,�!�H�M:*�93�KR���m	��_��su}p���Tb�m��#O��<�v�[t�OK������oY��:�Ey��*�,%iY����%rZy,'?i�!����A
����f5+a�����C(�2Y�5Y��i���E<�i�6zjc�.y�f2�m�=w3����&��n���;����Q��f����r�r4���U��n����
j��^�4�^;P�z}��.oll<������C���?���z����w[�W��W�`����	�4� ��m7�
^�H
/�	����VY����|����s�1D��|����%�zq��+�
^�j�E��z%�v[;�A�����n{4��Vs��JH[K@J!��{��m��v�M��#��QH��!����h��L�1��1G$�!UJ����`����8��	� /����4��(����l������������(\��,&�H�6'q2����-#K���m4Z����N��������^�^�T��e+�.W�����E�u�bP
�B��8�0	Sr\�wq�U�Ic$`n	�pQ��B��>�}�9)���D^��0�G��RF������3}��:�a����������^*���(��$pjf�d
5�=���0�b�*1��m��G8�`���?T��i��!���3:��"	rtPv��E��>K�r%���y�r�_.v��z��z3l�k��n�_���t��p�i�
�"���&����+���<=O�Bo���`T�}�������]<�E�Yx��y����������
2�t�K�s"���t����<��:���nT�8Y%�v"I�����#\�������?������g���7�
Ey��U'G�����9���
�IU]�=���:��:�k���uN�N��	,�C9��xRy���D��S��d��T4���o����(�hd�	�������s������i����v��������W��8�v�2�uj���Hs���8��>=��>�\a�W����rq�KU�U�,tf�2�E��0*/���@t�a�L�T�L���$��t�����\����������+���#��������&���)�G"at�V�@�����O���Y�JM���iX�-Q�����BR�_^��Vn���~�]�m5��^s���.`������A
�]o#�?�6;��~��~���,9�L����������?� I�(+-���@�dA`�j�
�����`�&�
�G�����Mi�5�s�8�"#��YaGg�D6��A�~
�dP��ggG�������'�W�]#��f��,z-{��v;��������~o�m�[9{��!�
� �������������W��V*U�w�0�l����AUu��J U����]����gZ��m`��N��S��j���r����iW�Y�PV�N�X�������"��X�sn	}�mw����nsX�n�t���Vo)�ymb�W��6�vun6���-8��a�Bs���_^������T�9s�dz�	r�bN���T�P�;~����I��p
��������>�@PZ�p\�M<�H�o%�]%t����#�=���N��/R*�<3��DE�|<S��������;�*��:�E��Kn�����N�4�Z�
�}�_���+��ik!n�R$�h�Q��f��&�����G�
���G�|tv�,#
2s;KLQV�k����&��J'/u0U������$QyQ����b+���7l�� `A=`_1a��[g�.�h	�H��
PrL���$r:��Zm�1���[���.��f?��9�c�����;Bd�W�@����6������o�F�~��v*�m<�I:,�
�g=.��x���its;S���j��mo4��=X
fj��t�XT�^���a<!C���x>1�-�|T l�0�-H^�Z#������9"G��//������p�o�S\�G�T�L�a/��;����V�
�X[����,�x>b	�����o�rI$���<i������K~T^g��
�F���*'(��{����������5�������������;�A1.ps�:�}3/?�f��M�/����T�E��L|�Vn����ag��������1=t�)�6k�5
�}6E6-�);P���"L�W���1����^����8�v�����HB�����!JY�&�^���S�z�ZJz����A��\(�����`��@%s�!�%�����J�S���B&g�[:n)����%F��E�I/��@�<�:w~�}��TK���V�����Z��Q�x���1���Gwb@��z����Z3���c���t T����:���?��w���M��.�6w�j���A��Q�sv����d
I��u=�n	3��Z�w.���1�krvM��3�'�q�
BT��E�-�%���*)�g#+������{�����`���Z���z���k;V��}�a�cF���57���o&6��db��4t/ce:�4�7b�|����QV(b���T���n��	5��4
4=R"�^.��U)�|a��_Z����,aV��
__'����JH�jr��Hdra�O�L�2��3�p���V���Y�b#�}Xm�d6�+���8n���F^����rM,u��f�
�3�F������pD&/�$�����������a���0������u�U��:�m7�������XLV%+�=�X��qk��P�����������@�e�}��,����S�����Uo����c�$1���
���2C���a�����������)�,�;�7��Q���N�6��'��9� ������T4��{�=�(��q����y�7N��n���`�~�ia�@�����iDH������9N<_=�G��HR����'OOP�hYle}r.
���g��Py��S	���*���5RPK�? �@��8@��)7�z�}fU�^])T��E��d=��kw��$��]�tr��g�+����_a���_��<��&R>l����������D"������p�OLN+A���`��|L��:�;�0w�����&�S�����>�!P�����
����I���}8�F�|����:Q��p���ppx�9��8�KGs������������A-�uv~����X/��'�K>�D0�����h��^���p�`��_8��%=��3rr���������n����I~N�T�YDL������V�I:����� )9R4�M��j�A��hV��4�r��L{4��K���I��e�:���*u�eb].GGf�"��O"��c:��� ��g���1�����.K�U����jG������ls_������c�T+��Z���#X��1��Y|������,6o?�1��0�8y���BV�
�3'�`:
���J$M�D����->'���8\���;���c�@����m��'�C=m����!�
_�������7�)�n�v��z��Z#���Vw��*6:p��*���d=�$W
����L�{�c��'���������'^������Xr�����Y�����z�gJ>�_�)��\��7��vhyQ�8��a��'QS�R���L���9�t�#p	�oc�h��{I#��Rp%����Nso��n8��`�;h���p
���K�����1=���1K�����%���'tT���3����Q���/1�|��DCqD"��Do�����;�x�p����_���������]��8\{��.?���A$xu�����&f���������3�
����Z�Q�������?���^�;��f����@���
�9�@Q/n��
����@���]n���	�r+��N�]����-�[o�w�����j�Fk���j��?����������0��|<���b����l=����Tl�5Z;ac���5�/��n�lw���`��;h����F[��c�*MTcG�����j������`����$����>��?��74�F�	vk���,�f�su};�������"�jl?������z}�U/_�����s������w��fs�GE��vQM����H4���e������6�N)�j���AH���6��o<���.s��OL�b��#:q��Y4����6�7�%����/#$��[ha�j"ODd&���#9s��%	��A�;���Hi���3�Is��)��p,�P�rnpTNu<+��z$~��x�����^���"���|����5�4��)�h#|$K�pJ����@����oO8����(�a4�r�mB��,�1P��#�`��(���0��.4HrGe�C"�8g80,Av��+���M�3`L&l��0�i�3���C��u4�&���8Q�����1o���Y�h<�k��q�H����l�|w1��� `���G(RD2ZC���\o!1(��n�p[��;!~�S~����j=��X)n~
��|b+q����z������?����2[��WP�NO��!��fz+���<:x}zT#�i�?������	~��J���l������c�������p��K�u*�C�w���
��BV5A�3�u��	D�.q
BTu����8���,^��Q?I
<�&&��f�&�7Wk4T���
D�3�-��n5�1�O����T4���Q�3�m��Zn�S��77�C��d��3?�)U+p��������m��zA5���7�FQ5wG�f�5�T��T�[kA����y������#��s�2��1PxX�hD�rF��Z���z��9�r�me�����T����$<.�eq5������I��@����3����p���l&���(�Kj�m#�V�=��r����~F���<���!?�X+�'q�CV=tT�;=\si�������
��J��b���Pw����U��Z�6����znn��bO�Mf]�&Y�n�~���D�������w����p�\����=c��5#}2F��}t@������N�!�'�?�����>�s�A��z��Ol{����x�x��K�s]�y���9�8:(^|��s�F�C�M����RGV�,#�C��
��
=��@��r"����8s�O������v�������z"�9�/\��)�1x��D�����U[$]����A�2��:��!����A�N���S��m����1�$�N��&y�x����*�����lgk�v,���Y2�A
����������/�1��������8[S��z���g@F�O�s@�1�yBa�~��M�l�������������5*w��K	�������V�Y����vck���n.'6��
)M�$�47w0v�:�EwB�cx��k"z��y�/,���&/�N�=N�_G������&w�`p&2!b�S����~������
�C�u�2���C���(�'W��]\,oBg�s����6��e�s���cCn�R�%��9.��Eg@z%V$#�V�SJ�B�J��$L���M%�x��t��_�[x��u��0�Bst8�j)y�MbA�R&�+B�iit�
r4����A8������k�zt��������_$|�
�C>��6�7���1$��N���8�J$2c�_?��	���n���^[�^{��d/M�$����?����a�����"�>��&7�}�qk������O���K��V����������G�|Z������@<na���;�Bz�
����$���p#���b���j��}�)z_�]�S���n��Bt�<�CP��C�L���c�qh��^0��������{k0.��7�~�<�4���uD��F.O�����L,:C_�a%g�m��A?��x���&�B��f����p����H�K��oq�1��j?���go����m���O�4�i4���:'��	�?�,�6|�����Q�@\���W����7oo�:������hkf$�!�;%�s!��p���7���4�a3�!�I��o�g�e9���(�(�r�,uj;U��Lo;@���At��.=wj��h��u�Q�TIW�����!^;/�u����q}T�����0	������������:Ai>�j����4z�@�����}��r��
�$!��$���6|���1dO�j���LYr�����E1y;
`��
`/�'
�/������2mUW9�Am�����#c�&�!FR��AP4��),��(
��$r�)U����&�{P�@L}��>��i��S�B`���[i��J�@�U}���1����a��<��������t6���p
���s����!��0,�K��e2�~9R�?���F\�KB���Ns�[��5S���������~c:X��fT��F��G(�3:�?�������T������Ze-�X��z���W�L_��)1���:4������Zmg�q�����z�[{��)�Q�2W
�-C�N�]z�w��6ZE���_�
�����{������s���"B3m]���C�r��W��7D|�a���t�AL�n�l�r�M�+�j���KrEG��V�-�M������[lb�^����*J&�Y/��G�q�z���~���Q�z2u��{���l���y�ot���6�������������nD�vz�n���?X~7pk��~�
�4vH,��N��N0Wx5l:��K<p��ul���y��_#\�7��N�FGz)k�=�9*��z��r+eA�;���]��TA8D�<<���Mb��;����^�w������&�s�n��f�i��!m��+�r���F'��rD��� 7YE's+�������Q���q����t�I�����'����n���g�-���1b,���R��x��X���
<MJ�I�k^���V^z.�?��K��c������,����2��<�����9�V�=ee�������k@�����bL�{�����,�o��<�"�Ij
Pd8������a������XT$5Z���M������l�l	�eL�x��9����4!�F�=_�#\,0����J�*7�����Q��u�D�����	'���*�����>���o��
MG0����v�G���w�:��d63�&X�5\����`��������J�Y�K�76�D���O���IA�{[��P�����
%}��d�T(�l��&(b��(�Xn��_�^:��R�)x�l��lD�g8�
�q��lLS�T1F����3�B>v���y8t�`wk��`wG���`�JeQ���d��7�/��q�CA^B�#GoeE������7���u�JD,�rc�>I��h8|��6�H8����B1B������N�f��A�m�E��[����D��e�k��������!������[�| J3(��0@�a=���t�NLq-h���F�������Sv�k37�>NLr�f�p��������D�K�hx�Z�MKW>7RC�:C�++O��e�T�-�O���>��!����"�{��c
_ ����
_���A����z���B����obB������f��B+Av)�������b��Q���T�#.�~RSW�9�]��Si�6S�!���j����a���p
mQ��6��=�����~������aw����aac����G����2pA2��c*]��*�������>?W������������uD?Zc��h�������4q�9�_�^��_�G&��W7Pt������4�_O��9�WM(I�R�����=A������7�z1��igj��y��BtQ��A�����o��{��%�����X����a�X�{�A@
��r�),p#���H�@'���!9L�����*��M)5}9�t<�S��ANg?*��z�������o��j�xj 	�t�'E�Ga����1U�R�b���"]1��-�p=AX���"�ZA`�=hZ����~�*��u��,p��:C�����!Tm�</x����x��A�=	��]����\�m�K�LVo���3��j@��:��~���h�@����t��j��(;ba\��ff��p����qd����C�Q+4.�7��o~q\�pvI�1�W���R���^����6JW��tN�YQ|L�'��>�r��������4�.�?������2���_4�YM�- ���pb����'�|[���e�� �<���
�9~���37��w$�PR�����$���G�%Lb��5FG9s�'"O
�c�[�e���%O�AW���B������Zd��M����4���a�%n�+|%[�$1C���	R	@����G�	R){;Oi���F2'j��i�?��K���H&����B��<�8������Ks]�#�w�^�����u��/�S��V��1���7g�� ��Y��mK�M��C�%_p�s�����0�wnY�-r�����4�Z|�z.�bR�+�x�riI����u������G�A �2ceE�������
Zyba��S��S����5��7a$NR�������Q�I�P�����f�1hOh�H�q3�����3��m4O2�5
�
\��6�J�TAGr���H{����x�>�d��Q�;���ibW�-�v�;���^���u���� �����������*P���S��j�0��9�!ZA#����^]w>��A;:���7~�`������G	����i���T�
�[��:0D\\���R��L�����^�"���XB����d��=1��|�I =�z|jr����u��b�Q���v�R��-�tj����)5
�qO�l�|�����f�1O\]���8�wPJSg8x2��T�e<�_%�����x�-�L:�"�(�j��B�>���ih<q���G.�(R�������i���8���x�C�g���s����he_9�gG�?���wR�o��Cw�E�&�� �z�W����� hay�Pi����-���dT�	sY����F�S5����T*�a+n��5����W����K&;��@,��!o{,K�u@��7
`cUu2����+q`��sB�qf�e����!<�������Wl]uNO����Y;�	UHO����<"�*���q:| r�x���s�%������m�)��)��9M�IpN�X�o���NzP�pN�[��@%��9�|�+���)*�&�����9NHL;��e������=���c<�u�`���+���4�Y<�{HSC�S����w���~�]�O��u�C2��&�d�y\!*xO'K�7���i��	�>�5R�l����A�^5���z�D}jI�4�03w�7�b�$���3�k��F���,�zp������M3��<��T�C��lVl��k�>����1�=dpr>����zC4�C<JH�B������,JT�0��l����~}o�mn���]�{n�����V=6�Y��n�\�E�?����8�f���a5����PXvAK�UDo���"y3�9K��W�����t���u~}�����5d�%RS#U���jP<�/���*��U0Qc���d������:�k}�����M���3h�E��Co�P��_�N�Q8ON���|���H�.�hV��q��8h��J9h�:&��*���I
~������c��L��v_+�������������. q�X,�~�R�n�������?���^���ig��d���G|��B�=�v/�,�?���������{�Vx���Jo	�Y���2���d������d?��+�-�{2t�/��d�{{�I�[��t���U�'����N���m�U�����H�j_�����+����Xn�r��svr�9o^^���:�`!�|x�B=��>s�:m��:������v���-��)��P�x����[Wg��`2��+$qw1x:��������	�/�C��p���S�i��^���"�0�9d�U��#�{���hLe���8��Z�#`���z}�����fc�������0���~?ET=%�N��^�(sJa�m,��U��'S��~�vk�9���'�.������\�@C�[<'��HH0��[����H��#<J+��Bk��)�dP�_9��Q��:�
y{,�����Y�yx����F�����[�G���u�m���^���Qu��5B^oo�g�����w�U���B���t�I}����|�{��u^
�����F�d^�]<��
�/~X+����L��r���%���v�{�I:S����wP����[LX9
��h��S04�ev�h����
��~'��q�=���}�T��������c/%��Y�����*��j%xT�Y����O��F�O�����L�V ;C7cr�����"_.bP����)T�"������*����Ql������x�^�Im���Y�#�yV
�����R?��������M�u�n]����;m�R�W�f��la��D x����T���C�v�S������<��X�O�#y�5����0[hE�!=�,
h!�A�.EeD\m�+Vc������$GYq�q�U�������qt�M�	N;b�����$�@���������0�8����9����#^8�b}%XraJi)5�@�<��f�7�q?y])A���O!���M^s���b� -���.@�@N��rG;m�y���$Li(���4&�0-2K��0�����4��b���)a���c����U������q������4��;�;A�A~H|1����x�
!O<�8�����#E�4.�d�#E�n)2�H1+���K��F��|W�\<NS�]��E��,�a�:�cL�����"���V��F������ �B���r<�Oc�B�E�V�	9��:�Vq�MD�
�y��Yd@&�H[1�T�p1}��Hp�;��HR���=n���;�\#/�E���0�i����l!?y$��0�?�8�5�����~M����4H;�����(V$g����]z�$U�p7ho�������6~D!M�La� �p�9��2 Y������������}}�l�������7M���
�S�����u�N��3�����y����:����C���V6v �X�X���C�n.���5���+��dT)��0������M	<��;��}-N��r!Y	����ay�d��P��4�-��+�0�"���/�&�-z��t���}�a�#mzU�s��q�PG�bb��X��W��������=�c�����}��������O���������~���SeNP�Nx�}Eh{c���iP�����l5X��-/wA�D78|����>2���d7�	+����	z����Gm-oI��SZ*L6���P=k�$"
DwC��:����z2����)���6�L�bHG���z����pUy��r��<'�b�)��������{{�[��Zmw�`�~����o,�j�5��D�{�`v������F�"���h�������8��n�|���J��������dCB��9=�W	Td�`�q�GRn@��&r�����f4���T�2�����E8���(���6��&�j���W���m�����������Wv����
�2�p�3�]�/�T�%q�Wo�S\_R:�("/���*�8�+������$��uxAS��K�<���V�
�Q�lnVw(������u.�]�_��o�>��{�����
V]	>j�7�m�m�O������W$B-�|;�1����ab�&�RJ�Q����dR.Q�C����e\vs�g�`D������e>j������xh�M���|@i���
�f�8�!��]|����1��ob�5���Q*>	5��q�Y��UMe���b��q�w�,���iPD�#`F9FH�(T`�����*�v�~,�4"83P�"�sP����wvqt������N��N��I1� d�N��	:\e�
���b�t��j��=�|�"��t����>`��s�@C�$m������=;�+
�9������*F�����4>�����R9�J��7��#��9��[Jh���`Lk�i�x��I0�PUp!�v7v/�j�8��Vn���p�s��+�D�N.M4M��E���HS�����]�Ugz4P���	�Mjr~RAa���#�����@s`/���Fd'q:7)���b���>!� B-�}��h�>�0P	����y��9(|&C$��X507������CF3�X���X_#O�����u�!KpyNqi��G$bC��`A�q=��*k���>;"��G�y���o��;9<A+������:l��YV1
�H���8RI5gciy�0&o8y��\@�x�b�����q�	N,Mbn!�w�A�@������wS
5*��3A0j�6�g���������0o����;t[��B���vw�i<���I��L�j�u���+\�������������L12��T��^���<��2����Q�{O1+k������gxt��l����$!��W��";8 z�M`g�?@�����q>�u�a-XW�J������3����i�0��}���?��p-&E��`��2�g�Y�y� U�0�J�E���������x/�$a��� ;[�����\l	�Rg�FRF��AX��I�����8J���{�Q���B���L����&U�����(�� �Ha8�gql�����`G��0$���]2������-P|���/
4�
�K��F���~$��I�5��ms:�{�Z5����C��Qb]���r�`����}�	�?�	��������3#3�I5�'<���|�y�aX>'�N���t6���s'��0��@N��C]D�>n�vg�����"�A��>�G�%r#	�p��'HR����]����������$g�Rg_�+���MmM"R���m���6$�l��.�����8��9���ty�d���u���f$����#�k���+��'�f�!��W7�����4}P��H��]�#��+b��a%��q=�A�,��B��w=��3rq�P��6*X���R��������+����Yg�����x����
����9���d�������_����3�~t��\{q���$�G&[TT��F���,X�`-a�.U���4�2��u��)X�^N�0vbN��`N;�>�p��������6�f���/v��V����(������>[M]RZ���|��<3�D���y� s|�
9a(
��;�C��!����>�
8I��_��lF�����~ �@�n�'X���}#�`:�,Bc72���\�o���}�d58��&�_��b�=��L��%t�7�YL�p���-eZs$%��,����0L����[���P1�h|U���\�A�h��rl�?F
����]��7������6l�B�=w���0�S��1u6��(E��2��c�����4m�� ��w��u]Yq��7a���lXy��"���!{�1��,��U�f����
���q��6#'��}��6aB%��J�$�0�+ M�?	�����K��x����������[TB���wQ��\��e�����
�\%jA��d�F�c��G�$��O��i�-�g�	����8vJ�u�q�������D&��5���m��N����*k��jM��O�W.S��\�g�U��	2��1s"k��1@�
�4�T�c�i��P���d���<����D	J*������_�x�L��(����Bw���r2m�&�pt���1O'�G�TX�KO��&�
�)�?�
�OrF���5��P`~�8�����n$�|�a[++Y�����t����P/��*���GN��DO�6(��o_�is��w�~���^\�3�_q�����\Y1���0���j^s��i�xH�sv���&H�iE`���|�R�)�~����|+w�~���m�7+�6�#+YU�|���V�v�+7U3�M���5TU��t�|�@dC68���:��R��y���9�(@�	�KD6BC _�p����Z��R��Jw�O��v&3V7m�b�<ZYK��\<����@R$���������eC���������r���l����J��B�]�z�]���@UB�hRq�[C��:���Q�J�>z	���t4�*�9$�j9���������uR�����`������D�����q�g�!��?�p������EYd��9$\P@���������w\?V$�GjU�V�k���3%Z"
<��(������;�V0�w�W!�K��f��18G��A?�Xx�x2"�Wz�!�+r���ds��T^���I��n���h�[�N�*�VT	3�M��_���9Q>�9.p��>��9n_�������o�����1t"9�3�[Oo�'?��C��#�Ij�=
��G�(�� m1�FJj���?��f��Z7N+C����0���������Sv���'L�����S m��/}��4S�h�1���g>F��3�F��Xz>�S`Y��V8���Y��V*Zj������`�K��=�%�����&�-����������?/�����d��Bq��
�#	�{>�ZP�/�����(�jn�1��O�sbN*�k_�p�_e�k1:|6W���,�Q��LN�-�dp3�m�wS����@-f����lV�0���.q
�����"9q3�@���4�x�����C�X�f��Y�e�Y��"�`oA[�vu��;:������cFg^�HL	=1��z����f���F���"|vg0\5�,����C,u��a�)cW�+��&;'�4?Q7��hb�Sk����E�&��f;C��E�G����):&%����|C�h��z�zE��4���l���G���9{P�Q
P���v��}4g�r��x2����A%�v���or�������������k�<����5�z���99W�A�	�E���j�&(��%g���;8s��D���t|�2%
��;B#���`]_q�v����|%J-�����W<a��/���`W���������8:]�0����.W�f�
]h�Nh9����`�]�UGAu�������&�>W�q��9��<I�
�@�
��)'mlF%��6�����d��q$9�O��>���T'���.������"���|�������snZO�WE��`��-b���97c������A����a��z�R)$�\���9�;B�G�������������2F���q]��=}P��5�Z��Ror	q����I��N���(,�����
��T��N��4���l����V�vE>�;�
JG��w#�Jo��s����EP���K��%(�^�����V)��f�4�"�'jj��W5$+?lX�f��{�����
S	a���,��� �f����H�
�� �n2�nZ�M�����-�f1���|�yg�7��/cQ��^���DE������>e%����38�G��.3����[w���;��VK��A��%|�IS�8w~�ix�
ZL6�lQ�p,P!�<��U���=U���,�J�2m�M�4X������((r�{��7Z���0LT-��K<�
iG�����M�SJ�s��GJ��i4O�/�H%��Lb���y?���	J�g��%����)�0��q�s���`����������(�����<C#��k��=��QEiX3��D��s�h\��'
=c4?�$8^tqE.~E,-�fJg�0��u�K8q(�M�l���	��$Z��c��`���7;�Y�� ���R9�+>��>�q8����YW�'W�#Y��\���������Y���I(2��S����s������b�n����y,�<E�6���<ZMU�S��$��_��%!�t�g���b�_�/��bn�P�"�4B�X����rr�4������Py?	/'������H�C%_zYA����hsN����uj^�����px����A���`X��5��,EN�*tJ�@�uq(���]>��'�����,����KU~�r�,�d�g���2��z��5r�7J�R�[����9�A�Ak���]p���E��-�9H#�*��7|B���-Q�����7��M�E�j*�Z�gD<���jH6N5�����s�������9����H�[R�[�^��}��( wT(:���G������n�;�	�<����B�����il�v��V�����n��s��<.��xr(?G���;��?�5sw.&���Bd�V%���j��o����1�������s��������Ei�O���4Q����{Zn���
Ef��<j^�x��_����^dS���)7��bo�G�������N��k��������������g	ag�����c3���&�.Jz�>�=r��Q���(���c�C�Mwl�)��:�I�A>���	�|�o��i���8��[��`35�p;��F�V[���zh���w��=24�\]����%������Q���W��X��^���#F�H���m����N<F|�h�����#��a�$;��l���)�PcG����H
�Q���pZ����J��	��f��D���C��r�X�z��hK����8���0��R
�0��I��~�\P�����A����8G\n�hVQ�/7���,�������xZ�N����5 3��4�H�	r�V@�����^��iAsi.$H����1�P�S���}����#;���?N�����<�6��*wfZ�A. ��dK��,�������K��]m�	�9�Z7J���?���3yQ�)�S�f����n�-:�w�W��U���f�}�~qd\�A���P��0�E�|7�`q�.�ke%��?{�`���6����6�~W�����T� J�T��u�S:�Q�%y�lf*����"�:��,%�_$�l'���<��!F�g������/G���	����H�/�X����O�Z��u�`�s�E��K����E�9�=|���K!�qNA������+�
}�@�����]L�"��7�������N\n#�%<��M�MAA��N,l$����U{~+�5��~?�L�����Vd�r��%�#����O|�<
�Z���b~A3�eo6>6��Dy)Y���R��b��`���H�_i�!���T�Q��
F���o���e}�������M4�(5>C%�!�*:�p]#.��[� tH�(T�k%��Q������'IR!%���o!��G�J�Sp�<n���br�(�����P#!��T~~� {+x�\�;'��.:h���T'�����;^U\Y��lN�r���K`��?�\����z\�,��Xq�H3r#������<
MU��B	�t���]��r|��IA����" Yj,OR�T�����I���3�R��9fs����dQ���)��|��R����U��&���*��LX;v�f���Z/Mi�Sh��E:p�B	�0]LTC���t����Nt��&��E,��*S��?H@��]����@@��z�1���F�������6����ta\�TID������i��7L�����\��'`�z�;�KCZ����od�����,�[�����zn�U�e(�� P������@������G��N\����Ie+U�5�dy�v��z�~�]��F��a8�����_�'8J)�U�"@J��{C��/����Fo�D���W��Sru�t��+=P��=X���	�4������sNe�|i`I-1z:u�|��v,���9��� ���q[i����&E�2=�K��G@��,BoI.rL`?jl�����{��A��`&G9Z���U�'�D�7<I9���E2�n23y�BI����V�Hm)mHeh��D��sv:�L���H��c�m������~�5fy��{��9/�@��bW��r�*������r#v^��D�y�VJW��^c7��;�yvC������g��WVM�\�>��6���5�n�N��&����s[<p�*j
w��h4���HD0&�kzq}��j��Vm���sp�,�)�=�YG�R3��'�D�����A�?�_r�,kG���}��#�k��x�{DT�5	@3
X�!���T634�	��L���C�x$�����Kk��������W���,^���X~(����,�\.U���W�$�������~O�{�����"OQl��#&�5���k��G�jr�%�T�DA����trn��I�;{{�\�P6�A�"�GJ���5:7��1���F^�J8������G�z.�Y�{C�(�"�m�n9�k�#�:T��=B����X\pV2���S,h����DP!��$r���1�����������~���m��$�.R�O��Jbiy�&��s�Pu`�T��4*���Gf�����
Y�r��S��A#"�<����o����yJFz��X��Ao_x\�<��<�� ��,*~��(�]t\&��M`�����F���m��Mz�l�����Z��r���lo68��������������g����AX
�<5��������*��_��e;S-�.]�	<^V����;����z�Q���b�h-:z�8���0�p�'���o�����:��m&��F���{;\h�[�
!f������*�0����^���U��sugr��}�zs�����$�(�rE��^�|���:%5/�)�#�Q �U��nZm���S�x�b�����rb�D2�v	����P�gv:��s�CX�I������*���#4���j��gw?r��7��]^]_�����+$���x�����IQQ3�#�" �R
��L��
�{{��o���U?J��(,�>����'Y��]02/���Y�/����4cj3up�Gb&������������6MX},UDb��9�t�P�ca\���5��7�`A����5������Z��|�+8��1rDE����=�g��prb����a�Sdh���j�/��@
�x�@b
�*��]�|O#I�_M��b�
����~�� 6���A��E'�����-�j^����	C��3�B�4T������|�>8X_�������2sJGu#�L�����	R�q���b!����A���,��$qD~h��`4�@#�sE�X�Q���� �mS���VI{���8g���#/��w���`(n���=�&���%����>4��<
P4����b�I�H�����c��V'&�_��?OB��2�x�`�"t�����K,[�C��i�d�)`�eeNL�h���@)-����Q�K��;�����$����m��to�&
�����QZ���g�k�h:���zI56�F��B�Z>5��>��e%Q���U}B!�~�\����%����rw��]B�T
�����c�.&������M&B�0k������y��_�������L�'�DY	i���Wi�t���A8�k�B�y���."�6�y��
?����NH8��g���E�����{A;#���(����H���"I�O=a��T�^�����5oN�*^�{��<_c��_>�pE*��L3�Vy-�p��Z����,<��w�����(��W?w��6p�*G;�_������*��J����f�t
Y��3,�n�r�b��p���r��� �7�����n;����7�b�2o\���<�[y	W�9�^����;�s;F��v�i�0C�A�I���F&�� ������7��3l��G�[V�qg���/m�{���THf�4���@a}��{��0V����b����#7�d�����{�5�9e)�1���?�_0
U���4������r�W��4�~?�D�����^����������O8�*9;�-��l��G��h���5\	d]� (Yr�~f���=����2�:L������6��'k�pO�q��"n$����3��'zw\��S�u�<cH8��h�i��!z�4����������c��zw��x����i^]!�i��m]���23L��e.�����KyvN/�-`Z�'�Q}i�U�(0�]{]���0
��i%�X<��O�S��
a���(^��N��	��3�]�
��}?�Q���f�C���}�g~Y�v�<��=n��b\��`kNf�N0�oL�CMg.|��Ai��Syj�
��Z]��]ru�F�'
�������"�BU�Pm@�r�2r����X����	�!���V�AP��y@�j����.q����6^���~���!�ja�Q� ��K��'
RW
�yd��JI�9�D��v��<HO�~���e���A�����
��Y�N��=N���i����@��:�n����1d��p.�4��=�A�5\9=C�(��>]�ep�&3���l�	�������|7w,g�w�~��Y���}{{�����e'�k��3�8����&@��*+��[�������������u[1�2�bv��@�&f�
�7�I���(�2K��_������� ��\m��������RL��k������K2���J�8���Ld���+�E�� 5�y������^�j�|{����7����;����K�����N�����G���Hb��^�h����0K-�b�tY�U����e+��v�\�'� ���\�:�@���>NF����.��8d�%�Xt'����o��M�������)d\�b����z���<^�@a|8��6�a)[����W4,1��~��h_�5�x�{�j^�w�l�O�����u��Va�`��}�����t�6E3�����C�0���R��n���E�������J��N}���!��()�+���rd�M��C.�Z��x\X��5�vS��B��MU�'�(x!c<�UU������o)�+��(��,��;�P�TR��z^<�8ju.���ux�9������UO�'�-$�����G�m���*�}�}S��j�\9��3#����;����)��g�B��Af�3��p<q���������z8CX[��+,3:���n�;��B*�ZP�zl������j����g�h���1h���t���.*k�U0(��q=��>?a�4��I����6)i11}%��������{I��X�5��8<�����!����^B�����?M��{fB�&�4z��PLP��Wb��#X���o��O"akG1��3]v�����y^��9y����R�7I�b�'x�GZ_������h���roy���0���I����Z�����{=pC13���K�l�W�4��\�,������)�;l6�.��9�d����G�X��>��Ij?3�����G|g�mi$v#�,�<�/[N~P��|LB�!�������Y�g�W� .�C��0��M���d�H#��,�3��
_lJl���'Mv�p
 g�26�-?���N�����)�
���{[��r����mk�N��7�Y�')
w\+��H}���)E����0G�����zO���/�A�JA��A~e&�����y�b��?k�u����v;��s�m�������Q�����nu/��[�~�6�qu�%�voy��:W��N�n�k�x
�R���a�E�����f��������y�S<[+|S�E������%
�aI�=����Z���W���(3W��9��W���������A����_2!����	g�6�nm��A�;�:b\)��	'���h�[��A���#Z��/b	����T���Z��Y�i�M�1,����g�Pb5�"�TenT��&
���t��2{
�Uh�����(~���m�6��W�K��=_a*�8}d���h��^dw�}4��}8�N�g�].��"'^r�`�|�YM�I�8PM������Gp���-<`sm�G��<��.���_g3}F�jlr���&&z�"J�p#%�3L��a�5Q���X.F��n�%S���Z42Q�s�����L�-X#��_���-L�/x�����
�8���a�����\w��~������uKyEM&�?�Rqb��l���z$U�}�nW��2#� ���=f>�a��� �������f����Gc�	���3����^rs�-e?G�����I r���h\��#{B�:LZ3�+nYy���6��h�03�1��!�t��"9O���)���na��~�knV�=������sD��aL��4�-�b��qzTtpG�$Mz���Q����%�&��L��M:�!�9W��Jk�4W�M���4)�<��ObA�Bx�~��3�x��]���As���=9%x7�'w�<��Lw@��9k�Nd����U�2�Z��K�z����m�"�1��/�������d���Ha������	������
�/�3�q6��I�ah��Nn�`�*~0��SN)po�d��������<=9>�H>��;�r��F!A������}S����G�p0K������������q0E�Q^��6Rhe��/�����Q0���Dm�9�Z!SI�����g�i��aP�WL��n����4u�C&jI��h^�O��*E�����b�
�[�ug��=&���A+�9I�_��D�I_`���$�����v��M���;�&z��4�#59�R�*�����sbr250��L�M8+$hl��h�?�~-�S,o�Zoo�<c�	o��<3���g�~`_�����(�{�/�s�%�������!��*�+G��&�n����a�^�4�r��y~3�����6�������FUi���W��f��)�����E���	6��}��)�"L�3
�E]�U�@&��#���2Q�����X-�d|��y~,��9���^�pk�pH�\L����I�����Q�H��+�m��*���q��L�V��f�<,��ZR��[t�������N@��nT%�w�)M4q[��?��q����W�QO4���m���:m���uI�?�(�w@����C}=��[���r%L�����Y���+���u��^�5Y�w��Q&M\u^��&�(@d/��KN��	�L�����^�:�+t���w�E�$I��kn�����a�3�O=.���c���y?�FVl9�����f�o��l��S�T���f�m���
.;sqI��W�W�$� il��=�w����3Y��g�
o����J��2��ed������q��z^��eR\�i��x�6�����f&��������J��a��o3a�����!�s\����#�G������@^�y�n~��d%�k�&k�o�f����� *'qQ���<\I?7
�f�(�.&GZ_W�(r \<��:��Z`���.�i���U'pjd��r�ny��fTF5��s��Y�����3{�;g�d����?|�6�j�����n'<�E�����
�3��A���e�.�[)N{\��D8.����T ��f�.�����#���I]�^�xSQ?�Xv��N/�~�!�kV�����l��!�����,�@J� ��_>�������$A}E�������������{�y�r8O����JqW�TP����'�k	l�=,������1�
��A���Es9�z82X���4a�)t�?��'k�<�y�R���S�W��x�q��`HZ�M��S�D�vN���[�,e����LO�H 3��%87TUO����`�a��
[�+���
\+�0�LEg
j�'����
cn	k���[`�)��Zt�L:C�F�6\f�-a�1����k���,�*�$�sI�IJ���I�EH�H=M������D_��[�����)�ONt<�#X"B6V����j��`�t�D
�nE���M�N�|�*BI�V.��VK�u�HpN4a��\��i�+a�A��^�����*v�1�]�K�R��~�0�z�XP�@f�:����A���c���6��F�')����'���`g����a��7U�1��RC	���7�s�q`X�����X���X%�e	�$��{7r��0�7:�2L���{vZ:�K����]h<Db�"E:�`��3��1�>B�a&�ni��L;�?�d�L��'��*v���W��
��]�e�SO]��w�}��� ��(_��z�~��S,zD`w���������V��w�A��7XP���<�b�c����p���^�
� �%I��%�c������)�
�`�I���ui������Z�����} ��w�^�"��L���K��l�gJ��8?�8;������##��`�q�C*�����{� !��!����6�>�:�V����X!y���5��_ST�^u��l�z��N/4�Rj.Bs��s�3B�����Nc/���Zw���t����[�t�s!K��;������$Z��`�}Mig�L������~U�"@@�2�,}��T���M���F����-A����Jt.sU�A<s:�W��d�U�c{�����y����P��|������$U����O�����z����7���{���.p��Rdj�ZH��)\���=Z4�����;�Q���u�G������g���r%�'�D��?4O;�N��8d^Eq}Q����p���<3O�,)� ��Z$�1;�?�-;��YI�7�|�;X,k�����]��e=
���'
<(�fU���d<8qPygcFb_��:x��H�yJ�tw?�ooD�n����6��(�[�L�����$�u�B�
g9�\��R�
m��g
�t*�E��:��LR�}����Wy
{��M��Q7�wY'�����dt4F9��N��=�;��XwF�gr�h��kPD-���A�0W^��r��V�{rDX��4�aE��]���Z��uP��[���R���p
�|
m�����2O0��U�9���KCt8�Y���W�����?��/k�(	�\�������7E�z��hr��8)�FQ"}����Oxl�o�V{Q*t.pp\�F�P��8����R�k���<5��������k�? \������>�������V�����o�s������!�D��~��@��I�6��L��-��K,[�7c�;]m�0y�����J�N�Y�!��4j�����y��K4
�w����*k$��#��^�����-}�
�n�����7q����g/�U��c�7��xa��E���:�P���w�2�51j�i�e	�[�	n[
�=a��7o���
��Y;�c���`e����������$�d��<�0�<@�&�����q�w����[O�:�jm��w.^�>g.z������^�}v)�&��PY������S������_�q^P$&��KZ�d�U�vm�zo�[c��S��m������F�]��������u���pe��?�V���jp7�g�������j�x-X����jPP������������Bx�e�yB�`{n���M��v����`�?X`�����S�s�|�:�@�3@������/qRM��S�{�:������3*y/7�u��1-��!QS��::����s�f�d��5�3��q�}������ewr��R�A�V��:�E���He�N46�lo�z<������bgok���F�~���vA��[�hw�-01r
��9���[do�9��k���S��<z5
,
��"��p��C�����{��F����C������c3o�����A1��x����QW���i��@�:�������<m�����6�7�F)B�v��������~d��R
�}��IP���u|s��>����i���Y	�u�oQ$�+7Rv��
Z���d�1E�8�X��:��&��������q�>@Y�"]c���)E�
�	���J'�t�E��,�U�}qsu�jw�����_]�_���Y�%�G�x>�FT �=r6�a~�x5�f�m���5?���._��g�<�|{��������i���Y���O<�l$��a4�����e+1�`�a�������+[n�������y=�iD`��]��������f})�Y��BNZ��n��4������s�w�L��Lt�geE�P�ahFJ�KT--
����M���O.�O�J���~|��=\�'�7P�NZ�������v�^���,va��,~h��h;���Nw�ht������Sm���G�~������������m����iK��<�M����1Z��o��{[%gu�&�!��_��"M���'�R=�|�Y�Et�����+N;���jJ����d�Kx$��l�=a��7�_�0�eC�[Q���������Fc{������
�n6�n�=R\�oVrA��`����X�16��� �K��{4����d�7�SQ�-Z��?�����/����_��_L`O�Q�-�k���l�z�
x���*����9����<=9
>������������S�{��,���R����~5��
�Vk+����������MP���#K3��}0|{���1X�9�rY���Z��+d�LU!Hh�c����l�����	����{���S9�K��R�5Z��R��{5se�����RR����n�G���b�.CV��]�E�5���@�R�Bs8,��y�����&�
��.#���o;�-�8�v��n=���ja�����8�w	���[�!����9�
�2���7��+���&����xC9�&����y'9�t04��~e#!���Ps����T��`�'0x?!O5������,k���:���p����@v�<�Ba)�C�Q/�fA,�M����5`�o�u���m�`@c-��v����y�T��n�X�2�����7I1�w>����|����c��Zp��:�V!�c���d�����T'	�`��BJ����9K)�.� p�'�� kP�*�W��s4t/���A`6�29�	d��*�9��Z��P@>���9N�n<DN��z�(]�o�������}�n)u�<;�X�����)�6f}��~~h���A��'5$zP&�7&��[��,6��'�V�~o�1{�����F��?��u�E����4����]�7�i����o���G�M�E�b.&[^��I�UFI��6��:������%��EZ@�i�cO.�E��]YS����b�R#�I����v������./zD8�`p��?��~��
���n���86��,���������a�T��*�11��M��S5k&���@YfS�b����p���0G��"	A�����^��h�$���Ha`�U�z��)�^$��%��Q�pm�(�������P�����F	v���}��������������!�q��'3�B���V{7H�9�P��J��xU���@H����+Nig�@���O��O��K��hS���7`e���LtJ8$��O������3�Q�tq6��-�-c���K���+�Ur��,z��o���H�^/�����1e�>����$�gp�l�14���n�h\�b�)M�B }�~qqY�q>	.���}�dU���Z?0�k�k���W�v���V���C�J-���f�7�r�:m]����xic��o�N;��E���>W]�A7�!��*{�y
8�_3�����[�O��2����t6��������U�
��v�{����cn�Op���������;6:��s����&W���}�:l��T~��:?2w7�w�pX���U�nY�w�c^�v������v��U���q���.~JYF��G'^��]���7w����(*��|��2(y��X�+p��_�����q	vo�q��&�fb��"�3������Cq{N��6M����G�-'����)]���*3U���:�!����0������Y�;��"�'�P~Yr�p,��J@������a�Q.�����y�0~h���0��e��7�Z0�GU�*�k��n.���-��w'Wm�;m��wr���~���l�
S`���H������36�����vu{�'�F���x|�������=�������Y�jb�����+�)$q�
�Hq�����]�@Z�D�����62�1�����
���G$�3#��_N��QP�M�Z�����J)	&x#D,jt��o�P����cQ�{w4��e2����#E���T���s:Qf\O��M����JFCQd�����S��\����]^\]����6�m	v�kJ��4-OhFW�������a7�������^Q����S�>�$�`	�g����M ���&j��A)a��T�m���x����;wi?~�H5��*�V�?��_�/�1�B��������������O�����������J�K���"UE��D�P$�]�6��9�s[�Z��7���_$R�iq�������_�i|�����7H���ry��yx��#�MSpA��q���y���N�3���!r��'|��L'�=��\pkY�M��2l��G�e����J��\��^�,07�Ls������i_����CP4�Z� ����5`GM�u;���HP��{��aR"On���T#�RI��x�+���Ny�����3<0�W��\���U�X��(���4��p���h�y�U]r"\p��3T��sP���OA2��E����]G}D��|I��l/������;�G����^��[����D���$�%���������U�/��$"�+�2O"�r%o:(	�{"p���:�?E�V���PU���t�������<�%1
��O�q����Jc���dmu��R���^i�� �ar�I]�*(z��:Gd����{��Y�9�z��x�����6�����B�ir�A�_@���(��a)�P9���l�~����;X��m6j������.^������z�`���I1 6��p���d���:c�?�vw�g}���0|�v��[[��������������z��9�{���|<�,x���������
v�0������������G���.�������z�f78��M�6��+��Y�7V��W�Q<����#0���/�O���%���,N0��7��z�����n^���0H�A��U���6����V}�=���W��.3
n�����:�P{
���6L����)����!Jc`����Imu��12����?�
�_���4~y~��T�B����W�Z��y��M�2qy|�����_��W���^Ve�k�o��{����V<0��=y������y��5
��������r�~��m���[�Gp�:��e��������[�H_�Z��}MC����[���A���%��#�����K����~cw��Sp����������������]���:���x���_��C;{�6Y]�<~wr�:j����l`�i��9`�1�����Z�[�m��}��d�ZR�w�Z�WXn'���:�Fo���m�-V�ywr��� ������7��W��h8����5 �������w?����&�n?F���+���N���<��q`=��m��5Zu]��-y]��#�(t����tm�	:J)�O!�L�QR\������>���������l�=p��3W��Z��V`)�{���:F�lHh�3��1�7#���h{�X�9V������X,�XPsj���������tb�oPc���5� f$v��:m^�#���Fx�:�����b6<�4���D=����e+;��� ���������f�?����r���Wzy����p��A	�_:f/���f�<���_wH�S���&�M�S�3����R��xx��_�X�P���s�g�'��rI���������:oKI�f;x!l�������������M����3	�O���]������vw������H���������@�q����?�J�s?)�0��e/!�t���������t��sfi��s��!I����@_�E��Nj���Y�����\S����������x��!w^�fDWg�{�����x��D:4��p����i�h
�l:��b�9��� �R��}���d7J����(�-lC��`w��q[��$��{�i����kw��u�8[C,\.!]TZWWWU)X����Oaa#�<=��h>��J�s�dF�����vc�G�<\�G������|w�$7���n����?e.�?���3%��v�`U���V&�\�d�zy���~�Q/�5�"����'����|]`{����t��:�Jl�����?/J�-���yyRT�����Wt_����A���/X��o����]:R���o���e���y�:���	�����,1������'\�f��b%c���-~��yK^����Lg���6Tq9��*������8"�V
�v6������[`�6nQ���dn}���E�z��p}����<�9+x�;���0�~�mF�)��l}�'�)���������`��d:D��7�$	��[��s5dP��~[f�p;�����]�"|Q%��[���6�����rz!@�:7p��~�s�]��y�I�p��!C��������<�
Yu�]5������H����:��{�@U#�sA�kYb�r�}�����V�,N�+H�����}����^�\w����i������������u�����T�%�K������&�C8���T����-Wt��������Fn�"�}A[���7��>fC1��Mg�H��������1E��?�2�"�F���F�iJ���<�j^\o��\w>���79~�Fg&e���]��h�^�]��4�<`T������?e�����2�L��eo>�5��m^�Ix�X������4����o��I=��+�����s�{����+n�D���de��\��a�[F�|�];~�n�K����M���M�$f�Y�u?m����4��<�`>fXw�%�_3����k�X��M�b��L��}��A��3���^�A��0�t������d~Ke)�&C���t�b������C���pb��T����q�3��|��dI�&s���d����~��"f��2��~��3�l����D���Q�����w���i��4^�����Nee8z@��8���t���j��}�&�?J,/w�������X4��t*,��p�����kl��U(�K����j>�#���Bo�J�zP��K�f>��L3��Z�����6N�{.)�e��`����yu���z���i|%�!�k�!�\@9������W�����Qe����S��������e}����`k{��7(�L{��O�6��:��/�7^��Km���O�[��Q��
� ]��T�x����w_f�@�xtc>��M�����Fpe�����F����3lA4��Ozs��w�x@	�nC�c�����������m5�7������B�)[��X����,J��7�[[;�[���� j��=�w}7�Ip�S���o5���{;
������v��p����W��W�;X�����kp��f`h3�h����6W�d�M@��VW�xY_�����������j�\�o��JsC���[�������}x\M�(���a���Q�����.M����\Z�<zy�lE��lm�~�����/?�u�����l�[���s�Kg�2���n���l������v�&�L��VT�T����<:kQ��fA_6���<A�ri�����f6���{���UQ�����6����{_a�`��GXZ��;��A������>���p��#o�3�=��'��o�^���Y��!��>���3�	s�U� R��0��������n���`S�����>��/q���h�`�V�]K���nw�[Z��<�2�Z�5���)W�1	x�}���VP�`t����x����(��&E�k2�_�����]4*g�4nP=��p�):�	��D?f��A���4#�Df��{q�x��@1�9�
����(�.�)�7��	�KG��y�V�B�������V��8�1�>�\Q�?�L��03��%�n]��~�4�S��C��U�L�1�9�Or�#C�#4V������X����;�SPc��R���}^�q`a���sCU^76*�;�]��t(xC�����������GxP�XlDH����S�-Rzb�s"�8�D����������\U��#(7��������*��dh�V��zS��i���3�,��t�*��ahc�T���M��0#�����g�P-����pp�$�~h�'u����Ol��t2b-��V\��\����.�3*s=�n��oP��p��W3�e��-��Kq�	l�F�Pm�D��9���������I������������k8R{A��y���!	����WW����r���!�J^a�jH_���C��Q_���W�W��!���!����1oH�
���C7O������^���������9c������-|�_seNp���������'�\��	���(5�������/��bB�#�;,~����xd�����S���*�
I&#5�zP�;�d������n�p�r���b�2��W�+�_U�|�
��|\O������BxF��[�+��9��6TL��������#�H������L�����^1�u��Dq y���rf������i�jF��F1�����NHo�d��4M�A�m�)�J�b�M���3"��t�&dI�.������qh��"���*&�b��c��s
���u��`�wg'�V����h�>��$�"��]H=P���:^g��O�ZM�-=Q��`B'���L���*�yf��+s��(m����6��#���;�xl�V��"1�)��**rF���v������A.����D^	��zh�,X&6�Vg1W������5c��F���c�Jv:�+&��)���a��4����.�	��-�Ql��a
�l��\'t�H����PZ�#��������Iz�(]dTbp�^J������b��-���/�L1�����Y!�^Xs�IJ')��K��X����L�f+�XO
������C��*����fk�$0}�]�pq��V�Z\�A2�S�k���4����r�������lz%����8���
r�����
�.��\�9��#�d�-��X ����QU�\UE�+�*��.�2
�Sf�����qg�����^�F�"p�hHs���:���?�B��g�Q�q��=�L>m�����;����I��(�I�C<�U�p�����������c�/�`^�e�r���i�E�j&�JH0����5TI	��D�1s�^����������Uu�5X���������RMH��oN�H'�n�Cro$�&�,
i���>#oC��y*�=�����5 ��.���Au$�Z�C^$6{A=E�+����b��g-lMJ�C�$*�lnV���pr�Fz4�dh�JW@������?F�"���*�����w��"!|
������dr$������������{S�UE$h&���������_�6�^R�����mM�F��T����!�J�@�U�Z"R��|1d��.�&�����iu��7�
���S��U^�g
��h��
1@�������2����� ��x.a��<��

�T����l���#�Q�����FQ�w�[��g����aY�����$`���"��6Q6r�ie�HJ�|tl,<�2���n�����R�p�*�4���FY��o5N����1�D���������-�u[6.Q�
������%��p�v+�X�'3�L���\F ��x#������8��z+p	�V���=iP�xw�k�L�� YH�M�,���C�#? ��
w���Z�����w�g������{�DK���4|��@`RN�f��q�����q��%y�8{���,Z1��dkjfmH�V���a2����u��S��_���%�~���=+V�Y^�D�����H��������8
Is,��S���������z ���8��%��#���N��fF�1�!����jS]N�U'�
��$��k&�|#���_��@�@}���)�D�Iu?�
�t��Fd<��	3�oc��0�M�zSYH���3*1L�?d��&c����F&�:in�uqj&�*9�����S#o���Z��H�8�H�KJ9�3�lq���r� ��1�0UH�j�J�j@*�
�{�"G�����L`��	.0���G����XZ^g����i�5��.jM��@G�����ip�`SB�&�l����r�B�y!t���t�?�P�������Z/��	�q����|��i|;vu54Y�[��HyGg�'l�o�z���E��V:�L%���a	��m��E���������u/��L�s1�(rF��o"`�	l��)��8���q����M��0����T�z�1]u�������AI,H	W$k�L<�!08���C�K�)kt2����5�P��u���}D~z.���|~Hs����"��?�	����7f��!�|L3F
��� }�q0��N
s�������R���i����H-u�N�a��H�d��1�K�TZ�f0��Hr{k;�qz�@Ty)�Q�M`\56t��p3s:�y�h2��9���`����Y��Z��k�����"U �8!7�&����J)/��nu�i3�<��5��5'jD@���&X
�S����I�J��G�G���I��"/��2sU{���P\��yB�R5z��n����`������N����vQ[�������t���_�fvU��T�������!hR�Q\�q�8t���]`�A#���4`+����<y�1�0I},�T���k
��`�*������c����F����/�)���MZ��]��vZ�mMk��;���H����e�����:[-@���N&s��L,	�
��:������ B����Wj%x�	v���p���{r�i
2`s�����)���K�_p��
��(�CM�����b�Y��-5��^�j�i��-l<<���+�;��	���g!Q4�|�Ao".�m��vbfG+%@W����GV"��s�������)�-���pP�	0�a�����6���[��}a_k ����^
���2��Z�����6�"��!eO��l��L���.�L
���
���v�
v)���;^M3z�B�
xj6
��D��`����x8
^GJ��(Z#GF���o�E����6��!���Q�������R�*0f��&���|��>P�yG=8��P
P{zf�K����0Y�����\Dk#j�}�!���F3����~�Q�P����&����T����Y�oF��[�]8�;�.e��V#�Ff��!9�B������G�����>�e�`���� �I�Fp��!*`��,�2�`��d���KF�ok�P`]AJ?w_����"�N�7\��V�� �K��2���Y]�A���jq.�7L	>�w0��O�x����
���W�r��F=��	y#}RQ4y��gC,��u����/����F����
xCG	���9�)"��0p���e�����F��Yl���u��A���A�X�;O;��_Z��65U
��I{.:G�z1)#��������cR�KA�%�mOfK�m�$���4�(�����P��x�*g{�p?�V^�U�
1$�2 ��$�U��ln�J:��@�����q���@��7���$�j�������N6�yC^#������0(�����v��N�7�����3�C���f4�F{�f�3��vv�a�Vo��V�V��Cl��j~O��c�U�]��_��������u�x�e�_��!wT�\�6'��\�h.z� �o��)'h���^�B�S�]L�v��H��~
R<�������g���!���PS)��&���\����`1aRu�;Z�v�~�u��t�z5���x��[�p���_�5��e�a��7��^�n��������T��{;;�ZsV����F�����I�*LQ�u� ���B�np�UA�J]_��!�U`c_�\=���bq�DSt�!��\���*:�:���}q V[�Nn�u�o����
���?`l/+1tX��2[��C��N�OE,>��L�?��J�w�X��1�<�`����,���6l)�+����k9��t`���wH��W��E��`�vH{8���A�3��!n+����G���`�mU��V����s�l����[���F���B�� ����������U�O8�Q�D0%�8�����v��f;?b�����y(:l,/����\��A5���=t��y��<���4�0��`��,��Z;X����B��`�4������K����7.�|������k���'��RTe�'���P�,����B������O����QL=.Q��e�vy����x���Y�9�l������M��^�u�����dI^��Z4�����'dQ����������A><Vs
>����>��Y��G�]_3�4��3��1�rF�����S�f��F"��/��Y���Z���Qq�]R�'W��#erV�����Q�!)A�b�V���=6����yY�����Wu��]��w��Anecx,u�����AO)�����'"�P����_�z�u�(	���a>�!K��nz���MpT�6� U��~L����6.$h.!�V2�ec�_��&�p����F�1�!z��
�I�-�D���?�I-���4�"p@�JQ�&�B�Fy_Z�
�W��?*�\z���z<��-f�����]�q�%���c~����SO(o;������>f��x|x�%��?��RC��hO���(�{����aq�N�g\@��������.�	��]-�W�_���/}��c�th7��y
��(;q{;�d#U�`���>���
�N�������������i�n�6�e��G���F�F��\�GT��@�O
~a���K����_��G������*oP���Q��������bw���9���q��_�f�������<�6��Q�H��1wi�p��?ga����v�z���^�7�]���+��$�j�����|�����������������Q2�W�A)YjwM��lK�d��2�t��I��\/
g���xnL�P��+��OZ������
l�
rR�����&o�c5(%&��yg�$W����:���C������l45�zT�,�Z��~��������/e����^Y��3~��1l[c�����&
���;�~���C��y�+�����Q�wwv{�V��	�a�A���=
�R����k���M�t(�+���	�P�A�����o�6.#��h�
����'elaq*�^N��*%]-�(�!T������u�Wc,/3�Q�LE�����-w�>������
�}:8PO�a��?�i��3@���j����~|�����q��B�y/��BU����������[r&�/M��R�&���D��<7�y5�{�������J5m����F���.a�3�pN��
������."��p:C��G��):E�����P[��"*US��2Y����	���E�E��wI��������I��A~`4:Pd����
	�;�~+KB����5Z�I�Y<P��6����0C��R�����ne�o`���@^�?(���Q��o�/m���������vxWAw�h�-I`��k������f���_�)�����i�F�����V{������T��A���F�p�Q����z�����^}�������	
��m���w����$7����/���A$E-\`�L$���4n��=9���>2��p!����=�-lA<��F��
�?I|�c�����_��0�^�e��������pI]A������h2'��.�l�p�������q� �^�?g���i|
�)�W,�5��%2�j�y���!y24��b��A����re�=;<��XW�J9�c��Zo4��|�p����D�E�����|�:�s��Z���W�����[u%�����������9E�7����d����i����Y�����#�'$�����|�C��Tg�qt�<d	M?�Hqv��T���u<���",h9x}������*�s	l9��d�@���j����#�����b��%��r��Y�6*m��z�����^.�,8+H�8j
�q�����w�>��w�ho���jc�2(A�Qo(��6�t6�����w�.��\��0n;����G<��s��I����d5@�����<o*=#�C��n��{�E�l��e��O.�Da����Q���d�%���t�ph��O�)���t�_\�=Ed<MIJSkOA���x�����X% x��2b��:P\��Y7��G�u�mU�lnU9�q5x�gQ+)E^�w��3�Yh=n�P��;�1�,�l����y{�pE�~SD�
�'�/�N{6���q�M7+_��f��$�X8�z����C���L
�/���������o�������:u
Z��?I�F�|v���f];E��l��7�������S7��S|��[R�C9:}{��Pq��$Sx�0�C�^�m��E��{y
���CF5>�|.��{�JH	�>�����l�I%�;C!���'���E2�
��������MMlPjr��Z+��i*��/�9x���V���z��<�Cg4pW8�
7��p8��"����B=���T���33��o9�
�Rx��J��,/�b1C���]��(,,�����};g�M������DZ�	��^��k����1�M��y2��>���R�����w�}E�C���C +A��E����Z�]�����7l[�q�C@L�t�����E���1�(^��%/
`IM;w�)�*�>�\(`7j��/ZvL�d�A��^��j����h	?���U��x�w�d.�#�����T���>{%��M�B�
���B��yL�@[������V�����TD9�=[F
���i�o�bF����Y>��B�z���\0�j���9x�I`���sN^!������l�K�J]fZ�#4.MD�m�W����&��X7�d�;��?�U��!"_�[�_��ZM��x��s*	����A�-l)��R��1�{��a���vL���H,)	��D�N����^����x;����rN��2�]@8kr�y� a�6�y���i1�Hj��3�r����}�j�>���E*9��GcK3�R����q����9�`��[*S~���U�9/���N%;�d�_�+be�!28&���w���O�O��)���4��KE�sr8�F�)&(��WK�O�
;tR���C�i���O2%��16&��'��G���N�T�#*����nV��]��T��1���gS��f���2�0�u��0XD�'�M����_�0����v��#]���Y�����pG���eE�2V�5����0&�O�#��+��b�C��T�~����|
���Z5j@��(jW;�V3��������jID)������a�Z�
��ME6{�l������
`q��F����4Z�P(�aAd.?�n.�y��Pl�.<(h�c2�����	i�����[<�O <].���4t����O�Z�8�-�R��A�sN	��t���������Z���H�,�4��[�����f���}��+�%<����s�9V�;��	a��W���C���H@�4�d(x0iq��rP-�)�>h��'��$�������)$�BC�j>~��`����-`���N�)���9wvI��2�����!�%���,rs������q`.�3�=������rC�%W/�$��obz#P��w��,���-zd������q�2�/����b��n������M0f���U���^�������}����@S`<;�j�Z���5D���~��u�����A���(���A5����5
�:��"h�h�5�~���>Q��gS�8�bu��f)(��_)��')FY�_����=(���J4g�baSNtJf��sR�	���x6+���7��Vu��q�Z[)��������������N�c=4��E��E"^��3%n���}9w��������QmP��;;��Um�V�a-�hK����*]
�KU�W��vN4:8���*y����*�����f�;��L�����r�.1����`K��(��KG�d����Ct����vV+��������l�0"�O|�
?i#�J�����Mdx��3���/>��C��6����e2�-g�D��v\�eB9����	��SV��;���;7��0X��wm�9h���qT���f}�i���]�m)�g�epIk-�.�G���.}���_�*Q�z!R�d���v�O�H$��|s���������(NBF�6W���-81+�&��6��Y[.\Ha�n��f���r����R���K���������i��BJ���;��(#��:u�
��.e�j�)��9���a����}���$`�s�]NB�����0j��t�u�����RC���)�:�����O���������7�q����N	��2��qr�C���lQ-�|0G#�8�!u����#�������I{����pV�����Z*���)���-9�p�y�z=�t�{Jp��Gs����U|��\����Te[Nv[0L%m�hz����/%3���g������l�s�x�a�_=�E9����S��p;��S�P����#�)r�k�)o���� ��x����&��;�;D�(Jd��N���@�[6>�	��Z���bT���
!`��b1N������?g����E�]�{<��������R�����Z�����u����e@_WV��d(��i:��?�/�Nw4(��m��v�9.��|��Xp�Y��O���w$���i��z��c�x�����[<���{��k�F=��_k������g�G�������;
��^}��uP4t���N-
�F��nkX���o1f	?�t�F�y������	�ys>�7��V7@i2��������MG��u���P?�M����^(~��Tk#�]�fFxtM���a:�����m����;��c�^�(Z���uu��	��j6$bm�����A;��*��-�O����J�E�[77W�M��T�A �p2R���}2�<O����F������^=����Z��r�2�����Y�!c�G�������
��er�'���bv�d�����%�.�Z�\�����ju3!h�q�����"�u�3LO&��]�����=���7����{�A�YkF�����Xa��Q
1�k+(f�����Q�?��
/�!���;����'��y����{/{'��R��L=��{�������A�b_�yd�	*&�}����2b�j<F����?{�����OO����$0�W���E���r
��?u��Z������%��d��0�����LY$C���aS��Pz~�JB�6E���=�d������������jc�����vhmau5P$��
�X����	X�A2\�sJ:[-(9L0W���"$y���9���^���M����hx=�'Q�(�����`��@�:\��/]�K
���0��vM����(�Ip7[Qz�;+["��
�nF���Jb.24Hz=t���y��rhw^$P��� ��q���C��P�?;}��x^��W?F���3�]������� ^�Z"Tb��6A�]���7,�nmw/�'L�����{�{R�X�dk��{��}�m/�..��/�~�=:��7���t�d��%+d���!��H=�] 8���L�Q�s�`3�D����5�I�u-�aU��� K�m����c����Z8��A�M���e�D���oUu�v��h�+�q�U��������aH����c�����,���k	��(����w��^0G�U�W��Ky��D�_.�| k%�����R9�b����=
�/x�^\v_����!�i�g7���vG��V���M4�����y�{��-n���XLDJ���?�7�"��1B�@r�p�g3������n�)����s:mTJ���%o�n ��l��"�.f��=-5��yt%�
�����^"�~8����q��u��K��:��\p��4�&�oA5���>���'�5�y�!D1�
����|-r�|w����"��A!��Y�i<*
t-����y�P��
S����Q/���;��
$����W����B/���G��}�l��Y�0���D�A\(`�[��u���^��]�Q-��`<�W�����J>;���>PW	XM�J��1"!8=��|AVj|b�V�a_���]���}���`[�Vo���V��&+�2�n$��%�V
�zj�y�	�V�II����
��=�����3U��5��@���c���|j��caj�.��(����"A>"g������N�$y�B�,&$Te��W9���~Hba�������!�9�����x��j����p�t�1�Kk������'+E1Z,�����o����?9��_�?;;=����t�J�$W�GI�������FR���9�3P%^�����J����#��
�����o�����c�[����i��x1���>������ u*-�kbe�4K7�'J�
��&�^OS�)��[�D-�����|�12���*�����������u	�^
�
�G����}� ��e�7�(VV���j��E�^��N����q�?��7���"X��8�@Hd�>	�6���"S{�.���9�7v�I��H%�b�����@�
<�6ZY'�;��9�>����������>��T$R�V�0GN�f�O�!���c����+��e��Cr�\���ciBc�_�4�+�M���n1k:�����X��1JY5�VyGx�R���t0�j��Pq��_��b@���-�����+�0��K�-O�@s�E6w��cyM�=�%G��f���w������sJ�L��P'����O(X�bTwG���l���~�N��y�s�^�^Xa4����	��������(��g2�)�dT'�[U���:���95�L-M��17�z5��&R�|��MWz
x;d�%��L�����I������
H4V �����8�)��lo��c%��n`J��ON{��]-��aP��7�em�_9�J�B7+��������.?����bn<�a~��Q��7
�Sc����d���r\���S
�Y*��7�WT@�SQ�Uu�Ok�6����8W��i���~2n����M�'�_8�_w7}�&u��e�W�x��x��C��}����)5_���`�������G�����G�!!���{9��-�����.��K�;<L>*�;�O���+Ff<�*�G>���>v�~H�`��fzz?��u�����B�����z��U�D��������E�������;k��8�O��9�������;���DtT�D�w�C�!���L����WKj�(dY��2E%��������<��h�w���&Y��2�
6���K1�_�/+a����C���������F���N��[�k}�j�������1
i���m���e�D��:�<��fK�x�B��NU�P���u���B<��$��=@Up��#�������K�U[��5_Dc`�*�V���j:����'���n{7��u������?�?����Q�>nw��^���T���F��P�V4k���n�6�<���_a�O�g�r� ��8�}@w�f��/�����~v^�jK;��.�i��hhrVom��0������s����Y�5Vc�n��Q����
���	�� �f1QA�����N��
�����V��b@8���]�����2x��C��Y���I���2�]��ZN�
��7
��86������5m�7�5�M���|�w���vmF��7�]�j�����k+��+�nU�]�X����N�1����������������s�`����z',g�����|vz������>��A%P�/L������S�w��#����M�[P������N�sb�2
�QKx������7bl���=�+4	���(�3H�?�@���4j�v���������i��j�5��F������3H�D@�Z�\S�~������g��)��� c_��J���l��Su)�mcaQ��BD�:���Au�<~x^���R�����$XV�ayt�F�(�
��P+�KQK�0�i����m��26�.���l�d�uZz�R<�j
B�E���f��-�J��)ja�f8�S��M��e�h%������;R��7@����b����_�2M���D��B���~���k_��7	�LL���S����y�2�P=���dV��A$�;� Bm��l!;[�(!<d'���8['h�����������/�r`�<���W1j����M���exUP[L��*J�C�s������FPQn���5�G]�_?������Hv{QR0tx�Ui
)|
�	{B��%�$Qu�����1���n,UG�r�o�����X�06qx�(���o���/�o�4�2N_���G/��L�L��2�1D`\��A�p�2!�������gJQK5�(����`�zLCA90==T�8DS����-�Q�����u��L0x�3��O1��=��������Z��XcJ	������H��F��
I���VL�~R�nR�H'!���5b�L��8y�t�P.0 8���_Z�Nj�&6������
:�6-��� ������r|0�����)��Q�Cb�|���}M4�;s6X���`;�V�
M$F�7@t3��!����V���H*~��17�$���������`��#�	��d����N�	��&��I(S+V���������J	�@��p��f�|I�����@���r�4�M?�&���Z2��)��t��S T�/<��	��G�
�~�|~��C� ����(��,Ou��VW�t-*�B���vX�x|�e����$�ac��[w3{q(�w���m���j��/Ux��8r.��7���g�v�S/7����:(`��$�4Sy����7��L���7]l���n6U��^m�B�n���^n�F�����s�q�5p_�w�"4}3/�}��X)lv'R��_�2����zU7�+������uc����Y&w�L���^�����t����a�8W�g�O?�j��53@��J�K[b ��W��D����rq��!�.���/���}��,������$���H.j`sP@#CD<�O��.dD�FP? �(�L
��j�1���>���w���_�������^������6��#J�����wa�����mQC����f��E
�n����v����~��R�><��{��J�r��h�k7����~F	)Q�
�WC��1(������V[A}T���ri7�iUB�����:��K�bE\=��V}Tkwv�?�a��l�j�+����+��%�F8x�Gm8Qy�G����PE/;:����������gi������t��@�8������B��{�x�Wl��(�������a�f?��`�����X�m���R�]'�XF���H)<C����������	���q%�T����������fJ�x����
�;W���V���`a�����\kU��0Atvps���i�����OH��h[�A{�����q{����h��,�~g����D'��eP�g������E�8qO����=�)����mR�}�m��}s��B5�	|��r���5��n�5jW���@��Q��������F�u��Z�:*�����D���c���GG�����4<�[�<��
�
�V�C
���B� �)�SK����c�����\��	��V����o����6�Y5J�wX����
��>���O
�?��m�|��L�����7vY��Qm5w��?�f��o�����1h7���n�3VK�h6���x0lw��n8�
�(�k:����s	Q�)��k�����V;����>�l�bI{x�lmN���� |e0!�tu�b�����vh��A��<uVbp�s�|���c��{��lx�6F%XM���aH��
$�$�XIh�-TA�0tm�'�>@�;��+b��6������G�CE��4����"b�#�&�G�$�}�K|Fi'7}/M�
�w.(ex�4�3����Z����{����Q'�E�7��w������|����Vm5��������[���n�fnbQoi��K^����T���	�����(p�������9?=QB�An�m����n�zI'��v��Pg���1P�9y��
sx��t����@i?	�!�?}��
�0B1���_)�9�Z��n�E����N�O���E)�)���R���(����T/������=��P�C��QA�}�b>�p��#_��]��Q��	#`�q
)j? iR)����D
%��ga���+Q�s�l&�����
:����HG�������L;5�E�3�B�V��(���gi����o
1�}`�*��<zU���`U#=�������/�@G�7�"�O���F�^��$R�pc�@ a"�[vT�j
���]zy��"_�J����3_�����`V�J���\=�C�QY/��]B����fk<��BE����U4k�
0���r1��R��\��C�>����w�g����>:��"`�J�4q��t�40O:e[�D$������fJ�B�� ��!���[�"�Z���/
�����_�5A-!�ZQ^�*�H���u'�����I�g�x�Y��9�|a��=^�Q��b��'=3���+k�zpsS�^�W7��C$������8���_9�A(���2������x�|I��+ae�,x���T<GJ�k7�����������lw��5��j�E�Zm��i���^��7jE�z�w� ��q��x���z��!��������j��x��*����x@
��~����G9�@�E��-�d�3v����J��2�������%�)%���Jf������H�Z�����q�oh�-��������J�8 �f���i���P�EhUj!~��x��^�2�"��(�s�m���qtA����%g��|z��,I�E3dd�\	�k9d=�J�jK��Q�^�MJ�������������F{0n����uA���Hz����j������:�Y��7j�1Y���(W��(�D�D�rc`�*��j�W^��������7��{��>@�ZY�����m��o.��fe��R�,�!C�@d
1�����nZRS|�=�����K��H�����)���R2O4<�8(Q3~%:��sE�(-pPDS����n��'�5
�W����v��3��t<����y
�*J�3�HY� 6uu)+����xvyJ`;��/|v�u�6W@�"��QgT����Ij�q���5�k�_����$���" ��P"1J����"�S�Xo;Pz��_jU�dYg
b_`'@@�dR�\}��=�5�C�����>A�K�����x������+��|�Y�M��	�o�L����O��<����6�k���sA��g}���_��o��G�|+7�Tm���'����[M` S����7��O���a�:
[��i�k�����x��E��n{o�G�j;������~��q(Q������j3��oh��ayyoR�oN�
�@���
XyD���	6P��`B��h������R&o<�|y�Q�p�����j�JMG3D�Fcr����V�4.F����vu�	�ZU��H�E�n���n��_�%��d�.r���Y���{�0��������8�$B?�����������<T����#OtDgF �`��ZD���a�sO��)����4�*�6i�F;[�"��'��K��"b$(��1���z�4G���*y��,���X��5����
�Al!qH��g&��*�0��&p�CUf�n������yq��<��$��|3��'�(�n��74���-�A��V&���1V�����Q/��8��=��������s�.	��{������Z�\�/�^.���g��v'J��<���������z��������?�����i�h������>���/{�{�/���Cr�����"y�D"W��I��� �+�6���joX��~� �`���OIiZKgt���B{��������Rg���A����(���������N�]��-�n;��l�����o���� &���'���`�S���3�QGq��F��T?�L?y�V����6Y �$IE��������V������w�����`� ?p%�7Y*533'u`W<��90+��Q<��b��Y���t'��\9����k%�Y~Y�7s����9���������
�6t%���3���M��O�/������jp��/�N���~v������O��������Qu ��NM�?��Z�vUB��[u��S��Nu��_-B%�ox��p`���3����1��r����r�D_��T���1�fwBz�=u��u���L�wl�N=0��j�P}�(C��<'q��Y['���H��`���:�>S�����e������������]{�^���#W����b6�{?)�g]�<4kl� ���)�����4!&��[�VnG�gl�#��t�_�������%ju�u���q���W����N8��E�Z_)Ncdk����.����t�(H�����K.�1c�im�|P%��[+/"'���L���#�����Z��u�e�JY������U�j�7vv���1k�Q�^R*����Q ����S��jZ��IJ�`Kr���jf�@��%�^���dI�����NKHa.�8H�X�/
�H\|H�<I�A������+H�$zK����t�Bf%G�<g����Z��hP����N��j�x/l����m0w��E)�
#Gj��ZUHJ����e���pu��2�?G �����/pXR��J7������_���_Y2�I��&n��C��6���&7�*X�!������
4�3�
'�-��8�����I2��[}~�_~�o�&�y
������X���dS���^g<4j����u�-���l&+�'e���kV������)���W��v+l���wry��p�{�y�S7_n��������Z���\��a2��v6���`g�<�o�W�:���xh�>���`BS���\�8�?��	��{�3�$�_r���+F+�OJB���L��%��<�2�q=�J��Ni��"~�#�����Q9��8��N��4�������Q �x���I�H�����I�d�Xb�_�����^�w���V��������$�?W��?����i��pQ��YH0i���C�,���@G���x��|}FZ���>��[��K*C��L�d�cCBj|W4.SB)��9�8��O���]�'T=���X�IN����z�������
��Q����
*�����t��v4��|��7o�}���l������6�������j�6�����?�"��_�m�&��e"���
����L��k���5�}�X�/������^3��"�C��n�!���R�U���:��C%7Z�fsw��v�E���$�ZE$�o��YQ%��+�)��(���l��������k�<�n��3��5�cQ?�J�a`p��W�,p��\`E��������A�&���G�v�.���XUw~��0�'frd����'�}R�C$��>����,�2'jX@�!4�=?,�e�"����y�#-�Jb�����+�S�[�q^Z��H^�\�u�{��� 0
�7��u��r�4�A'e��>�L�}X���3=_�f6�?�m�/�b�������%���d��9�b���(rAlo����A5�������`�jG{��n���H��x�N������� ����o+d|=��P�E��c]���*o ��,�Q�=E����j��&����u� vf�>�?�AF��`�M�a�-����)Pm���]`%�3u�%���(�����r���'sq<�Y���	_d���+�Ex���_#�����R�I������K_*�|k��Q������k���ey<����*����uTyO����1qE����o�����_Wj�H+��?J��,<'��H����F������_�7���d����To��ss�"���lH_��n��������"s0|Op�{kj���|����ky[��j����+��6�h�����\��������E>DM����Z�3����z?��y�Q��v����/���y��rd� (������-��E�4Hg2���>���x_x�Z#��f��k(���&
�S��fn��6
���8D�}�'���h����h�d�g���;�I����V����I��sZwF�/���w���\��~��oN���8�y����`Fc��'p��j�B&���(���V���s%�@5��Q��['��R�������v��W�^��S|&�5
L��4��M���l������Q���6��jH�q�!G����f�s��t��s����������������q��z�����A�'e���?�uO��a!���5��
wAs�~���B?����K@����0d_OERACp�uc�?��(�G�������:���^dX������������(����U��q��-o��$�{������X��9�� �'KZA��5��4xiQ�4�X>=C�[��H@��>������FU�wOz'�.9|����R��;c�{���Di�+�yN��W�����dOL�Y�����;Q�"�t���GC���E�C��~�x���'ZD����;E*''�[L��n�* ���
�����������=�����j��Q�h��b�pG���i��������O���/��u8�^7f�����lj���:�v����6�d�Y��.�����������>�=)S%�O�����u��������<Mr�����H�����/����A�l3x�=��)����a��[���N��P|��A{��dmB������
�a��mG�X�rm��o1�;%N�t�I<�����t&g����N��nPH�����r&�l�nn�����&e�	��X�����b(L���7�Fc&�@�(�1G��`�t���X�+C(���'���� G���
h�a���A��}N��z���:����^��mg�2�3����o����x�d�����SS�z�B���[���3;-���7��y9������7���}	���b���Oz.��|��E�e�+�I��&�d���IO����yn�:f^~�h���O�]��V���x5G=|�5w�!:vR��d�X
1O�N�k�9��t&91�_Lf����^����'i�1��D�
"���pP�tFI���I������0�Xs��
��,Rt�cb��e�q
�/�b?\FW3�	��T�����.�W����'�s��B���������Q��=?����;~�;���f?�-	�!��n�+���\����&����3��e�
�:��&}H(�����y�b�������3�3�x��]���E��W�mg��x
*8�5����zVt&3`��q���������M5��vV����WjJ=MW�I��������K98U����E���n�~:�<�M��U�	w�7L�CvKz*�:��,#^"b��;�iV7��,3�3���1p���}6�S�%,fG�-�s0Z(V~��D����l�����/�����O�Nk�t&�go�o�����D>;��}�s8��$�q|7��wg1��iAk��e��M�Gft�k/������%�=c[7���L7��G�[���
-���,$��0O������d>!6��<i��#�T�UMoB�����aT��
�K���
�K�%_!$��y�P7d��}0���7j!\�4�l?��B=�����!I�0�-��F���+N����bjs�M��i<k���/��3���1J��jz�d+VQ� ��_c��u2H@��H��
O<������.h7LV	Xp��z^J[��\��85���Ay��,d�	���C��������N�|��=�
Z~tkx39�������c�����+��k[2����A�5d��X,��Z�
�����7c�I��#�0$|��X�k�{����`���<�d�'����>
��"m�m
�>Syw��A�,;���K��s�U^S��-���t�H.�{�
���-�*�5��e�6Z��;B�����
3�jOCN������^U���������b��/V����h���'N�����wa���jX<�m���!��@9�ks��E4�@�L�F�tvQ�U��e�B�|��������1Pp[R�JJA������o�j���J�T���xC��J�����@���NN��h�p	4�Z1+��1�C���H�>jz m]NP#�7�*k�������������$����~�zx��C�$��S�%\L�J8Z����rp��x���{�L{�����yW�W��3�
��"�$w��9�(�z~�E���/O����i�ID6�py
����������uy���
��v�L/p�����0;
��f�=	��Sr�!t"N��C��d��=$�.��kPy��"L�f���!�LUfF���C�������-#Ix+S�������h����9�"�l���`�����$�T�:�F"+��zg�����v����������>>�O/����U��4>�2<���C�Q�d�D�$)|e�m}���HY`�AJL�0���T����N�9���p���&'����+9;�El\�FDD����<.`c��+e�����������C����EV��"#����;��e���i��s��;&��d,LT��E�
P4�	(�os����-	�3�)�ye�S�������r3q.����������d���M�U;.�+����3f��{���
�Z%��?���CZ�E��n�2�����b��������hq#�eq�%�(bYn4@�~��p���&���"��E�'N��!y����S��	2����v	��f�����a"&��[R2XBgc�4A�E��8�JG	2��YXM	?5����Cd�09�Q�3p��&�<��GQOh���z=+J��~�N�<�.x�8R���G����P��+^� ���'��
@A�*��W�'�q�Tr�,9-���c��D5���=���T�FI�����������%d��26=$��P!�b��@(R{��X-~�=D�R�5^O�GG��4������TC	7�������+�u���R@u�`}�[�����Z��8Y\c��"���K���- k�;�8��y**A�����uI�c"$T�h���FS���:G�+	�z���,{�Z�DJ
�Fj%�>s�f�*�s������!wt�V���*p��B��.]���W,04!]{���?�����i7��F��2D�P�1j����j�xC���Gt��N�.�2"�3O��
-/$�%���8�]�D�6D(�!��17[��[n�_4��(�%p���r�;�Ty�:�/]����|�O�[G����H���x�J�|OV��F�c(J����j�r`�&����4��wC�7#��)�n�v�
8E%�&��:�v��.{����q������`����p�AY;U
jv���R/2LEz��WeZ����������j3����^'�2��6"�Y���9��2s�D�]Tj>_��0bh��Ro��%KP�t$y;�k���jrv��
�S���Py%E=+m�L*���^j�z�����{va�������?q�o|;���g�&�������,T/x��d���+�UBjg=��^����a~���|�*!��b�_�����e���|�0[�,����i���'��c_/��NKoj/�:���~�M���R��E�A�}�Lx��(�M��ZT�(�}c#{a��=��t�Yu��ET�[k���	'�����G�������?5q��A����|y���@����O:��-H�`z����j��j�56����Z��jKkO]�*�{i�S:-����������*�����GpUb���S��G����D��^�S!K>N��,��:�<�����Cm��!�uP����i���u��I�0��J/(�9Q;(�n�5�y.������]�Rb����MD�.��
�z T���$i�(���d%�/���d��������:}�J(/(�>(iib����mx�����PV����a�6�.	����������&O��3#��xiD:���?�`����k��,���^���@�����d���y+�N
m�h�kD�@)���	��!BYD-��lYZ�1\4��h��`���=� O������_eC�v������K�j��F��T!P���KV"$4�;�s�=��v��lX�5&���`R*��:b���n>�f��E9��������;�����^�����%j0�L��!�l��;9��\R��)��q���������@]���C��Z}RuZ��MrU|�����L�_���������G�U�LBIg|2���T�]�.s�$b��YY����U<�:]o��
�"��|oaJ���p���2|��XDW��f}�r&-��!3���$bN� ��	����Z��]e<�L���E��#Y���z�@��KA_�����FJ]������p��r}�EE]�!����1�9��#E��zy�,�U�@�V��S�y������8=.����r��zF�������.��S�RY`e���&�:We��hVhuf�:�����-`EU�������������*������(����p���i`AG�,q@�1<~�S�!;��ol����}��
�=���8��|Ma+��%i
�8�K5�� ��_���tV��TPm]2��4&����p
Sj�U{m���f&�f}���Z���0N����G�v�3��8�
�<!�p��`Q������+����=c��!��$N�A����5�-���s$�CSc��\���V&>8f�� �+�c���q�8�n/ ��Y�dqu�`�DPn�ntsc�%���V�h���/�I�������~����@[���b�c-�(iQ)���(���������t�"�����Ya���V���M�'��,-��rds������������
����������pD���L�����N�}!�1^�Wq�$#'��1~���B2��|���4��/�%pvY*����0��z�6�yx������op����%�����K����W�D����>��gX�,���]y���a�_��6�����D�Q2�M���	3r}1)���
�c����h�,�d�I�� ���&N���f������F�$�f�6[>n����{:h5ROE�c���jj��f�9�-��M8����q)-e�H��
{��
F
�t���r%�+�v�"�8���m����R^��T�EP)E-k��#�#������"�*�V;i���
�Z{��5=����m!�\�d5���Q
���W
c3��b�g�Q����c�ezxP����H��.�5�����B6yH6��;��#�p��E�^	��]������i(��nl5�4����f���c���0�o��Sm4���*?~$���}����w,��7Z-�"�u�Q���19^����/��&�ZR�M4E��c�%�a�n��P��+N|m��^�$�V������.�-���l\�[KO�O�Gw�T����&e3CHG$��v�R��8"��!tf��mr��'C�Q4��&�'p+�D����@�g�u��b_����q�����z�(0u1����+E��v?������l
KW�����7�e�����?/!%��A�g�F��{�(/��Y����nDzT��%�V7�s'`�w�l���B��c-R�7�g*n���\�}��4X���NK`���#B���` ��:�w��zf����I.�"Yx���B��H!y���
1�R��<�i�
�u�_����O�������Ss�
hT&�
v�
'��]�"E���W{�%A�G^��1�`j�$�
�'4D�5#������^����x��v��()����r�9��[��"4o#P��4T�dI�K\�B5�%_aj�"��T3>��)���p������}�fM�q����-��l�r0����m!~��IP��>�|����!t������hS
�:����0%l������R�V����pp"�����v7��7!Jol=K���' &n��
m'L8��R��0	�m���
y�����"8�;?�+i���br���P�'�A�MN�����|u�RV�)�a�9�C�u�n���I��&�O�z�%�T����S�
2��x�!�e���$h����W9!5�y���&�f�,�+U#������.66���@y'���eI`��Z���4j.b��V�x.8RwD�0�Y��p�h�:�����]-T����B�i���;�u��!�tf�8r�����h�����L��z�ex��C�F��5�w����s�Z�s~�NRO�]#W�kd���=�4�-�)��
�W�>�A3�
��)��d\���x�z%j�9nN�\_bW����V����EZ�	���!��6)Q�����i-_��M�z����F(�d���n�p�6�E��?'G!����<��-�}2fk�PX� 
�Mr*����[����$-�����"�8|^&
y��![^`��S�7�r��#��<�
���i<��P���P��sn��ea�a�r�L�^��
�f�����U�
i���6L+�����,�<��Ok���}(T��K�R�����B:�9>�_le.��K`�.gs� K�P���������M�S�Pbq������[��mO�X�������)�2^�	q��{L�X�$�H"0�Ib
�(�����`��9a��P���/yO����'K��L��91�"��� �	�����]����P�f��Q"�����9�������g�������������w�o&���n_�������u���y�u���p�~�_�{��
Ng�*���v��{���q��pt�Z%f��b�0~�'��
+���Z��IP�o����4Kf�pRs��>������WZ�����He�f3�~5�=I��@���u5�0�� ��?qOOntd���
!�=��#a���I����vY#?�*������K��������&);��4b����%�
6�8k�(�&��=����A��H��������=�B"@>G�7����-�Re�|�l5>�Z�M�`�������y�&�����S2����u"�Ph��C��k������������;�07��A#S%&
��H��5���I��5W�t$�l0��m���}s%���<�����kr�c&���;�]� ����+�b���C�� ��#��s!K��B*��:{.���}"��qm������2�)_��?����?TK����{����Y��e]�b
|��b�,����y�Sm"w�
�}� n3��cG/�--0��_fX� ���]1|���m����)�shph�U�O��z�&U��T�F�F��'�
�'{��e�/��!G@����z�-%5�������-fOeY0Ke�l�Ix��o�%����E�\>t�t�_�7Q	j��
5����6��t���?�]v�]���q�m���������#D�o������������y9��vD�h�yP�h�+�B�Ia�$:���	�i,�S���/g���&���%O�N��YHDr��F���h�HSk�����aH8c��c����TL��}��B(d4���$�J������7����l*����S�����	���Q�����avi�Z�q:������
��k�i6x0��x���sa�l����1��xrWO�����bj�;��U�m]�Oj����t}���T�Y4O�������0�9�`��vRI�_��g�]E�zd;����s�2�������~o`�����q�e
�t��
�o�H�Z���[Pg�^\�u����: L,�G�1����� w��x����k����e�(��l�{��*5Gas����=/��l�/b�8��������SDh�7������my�!��#��KMhZ��q�DR�Xk��HGl�W�h������Y���l��6Lf��<�'@S�����u�w8|��h\.bu	�kU��������dt��-[��r�V_��J�|��Wj/j��#�a�g�a
-�<en>�c�"�,T����[��+�j������-HV.h�0�q����������/:J��*Y�1��9k���HP��qC��>��UM��� ��T�f�����l�����S9s9dJ��8Og����D�C�4hC�����f�������w	3/���wi�an��_���E,�37
�=�5���cX&�)`A���,�8��4�?��J'���s>����lg�_�=G��T�`;A)|P�$������N�a
7s-5�������/5i��.���ukG���=�
�hR����yE(�����2S���;���:�i���;g�=M��>
���A2
�j?�a�F����x3�gR���b%��@��svl�Q��E��,��C��
1eR%S�\�"���tC����2���K&Z[
�d�('�v�m����v����`9�1�kW�T���F#A�=O������X����H&QCWQ\�+�0�����w��-����4��w���5�x��3,^�~q*�������N�����������(���C��E%7��
&��\8i,:{\��9��h�k9r���c���2�J���g����{,���B{��s����4Cj��}��m�	�]Vk�]E&����i��f�Z����!7�Sil$Xe��8�c��n!Bw���{��5�Xf�3�G�6Kq�S�������#v��R��:j�0��B�C����vS����r����������7� ��x�>^v�9��Y��Q�J7,O�E�y}S�Mv�&L�H�_�-����RL��Y_�&�M���m�d�����a{;�j�Z8[��b�r<�"�B�j����a��Z�f��a���(_V~ �����,���VWHKP/i��N�p	���.�����[���-����<EbI<�nkj��pm��Pby
@"r����I�D�a�)e��\M���tJ���Pc���2���pu3�A�>b��<��2�pm��"�1g�B���%|O�	a'�
`��z{	9��@[{1x+��s {��`�����_A�/@7��~�t\���Z�?U�73}����u�5J�T?��q}�F��m���I�����a��M�����l���f��W��t����r��A&to������7��%S�A�)��MB	T�1�B�cz#��)���	��:x�+�3���86�o@��� [i
��9��������=��ql\/�J����&�A5���-(�����[��+4�{Mt��p4gXqI�MFJ�rj�����������i��u�5x����bH����@�y��G:�k��KV*I�`������J� �vb�b�
`0�!}���'��lK�zPC�0F�aV>��fs@1��2V�<�L�19rP�P�����h���A���EL����&r@���h���=%Q���/�h����6�H�U�
N&w#�B�
kES�F�[%4�H�ipx
�`3����v)ZL�0l��o�L�}N��0r�d�H�
�cd>�O@���t(�A��0D]���� 5���db2-k_*���l/G/uZ�X��8�	&��^�k�p�]w;[|�8�X������v��eJ��x��V�b��e�D���l�A��.h�H�����)q������� ����}��T��������C��"G�?��Fa<��}G�V��DE�]�o(�)� ��6����CIdM���'�(3+k������s�t��"k.��]{;�A���)AH��2����	�����>��gb�K�C^��E��`���&������%hm��7eP&�	�~��%��_J�N��xC�>~�|�M���*�h�6f
Y[��}��1��#47����e�0�DC]��
3�JR8qE��3\{eV�pF4��d�H�q��R��3�}c�Df����nEQ�����.�KY��~��`%�!��'��_}��vt���e%E"�����1�{!#(���K�0
��HV��Q��p���P�P/6\E���.d���0~������$�����p������%>Q��l$����eYM��PB�[�&�~wF�8D�R�9�����a,��^�j�`��j(�g���Z��M'�fC!������A���A�Mdb���1z$�!c��&�=����@sV3�e���@��	���0����Q�9.�-�sO�u���l���%{��}V��p1C�$�4��M���,@n�q���t�b3��o�Ys;SdD��M�}�p65�`R��E"�e	�O�/��L�l,f^�0�}�>D#�v<
r���#2'c
�vT��-�CD�h�qn}�dSP+��"`z}`U*���	��"P!'��KF.���~Jg���$�����01��wB��c�
!��#`�X��0��q
�5&9C�n��s&#2��M}�M�c����H��p��x��'��A�O	
&���kX���-B3�/��:'P>������i��j�&k~phM����|9[������;��oyU��NSEn�emEES�EiC��0v�n�jGU������#�r5��8��������������l���W����C�
��9W�w��1�e��s,��n)�D����1!���RN��0�����':���?�k,��Hj�O�f<6���S�����d�1tL��(�c��p�R��ZB����j���`UoGD�� �u�P��}�d�iH��:n��x-������%Dl��ez�^�(�.
�E���B3���m<��1dx�i}�H�D3$���k���]v@3�����H��vrqhy��X����t�G'��z!�J����~
�3�`�fK;^�lc����j��[�N��Y������m��N��N��k�Ey	��R�����#N=���z���7e)31�Ti�q[Wob���82��N��n?��"���P�n/����n�h����!�C4�@ ����
���������<=���0�2�`Mo��"��TlnO|\q��M5�7�R-�/��#��5�����|���	v�4��s���'����oen�I��i�>Bw����P���f0�F�$X��������M��})K}������t����G��}������b�{~ T��e����||���
��v}���o������I>�o��y��`v�#�3�h:���f��Qt2\'�[p_�
�0�zd�~aL&)�������|��E������p�5���=��5P+\~}��S������(�����V�j�,Qz�/I���8�Z���(�������������#��j
8�S�c��Lw���Z�$'i'�VS�Iy����b��
���g��Q������4f��Ob[a�K��/�@� ��~�.���(+?Y(R��a%|&�v�=������Xk��Zx�����C�������|/!�=��M��t�'�E�<[D�����\w����8[3Y�>�!�A)%W;�M��I������m�$[��,D��7�Z������uM���n������M���Wf��p��'�i��C��+S�=��g.pC�������;a�<���d����_�;���;P8�q]�=���n�:�A��6!��t���-FX�}��)����7�qn�{0�*v��O���N0���b�"z�O��[���	!\�&R��qp���	����>E#�S����W�:}���h:���-)Z�P�:�F�����P�R�J�i�/Wx:Z�W�7���X���YN$�������
<��g�d�&k47���Ft<M�I�"j�k�����I8�&&I�3C	V���
���@��b��!��g��3������?����,����5�K^��[�'	���Y���\�f9.Zs^����G?Vk�G���e��?>=8|s�;}duGz�R�Qq8=��G�������E�;�P(��AM����=��@�<~�/�����Jp�G4D\G_���,�O�bi|�nCuk;�z�>c��������a?���7�}���7�����2��^7']S������E��G3<E^v/���?�'�T�h���*�������>;U�sx�;�������w
	&�x�m���zh�X�K�n���oJ6������5~�c
%3bR'Y�/�z�`�^�Uk��j��7Jp���&����N���1�`K���o������+��l����@qt�������6�������q	^�=�Dv"2�E{��Y�r�1�
e��w�������5����E<[��.nH�NTA�DGV�W4�8��7��%�Xwa�\�<��k��H�
Q�z���Q��{��Y�O��#��ZJY����e6��9���F�t+x��U<A�	��	������S��Y*�ok��B0;N��w��������P
���[�FN]�)��GV5�w�����"���J{���8�����X�$}�4H�F#�3����SZ��b���;Q$C���`S���Y�\�agf?����[�L?EJ��d�q���[7�Z�\[mR����C��r��N�dD��������-�K�B�P�L�hg���YKc�e����������[����XLg0�O���?4L[�f����J|wZ�jA�+Zjq\��2��lf�N�����Efm{�WG^����(�������+_�n��0�2mk.�sm�+��M����a�Bl�C�����SO1i�����%M��K&�N$�I�~o����9�A�����f���Q�iv���}x�&`�|�Q�/�[G�z|f=+g�dv���C2J�5���M��&e��Uxsx��NV�8���������M���35�po!
�[���&/�S�T��5�k�i=�6�%��j����w=!:���g���-s[��?���(�Z�e������X�}�4�����tuv���+�HW���
�%��u  ��K�T��]��W�����=�DK_^4����hi��g_���|����`�nY���$�?nlq�������'�|Q��?��������>E5�m[2BF�4��b�mn3E��s�,��T��%����I��g�x�$���z�^���Sg�g�}����A�����]U����4Bm����������#�|d��
u��'��`q0�&ng�-�h�h�vr�9�����
|��?3�������������^��:�Q����1��^G�o�.i����1�:'�Y�&9w:	�������-�����`��s�$$�����F,�4��I�W��^(
��^�rM������P	�����\+��@C^���k��R�=E�)m�i2��v������a��/���^���]��b�������[�?���0K����s��<��b{i��Ot������
�wfAh�����UafP�42��;3��ER��s��:��3>3+�k�&
��
&��m�)� �77yS�<��n\]V��������.���hU18�A�<�t�eU,|yjNS��,�X�����)�ph�
dy#%�T-%jY�(Q�L��w����
�9�$ �vJyO��8�/@��V����6r�4y����.�l>�q�"�^l����a��M�6$0}V�q�*e�!E����$|s��f���F��?g��wE6cNW����P_e�D��p��P���$����9�46��iy>t�
�"w%����������f�9�����L�LY��K�r'�(+��;�x�����l��U����A��� w��p����x
2�d���Hm��	�(X��St��7R�+�G8FK�$f`�����Dl���_��9���)�I�����\�������5b����8��Y�n9i��G���i�O����o
2��]�9��6Ud�N�{�J	�����{���������r��Y���q$��C��hZ]/a�x�[�Clmw�����W�}4��y���Ftq*?�e"�h�Xl�x�}�7�0"n���6�9��
6�G�\_�������),�����g�������
���0}*>N�zND����KXhpWw��c8CZD����O"���C���<IIF��-�M�Jr��$��F�
�:1BA�4D3����ym��f|�;����1
����&�s�S#<b�O@,(K���,�p���&m�u�#��j��VA8Bn��>4��=��	>��K�P�^s�%z0F�U]�w��&
�a��1�i�u�y��:&�'��hb7�W��2�j��!��n<��E��E��51P����
�N**����1�����������&|�:�Bl7�K&&Q��8�rz�xxZ���D=���s\����d��+d<5�e�N��p�"����#_Hi7U��h hv�_?���^�A
�M<����;	�5���tY_JA&I��?f�o9K�H�U�h�a$dn�a��6Ys��>�#�&�����1�:�i���L�
��B��0X&5|7�����]�VB��fS�{]�(����A�\�bI/�%��Ke��8���w�t���n��z�|�a\P���Y�v���>�C�FB�0�N�A7��S���0�������1����:��:\M�,H�,���
((9jr
(�!;�`���|��
v�	<������ ��M��#�z
}hq�P�B6�r����r���z�N�D\�E�4���1$��g�����.
a�%��1��b.��'>>���f�:���FQz��_���Q(4��S�\kb�2P�����D�S���P������s�x�/7���/{��^r�R�8�Q`���q&�ebR�.R�\~��O	� ��6�
���}T�~��U��8=cK��T��>��2���y<�BLEN@|�8GVq�?��X�������<�� B��0 {M��7/����%�;�����
�������})P��$�E�.�����M�P�li�:`�2bU4��S��'����R!�i��:Yd�`<���j>'�Ou��A&J��~�a��I�kjc���H9���H�.O���3A������w�\Ma���S{<��L��K���@��U,�0�W�)��w(��v���*�:O��!�=<k(�7V�~�y,�c�R���?F��������n���9sI|��>�����$[!��>�~"��/S_�bR���G���44B�n�q��b"��j�7�Z�vT��� 	�Yg|�i� ��I�@��3�'��A����#����Gs�i%����q��Os���uN�����\����g*g��)ZW�������9����z�o�M��(;#J�G�>�0^��.W,�n���mj�X��l�E�h��3w��){�Z�.���|�
�����jT���$���HM|s`���/7��k��8Or�c��� ^��%W4����d��I�3�N)�.��H�������#r-��~�X+f:�,8��x��M�[7n��+3��r�r�]��|�}!��y|��������N/�����np�+������xL��J��G��&>�Km�7����,&k��8�3T��Z�I
�'��������x"�~
e}|��{�����J��w� �	��by�4+�����^4E�~�B��������$J���1\8)/!�L�F3���	�M���W�<lj-��W%H����d���6�N
��S�)bl�T�:�������JHB/*����AD2I��c$,K�v�
�su\��H��:]�K�LX
��a�@��`��6%IX���+\��Y����^Hg1�f
S��T��l4Kg �#��0E�mL&�� ���)k����'/�:�4��&.�L��z���8`� ��TV�h�J�5�I��������=���N�YB���M��?A�X["|h�K�R
��v��4~q���;����s��C1��Zs����/B�t(��<�4�o6s����7���H����{�5�R����t�.xY�j�wG6p4[���^��Z�b����b�r�6~�B�����^q{�!�����/r9��h���3����3 ���P�d�
@t��1f
!��{�(�#��S�B��Vp^��2;��p�X0�6��
���q��U���3�w�a�}�1�a����<���I�>#9��^��x|��O������9�rR��M,�h����e
�]�����h���Q.��M���9��*k�����x8��j,������Y�q=
���vT_�x�P�?K)��P,v��'�3������~�����	���o��`�c�p�0�	p�
T���&��A���G�h9,��Tp�P�xl��
��Nv��r8;+���=u���50��g��f?[�w����
�I�=>�E����jf/0�9����^
��+��1a��&[__L59���tE�sH�eHg��@��6s!~J1~��7�s�v\���be�-��6t�e�}(�����f#� {(b�"(�;������x.-�8oO��Gp��������{_}�{�\<b����-��y�
,sye\�>I>����.�n�������d�����?�.�����������{��&@x)�p�X�`>`|
h����������:�`�kE���k��G�B@*x���+���M�:�0�	eJ��c�;
�I�I6�l_��`������#���������YU~M������H���6)�n��xzf�rC=���j���st��I��3���i�E������y��'m^E�4�y�/	��z^��c��q~�� Y1����i�t�/�B3c�����.�s2�x���kZl���q]���*|D4�:S�{��)�����~�<�����Fk����,��m���zMo����v�?=V��E�{�0H�\�6�Wo��yr����m�]��[�V���n�n�tt5M���r�W��;�Qn�_�;�>�
G���K�Z�*%l���@ �1�$�� ,�E��$�\�Y��Y���S�M��>�(q;��	�c�"�z%����
*�jE�ol��<�tw�����������)��M���p5�C��E�<i��=������Ib��b��p���`�b�%'	�v
��2��g�U�P�.A&/�;M�������j
���J�,�X�V"@���aU�E������Q��R_���
��9�2�;3h/Q`��-�/��nzo���y��$�*=��egZv>�;�T,:������_[���"������'���A	�
��"d��R��M�������B� �h%[K�/��q~����9�W�9{�0&
�$`G��Z~zQ?���#����4�%ef�d�n����w�\�����h����������������	���S:Q}�r�?��z�W'D�8<�D3���=�������U0�DUcaB�#����Y��"�8��t��13�M�2�������T!��,��)!0�R3�.T�	��+3��Y�������gO�Ij�����s�c`���A�<�����^��_YI�8��X����E�1�3�7����q�a	����g���p'�4[����Ix�B�L'��1���>[��������}�?tUo�m���g�r<ZbGg7���u�,��sktB�����������=���o9������VY6��b'�
+<#&��UZT�]�����Mw�P�*��u����*��z���d��8��M�9
���h"r�"N�
Q����n��z���"�tb8�'1�5RH\#�N�A�@n��!�=_��az�K��$#z8{�d�(R���pAo�=�l���Zutv*�:T/>�h�4��x^�Ig>������'�p@j�"s,�@��y2������?.��;�zs|��d�)�����]������}}�:�h�6[m��C�/pEvc�Y�������� ������dz�K,:b:�vY��E��p����1������Q��J$`/w89����m���a���\+(}�\K�e�U���4�J�t.!���� AVL������2��
J�!��Gi������&���Y{��j��� ���Skr��EB��H��������U��yE
b&x7q�����R�@�=*'��[�w`&Yi==���R��qz�2p�tRW��>};��Eaa��\u�9�
�C�@����iV�O1l~�.��
��GY��!EC���a��|��e�*]�fZ�;�e�Q+����@/z]�H��}46�OH�~[��hS7�jh��C�w�T����-yH4�`����y�?�J���.����!��{�uFx���
��$��?q�-~�����d�
�3'�^����}T
09�E��t"K�������qO�J���k?�)����;2���]�s���Bef���!������]����;c*�c�`*X�]s���\<���_:*;�V�z�p2����>w3�@�K,�/]V�q�,���i��r�z	30`���������`S�J����7l�)%�C�C:K��Fy�����`�S���nU��M�J�����y�{����V��2r����.ZWm���.�q-E�g�1m"M��`<�2s?�P��<��l��3��7�������=�C�I�\w��L(�5n���-i2v��"��yTWj�oD����$�(�2M������6�[2�����nqS�����,���w�H��b�@��p�Vs4T#'V�q*i_��p���dHS��~i���w1���:D���X�����h�5�0�r:��1&U�s��(z�:w��n�(�(���u���;�<��e��H�Y(]�e�����D= ��pV����K�|����g��
���F�/�<�8�j�3n���%%R/Nk6�k��e&
��G����s�97Jl�c	���Rs��f��K��|�L6;IQ�|^����e
�>�q�S?���;?���ZG[����"�)��s�����	�z�/��D��k�����N�<�b���2{pE+������j���li�y��(P����*�d&�����sT���|*��Nar��"���Z�����yZ�����(��
wn,�����@�����}�Y�\�0�<���f��2��Z��LcQ9E�Z�[)p`�~-��
)
gY��d�*���8{!�i\���Y���1�\�B���8f�����D� 9."���&�B�
����
�yu.���1����_�k5W�W4�����8�R�w����Pj��A+� s�!�>���>uF�%7����2;�78O���?M��,O��(�_���CxQS7b�@
�c>���QZ��!�O7��(�LM"*��:)�9ym������n=���"s�6)MU%"�nS!!ZK����\s�	�p��K����3r��#�����1?L�+��q�TcCr���@��OJ�F�e�e8y����Y���j�}��#4kyb�����.��C����(�S����p�W��wm�	47A��������'�gW�����>��O�0�����m�k
8�M����&8��H�	����6�UP����.�B���������� :����t6��O�0���y�������p$N0(lM�p�<���/������_���1�����4ZP�W��yL����n��D�Y4�6���T�(�88vj���S��I �o�?�����x3�8��1��m���j�;�������-L� �lPv��$Q��Q ��.�Z�EOI8U�,�'�o������1
�7o[�XH�$���0�u��Yd<�D�j`��������&�4J�����ds�U�<'�x�r	���b�8v��Na����m���FK\�	���x���&��6��^��k��L�Nl�@;�������%X$���o ��b��+hw
�����x�&�viQw&i�;)����q�d�\�a�'l�hF;�CCb�J���zk)������I����,G0�����MB��;���
 "�@A�z?,���p2����j"����/�}~F�\2����=��G��`nQ���x�����o����p�p�@"C������n����)m�:���Tx��C'r+Q9������G�D�M��d�(~z�'�����v�Tu�$$��s���E�U��7\r]�I��.#a����
:�W[r�{{�����j�$�4����E�r�.�Yv6�Jr�l]_6/o���5X�a8��\~Z���K��w�S�P��e��m^\�7�hK8y\�#������M��QS�������k(oYs��?�#��No�y�'����5�.[#i����g�����������jw�^eT���
�y���\��QDI������e��N��n�n|n���l4���� �?���������eLtQg����A���qa�p�u�+��z����5���'g��YZ�A��S*�?���582��UOP���qpL?������r�$�
9o0���#+�[�>	�C�S
�[`ys�s@"!�����'����:�.������<�_�2�����7�g�[X&x�k���K�r�c��<�;�DAeT�r���!a���l��WTL��HZI��B�c�(L��:-����m�WWWh�U�Ls1��D�c.@;�'E�M���FiK:7�@���3���;�����Q]P���Y��?���^d@a^|�m�gA*�}r� �������i�c������-�F�
Q��N3�8���)�#�t@�t��@�����x�i�Y�Q��|��3��
�9��v�~?$����^/�7�*Vsp�-���-��������y;��1`���G.��>��;�W���C!�����@MI%b�Hw����=���F��m���{.\�
�*L�l����>e�2��14���PYA����Ej8F*M��I�W��+�u�W>���DS�J��"�%�T��8N[W��uCv��E�����$]�����j�3�G�1�^��E��6B�W�>F�8��&��3A~hfZh������:B�a�����W��0��e��V&�D=0���b��b*V��dX�@�z�t�?*z�9?���?������;X����a#jJf<b�s[��;f�;b_�7�0���w-R����>�����3��
�(|K%��*�|��1�R��t&�Y,d\R|y����.����C�~us�^D���8Qj�#�g��i��C������z�
�>�4����bP@	���/���"3%/1k+�����=�k6�uI�^�
b�\
�$��-U�q�����"��v����o��v�����Q�FD��s�(��o<�b������D/L~�!���8�l<2� �v�A7(���e���U08�����d5�-x��s��Y�	7�^����.H�b����hX(=��~�������});;��7&�������h�=
�����*\1�������
���
��~�6I�v�������
R���)H9+1��D�<I2����H��	�)!�>�R�K������l�t�hBH|D��d_'^��@=?1�;���ga*�ax�s�J�3W"��H�.|���=��Un��Z������/bY�#q�:4���==�^J�K$�"�� |Ve$y�!V��*�����k-c�'[?�I�a���O���9�0��hD~.�W���7[I�AI]��[��b�������d���<�������c����y��{��8��*n89�;��5�Ch�r��z�W]����BR~��
���{�����e����Aq{�Z]�aR1�#����Qv��gG�������*�h���2�������f�����'���;�LY�����q�2I�
\������J��i�H�Go�W%{U���������E�$�}�`#����2���:�u�����I�iY��Y(g���������)�s�5�`#�?�P���p�G��<n�w;+�4�FUF�,s��/!�H3M�%�0����S�7G��#w6�"��
��
PI���0&�<\
U�K�3z0�4�ci�	"���Q����	��kj�w�f��-����.�,���\O&F(��-�����YJ#�K}�
j�'_�Y�%�b��I��/h%4;�	]<j��LN�\�U1Z���3(_/d���
���jk��&n/m�	����|�$|f�q�}s�����L�^�`}g�_�����;�.^�,������g�o���7�������r^�
�Z��Xdq(�E#�W����yyZP�(w��H|G
jRm��U�|<�=y_P�l.�"��J����������b�db@��A9)�f�*�l�R���]�P��0���X�-ng�-���&�1(��f�&��d��`+hp�}#1�\��,�${�3c�v^)q�$r�L�����"M�a�]V��V^jM��w(���n��+�e�@����p�faP�@*n�6���g�;#4������?&�� ���}��
�~;���.���l���eh(,�q�v[�Y[Elc���":~��q�������Rt?/�,���>����Agq�U�������;���^?�l�[ �������������L�����K����llm���u�w?��_
�O<�f�5�xC��C��~�V�
�<K�Mg�6��Xb=WBRpN�SY�x���A�D��Q8�"8���!q�xc�����E�����`�lO��t�����>������wDt�8� h
����F������I�����%���=9j�����m�X_�N��TJ�����j��q����2�QVL�2
w������a���D�;�N����Sei�y�,-�+���������M\�w������y�<�
*�������#aI�0�C6��v�[�:��M���#��W������yy#���
��*�+�����
�0���
�2�O:���z����#/�&66��&�'�p�2,��0b>�@�C�"��&u��x����C�-���J�W\D���������
	�S��N2DLx���>��e�e�jv7����:Q#�����it����s�����),G;}�6�n/�2�|	 C8&�R���|�-��
�E(#�9�������h3���@=n(����f:D��E.����,Gp:�������8�?�'��l��^G[�d��;eU�EO���8���(���1�X�=k�&hsw%G �$�C���<3�|��e!���_�=K���`-i6�P�����������3�3(��@l��D
I������;j�����J���,���c+%���9E���[e#��L)y2\(&!B#�P?(��}:Pv��@��=4��qw�eO��r�Hq�������^�+��S��{�#u���Ac�^� �|JQ?����R���H��V��H�Wf��1�0�X��8Mg�K��<^�7��?NNA8(��A1?�Z��@��O��(��<��y� �|�pIm����w��o&/�w(;���r�vv�[�6������u�����A��[TW��-*Eg��>] �����'y�!���8�|��M��>�
p;������m�����m��������f���y���u�J��?S�����\��s��$�FkGT*��p*&������_����o�D7������(y��*H�T-�^T���i�����dq�7���rU�<�������]����0t��-����*$��*�7�X_~�
hx�M����j��]4��.���g7�W�?����?A���6F��Q�>F���L�C��\�|g�D�|>����	�n���;�a���������ac��?�V�g�E���>@%����G)|����@AG��|Ws�L��}!8'�iP�
��Y���[Bd4�W.Y�=��������79Z���n�1�K�f�Q��;�O��iH>D�>�4+p^���3���
(x*v5��u����h2���sd�����(�Q �K���
	�������p��������}�@����)j���`�����x�����
@�i2��_�A����W�<1=���j����	��]���?�N�q8M�}��)'3m���,��S��k�p3|��Y��:�^tz,�����^7U{�y[z�C��/���M{zg���lO����Uf���g�e�P�]^E_:|2����<����Y!�gO���m�W6�
�Ux���%f��|C������C����Q�����c�����e-�$�+����p]z��P��{�d��a���������z���v����r�I���J�8_�v�>���w��N�L<��f]j��J�����N������9�[�p2��M���x}�j^�_����
(��]���:;�����M����)	_�D��&�YEhR��V��&��-C�5�U���@/k������&������'����y�������?�w����9���������{{��[�����r)�/Oe%�,���?�+������<[Q�$������FR����������|/�E�!�6j-�:ct��P���V����/8������u��y�u�M�J�� P��,�,�ElZo��fZ9�L�GJj��0�t����,�|��13-���&�����(�#����!�&��j������+;�(L/B7��7�����0-�/�
"�p��>��6��,�J���@�~���p��fgj�5�p�ikZ�`�s��E�?������i����q�g���u@p`KtO�����it��T����c��!~������)C��8�#V�JPe�<T����Bd`;D��I8��*YI����48{yE�.DB������  ���C�`
�'�1���8�tj���|�P70	��I|����%��5�l!�ZH)_��y����4.Nf�"i�>�i,*@R43'�we�$W?MX5���Or�
l������6���-��S���-������`f�Il����	m��a���l����c��V5���j�	k[Y	�#�}D�L#upu�m��.����1:>�0������q�����A��S[Z���>�������
��G���5	���=F������C<�[�/��%� R^?4�]L�h8���M�T@X�]-���
�gpW��4s�H���BDP��Ns�D�Lo2�t���[7S� pGI	���-*��n�d
s��A�Y��\���)Z+7wC�8b����d���22j9���q���
�B�2�F� G��*���L������f�����yy�$4�������O�SQq���c������Z�
��S�{����E��c(�CN��f�g�0�g�D��v��`�v�3"m37����/%YaD��&����������%�4��;���q���4�z(�m�K�D���1g\����oA��������k*d\��%Lj^���&p�.��M4��U��Z�YM
_SI5��Uz;������	n�yq^~��	BW�V�3
��YltD�`�	D��@�
�f9IN%B�BS/��%�Z�eSm�l�8mkrV�AtA�n8EO\��'��q��)i���C��������D2��'x�y"���{�M��lm�aa���n�(�gC5x�s��ZK��LJ<V�)F,�hi\R(�'����i:�|W@?����*����9��Ca}�{w��L��#��s��)�7���@��������������0,��wnD�h�'S:���~�y>�����0��@:m(�f���e�}����?X��VM��R�Y�[�1r��vvyv�~����f���]���������6Mf�g4w^���J2�Q�d�s�f/"�<3��S�z���L�����R?���Zys�%/B�B�v�T�"��u5�-��3�V�B8��2���,N�iZ���@���m{5:���E��SrV���2�u�V������-���X�e��N��}yj Q�n38F�}`W��w�<�pk�S.���s���A��hD
��)����Q������aWRT��x��������P#TuH]���/�W)�����%�&��}]���4�������� ��|����S{�=�k�c�S�L������PoI������B��=_L�#%��_�JrZ!]Gb^�0�n�,����=��vB��W�lSx���'v�e�*I�F2B���������u�(�
P-�@�i���E�u������z}�`w�����}��~��q���)K����������6�������F�E����a����
�4��	h����SO���	�Ic��3,��C	K��;�%D�����9�CP
DP�h�K��I!@��@�Cu�������p�&��������GF���y�@��	=J�7K>%W�o�}��M�����R-����1����\%� �
����_V�X���2��U6�9o�p=5�K5��HF��bNS���F�_��u��1l�{�J4����?)d�m�=�8�:h�,^��b�4
'�{oj�X�������7��������C�-"�������#sb�g �4#�n����-g��s3'�����4��P������3-o�=��g���[��9eP~�Wh�����u�gbM���9�����2�����q��\+� �j��G���h��������{�~o�6��~��s��}x��w�����#����r{k5L�c�1I�-�C���|�:'��-�LvqJIu0����� k�C������2��o��6�J��R����-NW�^a@������!��m����f4�I����3=c����)���X��������i��\� 
���%mH-�h�P������I[]���9u����y����=w��q���d_��{������}�W��������nw�}�TU���B$�6Hm�E��:S��Kbmf
�(	�O��D�����,�r����0�F�;���4�(�����$����	��$�G��h��1G
�$���Q%aX��Q��
�S�S��d��
b> EE��?@+��EOrE���x��*��������o>��m_�,Y��!�����3Am3E�>}��O��[V�'�G<�_��}���>�@��_���{�pO@N{9x eD�����e����8�:��n���{���9�Y�����7�_�"���\�l��C����S�`��Th�������G�������q����y�>��n^��`���,$�I���?�����Wg�nM$��=�G�Uv����-|�J���z����+�\|������	T�~+�5>#��?'�E��|��#�/���2-I�+��O!Ml1�O�L��)�GPgg�`����]gg+lt���%���J��W[�#%k�(Y�/�Oo'p�U��W���jp5�)�"z�_�c�vkU=
[��#\���#��W��&w�:W`�
f@������]�<x�{�X���[<'^���9���w7{�n���oE��,����k+_	��|���=�p����W�r7nS�%.X�����zX�[�-U��h��;:cQ��z�7P��WO�n\�K}�Y�N�:��5����dT~Zr��y�N	E������Aw�^?�����`�-�J�"GI���Bt5�e7G���J�f��U�Kl������l����4(|G�wz��$rl������Tw^�f�?��_(�
!uR��H����5�v��8�����>s��X������5�9��e�4b�|�|g0����}����=��q��3����"����;H��}O��prK�����"��)"sN~��2e�1�|��4�,9���S����"����U��{z�-@��l!|���a�Y/�S���z��%�C�_5�p�"({�n��L���D����	/=BE�(h�;�x�LLH��ih+)���%� '�'w�)P��
iI����V��������"[������Yp�g���zd�\o���u���I8��p6D��O���������2�??�8�^I�X��o����&���
8����#!�`#G��O��C�F�k�0�l��������]�7�v��cK�"�#	�0$K���z}+ww�z��.�G/{�����f�������Y�b������*�#���������^��LZ�4�`R�>��v��70�LqG�,(P_\��f���;���S3��X��C4Zv��E�N��s��Y��������`(#�p-B3�!`��8��|D�����Z9x4�5�'��K|�kdqbO�^���Y������V	������$��	��+�j�8<����l���a�Fp�Z�dL�y7I$�FyM;���;Tp�?��������(p����RXe�����,'�I����/���xJ��"%R�q������S&�{%/�����2�0�/2�������M�����Ym;���R�4��
�Pxk�@j����/_8A�2���.�5��*f��P�]�JM+�*a��	%�r����
����}�������ryz��4Z�
\��%�L�h��l��NO�e����v����}$P�!�4�n��d�~o����?��Y.�n�gw�v�8,�7=~�&�#��m�NET1��9u9�����1Z�'#vdG���A5}�"F��:�����;���~/{7���7�C�I:��.��q��u.-�xK�3��M�
��NUi�B�IF�o��)�y�D��qj��44����e�Nf��x:cz����/�32���g�b'_�n!|��8�`�@h��B�n�m�����b+�,	������I�n��?��|�C����������DS���Vn��7O��V,rp*/�x�������y�>m�=�p~KA�7�7�3#i��c�1;��h$���P��7�F���L�aP^e�Y/������
�h�2~#u�(���a�Z�-����)��I��r������7x�����0��E'�U��_�V�9�$:�������@�5s�|�td�xi��t6F@��T�����$�3�;����e�Y����MS�kS2��=�Ksa��:#����9o���u���}�����x���&z*9m������%��>���Q,�%�Ow����H�"4+r��$�>��Q�	�#��=�<N��)S���A</��.�o�Z�\~� C���9������ y��n�SU	���[1��T*�x�~5^2����rM�`�eE"���	�/k��Y+�%2I���^i �������<�����Yb�����\ /�o������06��m8jL���yU�:��;=�=n�\�Z�o��9��e;Go):�G���Y<L�����B<�&���atgv�n������EY�����o?����G8^���u��o����=t��GDz��Q��A5������|M�J4���mh`��>s�������_(�����`R&p��/��l���Q��Qqj�E������q��R���J�:MCN�U*�N�������tT�%�"�u�um!{C5��$�R��8eM��4I�������hz��)�c�6"e�����x�P�����B���"jh��]��5P��������xTq�(8)�`�k9���b���?�5 �����T��)+3/��j���3�va�!�X��Y_����'���Vz!@����"D� ��BP�a���f�?"����RI��p�aFg8"�"�QB�.�/�����m��}�����[���-}+0���p�������UIE"���f�+�5g�!-hW�HL8��!W0l��������������]����q�����L�O��*��#����8����7JP�h^^����S�
r0�sj�����sI~���S��(E��h��6�LA���&i��|*C+����6������+�p���y^
��*���p�F�h�8#��$�}��s�S��U��g.���U���:>��fN�^Nm
)�'�����������6-����f:# �6s]��x���>����s8���9�zI	�v�\	%SV�e�DR
����x����,K�8N1����Ym'�����"Q\^L�A�ML�����h�H�|okva�E�Ak@(&����T�F�*
%T6B�2���A�������XU�J������;�o����b~_r���U�1�|u�d���<����h�!�
,"H��NG����)������2
�$�B��z�:���8On������x=��;e�����X*�U�D)���#C����_��K����D�I����z����R@*���r�	}�a&1n/���WM�V�M����c�S�Z	�4��<�|�7!�����s���U�GH�`�J�@��,y���[FX��D������V���n4t#9�J�.J��i)����F�]"����|id��o��6&Uh���	3 ������g�#;l��{QZ���g V�(?s��HA�������:�YW]�J�A���8��Tg�doy���jz�y�a[%��Y������_$�g�t�����g�L_��X��j��������f�u,��98I
�2���;r6��>�(u�6���	"*P{G���H�W �%DBz$yJ�B���r����V���9����B1�#��Wr���WJB�aG�����o!�O4�&���T��*�N�^LSn��ER��rn�����%�����>X@"o�Z����RlF�e��$���m<*�6Nz(���%��"�
�g�N���x������wC�4L����^���d��D1����z�����\j�ex1��y����KR���@�R� &�6����m�*��e9��#$>�l�VQ:��r!�	s
	'r`����0#5,��<�f�\��Cj|�Ry���-!.����������~�^���D����f�H��9�N)��9�H���H��)��Y�=c}\���@ 
��\����F#��|5Ni��q@�EFB�<��5[��n9��S������h5"�I�~�P��E,�f��/�8q'�T�O)������2ix�����n�n�����Fc�����/������]\���"��J�~�M4��!������&o�E���q�^�&L�3�������Iq�6t�H�I������|7a�_	�cE�����Y��e_nuw�Qt�?����������V�='V=WQ	8E8������AA�.��M%��4��0������.��X�$97��4[�����]���hn��~����D1�p�)�{�~��0�U�#�+n�1c�{{N?j
7��8J���x��'�4�f�����)U	���Q
1`��/0�	YI�rX���Zv� 7]�;i�i�n���Sq�����KNy���$=�����Q��+k7�4��*iU����.�:�5��(7����I����Q^�
~��?~��m���J>�v��Jv$��\	����f��*�_k�G�a��� � �;a����|�M����f�A�K&��S�H���?H�%�(���^�G�9{�a�q���������4w��z}g�������lq��:��[���>6��^����#�7�����9��0��D��g�<8�@dhQ����C ����B@�M�$�~i��\~{����w�)L���m�(O��9|�d���R�����sJ)x�A�����up9�v;�F����[P����J{����=���)�n��Q��9����n �k��C8;�p���M���L�����#�����,Pd3����M�D��(`���S�����7�V����X��h1��J�$���I=}&0{JE��e��C�>����.d����z�Q*�����nP6H���e������"l-�!3q|�����Qf��0�>�41�%�j�k�@$SA.�#&�(\)�.�����(`�%+������6���&��2H�n���(u�����<%�D� ZTx�j�����Ri ������Cqg��E<Z�7��(g�� �����!�On�o���a����&3�2� )yl>#�%�Z���w����6�e�O�Y~�BC���d~H*�[����.��r��h#�2g�����A�,� �[�q��AO�w�d���j�gZ6<�������������?�]noiF
�eUn�g7�g-��:|5$<��~����/��" '>��!i�����Bq���w����z��0���
�;�bBIm9!���P��%Z< �X�IB1Y|��@e����V
�	)������3��Qw3
]-,Gpa�^����a�����G��@]�q.e_+"_�E�{�����jWX��x0hb8\��B�@h���6�j<c�	'w]����?��oU�#��I�=V��3w��(��T��s�`l�<����c��grb��������������9��6��0n*���#A?�	�������U|/w�$ enH�����A+d���p��#��WSLps�f�����F�&�����tApJ�N&���O"��bS��Q;�q,dC��$����YY������T�d���6����%��(~#��.0sq!��y�8*k���(���N5c���'2'�-k�Kcsk�o����V5�Rt!�8lu�F��q8���m�,����=t��9�\&�����<��{�m���� ���k�D��E�������Z�1�����v��[;�$-����*H6Rq�q��O�ldH|��v�pv�.y�6O����rF��cn�&�����}�#Y�\��O'8�qw�+YR�P0����EP����?n6��������~��=k���_E�	t*8^K��R��f��S�\b�e[��$����O�����k�����7�]HH�EAk�$�W����HVT����xsj�r�d=���'���-�d?��/�A��^s�^�E����0��~yfl��,���V�^x
���q��@E��C�5Q3"���c���R�@%D��P�������)�|
4��^���W]	z��BPAJ��tra��|z�x��C��)��������*��E��e�l|7	{Q���J�����a����2�{������-BZ,�''��J���1�,|=
�2�Jb�D����_��p?�/Q��\\�	����UM��FAr�W��:�
��A�s��1�M��
����K4��j�j��B��V�(����E����|��8'��o����?�
6NO�o>�}{������[E���u-�8��� u����3��bwB�Z
T~~J���?7[������8/Z�~�&KS�1��#�]��1��"z������L�����~c����w7��v��R�VSJ�Z ���]!��A��Qd�v4�&V9��%/��z�Z��.�u��]��C�3�0�JU�6�9?�%p%R,�{��^m�_q�F�"��6���`����.�Q�%:FG�}�hnT��z�kU&>��?�U�N3��� v9_�����-��A��b|�0NI�zb]������e�%��D��u`?�����Kq�>���zc�^�n��o��W�H���+E+RR_����`�Q�m������{N��K����/�F����*�:�r{-�=�0��lR2�OJ���wS�2���=��'w�:���������a+xj6St��1�3��bt��t��C1���
�p��'U����K(��0_���K���1���KQ+� kP��I]&�k��2�4�����;��R��<	��R��/6P�g/������fr���8�V��F�hE����f>�#��r���p�����"���54�cW�a�q��@9K+kE'�yv�,�:�P2�F�E����iL�������4��'� ���go[W$���Ey
	��)��}���e@�1��h2�n�	����l<���b�M��T��7ao�R#�8�Z*kx�&3T��2u(����5��U7��"9����L���D���p$O��(N)��H�1�%�*�����.��8m�B��A����H7�[�{;���y���������[,�x���8^1R{��S�n���2��Q��i�Y���)~1KF�<����T�`qQi�d�"�NT��������]�`�5���O��w�d=>���1����v�j[��o�VV���kwq��(�N�P)Y�eh���c��R��Ie�������9����s�<
Z��+�u�i�Z��'s�7Y��#��A"�h+ck����?=�@5�-c����("1
��U���c�'�jGaqb4%DC����>?�l������:eZ�����N�$���J���>vr��__��+���>�	�|ka�$�|�io�r=+[�,5W*�AW������V5�-��])����l�$�be,j��t<�����ww����w�����|������VuT�8��M��+�z��17nZ���t���@�S���I�
u�)`f���
-2I��_���K�'�;N�I~P�`�YA���C��)��������6\y������Vg�`�S�	_XK�� ���L�7�hlZY�X�������g%\�����	5��v%�O�g���&���Ug��=�1�3��!���g(!#��~e%��=`��9.��:
���tK������[����Q��6z����va��9u�.�W��V�t����R'9��T��!pf�E�n����!�iO�"x5�mV%!�k�q\6?��>d@j[�o�feJ�4����^&}����������~�w����;�j<e�����{����c�C�Gi6��#6Y�?HBB����g�������#�l��>$e����^@�/	4{��jO�����$I��� �����IE�u<������D�^��K�c��a��3V����'���'e8!Z���I�a�A��,�D�<=c&"�$�7���f	V����T���n�;������nwoN�����[�[Nb�)�J�X�k(��;�:���J�����`����c�(�@��E�w��� '����@����rdw���Q��l�
\L�GqE�.w�0�J(h�����[o��v�X%�<?M�Q���+$�7�B��)��9�Fg���kK�K�w�mD_`�v1gGJ�Tx�(Mb)�Xlz�|]j�=7�c�����K9	<a��#*Ku��"����&,}�P�CI��@���a���b��_���AR*��#�~����}�_K(�L��[�~�#3��`8���\p�'�l,��Q���9a���T�e�M���HZ(�������Z���jd�N%:~�4[�3��1��}F��T�F-�t��d6�xr�(u"2��"�k����e��f���������o�b���T��AG(.Dq����x@�M������a�S��U����#������\']5��j��f�����5�o�o.c�61���~O�m��h�"KNCO���e��9c$V
���=��|��HQT�0��'�����a�\@9�W�e�\��e������*[�r�����H���S�Adz���G.��K���f��:��c�O)���	
��|]�&�����e��������V����V�z8V�v��b� ���J'����\�9p>7B�!	'�0�(&'EU3cN�p"���-|5����o�EK�-��/���9�
�Ae�5N�j8�J�6�9b����
Q���L��
�-�_���7�����?5a0�oM� ��5���D�z4��������$�o���
��"�tY����������&��'�Q�b
 �+������3 ���9��+a��9D^;CV1%X�0Q.3L1
�3tL��M���B+A�&0�G8;�[��X����_���BcSG�������� Hk'����M������OUM�rd������@���8"`
�����=���a��l�d�����,����i�s6�����9�����$vQBn��Z�mbH�E`FP2��8=��t�l]_6/o��?\=�+�`����6Z������������G�o9'����,�����{ke���O1�7��\����@���Y��g�K�p6ar,AUwl�W`��3�w����?;���$�� "�C��!e�>�wi�x���>b�3���Q�a�L�2&m��xO��������|��j�={:���B��Sy}��I�H�-�+	f
��A���b�	~�	U�������6\�c��rOYF�C+y ����������H��pW-P��������t"C�Q����q�����|=0�r�l
��q�x@�fn�1�O���a�����;*��&������N�6�c�5�%�1%��Y�q�	����UB�^GF�������������������5�&�>��Q4���H(_���7���I�$J��W��)�=��"��#0���j�\�������
��bp��D�[~#������_\9v��izSZ9��Wy�L���������~�����e$�A��3$Xc����>&T����������_�����e5F��(S6Ro1�Qc�`%a/c��kE0���7�	^�#���~�V�z�XH��rE���J�#�,M�k�����R��w�AY���d��u_�����R)T�PtJ���
��Jq����<�E���T�j��`
G������oWE��e�4���5B���<~(�*Wvju,	��7��r���,���H�Xk�P�#Te#��6��#U�^U�/`9�a�_��<������_9�)������/�A��N����`$�E���&P������H��|��MFL��`D��C������S��l��eXs�0����oSP�lY����v'�R���W�l K
�W.}��,�\����4���@r��
�rD��Xm@�����%��t|��"���]�)���J����(i~�P���nCqE J6��Gw��~r1V�im?'&���T�7�{t���l�����1���N��t���f��6�d-h
�"�Z�^��X�I�
7�A�;pvOZ'�['d
���\]\;���9��X������+�8k���/����e��<b?�N�m��G;.78�N�}���Z�w�^�UI��s�VfW��,XJ�&��)D�/��E�a� Ql����V<;���HW�=m�[@����T\k�f�An%c�
<�������@J�JW1�F�@�;E<��x��X!ZkjF��jF���1
�CN�H�OU-�X�)���"Q�$w�`�����F���SzO��:�IG=rw�Mh�j��\�?m#h .Q�
��^��L�l�����E�:�w���Y��rm{�9���<�i��2�'��Dt�R�����K�����m3Uw��V�"5���2;v�i������pw^h��Z��F�D7��3�l��J6JE�Eh��#�0>��dx�q�����������A7��4�����Acw��;l��z.��s++(��|���Y�<�L�y��$�����;�����]^�|��6)G{],������/�a�tj�KK�k�Nw����v��}t��������� ������!��}��������v��]���h���s���}vZ���Af9��=M��c���w��W�[������;��Te+bVq\�E�F�N6�K����y��]�d0X�������e�uv����F�$�B[w��4�q+�Wq�v����������_������ �@T��h��'�0��v����`���V����ojR�-hK77�q+���&��.�4�Z�N���ln���ps3�k����VTJ����r�o���*W(:�?"�����������|s�A�-2@�>��I�s�E���1����E���0H�G���6�Em������JE����=01��y�����-,N��a8
�I�#���4���Ij����!��0Q�����I7���k�)���jp��X
Vz�PSu��#�,M:��v�>8��P������4l����?�V��=FI/�kD������{�Ag�+���C3�).r��\�N���7���BkB�G3a{����	�Y�i����!�n]C��=Gv�c�W����{n��c�?,�1��K�����^����=<�����.�4/m�q6�9l������O]���4Ot�/H�6Q;'�y3�j(3�Yq?�>v�X��3�O���1����	����.��3%1�>d�#'��q�H,���m�����}��������|����S�D.���R�a�=l��ONB��d2�C���4 �����D)�1>�(|���x��
�E��&���H�*�Q�,e�5H9���$�,_���3�#���h���.o���E_���C7����P@;�S,$�x=�5
2�4R�8���E�M �|��z��1��Qan@���ht7�O�u5��.(&�d����qe��F����f��>\~�i����V����Eo���������\qU1���#�����pU	b}�sq�J[C����}�8�@���������_U�p�7	�/'a�O��!������ ��f�N*�%��LI����|�<7x�o����Y����p��
�l��%���6`����u���~�Mi.8�J�Z'��`Z���$��SV(b��QHI+�>��[�A�E��m�p���!:5��)Zo<�����a�<�2n�����Cl'��aK9d�M�+���7�L�������Z�|��L�S���L[T�@'rq�� `����m�2W�5��0�
��6�V���$n��4���9�L�O�iP�����}���'��X-��l��@v���;X�?��L1������c���l�]��Dt�Q�GwB��E�A���a?�6�I�>�������LXC���=lKm�z���M�.���ON��_���'F�����N�_JX��|Uf����3^����\��?���:�Y��?�|'�enk�Y��������^r�,��"����^jg0�/�N�����-�F����	
���_��G/�H����|e'�1���.-�p5�IR��c��5?������<�!��|[��4��z{�~+M\t��A(�`����n�[[��/�d����XF�!k��K*��(^�p�I�Ra���v���f������Vm��NLF���<p���^}(��=��z��[;NS��K<�
NN�i���"��;�U*
D��P%�����1��gg������h��>h< ���^�
J6W��I�'��_����1J�u�\9s�7+N"N��+�U2�S��Qo#�o�VU��p�����0�8�8�K�����(�g���)�j��d�wP�d�
&��'�	�;��"�#��F��~�GI�H��L�p����D S���P����bpmth��b��1�a���hGo:��5%"�����a�>Z�����5'��
;r ����u�������*�h�uZ}��I���������O%m?���3;m��8���vR���k�eE�d����q��n�T�K�9�P��h��:��2��F�c�s�BG�8Q����P�ti*j�mUna�M��7���{�����
��%�r�[��`��������n�)w�y��T�y��?\�C:�pNWr�Q,�[�=�8%���&�m^_4��������^�6�q��	�%�����cu�E�5s�.�UQ�9���
������^�^9>\-�� Y!�2'zv�^�.I����a	sp��w>J1���:N�DY��y�W�g�(��r��B�w3gI/E
�K(���]_��h�,����"���Z�asd���@�1�����r�i����w�������=��?e#���`��T��l��'�[�7� :�	�V��F����r�n8����u��8���F)���n%�
�w��~�#��(1�zw�
<�,yN������ge�T#M9���9������0�h���RR-���d]Q��y�3����=�t����E9;�����#C���9����z���s�������xLh����J{hV):t��^f��~E�GUg=�������Y��d<pl�A��K��K�1����|[�`�-#Jax��������*y��U���K=z�K��nm��v]=rk�u���3����	�E�yq�~f2@hh�U*_���{�����$�m��!�:����(;Q=��!�H����1�Ho������`6���##�X9+�$��;!����T�F�iF�����SR�>2��R��'���������`���e�����4��4���.Ft���TG���T����3w~sY����H6��+�%��)���H�U�i�pr1m����[
��tz8Wz���s6���T{��)��8w���tJ(�mr�>�*����~��.�.��B|����/_��-y�C�>�=�������KtwD?�A�@�7�A�����'b\��N� �G�/�=���*�k���6�4@l�z���L�A2}�1�E�O9��7P���8SVH�j@P���C�Z
�� ]��u��)O����fW�Pn�!��|AgV�a�.�"]A�q2K�T[� p(Zx�9��"�{���[�Z�'\~'����*IIDGq���RO�[s��#sQo�kb�<��4��s��']����{�����	�F��xwx,�j+VJ������L����Zq���`#����<������������
;�2?J������9���1Fa�d����3�Q��)��T�r;��d��L�xP�I��q�������)a$
�k���3���5�Q;G�i����G�R����������lx-ni/���v![���^�� �x)yC�vL���C�w6R�����"�U���B�`���^g2�\�'���M�������#��G;���"��NU�i� � �M�=M0�*����
6A�������?�#�l&�a�?�;��}��=n��?g���7h�����m��������;Z&0$�uK9���|
�u���?>�������N<�r<�����i���2��H	�'�L�G�y�Cv�^@n�<��
�d4�2��R���@�����������G��E��#�.����������7MCQ�O:�ED�x��z�lc���"
W��T���UB�~���
H�.����A�������*�e��M���f�?b�++��aN��'�Y�=�b�0K��
�2�������I8>��O����h����B��B�PW���0��L'�����x���1mq�����YL
�h�t�3�� ������[B2�/�,_���rf���i%�1�6S]�jc ��((�9��~�h���p,���B���+�])�a�����$��7�9U]�;�.~�$�|HU�e�o�������������\y��&����p�9M"�����/Y��1'?��(��^�g��d��;C	�f=�(V�F�V%����2���H�xX���#�Q�bGFv�JR�����0�/:@�}=�m���l<]P���\����}I���z7����0h�2��X4�mQ���q>'�(X�*�;K���'CY.��.^75��<b>�5�SWeT�tOc������9���	��4?���)����U��2R���3��;&��-�����.[ak;��H�""<sn��i<�����f��v��B�6D�1k~��%��2�K ���u�}��m����"�z�{��ui����(�q*�7�`w��}�j�=�U%����y��h^��o�|�,ZH��/�?{�xa�E�8�����]e�����jP�������9�
���\�*��_���
� ���������q��y�����:#��KK��,����������U����6�����K,oQ'����+7�f}3�	k�T������{1�
����l���{�m]�28���6�'�����C�������7�v{$��E��������M��N�,0(�+jqh�q%?�5i�:����V����R����f�I�3���gF\u}�T�9s����{������x5�n�??�b��	������m#g��|�scS�"��;#K�����$�N�����1I0iYI<����T��P )����7�IK
�����u�|�h3�`Gp� ��y���J��g]�GW��	�#Ksv�Y>�wP���Y�&������vR#`�}t�Bm�1�mw���{?�`�+�4�n	#Ht]����&���C��.�c�I�EK��e�VU=_��:=��M�����p��LHS$�C�s�	�c�e^�-�����.�����<��s=��6�s����.�����X�n����%���������|��$�o�.�n�&���}�i+�9 �|� �����S-�X��`/��~��w��^W�e�:LL�����������$}�e3 ����}�T��IS�>* L��[p#�����t|���5��2�t2L�-b����Gr�p��y�Y���R�]���u-(.��P�2�N��n���4n��������j����X�v��������!s����`j�D�FKe�CW]�l��|H�v>K��g���&��Hl��c�F��Wv!�OjT���w�e�9��vNa<Y�J6B�"�<��=�:����$|"
�
����U\�$����%����:�Io�8�`4V{�G�U)�cy (C��P���h�p�:�i��9W���k���4���I�j|�Zj_�I�>x���T���J���Gr���0�C�i���,�L���I!��ofiG3
�A�>	���pr����%n<��w`&�,\��A6O(E�Buz��m��nj�R����<�:�i�`Z��Yx�7�}����nt�Y\���,�����K�~7eu���n3�����g��Z����$_�B����
��\�(������~��LSXy�d
�Xhm2n�u����j���6��A���n�4~��
rP�9���vR$~:9���*Z*���fBm�����rR#|����u���M��D�|r��"�t����89�����������Z-���'`��I=�	���&��&W���C/���jz�	yU�f��d O��ekk<>�M�[�w-�	�Y� X?@#M[O��f�!��������[�97���s:
H�r��o�����L�e�i��.�����4��XR�����#:bb8�6�a�"���Z� OY����e�8��+nj���qr)~N�7hM�����m�db�+��X�b����j�B�0eO2�I��N2����P�jM�<��5�y�:�a/h6z�F�Zk�f,����dzW��SoD��!���o���[A��:���+.D����B�e"~DA���_@��^����"v=�<[M��2h�o��CKG<�.�������S�
~�qb)�iNZ=vL^�r�\�3�-~��`���RI���)�3����h�0�1v�yE��9���kV�:f��+��d�n�g��:&�?[���$3A�sq]��:���t�T���9��+H��Y&����.B�g��X�������i������K��9Z�$��f��h��Z�Z����������]}e/����u��<�D��1���\E#�|��{�@�5*"q�|V���p�����"��$S����������^%P��������?SH�	���k���P�`��j*�.e�
Z�@j	��UA�<hS��@L?�]S�*���gs�p����X���L�)�F��P�l�����~�������}K�Z����!�������L���W.$��������[��;�$��/�7`~�e(�|,%<��M���	X����Q;v�a>����^T��- V�����Bb�$o������������������~X^���Ogk|�<���^Z;r�FY���Y�$w$m%9/������������r%���������������������\*Z���__]_�_�t�5��������-[pa���Wo�� l6{a�����N�3�G����(��&��^�����6 2�G~K0�pa�!�D�������|s:8��2'e��X�c1�'TZ0�l��������������25<��$
��p���L �H4��o%i}���P�V�}p���52B��9����AJ9���s�
8�,��qL�3;989�?�p���]��`�fw)�I�KO�yBz*s��������%O���b7A������P�U�1�D��:�|��r�68���\;k��@���������@2�_4(3��l{v�s�(� �<+�s�q���]N?FQ��mei����7o��DJN.Y�n3��Tq��c��P�)�^�>?��S��F��}�1fTn�0�}��G�`y!�����rp��M$R]�����Y���$�����6^�1�(������0-�Q��h���&b��QFsM'��e8Mm4��.�����BH��8��X��.\�O�yH��w�#[t�_���r�RIx�2h��Q�Qk�����Q����Z��Bb'��_#�k51��������4�X�by���!E��h[$�Bp4���M�&�UrK��
�,=������T��4�1���u���~��BC05�;P4����"�dk
1����`�@G�E��M<	�F�e���������_���Yn�1|:��=���+3�@�|-��^XF�:Bq�����:��Xu���	!M�R�f"zf���2���VP�� ����`p�G?�~sZ6\�_�����/.gm����p
�3}����J0�W�'w�C�����^P�<d���������,;���7�A3C��|�A&���Dg��f8uF��^��Z� �w�v���M�z�2�]��"��w�8�Qy41����i0�2��p�(���"y%����4���4��BP* .g{v���3r�@_vL���2n� ��"��W�E��#h��f~���R
@6��D�0��p�h7r������bz��{���%&	����������'��`�R/����,��i�$�^�_
��DD����8��kN8��P �|8����N�!h��=�D�_=���noM��5pH
P`l��e�S��$�q+����g��`�+; g����Hn�o8n�y�UyC+8!�V�I5�3��fk���;�h�����Q�6���p��c�����U}x@���#�>x��p����+G�nx��x86���R��,��@>���@)}���wB����:sZ$\+��
Q��	:Dy��Er�Z��o�E�u�����T�-�>#��Hvr��f��:I�6��-wN|�z�B����3�����Cq�R��4 ����ad��Xj��Y�H>G��:��c�����!�{�	�����{s���Q������CC��C��_:������[5:7�NxW5�
��Z�j:
����)y5	f67I�|+�^� I�8O��_��
�p�����x��mm��|��K����^�g#��@�u��|j�[Js;ar�������&�����������d=����Q���Q>����p���� Z8��������\�����'z�����R������[,w��v��^����a�h8�����r��[��i���F�����wA�p�A*GtI�����43��V�6�j����`�r�'����n5��WU��w���lz�?�O��?��fW�������2���X�^����#t��6�$��[����Yg����)�z��R^�0�[���4'
�i%50�Z��VgT��+  ���T����
ljI�w�\�W��w�FH���>���wutrtp�^	b�� *8;a��k������M0�����o/�O�����G�G^���+g�x�x�g�������xQ0<{�����?
���}��~����/�
������b��R|>#�+�R���1\9�2,C��������4�o&�O��a��uM�4�dm�6�?����a��F��n�s&�j��(��Y�h5 ���8/! u���@�I�����+�8��|V�%s�&y��C���w���F�Z+���������B�n��W�5�IX���v:�,bb�+��������9�+������:�9w�R
~v;m�Yk���_���u�z�]�4�-�^��������)~�b*��l�i'���k��b<��_��[t������1j���x����M�k5k�����xQo�Z�������W�z����Q������6���w
[��~�Uqc�K�U��:����S��+��
�P���F�E����1�Z�p��]/��\�_����F��6pz8!d���PA1U�B>�X�����12G��\@P�&�6Rx21��a(��0y�)�0��Gs&Q��1��2�XA�/	�N0��*���_ A�h�'���5fI(�|J��Q*h�Nh0Huqy���%��j�(����jE>?S����N-]rL����,8)+�������p�ZR�B���p����k����rt��S�$v��t������)!�:x����DUr�����0���>+�����].�����9���a�7�;�?
��W�'���i���_;Z,��4�~������J��I��_���V7�� �����9���XLo���Z_�E���b%�����e�_������{V{)o�Y����f_�b�Kg��O|�	�o�/����~�t��V���cn��
�T1?�&�,%>ht<m3���`S�F�������B�B�jY����2�,�����[�Y�4��-d�-N{�eC�����w�x��V�^������"3��]����O��t���6��LAw&�������_O�o��	�xd
EeH��1G0/g�V6��l�C"&�
�/��"~��?�jN]E��7�
6I0�G�5HW�@��cS$�pzs�!���]��E��]�k������/���"7����k�6�8��6+*Y+*mZ����x�s}���O���� ��v�'}��Au\�,�e�S���"�bw�r����?�N������t��g�
l�Ss@�T�B���D=|B�C���L������T}���eG�����7�%7UM>3���k��M��W��y��S��{��]�~�r������|
�����=��p�����J1����|
�uO��r{A �O0�������~P��3�R2�c�V�K������z�����Z������a�����-���8������cW^S��c.Q�y���$�����Y�����m�����p�7Z�q�Z���v-���p�Y�[.I����*L����)-�f
Ll������X���!�Z�x:�v���k�%`7����}�1f����||�v�?����^G_-h6�~���?�lO����A&�8�?zxp�<H%}1m[ �xSq�t0���C������z��DI4�&��~��K�o�����������g)g���3�q����K?���������A���uzk^�`.0.��-�P�~%XC������	�;�e�_+�����N�FK�J!�b��,)){�V�L�06�"n����s��R��|E�ovv8���>��#�u�����*9������	T����n�/�g���.;#q��1wR/��T�&H�����u��F-�P��	���'/9
Z�)�]j�rE�����J��E�&>��7�#�C7�o��[��c���������C�T�i];C�
�c'Bz��%V-q@����\P�RBaP$)]PA�)���g�(���(�*4w��/*��9c�%H}i��'^�kH��/�< �R����(��\���I��������,���M,�������	(��-������PT�F<|V6J��Y����T��	�(I;x
=�&�CUi<�K�����=�v0A>�P��w�\	����@Q#P�R�(_`M�wk��NQ��	M1�iQ����,_��~��S2�l��N��x��{������<�+�JLm��|��U^��dy��H�L�_��>�
��,�X�+��vZ��)�SI��;�(j
}\eis�M��2�mAG�kB��j�B%��G��
�Ff���X)�#����oZB�����#�����-�����yyJ�����l�2���%��Bp&��b�O'���NAv�p(e#G����S�%#�/��8��l��q�b��GC���Z��o��TKG������?CR�m�s�W^
�����&s>�����&�M$����O4�N�}g��e�
������S��pV|��,#�\]����]�����:�\,B(�
�d>��]���R��2;��;|���w|�q9��Jsfc�\�C�\]�_^�>�\\������I�[���&H��"\�G	^j�
M2��h:�����l��H��c-�)1Sk�t+
Q�����*�!�r��3���D�{g�Df�Y���L�-�$�sj�{�c*
��RF�.���b���N5/~eN������4%���|��#�I`��x�V\��6�end��;�>Z�\�H=�n��m�V��f���uYJ�K�7O���f=^{;;��������jT�3�����	��
����� �H��X�Z��x��~�?�C��D��KD��)�
@KO���NU��������t�j����x�$'�i��%^��
04.����#�)��I����������kL{�)�����i�16�@��c��x��wv2.����[��-�9����_R�����-������N�[o���#�,L$��x�v|P���/_~�
f��~pb�@�-�':���<cDP����,��&R�/FtH��(F��x��{��K�T��}	�?�|�Sn����Y���v����
5�!pV���V��K������u�+1�{[�E��"���+��/3?_{�@tZ�I�	e�+�����N;�8>ag#��8�2"h�5p'��� ���j@�:�K��p�{iu���a���A��5:���j5\��=ij��6h�o���/~�T2��^�7!2��!�����V���(D1�_��S2G�?���U�d��p$h������'�nA���&��H�1�}�nf1: ���0��_�V����sb�
���'�.D:�h��������~������8x���;�N�9�5�=����������0�lf������G����|�P��X������G���[x���b��6���m|�=Q��Uf��_�^A������(��4�&��`����_y0��a�U���y�*g1������l�������Z��`���:�:�Qzf���F�wf�o #�<C�P����w�~�h�oN�.F���p9�$�����������p���sg�����M��4����[�N�JK����j��A)��|/cJ�
� ��HV�9��������z��D
9[�+'S�\���(���P�~���C�����8�A��`�Ajv���/s�}�O�V��s�[��/.�A��Uk���8hv��j�3���n8j�������{��Vi �'����Ke����m�V�����a�^�(g��p��������q5�l`��VF���l�M&gT�`�a����9�HZ%�O��3&�M0�SN7J�B�k��,k���rz�p�����G!�2M���ig��P�����*x�� ���?;���������	B��+��#�(�9��)H����[�Uo��������;����g�����9����ot{�z\o��N�[��:�Q�?�����n�������<����}�G�3��_!8I�QC#�vUG����������������v3V�`B��B�n�K@i�j!����`�������$���cH���(�dYj�.(��pWVV|GK8SqK`E�C�����Q"�	�����4�E
������IV���9���p�����(IVaR��K`P�I�i����\*���4����.4����xFv6���Iu� n
�'��������GI���0pL>%s�Ao���z��<���H)>)$!oQ
V������L0w���M�S
�����P����0�Ds����_x���+��`F��x�������vI���]<�������y=OZ��!=�
c�Zv�������g�~;��v{���U�w����� ��3�����[1b]{����`�ZAe��f���I��{��
V��%�q�Hs�P���R�g"�l�`{�V����<��w�Y3o8r�������i����T����A8�7�`b+������t��v�t[����:�5[����c)�]�w�N�W�j5h�z0���l�qc�����bB/7A�Iw�T����`��'h8�c6�_�%K�4�X���s�N�w�����(��@���s�p���8��n��R�������1}*�@E�I��y*��HLk*�U2�~0��s���V�L�v��Ppw������`)��!���1�����#�*�.�:q�]^*������'�y�$���dI��
Z6T�x��9#�Z%��R�<�&������C�#-K��F]��(V���J�i�=j�L��RN�����~�"���������K]�X����;_.��X%��F��������z�B���LeK�5��Q�7(sW�	1}�e6���A�ho6�w��G�;�Z�U�����|���N���88
��M�P$m�f��n���J�4+�������u-����R���w����:�����i��k�����_p����
����=���`�[|����_;>��i"���Z-*q�m�9��m�@7\p��V^��}�H`�099���l��G��T��1���t���}:�����M��lS2tk=��W����^�3f�yx.�!,���'�_���2n�I ������+7f6�F%%�C >�0����S���-��Q�[�1���UKvj��:=
o���u�� �<�t������r5q�WZ!���\��R�g���[��}"��
J
q:�#x8M@���Ao��+��i���*�
�5����	�1VX�5������\<�j
�_F�������S�]��	\|�I��,xi�I�`s)�v��'�����'��`��.(����H�y�W�������wgpW*�gig��%��rwW�x,F�o?N"%�A���N������!�i���'H�8�!��� �+m��3��_19��D/eO�!u��������qT�\�����:����:�S��3����� �B�����g���X�X�'�
�3�+�R@�H&���V�zQ�d�����tn�6��'g���'�,^�=*c�g���U�����`��e��P�$@V\��"��h�^�������H�����s$aY��t�
"W��=���GF��bF��(B-�t��$7�9IOh��(}�W�9���e1fNv,�C����\��1M
/8Sc���@�������]!.��r����}�*=���#[�q���+.�Q��� ������,J�+��GIE �&�p��Y}i���:9�����:��,ON��R|0k�$��j��'2���\T���|�<w���E#��H��$����������>1Z�C��z�(��*Z���0��$�Z3�@��'�*^�=�O���2�j(��I��H��T�J �N�]i�Q��7Z�k���@��&e����I�Ev~���KX�G5������E�Dw����dF���
$��HnAI�Q'���LF�����"�'����t��=e�1����KM����(�#��m�_r���#Y�.�������%v��(�J�Jz�R\���Y�����F��K��Xnr
^)�l�����������rR`8K�MC�
:��@�.V2����=^-��L&(K������xU*i��pF3���I�i��������������e/.�St�Jm�4g����Y��o�d�]M��0�}-��]���������q�W]�]��2/�fJF){��f��HF@;
���b��_bDi�Z9��R�S'A���qb��@<b��{�=a���r�vG����6J�p����qAf(N�����f]KQ���.#�;�1A)n���jO:���s���n���+�1���l^;�Zc�v�������{1�/'��<��pz48>�>���pq}U��r��%�W>�M��*l�J����v?u��q�:j�~���	��V}����<W-��W�
�6&�Yxw�F���
��)�?��,e$	Ge�����9��)� ��&��P��)�n �������1
���+~c����1�������;��u���;�BDZ��45���y V�p�E%F77�-�Su�'��F�)r�����pf�)I#�{@��k��l_����h��m�����k�����]��Z����a8
��x������@�������t�<���!�U�h[���5�P��)g��+hV�CHD�f����+��..��	�w��#P|��C$�
~vp~zz|���I2�@�R����9�TMI��ZV��������7���q�l\��5W:L���hO���G��M�t�mG�k�U������P]�V�ue��������� ,�9�_��P�����@��m��vE������g�=�c����ra�)Y����a@A�Lb�����7G���x7��SRb�[Y���D8����
#�{�.���M;��W/�o��=�Tem��_��W2�^�������K������|P|�O��F}L9[��9��m�N��Z��>��$�����@��
�d^D�(�A�%RB��u	�9��MP��,�5�zw��_h�=�V"M�4�`�#i4g�1&���!��+��L��z�nY[�M
}�8hY�<���H,����x����t(�?b6v�������a��<��`��$V��<5w"��.^|���gd
N4�V�DZ�Y�F��W�G�ur`}*��f��/��D�wC���m|gk�|bj Vl�F���Y93)B�Zw��M�O8��v;	|��|���{�H�l����"�0��E��;<7���4�n�r'��fq����m��Z|V�=Y�S�,bqz��&����n��|H�;�|�%�����QI��<����5 ��tZiE���Il����y����qC ��<A��� ��zs9���# �=�1e����"�b�F�%F������MC�����a�]N�%iC��yS��n��B'��P���_�����4xBd��K# h�-�S�a6-�|����H�/�PW>�Ua���e��9I\a���'m���E���Y/B)��\5}�*���1%��QQ��lz5��^�>J�v��kB\�\!mKv��9������kN
e�<'��BL�"���2 ���Q���|�5�J�F��� �]��SG�����vj��';�I��	e���+���G��"U'r3lA�����c1@��6��K���8��[�	h��Lte]qJ9�g�x�Kx�P`�8�
�����I��6��~��!_7������(��?��hy�t�����]���(��w<!��5(��� E�^�<1�5*A�V�r�)_.B��c���e��Z3�i������3��PkG�c�.M8�/���g��~��<o�;�3!�#����P�<��U^+�_e����r�s������x�MW��
�Z���� �I��������6���y���	�?��j����zH��H�ye�V�����h�,�FR�v���h��$v���ub�����%]�_�������^;m��L���@�a�k./��|H�4W��`�[�\H�r�C�[y��&�h��t�r.�<{�x��x�����������%V��?��r�+�bgR=�h��D�QK�7k|F7��fq�<l�'�����4����&*p�#����i0K}�Ur��&�����M\B&
|��
9.K�x"n1a&���\�������%�������*���
�>����L�s_~38��2:�OO�����VM(5:�&�4h���/�hkp4pO})��T~Hx�;�QR��k�/�ON�NLn�bR������^�=��.�!6t�bZG��i��P�/�@}�=������~��=!�AS�*W�KgF����	�)O�
���qRR��(�$-��@��V�.6��"���W����dT�/�=f�C����4d�>��� �]^�_b��|!����������e�s�T��D@cT�i�k��(5��f���1�������'#tI���tt��>~+��"�/(������%2����������Y ��#��w�(A#��t�����Q��$v1A�T\r��0\���#����}�} �Gu�������|v{B�2�J�`z�}����+�k5�A���	�*N�,�@SR��f\l�>���f2���4e/����1{g��6�, ����|/��NJ��g_�����N${���,o���O �T��
����X�R�KH���`��p��S���d�����]�/��)�y�L#�*R�,.�
t�j��c$��t�%�j1��Y3����{�/`������{��9�d������TQ4�9m5/c�m���
�����X,�����
����f�VCx����0�ND+5GT�b$	�swKQ 8e{+�)�&#M������M�U�x�mtCk�k�rc�������&�����0��|�v��*9)fY���b��2��Q77�vS����Y�W���q\�g}/�h�H�^����t��dDu�����0�L��.n���Wm7 �4��U�v�~y�3�:B�3���)��Z��]i�S�o�r���Km���C�>3���.�K5���?��1xL���2;��_to��|���x��`�3)����F,$4k�6��z3O���K*�i��&,����5�
��5��{��V���Z�K�K�"_�,�a�'pG���En�i���Dr.1V��&*�r�����]�/c[�>7`��B�_jBm-��ng�=�����k1>,���"?�39��Q�&����mZ�O����]�Y5��Y������$q
��^m�VE�Y����"���l���J�_c&kDL�Q�C:���B���BY�,�#�J
Z\����;�cq��u%du��d>�E�����GO���BZ�����!�)���u	e6�m-�pvLL����"�rs�N({~����
�����DBe k�JH���/���r�*8��m�WlE��geE�b��@N?M7�&E�'�������n���s��)0g Kw8��N�M`�G�qO����N�����������-M����\��������������m2����z��mc�� [vDi��u$����D�t��zP�q�����Cl��a�2��>�?(��@���djt	��J
��������]��}�K�Z'�$D��nv����(��Y%�A/Y���a��%
I�FKSy X(�!�]����{�������1�o���T�����F*5��������p0���-�0,����0�\|��f��C����p�(���0a%g�R|r���:�V��n�����"���?Qd�[�Cbk�Z����	���%g�'��^j�Q�E��e��E�����������/n���z[��������_J)��E�*���#���������]=P��er���G�����I6�DU�B8F{c6���"!�b�3M�U��#�&,��(L��U�}d:
*MH�t1���T�qj���\����-��������$�PL�(d�%��I����'�5t�3��D�Qx=���\oR6b��pO
.�3�0��8F��r���IRn��4��%/X��~{!a ���if2�����zK(�"������ynSP�kC��"��E����]�����b4;�|�]��OV�� sqSH���3'"��K;_b�<�� KT�k��O%G��kF������pd����;iK!%������A4������>�W�hy�+Yk
�J���hR��5�T4�W�2������MO��J�I�OdrH��5=�&�'�����
�f��hM	�s�e��
�����D������`�E��IH'���"I�B�u�Z:����3l���m�P����d�X���=�a��kyz��)�or+&���Y��t�O+����������w�R3�K� ��0�u��N�ZO�������E(.]��L��"u��J�'K��M
�8a~�WSR1@3#g��~e3�s�yF --*w��'^2
��{m&E�4�,e���j���	�������,��F����`@|2�OV	������h�H�aXW��<��(�A C���kV�3�_��0
0�7k����M.�V��+�$���`��>R���2�T<��y0QOu�3F!���B�"���0p���I�����X��Z�����R����	I�>�f���X����!_m�\�h�g��)�l�����A`dAl���I�
�����9B3`l��B��B�8VJ.����S�0�t$@E\\����<g�����������j��6i��t��w�>CB�N�`�t�bO�����=���<:W���`^�-e�X*���v�
c����{��;������:��RT�����������.k!���O��rio9�`;8�r�0�[�[{�m
�YgY5�7d�A #���N�$���������XW�]2�=�R����}�����LW\O����{?�ro�����m��Z��
�0�����Jw�����\]��<8\�A�M�+��"��3'���^���R���@:cRrL��T&{�c��b�*��,�/8}$��H?d�R����D�"���d=%��w�y���E�M����z���[5ob��f��#'w,�m�����K��9�����50�U�s���Y��#�r������r����S��IPZ�:�L|�����v+w�?|�Z�����=�~�����[�/k���.���K�F�����}�>=��)�^�!B2������o�e���t���L�P:��C:d%4��A��H�����v,ui$�3��2F���t1���
Z�\�n�������=�:�c���@���i��wq��g�s������;�A3������Y{���F��x��Y��O�/C�a3VPP�$Y�A����H�V��V��[���<��#q�������7
x]�B�����a��{�UI������B]��DHN���ji���s);�������H���%;}�����m��D�]���U1T�B� �d�����(��6�hdd{���*>cj�S�G��3�	�(�4<fd���N)��\H�/VNFN�E������V����ofr���~n�$~f��u�/������X���<�n��	��p�g���8l�k�;�l�� ������U�mlp
�bH�����������C��V�Aj5���F���e��K�,�p#��s����X������_��C��o-S�����W �B����rl����=
����{L��v@M����J�^�Vn6t:�?��k�Qz�a�?���v��8�A��+�{Nm	��i��"��(CJe�&@�'�Jk��S|�Q��DB4&N�����-����pM����U������d�1�&8<T��-�*@^
C�%,8�*Jk�S��r�����98�����"u��l�Y�������&��b�N��'G-j�|�<���]W�:�pu�|��B�~\L�������������<�%�
�k��2��^_�[w@l�}|p�����������������m��M��Qz�az�6�oj�k}.Y��)�	8�_+*����
�Mooq�db�;.d�����a|���(���
��F�k~�I�����c�{�K��>.�3�s�GE���u�,�	e���G�*2���	�g����="�f����o���v�uI���k��7+9�F9wmT�z8{@��j9�=�u]i����A0'c��0�G�4�f�TT�Z����YzX%q�r��7�,������)<s����M�Y]�����]��@��~%������+��c2����.�������{8GhC�>l`f�����B��h.mW��$�f����3x@�p����3]�����55�8�{�KY�7e����k��n�����%L���lf��������������\���
?|�2!���$����dv+�:��!���m@��6 E��q�
�K�mv?��?�3�+��(������wg�>:|��0h��>���� k��_���8=����U��s�[,���B
�N��#�E�K!t��O'��%��<'t	K@Ey�.����]R_�z��
��=�
M�[=)�Bi��Y~�!�z�����@=�)jYd�EtM�*J�p�(%6Jr	�XM��p	���a��{�N���N��y��}�
F�P����.B#��������V$����Q{��N��h����,���1�p�<1YK��45�x+��b��T�%O<%���q,U��y$/��;��*�L��P?]�@%�]d��+$�@�d���p��	������Gu�V9�)�Q|��b�����[z�f]i�-l�.�F}�,�rk�����H�M���Xq�*��dnpO�x<N����Ac�F���P��[�"�>�~������y-V��{zt����������������2������n*�%������Q�:,�:
���3+�k�S|J�#��W�H,~�/do@r_���M�c$�?-��+Z�L:	��O2�M�{ �9�xpE��h�Q��r��h�U�������o�p���[�k���B�e�������"���SSH;�	�A`�Y��_2LP<[�Y�P_s,����e4���On#g;�f�W@�Pfhk`N��T�������."��\�FB��f'���lq��J�c�t���VN���B1�D��
�Z(�5��>
���mA��1�\V�ec9y�C�zQ�G�4���>qkgR�8�:�������\��=X�zPPPf���H�me�"���|��0�K�Q0�������%��9�����Eq�*5
���_����v��A�Z��`4�`������i�����S�J���c�R���Bs�l��U]��������1JWGSGg�b.\c�h�,��J��?a��b��d��<�{�V�d��#�u��H��VZi]fLl�� ��3o�Q�I�8b��L�����9�[��>�-_����aKk:"�\*�AQ��d1��l{Z���������*�������0��4��^�uZ�B4��^-�W����?���p�)T*o/���V��U*=xTPg��5�T/7!wY�����.�:@I(�����([X�s>H�Ay�H	��s��]��(����e8��~��p(^4����)�h,����*�������2���j0

�� ��L���J�#�r� �7����-	�*���� �eB�@�p����JK�~?����;�d(S�(�5��.��8��dJ�������VI�!P(M U9���6�G�<)��IO��D�1�x��eb.N��;G��L\�N`|�
8c0HQ:'�i��.�+!��^S��&1����������R��������6�����������Q�r�H���
�.��-�`5
��;6MtU7����-�\�d����s�>�S�*�b�����oQ���/���aa�M��fD��B����T�xs�?ql6r�����N�n����d��(�(r�O�Se�]�Ia�w��p�6��.��R��U�9��ylA������� �KU�
K�]TN���W��)��A�FV(��1E�"d0�/QZH���w�$#����K����O�9����K����i��+)�!s��S:7�%5�EG�H%0��8V���,"Hnb(xx3��>OH&�k��j�&4����i1����,���]L_(o�l����\�����[$0�����-w�kD�������i��� ���������N��E'���J|(V�N�xU�o&T�
��R5��U��_�����{lb�LJ5�N\�M��6dRKk�J���g������L�'�����g�����5����Y�~}�����o���^7���^3hI�8������M���51H~��`�#=��, �)!��K����8�m�,WW�(����;��4x���������������pb,�s��%e?#�S�n����V�����K%$���s���+�W������x7*�
��gU3�����/���
��sJ���	N$���I�i.7���h�O\S��g�9�3�W�h��!��kk~�
r{�V�Q�MX��:�v���5�p�����0�h�5�}��P��h
���"d�Q
S���7!��y='�,���i!@�Y��I�"9�����5oV�H/Vr����_<�,���\+L����I�d�T(�q�A&���2�N�����A,�8��$��v	�[���'e�9��G����L�b�mu�$��I�{���viO�d�y���X�UsD�����1H��<W������;gs>�����c�+un�h&V��^�w���7���\��&l�;�:R���5��8�Q�� ��{#l������;���!
7{�c�q�7=�sF������Z��5�j	b�<���x&��z7���U4��X��{	�<�yb����8����� 'y����Q��M�&��j�����W���Q�t3��~/��8x�5-O��W��
��{����=GO�h4H�CGl:��a���(X�/� n:��I�`GB2K�1U���������_��E�%X��5���#E4
�NdZ�x��������,~;9>���L
0���5���T���T���;`��g��+���j>k�6�@�}y4�~y��B���f��& CX�H)X ��c��Q2���{A��$��+����I<�'Y���ils8�w�����j+���A�9�7�X��<*k5C�"WY�Q��J�H�i8[Z�5Zr�9�r��[2��t�=�����������K
?�`= �_�ED���T�N����0��b�
����5\9�*|�i���3�ze�����c����0�(
���-�jN�)P�j$�
�6���P����|���r�c���z}�����Zm~�7���&�r��_����a
�F
��a��t�������(��P��z��"}�����w���p,�g=��	�dz��y�G����Y��r�+;�LWP4^z1�H$�HU�s~l�`K�w3��oAFI�����7��yg�#�!�/TF8CS	�BD�����8%n����X���qQr���F�����MUP���L���W��h��������\�4�H'��b�r.m�h�s;�)���N��K!]�2�l�f��v�$�������Z��}����QS��.��It3c�?,��|�?w�J,��J������b����d���7U*1#O���i�^K�9j���4��j�6n����vkY���Q��r� 2k"��xD�O�a\�4��H��t��
�������}����
�L�)
6w��� �.P�(����<��	d��/��`G�Eg��������8�-J�����w���[�{vV<E�#�	�m�����p2��[���_(O�d{%!3������r#����!f�������	����w����1��!&�)d�6�e�@�b���XLa�]<�x���x`�H���>-�I�$�oD7I��,�yy?���*6z:������Z&��C�Q�k�G�~B:D�������AX�\�����K�Y��l.������M�l����U���to0/Z
�[cz��i��;��g`\U1�Y0�76�8����K��B�q�����E��n����ZBG��������"p/���������u��?�����2��I$���8z�I�oP2�F�c��(|��A?����: #C7�+��1j%aK�!A�=�T%� 7@�+���F7�F��gc������(7��j�Q�6DP_��o�|��;qg������S��{���� �;��3��Pc
�'*��H�z��f�).}���S��?_L��5"k[hoW����4��@�f��+�]��
��,���"s�B�~���w����������5�����-\��u��k,P[��S-��n��?���jz ��!y���/��>���2��a�X��9E�� ��6Y���A/�N�Z�
��?l5���s4:���1~c���~���p�2�wv��1<�57@��?�}��]?H��f��}�Y�/N��&�����g|}
��H%�	�[Te���*�NB�%E=[�;#�k��b�b�v%��.�z�����#f1)��v'i�q��yF��?L)��Ki�om�KU��@ZhT�j�P���h�m���Z�V���j���"�aaw�����+��_Lae.G���0yR���Q�5����v�������/�f�����v��l���y�Y�����O� �b*��l�i'���k�����~���{��F���q��n���z�;�a���
G�~�5�A�7�N��p��z���^��{�Z�^�n^x��$�{?�����>�?>����[A5��[={]�8|!n���5��v�z���F�E���j�f� ��_x���%���kt~�H�H����$Xl�T��O���L�s?������;��P���J�*��&(p
�qp��$'��"������*	x�D�V��K(`.V��2�,�8\��L|�Io!H��\pC(I)������
�F����N\	�w�0�M�E�a*t��.8
}j�3���d�n���Y){��\<^&__�~�AA��g�U�������K����D73����P��>�F����!z'�7]M�������c�B��'�-����\�w�R��O"�#2����������������y4������o��I�S@R��K������e�%
���2�!Nd!X�8S��7V��O@���D�����U��"�q�������C������w�\��{{wwwUi"���/n�8�]%
���F�g���~�����?wZ�k��$�����������7����.^|���}����UhaR�����)p|/�����b������WZ��F��
w�w��W���p�K����]�����������}��<W�l��U�=�g��o3���=/g8m��������bL"SX	D�m������nYL-'!�����b��3:�u`�>�CyN�����|#��������|OK��E�F�?��;���'D�V�o�;�Z������l�w�jF6|2���B��0�0���mA���`��&��V����n��)��vq"r�*�~��]At+��BI{����=�b`���� j09spj��A1g&���e���g�z���Q�1zL��9�p���3�YH9/bh�F�J�O����0���B�F7u�����$�sEenJ9�%����i�f�}�QP��rc����c���1���������b���_�!�E���bgc�j��2�B��4B�i�-fE���� ���j��9(��.Z;�����K������F�.�	�:#�c}�!brmC�7y��M�XX�2%:B�p�v'xT�`��#�?�!P�(�bg��U$��4�Y�������>	1����q������GJ@k���'��U��\s�����\-�E��K�q���Ux�*e�g��9����`E�x��K�vB��9�z��������/�D����JZivu�X���k�nG�f�A; ���FV�����#~�����R�����	�>'`o�	X�)<D\�@y���1���z�e.��pF�ch�9�H+m;2���#��������n����_T�����������#j&�b���C`��b���E�lK{A��[~G�����L�
�C���"�=�>Pz�?�������x��a�\��n��������Z�(h�����G�^������,Z� w��O�����T<G��N ����Ze�2�	����,B�syOvs�E���v�D��������J��r�����P�~�&���G~�6�D��F#��A��1fK��0��YZ��%w����X���-i�@�L<
��	f8����lS �gaJ��p`�D9�uT��En�����u��3/gWS���I�I��v/���������l�����Q�+�I�K�:�S2�#�#m���SI�A��B�1�r��4z�PTeuF�/�4E���)��1:L&�p�*�6�����Z���VR��;����Q�
FC/�gbS�~��m�N��5w�7�x������3d_�]�*�e����D���;�92�Jd |��e���-��6o#�;�A�OV���>!���M�d���9�b�s������)�BN���������	3�UR=(��J�0�����p`����!�de�\�)3(�3�Q��/>t�Nc#1}�� `���r4��?�������.9|z��=�_���v��'��T��'>��>E�n��7���7h	�^������oN
�������uC��4�;Q���,$��j��f 
��=+�y������R?�=h�� �lS�x)�$����~�����iG��������,d�)W#p���%
O�9p@��K���A���Pw���jd�7�3x7J,��<U0��3`�;t�Aw���;8Z���4�=f
�C8�F��6�r��A�Q�Q�C�Q�5�o^

��{7����l�*�`���C�����[��9��r������p�Yk�4�)�N�i�s�#g"6�
Z�Z��L���E���c/��h2��1$�����%��u��q(yzh�`
�Kg�#f��\������K�+lM;��`���lPz3���w�9pqd�E
�
�����L�;
�$�X�{�,4�X��r&�t��6&����;��3M���hD:&��4HI+g?�,d2R\�t���|X�#yNM�`��h��7�����@�5�0��d����iP�f��h�}�&���N��J�H���x���rF����k� B*����#�"����Z4Pl*}�_Q����E��9� E&pq�2L�P"���\O�;�D�g�����R�P�I���Bo`��3��9*��D��W�cZ!I/vC�/]���D����2L\-�p���Kd*}I�����?M�A�c�/�SI�B#!���&�����%lI����h��@�g�H%��O!{��n����s�7'�M����r}������z>3_p�r�+�q�-��$L��;�b��K���YE���!�'�,z�n|Da��*{.�W<3R�kI���;1�������,�>-��q���P���I#�J��A��e������d'�
%Ley�4��=���J���-yoK%J���<�q����4�l��+����5����#�2����f�8�ENEYB{t��j��)wd�*+�&3+K�ECf�cb��Q��<��+H;(k�b:�t��������rC�w�sVRsx*k���N)��d��C�����3��j�<9��+K)��d������tpv~y�Ba���D_�?I����T���)��!-`�}
v
\T~3��*�95�f*g	�=�����D�gr�N#��Q�k'�]��K'8i�Kc!H..�Uj5�t?���c�Y�8V��T7Rtj�-�`����H��FU��s��U�0�rx�pC�71*�H+��47�y���.FFN
1Mb��).���ci�YV����F�f�SIIPt�,��c��
�W_�9��#x��[\MZ����me\���=O��r%.R*A��8I&u��Y`��r-�[w�+J9I�8{h��P�������)��>��Z?���N��B	<�z�{�js��\Y�������_��q���L�'���If+,�3��JQ��CWNP�  �ii����}-��4���E�*��jI����+��4b���U�Rf��82r�����>����&m��G���)���+�y������������G�H�E?�
�"(w,Z����� ��Q��:N�KR8��w��Zf8���&���L�h�:K��0e�f4�Jr�lx0������+�(��8ED�=������T���r��2�HD�F�R���45���I�) 1U#f�J\�F-MJ^&�!+dHR����aA�����L�,OV���Re�����)=�@BwF��
��BV.g���������d2�[p!e�Y�{����"g�_��dyP��,L4�2z&�u9��9/���2R�cbi�5��%�'Pc�����J?M���	�(��F<M���MEy����;8��9%G��k��ur��g���c�H��P�����"SM��C����)n�{j5G���Hd��~-�t�!�
�FeU4��GS�B��g:�,���K���lt�6ZB�"�1 ����e�&�y4���a2v��2,u��]�.�8�pQ� ]T����'��R^
�dIO`7$J(�G
]�����J������s}�&�52�����/UP��8����<��YS�i�Sa"�^I��B������
���
��m���P���x��<.��;����aMaK���=�4^@9��7�G��Gi�d�`��t�����v�zo�����d3YY�bKh!�L��=���[���<��}��I����eEu5���4����E�9�.��_��iq�+��B���sm;|��8@�o���I��Qs����[Qy���b����Bk�(�R�������0ROn���9`�f�OPzhC�b�H��9��������`e�3���D2��7��*��=��|�U	��2���%����M��W�D�����@���n.r�k��ppk@w�?�)����n���K�TZ (qz_!z������;WeX�kiP����s�����J���Js�)-�Sg=\���4���M�q�%��a@�+�8���k�d�,}�2�0���*��`��1B�����,��uOG7��I�M���������J��:e��+������gGG�W!��c����&�������IOn�t�]b�CM�W�S��p�:���1�]b7�f��Yk�Z����g�K�z�qnN8�W7�J�J���"0pSZuOfs�-�St&�����_�P���x��z����'�������=���j��9��bI4j$�p��t��&�W�������`/�"�3o�S��}UH�pt����@W�w�N�E�v1����vCZ�IvlpR��E2��p��?
�gp����0r�|��{<[B��&�k����w��Q��k�~<�88�tp�����G�L�%���If��Dd����7d0�I�$����!����x���,3Q�Cf�5����m��98��*>E����w�<b>��������A��RT)�Z-�����������B�?RT���#t�����7��sf����C/�L+��gb:2~K�Iu������������&N<��2#X���ka�@�
~�	�lB��4�W�>	nL������-W������B��S��7�x�B]Z������Q��g'��kF�&ft7�h)�*�@��Wz0���2F����(k��d��l��4�e,`7h�a7w�[����G���f*5g�-q.�1���_5�cx�F��q����l������wl�o[���-�Q�WS-.}����!K��d��_"�G�/=��bIF������k
�t���T;�����8��_��0`��6��$�89��}0�K�"5e��"�w����O��ll��oI�xL��!U���M�
��}x�i�^r���������OM�h�"�M{09�[�gW�zA�*�N����(#C���t��CsVc�C���H�G:V	Ea�O4���=��������M1\h���S"Er�����a���I��h����=�:�KV��^���C�l��}�b-dF&�&�n?����p��,0|G�M�d �u���!���\��g�Y�UK � ���!��'��	a����c�F:[��A.�g;TAr��iD�Z��@���`�&�]@�G7U�j�B)s2�K��"<w��]�%[|�)���S[����V��G�Ar�&��Z�Im��t�$��NY�<%�\�����h-��5l��CnT�<'�Gt;�Qe���4v`Geh�TXuB����t5M�o+*�K04UH��8��5.�9��%�EO�@H�5�n�?��4u���+YX�n�9�
day�����;.��x��J�i�R:��UW�BRA�x�E��*����	I�L��pDJt�EFX�z�q�����b���p���1_������{��w�i��:�-K������V����USH�Me��pG�X�w�r[�3A�����hT�$*�7=��+$����E��- }xO���H%X����4�3���y&mQ�
�rJL�`�R5 ��k���a�
�h������M��+����r,��-�����MMO��]}<�>x
���}��"�WC[l���pv��b��D�I�qq��TN���Lj�eX���@h�
���m����U>X��;�?;:������� ���-W0��OQ�1��.��^����>6i�^[��z�%m���?�]G.�9ng����b���7dw�[�j�����aJ�]�]+%�R�����D<�.���b>�$(�8R)�m�u�1#;=]-c��U8Km�D;p��L�}A��:0|�(���.�-�]�F����[�j��&;�-�k|{��~�����a���P�����Qs6�,<%;?�����<0�C`vf�/�j?[��eu����9N��8|�pbG��������d����H�5Z�3$c��`�E�X`���f���R>2�Lm���P7Dc�_z@>�a�+rg�����4"{A�$���)?���=s���g��^�A�+1���
�������P�a]4,�GI��0�;�1��QAx����W�V�������p�%�%KH���)�[��~���o84���W�������0}
��sO���e���l��O��E�`�b�s��yW�}_����-�J*���g1��r��E���pl8��#	U>�?G)cp��+�o��"��"Ss�Qq�X��0L���S�EH�o�4R[���Z�����a�?���t��H�tG�� P���W���0��:�4��Hk3�4x�*���2���s�hpf_�*"&0��'�j��
b2F�,EO�=EK��#������+�-;.� �0����EF�3m������?����"�%^���&CElV^���]��B����O2N#/c;e�/�"�M
�/�M��c�Q����>(\���!�=��6�J\`��r�����k�8`G������
���&<��c�J�#g����k%��x�i���Q�8
���M���K� ���[t�
������+���O9		�rW��v4}on��V�S5/i�s�$���N SqVi�oJ��@�{���#���]-5���!{�N��36�%�J-�z�c� n��L5�
���LW�k��0��g�Bi9�8����#��~����2@W��,0Z������Q��^�e�$w~{�I����{G)�B��X_��~m
��8��
bA�&1���UA�F(,��:����Di��F��,9H���e��X��c9��s?L\b�)�w���%�^�C8)R�F����D-�I������.����Kd�����e��1mC���+,l���IC\L�������M��x�N;�Li��H���Q���W��\�w=��
�����C�J�94����R��#��w����=���t���u�{�.�"	9���J;���l�NS3N�+�
K,S���Z�1�8�l���^3���9g�b}��9�8����q4�X�����lD+����Obw	g��C�_��	o�=k��A �����W���^��mKs��0��	tL;��QG$��8I�/��`�UQ���$
�R���j��?D���s�����,|������8�W��6��=��|�q|�r��e���rv��J��4��-Q8p����j�D�qG���c��J��)H�:�����Z�H�������}h�bx���Bw�L:��/������zI��	����F��J����-�/z<����X��s$��Pf^l�{_2���`� TM�Y�72��3�j��
�����x:N���B����xs��v���0cp�
yt85�c��S3����0�B�����4�W������vX%S�u��h+d��hq��D�~\]��;���2����_0j54��m��C}u��v�g���I}q�VNQ\��<�Q�'(V������p�����a��S<���bl�C���VT$��~Y!ss����L���3bY1 P}-�p$��(9Ut������M$����j��jf��$�fY��l�f�m�
����*I%[/��QIO���[^+�$Cw�=�^�*��2###"#���$C�N�� �����i���oq;�#�Q���PXq����N����r�8�8Z�������K���e�P,F�\���Q��eb�J��*\N���P�\xT��BIv'`��R:���K������5d�+��\7d�f�_>���n�}b���
��k�g�a�)�l����p%j��Qz���J���FiI�~{�`6H|e� �r���
n����W�!����Lguf#�R����l�����-*����U\I��?6hj���r��z�5H��-�����;-����|��e8d4�����11��]�i���{��"����_,����������0�9Pm~����w���|����m1��\N���lN�XB(IRTP{�o@&���Mc%Uzq�!�i������kXw^��L�2\xC��9��������������[�j����MBJ�-a!W3����m�\��,�&��.rt4iL�s�:�VH���:�uC)M,�=�/Z���.�����q����'|������R�����Tea`�V�
��N���tH@k2�Z�e�� �|���p�p��sv*��FsA��^���|_)J�r����!�5ee������(�ed.��G=K��5���f��������*At�L��!��c��k������I�����6r�G:7��Wo*N��#��������_�K��t6B����D��a.����+��Hn�Zs�����Rs?tW��i�h���� �F����d'� Sa����V�|�/,�3V�HH�U]d�b�Hm�X���
O���Q�|�r�*3���h|8��|�k��X���4E\���M�w����.$�D�[����ast��A#&�M������q��>���b���(��%��},{�����3������G��)G:l#_l�]������c^B��l9KX~,a��7�'�1���~t?w��^k�'����� �u�ZFN��L}�������j�����a�3=�=,Q��4g��.\���1��~e�C��*`j;�V$8R:�����gZaz���f(�T  #�yW�����7����"�����;]L�tG|��3���W�:fgb1���\��N/L���������l���t0��b"�����t���y���)A��l���6���B��sy�H��\���
���S�L���*��nW��*�@K��Wy5C k�l1�b(�(��-�&F��Z8��H�*��
0x,�Q6;� ���$�~9������t8sR�6�o' ������i�9��0S��+zN���h���[�/7�-��HF4<���+������.���V��%�8O����l)��+�oT�<�t�s����PC���6
H���+3��j��:�	��U;����WVo�B���2�m�����9�^�^�x��Z9�z��@�b��X���b����7�d�
		��������lJ����1����^�]���5Tah?kto����������"�:(��\a�8���:/�0:�,E�.�4T�����R������ca	B���Y^�=���T��$��:!�Aw�����"���C'��o'	����v����%��FX�[1i��e���}�$=�7�|_����K��}�%%��#~��������X^�<H+�dS��C�CE������`L�,(j?���Y6�!����@u��.�4�D�
����%Lw�n��j��J'	V��z2���T��n-/�_�����q"���m�R��|7UI����Y���(���J����>���X������!�/@N��R.���.���)�t"v_C�����B#h�1eld���0���*5+���Z@�F�D�R�#�s��`��4�����I�0mRw@�Im���������FF��������qzv�Ev2�qB]p����9�����������*p.�����y;:�����8c�/�Q�Dv�x ��Ul��C.{��f�d)RW=��E���<]��_N���7�w���$p!b��:wl� qJ�P2��sp�5 Zd�sR
�{����� �j�>��#m�B��lX��s^�h�2/#ak�:��$>�/G�+E
�������	�P"���ECV�,mp�W��1t��!\_K>r��P�!e}bp�6|��EIw������F����"�MTNH����[~�*bP�!Y��$lN��Q�x{�xw����i�:��Dr�L���|��,�\b�,�_�VE��<P���<�q\�;�n��_u��co�o��-��E��w)��&	B��3y����Ez���/����A<	�UU�|��y���k��w��=/�]���SV���DP��M��1Q=���d'3��
.��E8x�zOU^;SB�i;��#��6��*���7�����N?({����'�����[��
��#=��DV��ZHw�X��q�)�|��p�6�?�V?��%o������+W��n�J��fZ�	����i����"�U����_�|�}���oo��vs�����\��ucc���� �?^'� A�K��*���e"	]��Bv����s�$_��%�~�|�����!t�Y�<hH��H0v6A=��G�� T-6B�1�k���x(�Y���-�p��`r�����)�b���F�'?��u\��;��$��p��w�&��\�La�7�u�B��4�eEI����j7��R��2��>�{#q���0c�����,���t�!��h��E"P��PSrFFk����:�E�����	���bH6�IB��J?��$h����@� I�'	hnM���S��'U[������xR1����-� �\����][����S������E��
AU��f�n�Uou�O�c�����r���h#Tr�)�����F����s����C�4lQ��J��e�9��Z.7�np�J��%:�����Z�J45/����� ��]��7f�H�P�����u��?��_�l!��S���b���<g�no�����d����a��{5K��=��R
���;�A���;�����)bL `������yRh�_���
N�n��vh�D5�d0�bdh!��nX"�t������C3�3�<��)�2���bS����B
I!��CE����_n�=�M�M�����}��	�����8���H,y�Y�.���E����>~$,�}�P3�������3�o����U{c�LN�]Q���A�����b�s
�+�VR{7l�yp�+��v-��
�`s}G�Y�,Mk
�1��n�|O
�B{�K���!����/������$$���G�j�����6s�S��D>k��z���%�$2N,T������uj��`�_��)Ns��4��z����v�����8�G�v�W��ygk�d+U[]:&!-���$$���ok����6a_}<V(-�TP./_�J��xh�:��C=��r<�����F�0��nT�qg-U�T2����iB�����(���k]8��
��l�������W�c<��uE���6����cO��_i�����|�����-�g��MA�I
���i�����	l�%��_�\Z�����|G�V
�h�/!{�wnQ�#MY�]��i���E�l���8�.k�����m�UKC��m:R]�e|V\����m���nIo����������Wn�`M+�8?+v�Bm9�pT3(�H����u_�����Q[���������AG�cgO]����NX/$�����A���e�3���|*F��RB��'�B�w���@�,�U|'�V�{��veA��`$���
�ga���P@�7�U��-!U�8n�L��BsT$G��-��~�i0��JC�.���{-[�'chR��A��=�K&���*�K������V6W���y7�4�+
iK�^�f6� Hu��A�-45��Cy,�G�v�T�v�u*�iK���h_hk�
\���2�������G��l���K�&M2�S��7�c���/��R��+E*4+(���6���A	�6��2K�
)i�WV����=~�[E�Lw�E������dw�����M0�9$'����=)�O���~�_+�~��5 ���4��R�K`�������!:��m?����`��,UD��K�����$7����pGu*��}��}��@������x6�����������,Q������R��;�FWf���9bS	��D,����|��_}S�.��F��wl6Z�K�+�E?�b�,�W�����Ne��]�3�����6-�
c�L�\���H�,�@������������q�<��S�
���b���b��S�E����&�.Y��:�Yn8���&
�4�����Qc�a���yN����x��y�oGG���O���O���O�E�R�}x���/O����*�8�&��P�;��\[C�tC������\Z=��Sv�N��.+_6��N�O�:y)��`������k� ]X�����o%�u����k.����R��l5t�w2��Bx��������6N���YW�+�nx�����fe�P�L���[���@-��v����eUk{��b�xR�1���{a��0Gc��s��X�/�,�05�n:+������	�r�uWBo���
�#{�ma9�_��>>��@�;����:�E�/���j�tAZ#
z�$b��m�=����a���&��er�M�C��2O�T�+��xb����N_��FR(p��j1����"I��=s�7�
���Q��S�-��b%�rS lr$'6BA�������\�:�'��i3I���F.�P���!��@��CH�
�~%�J(����!Tx�����?�xC�QT�u��	(�FAWz-����V{�1�s_�&jrZV��i������&��=���H�L��8$Y�j���.O�/V���*��i���9F��)zH0L���}F����������.]1*���w�����~���M-�F�p�>����f�+(i����]8��V�Z���#H���{�1�e�58bB&��rS��|�hC����ap:������&�����%����Z\�*[�@�J#,�(�V����%��Q4E���V�f0��\+�^�lhy<���;�NWO�r�2s�S�\����&S�T�1�U�����u�0��d���k��k�R���L�_pK�]���!��<l���z=y��
�X��J�a����/w���t:��<�4��������8����BO<���Y���&��D�����t��E:���qM����#���zk�D ����V����M�*W�O�Rm�����Zom���hw�|��,��ygg����?�r�T�vj���~���;�|[?���}�#8��l[i�����Z�|7�s�����jN[��������j�S���l�������x���,����3��x1�.g+Zj"���}u�~2"���>y�()��d�a�5�P�V�r��/#2�'���xZ9��~!J�=b9�}�[k���}����wgg�bS�a���p4������b��N��Y����+c����Sr77�OP��-�t
�>��6��;.t����?��0������rR��X��
�C`�����l
��?$���CPGb�����9����x��7|�,L~"S���8����A�<rMG�l����i;Y!r\���`�us� W����Ta)"�	�v,�#��������hG4��hi+�z��_�1�OT������������gN�u�g�����a�F�:��-��k�������>3��V��;��8���'P.�����Eh]��k`GX�������/7��f���h3�{.k)/�����"�H?�Z\}O F����|��$\	p�<���&��]�w���Ld�`cg���|����r�$��[~��j5H��
�f1	,�m���f�Y>:|vt|��O��\J'*)�UD�~:�*
)���8�D�@W���
��_�X�� �8��vq�\P�(����RP|2�7zRP%�?�S���������-H3DJ�,��|AnN������ieL�����AQEx�d6�qgF�YaE�?�+���
F#���m���'�������ox�>�����+l[Ko8O��jo��"p��=&�En�@�:��x�������'���uv��]6���dB�D ,�Mm-�:�s����"�iA=/~yut���>�o8�c�{u�����{�5A2���#�T�;�L��<�k�M���h1m���xc
(1�3�7�����H'��6�3N��B
��f$�M�!4`KX�;	�"a���@�~b�oL�&{Vp��h�����"y
��}O*���B��ZV������RWb�6P��l����O���e8,J=��
�������r-	zXw3��u��������������/��#�)wy
"�����i�jr�L�����\�C9�j�~�gm�H�[�����*ee.�b�\2Td���)W��k���et�����)X87XI�y�U�����#����n�n��3�Y���f�H8��jQ���o�*^,�W�f��e�	���\����M���h�|����#1�
��v�e������n�j��
V-dH�Q���K
w�6��i�R������:f�����p7]Ai�p����	V�<�o��T�������z��r\b\F�i`�Qku������:>���P�\c��X7g�T��5���^Y����n�F���
�Z:��-���e������8����f�T��������bl������*��l����}a5=�o���'�������u$�x9^p5�!]�>P����<N����`�zs���Q�g�o%&�����!����3�?;��w#;�)�K'��J�-+�E�T����X� K�:��klc��a�{�T���)kD�K�3��vS�-��o6.���p�0��f���s;�Voh��r�[���,���Z��\��4y�����-N���~�����Lo�}�:��G���C�d��V�1���!4k�-~�z>]n��:���	�;�h�a�dR�i+G��%��:� V��a
l����(`��^u��x��]��0���6�1��f�M:���7��:����y�m��5f=���J~�5�v���������z�W��_���S<�h�0	��j����i��	������w�z����pdyh����8�'U�������=JpO����$A�'����V�(����(��	?W�h��[��1�%gA���9A���z���L���@U��\�4���q�^��j;O������hQ^X�i�;�����.��P	����Xf;B OM4fY-U�9A�O��$jH�e�p���L�tq ���+�,A�c3���u�A�����-!�&�P��tiSP P�_d���
M�o_����f#Y�^�3`� E~3����t�B=`�-�{��!&{<���@(.G�5������2����s��n`����o\��z�����'��@?��(�����KjV�]K����)[�#��`�98���$&e�5�Uk��6��O��n1U4D��K[n�V'Jj�E��v�\r�_�"������e�+�x��p���]�g!���OI2#c�q�v>.�"��(r�&��~!�.N����T��h�TXlv(Tp���6��o��?�6��Ta�:�x�]��b4�A�)�f��K�����?��M�����M��|�As,��	���������	S �8�{�Gd�����~����0����s^8P"#?��"����3i�8��c0�!�g#��/CAQf����@^��Yg�&���5� �?��)�����k���s�HuH�!���s�(/����`��w�Av���Wd������}�@�%�����u��:v��}�E��fXw�]�������i/������X���~�����k�W����A�W2�B����Xy���m����6c�U��C�(��^�6����n��������N������o8z�J����` :���	����m�6���Cp������MD�U���f]q�?�G����k�-��ZLUv��z4��J�EG������(skt}Z�����n������<M�1�Mf�}:��eYj�Y�6n}��FN�2�dP�#�o"�9��jH�#�wm�������N�ynq
��Bv�@zX���x��E�!=��
6���A�@Ld�bR8U�DN�u (~x����rP�}S-%���b��T2�)6:JA���)��7K���.���C��3�q��l�x�l�������:`5�;�)��Dp~�T9F�r�I����� �#�{8����`�$�b�F[W	{B�&����d�D�Fl���@�"W{���J�~9��"����S���2�X�����O�n��QHLs�Q��z%�*xMW+P3"(��������i����>%�RQ��AS�1����P��[�g�,o��Z�;��!'G�6��@�#h�L�����H�������\��_B�ps���!UoL�w)����Qby%�%2v��Du�:-r&�W]��$�������jK�HdIl��Zk=���z���_��P�YF������P�B��@���qD�A��=G0��'(��$�b�3��gs�1�q�b��@���iwF�Q2p~y����w�'��\�����(������u+��R��z�)]~C,�d�F�K����y^�,��H���E}K��I��M���s�(LW�hV5n������P�Y����B|���8���z�~���oT*���?�o���}�K_.�W�c���A��~�����B�e�G�$:B��9������z�����7�� �������qO�MG���&TV���fQUScG��=���|=p��	,{���1S���	p�l�k@'��!�(��]��L����#l��p2����%U�)���ZQ��?��.�v���V��;�A����:��+kB����Yt�*k�#���O�u�����(��������LC��!���>=:9��l���4'w�xzv��
���	�i���q��&����%+z�����'���C�I�I)t�m�������$����R^�����k!��8�u���p��d�
��W{���s*��$�>��k��b�S��g�5^�������1��#>�2����D�����/	#�R!4���rBl�?�JW��������4�.pH�y#A<�L��N��R?��_�������2������)	����<��l6U�lY!�Q'�`���(������>��^x��k����r�T���"���Qm��������g������_�_F�K���>����}��aR��5Q��Z|�.)m�hq/�jn=�����'��`�U�-5b�P��Q���$R��,	�����D%'�O|��[��RFU��Y
�%�Q0<�K����;>;��h��9Bn��fgeR��~&�M~���	-�Ge9�
�
ZD������t'dJ�<9(�$����>q>���"P����iF����T��4�j�8��C��L^���?� �d�YZ��|��l�6;E����x0N�3Tdb��F��s��d�IcM
��D_R�H���D�����r���%sr4;�+ed9h�B@����*D�FW:���<6�N���5��#�,�$��?��9q��y��e%&5�>���;����}'`������nI�����4>8��
�%���JzG�^���.���	p>S�Gta�����U�O���Mr4	�!�j�G�Z���LRO}�L+*K��7;��1�������X]���3Y�� AD�����vV�q'����,���g��}'C8�3�	�I&$����j��I�n���Z���������,����;���/[zP��^�)��g�9�	��n���O����"z��� t��	��W���@��Y2����Q���`�8N��Vv'���E,E���T}�"2�Hf4����DO���rjG����,@6�����B[����#��1�������lt�:/���pu�����4
;����5ADCT�����}��o�	��s�����gx.B6�5��2���_�%��}_���d�����}�������gv%<��O$��f�$�y�lB�����yw�T5ru&��e��x�}���O9opo�rV�)��_L�/G���K�ZU �1��R%I��N�.��5��HZ�\�/����������_�b���Q�����������������)�E�L�X�������Z���co��X�������Rj�m�,�E���k�GO�?��k�������d�r!�����[)�J��9�[�W�`�=�.<�1�wq�u^�L�����..�������
E���`���L	L�J�

�[�.gP,��
J9�����%=(�������Y��x��.��z�|LFhiC�`�sV���F]%������R��Y�0(�_�������������/?~|p����,;��/�����/�(IxI��_�
��v�i��U��cK�?S�EHR������JT��h����N���x��}�L�����Qh��0���\GC������t���pSw�P'�<������\J\)=:I�/.����t�Mi�k��*Y�]�@����x�D��B'@\���������*�5C|�		]o��`���g�d�.�M�Z=��7�
C������!��v���|u"^;%w���>K�hl�8��nym9=�v�Y�,�IqC�Ms�9{I�7B�/#J5k�S;���!|k7(������4���}@��b�tH�>b^Ao� @�����J�_�>b�Y��
������j�O1�6h����s�b,$6c�������[j�D�����ZC���P�*Ad��4�E�NT�v��^|)U��J��J�z��Uq�Q���4����=�����fV��x��C��&c�W��6���e�d��SVaV�_����7���y��^�����7�CS�Gh0tfo�O�Q�Di���b�6
G��QO��:���T�����9cs;�|����F'd� ��+�Cr��G�0���v��va����.k���c�OG�Q30�,�n@�>= ��_��Mg���Sd\����W��
���R���3,�x��q:��e
��M��S���^Q��7}s;�C�����B�������9�NA#X�eJX��d/��$���P=��qY��[�hD�J��O0����bO�e)Y�k�E�=\��|��>V���$���D3vG	�;�w�Z�dN��W2&�)���]m��9{�w�m#����{��W����dq'o
�����>{\�0g�_�O�C����8{E���c4������&�q��(��I�������!�W��+�U�N��-H���0��c&�1X�r�dn�C^$~�M��,�N�C��Y���,�OaF���2 e�a�v��-��zMJ���W��mH�0/"L�{x���������@�Y�e�SK�l�%}V!{W���!J�5�>F�\u�|�k���E��8J#G����{V�g�h�J&x)RU����`��	Aiyd�0*"��P�d��[}:!�u����a�Y���H����!���VJ��t�Xq)�����/����@PKh��p%r]���(�s��g�0�t���d�2mF�2&t	Vk���H|����><��F�E@���-FI���s�`Z�����*����ad�6�1�aH���O��;N�@=V����S��7��j������[����pwJ����d+���r&%&dt�����X���ij��&S�!c�>�����ET`Y���b�E�s��S;� ��C�lx�.�6x�QL[�$�}c��D�-N����<[�o���b.�(6dq({���aP�6T��������&��X$29%Z���Zd)���"Z��,�c�sD9�@Y1�Y��xm8�qY��
�>p�NX����Y%h!A���Q:2��/Os�6	��l���@k��p��oIL�����|�{�h^7$����[��D���^�����jA������B�8�5�����Nj�;�^�	_xB��i6�Y��� �����5������wo��l[]�
�YMz>f��ZMv��(�V�sd�B
������tY<��=ktJf?����d>����o�����@�e�w2�h�5za���C,�j9����u���vG�P7�LGA	���*EkO1�(r;��f�[�qZw�fu��l�7RG3�)-mA����%����&���Y�X�Y�B_dQ����K:�]�W�����J�r�{s�,:�Y7r=>���B0!�~��,j�1s�#�
Y��w�V�?�>�6^N�t`@Oj���Q�]�z�W���m��j��l�H����t0%�;�,�<k��C���V���y��|�b�]��4d7� ���bn4�gJ�F����	e��|}�i4��Q�r������V��/�_�������[�6y���a^=]�$3���u��Ue����0��xb0�D�|J<(&�.���^j��g���������g�5��d6V/�y]�F+�]�Y��w�����u<�t�����
�f�m�+�Z�����������N����jm�Z���������GY������s2//�e$8����q6�_Uf1���J����o���j�?�_{���������&�U��Fs�E�2�y`(��|ZP�
����D�����9�D{�vu������Yk�v����v��W�Z��U�[�k�N�YT�����T�Vk���I�q����.�S�&�?���W@*~�	�'����z���I��������N
��<Y��?8|D��;D��j{���5"�|�-��,B�D����Mt�$U��e{��A���_�^;�q~<��Uq���+�������2�M��Z����k���6<����=(oof]�W��V1��nZ�v��[��Ri�:��vuW��@+4>�+��cu�Z���x%��t1E�����I9�������Ho'I��4���H�y�����@o��;������.�"6�<j����������[�U/����pt
�D"�����D������������r�}@+�_�����7�P��x�N��g`*�(t�h<�.^S���5-"��H��O��d��@@�]�P��������1����N7#�I��W�cP�V6O+�������3���Af��
mk�4:�$��<Vn��@n}�����d[���=��zhxL'���e��'W!E��G�Af�q!��m)�Fh2���~��A�jjNs�D��z#�C�i^h�&Bn�����J�%�]�i��[�p�@�>�J>��c9�'[wq���2�]�����������!���C<O�f�V���7��������evrs�E�������"2g�5�33�
y'\��N2��M,����>���H	O8�l0=�?��(z��o�7GG�N�0�o�da��j��Q���Y>����X�6wr�z~�S1��3�h��*)`�vET@���db�:��!�>��[4����S����6/�~���~�x�6,Fi��~�����
�g@g��<���e�������k[��96�l6{�������<[r`DF�����O_�?y��GB��Z���t�k���q��j�����I�Q��qaT�f�OI}����j7~%�4���������;�"B^� ��h�>l���������\��C�q�Y���e�����%g:#/��Z
�)�Z�5����
�v&���^=���Z���v�W���r�pk}v���9��#�3�z����!�!R����I�j�\�>T4��5�D���8� M�K^�	t�  �F�x��0��St������k�������7GoN�7�O��,�&�{W���������/^���0B�@�<�^Np%180�m������py�,�LT{������MQ�@��eP����w���/����uq���VbH&8��:TV�B�Z��������C��nt�-/�Z�u�w�%�^D��5h7�o7Ym�1��B�����5���Q��r�_�Q7�����+S>B~�6q�y���H����P�Qe�(;#m�������yn�������A��G�����b�B��n��~�P6��D1�5j��!-a�H��H`z����-��W��8�����2kO'	��[���:_�]��
������U�{�\�{��W
E/@!'F���/'?O M�+�U��������Q��)-m��Z����"��{��J$~�f��y�z���U������$����V/gw�N�^�TY��Hq���wy������a�5l��R|��h�C�2S���7�b��O�B�tw�y�;�[�na���)F����$�
X�P^�I�.l��d�`��Sd��6��cYL�2�)�6$�j���7����1�16XI4$]�'���3�f�V'��nh��v����ju�U���6G��7���Y������*�1��[�;m���s��_e
;u��N���86��e#������&���ot���b9����!��&��������o[�6K|1����������1�J(�|<E�S��F�0��7�,GF&��������"�-�M4�Imh_�Q��e�R��Z���Ro�.����K��C�F�Q����~��������6�Lo��feQX|w����Vx_N4#
��l
���4�:K'b6xN���&���eb�c�$��1��e(u)(����/�0��/�bTAf�"�����o2N����R���������|5�%�s
���7Bb�r������b��
��'�&����F������Yo��4J�h�O����V+��*�:]Lg�t05j,�5j��>L&��L�T��d�"���v��bC{EWA��[����,��a���H(��Bf7��Y7���+�1L��(������{<~d*���M��J�S���	]c�o��B�ve���+da��r�3�1����,�}�o,�f��f�����D�y*c���.�e_KL�Q�=�h�����_�~����������k��Q��= �Zfm���|������9[5�1����5	Q���i���&]Q�K�a��V���iV_o�a��zCp r>�0��L)����kT���4�H�u�mQ^��3/[4�%5eeM��r���VR�J��n�z���]��L����~V5;r��&vT�c�8��;��&����5	+�
�L��������p���:�o���l�e��;X����VA���!��7���]������<�� ����%Drk+�sv
'l���������W������������������1�ku��z-�5��F�5huz�z��?�}X���'x����4�l7���Q)jF�+/������&��F��^%/p ��"��!Q��1Q�e����[>����6����~%���]�pLc���p�h_Q��	����L�Kx��7��,~>�-%A������?�q��6�%������|���vwg�\I������G���7����q��������6?�}����?.����m�K���GO��/���Cz�_��Y%{�u�>�0Mt��g�]�ptV����zc�q~�*\L'#�t�J�yc����x�������V=��f���A�q=�=���w��~������F5�kU*��~�6���{�+V�6��T���v�l�����Mr�y����#^�$%z|~��utow��w����.������O?��>�<�9��������c83G�v�z�9��� ]�����?�G���;g�>�����WOz'�2O�������}b�X��
��/c���"y��1�7��C3���]�����������!p��F�N�<�K���Q���{���=�f��z���nL���������yl{��hV��'��|��n���#�`Zid����*�����4t�L0�	C�����$�r}t>�����`h�I�G�.������]��o���o�9����
��+��O�U�� {\�x��sYY����h�Na����>�LQ6�l:!�w8q�/�"F.?�-J~�����F���dH��e��,Fc2��4�q���Kp��P����a�L'��'�i$�����F���JfF\�y�s�U�2��Iv��T�����\��F�`K��{���J&w3�"`(����|I�K���^w0f�'�#��U�G!���C1�A�f�m�KP�PQ�B��y�l�����9�N�Xt�S6��
�*�������DW��J���,p�r�t�3������UcO�?d}��W�����~}P�7��Z�����4��j��*8l���_���[m<s����i/X��vQT�T��9a�|��UQ�	��4���d^���s��V>�����K��p��lC����RX%���1��O+�4��r�~�D�l�XJ���O/q(���D��I;	 �C���N��tK�/��Gwfge�Q�({��}L�P�6f/9������O�e�k�p�iM�|�}��-*�	\��j���~���Vu`��^N)c!�? �E��_y�b��JN>��2��0��T 2�	��v�<����� ��B1J��h�
�2S�%ehU��DG}��7��������q�����&~5���.XU����G
+���!�k�s��"��%�3�-����V_�;�{������1��RG�c�p���t��c&��Li��=<��7s�����G�u3d���|0�����;��O�^==�f��
�1]2�0���Udu��1�3S}�����.6��Hs8�p���:0Xj���!|�@��W��}�!H}p���G�
M�aE�D�}��(*�����R�.�G}�O����$����9_=K��|4�c<����U�2H>y3�q���}�W%��^����IU?�����R/�5&��n���+j]<�?R��)�}�1�,l8b����4��z�R�)���v$��"�@�f����*��|I�Q^��B=��|�l+��M��Q�9+����_��tY�������Fu���
#�,^!%���_�o8A���M��?h�
����G:�9�|�����]a�h��c����#�4&f�'1�N=�^��U�2��[~Y�	mEQ�0-�bSx�b�[��t����7�]�MP�<�C��Y2� Bs���Vy�w+!�8�mc�����/��I� ����N��gY���t��	A�D�����5G7	������U�#,�fF�T���hK�iD�MWQt����7.�������l���'�
N�i`H$�.h��n��O��2����;�	tm�.64��~��
f�Ut.i`�yo�/jJ��%*L|:�X������������l<b�/#Z��@W %Z��Dn���ms�][/�;��������~�j�U*������o������J�$�|���5��G���U��\�d��DO��1�,��2zx��m[��[��M�1����Qf����~b����[�9�Z)3�1�*���p�C���
T��H��Q8G��mo��i�d�:�R�\�A��m����p��(^.��_�9,�8�2$��
:��}����J�������B�����R[�q���%�*k���5�,[��f�E��l�#�����#c~e�=QYR�7P��I�5���l:K�R1}/;S�������h�D8�@��j�;h�r�.�#^�^���@�C����i�b����3#���8���F�/
m�"���c�|s���ejsd���h�t����>��#�����)�L��<>E��e��(2�{����d�}z���9'���^P:����������Q��F�����.��$��fD�F	P_>9�G��|���������8��2!mJz���m4���rK|�sg|!�P�s0PY�l�N�8���d���u6��a�%�I��R�r���K<�7wt0�*�A������*q4}m�/��}���5�yD(\��T<�CSw�`�x�p�0�7������)�/c�����m��c�@0�L�^?����H�U��������lmr��0�S�<��~F>)��B��?�rc&sNBD��92q�.���K�P�<���
>QHl���"b/�M3�.f��~C�1_�G��	s��/Z�� �&�X�M���lq���	���:��W/���'u*+��
3<���`������;r��w}N?h����s"J>���*ar}{L��K�Py6��`����$5aW���,VE]�VPy^*�&��(�������������`�E����\�����>w=A�z�����@BmG�����v�$n7�G�9	&g�?�&\�#��K���r4�d���<���{;|��2���J|�X�BQ�z�#���L������X�O� _�<���%��D��'���Z��[(��	,�����/�!����?Zx���3�8�p&�w;.�#�"�Jy����+*�%�{N�?������#��'i& ���g�!%�������S���!���w��W������a��a3��Nk�������^�]���I�h���Z/i$�N����qf��F�8z�����>�z�������u|�A����F�S�kd��kur�f
��B�A�"�B77�C��/�3`�,�4�vE���dH���j%��	�2!���Pcc��R�tJ�9���\��M���5@�A~9���r"�he ��y���cMl�|<�������V�a�s����/��st���cv��Vo�H��u��3#�~-����owZ��Z�^��
��{�Auo{�m(X�)�nCd�j	P�_d���p��W�������vgs�|)��nz���E�O�����`z9�2MD��_����Z ��M��lu-B����i�K
�+3u�jKla-�����
d����Mn%��x~�mB"����D:�U��.���JL��T��AcF���l��	��*�
zU��DC����FV����?9cBXG�C �}�y��( ��'�Rfa���������=�N��)��'?�3x��3zd���o�Qp��(rh`;:���/���[�6�?K�]��ra�+�)<�aS�����.+&��D8��{*2��~
�_�����*�e��y
zl����+���@�~�R��g
���-LY����N�f���x��&wx�.����g��"�����]u;��'t����i���{�2�`���Q�l��UJ�P8�U!
�hx�>����P��Vm[T0�]|�L�r�uVs:)s�M�[^�IS�rD6lP�j&��2J=AO��e<V��U�1���=G3�l6x�<^%{E�U�*��h�:K�L���CK��E�Ni���W���:��->J�
���e�W� ��h��d�f���#��<���x�W�#���&K\�p����"��6������������iE��w����r�4�� ����Q-����r�XFx������^"�a�� sf�S�������ka%�u���|����&��O��#�����y�?���S�������"4S��
l���t;��%�H�3�v&2G������[��].�� �S�������,���P6���:Jcq�����:�
��f>�=P�C�S"qP�F�b6�{%���N��)j����Vs�����f;>�e)����~
47[CLU�EL(�9�d	h����pz g��th�H�2��*;!l�1���4��v���r%��k>�HF��~-�W�6��z�~��+�d�I�I�����aG2
e1G2Eh��%�_�_v�GLq1�l\�JV�;��
��-k�}V��6r��7�k()����K��
WPa8O����mP�|�.Z,���J����h��%U����jE5�YY1;����82]	o�����������]�[2�lt<��g�6P���4HP!��� xu���J�!��'/������w��c|��U~�����e9����'A�<���Z��"�,F����U�|���#�����C��d���z��j��O��^2�U��p����F\o�z�y����h<�E[^��p�*��C1����t�A�A9@���j���L���X���&Pee_#�'�s���2������t��z�����B�V��o4�'7L��/��IeD~V{�z�Bu�Ri7�j�~��n�O��������:��<w:��S���l�o���t�����*XC�����6&0Q����3�ezo��>*��F����Pj=�w!��q%[��m��t9�CQ}U�%������Y�m���F����QoW��F����^���j�}E6���h�P)�P�q�2c[��PS����O��N1�UBW���%��9R�X/%��t�:��s8P�c%���t�/���NmW^��S\�Z�i�Z�0]�t�����4��C<�x��be���;�v�N�p����e���.�e�Mh���(.'������^;k��'<�k&�)�5(����2���7p��ox����y����\%��A���.����,��J���Q]G�Z��bN����S�iAS�w$=��eu,���k��������������*�~�F�����8a�:��,�����
��iw��5�F*�^�]&�$��k��rC�H�\D���87p+������k�#��� #��X�����Y��R��UK"�j$m� ~�7H��o�I�}
D�����Jx�"�������x��� ��x��V��D/��x#$��2�2���R���H+��45�zh�?F;���~��:������F\Ut!��c�	j G�	�e��#5�)o�VrPf��
�9�O�W�M�m���Q&�a�C��62�e$�;����0�5�kQ����/�������/��Y���~e�1"@�o�(�Vbo�Gg������B�?�+�Z|��.h������[�sK�s{��G�;�H�;�Pp��R��5(����A6��������2��e�8�~�xC��i��%d����h|
/�b�_��_�������?����d�Yo%{���W;I�Iz���1h%�f���'�A��j~��F"vL�����x��>hr���s�l@�Q�8M<�4���|��/��4!D��8A���m)��M0�hc�JK���	97L����.�q
r�h���Bi�9?����N��i�g��_c�Z
r�8��a%�1�R���$7|3cc]+c�{+��4�L��j�j�w@�Y�Qk�q�������1X��Bq���dd��x6���n���iH'����|-�i���#�oa����u���t2I��Q�����]�Em��
���y��.�)E�	���Q _�h��Y��<��,�^�k��V���W*��������������[(����*eW�������"b2�s��
�s�!u_N��="��1F��{74�t?�.EU�����������<;�[oP����!�H
�����_t )��9�5w���e����[�v��W+�V�]m4����Z���r����a��m���r����az5������$�1�p����C��}N��)�Kgu���N����D�2�����>E�����Z�K�N��[����NJ�7�x'���+Fq�%Z���WL��C��S�.;�oHwg0������mG?�y�������}y�A������A��������L����FZN/���>���J�$�B��������V��o��G���o�����W���%�V��o�z��M�?���v�_OnS�;]& �T������z�A�����������i�LqF�8y1q�HG�����N�Q�,��)����{"��g��G#��t��J�q�J�HA$]���,�����7�9iB�B��W��� ������d�R��	]J73���u���P{����������N�_�4��A�������{�e[��b�2,}�}���s���.\�&�e����'�\������H��]+��;;C��'.��"����(�{��P/6�tw8H��_�Q`���	y�����X��h������HP�=NSr�R7�X���b���]$�n����H�_��
�Qct���_q���-����
;S�<��L��-�-�E'���1���7Qj�!!��+T����#����4�9����b�X3^L�1e�I8E��c>�$��$#����Q�2H��Ir�D��B9�]�����'�	�Tz>]�15BD���s�N�#��i���(z����K��:W��A���0;����	o����|�l��dzY��
<Qh����>������'��1������RB]��m`.W�&�����(S�g�(�J���Q���j)z�=~����������S�����w/�����X�+8	�Pn��z�6�{����������5��\��
�%�X�f����DsuY
�ZZ�+B���k����r��9��'�
Y�N�)�j8��y�$��(��?a�QC� Cz�^��r�`�u��������	�s#����������������r�~C�T^�3�}���N/�j�����[(��WS�n�n��������p�e9+�0lQ�_0�)_��Q�E�����X�1�f��"�c}������������vk��{�j<�k�z�v�����A/�� ����0��M���?����^{��=����k{��q>��:#�:#E��(%���{������|o`����3�B���t��dw�9/A@��4����b����\�2�3ja��e=w����(.�g�*��@~�}�/��`���$�GM;3oF���B��~]�eo����n�2��z�7I��#���qX`
������#��N�hur\�� ����p�9�����{��U�0����Iq5�s �14�7��������x��f�_�8�J�.(������SB�*�8�U����YI�AK��>�������~�E7Fm�e��H5�FK5$����k�D���y������J���a�8�N��Ar�/���xA��Y��@��~h�f��	�{.���nez�N�l���@��~'t�� d�m�"��2b��,#�i	�F7/�G/����t��r�O��5���%����I\x��gIJm���NF}���I�?���Rk����'.DByD8%z���4:�Z��kU�q���o
����oTs�$�R�HeX�C"i43�3��"^b�
��������4Kr�Q��������\����V���+'$����&k�!�pJ�w�q����p+�^��=�!w��Op	�W��������7S�����8$P
U�����b7���I�����j�pn����N��E�Q���^)���E"�����cX�@����Jk�u����*�-R��a��R���mV$����c�B��HB�9�i����!����;����S���
���[5��������r��[#v�N%�
�)���7��~s7'�T��aP�'wA����z���L��L��L�o:S���q
T:�o����n����s�������&�V]-z�t����w&n�����N_"x��a��?
6���|�^���t||����;��8��gwp��I*���L>���N�<{�wE~t��)Er�+
�OB���&�a�}�;C���F��KG�7w��;�8���z2I?V7V/7����������<AB��o�������pO����9<�Y�'N��}��z�S���y����/�p/F���F����/�>~�H.�������U����~�u�.t��Bw�.��1��\=�z�G��������%�^�H��������S������yI��_3�)�I'3��'�����d��u���H�ze�O!������W.����_�<����9C���)�5�gw��G�SG��|����KM��&������AW��A�O�@�P��&�xq�����L/�I2����/E��I�Y>��S��9��/��z}"�������fE0��"4��zp�G��-$Z�NIGyr����>��;8�_�}cz<Tb6�2���<0]��u{����f���%u�:�.�g����@C�|?94����FC}�@�4����L������P��SJ���;�������|��#����a�k��,�w�w�d�=�Xy6H��G�
^?L�
���E�
R���sC���nh�]����GJ�j����
v�F�Rf�VkPm��I�RiZ�f��o�9=V4�k����z�������}��6��h������>:�{������iW&�������~x�}���P1��N��p`%`��^J�ZN������5�Bv�p�f����������I��!����_�)D�e<���F_.F�\H)'��p[x������3�X�Te�� �-�$���Zc:���_K��X��/��Y���8���w�$���ZVg66���/���m�C����E��M�]��>&;C��R�d<�+o����d���5�z�Y*��	a�QJ���Mm���F�Of�P��p&�v)rZ����uE�u�@�O������Z&D=c��E���������9J�=�R��O��C����Z�V����P������!���a�3���Ns?J���;�^s�����f�������J�����}@w�z���lb���������\������s���Gw���w��E\���D�:������4��`$+�T�P���E�a�W��"6�|:V�r����r:;�<}q��e�*����� ��r^W��?KU�����UY�����"�N�U��RsPk4���v0�7Iz.���r.|����l5��-i����
/Z'R�heb��k���J���)'������k�a�_��������8.��oq���U��_-t��_�3��r�KcT�����|���EI6��(���E]����Jw�a	�G����<b���
����
��6�
�%���-���7�yE9e=p�@��k��������FI�|��4������j��^�,������(='sl�)N����@G����!7�������>������XON(3� �������C�����eI�F�(D�ez�~�U�)Pf�7���Q��Hf�������|9����t�)`�]%'.���`�~�	a����K��y�?qJ�(�A)N��~�3%�ZH(L����w��<}��i_K�w��c@.W$�f�zkD)�(z������9���KQ��7��`4g	G�F?��1Tm����pz��5���,<��,�Q�����^o���*�V�������,nm%o�r��.�� ���a��&�$�4���0e`������&L���k�*j�oQU'*I0U|&��
��F����-��LW~{���V6�
"T�������1��Sak<�h���������@�Z�6��e�x'sq��y���j#;�����W���>��o����8B�0#������dh@w����hC�8X RL�����I�0S\j�/4���\�3.�[0f��F����Sa�`�J���V�#�p�
��n��)���)�5��p�m+Qt8N�#�&�;�h4���|�S�/��i�U�A��{6\�tP��f�^���C�
�����\���'g���e��{mv����
���RA�/SP�m���DJ�#,�Jzs�1w�t�8W������-�7$n�c�0nw"�[q��R��L��

��3�r����>��	�I�9!�L2�mF�����)C(����sl9g���S��FG��$����kK�Z�Txt��x��m�H���t��~7����X���aD�=�r��Fq��
l6��E���;>�w8�G�&o�UyJ���S*�0�O�Y$g��4�kp_n!���d�?�X�����9.d����G���6 ������C��m1#$jB�y�! �R�.9�&��{�A�y�
��^������%����!j����Y2�\P]����'�k�f�@R��$2y�X���9��!h�?���d�����.�Zo�m��za���^�<��Y}��j
h�xV�7����
������-h=��eY;��6���I-�h,]�1��/�?������B��Q�'_O5_�t��h��O���S('�4(#9������S�y�]����-���������V��M�E������o,Q��wb���d$�&�[�>.R��<s���������������/�����o��Fv����R�k���Z��7��_)�@��JkgC�w���	U1�)�q��zk��U*��`��v����9u���"��$K��w������!��C1��P�����_+�"��4As<&/���s�/����D�QHU����[�������=d��E������g��iY�w�����X+H~{�|��<`�'I����I�����*`:!g���/�����1��S�z4N,�]J�"YS,��iL6��#���vh����"�6'�k�|s���j+�$u+�J2?�6U���AC���+���{X�T*?��1xjQ������++�Aq��!����@����>#7�,_��3��
�����%$5��&�84��{F����e2�o��/8��,�)���9��m������Mni��s<���ne��q�Hj��c�=��B�3`�����,����h�D�����7���}DYZyF�������l�"���,{?�����=�����[����������O��68��������g������Lo��V	:an�M��>�'��??:��z��o�_�]��!�Mg�����i�����3�T��F�7��z��~!��9��(y��a��v��IAi�~6�	#i9t�qM~�h����p�D���m����
�R�����+�p����+k5���#$4i������9���[��c�S��6w�mjj[�A)�F�w���D	{�JQM�q,�*����"K������%��e��U�K��R[V9���r����o�QT��^�5������As8��z����F��+6@W���������N3�U3W�L�d�u�D�)�'��V���7���;�-�����`�����W���l.=���^�����o��L�����{������{�]N��$�N\�k�~\��I�����{�8�L��w�)�S���'�g�D��f��pXJ�2E���T��-��b���)�����;!��tz9��,7���E'2t�,Lg���5L,FEO�x���k��������F������+��Bj��W���P���8�Z�E����M%�N1�'�f��+jj�%�z�F+/���'��!}�?�.�d����^������$:��h���QW��3�9#���q���x��bN�L�oj�oa��%Vj����x��5���KH��K��	q{�(&qYEM���\`c��������I�I2��K�	�W��J i���Uy�bb�$r����j��-�l����)Y�R����_&���c>J������|��b�)U��c=-������������4������
pJ��%�p���i���]1�9�9���]"�������m@�x>���I��/i���O����V����XRsG-]�Y;�r=�����m��&5nn��Dr@�%��`:I������=ej�N8�T��Dm�������u#P��6[-��R��_���zx;����j:��0_��_��B��=@Ls����I�����p���c�Q�� ��u
m��d������e}�z��8~�d`�j���#��������g1���@M:4<VJ���2��-���(���kX7�j���j�����
k��r���q�~�;Y�*[ �G����#�i�����s9�����~�������4k��������7�����C�����Txd!
����w�O~��M�t����4)�
�$V�-��zo/&4�{W+��Pw�������Q��+��v�?�hVYK���vB���l�\�K�(1tPo��;���u!g���4�����a��Sj����0�?~��h��@�M�4��IY���K��,F.��F�|�.���>��������4���p���}�#��G*�!�S��@��[�'��������ngI������I�F��e0�����T<���$q�R���[�
0�*��W�C��t���d�%����3uG�����'�B�aI������?��X��*�x������v9���.�0#�8���D�>�����r�7�X����N�%D�d�;���0�([��
Bx������HE��H��dz	4d��hq�R�[���I����2E�G
�;�1��������|�����iA��1x��m�oLSu��4i����_n�dz�|����Zm(�E��p�\Dyq�j����$���[;-�iZm���kN���d��Pt@T���h��J��\��6U�R�-(^�@Pf�L`��Cu�\-��P�R�4�co4AD�KS�z�w��SWf?����1���$_}Xw�Fp#6��]�qM.���n`��v��=��rX;r&���/�k32��k�S=]��hV�P�|��{7*������I��^��Z�f]���X�9�[f����n"1�����t�QH;���3����c���h+�&:��f� �LZ�<JG cV����oqr�v.}�O5�j�4J6�v��.p������
�h;c�!F��/l�uieH��x�5�z�������F��
����M�Q����	��_8A�##�bA$3R��������	��n%;C=�����Q"�
+�E�~�����{�_v��������8mp6=�dr�e�(��$��=��a	���$F�}���d#)r���.���<C���NU��H�m���%
O+�0SM�_E���Y>N��������;TY�s����		X��g���2|��D�:t�
�CR�O��������<�A������#������� I����QF=>J�.��	��[��!n<���{�2���`��cO��${����D���%EG22��&�{$���A%�I����fUn�nwO�;����Q�`���,����)�#�v��L�(��5*���H��IM(�,��!J�|�0u�#�1�4:�n��#��cJ��n7��&�/����N��={{:
�W>��}������1�����oH�$}tWcCW�5��bf�x��SK+���=��	q���,l��_-
���|'�e �v��j'0�u���xoi^���&{^�PPd��`n;�ZG@����r]�?�n�W�SG�}���l�\~���L���eY���������m$	�W�W ��#�E�w{^Y�mmtx%9�N����� %k2�����
���gvgV|��������CcX4�&��S&�k�G����7��&�h���������!6!��w(�%91������4��
��Um�v\��R[U�q�=�������:%�!�M^R�,�&���V�U���'5��9���X��= ��]�2w����^����Y�Y�B��]*^������z�TE�E0���{I�\ccC�X�4b4e�dF�
5^d�WY��
��8R�� �U|�����a����������|H��n`���� .��i�(}Q�$���/�rg>�&r�q>��i���'!k�,�J��H��*BP�vS�����)VA��G��SS�E�;T�i���<q��e���BZ�%y,���y��<Ib�X����G�q,�v�O�k��I(�`I����)I����3J��Z���xv#�rf���^���Y�������P�$�9����*�C�e)d
���$gQUKu��a������m������>RUI�'!�-���UK��4[d��j��\��&�f��b	
���Au
�	�@���(Xj&;��4��K�F	�J(	���.?�/�/h�{�B^��.������������P%vw�a��i<��W3Fl%�hDbT.�]IY`���Y��[e9�����h�������@�U�U	��k��09�.�Vi��.�u��W~��y���^�ZC4�B���@L+�8@��(Pu{]�j������C'����n���8a��eDTc��$��/������P��Jh_����-���'n��������aZ�
	��I�d��l��M>�=��WHO�F�����=�]��������s�v�QF]Y��% 'z�������)�G����g!&�!���y�{Ky����*F�j��j�-dj����/���D���q�:��q��
mL4�����~�k�;���^���V�7����>�E�����R�jv}��b���k�,���
@b\SCF�F�
�_Yp�����2�V&pE�^�K+�����F,��������U��z�����(=����1�4�z��9^��h��u���Y���[�O�B�)2J������$}��
Zb�����I
x���+*�����a
��Gb�=7�����!�/8B����"�> �d/��#�����:�.�����n�������l��\���� ���}��'�H}
�8u��n��4��s�������:�a�����=jv��a����o��v����u�d�����ju�37���w��W���9!�XYnb����]dsb������������� HB��O���=��B� �����i��O���2�-����>{w)$���4�H���#�q>-�<��a4t7�EnE��lj����r��0J�+�P�'X�&�K3"����!��K���������� ��{�A]�n��)hF����L�d��H��
�d�����j1�����qKysDd"]�����;�rW0bX,���;G��d5U����q	�I�,�@$�GB�Z<N��i����H�V�iO����-o<{������t~H�!��5b�}�c����� �P�����p$�;���,�r�?�����I��e�*�)}g�.��������������������� ����������o�7�x'�����t��Og�?=��������?��W?���_�����c��\��6����i�q���F)h0/�xx m>������4��&�Zr����:[���j�Z�J���}�������c[�Z��lu(������k���S��C3C��4���� �gM��Y�����a���U8[�?��}�n�QC�e���h��KE�l��8��a�
�nD-��g��-��lEQM,SN
�����F	�����*���e�P!Q.��N�3t���;����V�I�V������>/���\8������$s���E��n/Z���	g �TT�"|��K����6^~��"������+JO�E���fB
�7cIY_����^vG�_���E\Q� ����fkR(~�1��xS?2l�,&�q���	�L=�<��C"�����a D� $J�A���<�*{�X�������4�@�5���h�bYq$����W�-�Z�h��F���9�����&j@�DQL�6���ts3��`��Q`��1�*�����_M�OS�my���Q��\:�Zf������U��n�G���������������������"�������K'+
T0T���Ks�(���B��2��$�7
��V:�]�id2��z��v��`�!1����E�;l	�j"f��5��������WR�'D�n{��}�V=R���T��FH�����]�Y�/�`(��x�c<��Ki-
�YB�?����~����P��� bB�<��8���?�����p~��w�9dev�����?�gU����z�������g��JE�������.����H�t�E������C�(`���J�6�<;�Z�6��I���f�F#��	P�u%k����v;������N���f�of��k+�<y�P?��a�A:�8�x��7�����m]�����c�k�����tr�p.�W�
����~urq�mh����0�8�
�q���_�+\]������4�rM���]4�:������HR���P}x������:��:��l6����M?���2��%h�d��|]
�@��)y�����g�Nj�=X��n����Z=�#o^g����;��&�{UG��*F��?������L���p,@����Ss4�[��U+t)�����]���x���|{�����[���:�2�._�Bl2w}��4��_���@N8�K
��-�������x����qgw{���������__�� ���������-o��������V�oM�|��zx��������������j]����1*���8�GM�X��`6])_Q��x��)��;�T���?��>�����g93e��4�P�C����
S�4? ��n��zH!*)�qx Z�z��kGEs�C�l��[�����^NU�(�0����ct|0���esW���k[��+�6����Od&��J��^
�������%�w�:�>||szr�2]7�D���	�vn@,4�T�9�B#��;l�N�my�!o��#��o @,*v���Q��dhS���������u�R#�l�������c�K���b4�P1�����m������"e��r�U�7�!5���)�/�
j|-7��a�k���
�}�{9�"���w���XG�v2�e(X��Z0�H���IY14�����c\s�0��<D�6�4|C�Zya��'�b`�.����};�D_b;�7����
�����*�8R�1������nw���<��ghw���v��a�*_���1��{���^���U6b�*�������`���([��=H�!�����=�N
�
����'�	�"> ���� f���Rg��o8�f�m�z�]��A ������!I{"����yr��������$5���\w�z��=A���u��5�
��R���7���I�- �-
O�k$��Y������
>�S��|��"B|�����,w`�7�X�inBg|��v��Vty�U���O�C�R�e���CDP����5T%�&�K-��ouz#o8G��i5=;h���#�P����:�������b%�a��?�
(�����{8����g'��9������%�To�R��]��?���wv�������N�����Bh��|X���
�����g[[D���s����d�]�E�-�z'�����"��ee#]���gpx��Je��������q6�U'����b�w"K_,�����j��9\.C�����U�k��$�_W��O���D����*���e�T���g�r��c�WFyV�������6�DOL"}��3��4S)3�N�N6�]�*�����*K�Ly����Ec��� ovD�����n����M�7@���������1��R�,4"2S�e�[M�(�����<��Q�F�����,*%�����M����>�Zte#����2V��#S�h�VW���?��HI�m$%b���A�/������^���"|g��h�(���	yH�a�>P��{�\sf7����VzBS����J��������WT����1T���D���`�p�:i�|!0��*���4��YMw�k������1����������v��h�H��2N��F%��.}��5�S7 �e�U�Y��`d��Zn��[�'����
g�&�um�����j�1j�6�@�_�e���,�V���
��{M,"cb���|��U���?���!���U�|O��h,��kZ���g��T=�,^c&�<|���5?F7(�M@ ���
����e�)��f5q��o����R��+r�4��9jD9g��a�sKr����dlW�as����=jv<��������N�c��Z�^n��LI��!	*���� ���b���_��k�s�*6��[��<��w� ��
�W����/�nx�>!M)_�P]���P�0�����<U����FWh�l\�DF��l
���qp�v�k{M��>�S��\\HC��y����o/N���:�$�$���i�������on�����{��"�q�C�?Y�2H�����XP{pl�GF=���n�3�r�"�H�^K����ns�����uL�
b��%e�-��)�n��	��6�1��v�q/��� ~Q:��UXeXT����N����Rp�#w<�����3��H�9YGa�S8B�Y�,HX:�X�4n��`�{[��:m4��_�B*.Y�6�~�����#���h�����dsx�,��G���}����k@�c��`�|�Fat�	s
h��^p���
:�bje�k�X���G������d�-!���]D�s�jpv����x�?�-�,^k<(�#��I����b2^��Pf��q��l�!'����d�>��p����I��'��w'W�����k6�cZ��0�Ru�������p�1�tx�s��y�������j@zoV��
�����
��.�?�������.��O58^p=��:2���1��t)�-u��z�k�uq<^��t;~���%$��r)I�$2�h��bKU�vv`P>,B�~.����0��\(3�d�	S���/Mv�Bv��L�!����L���/��J��Q���Te��T�����Q����nx;YE���u10hq4*�Q�Gi������a>����&2��>�&q�`C	���A*)���DZp�^�3�7�fDt���S���]#��
���Y��k�	k�@�V���7���&k�����B������1cpx1=����]W�i�C����"��K�~0����p���&�����]�5�IX��h�i���v<]�"2&7���"��W��KI�u�Y�v1�W1�_�������pV|�R!>��v8n�1l�j#q�;A����g>7������Y��~�p�p�����)B~���bw��J��LT�b���s�D�$�cZ��pV�L	����H��-�����I�+%����t��S�F�����#���pp'����2JD�q1�;jT-�{�fub�7�Y�����w��^�^��������K�D�]�C.��O�O(9F1�
f�����y��c��\��_�p.`U�-�>�x�����z����������Z��:
��mg����#�����
����y�O��8�DH�e�&��Y�`J�.M ������v��������0JJ���n��u2�����%b��v�j����F��SG��wj���7�qp�7�E���+��Z�S"3^���5\�T��q"�zm
�N�	����_��h<���������O�����9��|E���D�������t�����5��9�c�wz���sk����cwjQ��]q3�Sv^�C�Zytq�_�G#V��m:��;g�`-�s!0�@	����4���HCy��,�%`M7��-��W�%�?����x8�'��8a����A�9BP���w��9�L�ut�t�������
o��`�w9�qA��&�8k*9gl���$JI�6�����`	�����j�>�������{g�`�zqm�"��*�(��/bS��(�%���,�� �q��!��kc�@�.~��.d��zo��C�6��cO���|0������8A��VT'��w���3��grN4{7��3��������5���^������4j���F6��5��4j�����
���	.Z�e�8 I��2<}1��B��d��H�R�'���3L{��������:�	U?��0�~C>�OUE/]4y�"�
*DaMK����7�(
��;����D9��i����H,o�Vkw�z}���r��l�xm�e�J��|�yA|q�sQ���M[MHwY��XFlS5@3E��E�U2q��n��)��
��aK;���Mo�����������B�����UJU��xOW�`��2	��������j3X����*��^@��$�xvFM��s���r�=���j#�������o�ts�{#]�u�u�V]�'������UH����$��S��I��\�2a
�,#��j���W��;j�j����=���
���+�@\i�U�E���R�\p����b��w�T�b�I+�$����8\�k�"��
0�q��?�9�:����)�Ccd����
'h���X����Q�L����v�!K�YD��<��f'p�����l��i�:�kl�,��"2%���]��i�l��1�X���x9���\~h����3�2�lN6����Ec�^f�A(Y����i�iiY5g����]�t������Y[^Y��}E�����[��\�����U����AQ�U��]��
qg�{-[����Y��f��-������F���sC��;�P�X�	��N4_��y��W��	���-d�����y�����N����q$�>��9_bIt��@�u
�X�r+�YG>bU���d+�
��j�s�>�W�G+��r+���+��'[m�������W�RLw�a�v�n�n
k��4Z� ���X��+Auuv�;�[1���#���b������fA���	�mfxthN�!o�\��l��1��7@�6]�����]������L��L��VT���,L������b]T|���`���	h}q�F��4���?��������V�����3�������f4�/i��������9P�
�5��nS���5��Sw�
[��u?_M|6m�L�� xa�����LU�\#��ZM<�+^�c�:ld#������i�%ns����D�G`p�C���$l~DK�k,�/�Y��JP�f���pa�����R�([�4V���G	%$�T�	���,#C�4���4��Q�V�u��8�p�I�j-��K�Cr��8"�!��0@w0N{�P��fG>#U��h�w�`�a��B��%�Y�b�Y0���sf�vO���c������)�u9�������8�x�>��Q�M��f�8O:Ry�S
H���r�7q��.���uL7�4�[�e������������ ��q��t>2Z+`�o�h0����:��M�~��H�6��e����L�2�f�]�!����qK���C7|����LL��C/��}t���Z�X�XXM>�zL[��7g������S�58�x;@|���G�x�
@� ��&��X�a*)5������v���v���u�^}�����R�)�� [%�/n�l�%�1��j�%���#RVC.`����D����5��7���{���`����Y-7L����/��&�����)�f�{�������IM����,�e[
��P������B�C����[	�)�$�� �4��rH��7����/Bn(����l�7��fmjA��L���4a�Al�Q�PL�[&��m_Nj��	I�#<zEp�_��o	�i���{�������+;�I�����Y&�AFW[Y���_��h������N<,&9��I�*
I�^�8W�&���]�����s���^Y���U 4�c����^��]�������������
d����V��+���H�������u+�����]&I
�����r��!� $��~b4h��L���a�r�A�a���
����L����wN�*��a0
cZ3Y3	��io&�p7����v��J�<�7#�����xe�a6/���3#���,�OO 2�0�������}�t8<m����E�:u�B�$
xi��<a.i��SX2Z��l�li���q4�++�)>��7%�,��n�X��������lRm���U�2?~��q+�G����A����,��N������oN/�~\����S�,��u�G��o���a��`^�`�-v"/�T��&�%��v�z�����[�W��4��FV\G��.��6��P�g�$=���+qH)����Gl-I$�����>�;��of��TH����"�X�U`�����
������
S����E����p�����J���Q�qp�@*�G[
/Y���><?>�<~Y6)����:��.�e��I@�\1�d-�)w��Q�kn0�l���2T��d�y6��D�o?�������k-�@��~�N�I��!�qPH
;J�&`}����f#V�Te*w!�c4@1?f���|X�����_�����4�����8�:�*������S�O����N�OK��x������F����/�x��p���1biVO)4���<V
����S�J�a����.�C. ���vO�x�t��^3�}j�buD��l��]Fr��s�����]Gt/�%g��+]��X�wOC�k:��Z^8^BBc+
(#i���<o���&Cx���s�!�<�T��Z#}MV��8�k����8^� ��m����Xh�Y�~��A{�Q������3
����������A����#0�YJ��J����d4�c��3�Y%8_��Eb�A�f��0����fw;0Sw��/��p.�Kw��T{R`$_��/�m�PW�����(X�<BrH�Q[�K�i�!f��a�2�r�;��"����Hp(�]|������q�r���z�;�����j�e��$���5.�v��R����q�m�I�uN����!���G"��������d�<�WY���F��20�4����4�=��Z��Ep��}�d���!�C
R����RBpP$.c������������
���{�}W����S������^
��{�XAwU����bG��l��[���;B��]������)�G��)��_@(
����0���N���|��ee�StJ���.�i��H���.^�;�	3kB{��iA��H����A��N�S5A��T��,T1��*�F'��#{r���������
((}%E9�#����Zt���_L_��������������Z�k���hX��FG�l���+��bm������W��L���-f�FM�9B�)�����!X�xi6oX��P��KXX����:B��8"S+h��g�i�$8<?9JH�@�� a�A4������:�����F���X-���bE.��H��2S[��U
�P=��Hs��z��,����V�����6�_^�<:8:���0���.��Sb�`����`����&���
M�KK�}�R������9�"U�S /�a�$
2��E�����_��������z���C�ZO�8��h*�cjB^f�����7�����5�A��*��;9�V�s@/���Mi6	~M#�6���)��W����)�K�)S�%���w���@4�r$�7t���l�.�BQC=�
���L.���
�'�4�r' �>X��q��Y�5��L�ed	J��^	�m4�o��/�8������R%p�4pe���WF�!�`�e\�bS�u�K`@��28�~�D�f]��ov;_f�9�{�Riy(c���������N8�MaT������Q���O5
E�oa�w�w4���"�G���h���bWt���������e��[��[��=n�t��/����u+������W<�D�!�^Zz^��uF�z�z��{�N� �C�����-��V9�*���#ZzEeM(�A��MY�^��<��i���C&�1p��F�-z��B����j�E�_W�,��Qt��c�$]�)��K���#
@9Y}���R�b�x��H����Z��<�5��bk[k]�]Q����s�����bm��4���S���)dq�BK�	�����X������D;���h>�����)���F�!L-�����o�j�Ot|1�Tp)�y��b�a�R�d��1e]�����/��c2RFdR�~E����p�����c`
B^d�y���N���L�(:���e��})h��y���� ������R���z�q,����dj6�<*xs�^_~<?:��K������,[T����g��������'2u�����-�����$�[+Z���N����[����f��~V�e*"������������Z�N����^�-8�L�N+S1�h;����8���j	��GT{t<��K8�0XL��<��������^QQ��l�^#u�N�Vk��X�z�U�����j��?]\S�R�;|mJ5����G`��yi�y����_d�n"V8+au>����]Az��*�T��D*��p������H����Ti lcvSp����)�HS�C<�LG_
|�C����������3f���I��/cbp��)���]w{%art�A`�'���c51p�C��9����zp{=���D�jx� <��n~��m���p� d�2gt���	����q��S�ul8��^�]���L��N�t-��A	�BsS�Ev>?��gGB:^�(J���>|����?�&�C�4a9�}��f�����dJG���y0u0)}�_FD����h5aC�������`�U�+�z�	s�<���8Qtl�*�e�9�:��1�,����o{�����5%����q68�q������^���V��nmGL�����2�8Xg�"a(�s^%�5E!�Y�u@c�/1����)�;��h8A{(�E���7��F�sZ��������(��0�:|�����,�[�&d�26��[��i���2#�����9�����pQc�7`0$R�^��G`
/��J �nPET�zBO����M������������J���C���.�W�9>9������u�=l��2���(t)�v�0�(}���^��'k��6����t�o$y:�KI��9D*u���<��:�����o��(�������b�kC�>'3'%��T��
u���yj���tmT���WZ��.EEE67��9�-'��i�TJ���S��6h�'���� ��{(�6����?�D��4Di"�m��ZW�w�2]=��f��[���G��G?|�89������c������G������~�A��� R��Fjf(�Y����:b�j��(#�L��W��$]���9���!�N7�w�7�v�h>*~���N�j1a���f�@�tf�:A)�6�t�/�ZHE8�($d�$D�����P�����g�eP���f�.��T?V����`qR�e
*�z�lUY)j?e������'usD�d-���`d�����;����p?�~:����Bp>�:Z���q��0�[?P�;l#H�=�\V$��1@��l���3���4�M��\f��o��$`r��W�|���w���3
���@�v����gt��{�R�s�����vMwCpSU-{��jI�+������?�pqu������|T��~�z>`��q�ib��n�U�1t0���nVm##	jI(��X0M�J�����pe��{�������K�f������}�SiXPiHA���������r�:���Y�.-^��'I%�����6�G��;�!�I�..��%���!��u(�l���I������ �D;�g���$Z���KqY$�
tx4��!�-BX��pL`�c|X|�~��	�E,h��tc��Qz
6o�ka�|������S�/�o!K��pa9��	�q9i����m���Q�6���N�Q�����Z�T+K2]��{
/SR���5����qo��UK�Z>@
�0���4�*�����R�SdxQ�s�j���?	�{Bv��h>Qn�b�bbb	R�@�.!/\���� 6X������v��Z#�l ��x6��f��B�V�Wy��K��\���d]�qP������6d-�!H���*$���u��"���4���"3#5���9�-��KR7
:0�_�D�X�	�gw��Q����Z��u��Z-o=a6��O�����:![=b ������0��t���7L&N���Q4������C�w\��.��q1ip�&�Ui��5,�����,�"GY�^������t���-
��Q?�X)����j��#�����g`�����XkkM&����#G�Q�1����|�1t�m�mo����R��J�X������Y��=7\~�M�S�K����4W��K���.�!MN�����������F�GN�k���.m��q���w68��?��p���oVZ�oV�����SFqKh���tU)�b�O9��8���X��3�����5Fn�����^��|�7,�kqK��!�.�("3��1���`:� �(���G��dX��	k���?�5�w�-)	�(Fg8_-�3N��-�9�W��x�	VS'�|X����u+8'���7go��EXJ=`����rk��w�9F1���}\�����{.Qp3�z��cY_�gj����^������I�$�hSn
�d��a�t�����T����������������O�+{�r��ZC^�]�gtv�����>�|�MQ[)��+y�*���IW0u�Op]��P�h�+������R��=���,�/��R��"����?�44	��~�B�Q�����+R�+fH^E4��9���<ya�?b0r�EI�Xc��X�|I���&�HZ���� ,;�D������~u"�������sL�����#���MQaXH+�"=�6i`0����1���~
���\�o�@�-m^�+�����|�������	;���=,�{��P����5%+�&�/1�17�]10��|$F5c���*�4���)�������r0co*~�6Q�[��[�h7�G�(����IO��x[4�j�r���3�#�g������v��bj��+'mux����P���=�����8��DSUY�'ZW����S�J��u��N(
	X�N�(h�&�B�4S��x-����Pq c�hK�+v�����3�N����z)������3�5���5~���o-����5�X��)���cS�t�n�9�6��&X�Ux�>�^{wW�WW47�����n��-�'|��������L;C��8��]���x)K[/���t�#��J�N;�F������k��D3�����~0\F��h	J;�K�c��N��S�7SX]!��v���.I��KQ�i�VBfP�]�����a�����=r�-�/)0E�L��RxD#?�d-d2E�5B{*:��i����{����q�/�UE;������������T�U`�D�,w�2�8�����3,[�f���d����� mH\�?	�	�����/�h�����5Q����?���0s���))Z��@x����!!|�?�w;��b�Y^j��A�;��!����9v���nmaT�����.!���lP�+��S��J�}8y%	����!Db�{M�I�g�+�z�n_k�f��kc�-�J�d��Chu�U�K������O�;@&�%����#H�'i��uds�W���ge>N�3�
�[�"�W��fb��vA�3�0�~�~��0�L�e|ID�#5]T�|��*���~�8y.W��r�w@��������J�m���[�Am*��o����
�
�N��}�<�J<��M�IKzF���bG�OF��9]�3�?��n�A��@[��b5�"�Z���5���0{�g�Q�^�i7��dJ���|:��;j��f�����Q�k���m
`���v��A��^c��/��A?/CA�@)"X�B�_��9G�l�k
��O����Y%*�����Y���2������R9��e��q�G��86�T�/K�����of����T*����I�*	�g��
"r���p������B���RS	�������N��rk���7l����aJ;X�VS���'�ok	�ae����q���ZSx�^Y7h�#��V"�Y���*�`�&� �-i���r:�e|����~�l0	f���bL��zf�S��"�U�"���E���A2��e02G���&[y�&�]��s��+�nU�KX_�g(|�"�7�����B!����w����&2���u�����"n�o�5
(?�TSi�����w[iw�'n��O�NH�e�������\��q9.��z6k"�e����Z�.7C�/�-+���`�dQ(�A�A��}�p+s��g�d�2��������R2r��n/p�Z�
z�����Q��-j+>b�J��^�}���.o}�, ������V��7�/��dK�J&YC�BU�����mS�J����p���l��Sc�$�\oY�F����4����-��"�\�j���OZ=J&��������9���mm�J$Y#��Ib<���I�N�����Z
�6�q�0P������S#.Z\���K/o,	����?Uuh��L�h��!�����.B������yj��K,���l&����^�mJda4�����S����e���
3���N��3�����%S��B��O������(s�pr��CD��C��t�n����-��l�F��k��/j��Q�������!�#�`��c����=����'�����Nk��\V�o��E���I�����7)��C"/�8��@�6?I��	�J`�rv�T�)�;��'�����������R�@�,� "
-������^��*Z��$
1���U���_�|lY�D.^�K1��p4��+��dT�6)�6(�\��F�����^��
��n�~�����fMR
Y�fP�&����L����vdQ��{�z��<����s��x������������):
1�?]S�(�3	���1�K5���)��n�+n�
��R[Iu:S����'������-���d�[
�yi�6NN�Xr�}�t@���N��F�!>����[(�$���6P|9u�I�i�wp���k�F�;{�=��aD������t/� 5	�#QJL;9����*O!�"_ :����@�!�_ap0���\^�}(��3���fN�G���e�B|�E�,/�GlFC�hN)��{.���R@�s=,�b���h��&�
*�b(h��M�*�r���T�����h,=�R��9�������S����q��5�I;-2{]cIv��1�q���6"c��7Ky���<�"�����]��mkG�S���W9^W���xx
�D|����W�@�e8\XQ�PkH��#xme����'g��5	�*�	~��)�:���39��������
`ST�Ib�fLp�Q-����/�����m~+����4vR��U��v�h#c�������O���9�=�_/^���������X��'�'g@
���2

TD�Ist�h����	�ZH��#�����D+tww�&a�Ln���<�+�G�yI�O�������V�7j;���j���p;~�ht�R5TxQ
,Cae�����B�=���j���h�.{Hj�.&~m���$Y��IG�2��E�L�
"di����J���^�y�Mbk))iK��_���U2r��\);���i�������'tM����^���n�JW8sCV+��R�0�bS��z���KW�|�W����t����q>`�QNIS,���(�SCW��`
��	���Qw�����eDv�����^�e�����S?���+�i�k�m�0��G�V�4F��_��|�����S[	��L���L����i5CYY�_K���A�����r��^�� o��v����Q���w��5�O�����v���S>��x\��^gh����l#���C{�jG�N�(>�;��Y�Kc���K64���1���.����M3��0��P=t���k��cr�%�A]����z������qe0�J��
)%E��(o�c��&G����n
���
�/3�(X�Ad��=|f��eh�U����n��e���Fn����p�^�	r�Z6���6����%4�@K
G��%	��_/
���H�
�?�J!MGQ�O��?`��^M�&�*	��XE�1������,�q� ����G�����id�:
�����5�, 1���t@�j���`(^7��?���y�tC��'�`�.� ����YM�R�UhD)�>���Tc�o��k�P����}��o�.~U�d^g�?�5��Z��i@E%����fq����[)6��Ss�Z}{���?���������y�<�?���������y�<�?���������y�<�?���������y�<�?��������?�����
#72Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 7:53 PM Thomas Munro <thomas.munro@gmail.com> wrote:

3. Recognise UNDO_SHARED record set boundaries differently. Whereas
undolog.c recognises transaction boundaries automatically for the
other categories (UNDO_PERMANENT, UNDO_UNLOGGED, UNDO_TEMP), for
UNDO_SHARED the

... set of records inserted in between calls to
BeginUndoRecordInsert() and FinishUndoRecordInsert() calls is
eventually discarded as a unit, and the rm_undo_status() callback for
the calling AM decides when that is allowed. In contrast, for the
other categories there may be records from any number undo-aware AMs
that are entirely unaware of each other and they must all be discarded
together if the transaction commits and becomes all visible, so
undolog.c automatically manages the boundaries to make that work when
inserting.

--
Thomas Munro
https://enterprisedb.com

#73Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Another small change/review: the function UndoLogGetNextInsertPtr()
previously took a transaction ID, but I'm not sure if that made sense,
I need to think about it some more.

The changes you have made related to UndoLogGetNextInsertPtr() doesn't
seem correct to me.

@@ -854,7 +854,9 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
  * has already started in this log then lets re-fetch the undo
  * record.
  */
- next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+ next_insert = UndoLogGetNextInsertPtr(slot->logno);
+
+ /* TODO this can't happen */
  if (!UndoRecPtrIsValid(next_insert))

I think this is a possible case. Say while the discard worker tries
to register the rollback request from some log and after it fetches
the undo record corresponding to start location in this function,
another backend adds the new transaction undo. The same is mentioned
in comments as well. Can you explain what makes you think that this
can't happen? If we don't want to pass the xid to
UndoLogGetNextInsertPtr, then I think we need to get the insert
location before fetching the record. I will think more on it to see
if there is any other problem with the same.

2.
@@ -167,25 +205,14 @@ UndoDiscardOneLog(UndoLogSlot *slot,
TransactionId xmin, bool *hibernate)
+ if (!TransactionIdIsValid(wait_xid) && !pending_abort)
  {
  UndoRecPtr next_insert = InvalidUndoRecPtr;
- /*
- * If more undo has been inserted since we checked last, then
- * we can process that as well.
- */
- next_insert = UndoLogGetNextInsertPtr(logno, undoxid);
- if (!UndoRecPtrIsValid(next_insert))
- continue;
+ next_insert = UndoLogGetNextInsertPtr(logno);

This change is also not safe. It can lead to discarding the undo of
some random transaction because a new undo records from some other
transaction would have been added since we last fetched the undo
record. This can be fixed by just removing the call to
UndoLogGetNextInsertPtr. I have done so in undoprocessing branch and
added the comment as well.

I think the common problem with the above changes is that it assumes
that new undo can't be added to undo logs while discard worker is
processing them.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#74Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#73)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 4, 2019 at 5:23 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Another small change/review: the function UndoLogGetNextInsertPtr()
previously took a transaction ID, but I'm not sure if that made sense,
I need to think about it some more.

The changes you have made related to UndoLogGetNextInsertPtr() doesn't
seem correct to me.

@@ -854,7 +854,9 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
* has already started in this log then lets re-fetch the undo
* record.
*/
- next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+ next_insert = UndoLogGetNextInsertPtr(slot->logno);
+
+ /* TODO this can't happen */
if (!UndoRecPtrIsValid(next_insert))

I think this is a possible case. Say while the discard worker tries
to register the rollback request from some log and after it fetches
the undo record corresponding to start location in this function,
another backend adds the new transaction undo. The same is mentioned
in comments as well. Can you explain what makes you think that this
can't happen? If we don't want to pass the xid to
UndoLogGetNextInsertPtr, then I think we need to get the insert
location before fetching the record. I will think more on it to see
if there is any other problem with the same.

Pushed the fixed on above lines in the undoprocessing branch. It will
be available in the next set of patches we post.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#75Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#74)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 5, 2019 at 5:20 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 4, 2019 at 5:23 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Another small change/review: the function UndoLogGetNextInsertPtr()
previously took a transaction ID, but I'm not sure if that made sense,
I need to think about it some more.

The changes you have made related to UndoLogGetNextInsertPtr() doesn't
seem correct to me.

@@ -854,7 +854,9 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
* has already started in this log then lets re-fetch the undo
* record.
*/
- next_insert = UndoLogGetNextInsertPtr(slot->logno, uur->uur_xid);
+ next_insert = UndoLogGetNextInsertPtr(slot->logno);
+
+ /* TODO this can't happen */
if (!UndoRecPtrIsValid(next_insert))

I think this is a possible case. Say while the discard worker tries
to register the rollback request from some log and after it fetches
the undo record corresponding to start location in this function,
another backend adds the new transaction undo. The same is mentioned
in comments as well. Can you explain what makes you think that this
can't happen? If we don't want to pass the xid to
UndoLogGetNextInsertPtr, then I think we need to get the insert
location before fetching the record. I will think more on it to see
if there is any other problem with the same.

Pushed the fixed on above lines in the undoprocessing branch.

Just in case anyone wants to look at the undoprocessing branch, it is
available at https://github.com/EnterpriseDB/zheap/tree/undoprocessing

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#76Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#68)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jun 25, 2019 at 4:00 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

Fair enough. I have implemented it based on next_retry_at and use
constant time 10s for the next retry. I have used define instead of a
GUC as all the other constants for similar things are defined as of
now. One thing to note is that we want the linger time (defined as
UNDO_WORKER_LINGER_MS) for a undo worker to be more than failure retry
time (defined as UNDO_FAILURE_RETRY_DELAY_MS) as, otherwise, the undo
worker can exit before retrying the failed requests.

Uh, I think we want exactly the opposite. We want the workers to exit
before retrying, so that there's a chance for other databases to get
processed, I think. Am I confused?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#77Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#71)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

[ new patches ]

I took a look at 0012 today, Amit's patch for extending the binary
heap machinery, and 0013, Amit's patch for "Infrastructure to register
and fetch undo action requests."

I don't think that binaryheap_allocate_shm() is a good design. It
presupposes that we want to store the binary heap as its own chunk of
shared memory allocated via ShmemInitStruct(), but we might want to do
something else, like embed in another structure, store it in a DSM or
DSA, etc., and this function can't do any of that. I think we should
have something more like:

extern Size binaryheap_size(int capacity);
extern void binaryheap_initialize(binaryheap *, int capacity,
binaryheap_comparator compare, void *arg);

Then the caller can do something like:

sz = binaryheap_size(capacity);
bh = ShmemInitStruct(name, sz, &found);
if (!found)
binaryheap_initialize(bh, capacity, comparator, whatever);

If it wants to get the memory in some other way, it just needs to
initialize bh differently; the rest is the same. Note that there is
no need, in this design, for binaryheap_size/initialize to make use of
"shared" memory. They could equally well be used on backend-local
memory. They do not need to care. You just provide the memory, and
they do their thing.

I wasn't very happy about binaryheap_nth(), binaryheap_remove_nth(),
and binaryheap_remove_nth_unordered() and started looking at how they
are used to try to see if there might be a better way. That led me to
look at 0013. Unfortunately, I find it really hard to understand what
this code is actually doing. There's a lot of redundant and
badly-written stuff in here. As a general principle, if you have two
or three data structures of some particular type, you don't write a
separate family of functions for manipulating each one. You write one
function for each operation, and you pass the particular copy of the
data structure with which you are working as an argument.

In the lengthy section of macros definitions at the top of
undorequest.c, we have macros InitXidQueue, XidQueueIsEmpty,
GetXidQueueSize, GetXidQueueElem, GetXidQueueTopElem,
GetXidQueueNthElem, and SetXidQueueElem. Several of these are used in
only one place or are not used anywhere at all; those should be
removed altogether and inlined into the single call site if there is
one. Now, then after this, there is a matching set of macros,
InitSizeQueue, SizeQueueIsEmpty, GetSizeQueueSize, GetSizeQueueElem,
GetSizeQueueTopElem, GetSizeQueueNthElem, and SetSizeQueueElem. Many
of these macros are exactly the same as the previous set of macros
except that they operate on a different queue, which as I mentioned in
the previous paragraph, is not a good design. It leads to extensive
code duplication.

Look, for example, at RemoveOldElemsFromSizeQueue and
RemoveOldElemsFromXidQueue. They are basically identical except for
s/Size/Xid/g and s/SIZE/XID/g, but you can't unify them easily because
they are calling different functions. However, if you didn't have one
function called GetSizeQueueSize and another called GetXidQueueSize,
but just had a pointer to the relevant binary heap, then both
functions could just call binaryheap_empty() on it, which would be
better style, use fewer macros, generate less machine code, and be
easier to read. Ideally, you'd get to the point where you could just
have one function rather than two, and pass the queue upon which it
should operate as an argument. There seems to be a good deal of this
kind of duplication in this file and it really needs to be cleaned up.

Now, one objection to the above line of attack is the different queues
actually contain different types of elements. Apparently, the XID
queue contains elements of type UndoXidQueue and the size queue
contains elements of type UndoSizeQueue. It is worth noting here that
these are bad type names, because they sound like they are describing
a type of queue, but it seems that they are actually describing an
element in the queue. However, there are two larger problems:

1. I don't think we should have three different kinds of objects for
each of the three different queues. It seems like it would be much
simpler and easier to just have one kind of object that stores all the
information we need (full_xid, start_urec_ptr, dbid, request_size,
next_retry_at, error_ocurred_at) and use that everywhere. You could
object that this would increase the storage space requirement, but it
wouldn't be enough to make any real difference and it probably would
be well worth it for the avoidance of complexity.

2. However, I don't think we should have a separate request object for
each queue anyway. We should insert pointers to the same objects in
all the relevant queue (either size + XID, or else error). So instead
of having three sets of objects, one for each queue, we'd just have
one set of objects and point to them with as many as two pointers.
We'd therefore need LESS memory than we're using today, because we
wouldn't have separate arrays for XID, size, and error queue elements.

In fact, it seems to me that we shouldn't have any such thing as
"queue entries" at all. The queues should just be pointing to
RollbackHashEntry *, and we should add all the fields there that are
present in any of the "queue entry" structures. This would use less
memory still.

I also think we should be using simplehash rather than dynahash. I'm
not sure that I would really say that simplehash is "simple," but it
does have a nicer API and simpler memory management. There's just a
big old block of memory, and there's no incremental allocation. That
keeps things simple for the code that wants to go through the queues
and removing dangling pointers. I think that the way this should work
is that each RollbackHashEntry * should contain a field "bool active."
Then:

1. When we pull an item out of one of the binary heaps, we check the
active flag. If it's clear, we ignore the entry and pull the next
item. If it's set, we clear the flag and process the item, so that
if it's subsequently pulled from the other queue it will be ignored.

2. If a binary heap is full when we need to insert into it, we can
iterate over all of the elements and throw away any that are !active.
They've already been dequeued and processed from some other queue, so
they're not "really" in this queue any more, even though we haven't
gone to the trouble of actually kicking them out yet.

On another note, UNDO_PEEK_DEPTH is bogus. It's used in UndoGetWork()
and it passes the depth argument down to GetRollbackHashKeyFromQueue,
which then does binaryheap_nth() on the relevant queue. Note that
this function is another places that is ends up duplicating code
because of the questionable decision to have separate types of queue
entries for each different queue; otherwise, it could probably just
take the binary heap into which it's peeking as an argument instead of
having three different cases. But that's not the main point here. The
main point is that it calls a function for whichever type of queue
we've got and gets some kind of queue entry using binaryheap_nth().
But binaryheap_nth(whatever, 2) does not give you the third-smallest
element in the binary heap. It gives you the third entry in the
array, which may or may not have the heap property, but even if it
does, the third element could be huge. Consider this binary heap:

0 1 100000 2 3 100001 100002 4 5 6 7 100003 100004 100005 100006

This satisfies the binary heap property, because the element at
position n is always smaller than the elements at positions 2n+1 and
2n+2 (assuming 0-based indexing). But if you want to look at the
smallest three elements in the heap, you can't just look at indexes
0..2. The second-smallest element must be at index 1 or 2, but it
could be either place. The third-smallest element could be the other
of 1 and 2, or it could be either child of the smaller one, so there
are three places it might be. In general, a binary heap is not a good
data structure for finding the smallest N elements of a collection
unless N is 1, and what's going to happen with what you've got here is
that we'll sometimes prioritize an item that would not have been
pulled from the queue for a long time over one that would have
otherwise been processed much sooner. I'm not sure that's a
show-stopper, but it doesn't seem good, and the current patch doesn't
seem to have any comments justifying it, or at least not in the places
nearby to where this is actually happening.

I think there are more problems here, too. Let's suppose that we
fixed the problem described in the previous paragraph somehow, or
decided that it won't actually make a big difference and just ignored
it. Suppose further that we have N active databases which are
generating undo requests. Luckily, we happen to also have N undo
workers available, and let's suppose that as of a certain moment in
time there is exactly one worker in each database. Think about what
will happen when one of those workers goes to look for the next undo
request. It's likely that the first request in the queue will be for
some other database, so it's probably going to have to peak ahead to
find a request for the database to which it's connected -- let's just
assume that there is one. How far will it have to peak ahead? Well,
if the requests are uniformly distributed across databases, each
request has a 1-in-N chance of being the right one. I wrote a little
Perl program to estimate the probability that we won't find the next
request for our databases within 10 requests as a function of the
number of databases:

1 databases => failure chance with 10 lookahead is 0.00%
2 databases => failure chance with 10 lookahead is 0.10%
3 databases => failure chance with 10 lookahead is 1.74%
4 databases => failure chance with 10 lookahead is 5.66%
5 databases => failure chance with 10 lookahead is 10.74%
6 databases => failure chance with 10 lookahead is 16.18%
7 databases => failure chance with 10 lookahead is 21.45%
8 databases => failure chance with 10 lookahead is 26.31%
9 databases => failure chance with 10 lookahead is 30.79%
10 databases => failure chance with 10 lookahead is 34.91%
11 databases => failure chance with 10 lookahead is 38.58%
12 databases => failure chance with 10 lookahead is 41.85%
13 databases => failure chance with 10 lookahead is 44.91%
14 databases => failure chance with 10 lookahead is 47.69%
15 databases => failure chance with 10 lookahead is 50.12%
16 databases => failure chance with 10 lookahead is 52.34%
17 databases => failure chance with 10 lookahead is 54.53%
18 databases => failure chance with 10 lookahead is 56.39%
19 databases => failure chance with 10 lookahead is 58.18%
20 databases => failure chance with 10 lookahead is 59.86%

Assuming my script (attached) doesn't have a bug, with only 8
databases, there's better than a 1-in-4 chance that we'll fail to find
the next entry for the current database within the lookahead window.
That's bad, because then the worker will be sitting around waiting
when it should be doing stuff. Maybe it will even exit, even though
there's work to be done, and even though all the other databases have
their own workers already. You can construct way worse examples than
this one, too: imagine that there are two databases, each with a
worker, and one has 99% of the requests and the other one has 1% of
the requests. It's really unlikely that there's going to be an entry
for the second database within the lookahead window. And note that
increasing the window doesn't really help either: you just need more
databases than the size of the lookahead window, or even almost as
many as the lookahead window, and things are going to stop working
properly.

On the other hand, suppose that you have 10 databases and one undo
worker. One database is pretty active and generates a continuous
stream of undo requests at exactly the same speed we can process them.
The others all have 1 pending undo request. Now, what's going to
happen is that you'll always find the undo request for the current
database within the lookahead window. So, you'll never exit. But
that means the undo requests in the other 9 databases will just sit
there for all eternity, because there's no other worker to process
them. On the other hand, if you had 11 databases, there's a good
chance it would work fine, because the new request for the active
database would likely be outside the lookahead window, and so you'd
find no work to do and exit, allowing a worker to be started up in
some other database. It would in turn exit and so on and you'd clear
the backlog for the other databases at least for a while, until you
picked the active database again. Actually, I haven't looked at the
whole patch set, so perhaps there is some solution to this problem
contemplated somewhere, but I consider this argument to be pretty good
evidence that a fixed lookahead distance is probably the wrong thing.

The right things to do about these problems probably need some
discussion, but here's the best idea I have off-hand: instead of just
have 3 binary heaps (size, XID, error), have N+1 "undo work trackers",
each of which contains 3 binary heaps (size, XID, error). Undo work
tracker #0 contains all requests that are not assigned to any other
undo work tracker. Each of undo work trackers #1..N contain all the
requests for one particular database, but they all start out unused.
Before launching an undo worker for a particular database, the
launcher must check whether it has an undo work tracker allocated to
that database. If not, it allocates one and moves all the work for
that database out of tracker #0 and into the newly-allocated tracker.
If there are none free, it must first deallocate an undo work tracker,
moving any remaining work for that tracker back into tracker #0. With
this approach, there's no need for lookahead, because every worker is
always pulling from a queue that is database-specific, so the next
entry is always guaranteed to be relevant. And you choose N to be
equal to the number of workers, so that even if every worker is in a
separate database there will be enough trackers for all workers to
have one, plus tracker #0 for whatever's left.

There still remains the problem of figuring out when a worker should
terminate to allow for new workers to be launched, which is a fairly
complex problem that deserves its own discussion, but I think this
design helps. At the very least, you can see whether tracker #0 is
empty. If it is, you might still want to rebalance workers between
databases, but you don't really need to worry about databases getting
starved altogether, because you know that you can run a worker for
every database that has any pending undo. If tracker #0 is non-empty
but you have unused workers, you can just allocate trackers for the
databases in tracker #0 and move stuff over there to be processed. If
tracker #0 is non-empty and all workers are allocated, you are going
to need to ask one of them to exit at some point, to avoid starvation.
I don't know exactly what the algorithm for that should be; I do have
some ideas. I'm not going to include them in this email though,
because this email is already long and I don't have time to make it
longer right now.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

lookahead-failure-chance.pltext/x-perl-script; charset=US-ASCII; name=lookahead-failure-chance.plDownload
#78Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#76)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 5, 2019 at 7:39 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jun 25, 2019 at 4:00 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

Fair enough. I have implemented it based on next_retry_at and use
constant time 10s for the next retry. I have used define instead of a
GUC as all the other constants for similar things are defined as of
now. One thing to note is that we want the linger time (defined as
UNDO_WORKER_LINGER_MS) for a undo worker to be more than failure retry
time (defined as UNDO_FAILURE_RETRY_DELAY_MS) as, otherwise, the undo
worker can exit before retrying the failed requests.

Uh, I think we want exactly the opposite. We want the workers to exit
before retrying, so that there's a chance for other databases to get
processed, I think.

The workers will exit if there is any chance for other databases to
get processed. Basically, we linger only when we find there is no
work in other databases. Not only that even if some new work is added
to the queues for some other database then also we stop the lingering
worker if there is no worker available for the new request that has
arrived.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#79Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#73)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 4, 2019 at 5:24 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

PFA, the latest version of the undo interface and undo processing patches.

Summary of the changes in the patch set

1. Undo Interface
- Rebased over latest undo storage code
- Implemented undo page compression (don't store the common fields in
all the records instead we get from the first complete record of the
page).
- As per Robert's comment, UnpackedUndoRecord is divided in two parts,
a) All fields which are set by the caller.
b) Pointer to structures which are set internally.
- Epoch and the Transaction id are unified as full transaction id
- Fixed handling of dbid during recovery (TODO in PrepareUndoInsert)

Pending:
- Move loop in UndoFetchRecord to outside and test performance with
keeping pin vs pin+lock across undo records. This will be done after
testing performance over the zheap code.
- I need to investigate whether Discard checking can be unified in
master and HotStandby in UndoFetchRecord function.

2. Undo Processing
- Defect fix in multi-log rollback for subtransaction.
- Assorted defect fixes.

Others
- Fixup for undo log code to handle full transaction id in
UndoLogSlot for discard and other bug fixes in undo log.
- Fixup for Orphan file cleanup to pass dbid in PrepareUndoInsert

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

undo_20190706.tar.gzapplication/x-gzip; name=undo_20190706.tar.gzDownload
�� ]��kWI�(:_�_�s�
�x�'����8e�����b%R9J�R2f�|���xe�$��u������;^;v��^[[�X>��^�\���<�����\���(_����|��\�z��(���^�2����������[�w���5���������f}����������Y�xp������9���I5�F0�^�/�&W����Y�i1�(��?�����l�������k�o����l=��\��]���=�=~��?4���9��f��Y[{B�g6�8���y�{d~�M2��
�{��f������z�UV�o?���sr9��
�:�P�������GO�7�����������������p�d�����g#k��qia
 �!�5��F�������27�l�����7���]-	���W0����1��v��J3�)$ ���{0+-*��n���.�}3��6]*M�-���(���G]
au8�@�����<������c3*'�b�r=*��!�<����n�
��i�=\t��s��o�+�~�`����Lv�� L�TE5�Lyn2�1�Y?7�|p1�4�0�|�1��~�e��C�f7�2���F6.��V�^7��b|	���7�*Gc�_\p����+;�a�A
pG&����2W�����a����m����G����8�fK�&0�Qx#p��C�Qq6AP�9��wT����������bl~��E?����9��S��������:��������"3������c�_�����'p8�~��1U8o��1" QnIksv��>����x�_��N������
A=1����z��z}}�2�{}(���J9�X���
6s������(N�,O���S9����[�����ru�;�;yv����e������r>���������l�}��\���U{yy�m�Qw�,�~����W�Z�_}�}�����/��
�4�������{+�wKF���c���
���^�z��?|�f��L����[[{���XE�]:�����=�x�k)��������W+�&�C[����[�e���tk��4#=���=�D?���x����1�U7F��6&���+��u*���y�c67��L7tai�c�L/��������5�zU��sz��u+$�xx��\�v���W�S�r����'�n���s��|�$[�yc�f6�7��2���?XYY�z�m�?�� a�c�6��<c���fy}���,������W����]n�+;<=���[YY��k�)���V�4t��_�e��i/<���B�J�^�~�c)�g��<L$������^��z�a�sJR��}L�� ��+���*s�k++[��Fw-��\��W�~��%�\���^�����K��E���K���K����!�3�?��q�,���
�qx��������HS���K�-������p�������5��l/���;�^r,����sq�K��w���-�[��������K0eT�V���>�0���2~��I����-G���2��|���s�%����N�t{i��'�A4'3d����a�
�OK��4�E1�	����%s��<��T7�[�WQHv�����R�W��^��0Ws��sUTW���eE�&��r�>�����s2#�[\�t�/��}��@�Hi��n���VfB��_�R�S8�e�tE��v�G��|2`���kl�����s���WT��W���� ���(��g�4����?��s���T�)\UN�u���������p<��6���������g���������@����������uc�#����������#��u��$���������Fm�Y>����C����D��
�9L~��s/�]���F^_
�((`!�K�+3��P�*������7��q�?����]�%ks���(��T,\�=.~=z�}���{���$��K�zo�sw�?�%s����-c���������
.V����o��:,��6���fT\\��Bw��?~�`�BhA����ye^�d�V��!=Z/��g@�����"����� �x|���U�9��Nk����oN����_}�l �Ol���tAX_E�����z��;�s=P���1P;X��?��0x`#��C�5��2��K�E�Y��
^|��+�n6b�����1��V�����G7C���'S��|v�l��K.�,�-���!��V�`��$���B�_S|6+V�LB,����y����X��J/}B��eg���y{Dp^�S��'�e�c�����Q�N��C���P�)��U��!#V^!k�wUak{}�����sw�Y�
�����@�� ��,�%��\_������n��jP}�9`��j��0�^�\<o}��E}W�Mf�wh����-�k�Z\�A�3~����
V���7��������d2:��py��mS�l3��|k����TI!�q���x��4/�g��W�B�\#mO��w�0��Bjz�	�)A�*�(Sm�����@����$��������t&���������U_�U��]����Zu����y���-����Z�^��Ox�����;���'���Z|�Mn����������������t����q��
����z1�D9�������)�$H%��'�W������|��B�=���?D��j��J�3@&
 �L����b$�>� J��i/�L%��s��&����5����sX�V?����V��\��DD�,G*��l����IL��8N��U9�R&[l�3"����l����vx�� \���#�d����o��0�~�r
������>�yUc�y2�e�}��]���Fv�M����p�f���|>�	+$�T��K��D�E����q��#�fWA��l.�Yg('��B�������h�P�*��rd�YX'���(�I�|�7a�Z��w����H=N��x�����������9����
>r���t�cT���=������m��h����a^��
K�Z��S8���`�r������������YH5��[�r�L�d4D����7(r���z�W��.�w(Nw�/a�g�'���)��m;s �-7�CV2�����]p�X�27���`����3rJ+�!LU�O�c�7"�n���v�u8B���^V�����V��C8�I-:���
|
���6���g�����>�[g=�5u8��&3���?02���<�W�`!F<��b0��OY��m�v
������O��sX"0���W��w}���kj�9&�����P�G���D��\�}�O�����/8y���<�!��kZ3r��`�m����$���ZM���{Qk>�V�J2��
�Z-�y|.���?��y����{G����o�<?��$
AJe7i�
���X����$���	��:w��[��dm�D hj9 P��6�YV]�������{�I���`2��^n%�.�/���r�	T����H;b:6�n�E��~8_��D�N0�W2�p�������/:Mh&�� _+�Mp��"�<(�����ud�����K/(������j�\����d7���^�V��Q)�P����'}���Gx
kXr�'H^u	�So|�������V�}f������P�C��,p���,7	����DY�����+=��V��_/h+)B����
5b��!�>��|��{����G��dt�h?�^�mKeh!pNqK���Q@s���~<5k�'�	R�
?��9��C�O�FzB���`��������]����}~��a�]����b�-oAP��n�N���'>��YA�`C�.�������\�#�3z�T��P�a��/c�w0��W�?�' ^| ��������F�O%R�!�J����Gd��b��:u=B��W��,�������"�����]r���N�@4��z���f���hT:����9p����3�,�Hhi�";I��_�kQ��K}�&�����c-��Pi_a��	��=/�?�1Y��6A�F�+���L�i��������@�e+Y�Z���VT�>z�Y�@��{�f��/�]��*}R&U�D�g�O���yEG�/`�z�I�����aGcZL���`���`����UAlO���a3�'*��>9�R����E��[O&���v��u\��������z��r�zy,_�`�#a�!��L:��d1g�w�������V�[����-)�p�
��UI���ya},��������y�����"��F��WP���������	Jpl�a#��0x����B�H����hR�4ocW�:���I��W�8g�;�^������?j/��*ts��I-[��^�el��>$fe��:Z9I
h;wJ:Kg�B��*�'k����������H�;��X(/W���d_��l	���ae�b�!;"1!�������*��L�n ��C�}��
���[T�^���f9e|_`?�x��.��Q�@�����wH�d�������OyO��y����������(���{��"`[�1;� ��.0�n��m�����G���~�'j}��H��WM�/�\�8�-�Y;��(���	1����-���������
B���=q��q,���^�)��e�,j�w�.&:u`�7�ez4|;|U7
6Bf��-����k�sh�$p��^�@��e���A.�5�hQ�DYe/�s�G�����d��4A��{��.��o�o]���(�6E.�h�nF�E�\�re`��(��Q�6��c��_�o�[�f<[�V9[53?F(��Zd�{j��}��{�������a�G�F�����������N5�);O�a�?;T��QD��Ef�FIcy�:z������3��-�z��E�M��H��#(�0���F�|C�
�F/D�]���O	oJp��R�_t��\�O[w{o�nFo���;���.��2,�d�G�A��q�I��!_��������Vt���D������hL?Yz�������?oKcH���Ji����Tz�8p~?E��nD��4��w�����|*��]��,8��?_x��M[����
+���VHvc"���,���C1t(h5���X�@��B��yVO#L���Q/.O�yI:e�^�L4Y�LV�%�}'d����-	�����9�a��G��V�*��ox����1����j��Fx�����[EK�6�������t��,�:�+����	Z�?Et`�*��J�A��O�z_���1G�������A�6B-Q,�b�l�C�*�F���P�TJ�<�Q����S�?������k�py������r9���X�����]P�������%\��j�T��xD���T%��!E����pD�$�����*?��	������+�h��&y
5W��wt��c�U�?d�*���"����G���=��q���aP%WZW�Bq�2S]��~/��B�?�����l#�sB��"�+�&O�nX��&���l�)C�~I����jf��{��Q���U:��d��E���o������w�-��7����l�d���N��+��2V�K�s�}��lOJ�k����gv�ic;���m��FS������,-��:Z[���������9�3���\\"�W(�@@���n6��
'��������l�r�p0N#��w"��2�
z"�����7gO�g��*�EP�p-��'�@Vx��b�B.�	����p�{�8�ogj_(�#�_��ZW��T����&�$,J�K���
o�}�_a������c~�Eo�a�����{v�:�����
D������n�`��<��-p����Jz1p�y��t���������[�'Y }�-����;��r>|x��������>��<����2o�|,�`����E9B�BDk�������-�6���Kv�nv����4�����`���o<N��,1pK|�I8�bw��e���5 B}���'5s�����Z����9���j�jY��b��m6ax�!
8���*�_��s����=u_�x"�Y�s:u�����&������oK���{*+L�:k�z������8%M7�r6�x�?]�b�)��`K��`{��r3}�#�Y���>�?7����F|~��q��$�`T���|�l����
2/���.��12t�u6Wu7�Qcd�i2���������/��0��B<uO�U9�Qn��5�g�Ro�������;{������Dr0���~���*c'�z������&P�s	�<*����%������rr�Q������������z�P�3U�9��F��k�bdt�E��U����� ��?�{/k[������I����{{�����;�*zlPX���o���7x�H�_�(�����3����������yqp�~��{,�M�K��(=}��+?��R��	���HL����)S ��y&�
���O�Ld��A�T�s���0��\3����.�x4�}��}�T'Kz�H&��H�L����Fo�J��0���^k�bvZ6h�w��b���n������y,�p��*#d!6�BiA�&4��M'e�'ZO�HX��X
�����{��Zn�sMD�h���T�b�1�%���������C2^�d�CE�S��=z@��c�EF
��(��/J�v!#'*��v"a`��_=*i�����rp,�������m��6L���(M�������A�1F��i����c[%�F7q���!U����
O9Q�)��99�1c�y-��M��J��A?�&�y6�T�M�������&P���|1�D����9���&����{��M�T��z���s4��M�Q�.5����),K�
5RJ	�����!����������?�HDRyl�E�1����$�����������W�Y�-�����s����H��O�kw0�hz�f����hx�w�~�5��ntn�������ve�gUV���1���<]���J���j
DL�j���A�J�P3�;�3��=�������m���ba�����c���`� ��u����e�o����T�4l7���
�&�����3�~4&��p<p�46�^�M}9�fkS>;�l�*r���rd����9^��0WG�g�P��!���X-h�wj���u���x���#����d0t����	i��Q\�����P:��C��qN��k�6>��1r�=&�=rm6�NrQc#]R���eS��z���^����X��twCA�Q��@�L!�&f�q87�.��\�-c��)�zqxl�|���^��[+���O!gf"�tek���{)�{&����GF�P���oC
q�??���T��JR��>�7�D�=�|�SM
�������U���=_F�l�Q�*�#y/R9<�!���rpS�n��5�I(�K���'��S�,a��1��(��P{��'�[�f�-s�)���b"	������T1S���e2)��� ��!����0�[M�nJ��`�Ck�u�����?�]������N�U���0o�P\�g�m�e-��a4mk���)����4��4E_�U-I�b���R���WG��
�r����\9$��<�V����zk�����F31����}�������� /�����������j��B��"/�wDp��x�>��jJ���U:��B�K�U_�+e2sQ|D�@�~H�������/3z� �4��FM�pa)������*����#t�aJ����m5h������c86���aS�����*u�Rk�> u'����%U��
���@X�%������(����M�6�3f$�
2�]���9�e�3S����S��Pa�Yu�c�f��!-i,��xt;�9���n��������h��1p��of5}u�����������;�=x��`Bc!�n�{��t����yF�N��7��u�h�����y^\`R��k�%����S�
�&L� }L�f�3������6���ZEEa~5,G��`�xg@��������r2����-�����>T	�����
��K������t���6
�����aI�-d"��8���o��8�=����s�n����6�i�U��(4����|~�C}��UC�������Hy�&�J���c�U6W9��@!���&I:�}���|Z��(������{4a�����/-�*6��g��!�~��cIs�Bf� x#DY�2�E9��(q��V��8����N���Uc����aW�]�����J�����Z����j��8�����r.89LpS��Q��(3��b����q\�cc�/T�v��L����}������DhSR+���D	N{���Q���vg�u����n���H��b����O����PB����`[���4�P��,9�����4���Pb����@�7m,��j[M��5ep�X_��6���������Z���Z��Xr�]�Ttg�r�)�L�G�2�NfY�c1��o�Q2Y� 	$�!x:$��������lo�E	�=d�o�Bc���Y���-YQ��E�:Y	B/�^�:e���c�q,�*�Y���L�����I���3�OQ���KI���p�L>>������1"(!h0��	��"6D�+��FD�������M�-kxz��
�/j�j��g��z�D�Y	�>	p��.�0�Mec�����uTp����A�tnS@�w+���+w�iJ�I��E��k�ntk��>!M�P�c��T8��M���L���?��-��0T8�M^t����)7NQ
	@��xi;����^�r�$�x+�5o���l|��L$�������L��N�;������04&����s4�
Wd�@����28���������G�d��#�st��D�Z�Fs|�w�N��8���o!p�&�I����8cW�fxy#y�pk�y/�h
q�Qd����M��4�p�(J���*�����dN�S�o<�T|$�4w�7L94�0���s�c�3e+��1����Nj?�S��/���an�o"\�z&S�X�M�	�r�����LB�@c�5���9��>��)i�q�sQ"�"n^�e����D	G���f����8�mE���w]���/���]�!��)�@�h����rInXV~8��zx�<#[c���]�k���Oo�sZ���$�pN\Ms��!p�	g��PY�F��R-�h�������FO
��l�&[+8�_y9XS�����K���j��n����������ykreFe��Pi��uU��#c��L���C���X���J��k?*V(y����1��A�uL����<�R;�����2!��WU�O6�4;'%v��-��zKh�:�x���4�M*��!�7�:��fo������S_�A��������U��'P	�y���gJkA�F���\�sD<E\����r�p���>.O"��=JKm�����i*�������/���U6��7������������b���_�c'�o�c�L�����~�(�>5������s�������6����Z�<!f@]U��_d���"'L����,G���<�nN;�"�nk-N�����@�c���s��hr��Y�QN�S��3X�`Hg�$
��tI�c�#A��28��AU�C��:��DY��S����?��2�u
���u:�<�SF<�����7�x�v�
�:D��?s�
y�l��%V�_����Dg0F|�?��y<��s)%(�N�U�g�����1.S�P�o2��YcM�&5�<���������N���*�)Acs:����^�pb(Y�zt4� \���g�C&���Dey).)���.����kkD;��QoZ�]�S���`)	m��;�?�������7{��.gpd0L�Oz�m�'������J����~�L
ik![�������.��}]���WX]sE����i���{[R��b\���|I"��E�=����SI����
��Q���~>���T�����9����\��� �%1��5}(�?���T�R�U�2�����P�X��p�5�p�������S3P��@n�����\R�T����_��[d��'Vy��%�+��Q:o9�����&���ErE����y�:h(�Qi��+]�R*����#���5�K����=a�������q&J�p���9%2 )�����DH�x�z A�M�3��w�@_���z�����b0J��u���@�,(��X��W�����?%[J���8��������0 o}�DVr����n.��F�)�-?�/��5��X�=5�-��po���o\����J`!�����|�.�����n;����j���@��V�G���U�N��L��"�tZ�8���D)�D�x~�B�O�Tl�
9��>���G��V�vItr!Z&_y6O��bjN&/��.��zo�Z�{��p����`'��5�j�5a�y�Y��c����{��#m��8�C2�������j	C,�$v����k���I1�� ����Ky,�����������$��h�N�*��k��]�.�I�q�dj�;9��Q6��gU����#C7��������PlB�q5�4��S�(�#evj2&}}�1�;�J��A��*�U�S��d�sz�Z�O|�������``?�����k(����oYw��KE�E#R���d�S��\2��"l���Z�`rMj�y��9'E�I�E>�E^�A��z�Hle�h�B�+����k�d�vn:IO*�~coh���;���������/�C������0����������~�I;��tv�+^��������h��"O�^�z��S��0�p0	���P�S��&FrY7��1%�(����=������l����n�*x�9'1A�L@>����;���`n������q)�\����i��-D&%%������$��Y�I��AL�vv6#u8M1�,��&��
 �|����������OB�����:��d��G��*��4�����6����c^�9��LV��F�hE�Um��{7D]�c���Ef7�/�WW��X&��d�����&��+����oJgBz�����f������M1�K�pw���R���Y��O��y$u��X����3��Rw�v�5�Q�	�����S(r����������>�O��4�=�V��@T��O?���?� Vu�����X�U���I��3�R���6���sj����I�d^v8��zy��Jo�L��x)�4��<�A'n?�:��<��Z����M��S����]����%�nNvV.v�OE9���mR/��F�������>G�t��%�0�w.t ��k���-����\9v�-��,r��j"U���JS~�*�:y�p����kq�b!�/K*m��;V�u*3T��+���B��R��"���b�qhN������h�A�Z�R9awfwd��B����;�EhR7Y��0{N�=P����A5)��[.
�8Xm�3��I��Kp���Ml�M)PCE$�{h�\1���wI��S8��������A�\P�~&`\L��ZJK��|�����[����~��r�����R�A:��H�m���`�.�=����5�U?�,�	�V`�%@������v�?��{{�=��R��(/116��0��>��)KX���,��d_�.���>�����$B
��t5�sH#��9u�If�`��Np��5�-�����*u�u���
-�`^��#�I*$��U��������&@`���4+�1s�.�]����g���3M��D�]���c9q���Y�&WW#��E����^�1�d����V�z��b�7�U�� AG(S�)p(�xt��>�tM^�.0w6�E`�87B�P�U��J*��4��=E��[��H$9.��(;N�4��
���
����x.2�����J<9$�1��y9���k�h�)�2��E�(�������(�j��e�7/T��=]�&��{��7����}�hfC����a�1�u�������8�M�qs����s��I�H��241	����5*��*���z]��`Y
���H�	D~�p�QE��]nk��(R�����������d�:��b��Y�?���8��l��lk�?��L��U8�`&6�d��k���_� q	�N�~���~4��FM��D*��y+��R,���e%a5���M�BWp2��{�hA����J�LRSd �G�O�.C>����E��?R�L|�m]iv�k�����������Vn�D��e/5������|�lk4�M�\��u�)5�_[\�Hd�f%��!��b?
�%~��������-�����N]�;`�)-i"9<�����R�Mg�VA��3�V��������(���a=�������X��*��T�
���+
A]��(�IS>h���>��EI"���j��PW�c)7|�����c�|T�����
��M���MS��	����LR�+0Z��(g�uvS���g���Q�Qt��K<�V.�n3���nI����>K���&�
7���S�$�b�������g��qW�/�n����TrP�����0C�Z>U	��A�M����_��c}�&�����L%RT%���7���v�`���N);F]x��@���|l�W��:�n��3�}��J��F;���pZ0����)fV�vF����:y`b^8����s�
�J.vpH��X�+���T��8l�YW�d����
$������1]�H���`�AS5�PS���
����c)�[T��VR(������������_}#�})���w�
2$QR�A�NH����s��l�
���0m�{��@FT��:�<�T��vIp<;'n��@��C����UVi�����%��?�41#����1C�)2�K�u�,]jnP�������f�*0�NkB��TUu(��r�4gl�,�������IJ��@���ZH��a���x/;Y�v\��-�EnU~�NM���x�#�W����1��G����=�]���������;w��/�BE��"�o���u���VA]���9��=,����S����1)�^��q	J��/�v
K�GXP_i'�nha'�v!�@��2�;I��4�i]^�t@Q{��X�������G?�y��������/�4������B�3����h���y�$��i�t���sIah��IZ�N���s���lR����#����iu��\���&�3vzA���E�GQ�W��RNM��Z+�h��{%^U�p08�Sy�Y'�P����j-�h�����5�����|��y��<-���m�������fz��f��uI�'�����h)�:u)w�3���������0����j,�F�B	���{(�_6�����)5�g=0��_-�M�<�.9]*j-�k������Kf��Hp�n�xF��X�N?;�8va~��wm4o\I��A(Q���u���3�*@��J�O`�M�J_����������j@�'��GHa��af&�Z-�F�v�����}��*%��p��3�8�������������x�<\��s����|j��n�\���v��S*��T��'�8__��
?'
o�p�#���]���8�EB��@�bk�C����+gD�kiS�
c�)e�m��������$�Z'�-�Bg�1NR���1E���> =�U�"�3����C�����We�+
.S��o���nkV�'wE%�(G�R����P�:V
F���T����mM�fG���xw}9��SK%X
.n��,7rw�*��_N�lo;4z��O�1kk����g9�4�*�RW��/�_Z��'
��o1�3�B.�*�_D[��3�����iiR���<f��=j?�Q�c�5rES������[S�-���l�Lle���0���K�H��b�-4.��W��L��g�_�<�u�h9V>����������G��u��[���"��<(���91v���5�����^
��j.I�P��ke���-p:����	>�&�=h]��c�����,u"G��S�1��p"Z8Q`Xy�^���+�����B�G�����
�K�Jz�m5i��c��w-j�i��=����A-�����0�b��xv.���s
���?�X~|�Q+'������'ug�Y�����s��u����5�ZEI�����v�l����8!*5��7������~��J6�iX�L�!G��L���S�2����lB�^4bL��M0�����Nd���l�
,���r�y�)�PN���r9�*y�=�=�����zd�B��]%�bl��gyd�����g�[��.s����.W��#wj����y��j�r���8���LP�A�&�Q�b�c�q�;���"�wRH���+������7*&�x)k�4�*�6'L��i��Y�_f+�w����K�l��fdA�^UWK���Z�}�)�����3���,/zjH�v�JQ)�M$0N�1,V��8�P���T�8�\h������B�����d����'��p��w����eEp:aW�pG
���)4�L���HT^`v�&��4�!�����aH���K
y��N��<�?�����g��YEo	��Sl�������V����H�l}0�}�eX���o�'�k����e[V�$$�|���D�v�s�����m�10�;��~�R�$4T@�E(����=��Z��W�"�G����P�P�)�D�x�)��u����9:k^������3o!'�%T�E�*�������������H�y����?h�&L�Cj��c���x�g���~tc���HZ<B���]��+�K�?m76u�1
rC��`�m"Y���[zC�a�q��xRN��k�&<fl��X������:�%?��/<`J� �l$^�^(%�B���������X���y��H�
1U��>D*��V�p*���2�G�GO��J�t��n���I�������*�-�}���4��Kk������b�~U/k��nL�sPq�T�:�/��!��d�s���h�����~�Wq_I�V�$����!�}��g�t���
���t��N����D���^��{o���#I�Aj�S[�>�Q�������]�� �6��{S)��O>�8���������8�qo���������������C��~{���<��'��Is�w�a�t�����a�p�V����2e�G�
�U.��7Rs�����32����#'xu����C�������C�9��w�,��(�1�KQ�rx���ts��uE�I���A�S����c�<f�4�_�\5�	g]��=�$G����3�����j7��D���)!HT��z�H:�����>��8��xZ �E�z����m����8��A�h�����s�yg�
�gX#���ew��2+	K�w��46H�q�s��w�bj�����i���!����8?����������<���L�^�fJ6��v�� O���R"����������e�t*�6����a��Y���]�=`��E�"�M��W|��)wMoY��e�3(�<9���9#��A��*g��mppb���U�d�HV�Y���~�,;
iu��������V��
���7I5�y�S��O3�ta��Y����]�(��(�Y����=x��mw�E6�;��	���EY������0�O���i�'��(�l�&�1o�O��ET	��6&����HN^�1��Z0^������x.���F>�%;�f��mx�&�:�ZGA=|��#<����S0��z��h��Lk�4oq���]m�OF�/�COdC�t������~�d�Bb2s%��^�{���[�M�WXk��xzZ&�g���:X��Fq���$vG�l�f��s�����6��'�\����U�pl���F�������%S=
��l���|���yZ��Fl�-��i��m;&{�r=�d�1�����#BWZ8�����
�n�����j������3N3�C�s�|�m��6�ddk�}d��d�i����ns(4� `��W���7E���Ffy�i�j5���S�����K��X�YV�������E�A������������^�?++��G��������f��\L�Y|ii�A��w���Y3K��������-�?.��?(z�x���
w���_}=��yX�8�s����ud��h�?~������-���(?�?��K.��<����!�r����H[����7|���tL��v1�L9���\�
��V�"^�{A����u��V.�~�	�����������J��~�;��}X^��1���
�Q|q��Zen�'��A�%��7C�`�^URdA~%�~Wz�
A������[
���.q�{T����pT^lG�ce<	Y����W�l���?5$b���U�NFRF&q?4O�����C4%�����w��i�}�JYS��P�j|�}%��m��/�i�_
�p��Z�{�YJ�8�t�$��t�.1�oH";�����J}	8��i��4P?���yR���1)��;{j���t����@���l�Arj��]]��{3����� ������2��4`�\�p&������`'(��C6��\�������[jE�!��i�n��U.1��A1��h��+���8-���Z5��(����-Y7��y�tO!j�/s�S����l`�:������*�bc�jlDJ�b��
z�aF��@�dog�P����S���OQ;Q?qD��g_�oyz�\����/.����V)���t���#j��8���vs+[d.\�"��Md�Ee�#Dw0�q�;�U�N�W�sBw�P6�h�3)�2,�6���|	�}���2V��K5K~X��z���}�������
it�WX�24�����?�������~!i�kY���y�w����%��V��R8Db�
=�;�h�9".]��u�9����\�Y�j�H���j\�u��|�����y�?
V���D��
��i`]����4�����u�/S���C�+~q�g��#f-]��vwlf�����5�W�f��� 78%���;
j7jK��PV�}9>F�L�J�e�U���g�[}�8�H&J]������R��l���.Vh��BZ��a]�C}�UE�Y��h������q�e�	-5O�����=��v.����'�$�U�N�$e5�:��T�N[�fC�u)��f
�2
�����4t#qm������?��;9A��^?t<r����'��!_e�����X��c��`�*��$�\��X�KR���+�W��>�4����+Jo��
U��\g{q���s��.f��b�S�/���i	��.��Y?U�F�I�~��
+�]|���������M�i��I���z������*K����1k~A����c�q��Xo��A�x��vc��B6�X�5K��y��9����rrq)���]�z?��6w���
��7M�R���'v�#1�%6Ee��v�s���c���������������r�6�=CT�W3���|1�+��Y;�����-v"%����H��IQ���HT��N+\�z^4Gwb������;����A}��N���8"�9�4��4��{����W��:���q\]�����OsJ���'%=��qDJ����8W�]y�d����9/�x������*�:O?����n��%M���\�����������&�Y'G��/84���e�/�����n��f����R~��$f�eSo�������(��p����X���~�����'(P���a�����(��GH��lgnd��|�X�*�����%Z���&l���Y���t5�-�m�sNNn����W{��}���9w:<�_b���b�_rpV#�g<6����7/O~���y0��^	����W��&6t�����s�1�\~�����)��������k7��j����I��v*�����m=N�����q���b�y�D^&��9��*�9��I��,����>��\����82�B�8G�{�G�UV(s<��e}4W�'Ua����l�zr��
?������e�cv)(�*�-���
z����
!T]�l�k�]���=c`���������7a�����-zn��I�;s�#�h�)���5���iK�9��,�����L�����!��=�i���0<[�ow�K���~��o�4����B=�wQ�����lh�N���D�����O�QO��miwu�7�������!m�����(�?��|
�zZ��g���?�>{��������������p^Y�A7����������3p1��@���2���s:�~?���	�7���5����X��E�Y{����t�-��y�� ��w����2��mT��>���ci5Y��dctz�!��JCfG�m�CE(G��a0�*)�N,������
6�
tC	�\�����[���[��R)|�@
�u���}�VS���22�l,��DO�Q�w<��t���9���!��0�j9��`a��c>�z�{�PU��*z)EYVU�[V����D)�+A�J�&���OI#.���[��M����(�Kp��Kd�.���u�ur��W���V���w8�lb-O��-�j���;B��8gD)�����{5�Vc��%+_N|J�����o���p����������'go�K����{a�������P� �������&BIT�y��ggK.{�W���X/�I��$���H4#o��'_�����	{�����?Yc��-�0l����*����Mj�x��@���)�`vdh��mO�ya/]��~����`P���gq�+%���A�k�C���V8�	�%^�U~��,x�`O�,E��\W����x�$���|�%?��b�W!��,=5���N7n�k	�^_�b
F;����s�$��;g��f�p��)�[�g��$X|��%�c��RLj<�1�K����P�������R_/b���#������M�E�s����u���t�{���e���j^����-fm4u�4EN���}s�A(�+�����VS~���`f��6,��;I��p�O�^zU����:	>�|��9Id��#O�0C�\�p0��p8��+v��N��K�
�x����<����m�H���^�'2����s��f���Z`��;���pW����=��<�-�+����d�N�zd�G�����Y���#6�NS���>O��#��T�,��
��s��
����#n�LS�oi����-���[�}�a,�
M��nz��`\��?��O7&����[@L�?M�G<�9l����y�3HEC%�B*��x3m�sZ�I���[��=�z�S���!O��a7�-��0+������8��U����G^���i��r.)��e�Y�5�u.T[�Ui%�6���pS"�L��EH5V}�0��b��M��r�W_��e���Oe6N5���Q.T����y1�Y�o�K�������^rk�5�/�A�����p��	O���-_AM�1-��	����Vl2Pd�j��'�mN�+�����A�|d��_i?o���\3�6?�AsM�ZW���l*���)h��U�j��Y7��wH��'GML�wI��D�O*���tq9R���2������$��}D�C�*
[��;�<�.�rM��an�9���D��L�S����g$��BTJ�����&q?N�������%v
Z8G�)�j&��2��;`��v&�?P��D��[�"�����d8S�?/�R�:������MN�������3�L��tBVw���MHL�e�M�4)j.
�w$���H���+�%M�B�vi��Q3����u�Fa�������/�ks5aGCv��6�+�3��3J�SoI�/����^<VCV���� ���C��������|�����I�.���\�S���F�)�b�}�0�tB	�/Z�p�3H�R����v�*�8��S�
�rE���{'�]:m��i�(��$��_�������2w�Q�A�y#�R�sFLx�1�;8"z��Q��w=����CF���z�{�nP�����|H���&���<�HK���1,��HW���m6�z8B'E��YVq�*G��v��"��*"��_���-d���a����W��5xp_���Q�h(��~y	~�1 ����jF����9��$���r��g#�	���9�_~I��������b�C��99���q�t#���l��������������XK�����g�D=���XM��������2�o�(D�����*T�_�/��j�w�Jpmy�k����'7�������7�[-T��CdvV~����
�����6�
����Bh[��#�r.�RV{m�V�
�g�����>��0U�&p�H����
�Q���2���dX�qZ�tAE��wq���ju�X~��<������qQ�7[Gg��P+����y�i��uV��K�{������&�J�rl�2?�O>��g�&A�s'H���,�N>��a��u���0�H�����/����y�E<^�[N�Cj������7_��J^����7��-m��#AL��7�>\�1����z��z��`������K 4h0���oON��wt��ybLK�bYB�
�p6�:A���Z���QW+�B�>+����u���e���7�D��#?�M��l=D��}�U�����s���7,��/@��y~-|Zl-PJ���E��?-�0���~Yt0��F>��u}�`kZW�cI{����s\�H�g8�L�	wl��>b��! ^�\�w��@�KSN~��?\!��V�V�'�Fx
�������L,E*�mBT�E��:�hTs���F���zc��i��r���j�u;�V[�nT�
�$�����e���OQ}�NK�����X�����,v;��{���?����PV�_^����|�=a��b�W������\S
Z1��*\�]j�)��}A�P�Z�������)=/�BU-���Q�[u�]������0�H=i"w���x��Nd]��!�����_��D��T��o 3�$+	#(3�`����0^��m�\�^pD��
�/�;�N7nu�2�E��`�0��1��I�e�����7��p,�kPNL�����������$�l@��
��Rs����M���������u��#X��@���GEC5d�QwTV���H�,�_e�t�R��y����{G����x�`5f�����Sq,��� ���X���*;�~N
����,.T�v(�#���#�R�|��$���t7D��qQ&3��x=V��'�>�T��d���Pb����Y����������,�h����O����\��,5��|�;[^�\8�����5�R2�k�:��;�'�G%�bD��]�n��8&�c�������b��S@'����ssY�4���PVS��[�.�r����
s���y����w.����Z;g��s���X�
d�D���D�h��!��k,��hFH��r<,����F���p��Zz��S�����H��|2fx��/HrPa�
C�n2�) #��f����#6@��{0YyF�[H$��������E�h��W��8!�>|��m����7 :K���;	I�����p����Ec�%�����K6mq7b
�/�sQ*�	J�`�� ��4��Y�{�'��~W%,s�O���Z��O	�3���}���z��q3Ni��dj�j��t����uG��� W<����b9$i�F��	��h����/4'���
{��g�X���/�'���:�T��?o3s����g9���S�z���g���d<pJ�����u&bs���8����/M���Z�7F�Ax4���2N��Z����rZ?\`A��l��U��o�H�B��@���Q�=�k������v3b�cL�K�)|��e�6 l�����S�;���7�@M.�w�+	���nD��=�="L:��x��S>d\e����{���~�i!9qs���F���G���3�A�?�-�9�sQ�?e��J���H��/�_N�K8������j>U:.~.����@$�y�=������$u|����)��i9�um���YGU"�����?4,�zE�
K��5��x����C|Z����!gAs�R�A~�d2u�<�#���[k�X�w��2�K�Q~����(����jSD�E��A�>���u�����x����$x�(I������|�0���6WV������L�0U;�MP5������,��������V0*o�Mk��������������b\-�_�c>*cQ�O����T�Y?�nM���yP����e��j�_��B����OE6�S�4���?]q`�|+C��:S
������a>"���-�r���\��m�g}m����2��d;
����"���m���?�|�&L�����p!2���F���gd������~��x�v�0/���P�)�������������,���hf�5��7�.%um��V�sr��Y�A �+�P?�]�y�=_��Av�[#vX�P��E�e`��W�����d�p7����X��}�h"	��k��"����c������:�X"2�dq���v��wN����D]����1��+���"m3�x������@��������B����(�Q��)��f�C/����ym��PJyz_>�j�������:�;z�U��P�?u8��8�
/Na�Qq��4�_g7��}�|�`�=���k��p?�����"x0��
�w���?�|3��Ok�I5(�bk��p����0.����l��"�	����s���Zv�����\f�{���G[��������..���s����c�m���������8�~��q�������h�j�r�����A�7�]j���^6kg�-���HP���q�K��s�9Ef�S���5&Y�����}����$���K�:i��/1R�r`�`����RN9�Ht1 ��	��m��S����s �b�}�<BV�3�����aK`�F���,���� C'g������z����/��>j�\�=#3�azt��u��4f�	�K����b5�-���8�"�E<3<AP�f��C"c��[��g
��A�JnV�)��hm��k�������9�ak�.�9����������� ;�(����K�8{r�s���|�(��Z�"L)�=]"M
-c���XGN��U�+d��{�-h_sG1����F��t��i�h��F����hwP���De�w
�<�F6IR�k[u�7I13�_#��,�����{';���%��R�#��s�[��g_���^�@��i�$Gy�������48�8n9C���2G�M�=O&�h��(.�ip��c�wH@2�MZ�S�j1meU��r�����r:����Y��<)QA���9���(yf�a�w�0I���+Eq�o�q�F1I���J�D
�
�>�����9����F���m��nBx�m�����V�k�Vr��f#��7���g��Vz�X���x���W\
2Dj���=���>>��w��5P"��/���%��_�Y�O{dc3Y�����.�>����.�0�Y�\��fo��V���������=���Q�j����o���	�����H��^YZ�S�\����d��lI��Q�`�*��xkA� ��3����7w�u�)��(��0b�B���w=*\ �2�jy6Z:W��`C�+�*YKc���'���$�46��"��{p��`�F�(k�"��7�*K��0�3�a��f����X`?������`7��N�)_�U��#tl����p�6���������-8vf�|c��������������05m!g������b��{ �����j�2�A6%�;x��Y�G��go0��s�*�T.�9�X�T/�b{�i�y���J���<xV'�&"��T/<�!W5O��l�_�>d]*�1=�{94�9�����x�&si�Lm
�/��k�*H�pe����b��~aF����r���Buu��$��9bfY-��J0���P��8	c�Uw
��T=� �[�{&
��TV��� F���+<r]G�b��v����+��*U4��f4Q���j���Ob���u���n��oL���K]z,1��+�`l"��:��@SP=��1/93�MaQU{�K����T����|P�~�.�`��C�2R���YU�?e�JMTn�H6��k�Hi���CG`��C���`j
	�����h��La��E�c����J�}{���4"�7D}mJ:b�|���[�����pK����d�h#6�
*'o�\�l|YV��BaP*4L��P������mYsTb6����*�L�"�)A<u&�"��^�u-�*2%��\zL�8L�1�o�y��g*�$�vJ��r��[�-�b���'���|MT����+��jVD`���kX�|�������%t|�%�(mM�]����7����.	R��A��
����g��1Qh���\���s������E@s�64�1�9SEQlW�Q'��y�{�^��zR���<^��05���ea�4�1��LP4�Wq:�i��cbQk����#o��#��F�T�b�;=������
��J�z��1�m�6Y�����E&EM�
�+���S�H�7I��m_~�p|�����o.�i���^�J7�g��=V��!L�������kH��6X�A/]�D�-	�KJ������B>�z���{�$��]�x���|��������X������f������x�C�ya�F�z�[�BQ9�B�di
�j��f���M�r"��JRc����3����"E�^�_Z��z�����tk0���W�~��8K�}��[�W���n�����Ek�����t���|�m��*"��@V:�����������8H�[��(�8�4��y:
�V�O�41.�|��������''��0/�R#��un�#X����p�#�F7�5{��I�+��y�a|l��N�����d�0k�+1���v������D�f�yN�:9d�r��l�3ns��jd�G�8RYY��F�8%,D��Hs�J^�:��1����������I��fLu���62�ab�4�V�������5Je�&R\F���V��AY[�z�� c�j���� 7����x�k�	2���Eb�H�;e�g�B")�KC�9&#�@����1��? �����Q���>{��9*����#����M�"���_����z�N��Pt�b�a��T��t�:<5��(���!�������$�N��Dq����92��=�}Tn����='d����cxB��m�c�8T�S3��S|��cp��sjN�3��}�������������~s�9
���u\oC!�0��gk��	�S�=�o/�#c��=��1���58�>��
��%��y���������H�-l��w������_��'R�Z��"�S4�����QP�������9�h��s��&9��6������&/yuL�cW��T����(
;�!�
.l�]d�����!h/<�X��{��	��dY
E�i>����HpCx�Z�s���d}�a<�H/�M#���v���c�\�F��+I��9{t�����Q�>�s����Ngw�\�3;��n7���<�rq���t����m'��K0@�.L��S8{���R���gv-�D��y��/���]$t���A����[��)�]�W\������������;�\g�37&xN?�����nl��)���9Aj�3G��$;i%m$���� o&����_�����Z���o���]��,��b��j�c�$�r4�����T=�9$���S����:����#����7���PT���
��������1*��������	wI����C�R��$������������"66�o���7�u
�J�g��������@�8�;�n.QO�$������ ��������W�l�������������.�)�zS�_�J���~.��q�or9�H/��~3����y�x�`����?����/�Y���z���o�__[��X�Z7k7�������k�?|~a*��_?L���?��s�����'����/�z��y��������y�����m>zt��?|����w��5�c`~����'�f���`���+n~��E?3�e��������l�
��V�E��
�F���\N:p��O����'[O��|��D�>���7��O�O�;k�p���T��8l
3w�&��������h�7�h��n���,�k�M��6g����3J6a��������b�������v��:�d�"���V�G�&���Z@�i�9&�����e��K��/5��B�����U�H��H��$��=��h�3���u�\bGR����E>��`=���I��V?���v� �����_i��a���q:�=I�����Jb��=*�sk���&�����Osp^Y���>r��<�m~�m86�
�U��tT�c46?dY�u��I��H���uI	�V����c/-M��1U����znlA���0�<����?��#�������z������
�)�,�#�yl>n����H��n0��D�@��W����9�2�s�n��a��~���zA1�U��{����z���_��Y_��5&������u����A�v�l�31�Ls-����Y��4t��6�nr�����&v��i(XX�u���.�B����1�x�s�a8g�l�"�o�r�����l�v�2����A������G���?��@4C���hn����
�F����}J<w����i_O�{nZ*��1N�`�K~�������|�A9�o<�����	Eeur]�L1I��,+_=�)5�_�?7��_	��3�P��j�����U���B#�=�V W��i���L�������Xb�4�L�2�%�������'��*�a]>?���r{�\��:Q���^1�37!��g/`�&����T�F�:ll��t��Pg���5�-2���2X�I�j3&
HQ���s�t�u���'���_�)��:�jZ����K�U����a{;����@�m�FS�#�u)/���F��y�}�O�}��6���=.h��M
�1� ���p�&?��PY����]>AxQ�H����|�� ��8��o������^��Y��-�])��7���r��������]�����<5� }���u�Y�D�-?��V���b:N�K�S�/0�g�;�&�������:�0�m�i]�F+6��)y~,�?�����MT���t�GmG��kA���_c����2d8:S�7+��M���?<�����1��x� ���������|�h[��^��I�t�o��1�.5R�+x�0#��������u�7���M����T�z����[nl=��Y�@�`��f���B�0����,��5+*^!��Q�G���?pA8�Rr�z�_"t	�$Z�^P2z���UuQm��dM�E�5��x:�T�y���&c����+
Xl'}e���S*��9��?@������#Z�_�����I��M��
���r��w�>��}�/������c%�)���1=���a1��;�w^��|��c���vO/N#H�A���@|�i
d`
	/<I�����=L����j�#��J���c�/���\�O������e��T��1>�X��{����"@����{��2N:���pF7���
'F^��hx���-FQ]��x�iF��?�(������\�������?�0���/�k�E����e�c�uX����,b��nC� �� �4i���Z��C�R8�"St�G�J���p�%����[�9��0�c[�|�;�+����*�������ch�eG�Q���>71��������R���]gy��1����`�D[5wO���L�BP��<��k��_���OD�:����50��R�������o�9
%�G�����������_������a�`�D�����D$	@*m$=G�ae1���z���J������#�=�����),?w@����Zi�Kn�H��VK_;h7�X�f��x[���9@C�)�0���Z��^ae�S��
Q�^e#r�Fm
R���RU�*�B�0k'�������|2�{�.�������j�����������h�	I��J�s����_�i�d��!�.g()N=���pXF<\�U��D�V�	�������!X�����;x�5w�
����0������6��i}&�����(�9��@(_@I���q��>XgY�������r\0���W%�������W��B���k����1�����a��?Q�[h�7�8��*��}��m[�I9��%@���en��;fAQAn���P�r�nu���t��]�}h��ju�
�����$�w�w�������������'�����Wi��T*�Y����Om��������������G����Z83���%�}���i��6(.�7����{�4a��"���M�(��e����6������`5
�e��R�X�k��o^]lB7:1���O�%���'��(^��8�z�I��-KSZp�C�)�B���s����L��V������.u:��;�]�D�dX4
���r����o�h��-,��~y9��[6��t��6����w��������J���������e&rK;��eW�M�x3�W��`^3��fCX�����Y���C�=�U;<:x	$����[��DI���4�?�.�Sp�)��;��lS�������h�p�hO��� j�6���Y��4�'���x���=
t��7Yt�fu�m%�/�����>�{�\���>};�����g;�?��"/0�)�������y���}h4W�hW�~�%����p{�O0�E1�]��F�������[cSjf��������g{/���nx������`�����6F���-D��Y;g���������F���'A�����5DP���t�
EoL��,����Pp�s����;��SN���+���W`��{���1�f��2��y%�����@����u��_a�t2K��vpu�S1>��E�P�.���S5�m}��&�Qw,�5���ST�j�#�����)����)��Zg�'��};.�u�����2���!��m$��������M���UL�R27����|��C>��?��&5�^u
"�{�@����Tue����*�mn�W��}vD�b
uY^�}����T3�����:�I1����=����8g��wa���wO��o�#/��T,!���Z-_\�6��F�1l�CqI��j�-�IKS��r�X��`2\�.'�V��G��&�&`nD��3�*�n���{��\��*1#������Tud�/+QN�W��#����
�����jq��������J��_����XE��X8��6~�9:vH����/Td��O����\3OC��/��\$��76�@n>��o��yr�I����B�3?Dt��
K���}�w},#��r�q4�������,g91'��+�eC���;�
;���\\bd7����� AQu�R����������hM�e�}��mS�.�t>�����3�B���0�/{o��6r����O���)��H������%����Ht�~9}�@�pLAZV�����S
@��l%�sWz%���P��]{�mT��S�����[
�/X54�Ag<��in����zO/����`
9���x�o�|�g<H���z�����?�-
�+�h!v�dh�y�����=���!?�O�l�e�F|Y} ��K�
z��]���G���jv���8Rhx�J���!l<����� �G���Mj��a���x����0����b�������,g�9M�W_d�mfZaE�*��>����j�he���\�G�	�c����+d�k��P���L_�>�C������*�����D�Qvu��H�����C��E]+�I^m�� �m��r����Z��7��^A:��v�����m�i6�<#9�+~8+J/~:+��<R4:�Cy�z�Z%��cM�i���h��C�O:��L�Ma����G��d����Z1�N&y�f���X�*���J�Q^Gi4��������M�K��L�\F�Aa�9������!�M�ak���j��
���x����d���16�|`u�w�&p'�gj�5R�3�6�D��m������� �pY<a�,���Zz�����Vo������gU88\
�O^QY���u�)&�B�>��X�D���V<��;����#���3�#e������D6R#����|��3��7��N�"���tYW�w�����q���vI�A�f�|a��#����DI�1utHq�-�Q���g��������+�o���3����y��������%
N�i���>�!���y}��`#;��T����h������[��&�Y������b������8k�8!�m>��rN��?�<l������w���_��AA�@��s��Z�`�x	4a_j�]d�%6Bd�����VDR���J����'�)��Z���8���fm�;���L"GU�Rl���������FW�;(��"�f�$�N��_�x*��>������������L�!h��B�����I�'x

,\�"�_��jI��&,:�$���m�C�sO�j.���txRE)l	��Bh�lJZpS#��J�J&�dYutA�g����������dP�H��Mh���$-#�eJ+��9J8�����3��Q��A�A+!�&�Vc'�NZo��<$����Y�6��3�Y�(��[ �`k���l�`��p��s����|�!�Pp�m���L2�w����l��Ym�9���D�f�����pK��;s#\bL���|R���=�����+"-���Rr�]HkI_3�
���eM���9����y�sT�����!xz��
��P���j=�����a��h�jD�J!y�H�p��	��oe��wr�d9������9����a��n�Z-�W�:��Oo��p���Y��)�l���P�.��F�-��mHf�H�/A���lP�Z���J����	����^&���^�CP��
������#�o��M}L���v�~�G�}��(V����a�z�p���2R�-���{�ro�a���+������6s���}��Y�����kkD���@8��"��`�0UN�I2a&��(�J��B� �V���'�v�y�q��7��&��O�M��>���j%N����:�q^��K)H�1�f
��KD5Y0������R93�h��x|������=?a�B^VR|���`�9��
���J��)���
�\#us�M��Y*���D�O`�FzY�����>���u-Eb�;~��|�y��,���I@h#g�Lc���j�&�a�
l|BQcR��_��� �0�
a�������=aJ��:�F=H8s!}���qJ��)��Bn)N?��������>j>�]gd~q�x5Z�b���=�;^�v|��cSxS�t��-��r�q�fw�~�H$5R���	���S�kF���/�����k}��r�9��bJ��]��O.�����h7i�*��
�x��������� �L�v�Rg������;���4,K�����f�h����������vV��}�#6c�Wk�C�Pf��N��c��b�������5\��I	�S�
B�]<�f��Y^���QQ{�<�(@��H3����o�e�amYD�����R�B��u����E�[Rj�w�M�+�m���F�b�+6�x���b1����x��%��
0�^�X�p���:6�B}������TG�8>T&5z>)��l�%�� ����}��{rZ��������{��!vh1.Re�1��U�'��~T���������Xe[�U�'�lE��hl��&��6�Je;�]����d��1^-[�^�\��E��E.m_�y��\��U=/^����/��l{u���Z�{R�DE���Wq�lY{,,R9�����Lz���N�y�PS[���y��(�J�������?E	0�I�c-�����f90Aj=>a�"�hr�t��qy`c��B��
y��������mAn��!*%w��$i�2�N0$�z0�K�cle��8���I�/$!+�:�)o�}���p&
,�@�r5�n�����?���v�<`N�D�{*4@Iz2�,�.L���0�`I9s�!�m6t���H��j[s��i,���Gs���5�,	�v@�V�sS���4� "�[k_���H�h*�!��8H�@��mF*��u��x`�L ����aH)#R;T�M�GaJ�jJ���ke�)�J�A��O������x��M�=M���;P	���D������o[!�6�	O�#��D7J4�0���Mp��d���
��R����c��/�V�����;<2��������u{������
�46��bx�EO�?�h�=����|T�s��j>��`��"����x;h�l�XA���(V`������o��M�x�w�>�v����=�0=d��������0�saz�����-��C�2�S��G�fr7 �\aK<6 �e��J�������]��a�����cm� j�(_3!`�L�}��=@�8's\�7sc`0����������#����h�+z.��G��Wp��~���,�0�i���`�$��{��|p�5�%7q	O�.2>��p($�S[H^t^� ���91���;���.dx"N86f�����`�}$�"���
�#R%
 �'���Nz6��j����x�EV�:W XS_�����h��A���G)?�A��q���
1����"(q�,�F��2Ik�/�N:���c4+��5�^`C�5g�1n�+|�>����&�8��C(��L�[�Y�b\;7JpU46�P�&��f�18�w�+���oX���,����\j�:e2��y��r�lx���#|{s"�U�Q��[.8�=F9/��gNo����-�z��P�`>��Ge���!	�����Nl��5��X4�����$_C�i��P��nq��V���)3�z�9���^�O8��1FmnG��]ku�[7���[���Y�ye���tc=������P�j�}���mnk���G��nA�Jc��H[xPp&�h�p<M�)�[��HKT��`��0���d#��U�(��I�g�ck�hm���D�J�	b�U�NaD~�`���Z-�R���<�]��=<:�(y,�����.F^�oo�<�w������������!G{��f�������b�4�5����$�,�W���~?��5��4g��s����. .��8_�>�f��j<���5�&>'��#���+qFi8�s8�3X�+��?��C�)��>��#��'��������Y8�&���,|vM�18Ck���q556�j�5�����U��_]�L
eB��=pt���F����P�I��T�n��d�_�0��M(��qH�9���k��T��gT�tK�4
f+Q���3d;����������������2�����|���~�z��Et��[��#�����o-K��&xeO{q��7H�����_���*�����"6[��g3�UC��F��������K�X���/���� @s���El�^#�et����.�=n:��i��A�'�B*�7TA�FA�F��
���� ��G���HP
�r�������N��Xs��O��w��qP�[
�)l�q8�X*V���#l�N�����<�46�<�u�@���t,�ff��
@v�T�i��4��n�^�N��cY}�L�Q&������@|��u�:����vR:v,O|7�B���3~�cQ���&�-85V����$?��+N���������9l5k��n�>����j��wzV-���2��khh�.�<��~��/(3�d3�LS����nD�'�6�}b��U������������SDnQ��f�+�E���=}�!A��P
���X�qf�����2�t�B�C�$8������Z��,��y�>�}�	���gMNC��x@�;��b���V����}��F+��r>�4���G
cn���z���K�{�,��4>Q�$�g�q�qa._c�~TFVyxd��-Z v�~{6����-���	}����<��������JV�
i}�y}�={jU��z�
�� C%{[�b������W�KPL����a�8
S���?�c��ma�>$Q�k�S���3>Fv���k�*����`��%��j��s�3y[S����YF��DQ�9��x���(�������2�Do���U���2H&Ku��i$���Y�6���x�u������I��ca��7�4��>����{����0��yz���Bi������?�~���'4����D�:�z�{�&�V;X'���q�������������EV��Z��).P�0pa=!l�=��\h�ROA�%����/�CH��aC��{������T�-���yoc����A�~|�Bx�H�qK��i����W:h���r��+l8�i�U���~�/n�*dF�Q��oU
B���U��v�+M�^�&`��&�0_�w���M�n~>�A�5=+��d���u|�����k�l`������>~2����BK~����P�KWVv���0���()������u1JS)�I��bn�XR���X%Ce7]5/N��^�o�i����������0�(�1i��[������f~2�+6*ZX�Q�d���E��J\I��B#B<�]t�6|��wdn8��`��[�������xxG��$�$D������V���<���9�j������3?3���8��y���S\-)%�
��q^�o+q|�1_��@$�������$����w9^�#��WI����t�$�@!e�&rS��%�1�c�"�9<�i��d����.�*�<�!�X5�2��.�f�- ����D6z�M_���\�ZD^1��]�Tk���
	��$��]2�'���Ov/'���9�4O���+�neG><+N���5�u��L���{�t�%,��]�}��X������R��8�fR{*���F�]l��k/�Xf���m�L�9-�+��
P��poH�n`n_�73�2��3��/��L6*s)PJb ;9��p1������,����J	��3��i����7���3����h�0��V��n��w�����r�����8�
&����<���~�����f����az{!����M@�Og>��������=����s1�JxrK�$���w���n�{�^��`aK��t��sGh���P8��`�h�����nA�)�_��e�`%y�C�V���������������E�����d[ZD�=H��!�=X�
xPiPP"l�	=��]$����B.eP�7��������i��?`.�����r�5t
����M�`��[k�O��d>���No+�*Y�Skk�Q<�����S�2G��/����~C���}�,���M&������4��fS���b���h�2��.D�k��_���.�����#]Q��%�H�[���A�a�$�@Z�G[��!��s���Y�u;��)S ���z�/��<�5E�r�����P�,y?V�d{�V� �N:��0���U�~���<9oU`�WV���_L���h��ju��X����kP��*���G����v�������y��������Z�[��.���o=�}5;�}Ox=AM�����#��n)���� ��B����oyj� ��C1su?�?x�� ��>��Y��o��,���5�i��QO���,�����`�[�[�[��
� �6o������p������p��bJ� �h>���s�&�j�#�6@0-r����8v�!��|"�=<4u���yj<��[C�,��q4F�
�'�[~�o?Qc4�'��O���g�:��\ 6`��-Y0I���]b�KH�����"��+t��'�����ly��)�����/��a���h�����jX�U�*�����i�����U�Mm36��k�-�Y�z5w~�������������p�oaT��?t��K3�������#�'5^����-+���vp3�k'+)[�Z��,,��e���"HU�
W
���h1��B�+	����|(k�9�������b�ae@�g���K��^�����W��)K� 
[���#V�4b2�L�A4�����t%�qa`8L����#/,���n���S"�b���U�Fi�Pf�=-�)�@$jYuEA�����
�}D��}��%�h����;��p��j���[^3�G�-�u��B�y�A|;Z�p�:�J1���Xq&�}�[�n3����m+����4���D����<�%���������A3�j�z�O�PV�vH��P�)ks�n�%����B�U�w�
���WS����[m	��)�����Xy.��m��8y^�<�m#o%����+��������A����T������ ����%���(�Z�DW�QC�Y�S��M��.)-�nrJF�����d��;����������o���;W����F��.��� X9D��~%�������K���5';q��cR+�j��'5������<	������z����H�����ib�"L�V'a�%Io�0�3k����x}�	2���q�f!��g�0�A�����`r=�6���R�����������D�P=a���#����?��-&�@uFh[)v�h���VF0�a<��	�L(����F"�vHi��������������t:S�J�-�� �jE���n<�glr�D�Q�O�X.�`h�6bu<�!I��|
���M"B������H�Y���Q�-)�1��&T?����J�X�Is�#����qcEV+��^��h�D'���\�*��W
�k���7�h���X�J�['�6�W�-xo��a�)�+	���8��V����Z����v1'-�UU'������+�.Ts�V��B�e�8�#�)e>�m1�����VF,c�H�
���s.��'`���E
��s���W�-D�����g�����.:$!p��H@w���-����[T��������~M����G������j���|Q�qK"��B,��@�p�1�>�uR��Q�N���z��%���a`&�����bCJ�UCU0TT���i�]�����%�
h��c�a����\�9��8�kA���������og��7�b	�����t�]q��>g��S��`Q��H������d^������u��~}�iCvL���,��T��A�D-@��YF�����0|����m�����s;L��0/����r�;8���������e�e����2��Rq��GMx�"`�3u,*��wF�HMkm����G�j��g��������N%EN�/�4&@�i<R��f5o�F�G����:�yc��u(�4�&�+���u���1B���H���L����j��{��d�B���7bJ@{����Wj�(��s4������9(N�(��}����P�,�@0�����=�1-t�Z���Tv���~�]F������,�qXs�^�q�3u��ou�J��������1s��D��{��+�W��\ex�(=dt;-�Ozp#�(8v��S�|���4Prn	�"R`���9��H	$~���\n�[�dPI�r>c"h���D�m�����h�!�%(z��L������*�.�m��8m',
i�n��GUO�j��������e�m+��z�\�4����`RU)c\\������i��F����J�&���������b����^���G����zp��O��1���b���c��IUa&�*9[<#V�v���q��}:)�T�YkU�����#����
���Qu�{Ey�V���f�� �E)
~��(A"�R�E`�p���b5�%7��
et�d3�&�Dc�pZa
�<VZ������Tny�RzQ�7&}Hz@)��q����t�_v�;��N���?A�v(\M���j=�+����n����'�O87�b�0��u�_��1��Z��r*�r:�O�����
m��n�������Q�ch+n��T�3(�a�A�������������=����>b7:�<��_���������j&-��?7A��=L�p��	M�,l`i��K��P������L�D�S
l:p�1�H�V�R[���������D�7�@T��1�m;lAl�,��{��e��W~�
|u�������������~�����T��k�F����'��
�����R�����C�6���������<n�D/?��5G.�
S�*��a����9�	��	��~�)~�B�i�K
�z`y���%��Qp��V����0%��R)�����((z�z��6��[�Y#�-O�����RG������s�	�A$�q��'�%,������E1���?0=�������z�����T'zO�q ����(}C��jS���G� J�������0���^���4���~����?�����}��[����oW�N��N���Jl�O��>��f,zz�Y�P6Azm�����\���7�����z����������1B�?�������d��I�o������a4�T��z� �h������F�~D&m��^AP4�*=>�����.m��l��}��fn/ we��;��y�/v���L�b@u��}��iO��~}�@������b9h��d��\���
&X|�dl�dr�Co8S]�B
�8!+w)��i^�7�y<gy.��V�=����*�@���<��t�V9��Vj���������jc����n<��J�lK�|��$��������@���U���_��W����Z��[�6jA����[�_A�	�Y���a��~<�'��#����e�i0���/��[�?v�;D������Q���w�����:d������{�G����\i7�$���K�_PWK������)T*s�1TT>��o�r2�����(�������Kh�����������j�e}7����V��������W�|4v�Z�V��'�K���:��J��R�
���/���2�3��\���8��9H��������:�
I�A�\b���G��uN3P/��W�0%�Juz>�K�/�?���{w��w�kZ��\l7(4"�T/�5���������p��w�� �A���\���T�f���!��������B�#�{{s��u��zw�m�&1T*{���^�_�(�5���Aq����VYa��(��U�^���jq��m���X��;E_�H��h�4(a:��L����D����$�A�����o�FyH�T��}8��;�$������O�~,`JH��1,�A���pz�	�4�Q�K�C���n{^�Ow�c���qz�Zv�jG�5%�_N�r��4qz
$�j��@�M�K�����	���1I��]|���A��/���\ �`�iI:�>l���Y����>�J``�F��R��������+�G���]'U����+�.���o�m5����s�L��J�K��wJ ��B��@,'e�g����f���
���!Mf�~8LR
aO��G�w��
�za������Qf�L��P�T��<�Po�Q�/� �d�%��:���n��)U����^��������0�*lr^��m���&��S�2
��Sff��a0x�Q���=l�#~�S(�z��:�-�
�Px�*��(B�}�S�lP5�"���K���s%��J��#����Cx�"(�S��u�s&�Yh2��R�B�.f�������cV�Ct�O�n8T��Dp�t�_4^���.�"��T�o�M	���,����V�������<k�T��l5�Rw�������y�g�i�{�?x�DcTri�{��3�2�8���*��ua)�u�J��L���������Mhn�����M������|�K4�#�a:$��emYtv+� ��0��3�h� ��H>;�y<��6i����~�8�����v�Y�,��	�w>Z)���}���>��pb��8�iv� �St�����1R�#Z��{����=�>�*_��C���~7&�T��B�8*���0��n�c���n0g�/���g^�!Qi������s��5,%��oj$��|H�s�2$q���/��
�� "�v�f��^�w�*��F��v�\y�o�2hB�/�/�K�'�{�#Qi�e[m�X�Q���X,V�g���1=K~�ym�?B�yt�lV�m�/�Q�����1b����!]5�8-nsaK�]�;������������a�#��E����VWz{����|�@v�$�_�����ME�%���@!�{}��Jo>��[�K��^�6�y�9�h^���������/��V���MH���D�8o+�q������6�mu�M���}���B�}��@�m��������������Q��{�E����~������jc7������>���}7/���s>f��������Q�����H���VTp�J��hUwR��-#;"������?V���`���h?�b��N��p'��jx�Y*W����Eh��AU��u�:�3RE'��/�N�0���)���z�a8]�@45<h�E������3��J�~�IeF]
zM>�YD�fJ����:���2���6cr����B}�6�$��AWHl�����jM^e��5��4����N'��{_"D������0����>2`���-�*6����+5�R���0����W�v���n����?����8����4��W�FQ��cg78�l�1��;�3�@��y2%����S����!����?��-�p4�2���V�x���_�q_QQ�v�f���IR���!x�.�_���iR�;�8n:��B��bx����#�����u;_�i:�������e�a ��}O���5�<��V?*x
������>��V8��S�������0=��zm/p|Ka���.�-���^�5h�e=����pP_4�������#����g�
_>�,p���1��`��Q?��O\�s������^S�P�k�[0{d?�_������5�s�_�����q�c������j���!����2�:3�~
_�dg��r�@�_��s;��C���&�]V���v�D�Y���`bw��e�V-x����:Y+z��`w����^�h<��v��X�ax�Z���[E����t2�����H�$[^������<���gP�{{���{�.�^n�5U"�e�kH����)P�Z8���%���p�s`����N��~������d�6�a��"�l������v�p�S�Q�:����\����x���
���������#/l��A�6���W��J��4���1�*���=lm
.�7�A6f�8��������`r�!}	'���p)�__�}{F�A�0�QZ�	���z��������;���%(�	J��K�������tN���rO������6rO�2�V�?I��I:�F����������>o���WV}[�L���6��R&{������4���u�l�>aZ����rM��qtz��J�������s��w���!�$�&m�����1Z���QkMT*?Y�����7r������z�=��Y,��7�UA��T��|�Ap��K������'�/6��6���<W�KE������5:��i����}yz�����*�~�>�Lf������s����-g2�v
9L�I��8�Z�[�#�(3�]�"�Q�^��t���64'�J�'�"�<�
���~NBB���a��uB��_H�kc�J�2	� �Z������q3K��idT��`�i��������*�5�0��`��d�=m-R����&4`#`���F��P������!���2�Y:LfiecsS���y�U�a��g�Q���d������d��axTD<��������I/WI�%�����V�v5�;g����N��q�u�:q�����/j'cM(�AV��i�wIc�Q�/���z�@#����qi	��?����7��RV��5�����""���?�^��i�Jj���`�D*k�gss�>V��_��>�k6m��7Y�#X�Th�����,>BAR����#7����`!��7	����o�����C��pF�7K����F,T����z��~#�A'8��[h�����&_��j������V��K ����|�Wz��a�8`/�@��%���.��5����b�$@TE�xP�����$%�Ab��V�>?(Q	��������f�`j��.I�������������~=�!z6�jBw�K�uC��12�B
�1��97��T)������(�u&�3W����P+VP�p�z�:PzQ���n]��r�>�|���<T�>~�}�Q�����Sbq`tc�#����s(�I����Q��=%H�������!��

���"�IQ�14�}�SQ?���,����!�����)52�����OCtd��\�S���L�*��Nq�I���f�Yp>C��U��E$��^�|��,:{�@h��"�V�0��q�����������v���������������4�z����'X��c��p����W^��g!������*L?�(c��!}p<�@1��������W)@}�!��������	S�V�h����c�h�
���r�C�c*�Dn����4W���\1S�|n~K�"ql^c����>�OLe���B��a�����O�j9#�<���%�����������H=�Q*���QYh��g��`�zp�_����~���U�[Yj��03�kr	��8%��Ve�6
@�������*���%X��B���{���kl�7������%?:�(u��}�����;�3:����8�*W{PJ�������j$C�R����X�NS%�ES���2"�.�P��!�A
����I�����	xh��"PQx'�~����������W{���8 �z���a��~�1b�]PT���b���k��?���&I :��7D�zi��KS�y�L%qG�x�����JQ�_;�M��`273�f�H����T���Vr�������>U����*MB��=����^����w���_o�K����<����C�#�I���'�������~U�(r��s�����3NZo>����/�I�s�BD ���O^f��V������|��-d(~�C�n�_�M�e�W���c�:-9Icx��w==�����Q��o4�;��~������{<SZ~��dr%^
.b��a/i��5K�-T���H@�������~C���p��f��4�)R�aNF��X�CjC�vM�TC��+���u�8����*K�%��c�7<V`S��$E����0�����J?
tIv��o��uD6����B}�]�0���������i�M�����<�����&+�QEQ*
u��)$^������'����#S�7�
��PYn���������
�)�[��d�dz����:F:�����B���
@8q��l��c�c�jQ��4l����uI��.�gu����sH��m���h��1����V���|p��K��x���]�"������,(��u'���>�v(��P���)'�4��IY
m��im6TbC}S��0
�������������E�XMu���D�1��PO�>)�q�T��B*�A�v���ZG�=N�^e���qL��o���-f>����� Vg,�=��]��R���mTV���7��~|�~�'���3�Az0|�[G��I44��I���IN��s}*C|QgH|e����iY8�����9s7�G)N���^��:%���"�Pq�}v*�/��r��8�{k�9�!L�`����p8�;+�e��Z�]
�/�k��7/	ad>�Z��	z{�%N�\�Y���3n�RC.tJ���yX�\���).[)��������U*�Ft����.ldq�L�����VcD�t��������h�T��JeG���+�?%��M��|�B�2@�^������5�����?PO��7�I�_��pd���d}��w#���~N�S��-�����+T;v����=/v�����j$�O�*^s��LlQ��"El�����5Y�-.�<�;pH+��C��x����S�
�����
	-�;����������������v�#��rq�h�zhJ�E�Ic0����Qd�C6iz�e��\���NV3�DKBD?L�P[�jdX"{}kB��VrV��Wg�� #'�j�����~�q]�i)��o�����@t�[�����|j��j�
`P82D���������)��������
�
,T��jzc���E�� �m4[�Jgs5�\��l,]��}��t��u��!E����A�&���;����`P�I,�-�X������e���j��������Fm�I����qvn=�GE�Pj�����V��"
��������}��iK[R��B(���9����AY�'����h����p��+��^/�K������Lx��u�i�a$��o1���{��M�e?�\���
���pI{�-�(�:�p��P&���v/�Z<��M����G-�X
�D�S�~�R����1$��XgU�\W�#�)��=3(A���u�]`���"����h
��j��j����W;�I��r��H#v	�l���|�������3<��[���al,���,u�1�a�L�})�0���2|�Q����p�U2�]$���MbEP��2
U�e��B����=6N6)X���#��� V�0����Y\N:(�b r����)��U��/��p��y���Gp�Sg�|6�����������)�%��8r�ADts8G�dB�Q�;���qb��q�#qV(���_s����uC~p:}'����q����ga�n���F�6fA����P�� ���L��7r<�����d,x�:VC"�����$Mq�{]���(
���sI/�%*����3�Akex>�
�$`#���I2=2��|��@�r��Tr����� F*���&��[u�L��}x+T/[Qs�%��P�h0�{�d��s<��;m�x��
����4v�-��(��6��KU7yhHC<m����V�����(��us�9�<�:k�['X4���*��H]��.{���lCU�kOC��^7$Hu�(R��x��3�*trPw3�h�k����
�.lg��^����fW83�"��I>��IF�� ��X�wa� %�|X�	(
�V��+M(7���f�+�{�MF�J�3���>���>X���>Sk= QD+.���f���2�a}Q� C�h��xH�>5s�PG�tc�S�T������|8��O5v�9�����y�����z
:������j]C�w���M�"��O��9D4K�6a*����#�����~Ib��`?9gl�o
��OZ��6���-��Is���9��I��9�'5�o��W:��7��=�KB\0��j�#t�LQ�5i9}����}�N;���+���6�3��cqqj�J��=c�
����-X"��<����I�Y��O�/�0+��%3�$i�l�!�'����H8�a��W�{�A�I3�H}�IL%F03w��z�����}���-�ih��]3�|�1$���"�)�@�qH��	��O���:+�����sD���SKbB�]�q$�:��7%:��,������YL�b�`�<$h8��1�hI���J�^=���Z����������!��d�\
�j�:V�x�%����0����kk�|������uO�Q�$�X�X���Wt�WD�i��)F��z�a��
8�����n�|��n��\�L��;�	'KZj����V#h�>+v9M����+�C�x��@�I��}����2x��R���6�b8�O<�/��T�M����y��G�I�KY<&��t}��
��������c��a�-:��j���g��;�x��\����>�`�Y]�l[r�au��<�T�O|�I~���Ll����nlqd+�I4����pF���-���VL�G�����#
�u����; ��u��s#��8+���*a[���d���G����u�G��v/7��MOzw0
����yoZ�-�9[\)��7�%��G��'����:�=B�;�D�R�P3�N����C���ts���y�i^���������x��F2�}���!&`�a�R��l�������E��}z��\)�������Gg����Y����<��/�/Z�6?��-~������[��`&cG����>����+�6qw!Hz�����>�)�2`}}���:<s��;uN��sw^b�N����;�He]����q���&�
�M�7�r��Z��N4��!�R��;A\���R?����5~��U&~2���([���7���*�?�ON���.d��-�L�,�(�>���>	U
���x��_��7A��	#����4+=W�A�py�'���_*���rUy��
#7��[����q��&����7��?�.N:'�v�M���9���h�$j7Ie�����R�0v�#�o�V���X�i<�������TC}����Y��l����#H3�
�.������r���3��J8�pf�m@2�=��8�������������u���Lk�]?\��E�}2���$�h����	��N�M�)�>8�|��^�2�@P��D�_�\���618�xK���ot��������
dg��M�7�JZ�/����!T
�Hi�q���8PJ�\6�tR���d<�o/�C[�@�6	zH0����
�����G�_k��$��w��7���V[��������Rj��f�x��wc
`W�Vv*L���~�D)��o�  �
���49�8�L�bg��u�5V����dA�A�.Fc�����9
��d�SL��b�r!F��G
�Co�SS����H�D�:�-N/��(��^/Y���H�1|�HJ���E��r��)$��H�
`N�s���co����;�;pJj���h�j�kD1K�������
�o�
��j#�[��s��,��L�E��(��P-%��d>U<;�`���<���.�������/������:o���n:��KN���B�	r�C���AQ���(O�����tYe]m��@�D%�b�c��#��0�n/
$Y����D<�S�\��F���yzQ�>G�-�Xfu��/�����ue�<�-P��t��g�i�R��:���4����]�Fp�M��
�Z��Y`@:�HZ��)0���PW8(��u_8�(�b�5���B��N#dPs5��8����,�H*
��<��i
��^�hm
�����S���v�f�hk�FrxL&=�A�CNq7��&����l���pH�%a�jW<��A�u���_�O��������}��Q��s�j��[o����5?�/��v=�'�7�g�����y*G7����s��bR[��Qe!3�sz��x���}S�t�I���+�KF�������:����h�������0���k���F@VLn����k�<�TSGp�Bm\��0vAMQ�~�����i����D�������,����gAd��Y��:�T�.����7�+;��p�r{�>v�{�h��C����[J�7�1g�9s�s���+L���k�����|�e��Z��--w�X	fp�5}�>��<���@� �(�����y�0eQ�Z]����p�MY��C��Nt��i���D�h8L!�cU��I�
Z��pf���B�h����'�s����n���S;��9��u)�������h�`�_T*�{�^m������<�Q�q@0��<��X��A1���S��`'�3��"���)�#+�n���C���%K����`!�
����(����!p������g�V9�`g�D��lj�����w5����Z����/�F�~�����C��_ADsD^0;�P� ),z�X�-M ~w&�R#�
��)\F��`�M��ET������<��m����q����JU�W!Lmr�f	EVc�����B��\�0�}��%j����A�Gt��{��J�c�&>Pz8N1���S(V� �5�)��=L��"�#x��i��w�d5���E�~���h�E�n�����k/eA~%w�w3�����HI��G�4:A�"/D��Y
������N����/� �Q�����Z������bE���z�k�����#j��B<��0�G.�d�4yg"K���|q����R8O��q�����KD�Wc���r��okj�!���Q�o�`���')���{+����,��B�Y�������9�9��V�Z�������O��:7>Ck,�y*����F�s�C��-�r`QkI��M�:$��@0������u���}���j,�I#�w\�C��s|�T�(�3��,om���%2���@���X��}�C�\�/���@-������M�4Nz��\1|�nP���B��s��<\�����ye�u�c(yw���Mf�A�]�,�t? 8c1��	�6��zd��<��	��Pl�����LJ��&`�L���x6�i��a����>��.������N���T
Svr��y��)P}�CD��X�C;��|����I(�!
�/�J�bw�<��F^��F`	6��t��,W��h�y
DL��0%� mX(�\�����H�c9�qzq������S�p�[7���[7����Q���p��_`�E����YF@�0���e���X�������g��98�������]RRg�I�f�;�0(��7E�v�����j���\��
x ��L�F�(��������8bK��(a��[x3�i��?���6�|������#�L j����V�R�K�����]�S����i6�5����O;`*��d;����3��/v`@T�;0�@@R�o+�h>Eah3�xL�1�O�Qg�X�V@W&��0	�xm�}�:��"R�x������<�i�g������,0���@��&9/��r�N�	�
����Oka���bm��f[%����j{���t�b�vB=�Tl��Q�u���*��m�6����N���(Oh�@z�0����V��1I�_oe�h�XE.	��#���aA��5�w�M�������I����@�m��I-7���3�$��|��K�	��2UYW1���|w���AcZ��h�����J�/�TeKR�	��)m\[b���3���{�Z*!Q�N,��-�GJ
,�:x�F��qf�W�;	��^�F������5T��(T�����|�>�w�����c��:+5���f
�P��V�U�+�}|�M=P���k�%���'�%EGp%���*a�+��_���������U���!as<{���j+�5�3@�[f)M	8f4P&jI��l`l��2���A��b���a�4+����X�����CoH9�����v��24�a��HUD����G)�����G�%�
�D	�K!8g�;�������8�v��
�o��5|��}$��q]!�
��UFI������{���T�V��Y��b$z:'B���W{+�&^E�2;��f\gF�P��@&����$HfU�Ov)�\�M��C1MDF�Y���
�Y(gv�mk��MO�Rx�a.��mD�"�n������n����h�4���n3��h>n?�FO���Q�~��������Ye�����YF41>}���Fk�����6���g����a}�m����um]�����O��a`LZ�Y�sl����%8��]�%�e���/������xOE�������&����fv��Rb�[���;]�p��1��@����]$�%zw:��D�s���y7��"/��o��1�VrC��J��t���
�4���B��`)�P�e��f��
Ye?��^H�J6vE��nx3�?�Z�o����`��&������O�C����X!=Gg����8L�T�9>�|��>,c����������e;�F��K������K8���e5�%Im��Rh� ��8R
r�@2�����0+	Jg��C�MP.mf:�ppV-%r'�7r��Y����:�_��+�2�S�lz��E3�Ef�!��])9����7l3�X>!���F��W�YE�����_�R�b�����T��o�����Y?_�j�071Zi&,<
6�iX&��0�l^��%�����'�ja
�8�yM�\�g��i�@�`��A3����f_�����Cf�<g$���[0-@"���0�D�0d�*���1/����i'?nk�W�-f,���C�~����io�����D.B��@�������Q	='�M���$���2k�0���=��q�����������Iq#:�C�����J9���kk�)"D�������'@V�3�(k�Qa=��'�K�t�'�3*���	&W����}t#���W>$�1�/X�$��%)mE��A)��3?���%5���t����dJ��@��m0M�<����f�	0�����P�[pa�k�����_=+��>���@�}R���9��A~���=�>R��4lk���q%A�2Jrg�q/|ik>Bv'7�\�"�
�9�_G�hi$������	.�I~�'
v�z(n�,?e������(@]��mZ�Lk�|��
	&��+r�;>����GM��nF���=�
E2
n�� bt���� �K��:�	�����nL�4����,�������g�*>�����1�5�HW�*�����IOh���
v6��wt���0�;�&ld5cM����������1���KiD/�Ka�#��yO�x���
��f:��X��=��8����l�\���C�+Zm���J���p��I��#4�#�qe)M:I��9�J)%����,U��	d�W����<�Q��0W��kk�Je��!�W���=�>��N�Y�����2\�b#���X�0 w�)�����`���p�lc�y�0���L^fC�?:V�9f0R�����p����#^�g~����@�}������v����=�P)����]����'fr�-���3�$=�k�����K��*R�?����	|����������&VH�H�dA���E��,�	��b�����@`�"�1�
� �P���~��/9�D���mD,F�F&�*�%"�kq0��d%�,�9Q
�;b	�#H{%M�j����I6����fI���Q3R���}������m
EV����"5��OD^�1g���z��A�'�q�:
\�x�0Yj���@oaq���q+L���n*���������d�M	�9�#Dl�������������k���������|�i���Y)JW�_��.@%�5�p��DuT,���*�m������jA�6���t���F���kN�:e�{���,���=D\N��q�n�3�"5K���x������q�����H'���l��+bI��~�B���:=����s{�9'�3�$_�&���l����A�`���`�Qt���-u�';/L�-�U��AZV�z}��Q�9"R����e���*����G��:�)!P;@�:�{o�h�
�G��3%��
;-.)��9��M���k����y�;
��b�+�������5�]�]bK��,>�H[���Zh��uR0��{��
27�81�b�[I8�W5��`��^DR��T}-7G�U���2�J�e���+����}�m�����|1=�NY�_�S�Z��K��&�a����7�$�f��(i�����I0Fw�@���S<��|[�nHj�NU�I�����1>-�*B���i,`����w�kX�����h`�|��b�3��"�d�@J���p�z�U�67�A��
]�!ea�%{`%A���/LV����&S�M#"��?f��Lo�6��^�l�9�RC%l�����K��9��F�$MT�o��7s������$9���~���<��9����3Ha(�|���B�h�j6*�6��K�s�#@+#��K����y��U���E��������\�i:���3B&{M�r{��(�G�5`�����,�0KF���#��oPB�?M&�����,c��`6R
��l��=�L�3��5}`s'��1�b�'"�%�B ����f/����<J	�.�:2��xn�n�����5]�+:�x$�sxu���+d���u���K9bG��W��U��ja-���Vv�kj.�u�~��H�O*41"�*^`H�Z���g9���6{�������6�W��S`�u�]Y(����h�2Y�,�A�q�����3�b�V5v�����4�����urZ[��I����0y����n�����$��_*a�#���G�Z
�M��d��Q���S�1j`4��
�����U�B.F����Q�T�B�*$�'*��qQ�W���;9z��cM������������N�d��lN��&=Yb��R�*������\�c��M����.A^$}���2&1V�?�T�x��p=njH���o9���)��^�dq��
P�B��t��`�YL\�B3T�"�1���R[��UO>��2@\7�)d����L��x*A��������[����9C^D�+�9=�	d�'W�$4x��o��I�8E�+���T�Z|�?�>Z��N�[�w��pzi�vtp���TG��~��:,��<�����������CI���
�w`��B�cY�����[�vv�r���l����%8��_#��_���v�#_�0Gz�����m��>��o ����f�Ec�M��G3����k^,�r�/rxTt��J�^?�������|����>�(�����g���|�b�� ���#��8�o�E����
n�c+Lu]�)��
�9��>�����dK]�M=�zTGX���a������jh�5��q�Ys��T~+x����b�~c.�\��Hdr���Y���=��0��M����N<�{[K�J�?��>(��"V�#�|�0���>�z����J��K��|����D�%�����)��qCh��|�Q��ur� ��#+���<@P��<8��R]������U|}�$"�^���
��:�Y�#{�I��s��TEVL`J
>�Y��6��$�^(	<�2�T�d����e��$�b�i.6K5��KE��{2C�^��-4�Lo�*�#���l�:
��B�-�>H%R�b�eR	>���O���X�1����6�����u��}D�W�P������{���=�N�[�~c}K�z�����bgy�< /a����<8'H|����XX�M�Z[K1j�/zF�b�i�7�W�	y����Q�)8C/$h�����:�Q�%~Jof,@���`�Z^���}/b��`>E��i<�ab����_������%���^�R9M���+Cn���i%��L��\x���������L�@��F����[���7 ��}��.H6������/��H����7�I�����&6���b���&g����u+�Gm$
��D];vs�K��=�nq)�2]�[�e��`�2������=�VR���G���/h������f���A�w�
Z��B	�`���p��������6����2����s�i��v)C�i:�����B�E&BU!g��y����^0�P^#�z������i����S!���fN��9\z�����9H�1�@�����@S�y1�T�<�p�:��^����2y���=��K^�c:^�Y��f�]^�%���|<m���j��(qlIX:fSi~:Rjn�o���y�-��i���T�
\mM<�y2%p>`�lq}�k�8�������{CA���;�����CfC�Y���-�!.��r`����N�����u�5����R&����I5��4L�D~.��N�]EP���)a�J,�8a�I�M��hE�IY\.���M�<u�\r? ����
�Q��-1h������a���P��e���`�����	��4;9a�������l\
�>�_-�UV{�=+!���OmgO����?����@���-��"��R��bhU�3�I9O��#Tl"�����	�4y���5��As���F�|l��{�z�;���J5����P������8�6x��N�
LT�p�?$bA��
�g�,Wb���m�C*N�5O�6��U
����`I��>gc�W3��-xc�Al��H����%��>M{7;<j��-G)�4��HfC�'nWV�Y������T\G�G[�%���9�/��~�
28,O������,T
�"�5`?�Y5y�������Q��h����9DI,���~:�D:C�i$��/
���P�K�FQ?���4�����v��f �D���"[����|G(�)f^�TG�99YBW��^c;��;�yfpvM� �/���}H/�;�1���:l����A,�x���%���G���-8k35ab�^�!hA3\N "5&*�z��|_�n�&�C�)�q�)�>�Y�R=��'�DW���(��@
�D��&kG���"�U#��l�x�{�3�Jd
H�Ug����g�j�lV�K"8F�2P��Ep'����\��Mb��G�N\�bi�'e�,������^��
gAg���_�N���d�^� uL���;H���Y>T����C&����k���5�*|}��������m��k�t��;b,h_������>�����������Zck��gc$J�8XlE(����-�3�=������V��$f7b�����U�����BCvA	���J���x.`6�3����h��.L�i� ��4��Kh�e!$59&�5;ev�b�F��� ��~��-���l�������{��������P Y���E4Gz���=+jE3^%�/t,<�r,�-x���n4�����3_�<�'"�FCq���~�Ne���<��:m�7�;-(��!���)��m�}7�y�������wVt�$S���W��`(���DV�`��w2�HN�lA	Lu��l]"�+X^F
��;m���{���q���)���e���=
i�%��q�5L�W�fV�+4��Qcj�{������.�����O�MP���x�u#�������1�����\��p ZW{8�9���U9�O�P~��ye���k�v�����
���K�L�.�O��9&������">��y�h��thT��[ J�]��V��� �_��/�)��Z#Tf�vu}���us�J�"Ic�8�����:ipR����!��"�P
	�H�{�]<���v���i�+V��t�9S�J��(�&X�m6�g^�9�O�$��?���YF�&�����L�;'�f�6Z�?�7,1���T���~E1.+W1%-����V��p�{X?8�=�f���U�A��-���Ih����("���K/��k8��������q<'<4{�ME���$`�i��	����S/��Xc(���8CQ��~Q�%���xx�Y!��\vX��V����N���&���:,i��<>�Q�����;��ad�8��Y�L:MOPK�B<4����d���c2;h���)@��`4�A���$�>���qD�&�aS�m,�k����x@�(7*X�����2��DI5�x6a�]Wp�dk��ljH3!S�+@�,+t.Y|vz�*��U
*��B���AQBP@w0������*�@P[8�W��1G���������"ta��b(���t�4�����L����R/%;�E��V�'�r�-|�����4�c��v���i���t�z�U1�'E�M����>�*�}�`.g�d��@���]*�r�F��w�-�2�F��*�XtZ���&�1e�W��d>PS�=�_��]���g��4f&�JP�]�G��R��U`�� �g����xb�)��j���L��!6�|6�����<�~H�����c�c���3��j�<��quH�o�A�XT#��T�$�s��M���I�m��Y�Su[�wf�s����g�>r@�$f����k�����
^J�1�����6O�>\����������G.S<�x��v������r��s��������3��v�R|a�]�m��V����HtD%�I�3w����{���h]��v����t��`%���I�t�w��v�8������������7CI
�[>�V�wT5��q�U��E������n�\l�,��u��qg.0����z��
Q���fJg��;���=S{MZ�_[3�rM��<��bghg����e:Gx*�x���c���tC'Z����E�/6 ��V.���x�������I�D8�{��!}����MKY����������5f|�FY3z:��2�."T�,9`?3����A;C����g_o�����0�%N��PO'q�30J�;k�����F�g����qg��6P7�/���h���jV��=V����g�O�o�Z���5�����������a���/S�n�fo]���9���*�P�H+Z�r���WE��p<��r�E#(~�4=�,!���s0T��R�mO����)���4C��o�6�'~YR|��y�f����}���,@�d����Z�9��b�sx�9;�����i���~m�+��g����9�0s�g�@�.��	�WH����DcG!v���	�!����q�
���0��3� QP��`�n�����tG�9wP����p���K�?j��Rj�w����#Q)
�D�h:�"\�����O?vuY>jz�A�h���Yvdp����!��Bb~����i��k��P����r~���)&AnA��Fq�*�����w���&S���l�	�_d����������L��/H���}s{O����x��|���K�^�}>]���v��u�������)���?\�o,�
3���0����.4\�,��IPa3���W'Z�Q���~M�x|�
P_!H���M�	o�.{��BQ�\ 2�A��X�&�@��~���XQ-�5�����s#��Wq�D�Z�Oh$����Oo�}|��i_^]10�=�����V�r@��l�~��V�/F��i�8���`�g�,�*#���es
���0LDA�7�qQB�T��#� 72��+�8��}�Cb[���[Lj��A�f>3	M\y��V�$H��N�=���^h�Q|<��5��j)[��W�KH6�?�.o���p����������Y9�bb��@�uE�������W�F�h�&�qa�a����*6�Mu��������@��T��S^�#��:J='��fc���������!�[�����E:��b7��XH��Dt~��R/d�����#�3t�gz����0��������q%z���:_AN����'�����l�;���\]��!xzs�na!��FwP/�����-*�~��F���ZA�r��
}V1���b������{����B���
�F8N,}67� 7���=� ,��ktftPl���Uv�A	E������l���!������g}4f��|��F�w�A����l��v���@<F�)	�8�QV'E)&��q�� k�TW���4DW,����O��7cf�[[f�W ��[r�)�sGM�f�u�`�Pt0��W���3X���tO��>��5�F,f}�d�a+��;r�s�8U����w������������MQ?������Y��0	�_�~Q�N-��Dnum^��?��g� ���,�9$�U����+�uQ���
��l�y��u�i���-���'�{���@�fx����O�k�-	���^JU[�K��q�v~`��|���E������{x� �g���_^���qj@6?rMj�78�k���\|I�iaEn��&�T�[nZ3�!���Y����M�![�������yk@���r�>'t��}��6��v�~�>������z��W��;��
�-��p�����QT����s�/�A��!�����$&�~�����z������;�����M�� ������Z'��f���y��_^\�0��n����J����1 s�\sf�[.	�������
��]����F}�s��t��(�`��M��b���j�*4���{/�	.x��I���Lq���+���R��;�-T��YE���I��"B�' �Nof�[W����i�BA'D8Y��D���t��-�h
�>K�#'��}�yh��f�Y6F���#�	
�E���S�Qe���4�f���}u"��	*����i��x�I���u���K`�=���X]2�B7������:�>��<�G��s�.��G�;^r������4}��b�)Q��,��.�e���6[�_�^s/��W@����/#��#_�&:z�"L��#9�3�y��A�5R7�`V�@Hl�%A���%S�a�8'(�pEe�l�6�L�����:v��3jW,:���|�4��r����N��'��5�##��G^���{�+nz������l���wDQQ}�����yF�b��\��0��	o�%����������������3��r�v�!n)�9��e<I�`�GL�"qQ&�	N��y�@��x�i��� ����<#lMz��=rD��k�Q��\��f����^]���]a�����/p�`��t>�����r���<;�9���$)�8����;HT�����d����������T��Z�x��pM���J��y��cp��.`��lh�r>�4���c/���<�����3��Q"���V�����h��"����Z�\/�q��!�����	�"DH�����\%��"5�m�6�o�@�]��B<w��+$�S���mbl��g��R��j?j��)���Ee����N)a�yv����	����y�C�7��?{o��Fr$��W�Wd�w���p�Z�C���iq������S
dY��b�������ZYPR���u�&Y��Kdd�I;�O_�v*�e��l�L*�	��p :��A'��0!�
���v#�^�e���P��A��sS�&�#���	b�GO+(*)���&��uZ9t�������M�=���xPD��Y�������J�y�3�|��Rp�X{�s�c<���J��-����"�Z�!=��N<
���r�J9"{'WDo���lRc������c>���&�3�h:HD��� B�P�X��J��N�}{t����&�X�Q�2F�5�6�h�5��}Yk���*���95.v���%��� )H��]���+G�T���M��������^5��r#�<7���~�0R������ViB��y�F��5����w�������y��/��0]��K/��6S$y`�]���/����Dl!�#�[��y���c�-��:���o�N����;�sRk�\��b�2������x�*���������De�s�PU�5l�f�=c�q��e9$��F�$���=��&6t���Qo�A	�KIQO������h��j�����8�^��$nV.^��Y�
,,K�)���=]Lbo�T�T��	Zki!����E��w�����b@<�����.�:!y�P]]�������pv=�9Id���a�����!�s��9T-C���^EFJf��yHjZ=��G�?S���$�PK�b�Ml��9s:Qq�u�*]����y��	f��`'�^9�b;7X[�i���]- ��d����s���?�J�:��nd/�+^�g*\�����8�BY������E���g2����s���F�2���w.��O��U�]���4�Z�����*��J!Q�"5�]�B&�?S�PQ;;��|�\p&[7�}f�l���zi|=.dH�p���k�[{�B��8�1,�N��ux�z����=���c(���[����E:��!����lw��?�����D�����p3�E)�V��i�	uf*�<�)�mzW
�=?��d�J|+`��.D������r ���q�~y��AZ1��)j/Y����F��WQ�j��d��J0�C�Y<��k��<�[��������&B}F�������r��N���yd5�-�S+4�����e�{��f�&r�W�2����iLg��Zg���ESA�Lp �z�ua�)���YO��i
���fkRV-\�o�%�������V�T��N���[�(e�XIqse%�Q���|
��������?W[!�}�����l)�0�u�k������an
i.$��s
�r%�"�N�f@�S����5�:"2F�+���������fu.ia0	TI��������z*0��&�}��o����@����`���D��X��v�T�}Hj�������02��S;�
��u�V*fb�3�O��9��A:�\�*��@C��������u� ���|t�b��4���D*�� =A����f�@�J&���0	����o���:��pz>0�g�S�*"�1Ex�	�j�����qR�B�3sQ�2�X���"��%��;�s��&7��j=������Hii�.5E{pt����
i�����|��uS�kbR��gy+mI8�8�����������%)_�FhP�B-3Y=���nn��!6�
���������A���j����v�=����h�������
��S6�T��6��l�1�z�Rkz�F�ENW�[Av9�l���b��v�X��H��a���
�lw��\b�������<&���t���o�o�\��������S�z]�_<�d��y|V���<#��0����UU��%?������u�UU����lY��M�u���
�+^����G�i��J����,n>�0�vvz��n����v��|<C�"r����������?,'I��8�R�&������#m4�s,'9�������Ny��b�rQ��9!�v���V��#�,����
:<�8����{���H~�L��G����1c��>�����gk�(�]������������v��3, �!���[�^m���f�O�-z���]�OG��)Y�W@�<7�~"��!�����o��/O�Y$���{�9!	�����=���%dH��JU��/���-�b� ��_p�y��[��� j;����PVl:�}LF�.\���	eZ_�rZy+�P�j�����
����`0������E��X�e(���OAgP�*"����_Z��*+�U���~o:]HZ���o����/t>)
�j���W����`�'p�v{��_�K�����1��B�����r�,]*p����Z9���r���{r����Y������2��:��DC�������c�
1]�$,`�������!��^�`o���_h�@�/D	D��~g/���?���
]l/����J��:����������P�G��@9����nM�p�����S)�@y@�o�*�FST�`-(����bd<����VzY��!	�J&h��[�})H�j����YBh��B�w/G����4�x�Mg� g������\��	W�M��O����@U�i)8��n_�
��t��K<�f�r�)�`��f{"����������J���M�l~��\g��^�
���=��s��
!�����C��Z0)�������-8t��5��\��+��%6���j�`�� *�9��r	��I�9��[��"
�Q�,���&��������l?|��5��/�����O�C��:�6����'U�x:X�I3���z�%XM� �����������!2�������asX�7�;�F��/�O���a��a����XN��S}H����8�/������MI�RUy�gb��NY
��'�z\j����<�F�Z������Q+��w���N���3�K������g	?����������#
N��|. ����DN0������H��,����bvd��9CxFw���<�k>MG�`����D��,��\c�&��(UPQ����$�-|uE)�]�0!���}�T��:��Yf7��D��~g��W���oG��`�3���i��-�����S Z���_���Atj$�@
5�������PEk�R�p��T�b��!�W���K���?��p]�.��_��(����B��#y��>���Uq$����`->����+���HM��L����#)Fb��k����D]�G������^�;T�y�N/��S�:��x$�BO�-3SR��$�'��tXIF0�����U��;���M��~X� F6������6;n���.�L�I�Q}(��8������w���W���?���<?;����s2^��d9��X��6kI/�!�����mm\����/�O�m�&���h��~�����u%�����3���S%����ac�sd0U	��"���_5	&+�d�!�*��Q��.i�(�R�p{&��f��oI����v�
d���fN@��&�^��H)^��jg�u=�
��^y��c�_	�+��,nz����~��'%�
���-�^�����f5v�����d�N�����pS3WY<a�VH��7Z��Y�������U�V|��a�j�n%���R���� ��PV�5�y������M���B��d89Xi�Y���0��)oJ�)�
��VF!���e( C _��'=�1��q�b�Av$S�Q}L�qvb��?��>�[y����~�i�1����z������f����=d�	�]dKw�qQ}%��x6��J���&�"bxTNS������{9Z0�nr��`�3�)o��N
����~�n7���[�u�����s�g�#����*9|��34r�BO���h��V5�2w�hQ u:������1����r�����������W��c�o��d-b��kE�~c]b�~�&�;�k5I�W��0��*1.r��� �uW�w&�`�SV�7n�(�q����}yPB�K����Q�)N���]�
j6���0<�2N ���'�I��E�V.1���(xE�������ogA�����;|�tOq<��������5h�W^@���&�u�C1~4Q���$�uJ�T�U0��X'��2l8��_���
��t6��J^�����vs� 8:\�(|`8��t����~��|������ �����uQ1]�TQ�h�<0`���]�8�����k�i��V�����9������u���R5�k�]��V�L�R���T�"�����
^����u�����h=6�0�T�O!����CM@0�}J������Z!�V.�]���H5�-��Rn��	SN��:
��z����_,%�V��h��dMP-�������<AZ2J�@��$�J���������C^�=�6AP��<����h��b l���P��x�'3P^���)��������i�^2���e��Z�Xt�[��*4{��vsm���os
����A��z�?RD�����gp��3�1��w(�3�9��+T��h��udk}"3�s�+���g����k����V�,�5}�+_�4�����"�eS{�
�����w�CM�b��{�})���^��p�*��(�f��#��z$KAa�M2�bt�Te�tX����q9M����0�`��P��9���H����C
n��%���a������2$�r�AF?�����/K�a���*+�<�����w��>�:�vN5o�~��D�z�&0;���H�H?��9oye%�9%"����[�jxg���O�\� ��U5���%/EI&�H9����$�����e�#�b�+��&##��J�L��t�P�������1�.�������f^xJ&��L���������W��N#�k��~?��v
�GB�pd���h���X�w�>�DR��	9���I����g��U&gSq~&�����SE�T��K�A�Ew��<�:���q����U!������EP���_�a�+� ;�JNt�j.r\�]u��v@��Z1u����o��>�3M��S�T�����2��p���]���F�et�,��W���C��A�6��?������<��C���W������?���:>;�ok�[�[�B����6��:����5���f�������
z	g68�J��~����z���z���'��9-����}r��u�`�����a�~�z������y������=d F!(�E���l����98;z{l����*+�{�+����_����^o����M�����]��nG�	y�m�G��9���2-�	�{r=N�<io����m�c���?�3���<�KxT��=�L�wG��n�W'�W��\ad����c���F��
���F���U���$�����<	�~o���{��s\�]p����V�����7G�r-�{��d���A�����p�����?��@�
q�����\k�M�|��-�	�#��	&����J�s����-6���,!��,"��j�M9�dq�&9Q�������x*��k
(�TN�[��xI�����"Ik��2�A?1���>"
a�q����������k�^<1~gk��STT�2�PqS�^og�U�7���~{=�V0��Z�"�FJ�������(������BH��g������+d���K�$E�Y\�>`������~�������nKiJ%�N���]������������K���F0�kt��U��J���@%h\c,��
���[A�b�r���b�go����-�����/o"_�mv����������+\&q����ke)��I�-���Q}��P�>��
2�M�X�8�|9T��2����PL�fr%���K�������bAwo�	�����V��4�W���d$���Mx��oH2�^��?�Q�����fJ�� `dOn@c�����<��/��K���3.j���Y1����+7��A�����F�m���$������k.���7{R�T��*��1d��B��[���mQ��x1�P�.�c��52�����F���w��~'���5@��Fc@�=��X�Oc�E	�����6\2�w�I31�s�|����������T�x����S�_ht+(,���Z$�|X�JsW2w�Fy3(	�6+�=� �w����)���(h��Nb�F��e?�|�<��bUC$�RY�����6y���=��������+q2G#'j��(����p1�Q2��-���^}��,�����5�N�d2��r�K9����\krF5���`�j����?�g��c�o�H�P�l�v�O�����A���4��V����ns{��D����K`��P�(�}\������5K���C������r�[������v/j��q���z��a����5v�=q*%��x&�9j4���DKng�<cI�~�f�(?D���G��?0�l�Hn�W�O�/�G�"~*�o�U���?�&1��<mJ���lt���%���?_@*2����I���8��4���P�J�������~B_�H
P�5�����7qESW�������d������FL��c�'���;PL�D�dQ�"�I������Uq;�C-�)�	��)���������^~� �o�**�'�S��(���z����K1W�C:
@e~0Ar�zUX[�L����6���=��Q���_��X����~U�KMv�o�L���I��$��7�x8��\�5��&���s	������G�2�:`��z
�A$.R���sy�����-k����9I\:@3��bcnP��nr���-Sv!���]
������a$��hV����%�AX:ed����v+��x*��yrnP��~h�.�x�{^g09��������L�#Ss���W	'��0'-A*2&�iY�?J�	�(�6�� ��	����Ci�UR/(�P>p
���n�z�K��iO??��91�{t1l���WA(�"�K�	��M���{����-�+��m�
~\N�H�����U�-@6!�\D�`2���x5��W��3��a���Fct���)��%6:���e���i������V���d6����
ca�$��~�����BI@��
p���	���p?+z0�����|�(���������g�%�%�{�w�~��t�`�_k�!���V���(��������J�p;���&HNdwG�K0������)���5)�#��k���_���q(�K�r�Z�8��,Z�r~�V���V6Q�[�������w�[��o��g�d#���(�x�A�\�	dh��[�u��-������[�e���?��D}�~�zZ)���_���7�GA^�aJ��E}Z�\���wh
��;,9=(;Y�tR\o?���b�U�PK�5:�e������{��^o�w��{��|�x������\@��9a�����*+�U����y�(_��z�4\�0�6h3o6�:w1�%�KFeH�.���k�x��_tB9� Z{�\����P�~�������iM���(E������r-�F���5s�6�x0���^���\\�E5�����+q�2��G���*N�f9���s1�"��G��������~U���~/��*w�-�C,�R
b�Y��d��G��r���k�e����
��T�4�@[������*��!8���(�l`��U}��S�P��W%.O�}�[��fz���f4�;��\�W� J��M	�#�Y'�h1;�%�A?}�EUo��2�;�L�G%O�o�g�4�`��xi��$1*�����3�Lo�qKp��A���K��Za3�F]&e�a����9��w��U��9��We��//�/y�P)
*w����#�=����'G������cp5��;VE*��,���iwq8��3�}�'o-F��*���l&~.�'���O���B��G���ru����^=b�K�JJr���n������^��hv�u<�G�tn���v���*���<
��'N�|���U8?��{���Re�����������;Q1.�7���A�
��[X��:�*5�#e������!!���&J�U3��Zz��2^��G]l
z2��!s>���i]BT=p�1	�g�
�V1g���a��CwY��,�&pR�3��X����x����D����L�fgG7r&�m�5��y�������U�D]M+��T�5���|���i1c8�u��������?����r��a�J��P.$��8�K�7NWke�z�����@�%���P��vX���W�
�<
����ZD+$�[heOUx!�M������4��V_��Yai��B��>��J�a�P!��\�R��Bp��8�c�78�(Lb,[���� ���q�0��a���N0Zw�����r�&TQ�w�z�IN#��l�S�Pp�1��6���_J,�L.x�H��s�h�����������8F�:})uE�QK��J�"o�).��CX��z�=�QjHd��u�u��p�bo{`���v�U� G&z�Z2d�r���������)K
�?[����r����(�{�����Y���A�B���g�`�?q�4�bt�$GtRo����S��t$Q>MS]]aQ�g�I����Z�	�zoHd!��_",����T*6A���$����55��e;����Z��n1���Kf���MSLf�3q$Ep��es�D�@s�GP<�j��"�8��$
!��)N���t��Uk2Q���3��;�
G�L�F���\�3+i�+����HUe���)�.�qp���*
�U��P!���R���e���U�@&��{I4~��}��m���0����X���%�B���
���Q�)���O��������S(-GY���������	9\a��H��X%XmD����B]����s����}����+wTU������������c�)����Ud�4l���3�����.����9����������4}��Z������|CY�<����9��C��yi�o�h��ne~��|�J����8O�&�5�����%�h~��I "E���<�e
��9�,�f�=��(����yRV��9BnAg���w����J�n�����������7�R���|up���%�m\_�	����+� �����+�F]u])�N��jS��NhG�����m@�!p7]�x	R�Oz�SZN�����%%,�,�HV������rA��/a`��c`��5���=������N�>h8~�������5;Ox�C�o��t�1�6L��V��>x)�\���g���Y����!t��_�j���l��������v����#�/��s�%��n��l<�+�����C����b�iUC��u���#��]7�!�`N���lq_���*s�1>/���u��H��30a=1���=�s�����������`_Og���|
�n��/
�&�+1��j +�Lf��1�-nq���Ag�y��&��c��I`XW�m�x��Z�qW�E�;EjU�|p��l]S��j�������@���HM���_�����C�i�z� �w����
��=�z@>���2gT~��_����bM��_�_E�+��G��q�����<�_2��)w��{���{���F�ylo���,>���K(�K
����!�������Z�L�Eu=:�4���b�r)���I�7���6�L,���\�\�0�0_�z�!3�/�(@!�bF���6���7�\���<�2���.h����f�����+!D�!,����%�S���a?3��~��y`O�oC^PK�����%��	�Z����$��r��4o��E�����RpD9G��R�HJb�+�
��W��WPI��T2���[S����G�HW
i���a\R2y�.$��rZq�V{�w�8c�|��!w6�7a��2%��{��u>���j�A��������:�������Sd��jP�ypZk��Q�8�X��Bw_��{!���-7�av��
��MF&&3�k�����e�2N���K�@������*�����n7��`�.,�M <sAS0�J��b���rL�J��lW��#�j�$�x����^��Kq�#�!���*���Kc��2~��db���
�Do��R��M\UR�w���h�������|�i��m��d7(:Ny�Z����3��w�t�u�y*�bm�t��+63N���#�g�Z8��;���f��+l��Q��&��������p��5^�����EW���`nl����������\�j���Q[D/<>�"}�����m����!j%�lp
�I����l�_�M#�+�*w������i�V�?
���c�xZ����.���xyM��l4�����:���W4������o�<�����J8f����r�Q�N�\Y�y�\W���q�Rq�Q�v�`{�gw�d����v��;N{�
�[4��O�J�%����yf.��=�>+��_��yI�	D$�j�H��YI!x�K8(nYg�lq	]k����;"�wC����M����d��a���mN�<iK���B�Z�mk�!�Nc�����L��fU�����F�L�~k�w�4���*������
�5DH� �z�nR���>�7��J����N !������e�9�)m1O%l� p�$�����Q�=�%�uG4���p��H��"kUy�G8W��t&&������(���+3����|�$6�����hmi��1C��Z�����#��OF����'-��T��:� �Q�{�	W����P�x9��y<6�Q�rQD���$����Q4�gN�#x/��>M��;���u�*�������@C��%��|4@�R�h����QM�"n��M%�_@-�����&
^���'�S�L
�S���������:���
�!��
�]�������q�����y�c|/n?������?>��S�:'<��r����!��t��!����l��d6p{��:h\��	n���
�>�y����M�d`��,5���E:�j�Pv��=N9bf$�� J����3�p��$��u9�����	���$�����_�}U�'����o�$ 2�h}Y��4��w����i�!2�\!��&�$��bU�x��(4���t&t*1�2qmQ'���r��\_DN��!(PP�rS�J(���9�e�'�nf���:+L��������L�2p#�/Z��|���W9~8��Z���W���Y�3��{�<��
���������[oj�c��9��x=�l�������e��k1g��x8���	�ro��x\�����q���������������!���L,5�j_���&������}�P,3�R�J��+9
��Kt��)���y\^�~�z�j}���z��W����1�Z�G~v<���V���]X�4��<�BeTS���]D)+�-mM]�'�#p���"	��A~�5��hAe�	ACa<�I��G�U����$��^L 1�h%�\��&H*:A�]���s��f�l�)F�����0���)�zhI.	^�c��XI���,8�1Mgreh��|�5HD���H.#�����8+��i������]������\*9�AZ�{]-����s�Z!X�Q�Z�����
���S�z�������#��u�[��9�=��8�eY{�-`�j�wo9����{�a�k�{��b����������c�}����2����"@�"�K(�|��B��C2�i'��������,�y�}�E�D^��@s���X�8Y�>��K��&u}?�$Ua��p��x�\h=�����biVC���1T�}j�2�A���v��m����`�g����������ZXz����*rB�������]�u���YE��~!U�Z7{}�kn�r��0���k�6�:����_Cw�E����b����w��L����Op��m��U~�9;c�c��:�8U
����X�h�q��\�Rdp(r��W&�'z%[�<e$!?�Y�O���UtI��+oJ��c����1�$���������-Z�{�C���������S������b0���?<G�k4$�
��X�'�t02�]��~������&��f������l�"��_�<z������I�����m��{�+9,��*�!�LB2����TH6h���H����)i2WO�Cj�p������h�����?=K�s'����a%�t{l�+�S�K"F�Y����69N�h-5�\���t��[M�	��wrv��R�F��&�6����p���5fKVD����K��C�n*M�o5{��
�2h]���(��$NuW4;!o:0��^1�R�K�<u�%�t��>���x�.:Jgm85��66�%L�D��;��W��t"�
 ��k��U�8���u�s��|�y��3�r6P��%m�,�����y��_U��K���A�\nZ��L�Wi������*�q�#i<�b�X�zy*�����P[KE����><e7%%t�3��N�w�P���A�~Lfv��;}koD%b�1��`��x�a���*���l���_h�0)j�yH����?N�/*0�r��!�������58_��RY����PY	Y8=I<����>������B�^����D3F~��m��	�/)�=��-��0!�D@��4�7�M%����
9C�t\3.����2T��Yj��S����Tk���*�`�����-���dp���9C�9�v�z!�,�O<"��,@=2��N��C�f$�jb=Y.������Nm5���$����h@%C����&�����1�#�E�����$z&���DVP��u5�pG;�.9��s+���0Q��$���}xLF
I�B���q�q��#��C8������t���	���x�(M���-�����3�<.��)������v�p�7������������#��[H�iX.��M�L�aYW�+1���gC+tx��l���p���C��,�������	�������|�\��K%u4�������{`J�:�8������Gmd����XK>���T��	>^F}|��
��7������z	�����">=.�@���[:����k��������F��o������L�J2�!�]��hdA��qW���,
|B�]���F�@�$��y���Tb����T�b��p�Z\%���JdH�5�r�r��Z	�;6/�L�>g{��'=H0�������a����y�����������,3�CH��|��e��8����*_�Z�f�������*����f�4��>� )�&����$� @�xe�:�.�������I�k	D.��%a��P*�U����$�ULo`�>��K)0�C�|����#a����� ZH-J��tP���s���j�|o���W����������K*��k���������3�ISA����
.:N:e@��%-$s_)N�,�hI�NjW�x>]U�i��Q��2��9�C�*%RI'����H~�Q��m��Y�_av��G�S1��o��k���,����S�W��
i%�7�i3:^j����=�-��Vd�@S#�L�Z'��u�����fp�����LiP��!�N�k�$S��P�G�G��b �2 �Y�6��N^o�R�Z�D���m�I�z��=���p����O�U��]���=��FN�h���Xh%��r�/�TNP89�.Ds��_���,;046�U�@d�`+4��G��6y��;���39y`WX�a(���4����B��HU��'�R���Q�4	�c�PW��+�2���*Gj<��m���e�L�Y5+�w=��GL�dO}!���Oc:���%��v�R��� �x8U	4�����M-��*��G>!�����@�Lr
=�)��&4�!(A�!X7���L�>������,�4w��W�<~�=��|��}�?����[��L�@%[���^9������q?��AX���^��)+�����<-���5�	>\�?_O�{FX���Ym�-���T!H��S�����K�q���+F����Z������C32����=�|I�jQ]u���M\�����t>���_g[pQS���mQZk���j���E�XN%�'X$�8qBY	%/	�_��;p�����C�T6R1@p��#<f��0+C���^�!�x�a���ZC� �x���n!��D������^��DZ����F�U����%X#��Oq]Tz6W��
�=�P� &�������ZNzPreYcUu��� �x�
���A&��/��";Y�	��&e�Q=��-<�S���"�/��'ktMj�V$Ev�r���:=�����(�3�OT�Xm��h�x�����,S���Vn�������ys4�Z^2���i�$��B�
����Q���T�g��G��	��:�(���������)j�N�V���ca.h=�Q
�7���o��%�����b���^2���I����PgC��{s�@���@Q
�c1T����l�Aa����E�����:�S[����pR]������1����`u�L9=]�
��6Tp���?�m�Wr�&t��J=D����6��o����0i��J%����F���^��{���n��|�z�:��<GLXP�e~�+��T��xQyS���Fg��U�����1����n�3�/M�e�h��H��K�~��_�{&2
J�����G0!q��������L�q|d��D��]���E��c�����S�W��zw��)������.8���[7�|��HXn�<���I���&�������jB.ZN�{��e��ie|��:_a	6kl���|��G�U����-;'f��q�_�*�;I%�x5��T�����K��J*.*z�6B��T���g�{��{0���><���W2;�~�?��
ED>���K�pl�&�Q}�.����h�m��a�`#M��w�J�|�V���G�zM��;��4��=-�������(�����nS�9���~I!�������L=�C�R#��0Yi^���")J��
"'��A��q�>��1��sbAjO��
_�������t��V�G�f: �ZO���7��2yyy��������9��T��_�{��
:u�w���S�H�;9��iw_�_�\�GU����G���:y`��{:��9����t�w;m�*3N??��F5�y�������pcJ��!OC�'��x�5&�nq12/9�|E�q 2���JC�A�v�/����'C/�����S�% -�_X��J��S.�7���R�PU��NE��,_�=�>X~�N7�EK�@�����Ud�H�`���);�D�xE�S��ry,��������"����Y0e9��W���=�t��o����,�J���LV������,�fa����������w�ly���%�Q���dH�[kO� utco�3Q�rQ��UlY��������o�
6�3��"#`��������8\�`l.R�&��������Tf5���v:4��|�	���4A@Z����GQC&�1��$-jF�Cyp!����nr��)C6H�cD��Y��i9"O��vNtV�1W{-?7McF�M�����$�-��2*�
��(�gJ������)�?�Np:S?�����"������C0D
]�x����E����i?��,Gn�hM��A�������*~�k�,7F�B;+l�4%�A���&z���Y.\b'`~�XHVEi�y����c�'��d���z:<9Y��zI�2NmZa�<�86�����[@o����-K�����m��>���NtI�-��S�V:�xbY1�k���y��FP�O%�/�T��L�����M����)?Om��- 	�E�[����������#X������l�����<?�Je���DEC���e���*�TN�{S��]#�W$lrh��N�����/��+����6I/����<�`d�6f���?�
��C-P�cE�{����?'�"UvQd�,��p:�a({05���@����s����(g|-��rl@�U��[�q�hG&Yf>���x�������v�Z���98A����:C��n����#����������,u�0�l�*g�0�'w��Na&�oh����UMF�?�q=P2���
�����;R�Q7P�7����L��"V.����7����
�;���u[}|HF�o�d)O������r�;�8����6��m@�C�g�qA����q7����������oM�����X-� d����a��//�$�yI�
..�___]=����T�Yd��S����P`���^w-��>��A-�<B>;o��������T��$s�m���]l�Bg��G�$'cv�syq.�b����@H%#�)HQ������G���"�
�D��{K<�-��oT�S
���������Gvt����R���$C�|]�H#�y�A<[�������k���
=�>�-$�*Y��B��	4]*>W�R���b�$/ja�����$1�~�������������7��$!�����kdw�g�4�[0,u�?D��
�����'_)��e��M��\����`�B�q��?WX1d�!Kh���~acmq���"��BG������N��fq�=��>U�?y[���\�>����x������/��L�b&"��2�]����1Xf����rW�-@/V�e��jvI�:�k3WY��,���a�J&Y3V���09�����B�!�&r�����7	���A���	-��b*���fzV����|w| "���@��V�A2^,p��oE��dPqm������ 
�T�oUw1�t������i�jC`�*#�i��(�A��w�6~�lb'����X�|��N��u���dL�"+����i��)�a��]k��	�]d��c��'�3�5|JR]n>�����Wz,��(��dSr����u���� E�b-����,��
x�GB�RC�T
xH.@��|�Zi.���j��9qhv��g�e"��RY�]���d
4r�]�y����j�kTP��t@���,?{Z� CY� S�,��zj4�5m�������)�=NG���ES��4R[1�s�r�0kS���7�?�%2��8gM�f��q�h�fF������N��'LNO�z��t�4�k1����5���^�(�C(���q6������h��|H���z�
uN�	��<0��<VQ�L5������V�]*��Si�~�;/p�DK&p��|��R�8�>��R�1r��6��1�,�����5>��Ndr�!�s��$�7�>��e_�d*-f%~�7�Ro�$d�Z�3�����)Ie&	}����������y|yyx~t,���w�^�����,�������+�dM����(t�s�i�R����}����Z�G��+�9����E��a�3�����3K���(k���s��s�u��#�������~>
����BN./��L�7y�/'���t!����i/�^@B��M�I6�B�P�Yf��:�^>���D %��;������Y��|f��,_v�������������t �^�ms�y�=�SkP�����eT���V����E�
�\�:�K3�*��6q��.�W:b�V�S)��4���l,z�Z�\S�`�.��M����:�$�7������H�dM0����9��0�v��@�}9������UF��N��q
(<����5��O��W���c��)|�!�z������l�#�9q��0�IP�Kj�DO���]���@�,W �\�����_�t���$^������\���;���k�g�b���ZyK�����Z20}y�~H�m�J�6]��n���l����_,e�
�Q�@�|�E��oO���O��t�qA�NU�Ti��`J�H:#;��D*yeI9f����6��e��������+U����Q�D��O�4PaDa�A7w!�a�r�>a���u�����wu~��������m�����K�jl-���f�n+��4�����Z�����7��G��w�&^t!
��F�����w��XO��1���3Ax�;!�{�:���o��s��������h ~'����f��w����������C�g�w�{�4��-����u����]�JD�5`�!���C|��M���r�(��g�����@Z��4����@+.�a���������c5��Y�
�������W�}�.���	m`�4���;��<�U���������� ��D�v#��h+���X��J�yto��7���h['�8N���Rz^�i9�����k��f�^o���as(���N�]��j�nyss�!�����N��-6��B�)���$���S�w�`���l��B^�R�]�3��w�eq(_^��2(�7%9�*�������`��YsO@������Z��������{{�a������b���wk��#6���8dO0r�.h��<��M��K���dB�}�(��(y-��M�Ew��S@�
'q��@5D�J��^�K���b�PC�6�[Y���-r�g�HF�����4]�/�
��-��5��E���������0��&���65{�O����;%[���9�a�j���� {���,Y����f����	A�V<�����?���K�$���d����} T^y1]/%C��^~�B_�9�^A^����"S_�",j�d��)'����`��p��x��P|��	\k%��=��C�������-gku���>+pn�����e����Z�����F������Wb� ��&�_�-�.��]�Ze�U��Q����B5�o�^�>�����n�V�����E�����y�7
�r��&��G���-����8�F��3m�[�LVp��	
�a�p�!�*��3�r2Y��'�\����pf���U��T������a��������a���{z|J"����gy2��*��������G���du��9u����������D��������K����������6F#�'��'o�_�����*H�_`]�x�x�yf5����������������.!0���Ve0�����f&��-J�)sq�'gr�>�����J���2��.=�=����������wr9Ef������H(���.8����$)7R�9n���J����H��:�H�S�Zk�[�b�z1�X&q{�C�f|��<����*�\�n������_�w�q{���A��XH�r�)�i����f�2t�r1����q��j*H��
�e�?�?'I6�P���$@T�a	Ryu�� s�L?;m9�A<��&&���
n�
Ve���@���e��	��,<�|��4 �I���zi����	������R5�:x���*{Z�����CF���|Qw��-��~g���g!�A��&���}@t�_�������&��F�T���#��0=�
�EU���]"�.#����P����
`3�3�����|!��E����"����0��l��=����F5X�P_�hp��<\�[0�
w[�N�Y�[��v������B���[�H�E�@�R�J�SS:oJ��\�>�%-�����5 ���fV��`�\��J�����x5:����*�>��Q���+2�kA	-��U�R�F'S?'�3���#7�
�G��Cn,%y�WN�$kz�}�V{������M�P6l�=��dcjG��uyr�{��ok��,!�_��/�5�
�_��6�� �}�@9W��O�Z�6N3
����g��l��w���=�0��7Ds��j4�%I������G��x4�����|��ih+��~�E��m(7n(0ZPY
���?y�|��p�C���~oA����3��y�x����/9��1
��r��0�x�������A���c�O���������7�#��Tb-TFx���L�Fh�D������'������T
sd�r���T�,S���)F_���w�p�f(=Ov������f�4r�[���mH��
X���^P
U�A�%��H&t���8��? Q
y�U[�
����HBeG1k�������8��5�m.�D��b�c���daF�i����6l�r�?~x�v�Mk����A��CK ��d����=m?2���{1�'�\�+�rl��� E�1���XG�6�mT��lrt��GQ1�H5'����N�l�#G���.R�%�brN�h��������4��&\%����f@Qzfu�[���wSFyS��B5M	���_V��!�������d���$&6��d������5I�t����ZJ3�0�i���N�C���UB9B��&�pC��L���� r�K���?u*)�^21�T�����h-���7���5��������g�N�M���~���4�K���'E��\ve�o9�
f�����"BM|��"*A�@����u|�z�����K������Q?����������[b��[��V�����H��k4�Q��^��N����&��4���gex��N,@�s�3��:���o����{R���z�;����V���K�a7sh�@}�#����������3����g�����@�H���y]E��
}U�^�����d1.�.���1���c&�^ �G���*�p(�r�Hf:R�pB��F���������i\�����SK�*�De�J����vQ�7������)����vv���o�s�
��O�9L���xL|*8cN�x<9(���:�7^�k%j^�BEP!���-��P�C
�^���L!�'g
1U5p9s��+�zr���� I) ��F�Tk�*���9�R���Y�+��8qny���eu@����LY����~WL1��~/<�������U[K!;*�"s�����m_���l����s��:���*r;��+rQd���-r_���$�2;����q�B������1B�P�~����Q4���U�m�%�e�����������E�z|*N��������U5;��x�������z����?^w..���������������#oP��������e�����r"�xC� �S07�{�b�"s���J�p�+�t6]`������:���#��e��J�r���jJPM��4�L���R4
1��G���J�U22�y����M���3V�8�}��NXC!�Q�X���L��H`��m�J��=��/�m�6z����������a�5�;���P�[�C����<�p����}],����C��|u���B��,�/�Q���X/eN�n��}�p���A����u����of��S���c2E� ��E<���Pte�����������n2�lo�s<��]���hZ|B��Y�9
P����F������U��������d�@�_��m�a�N3j���z�
���v���7k����O�����O��1�O�J��t�H+���$��|���_�3�Z"���T5��E�
��gZ~��9�%4������\h8��`�'��K7�������W�7�����3��X������C�V`T�� �K������G�Z��N/^��=�&^��Egn���7��A2|��7G�r�)�2{��������/�@1��CL���yg{�]��fq�@��*X:jM
q>B`/��{�E�q���=>����G�"K�H�:���d�
������A D�w:`KW��`�Ln�(7�{��z�\����F�Y{�|^����%w�;���1]��kRQ�A=�cGr��gs�������NK4�;���n{���V��l��h�>Sv�-�P�(�}\������\���?������`{��l
w����`���o����~�'�����p�%N�)��DsW4O��%��`��#X#�#,����+�1s���d�zuyy�(E�����]VE[��r$�oJ4w�v$���lt���e�/�*x*�|qp}��'FT�thD���2������-��-�!/���ln��G,hvK���m���?���C�w����X�(�wm����Vou�s��;���N�����~� �3%�fn����T�'6�'r+�n6�� ���^H����cxW{1��]��<�XAy���Y��Pk�\H���y2�M���������u:��.�j���.�7�7hQ�cP����� �)?�+����;\0Z@���x0�|���e
�9���'�dW�����G��8?�=��.��z"O�Sl� b��
���@@���'���p;aLO�%~'����D������!�]�=D���������k����v���]�gg��R�b����1q������Io�?������9!l���K))�_O�����U{���Zm�(�w7��7b��9�%��t%�$<c���[r�l���� 'G��d���w/�g��.�b��� |\Z���[2j5������3��b���p���BBJfL�Hp@���*���N�`���#l;�u��)�_k	�l����
�S���4���	]
�r6*����������b.aRu�����3�C�Q������F�(G���K���+���v�
����.��Ns�_����_.W�����$�^iI��V�S�7w���� �9!��d� ����6[����&��q��9�t|3��1��r�^I�>�������@��I�����:XU�c�>.O�W��/��=+�������/���-��*[^vO�^�w/��������2$�x���4#�in����~�!g��L
�m�6Cs���P�!�hQl�����@S"s�6���z��@ds�gfm�.
���e:����H�@t���/=���h���@G�2�D���]�������7�����I�r�9g��Aj����&���&GDv��c��/���.uyzpv|v]���L�'3����<�E��d�[��
�_k�p�G�)?�
��%X�XB{�������j'L�>�9B���f��W��K
v���:���x���a}g��~5'P|�7�M�����F�)�����������?��t����n�HV�?���^��������A���uvv�{��_��|�i=�t����������BV��Z._���)	���I�1��(��K�����`���i�{*��)���A���u��RR:�/���]�J�S"z����:��'������|�7�n�.t7O�$�;lMAEbXe����}����r��.J�/eN2W����(n�oA�v
�H\E��v�j��D��5�
���_���Ai
���i����tpM��{�
N�X/��1������
���f��������t�P�i@�uq�)%�j�BV5��ln�a�����(�����������y���	T$����	��J?F���A�V�:2�5���f�	���������&�,d�T�T��*�-h�P�4����:�J�F�L����Y�7��191+�xlM������3E`@L���7��-�m�.OO��!3�w)9�UL�����5���@�>�(��z9M�L����$���s�.�4.CS��p3MI'�@_22����at���J����m�xX�\5���X8J��:@J��d�����]�a��i��AS�K
���v���1
����	
ib�@��`��}D�)�U��P)��&�_����9���������8��@�b�
����jU��)����=�>�_�N%���l���D���V9�R����9���>$�\�����h���b��[H���'L�Y�j�L�xv\��:t.��r�yT���Ly����c�q,������?�����U>�QZ^@S!j(���!���P��]�K];���/��5�c��1"�UN��$�B��/��L���`���K��w?�cVzS�+����p� ���U()�Ec�d1O�&�&�[5�������>.���S���UV6R�]��==��2��A�H����������_d�Fbg}`o�����d�8<�E68K����"��n��Z<atc����.5f�r2$�s���i0*�]���8�d�(����'�d����s�������^�����#�;�=��]�@���r}���B�W���������J.b���x�l��:���}���[sP�&�K�{�q��/x�
��
q�qPfQ}4L� ��-���x���<��Q�o���6���y������#`DUB[�B����$=������;�F��F���z��4�����>��o�~x���q��������CR@J1G�"aq�l-jB��������Lx�1����k|�$�"�x�u�n*���u�-�]��<I�s~yD��_m!�\MJ��H�:�����1�4��1��
�B���Z�[���o��n�K����G��a�3��A��?�m5�z����N{{�����;����Nc����F�,E��H�Q�������?�Vv��v�v�r�;�t�H��Bt#-����&c4\*��>#��jo9�H9k�rd�J32���}KA��$���A?M����Ey�@|��4�R��GUPY����Z�|h��
�������&�'�hw�l�X��J����{2q��<d�,r�N��>�Z�H�4�;z����U���1�.'R��u�q9Y@���)�x�d2[r�����')�xEi�Jq>���y���N����?Pn�*���	��?Q#)��Bk�~����ec�|���9T�eF�r�Z��y��v���5�qD�����h����g�@�a�����8�%
R�����5l
b�3ES�^���������l���7��j�YS��h4�����p7��?�;y���~-�m�`^�l7@��Q��3����Wt��������H?��7f�$<D/������+�������*{���]��U�I�N����.��aGB�����h�A!K����L3���������vc�����7(���mJ�]�3�IJ��*����[&�� �o 8��~�eq��?�� ��'��S����~�O�X������&���������%"�h pu����~�����g��� �3��&=��<�uW�b�yb>��?k 5t�P4���H�q2���_��(�����)�e���~
��/l�)FK8�|P�������Q{Z��i}�q}��\����9��$��o��t�Z��d����W������u>K�eaX����c�!�cL�U������-���Bd4�����$�$��dqO�Q"<I"5,xrt|v}����ld<������V�T�b�J6'U�E�����w0���������u�G��V,�������\����C�h��O��C.�������%)A�dLx���	
��@h]��*P 2�#�|�+9�7@2`
��Y^p��t��-���:����N�H�:���X�����6H9g�H��h�����	W����QS/���zu��
o
���>
�~�W(}9BWUy�A��QA@=)0��%��������U���RT�2	{�$����P���>�2V�,�R^���sL������<����y�M1�v'��z'��<�v��������I��_0���_�(��#��
A�x��<}}YI��4�����*���:U����t����*(�;�5T��*sU�#�?�Z��B��J^�T)�����KFW������

WP'"z���s���S(�=9����<��P�����*f��&|fw�[��FK��{;�;���H�����c�x����t����
�-������-�?��\!�k,Xb����7V��?H��lr^O��?>h��l�c�8��)v�R������^�^���N��~���uL7���x�=F4��B�b�;�B�����G��SE��i���������������C��j���6�dm,^���]���v����%�/<���;T�>d5k��d����^9��W�d�M�����&X`�k���G)>��S�����N�E���P2OGN
y��k��P�j�����~e��$N[cx�+���D�yTb5��M�����m������J�vm�����!�}4�7��6OA������+`<P��	�r�����/n%��:k�1I4Q�e��tb����.�
��.��'fN_�G��j��w���T������v����<�h
�:6����E������L�jX��U��c	#����$K�5�X����1K�Q5D�l�������>�]E������!�����x��/d����wj��yP�]��?��T7y�����X�y���&<�$��d�LL6T!��o��r�gS����*Q�`c6! z�(��X����>x���"��DuAp��	oj�!�{�[����0��}11���H�Ae`e]oW�����Ei+���utx�Vk���^W�������������;eY�����:��&������og�D��oe#���AA^#���k<�����U���s����������0�+���,�X��d'�����#wUF�����C�����xUal�
J����9�ZO�:��A��s��Ap�A9��8 `:�4�=q.����~�J�����
7*|i��6P*���w�5���QaYM 7��Rl��qw��P��C��{���A�{���0�q.�������yieO~4�<����<�glPH�?/��V_{�)��]I"���/��D��������I�i��I������3�K�������=�&f���a9�FsR�f:��N��R��]�w�BU������P�Hn���~������#��k0�Li5��~zt/�L�&�#�����OF�p�2a���YR�KFR�k��� m`���������E�������l?������0A�&z<�@�=s;b�W���h��R����:S�	���d�S�E}�������h�s�����y�C��X�
����
�p1�:���73�\�����%�)�H2O�s������P�����`�,�c:�:���,�o<co�`��.j�&�S)�Ds� ��)�y���8��d�a�C���Pb��W"�q��2�8
ZP�cj���R�d�$1���G�XA���S��^w��N��pYb�MF\ei�-�6~�����(��@	�>j��m��V2�ui�U-F��Z���%D����K���|;�Z�4o���A�U��Yi�V�gQP01������{}�U�%,V��ae1��}$,�E���'R,9�P��Q��,{������3�������tj���@���g�GJ'��]"�5�=���U&�&�L3KM������x�)���D>���c}#D6��xA��
����kx9M�(��^��w��wO���^#��S�����������6��������3�#h�5��I�����
��gXlI�/��|9��?DPu)�y����Li� �t�P=:
l*�g���q7H,��U�����x��e����?��J?;����%�,���Gk�2iEc��T�I����,�3��U�a�KX2�J�n3��f�]�I��vk`�zf�
�p^�����L���`0�"��`����A������X��9��r�f|���y'[���@K��2:O�3)������.�Q7*\��e��	����R�-�+T5�:���&T�3�j��R{��E1�Y��a�mJ�����<�"��#s^�-E�*$~�-��<�����):vJ��J��E"��E������G��,���T�r��#O�|��oG�0!���� �Y(v��s��smg�9ZG���������V���=���K��jFj�������>.4T�5�H��O�������NU�8��:n<�E�3��� ������W6^9�qh��-P��h�Vq��{�z��Mj�0y���u	*�	�|z�����>�T W������g�7���_2�� �J��O��2��;�P����\�3��]�s0]�
 <����6�C���N'�r��=
����,�?A�c����&S���2���$9�gB��D=�gh�.����dR���;��1�%Q�x�h��S��w���0�to��������`�cy\���p$d�/
�s4�Lg�d������Pq1��:;���D�9�&}S.�u�:�f����I��#X�iT_C�MrT )>�l�K�|.�0�R�,8�$��������*�WU�
k��^+�U��Rd�`KEB�%���3)�I64)`,C�v:Mc����h����_w�&�|����AGU��x�.����
}�d�{��o�S	�s������y���
�X�T"!�i���� qU�Te��t��C&E0��,�LT�]<���TrI���D4��[w%����5�s�Q��l\����d����6
4�e u�K����^��8�Mp��E2��P
�����)��p�`� W���Y �T���$��V&huAn�c��S�k&
�Q����c�g���k�Q��Z�������u|2��P�c����gI�=�x���&���Dn�T"3{��`wm^�w1]C�n��$�0X�GF�����\
�CO��j�,��P�a@[�O�d�v�yY-�����r���<6Z�U�MrV���d�o�(�a�� %N��)s����F��B�*s1���8x�+��1��cK4�	���Zk�l��s_����iB�y������`�y��AI
T.3�G��x.��p���H;�5�$�&�E{��y���so�_SQKp1"���'�-���Et�&���={� ���p����}���s�<��A���O�A���k%�<E��C����w�oA��%v��*��Y6�t1��q}7��G>Y+����m���y��o��Ur_0���FFOp��{����Htz/��]�M��J���$��Y�?��������$�+�s9imo8~��} I,���6��d
��8�1�u�����z�s��!�s)����������z��U
��7��[��BR��"�Fr���:�(��g&����V����FH8"V\�3���D�����x��������6���{��9�>�.���@+�e�sq�TB����X��Od~JPx�=OY�@JQ#7���Q�m��k*�F��`���j���Ob��*]�Y �g3h��i��c9���#u���MY����]a��m�4�����dE��2��"��(Z�4��G�����`��o����!?��u^����J����`����P����PkW\�4��X�v<�@����[aHQ����k\�L����\��*b�����X�:��~hoGY]���5���Y��hK�FH�X�Nf����2�������|����o�g�d�H�
�������[!�������>I��A���"��O����`	�}��E���63�*t�8��nAZ�<����s%�pN���[�d�����|�u��+��M��d��)rv������n_�W��a����s�>-�(��gt���MMV<��pa�n%�����5inYM\E�T#�i�!�����S�YX�7E�?���d�a��sH	��"&MW���~��ac!���9��|\9D
?��lI@y9�]��o�����u����8{���;{����V��8���a�N������<*	A�(���x�/�}v�����eZ�]�$d�n����l��HO����4�PV���\m�K���6S��;3s9W|T6_W�P�U�.�6���Z���V�*�Fu�vn������q(K���?�>F�'0eJ�{��vd�i&j?�F^��25�}[��w�F�}@aO�X����#�L��!��`�1��r��%qy��
��zi |@&nR'6��SL��wN�����%���s���&kL/�)[)O�]h}��n���h����l��5��(��+������;���*��r�Y���D3���jO�0Exz�iQ�����	:�KJ��>F�H"�Gv?6f�<�F�0Y�0����<���zk��V���K�J��A��<]]<���Vl�(QMg���M��=#�.��u4R����^t���@��<l��c�s�//���b���������nm�5�X��4Q����E!�tTU5+��Pc����
�����B:��%3$�� �����PY����?`�?�\����cx�I�U��+D�*X�;�e�me���R��`;��$�=k[���Qz�e���S	+��)�G�U�3����=X
Iw6�K5�X���T����pEQ�*��$
EL�ax:���F�=�V-E\jz> Hw��-����f�ug(�b���'k@
����*�)�C���O�����i���Imb����1�L��]���<��2�1�Tg�6���)%��BU~��n��@3�U�> �O�D��D�;+LMQ�)��@���pG���N_-����C*���F����6%(e��5����A������X�%1��	�]L�
���<�f���{����'��s�����r���$���r@���
�q��:������X�Q����,���o�_�Y-���������E���,�D�W�EA*����nH��z�a�!o�%\�����+�J"�hZ%c���3�O�0c7dd�8j~��:c��6��
��S$&(����d*���r*���]���L�������o0V������]�	�P$�C�0p��A�[�[��S������?��99b�t��9=�7m#�B4ZI����V��Q��u��q��b�
i�!�f	�����f��M���A�f��I:����3�l���{W�g�����y���5+��P�sGRk��U��������M���Q���4������Xqg\�9��D��������7��;�u���y�P�C	�!�!����f���x=8y��?�o^�����3�1<~j?VQ��Xh���bG��(�`�h�����)��(����v6��R���n�5�c:3�1B�(@�R�F�uN���35t:�N���%�t��� ���P55��&�7�~����J�(�HQS�$��=p�y/$��
,��Pva�XQ}>K8c���l��K#4HZ�;��R\a��B8D
��5���e���I��9i4��G��f��5x�Ajhb��f(���\�+���md�n��;����A���4s�z�|[t��l������[�,n�	��*Jr�%o�Gz�T�t����Q������N\ZE����q�������6��L�#�@�O�k]-��%�+(�@~'��3R��~N�"-I,v�y�������I.X�@�:$�R��6�J������D��������6G����nV"�����m\?�������������;^# �@��M���$�9����!}���:��'XxI�U)�:���m����0
C��:����A�eU�C�?�;`�D��K��U��),���Dc!�����}
�
*I	�XQ�!�5�����G�.��g�c4-��S-&��7��Tm�����\O�%(�!��hC���f�=����66�_\���%qp���O�\���j�o*���w����%�����.�XI��;&�����~���x���*0p�&�t��n���k��m@������$�,_�@@c��a�5�k?tz��wo���g��o���7\r��2�oxUL4����������+
�>*�6�C�D���'���#�V�ccY�Z�r�\�[��[�A��hQ<*69����
v�9��,��$Tq�D*��h�7��z0����-%��+�4���CQO�H����r��
�k�f
/��V�\Y���N��������h�<s���'uN3�g�y�{����:o2t�����5��CV%��)v�H��TmX����� �]I�;�B��7!��)��������7AV�\��Y4z�jr��j�xPu�Zy"�d����,��
�;jE��k������V�)��5�5	�����De�lWJ*k�1���6Y�*k~��U)}>��U�����*�mI"+kj�2Z���'��5��r\y�_A�+]-[��J[z|�����'�n^�*!����&3�R�cJ�e�-����"iVi�H]����h��z���]G�Z�T���_i~����V��4��A��x\���`��W�	o������9�t	�g��J<e����W�j�����_rJ���FZ`�����{X���,_�'�Q1�rH��df���)��TNG�)	_���(��!������J�}Y�9��]�z�������QbL&H��M�@"��� �>�$�KB��^qi�uTo|V��p�M�'���ai#��a�k����7�{�q���7Q�����Eh�@�NP��J��<�6-�cX�+���n&�+�#����YL��k!y�R!?=���T��&�~���er�<���U������(�:�St��@��(/�s�/4��i��i�r����Y�@��������a���$HV\�S��@t������K�>����v�:�����'S���!/*�����b�����O��C��s�	�%g��*�WU�
sD�	sW1!�I�W�V�8�PE��P��_�Zu�P��;!��U�����_��Y��#p�*~���o?��hV�����!�AOC#A������qF�a��e�2��|���
��tT/o���d�V"$U��6m�a�����@A��������]NDp������B���!:���j���M_�+����'(�#��v�t�� ���h�9i���&H������j��J�c���g��W�SD/�X/�o��������=���Sc�~�l�YX:�����.�H�a�~���Q�N�:�[�w|Xo���'t(���#L~B���Q�z@)������e���Aj��7�N���:�_�n	;�p�����;���<����x�g9�+���`\�2��]:!��<�
���g��sZ]F("$�0������>��/��v1�$�����fT�W��e�@��O��m�}�'���S�!�@Z�!<>m7O�������	�	��V��q�A���#��=����-����2�O �z�����;��}�~���7�5���4v���Gk��<��8-����NO��� }�!>+|����G��H�h���Y�_>��8��~=���PG����3@^��%��1�8����7�M���`@��h��8i�����}�����������������
#80Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#77)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 6, 2019 at 1:47 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

[ new patches ]

I took a look at 0012 today, Amit's patch for extending the binary
heap machinery, and 0013, Amit's patch for "Infrastructure to register
and fetch undo action requests."

Thanks for looking into the patches.

I don't think that binaryheap_allocate_shm() is a good design. It
presupposes that we want to store the binary heap as its own chunk of
shared memory allocated via ShmemInitStruct(), but we might want to do
something else, like embed in another structure, store it in a DSM or
DSA, etc., and this function can't do any of that. I think we should
have something more like:

extern Size binaryheap_size(int capacity);
extern void binaryheap_initialize(binaryheap *, int capacity,
binaryheap_comparator compare, void *arg);

Then the caller can do something like:

sz = binaryheap_size(capacity);
bh = ShmemInitStruct(name, sz, &found);
if (!found)
binaryheap_initialize(bh, capacity, comparator, whatever);

If it wants to get the memory in some other way, it just needs to
initialize bh differently; the rest is the same. Note that there is
no need, in this design, for binaryheap_size/initialize to make use of
"shared" memory. They could equally well be used on backend-local
memory. They do not need to care. You just provide the memory, and
they do their thing.

I didn't have other use cases in mind and I think to some extent this
argument holds true for existing binaryheap_allocate allocate. If we
want to make it more generic, then shouldn't we try to even change the
existing binaryheap_allocate to use this new model, that way the
binary heap allocation API will be more generic?

..
..

Now, one objection to the above line of attack is the different queues
actually contain different types of elements. Apparently, the XID
queue contains elements of type UndoXidQueue and the size queue
contains elements of type UndoSizeQueue. It is worth noting here that
these are bad type names, because they sound like they are describing
a type of queue, but it seems that they are actually describing an
element in the queue. However, there are two larger problems:

1. I don't think we should have three different kinds of objects for
each of the three different queues. It seems like it would be much
simpler and easier to just have one kind of object that stores all the
information we need (full_xid, start_urec_ptr, dbid, request_size,
next_retry_at, error_ocurred_at) and use that everywhere. You could
object that this would increase the storage space requirement,

Yes, this was the reason to keep them separate, but I see your point.

but it
wouldn't be enough to make any real difference and it probably would
be well worth it for the avoidance of complexity.

Okay, will give it a try and see if it can avoid some code complexity.
Along with this, I will investigate your other suggestions related to
code improvements as well.

On another note, UNDO_PEEK_DEPTH is bogus. It's used in UndoGetWork()
and it passes the depth argument down to GetRollbackHashKeyFromQueue,
which then does binaryheap_nth() on the relevant queue. Note that
this function is another places that is ends up duplicating code
because of the questionable decision to have separate types of queue
entries for each different queue; otherwise, it could probably just
take the binary heap into which it's peeking as an argument instead of
having three different cases. But that's not the main point here. The
main point is that it calls a function for whichever type of queue
we've got and gets some kind of queue entry using binaryheap_nth().
But binaryheap_nth(whatever, 2) does not give you the third-smallest
element in the binary heap. It gives you the third entry in the
array, which may or may not have the heap property, but even if it
does, the third element could be huge. Consider this binary heap:

0 1 100000 2 3 100001 100002 4 5 6 7 100003 100004 100005 100006

This satisfies the binary heap property, because the element at
position n is always smaller than the elements at positions 2n+1 and
2n+2 (assuming 0-based indexing). But if you want to look at the
smallest three elements in the heap, you can't just look at indexes
0..2. The second-smallest element must be at index 1 or 2, but it
could be either place. The third-smallest element could be the other
of 1 and 2, or it could be either child of the smaller one, so there
are three places it might be. In general, a binary heap is not a good
data structure for finding the smallest N elements of a collection
unless N is 1, and what's going to happen with what you've got here is
that we'll sometimes prioritize an item that would not have been
pulled from the queue for a long time over one that would have
otherwise been processed much sooner.

You are right that it won't be nth smallest element from the queue and
we don't even care for that here. The peeking logic is not to find
the next prioritized element but to check if we can find some element
for the same database in the next few entries to avoid frequent undo
worker restart.

I'm not sure that's a
show-stopper, but it doesn't seem good, and the current patch doesn't
seem to have any comments justifying it, or at least not in the places
nearby to where this is actually happening.

I agree that we should add more comments explaining this.

I think there are more problems here, too. Let's suppose that we
fixed the problem described in the previous paragraph somehow, or
decided that it won't actually make a big difference and just ignored
it. Suppose further that we have N active databases which are
generating undo requests. Luckily, we happen to also have N undo
workers available, and let's suppose that as of a certain moment in
time there is exactly one worker in each database. Think about what
will happen when one of those workers goes to look for the next undo
request. It's likely that the first request in the queue will be for
some other database, so it's probably going to have to peak ahead to
find a request for the database to which it's connected -- let's just
assume that there is one. How far will it have to peak ahead? Well,
if the requests are uniformly distributed across databases, each
request has a 1-in-N chance of being the right one. I wrote a little
Perl program to estimate the probability that we won't find the next
request for our databases within 10 requests as a function of the
number of databases:

1 databases => failure chance with 10 lookahead is 0.00%
2 databases => failure chance with 10 lookahead is 0.10%
3 databases => failure chance with 10 lookahead is 1.74%
4 databases => failure chance with 10 lookahead is 5.66%
5 databases => failure chance with 10 lookahead is 10.74%
6 databases => failure chance with 10 lookahead is 16.18%
7 databases => failure chance with 10 lookahead is 21.45%
8 databases => failure chance with 10 lookahead is 26.31%
9 databases => failure chance with 10 lookahead is 30.79%
10 databases => failure chance with 10 lookahead is 34.91%
11 databases => failure chance with 10 lookahead is 38.58%
12 databases => failure chance with 10 lookahead is 41.85%
13 databases => failure chance with 10 lookahead is 44.91%
14 databases => failure chance with 10 lookahead is 47.69%
15 databases => failure chance with 10 lookahead is 50.12%
16 databases => failure chance with 10 lookahead is 52.34%
17 databases => failure chance with 10 lookahead is 54.53%
18 databases => failure chance with 10 lookahead is 56.39%
19 databases => failure chance with 10 lookahead is 58.18%
20 databases => failure chance with 10 lookahead is 59.86%

Assuming my script (attached) doesn't have a bug, with only 8
databases, there's better than a 1-in-4 chance that we'll fail to find
the next entry for the current database within the lookahead window.

This is a good test scenario, but I think it has not been considered
that there are multiple queues and we peek into each one.

That's bad, because then the worker will be sitting around waiting
when it should be doing stuff. Maybe it will even exit, even though
there's work to be done, and even though all the other databases have
their own workers already.

I think we should once try with the actual program which can test such
a scenario on the undo patches before reaching any conclusion. I or
one of my colleague will work on this and report back the results.

You can construct way worse examples than
this one, too: imagine that there are two databases, each with a
worker, and one has 99% of the requests and the other one has 1% of
the requests. It's really unlikely that there's going to be an entry
for the second database within the lookahead window.

I am not sure if that is the case because as soon as the request from
other database get prioritized (say because its XID becomes older) and
came as the first request in one of the queues, the undo worker will
exit (provided it has worked for some threshold time (10s) in that
database) and allow the request from another database to be processed.

And note that
increasing the window doesn't really help either: you just need more
databases than the size of the lookahead window, or even almost as
many as the lookahead window, and things are going to stop working
properly.

On the other hand, suppose that you have 10 databases and one undo
worker. One database is pretty active and generates a continuous
stream of undo requests at exactly the same speed we can process them.
The others all have 1 pending undo request. Now, what's going to
happen is that you'll always find the undo request for the current
database within the lookahead window. So, you'll never exit.

Following the logic given above, I think here also worker will exit as
soon as the request from other database get prioritised.

But
that means the undo requests in the other 9 databases will just sit
there for all eternity, because there's no other worker to process
them. On the other hand, if you had 11 databases, there's a good
chance it would work fine, because the new request for the active
database would likely be outside the lookahead window, and so you'd
find no work to do and exit, allowing a worker to be started up in
some other database.

As explained above, I think it will work the same way both for 10 or
11 databases. Note, that we don't always try to look ahead. We look
ahead when we have not worked on the current database for some
threshold amount of time.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#81Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#80)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 8, 2019 at 6:57 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I didn't have other use cases in mind and I think to some extent this
argument holds true for existing binaryheap_allocate allocate. If we
want to make it more generic, then shouldn't we try to even change the
existing binaryheap_allocate to use this new model, that way the
binary heap allocation API will be more generic?

No. binaryheap_allocate is fine for simple cases and there's no
reason that I can see to change it.

You are right that it won't be nth smallest element from the queue and
we don't even care for that here. The peeking logic is not to find
the next prioritized element but to check if we can find some element
for the same database in the next few entries to avoid frequent undo
worker restart.

You *should* care for that here. The whole purpose of a binary heap is
to help us choose which task we should do first and which ones should
be done later. I don't see why it's OK to decide that we only care
about doing the tasks in priority order sometimes, and other times
it's OK to just pick semi-randomly.

This is a good test scenario, but I think it has not been considered
that there are multiple queues and we peek into each one.

I think that makes very little difference, so I don't see why it
should be considered. It's true that this will sometimes mask the
problem, but so what? An algorithm that works 90% of the time is not
much better than one that works 80% of the time, and neither is the
caliber of work we are expecting to see in PostgreSQL.

I think we should once try with the actual program which can test such
a scenario on the undo patches before reaching any conclusion. I or
one of my colleague will work on this and report back the results.

There is certainly a place for empirical testing of a patch like this
(perhaps even before posting it). It does not substitute for a good
theoretical explanation of why the algorithm is correct, and I don't
think it is.

You can construct way worse examples than
this one, too: imagine that there are two databases, each with a
worker, and one has 99% of the requests and the other one has 1% of
the requests. It's really unlikely that there's going to be an entry
for the second database within the lookahead window.

I am not sure if that is the case because as soon as the request from
other database get prioritized (say because its XID becomes older) and
came as the first request in one of the queues, the undo worker will
exit (provided it has worked for some threshold time (10s) in that
database) and allow the request from another database to be processed.

I don't see how this responds to what I wrote. Neither worker needs
to exit in this scenario, but the worker from the less-popular
database is likely to exit anyway, which seems like it's probably not
the right thing.

And note that
increasing the window doesn't really help either: you just need more
databases than the size of the lookahead window, or even almost as
many as the lookahead window, and things are going to stop working
properly.

On the other hand, suppose that you have 10 databases and one undo
worker. One database is pretty active and generates a continuous
stream of undo requests at exactly the same speed we can process them.
The others all have 1 pending undo request. Now, what's going to
happen is that you'll always find the undo request for the current
database within the lookahead window. So, you'll never exit.

Following the logic given above, I think here also worker will exit as
soon as the request from other database get prioritised.

OK.

But
that means the undo requests in the other 9 databases will just sit
there for all eternity, because there's no other worker to process
them. On the other hand, if you had 11 databases, there's a good
chance it would work fine, because the new request for the active
database would likely be outside the lookahead window, and so you'd
find no work to do and exit, allowing a worker to be started up in
some other database.

As explained above, I think it will work the same way both for 10 or
11 databases. Note, that we don't always try to look ahead. We look
ahead when we have not worked on the current database for some
threshold amount of time.

That's interesting, and it means that some of the scenarios that I
mentioned are not problems. However, I don't believe it means that
your code is actually correct. It's just means that it's wrong in
different ways. The point is that, with the way you've implemented
this, whenever you do lookahead, you will, basically randomly,
sometimes find the next entry for the current database within the
lookahead window, and sometimes you won't. And sometimes it will be
the next-highest-priority request, and sometimes it won't. That just
cannot possibly be the right thing to do.

Would you propose to commit a patch that implemented the following pseudocode?

find-next-thing-to-do:
see if the highest-priority task in any database is for our database.
if it is, do it and stop here.
if it is not, and if we haven't worked on the current database for
at least 10 seconds, look for an item in the current database.
...but don't look very hard, so that we'll sometimes,
semi-randomly, find nothing even when there is something we could do.
...and also, sometimes find a lower-priority item that we can do,
possibly much lower-priority, instead of the highest priority thing we
can do.

Because that's what your patch is doing.

In contrast, the algorithm that I proposed would work like this:

find-next-thing-to-do:
find the highest-priority item for the current database.
do it.

I venture to propose that the second one is the superior algorithm
here. One problem with the second algorithm, which I pointed out in
my previous email, is that sometimes we might want the worker to exit
even though there is work to do in the current database. My algorithm
makes no provision for that, and yours does. However, yours does that
in a way that's totally unprincipled: it just sometimes fails to find
any work that it could do even though there is work that it could do.
No amount of testing or argumentation is going to convince me that
this is a good approach. The decision about when a worker should exit
to allow a new one to be launched needs to be based on clear,
understandable rules, not be something that happens semi-randomly when
a haphazard search for the next entry fails, as if by chance, to find
it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#82Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#79)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 6, 2019 at 8:26 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 4, 2019 at 5:24 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

PFA, the latest version of the undo interface and undo processing patches.

Summary of the changes in the patch set

1. Undo Interface
- Rebased over latest undo storage code
- Implemented undo page compression (don't store the common fields in
all the records instead we get from the first complete record of the
page).
- As per Robert's comment, UnpackedUndoRecord is divided in two parts,
a) All fields which are set by the caller.
b) Pointer to structures which are set internally.
- Epoch and the Transaction id are unified as full transaction id
- Fixed handling of dbid during recovery (TODO in PrepareUndoInsert)

Pending:
- Move loop in UndoFetchRecord to outside and test performance with
keeping pin vs pin+lock across undo records. This will be done after
testing performance over the zheap code.
- I need to investigate whether Discard checking can be unified in
master and HotStandby in UndoFetchRecord function.

2. Undo Processing
- Defect fix in multi-log rollback for subtransaction.
- Assorted defect fixes.

Others
- Fixup for undo log code to handle full transaction id in
UndoLogSlot for discard and other bug fixes in undo log.
- Fixup for Orphan file cleanup to pass dbid in PrepareUndoInsert

PFA, updated patch version which includes
- One defect fix in undo interface related to undo page compression
for handling persistence level
- Implemented pending TODO optimization in undo page compression.
- One defect fix in undo processing related to the prepared transaction

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

undo_20190709.tar.gzapplication/x-gzip; name=undo_20190709.tar.gzDownload
#83Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#77)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 6, 2019 at 1:47 AM Robert Haas <robertmhaas@gmail.com> wrote:

In fact, it seems to me that we shouldn't have any such thing as
"queue entries" at all. The queues should just be pointing to
RollbackHashEntry *, and we should add all the fields there that are
present in any of the "queue entry" structures. This would use less
memory still.

As of now, after we finish executing the rollback actions, the entry
from the hash table is removed. Now, at a later time (when queues are
full and we want to insert a new entry) when we access the queue entry
(to check whether we can remove it) corresponding to the removed hash
table entry, will it be safe to access it? The hash table entry might
have been freed or would have been reused as some other entry by the
time we try to access it.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#84Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#83)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 10, 2019 at 2:32 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

As of now, after we finish executing the rollback actions, the entry
from the hash table is removed. Now, at a later time (when queues are
full and we want to insert a new entry) when we access the queue entry
(to check whether we can remove it) corresponding to the removed hash
table entry, will it be safe to access it? The hash table entry might
have been freed or would have been reused as some other entry by the
time we try to access it.

Hmm, yeah, that's a problem. I think we could possibly fix this by
having the binary heaps just store a FullTransactionId rather than a
pointer to the RollBackHashEntry. Then, if you get a
FullTransactionId from the binary heap, you just do a hash table
lookup to find the RollBackHashEntry instead of accessing it directly.
If it doesn't exist, then you can just discard the entry: it's for
some old transaction that's no longer relevant.

However, there are a few problems with that idea. One is that I see
that you've made the hash table keyed by full_xid + start_urec_ptr
rather than just full_xid, so if the queues just point to an XID, it's
not enough to find the hash table entry. The comment claims that this
is necessary because "in the same transaction, there could be rollback
requests for both logged and unlogged relations," but I don't
understand why that means we need start_urec_ptr in the hash table
key. It would seem more natural to me to have a single entry that
covers both the logged and the unlogged undo for that transaction.

(Incidentally, I don't think it's correct that RollbackHashEntry
starts with FullTransactionId full_xid + UndoRecPtr start_uprec_ptr
declared separately; I think it should start with RollbackHashKey -
although if we change the key back to just a FullTransactionId then we
don't need to worry separately about fixing this issue.)

Another problem is that on a 64-bit system, we can pass a
FullTransactionId by value, but on a 32-bit system we can't. That's
awkward, because if we can't pass the XID by value, then we're back to
needing a separately-allocated structure for the queue entries, which
I was really hoping to avoid.

A second possible approach to this problem is to just reset all the
binary heaps (using binaryheap_reset) whenever we insert a new entry
into the hash table, and rebuild them the next time they're needed by
reinserting all of the current entries in the hash table. That might
be too inefficient. You can insert a bunch of things in a row without
re-heaping, and you can dequeue a bunch of things in a row without
re-heaping, but if they alternate you'll re-heap a lot. I don't know
whether that costs enough to worry about; it might be fine.

A third possible approach is to allocate a separate array whose
entries are reused, and to maintain a freelist of entries from that
array. All the real data is stored in this array, and the binary
heaps and hash table entries just point to it. When the freelist is
empty, the next allocate scans all the binary heaps and removes any
pointers to inactive entries; it then puts all inactive entries back
onto the freelist. This is more complex than the previous approach,
and it doesn't totally avoid re-heaping, because removing pointers to
inactive entries from the binary heaps will necessitate a re-heap on
next access. However, if the total capacity of the data structures is
large compared to the number of entries actually in use, which will
usually be true, we'll have to re-heap much less often, because we
only have to do it when the number of allocations exhausts
*everything* on the free-list, rather than after every allocation.

A fourth possible approach is to enhance the simplehash mechanism to
allow us to do cleanup when an item to which there might still be
residual pointers is reused. We could allow some code supplied by the
definer of an individual simplehash implementation to be executed
inside SH_INSERT, just at the point where we're going to make an entry
status SH_STATUS_IN_USE. What we'd do is add a flag to the structure
indicating whether there might be deferred cleanup work for that
entry. Maybe it would be called something like 'bool processed' and
set when we process the undo work for that entry. If, when we're
about to reuse an entry, that flag is set, then we go scan all the
binary heaps and remove all entries for which that flag is set. And
then we unset the flag for all of those entries. Like the previous
approach, this is basically a refinement of the second approach in
that it tries to avoid re-heaping too often. Here, instead of
re-heaping once we've been through the entire free-list, we'll re-heap
when we happen (more or less randomly) happen to reuse a hash table
entry that's been reused, but we avoid it when we happen to snag a
hash table entry that hasn't been reused recently. This is probably
less efficient at avoiding re-heaping than the previous approach, but
it avoids a separately-allocated data structure, which is nice.

Broadly, you are correct to point out that you need to avoid chasing
stale pointers, and there are a bunch of ways to accomplish that:
approach #1 avoids using real pointers, and the rest just make sure
that any stale pointers don't stick around long enough to cause any
harm. There are probably also several other totally realistic
alternatives, and I don't know for sure what is best, or how much it
matters.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#85Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#82)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 9, 2019 at 6:28 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

PFA, updated patch version which includes
- One defect fix in undo interface related to undo page compression
for handling persistence level
- Implemented pending TODO optimization in undo page compression.
- One defect fix in undo processing related to the prepared transaction

Looking at 0002 a bit, it seems to me that you really need to spend
some energy getting things into a consistent order all across the
patch. For example, UndoPackStage uses the ordering: HEADER,
TRANSACTION, RMID, RELOID, XID, CID... But the declarations of the
UREC_INFO constants go in a different order: TRANSACTION, FORK, BLOCK,
BLKPREV... The comments defining those go in a different order and
some of them are missing. The definition of the UndoRecordBlah
structures go in a different order still: Transaction, Block,
LogSwitch, Payload. UndoRecordHeaderSize goes with FORK, BLOCK,
BLPREV, TRANSACTION, LOGSWITCH, .... That really needs to be
straightened out and made consistent.

You (still) need to rename blkprev to something more generic, as
mentioned in previous rounds of review.

I think it would be a good idea to avoid complex macros in favor of
functions where possible, e.g. UNDO_PAGE_PARTIAL_REC_SIZE. If
performance is a concern, it could be declared static inline, which
should be as good as a macro.

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const), and I also think it's just all zeroes (so
initializing it isn't really necessary), and I also think that it's
just used for initializing other UndoCompressionInfos (so we could
just initialize them directly, either by setting the members
individually or jus zeroing them).

It seems like UndoRecordPrepareTransInfo ought to have an Assert(index
< some_limit) in the loop.

A comment in PrepareUndoInsert refers to "low switch" where it means
"log switch."

This is by no means a complete review, for which I unfortunately lack
the time at present. Just some initial observations.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#86Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#84)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 10, 2019 at 12:36 PM Robert Haas <robertmhaas@gmail.com> wrote:

Broadly, you are correct to point out that you need to avoid chasing
stale pointers, and there are a bunch of ways to accomplish that:
approach #1 avoids using real pointers, and the rest just make sure
that any stale pointers don't stick around long enough to cause any
harm. There are probably also several other totally realistic
alternatives, and I don't know for sure what is best, or how much it
matters.

After some off-list discussion with Andres ...

Another possible approach here, which I think I like better, is to
switch from using a binary heap to using an rbtree. That wouldn't
work well in DSM because of the way it uses pointers, but here we're
putting data in the shared memory segment so it seems like it should
work. The idea would be to allocate an array of entries with a
freelist, and then have allocfunc and freefunc defined to push and pop
the freelist. Unlike a binary heap, an rbtree lets us (a) do
peek-ahead in sorted order and (b) delete elements from an arbitrary
position without rebuilding anything.

If we adopt this approach, then I think a bunch of the problems we've
been talking about actually get a lot easier. If we pull an item from
the ordered-by-XID rbtree or the ordered-by-undo-size rbtree, we can
remove it from the other one cheaply, because we can store a pointer
to the RBTNode in the main object. So then we never have any stale
pointers in any data structure, which means we don't have to have a
strategy to avoid accidentally following them.

The fact that we can peak-ahead correctly without any new code is also
very nice. I'm still concerned that peeking ahead isn't the right
approach in general, but if we're going to do it, peeking ahead to the
actually-next-highest-priority item is a lot better than peeking ahead
to some-item-that-may-be-fairly-high-priority.

One problem which Andres spotted is that rbt_delete() can actually
move content around, so if you just cache the RBTNode returned by
rbt_insert(), it might not be the right one by the time you
rbt_delete(), if other stuff has been deleted first. There are
several possible approaches to that problem, but one that I'm
wondering about is modifying rbt_delete_node() so that it doesn't rely
on rbt_copy_data. The idea is that if y != z, instead of copying the
data from y to z, copy the left/right/parent pointers from z into y,
and make z's left, right, and parent nodes point to y instead. Then
we always end up removing the correct node, which would make things
much easier for us and might well be helpful to other code that uses
rbtree as well.

Another small problem, also spotted by Andres, is that rbt_create()
uses palloc. That seems easy to work around: just provide an
rbt_intialize() function that a caller can use instead of it wants to
initialize an already-allocated block of memory.

Thoughts?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#87Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#85)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jul 9, 2019 at 6:28 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

PFA, updated patch version which includes
- One defect fix in undo interface related to undo page compression
for handling persistence level
- Implemented pending TODO optimization in undo page compression.
- One defect fix in undo processing related to the prepared transaction

Looking at 0002 a bit, it seems to me that you really need to spend
some energy getting things into a consistent order all across the
patch. For example, UndoPackStage uses the ordering: HEADER,
TRANSACTION, RMID, RELOID, XID, CID... But the declarations of the
UREC_INFO constants go in a different order: TRANSACTION, FORK, BLOCK,
BLKPREV... The comments defining those go in a different order and
some of them are missing. The definition of the UndoRecordBlah
structures go in a different order still: Transaction, Block,
LogSwitch, Payload. UndoRecordHeaderSize goes with FORK, BLOCK,
BLPREV, TRANSACTION, LOGSWITCH, .... That really needs to be
straightened out and made consistent.

Thanks for the review, I will work on this.

You (still) need to rename blkprev to something more generic, as
mentioned in previous rounds of review.

I will change this.

I think it would be a good idea to avoid complex macros in favor of
functions where possible, e.g. UNDO_PAGE_PARTIAL_REC_SIZE. If
performance is a concern, it could be declared static inline, which
should be as good as a macro.

ok

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

Currently, we are doing like this

a) BeginUndoRecordInsert
- Copy the global "undo_compression_info" to our local context for
handling multi-prepare because for multi-prepare we don't want to
update the global value until we have successfully inserted the undo
record.

b) PrepareUndoInsert
-Operate on the context and update the context->undo_compression_info
if required (page changed)

c)InsertPrepareUndo
- After we have inserted successfully overwrite
context->undo_compression_info to the global "undo_compression_info".
So that next undo insertion can get the right information.

and I also think it's just all zeroes (so

initializing it isn't really necessary), and I also think that it's
just used for initializing other UndoCompressionInfos (so we could
just initialize them directly, either by setting the members
individually or jus zeroing them).

Initially, I was doing that but later I thought that InvalidUndoRecPtr
is macro (although the value is 0) shouldn't we initialize all
UndoRecPtr variables with value InvalidUndoRecPtr instead of directly
using 0 so I changed like this.

It seems like UndoRecordPrepareTransInfo ought to have an Assert(index
< some_limit) in the loop.

A comment in PrepareUndoInsert refers to "low switch" where it means
"log switch."

I will fix.

This is by no means a complete review, for which I unfortunately lack
the time at present. Just some initial observations.

ok

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#88Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#84)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 10, 2019 at 10:06 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Jul 10, 2019 at 2:32 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

As of now, after we finish executing the rollback actions, the entry
from the hash table is removed. Now, at a later time (when queues are
full and we want to insert a new entry) when we access the queue entry
(to check whether we can remove it) corresponding to the removed hash
table entry, will it be safe to access it? The hash table entry might
have been freed or would have been reused as some other entry by the
time we try to access it.

Hmm, yeah, that's a problem. I think we could possibly fix this by
having the binary heaps just store a FullTransactionId rather than a
pointer to the RollBackHashEntry. Then, if you get a
FullTransactionId from the binary heap, you just do a hash table
lookup to find the RollBackHashEntry instead of accessing it directly.
If it doesn't exist, then you can just discard the entry: it's for
some old transaction that's no longer relevant.

However, there are a few problems with that idea. One is that I see
that you've made the hash table keyed by full_xid + start_urec_ptr
rather than just full_xid, so if the queues just point to an XID, it's
not enough to find the hash table entry. The comment claims that this
is necessary because "in the same transaction, there could be rollback
requests for both logged and unlogged relations," but I don't
understand why that means we need start_urec_ptr in the hash table
key. It would seem more natural to me to have a single entry that
covers both the logged and the unlogged undo for that transaction.

The data for logged and unlogged undo are in separate logs. So, the
discard worker can encounter them at different times. It is quite
possible that by the time it encounters the second request, some undo
worker is already half-way processing the first request. It might be
feasible to combine them during foreground work, but after startup or
some other times when discard worker has to register the request, it
won't be feasible to have one entry or at least we need more smarts to
ensure that we can always edit the hash table entry at later time to
append the request. I have thought about keep full_xid +
persistence_level/undo_category as a key, but as we anyway need
start_ptr for the request, so it seems appealing to use the same.
Also, even if we try to support one entry for logged and unlogged
undo, it won't be always possible to have one request for it as is the
case explained for discard worker.

(Incidentally, I don't think it's correct that RollbackHashEntry
starts with FullTransactionId full_xid + UndoRecPtr start_uprec_ptr
declared separately; I think it should start with RollbackHashKey -
although if we change the key back to just a FullTransactionId then we
don't need to worry separately about fixing this issue.)

Agreed.

It seems before we analyze or discuss in detail the other solutions
related to dangling entries, it is better to investigate the rbtree
idea you and Andres came up with as on a quick look it seems that
might avoid creating the dangling entries at the first place.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#89Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#86)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 11, 2019 at 1:29 AM Robert Haas <robertmhaas@gmail.com> wrote:

After some off-list discussion with Andres ...

Another possible approach here, which I think I like better, is to
switch from using a binary heap to using an rbtree. That wouldn't
work well in DSM because of the way it uses pointers, but here we're
putting data in the shared memory segment so it seems like it should
work. The idea would be to allocate an array of entries with a
freelist, and then have allocfunc and freefunc defined to push and pop
the freelist. Unlike a binary heap, an rbtree lets us (a) do
peek-ahead in sorted order and (b) delete elements from an arbitrary
position without rebuilding anything.

If we adopt this approach, then I think a bunch of the problems we've
been talking about actually get a lot easier. If we pull an item from
the ordered-by-XID rbtree or the ordered-by-undo-size rbtree, we can
remove it from the other one cheaply, because we can store a pointer
to the RBTNode in the main object. So then we never have any stale
pointers in any data structure, which means we don't have to have a
strategy to avoid accidentally following them.

The fact that we can peak-ahead correctly without any new code is also
very nice. I'm still concerned that peeking ahead isn't the right
approach in general, but if we're going to do it, peeking ahead to the
actually-next-highest-priority item is a lot better than peeking ahead
to some-item-that-may-be-fairly-high-priority.

One problem which Andres spotted is that rbt_delete() can actually
move content around, so if you just cache the RBTNode returned by
rbt_insert(), it might not be the right one by the time you
rbt_delete(), if other stuff has been deleted first. There are
several possible approaches to that problem, but one that I'm
wondering about is modifying rbt_delete_node() so that it doesn't rely
on rbt_copy_data. The idea is that if y != z, instead of copying the
data from y to z, copy the left/right/parent pointers from z into y,
and make z's left, right, and parent nodes point to y instead.

I am not sure but don't we need to retain the color of z as well?

Apart from this, the duplicate key (ex. for size queues the size of
two requests can be same) handling might need some work. Basically,
either special combine function needs to be written (not sure yet what
we should do there) or we always need to ensure that the key is unique
like (size + start_urec_ptr). If the size is the same, then we can
decide based on start_urec_ptr.

I think we can go by changing the implementation to rbtree by doing
some enhancements instead of the binary heap or alternatively, we can
use one of the two ideas suggested by you in the email above [1]/messages/by-id/CA+TgmoZ5g7UzMvM_42YMG8nbhOYpH+u5OMMnePJkYtT5HWotUw@mail.gmail.com to
simplify the code and keep using the binary heap for now. Especially,
I like the below one.
"2. However, I don't think we should have a separate request object
for each queue anyway. We should insert pointers to the same objects
in all the relevant queue (either size + XID, or else error). So
instead of having three sets of objects, one for each queue, we'd just
have one set of objects and point to them with as many as two
pointers.
We'd therefore need LESS memory than we're using today, because we
wouldn't have separate arrays for XID, size, and error queue
elements."

I think even if we currently go with a binary heap, it will be
possible to change it to rbtree later, but I am fine either way.

[1]: /messages/by-id/CA+TgmoZ5g7UzMvM_42YMG8nbhOYpH+u5OMMnePJkYtT5HWotUw@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#90Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#89)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 12, 2019 at 5:40 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I am not sure but don't we need to retain the color of z as well?

I believe that would be very wrong. If you recolor an internal node,
you'll break the constant-black-height invariant.

Apart from this, the duplicate key (ex. for size queues the size of
two requests can be same) handling might need some work. Basically,
either special combine function needs to be written (not sure yet what
we should do there) or we always need to ensure that the key is unique
like (size + start_urec_ptr). If the size is the same, then we can
decide based on start_urec_ptr.

I think that this problem is somewhat independent of whether we use an
rbtree or a binaryheap or some other data structure. I would be
inclined to use XID as a tiebreak for the size queue, so that it's
more likely to match the ordering of the XID queue, but if that's
inconvenient, then some other arbitrary value like start_urec_ptr
should be fine.

I think we can go by changing the implementation to rbtree by doing
some enhancements instead of the binary heap or alternatively, we can
use one of the two ideas suggested by you in the email above [1] to
simplify the code and keep using the binary heap for now. Especially,
I like the below one.
"2. However, I don't think we should have a separate request object
for each queue anyway. We should insert pointers to the same objects
in all the relevant queue (either size + XID, or else error). So
instead of having three sets of objects, one for each queue, we'd just
have one set of objects and point to them with as many as two
pointers.
We'd therefore need LESS memory than we're using today, because we
wouldn't have separate arrays for XID, size, and error queue
elements."

I think even if we currently go with a binary heap, it will be
possible to change it to rbtree later, but I am fine either way.

Well, I don't see much point in revising all of this logic twice. We
should pick the way we want it to work and make it work that way.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#91Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#90)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 12, 2019 at 7:08 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jul 12, 2019 at 5:40 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Apart from this, the duplicate key (ex. for size queues the size of
two requests can be same) handling might need some work. Basically,
either special combine function needs to be written (not sure yet what
we should do there) or we always need to ensure that the key is unique
like (size + start_urec_ptr). If the size is the same, then we can
decide based on start_urec_ptr.

I think that this problem is somewhat independent of whether we use an
rbtree or a binaryheap or some other data structure.

I think then I am missing something because what I am talking about is
below code in rbt_insert:
rbt_insert()
{
..
cmp = rbt->comparator(data, current, rbt->arg);
if (cmp == 0)
{
/*
* Found node with given key. Apply combiner.
*/
rbt->combiner(current, data, rbt->arg);
*isNew = false;
return current;
}
..
}

If you see, here it doesn't add the duplicate key in the tree and that
is not the case with binary_heap as far as I can understand.

I would be
inclined to use XID as a tiebreak for the size queue, so that it's
more likely to match the ordering of the XID queue, but if that's
inconvenient, then some other arbitrary value like start_urec_ptr
should be fine.

I think it would be better to use start_urec_ptr because XID can be
non-unique in our case. As I explained in one of the emails above [1]/messages/by-id/CAA4eK1LEKyPZD5Dy4j1u2smUUyMzxgC2YLj8E+aJpsvG7sVJYA@mail.gmail.com
that we register the requests for logged and unlogged relations
separately, so XID can be non-unique.

I think even if we currently go with a binary heap, it will be
possible to change it to rbtree later, but I am fine either way.

Well, I don't see much point in revising all of this logic twice. We
should pick the way we want it to work and make it work that way.

Yeah, I agree. So, I am assuming here that as you have discussed this
idea with Andres offlist, he is on board with changing it as he has
originally suggested using binary_heap. Andres, do let us know if you
think differently here. It would be good if anyone else following the
thread can also weigh in.

[1]: /messages/by-id/CAA4eK1LEKyPZD5Dy4j1u2smUUyMzxgC2YLj8E+aJpsvG7sVJYA@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#92Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#91)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 13, 2019 at 6:26 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I think then I am missing something because what I am talking about is
below code in rbt_insert:

What you're saying here is that, with an rbtree, an exact match will
result in a merging of requests which we don't want, so we have to
make them always unique. That's fine, but even if you used a binary
heap where it wouldn't be absolutely required that you break the ties,
you'd still want to think at least a little bit about what behavior is
best in case of a tie, just from the point of view of making the
system efficient.

I think it would be better to use start_urec_ptr because XID can be
non-unique in our case. As I explained in one of the emails above [1]
that we register the requests for logged and unlogged relations
separately, so XID can be non-unique.

Yeah. I didn't understand that explanation. It seems to me that one
of the fundamental design questions for this system is whether we
should allow there to be an unbounded number of transactions that are
pending undo application, or whether it's OK to enforce a hard limit.
Either way, there should certainly be pressure applied to try to keep
the number low, like forcing undo application into the foreground when
a backlog is accumulating, but the question is what to do when that's
insufficient. My original idea was that we should not have a hard
limit, in which case the shared memory data on what is pending might
be incomplete, in which case we would need the discard workers to
discover transactions needing undo and add them to the shared memory
data structures, and if those structures are full, then we'd just skip
adding those details and rediscover those transactions again at some
future point.

But, my understanding of the current design being implemented is that
there is a hard limit on the number of transactions that can be
pending undo and the in-memory data structures are sized accordingly.
In such a system, we cannot rely on the discard worker(s) to
(re)discover transactions that need undo, because if there can be
transactions that need undo that we don't know about, then we can't
enforce a hard limit correctly. The exception, I suppose, is that
after a crash, we'll need to scan all the undo logs and figure out
which transactions are pending, but that doesn't preclude using a
single queue entry covering both the logged and the unlogged portion
of a transaction that has written undo of both kinds. We've got to
scan all of the undo logs before we allow any new undo-using
transactions to start, and so we can create one fully-up-to-date entry
that reflects the data for both persistence levels before any
concurrent activity happens.

I am wondering (and would love to hear other opinions on) the question
of which kind of design we ought to be pursuing, but it's got to be
one or the other, not something in the middle.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#93Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

2. Introduced a new RMGR callback rm_undo_status. It is used to
decide when record sets in the UNDO_SHARED category should be
discarded (instead of the usual single xid-based rules). The possible
answers are "discard me now!", "ask me again when a given XID is all
visible", and "ask me again when a given XID is no longer running".

From the minor nitpicking department, the patches from this stack that
are updating rmgrlist.h are consistently failing to update the comment
line preceding the list of PG_RMGR() lines. This looks to be patches
0014 and 0015 in this stack; 0015 seems to need to be squashed into
0014.

Reviewing Amit's 0016:

performUndoActions appears to be badly-designed. For starters, it's
sometimes wrong: the only place it gets set to true is in
UndoActionsRequired (which is badly named, because from the name you
expect it to return a Boolean and to not have side effects, but
instead it doesn't return anything and does have side effects).
UndoActionsRequired() only gets called from selected places, like
AbortCurrentTransaction(), so the rest of the time it just returns a
wrong answer. Now maybe it's never called at those times, but there's
no guard to prevent a function like CanPerformUndoActions() (which is
also badly named, because performUndoActions tells you whether you
need to perform undo actions, not whether it's possible to perform
undo actions) from being called before the flag is set. I think that
this flag should be either (1) maintained eagerly - so that wherever
we set start_urec_ptr we also set the flag right away or (2) removed -
so when we need to know, we just loop over all of the undo categories
on the spot, which is not that expensive because there aren't that
many of them.

It seems pointless to make PrepareTransaction() take undo pointers as
arguments, because those pointers are just extracted from the
transaction state, to which PrepareTransaction() has a pointer.

Thomas has already objected to another proposal to add functions that
turn 32-bit XIDs into 64-bit XIDs. Therefore, I feel confident in
predicting that he will likewise object to GetEpochForXid. I think
this needs to be changed somehow, maybe by doing what the XXX comment
you added suggests.

This patch has some problems with naming consistency. There's a
function called PushUndoRequest() which calls a function called
RegisterRollbackReq() to do the heart of the work. So, is it undo or
rollback? Are we pushing or registering? Is it a request or a req?
For bonus points, the flag that the function sets is called
undo_req_pushed, which is halfway in between the two competing
terminologies. Other gripes about PushUndoRequest: push is vague and
doesn't really explain what's happening, "apllying" is a typo,
per_level is a poor variable name and shouldn't be declared volatile.
This function has problems with naming in other places, too; please go
through all of the names carefully and make them consistent and
adequately descriptive.

I am not a fan of applying_subxact_undo. I think we should look for a
better design there. A couple of things occur to me. One is that we
don't necessarily need to go to FATAL; we could just force the current
transaction and all of its subtransactions fail all the way out to the
top level, but then perhaps allow new transactions to be started
afterwards. I'm not sure that's worth it, but it would work, and I
think it has precedent in SxactIsDoomed. Assuming we're going to stick
with the current FATAL plan, I think we should do something like
invent a new kind of critical section that forces ERROR to be promoted
to FATAL and then use it here. We could call it a semi-critical or
locally-critical section, and the undo machinery could use it, but
then also so could other things. I've wanted that sort of concept
before, so I think it's a good idea to try to have something general
and independent of undo. The same concept could be used in
PerformUndoActions() instead of having to invent
pg_rethrow_as_fatal(), so we'd have two uses for this mechanism right
away.

FinishPreparedTransactions() tries to apply undo actions while
interrupts are still held. Is that necessary? Can we avoid it?

It seems highly likely that the logic added to the TBLOCK_SUBCOMMIT
case inside CommitTransactionCommand and also into
ReleaseCurrentSubTransaction should have been added to
CommitSubTransaction instead. If that's not true, then we have to
believe that the TBLOCK_SUBRELEASE call to CommitSubTransaction needs
different treatment from the other two cases, which sounds unlikely;
we also have to explain why undo is somehow different from all of
these other releases that are already handled in that function, not in
its callers. I also strongly suspect it is altogether wrong to do
this before CommitSubTransaction sets s->state to TRANS_COMMIT; what
if a subxact callback throws an error?

For related reasons, I don't think that the change ReleaseSavepoint()
are right either. Notice the header comment: "As above, we don't
actually do anything here except change blockState." The "as above"
part of the comment probably didn't originally refer to
DefineSavepoint(), which definitely does do other stuff, but to
something like EndImplicitTransactionBlock() or EndTransactionBlock(),
and DefineSavepoint() got stuck in the middle later. Anyway, your
patch makes the comment false by doing actual state changes in this
function, rather than just marking the subtransactions for commit.
But why should that be right? If none of the many other bits of state
are manipulated here rather than in CommitSubTransaction(), why is
undo the one thing that is different? I guess this is basically just
compensation for the lack of any of this code in the TBLOCK_SUBRELEASE
path which I noted in the previous paragraph, but I still think the
right answer is to put it all in CommitSubTransaction() *after* we set
TRANS_COMMIT.

There are a number of things I either don't like or don't understand
about PerformUndoActions. One is that undo_req_pushed gets passed to
this function. That just looks really odd from an abstraction point
of view. Basically, we have a function whose job is to "perform undo
actions," and it gets a flag as an argument that tells it to not
actually perform some of the undo actions: that's odd. I think the
reason it's like that is because of the issue we've been discussing
elsewhere that there's a separate undo request for each category. If
you didn't have that, you wouldn't need to do this here. I'm not
saying that proves that the one-request-per-persistence-level design
is definitely wrong, but this is certainly not a point in its favor,
at least IMHO.

PerformUndoActions() also thinks that there is a possibility of
failing to insert a failed request into the error queue, and makes
reference to such requests being rediscovered by the discard worker,
but I thought (as I said in my previous email) that we had abandoned
that approach in favor of always having enough space in shared memory
to record everything. Among other problems, if you want
oldestXidHavingUndo to be calculated based on the information in
shared memory, then you have to have all the records in shared memory,
not lose some of them temporarily and have them get re-inserted into
the error queue. It also feels to me like there may be a conflict
between the everything-must-fit approach and the
one-request-per-persistence level thing you've got here. I believe
Andres's idea was one-request-per-transaction, so the idea is
something like:

- When your transaction first tries to attach to an undo log, you make
a hash table entry.
- If that fails, you error out, but you have no undo, so it's OK.
- If it works, then you know that there's no chance of aborting
without making a hash table entry, because you already did it.
- If you commit, you remove the entry, because your transaction does
not need to be undone.
- If you abort, you process the entry in the foreground if it's small
or if the number of hash table slots remaining is < max_connections.
Otherwise you leave it for the background worker to handle.

If you have one request per persistence level, you could make an entry
for the first persistence level, and then find that you are out of
room when trying to make an entry for the second persistence level. I
guess that doesn't break anything: the changes from the first
persistence level would get undone, and the second persistence level
wouldn't get any undo. Maybe that's OK, but again it doesn't seem all
that nice, so maybe we need to think about it some more.

I think there's more, but I am out of time for the moment.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#94Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#93)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 2:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

PerformUndoActions() also thinks that there is a possibility of
failing to insert a failed request into the error queue, and makes
reference to such requests being rediscovered by the discard worker,
but I thought (as I said in my previous email) that we had abandoned
that approach in favor of always having enough space in shared memory
to record everything. Among other problems, if you want
oldestXidHavingUndo to be calculated based on the information in
shared memory, then you have to have all the records in shared memory,
not lose some of them temporarily and have them get re-inserted into
the error queue.

The idea is that the queues can get full, but not rollback hash table.
In the case where the error queue gets full, we mark the entry as
Invalid in the hash table and later when discard worker again
encounters this request, it adds it to the queue if there is a space
available and marks the entry in the hash table as valid. This allows
us to keep the information of all xacts having pending undo in shared
memory.

It also feels to me like there may be a conflict
between the everything-must-fit approach and the
one-request-per-persistence level thing you've got here. I believe
Andres's idea was one-request-per-transaction, so the idea is
something like:

- When your transaction first tries to attach to an undo log, you make
a hash table entry.

..
..

- If you commit, you remove the entry, because your transaction does
not need to be undone.

I think this can regress the performance when there are many
concurrent sessions unless there is a way to add/remove request
without a lock. As of now, we don't enter any request or block any
space in shared memory related to pending undo till there is an error
or user explicitly Rollback the transaction. We can surely do some
other way as well, but this way we won't have any overhead in the
commit or successful transaction's path.

If you have one request per persistence level, you could make an entry
for the first persistence level, and then find that you are out of
room when trying to make an entry for the second persistence level. I
guess that doesn't break anything: the changes from the first
persistence level would get undone, and the second persistence level
wouldn't get any undo. Maybe that's OK, but again it doesn't seem all
that nice, so maybe we need to think about it some more.

Again coming to question of whether we need single or multiple entries
for one-request-per-persistence level, the reason for the same we have
discussed so far is that discard worker can register the requests for
them while scanning undo logs at different times. However, there are
few more things like what if while applying the actions, the actions
for logged are successful and unlogged fails, keeping them separate
allows better processing. If one fails, register its request in error
queue and try to process the request for another persistence level. I
think the requests for the different persistence levels are kept in a
separate log which makes their processing separately easier.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#95Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#92)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 15, 2019 at 9:56 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Jul 13, 2019 at 6:26 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I think then I am missing something because what I am talking about is
below code in rbt_insert:

What you're saying here is that, with an rbtree, an exact match will
result in a merging of requests which we don't want, so we have to
make them always unique. That's fine, but even if you used a binary
heap where it wouldn't be absolutely required that you break the ties,
you'd still want to think at least a little bit about what behavior is
best in case of a tie, just from the point of view of making the
system efficient.

Okay.

I think it would be better to use start_urec_ptr because XID can be
non-unique in our case. As I explained in one of the emails above [1]
that we register the requests for logged and unlogged relations
separately, so XID can be non-unique.

Yeah. I didn't understand that explanation. It seems to me that one
of the fundamental design questions for this system is whether we
should allow there to be an unbounded number of transactions that are
pending undo application, or whether it's OK to enforce a hard limit.
Either way, there should certainly be pressure applied to try to keep
the number low, like forcing undo application into the foreground when
a backlog is accumulating, but the question is what to do when that's
insufficient. My original idea was that we should not have a hard
limit, in which case the shared memory data on what is pending might
be incomplete, in which case we would need the discard workers to
discover transactions needing undo and add them to the shared memory
data structures, and if those structures are full, then we'd just skip
adding those details and rediscover those transactions again at some
future point.

But, my understanding of the current design being implemented is that
there is a hard limit on the number of transactions that can be
pending undo and the in-memory data structures are sized accordingly.

Yes, that is correct.

In such a system, we cannot rely on the discard worker(s) to
(re)discover transactions that need undo, because if there can be
transactions that need undo that we don't know about, then we can't
enforce a hard limit correctly.

I have responded in the email above about this point.

The exception, I suppose, is that
after a crash, we'll need to scan all the undo logs and figure out
which transactions are pending, but that doesn't preclude using a
single queue entry covering both the logged and the unlogged portion
of a transaction that has written undo of both kinds. We've got to
scan all of the undo logs before we allow any new undo-using
transactions to start, and so we can create one fully-up-to-date entry
that reflects the data for both persistence levels before any
concurrent activity happens.

It is correct that no new undo using transaction can start, but
nothing prevents undo launcher to start the undo workers to process
the already registered requests which can lead to some concurrent
activity.

I am wondering (and would love to hear other opinions on) the question
of which kind of design we ought to be pursuing, but it's got to be
one or the other, not something in the middle.

I agree that it should not be in the middle. It is possible that I am
missing or misunderstanding something here, but AFAIU, the current
design, and implementation allows us to maintain the pending undo
state in-memory.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#96Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#87)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 11, 2019 at 9:17 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jul 9, 2019 at 6:28 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

PFA, updated patch version which includes
- One defect fix in undo interface related to undo page compression
for handling persistence level
- Implemented pending TODO optimization in undo page compression.
- One defect fix in undo processing related to the prepared transaction

Looking at 0002 a bit, it seems to me that you really need to spend
some energy getting things into a consistent order all across the
patch. For example, UndoPackStage uses the ordering: HEADER,
TRANSACTION, RMID, RELOID, XID, CID... But the declarations of the
UREC_INFO constants go in a different order: TRANSACTION, FORK, BLOCK,
BLKPREV... The comments defining those go in a different order and
some of them are missing. The definition of the UndoRecordBlah
structures go in a different order still: Transaction, Block,
LogSwitch, Payload. UndoRecordHeaderSize goes with FORK, BLOCK,
BLPREV, TRANSACTION, LOGSWITCH, .... That really needs to be
straightened out and made consistent.

I have worked on this part, please check in the latest patch. For
some of the header i.e RMID, RELOID, XID, CID, FORK, PREVUNDO, and
BLOCK have an only one member so there are no structures for them
except this I think others are in order now.

You (still) need to rename blkprev to something more generic, as
mentioned in previous rounds of review.

I will change this.

Changed to prevundo

I think it would be a good idea to avoid complex macros in favor of
functions where possible, e.g. UNDO_PAGE_PARTIAL_REC_SIZE. If
performance is a concern, it could be declared static inline, which
should be as good as a macro.

ok

Done

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

Currently, we are doing like this

a) BeginUndoRecordInsert
- Copy the global "undo_compression_info" to our local context for
handling multi-prepare because for multi-prepare we don't want to
update the global value until we have successfully inserted the undo
record.

b) PrepareUndoInsert
-Operate on the context and update the context->undo_compression_info
if required (page changed)

c)InsertPrepareUndo
- After we have inserted successfully overwrite
context->undo_compression_info to the global "undo_compression_info".
So that next undo insertion can get the right information.

and I also think it's just all zeroes (so

initializing it isn't really necessary), and I also think that it's
just used for initializing other UndoCompressionInfos (so we could
just initialize them directly, either by setting the members
individually or jus zeroing them).

Initially, I was doing that but later I thought that InvalidUndoRecPtr
is macro (although the value is 0) shouldn't we initialize all
UndoRecPtr variables with value InvalidUndoRecPtr instead of directly
using 0 so I changed like this.

It seems like UndoRecordPrepareTransInfo ought to have an Assert(index
< some_limit) in the loop.

Done

A comment in PrepareUndoInsert refers to "low switch" where it means
"log switch."

I will fix.

Fixed

This is by no means a complete review, for which I unfortunately lack
the time at present. Just some initial observations.

ok

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

undolog_20190716.tar.gzapplication/x-gzip; name=undolog_20190716.tar.gzDownload
���-]��iWI�(|����bn��cl���A�5e��vOM���K����������g�-!������>U6dFD����/���;���qz��i��z�i6\FE��&��8���I:�G� *bx�
��G���\��E�lnn���(�w���M�_�����Lm=y�����3�Umn�>���_js�����,/��2�����l�����=�E��/��6K'��dws8z�:x�=�����������Q�w��[����h���\��:�S��Lmn����6�2�B]\��(WG�$K�O��1�����i�z�������Q�_���h��m��Q#�'6�_l={������ln.������������{���xk�WE�bC�B�U0;�X�C�"�U��\���i'����i�~��ZE�:�UE����W<���=H�a�CE��m[��*��B��_�'��l�������G�.�U1��\���*�������g�e�U'��q:�b�N�G���ce;��dn�d��qz�&�,N�M�~M�!�����dp��I�w��4K�c=�!�+�v�h��9|q����|����Z���/��|0�s�����������~4�����h0�y����$�&�o`~q��������S����������J�?�)��a;�:�yG���$�5L)o��./�>����%�S�x���m	?�7jk���g��ml<���v���-@v;;�'�u��_������g[�]���lo)x��CzyF0���z�:�\f���&1���v�h������R��0���R�h����\���V�/������8���u�y��������i�p�bU��Jm�V+���v��z�o/:g��S3(����$�:-��it�{�<�O����t�����oW���+�O�v���:����[�9.��~p���������_����K���Z�9��'\e:�^���������]���7@!�&�|^o������������Ju���8�]Q������i��XZr}m+^���������([��8�dTo���������'�~>�������=}��z{���n��Vx[���e������b�������x:��U�z�/C=B�X������M%dj�#Dj4��q�U<���/!Y��r��6}���y�����B�q%�D������y@G�Z���iq;�9J35����f�u����m^PO}&��
�

�>��!\&8������'[����l������U������H����O�@7���7����;[Ov�J�����'������C������{#d��`ow=m�F�u�;��<{2����v�o�v��r�c����L��:�2���v_�����o���0�?�Y^�T�W���&@|�Y?���n���C���G���H�0�>P��?P���"�AbAn�������~ T:��2���j���;��2������L�4�gC�P����v��Hg0*��C�	�$�cB��na����*(��qN�E�rM?l��/'8<���?:[���������e-$ �p��4h��6�����,E���#��
��!���L'�~p��S���x��l|k�"�p��dR9��G7�X>2r���$OA7Y�=W$�y��>���"�i�'i�
����(@�����<����2;0������H�nfM���2M����0�!
�f�Wxx��o6��	6����c0��)�<�MIv���|�{X�h��,���Y��15GT���������,�����
$�s��������?>��h
��(������/?��y����������N~|r������_��w����|}�e���7�v��������;���e7Of�O������I��"�F��(���
����S[;����{�N���3��J�m���o�g���>7;A����s���<{�� \G��a'@���>�]�����e)�N�7�����a�S�Sy�O�s^6��>f|�C �7����>;�>��������w�+�A��K.�|�
����^��ca�	a��d:���U���6���C�&�4�z(A��1��O�!4���4N�K��t�N;��3}:����j�P<��q���XM�1,����^eP�^���|��)����T�������;X��	o~�?���j.��w������A*����n\��w����~|9�N�����}������}�fU�g�<�%��m��S���_�E�~.-��n�C��e��� 1;�����K���CDR�1r!~OZ���>�FW�ZU�V���X���n���*�i�q7�s�~��<@�np$��djIbn������>c`A����'��`��p�g
}����y������c�&����n����W�	n���29���@���:TZ��>�G�U�y[;�R�;���X�)Yd������6~(��[�VT� j�����CW������A"n������R(�e���o� �j �3�66P>��{�<�X*v�>�*��6O�`}uY�~��iQ�.��.�;������w�j�����0Uc�&$p�r���U����L�H��w���>|sooSoF�����(���d�7Z�l��?G58���������0��+�����[�
��������f���q����R>����{v��`�J�3�����2���B]E�����T�2�~�i���|J��cy5��xw���)��i���'�.&n�
@b&_�<���X���fr�O�n��[������y������y�,��7�z��<y���r��JR50X%�S��"�_�(����:�Y<���cY!4\�A�t�G�#��(��$����D��F~��xb��P�f��X��fY�Ar���h�������V����L����]=��7������Y��-��OH>PWE���A������h��h�B`B����l<^R�/!�n��Nn�����������gyM=��U�MxG�R�$����U�;N�&VO��p�)nu�No��
������w�Q��������A�����W=N��t}���1�N[��K���9�c�Y��� �0�$���h��a�����{��=9��.~2�zh�4����B���V�R�^�	�J�K!�4����\E���u�MF)*U���Dv^ m��3��kh��,���O��]��.�koIS�������O��C��z����g�������������6|��O��>���g��������f�3+>�������y��.@������-}�'��q��V4��kzBB��Q;������a������g��czhQ+d��}=�r]�M�IT���;��]
�[p-�����������~5��}�c:�.	��r����l����S���}�.��q1�����>{�V��q�Q���a����L%\$�G�jl���������5�Mc
e"=��x87������^�x;t�������2��=v����`7$r�����N.����]�n�]��]�*�}�����������x)�����K��E���z2���S����^�����^���g;���(wz�I;b����~F�D"5����)T�50��|U_Ky�0��vo��
�6z�&����ZYX�����{r�I{Zzss����'O;;���1������������.��V��3$�P���t
���{^�Q�x���2w�mm�B��'��(��#J�M����S��w�,���)N���Ut�[C�6tn63t+f?`q��}��K6������N��eA�y��u�oA�XM�O�5r��3�B,�D�����p���k�1
��|���E�,�A1^]��}�6����8����{����d���$eE< ��eu`���y�s�t��G�������0[��O���{f��<���MB�E�*=?����l�������gG|q�;o��w^Ei(��t��l����o�������f��P�i���
�)�������������j���4����^V��3%	]oae��O.��C#���??%/�	��d�K��WY�"6Hr_���FC����JuG��q��7��x6�������rk(�0�Sat�F�p�������� ���q4pw���b��[9�Bb��L��jhM�vVfj)/�5����Gf���b�+f�e�V7x�����[��^O�����R�����:$;����)}Y��!��� �%�U8\d�����8���z���~��G[~���Vt�6�������g�4�l�f~��w�@�
<�����9�{���y�������e*^�/x�k��������\�6Lu�<,L����K�����������i<�V������%g����0`�:L��g��^���w�2��U@��&�����Mo��[F6�=��'���Z�K]�c%�iG������A�Z��ou��m���WVfa���;H�$pQ�������M_�g�8X5?j\����i�������H��Nih��!)��9�4��A$}L��u�M��@%	~�wc#���m����`���-���F���s�����	O/��J��	���P't8
L�>���0�����t�� �����
��ut���5�4
?G���3�i�v6�����=������B���g��h��Gc�������%�S�����m}�9t�
Ci��q�j��k��z�hC8�����,%��+��_��7G�������{N~F�X���Cg���)5"l�p���':��Z�@\!~�Ds���u�H����
������(������Y�*����8�?��������R���"'3dT������4J��Uf�wv�#^{����q=lBtZ�DkQ�ZB&�	����8/(�,$�,��x���O�6�%|d������{�D1"E�����t>�v�������N~>�?�_��b��P��wio�v��V���]bC����xSh.����on�i������Q�M��s���|}��������G>��O|����=��13�	,��U��hR�q�7YG��<�a���;���v��Y���V[����m���C����s����y�_�����u��s��k���?e���������E���<c��������������:L�
(��}��G}�\���������%�����ot(Y��|L��-�g����������E|��|�����<�#���-���9����~D�g����J�/��'3��#}P?��}hjH�������Hn���w"V���Im�y:���p�����dKs��P���`%l����z+�3��`_>�m�Pf�$.;iIs���m��X�s��d�����:��)�=���:
4��k�4��7IC���)8xm,�o���e�Y)��	j	[`�v4�KO��?N��#s���:�Tzk|����sq�r��}����/������.�}���Q�E�
����.C�}��ZMo�������^��0R5@=���<�k��$� �_~���N����)���	���t���V]w�3&T^d�A��:��2'TT��$$g����H�r�&�?:4c�����(���H0b��
hm0G
��F����LC=�����<�8��-y����(���Q�/�� *�e��:G�m��8���A�U�o��w9,9���,-��_�~�|�����������w�9z�9�o�^]�K>7��%��HZ�C!�7�)[�*��G�#���>B=r�}<"1�����PV�oXw(��!���w{��P�F�"=r�g�`3�x��b/�&��������p�eb���)�����P����7�vj��s��C[�@k��q�������"�����Y�=�*C���`4qO�~���V�5�q��7q�B;���9��N|<�,����`93���_O�C��	��V��3`��(�7�^��4�u;���K�������<�X^;}�{���E$����V�)���Bp��������n=�r%�`@Z����'�O����]�O�4�o�>�4�$��'h���3����x�qdRi�Y$�m�).8	��L��&�gFe�>�����5�Go�0��_O�H����C@+"_Rj����+2�C�*B�7���$��A��i���C.�_5���+��k|���c[�5-����_)C��X��h.�	��Em~��1�|�vJ(c���q�
��I������M��L����V��Ojf��OG�
^Ud,,/kY��<_-�������y�$ b����,Z����?�������
P]CW��cG;'��Z	��Ue�V��}I� B�\|���g�ym1Z�H(-W7���N����i�X��0�YV�~��-��`�?iqF4�\o'*v����V$�W����H��|��5j�o&�.8��	��}k6�,�|��m��6���UV���_3S�V�8j]���V�=�V���S���E9�F����}�-������L������D��<m�z�H��y/}���Xw�g�l��qtK}���3�;�r��#
-,5K/e� ������s��LO#2��.3��+����l�����*_��n����u�N����+0+%�pK��~�w��/]*��������14*e<�6�D��/��bL�	�P N�f�:�R�%Rs�t6=4c�BN�-N����y#��r��z�;o�?~���	��t���;���0l)�zB��E2���`��:��|��t��=��LZ��P�@�Hd�f����M�o�A�>pQ��������_�;}t����
�0 ��1���6�����+np
��3����(�c���H�%��_�&�jo���Y�h:�Z�;S
J�����Tu���\<�3��!��������9�L�D������!N����W�&�A<��"�G<��8J�1yuq�u"z��4M4%]re!g�@�����7~������Zz[��O�S���K��@@�#�(N���w�j|kH�P��('�0b>�������� 4�F �s��i��/�m~/qN`�����������D����,rdc�XEM�1�$0���&���B��\�@fu,2����H�E �����8]�
��������u���~��2OH6�_#t���V���bN1~	1�oG��3qK���c&�:&i)wT��p�WK�\�����X}�Z��pR��w����"��%?��w�����f��a�7|J�/*�����KF��Z	�����X���������-8� /�"�s�A���L�'0�2�F���z8q�0
.�M[���V�=�� QbmF�������y�����&-M#x)��u��������w��<�%�T�F{�������%�eR�5�BexB�����e���{|��C����D�0L�h��j������+_��I�T$h�/��8�M<"A�6���J���8�I�B����!����2�i�2�U���D�����t�Di���t����S�x2���e���W�$&���$�����k�;�"�x~6!�V��]�s��bF��&61���F��N������|�iu��hf^�e���9[����i�ZW[A��9�5?`��BE	7����8�mY��!���1ye������&�3�4 �"*�:N�(Q?������a�P]�7(�<P��&+l.J��f�n�]R���V!C1�C�6N�����7n�##�"�����c<J����r1���;�L���'
f$��.��<�Y^X�y��q��]k��6��1��SN���Z��_t�����NRdQ���K��U�����U���
�,�a��3�5Ng�d� 7���(Z�%�etx��#>D�(�VPPJ�4�w��C����C�/h��Q�k`l���#�����uG������QRM>���<H�I���	���e��(���2�I�?MV�Jc�y�CR�����k�	ccO�"m��?XZ�Xj:�RFPL@-�B-�6�,/PQ�0���������4@]�
��������,���,�[dRQ��?��:xs���0�;���������_:��vJ!�4�T{7~�w����L6k����0#]���I���/�/>���
?~'�"_?�Y�
L��+���(c��,�t|M��^
u.N�>�z�NL�zU�wzzS����W���(}
1*	
H������1���fm�I������Sr�]����Qvi�\���v5�U���i�QF��pB�@���2L��w�����p
��'��6PW��H�dA��u��@�"[]���~M���y�z-WI�e���A��|�)�]"'lq��h����#�4*1�B��Y|�zD�������������m=�~i�|T2yj;Y�:�k�=�@gG��PCf������
����(GZ��3T�C��#����&9�Ect��5v�\��aALY����)*��R]�����A����$���e����"���9s���#WB#����HK)�y03�����������<���w%��"nLk�w��&	<#���q�D:��?8������V�:�'��(�0�YW,�'T�&�����B���rF�	\��l	pwx ��HZ��t�=��	��T'De0>�����wv��L�?P����]�M�x�5�L���j610�����B�hHwL��z9D+t���L��0Nk�x�*��T��u���N��|�a[�E[���g�>��\��2�
;����O��:h�I
H�@#�~�'��f���H�����|�Kg��E"������z����;8�I~�Z!��w���m���������d��
�MX���d�M��F��yx)�������o�R����:���G�^��4��{�`hF���#�f$$�#�������H��cH�Kt�<�XJ ���AC+��O3f<����'/�����&;�������W/���,�c8��J����2��gW�����v�	��=a����`g�_�.���?5���@v+7��)ry����+M����O��D� ���O@�K���N2�{@*WY���SW�i�_�YZ~��BVFl�S�z��m����}���@��	s����	�F�ud��2��x�b���)���K�b[�wJ��fK�`�	M���Tg�4lE�\�)6#'����
���mu'�c����:��W�`3�Z��(�-���km�5��6�w}���
�Uo�(���8)�x�HYh��5,Vr��������C�M��%��d��:�#�� 8I��������p�������
�F�[���XLZ&+80�xS�k�������>��l�<"D=������Ae`�������;�NW+
oz�Hv��
�V\���f��h�t�-V;��%-)�Q�Ba@8���\�`�?��nR�z}dY�5v'.nE ,�<�&�i�X
� ��{>&V���
x���6���r��h�G�1���L�Wn�W�/^k;%�K�q�^��`#'��1�~���"2F��tL�g���E���*:���8��7P�3�$����������Y&3'}w�U��6�W�h������T��P�3l�N�t��]y������kM�-���<$c����`�0'���U�����}4�}��_�4o�D��t|��������Q`�G�����s�'tx��A����/c�7x��I���8��I4����H�9����<�b�F��s�Y�2���P�D~��3���M0"��#[��|UE�Q�RJ�F��xv��~�j�^��J��]6��%zm�l��������v�\$v�����2���oU=�a�0�&���f�����l>��a�6i��]��T�=��\����.�Gl���C��A4�lt
��`�}F�����=
����M��3i��0�t�����:L�6a����l�����Ye����t�4��#���p�YE���vQ�E�������2���H�;���[��?�^��ust��I�>\m$y�h ,<
����h|���z�^6.��e7�'��v*v��I���S7���8^�8:$a�!rf��mv���CDQ4���\O�V8���������<�Gr%���� p�V�d�����E%�%%
������Q�8�>MB�{kLv���0���R�
��/��W	FS����8�uA�.0��q��v�����Q�Y��jb`nM����fk� Rt�������i�	�n�G��{<�i0o�O�VM��X(�#z��pD�1������8���������2��c��=4f�HH+$��Xg��4����ezP��~��`������j/iai�0���B����ut���\$���;�1
*2c��v����Z&9QY}B#b��P3Vt������������(9�I���r����{�)!�w�v�d�8Y2����Y�)���D���@�j��#��;\:r�t�!�5m����,���
���`��6�E�3�Eu���D���;�����*���D����S)�+S.��aj��9+��������'r6��N����D$�����;���Tp��*��0�*�x�m(y�<i�GF����a�:�!;?�]�>-�����B%Kr�nv���=�a���$eU�B�N9��O~������������xR����pl�E������'$h�dh�k#���E��:o���Y��M���T��%M��9]l�iN`��y��7����8�����2k`/b�ioT��;��0"�$��,��u�I_2T,�N�D�x��]��;����0�t��$r������B���r������.�����xc�c��9�w��.�9���.������!���`W�2�K�{�Q�[��Ec9��F�h*����(<�#�'Sq������xp|�9�q���.i!O�2�����2�n��aR>�`�V:&d��]K�V�w<���i�+k���C��u�vp�	�*��.����!
������@�-r,~S�0_sF�*ixc�S�/�Pz��'��J�n_���\k	�7�F�X��=/0��i����H{���`^q%��$��|$m�uX2��������C�j�M�����g"��A��YZ*g���aY����p�la���}�:�}���<�W65����
�"7����y������H�Bh�VC9>�I���M�S�����W���-������Qb�@�g'�^X����c$�U�q|nb9H��D`�M�����&�=89y�	��C��kr��|M��eJ����<qu��o\�A�)&\,Z�?j����3 �)�e�O���K3\^�3;�N2��#��]�������5&4��.��-���i��/�,�o����UC�j�u��p�~�_�{�r���Ng�*������^��;�2��W	�p�*	k������o�7h`�=�Z]6��/D��jy&��V5���D���|8�����k���t��Ye"f3��z5�5z�*6�T�nF�$���O�0�=m���Z�-�=��#�y����q�C������h:nP�R�u!{Z�W����zufo&?�-9���q�Y�gy:��	B� �xc���6�k���a&L��5��y��{�XI���i�arI�*��,�k�y�6���������uFf���sKJ��ay/o������
��0���A'S�.
��XB������d��9$�|�x(���F��4��� �3�A�<�O�;�`
i�3����
$~9�p`���J�XIo*
��a0N�0�y�K5B��'K�(�������������<Z�#Q������L��#QY>����������4��1��LX��i���#���x<�W��9A3����@/&#e���_� Y� ��]1�����Z��RM�:���{m�q����	�����U�N��PC�'�
��{i�m�/��!����T�gk�-��������[,����<U�S�M��-��U�aG@Ev��
��~�?���2��J��>��?��������~�p�������x�a�D�g��)��qF�o]���T�����~����r�Up$��
�<�I���T����P��m���c	��\r;�<e
_������lDO����]���ft�6S�	����Z�r�GR�9+��.G�I��x�ey�*������Xe^��-��1������@Y��������.�(�������eM�sU���y^��r@�TX�{M��������{e?n;�'��lDn)5������Mq�����.�m��OZ�;��f���5�X�4G/0�5���L.�5�i����J�~�F?ki�������<�t8J+��������{�2����&��V5X���Flp����j^-����"����Z�^��+����{tA�-40`���H/�h�f���[�,c�W��-B����XZ<��\U�����3_�K�@����nQ��x$���#��a�H�G��M�Tc�)\A�%�MqY�L:�d$������:Zh[��"�p��DcR���HyA�wj��u ��
�������u��-M� ��E���������1����E�Z��QX�+A�&��m����aBw�~
�y�Q�:���E�B��@Boe��ta��a���M&+�\N��a�Dh#<����y#jut������DC���������0�&&�p2P�����jaHvt~H	H�j[�A�I�vi���k���a�!�\��pq;��%���k����V&h#�����I�nc��R^M��2��In"�����35?�9�`?yx��9k��Q��"<=I�D1"����M	N"�.��3P�J������K����Je���wg��\*�W%��"��Ib��W�8� �K�27�R���Y�xV��F�������[��8~���������^+�����&��B'�Z������Q����I�u�N��9s�iJ�5�(�>*T�DS�g�f��3�q��|����
)3��be@�*����.�Xb��fr������2�����.R�����,S(�z�Q�;tdFk+A�8#�Iu��:���o���
�������5��d4
2���?�M>4B��(��Y��M�����t:.���\�(��`zk��y[�Q�^;{��9�x�!��xU��������(#;���}��1�����0�,L8$�^�������
�\x�#8e�x\4�%[V4�5�9��������
V���<6u�a�7 ��C��u�y��C�Ec�M3�����g�0�b_��0�K�B<�_JJ�2�W�Lk�;tcx*���V���8��1��5F�NC�t'<�+lq�����)�e�mT����q(n�^iAxh���
���eSS��]W��>�
}���MAX�������-�p�F�J�'��y����B�E��&���V�G0:�;��ff��}������z�2��P	5��5��Z�5p����T��
08�!�9���-�}���WQb~Y{�]�\M��am�&�;����%��Kz���j����&���U{�$WBm��K���%�h8�����8�[��%V�%������V98L<����$Ws�33�&|��P�Lre�����a(����	���"K�D_��X*CO�6����h�A�������|{�9;��@�Kj;��s�zAM���'
[�|�B/P7�~����u��{�!�R������=��@�[���,}gU����Q�k�a�u����h�zj�;s���l���f�fW��u���\r��K��?���\���F]u�����V�
J�rp�ak���^b�qK����������r�
lA`���L��,�rY�~oN�~!��%�I��&~�4 �aR�#����82�A�
���L���v��s���Ws[� ������!H�^�@k�J]=��_���$��dt`�����-�|���]��_�B��[n�R�b.Z�����+����ubI(ds0�!}��Q6��mKTz���7�!(j���	�K����z�xa�|2�+f���AS���F`&z����R����Y��Ar��%�S���h k���������@O���
�FccY�������Q�"ZQ��9qk��"�F��'�,��1A>�.�l|Ka�~�&���D��[���r��l�(�
�Sd��O �-Y�H�C2�Q���!�!lls�����l}�li
^$�^��:��$�9�1��^�kJ��M���4�"q�X������~��e��i/�VU�b�pdWDR��j��i�#���jW-���S��W+����_������r������s���D�����lcQ<��}a��5\���M��n�a��^<�Y��T���p��x�F����H��d��A��o����t�<E�\^���v6��mK��:�7VB�.[�����.�b���J�B^��E���(�#DMd��ci�K�XU�e��)�}&YK,�/L�N��D�H�_:�2t��O��aa�P�5v�w)K'>$s���9��f��Tl��J��(�qjL�%3����Y��	��S1�H�q����O�i�UM;��[JV��F!�0vs������R-��{+���x�/V�>��v�::L�byE���r �e��S�}w�6��Q�3`t��f$�sL��bD����{d���4~0������"���`��p�{iYaAM�8�D	��2�Dl�n����L��x�L�!�wS.�at.��Gf0�����K���)X"�
L�3����Lh�\[6���(�[!����=�zm�Af��x6N�dv�Yy��(b�l�9����e��ee��9g��a\�w6�#�5����5����������z������A��pY� �M�hx Jn�����4Igb����Y�����{�qs]O���Ep%��������$��J@����y�X�
��M���Nf����q������C;*m���!���k���]�1h+$4�"���$�TJwH�'r��")P��T�B7��luJ��a�(��r�N!y'��t-d Jf��T>��1�4&�[����5��
05+�933r�%�����G��f���m����!!��.L��R&(�)_o�B��[�"���fV��`J���BS�{l�b��������> �� ��E������R;��
^�������2����EK������c�<)��jGax�B���5�rC5��q|�
o3���K��iEDvtE�Af�
����R��j��Z��]g��^����}z�q$6��]fYZ���a��cW�����������R�k,����L=!�����f�r��MR0� 0�F�hJek�1&�*�&|�2Ysb�����c/��GN���!&�uD��k8���
�S����Fk3^��B���
Db��cz��^�( .
������a8��u�X�6������O�2��WsR�����*�9�U� yy-��a����"�&k��d{
�PR/��(Z����=a���f������Q���Z���~����5�aml�/~Q��(�����#
�f�M�sq����M[���t�2s����9���]LC��kG�l9����i[�����^�w�1�w��[���q�MD��O����t�6a��Q*�%OW��mu3O5�k�fn�d����s{���!�p��RBd�pu%����Y�:Y�O�%��`K+����1}���KnZ�p�Aa���x�r7����^�j���3��h�������,��,\W��.������i]���>|������{~�vh�����JI����G?�.����1B��F�����z�j��=�oK����0��D�'�%�N�)������K�U#"��Q�P��%�����d[t��<m���Y\/���nh�&��'�f`�
L.?�R�	}��'�J���;*�z��Kl��[~�b�
�-��/kE�z"��O?����V��X��s�p:FrCe�5s�
e�
��"]�����sJ�.����i����5jx��qbSf:3^���V��R6�&PF����E�JDE�)B,\�?�
>�G�;}�:������}a�73?|�CyJ��U���	��f�E�q����Iy���L%*3JA�;'�� �:[��EV�f$�kTJ��v�Z����5�m1��9Hv���b!��7{#��<���pj]Sk|[�K���%������Q`�0�ws�����fa����.����^�7�\
b}?�>f}�������������\��{<��D�=�&D���������>X�(��/u���
�hJv���O�����^�v�[�!�'&��[���c�p-�H[�'���D��d��K������6��uKM>R�O2�bw,���<l`_���9�����R���4�/�	<��+M�����x���Y�Mx�X�Yz�rm��~X����
n#x������5�����p���q��cW�����	V��I.6L��r�o�0�z�MNe%�B�/����"����+���~�t�sN��W}��)����=o+��m��������_����N�o��3��Y�e�3������0�Z+��!����R(6������_������<~�tu���nS��!�J�(�:�e�i�
��u�vxJ>c��G�������`���r��Cd�G��J�!bR�������D����%��H"��'3<G^��t���?�`�c������PE�jy������OO�{o�:��G������DC�I=N�
�DF���<EV���L[yp��g�5pU(9��?��Pr3fu���b��N�5_Q5����?�w��2:�D�vr��*��[��2}W^M
8������6�]�DG�q��ks��n�;�������
x���r��e����������#w�=����|��4;�#2��:���WzqR_���\��3�D�G0B.�<���?�hh�QKpz�l�d{������Y_2�����q)W=���W�tJ>/u@
O��M�&�w��{����q9\p��~��<0D�Q����8.�n�������Pq(��d������O��a{��
Sy������5a)������������3������������s:^�Sz�Qb���;�H$#��)' ����;�\Cag&�~�No�[�L�P.��pXq���m{mc���
@�<q�@p)��P0��K_r"�9��*�t1���|i���"R� Sc������V��V�n�'���`z[M3�%6�3���x�����n�u`M�J�hY�8���W|��q
Yk�������v����kkk�����@1hy`���H�=��02ms������%U������(!��i�{����D���t������b��B�5E��,��\ ��1Xu�~�N�pV���w�/���1l�������P
��������]����g�^�4X]l@����Z4
i@��`p�����:E��F��f
	�g9i�R���5�R��Fd7���������_��������tP&�#���~�Co�-�s�	����#�c+�`�#���k�[��l�ZU�j���&�������A�+�HWW����R\������P^&X���X�r��|��,U/������o��+o�K|4k������Y��P����^�kj��OX�8B}�;�����{����x��jH��m��Q�V����o����8���*4���<v����9_� 
���wpD������-�h�D��Aj�s���-5t���It�^Y�K�\X�������d����M�7`
������V��oR�s�,��f)$�>��m�����`��+�$$Y���?�-�2��q�'��z'78�{��5�����"@��w����:��04�u�s0x�������>LL�W�����n�>��<L��	��qA��?����.f
�lZ�a��x���,����������e��>��
��Cxd���m�
���(�
#����8�D"�����(�_X���'�gf���5����x[��6q<�����������l���J)����g�"q��ZU��B
i!�7���*�F�Z�0����l�Xp�W��k���87���Tt�h)Q��D��d"u_^Q���lg�� �� �G�S��H��0�C�����6*�i����]�����+��b�~�@�ob��C2��g%�R�R���z!���B=M�~�F+���3���$���+x�+��r �Jxs��Fpi�W��e����$����W��DVr��i�,�p�V��?�]�Q4���`�~��
,I�U�K�x��6���n���f���nW�AE�q��5�/�~<Z6�@f��u��	�(X��S'��)���#��%%�9�����e ��L������C�����^�UM��ou-�op�����Z���JC������a!�������cMvMtlm��
�tx[�8�aa��{��>��?����G.�[����������H@���6�vw�;�]���Y���^��on�t7!���l�F���q���~�	�@=��)��l�t6���q�Lb��[���m�zpU*@(����o�o
3O������!o������{Q�1�S���u��4��F �?II�>�Wd�b�*��k����/��]0kH��
ffB4U9?�f��^m�{��bZ�SZ~6.$�����1����eIb����b��.�7�e��yv���j��#T���C��_�/��'���7��\�����|��.�7r�
����W1�e��	�2�tl�O����n��P�eS�#�)p�w��-���,�A�����o,����f��~�[�\��M�Qs9��/NGR��
s���-�bB��8���\<�$Z����D�R�;<��x�z��V�0"d\��2 ����<c/u�q�)�
��J�%���oX�o�P�h3�D���Vf'a�6���]��R2����o3��\�/�z��x�e�����f�A?����$8�}"F�m�#tI�5R�u���v�-#�F|�0X&�x����;��	[)��5�����������6N
Vt���@I��Q�c�sn�=5 {�-���P��L�#Y��e�P�W6K�F���~�G7��K���pb7�����1����6:��zXM��)�,�|�&���h0y
����T�#I�V���
�m����<�6�����.����@��W�R���[���������tK���o+������s���u!LxD��	g949���O+�������(J���k�szm0
��1v��km,P)U���u����
�Q�:��vI��������;!�-��c��c0��L,�0@���&��p	�����l�
�Q��8�QF��� ��!�g�@ck?�������S�y�':�RdQ�k�9�l������R����ol���0�^����`�J����b�zK.w&�y,��p��"�ux/%4e�`�ee���c|b�����`��$��U����*���4��/��&��WE��3�XR��������	���a������+�gjc��H9��HA��e�� �v�q?��"B�m��#��^I�v3���N�e����\�B�*�G�T
�A�7��]|���.P���!����M�~#('@�2���?i��������nt�<9+$�>����m����[!��=|�L[7'_�f����'�Aeh��1����-b i�jx88����h�FE����_\����.������p
��(�}�>Z2g)S;*��"PX�������3	���-�+?����S���������s�kx[
�����V?�D#��P�Q/�F+�k7*�{O�JR�uu��E��`b�����0g�V����7�,lE��I��M��2'�Y�Xr`�:�/����$�<���1;<F�66��Kn������\�k��l�S
����%%F�PXk��#|-��y��X������*����4l�����~���W��7�<�$&Y��~����j~����!�r�k� �#r��
!������T���_���(��#��V���������z�VX���Au�����QA��%�!,�l0����p��b�;s��CH�o�X� �J2�8���1g?Z�(�)�9K��r����<�N�K(������2������5�<\i-���%H����t�t�U�,����u
D�M�&[��'IB��!'%$��~�T� ��RJ��I��L����x8��������P+���`!�t.��I����]��Lf���7cB� $���8�����SG��,��3��cQ�"�6�Yq��)������N���y���gM�n�����\����	,re����t^���2�"�w�T��q�0�K�K�R2�60�d�b�@�L���~%v�fW�6���^S�
C���k�y�6��(���@��s���gK�X���q&>G�M���8���T2M����S�����v�{�G��}��U��
[lt��.Y����>2�����^q'��g�OS�J�24���%�I��[%�,�P��%At~kb�L
iO�!RF~�T��(����^��������
F������"�va�W���FM��;�2�<����[<@!��qE��n�'���1>���z��T��}�7W���I�������Z;�P���	�7��_do';-�iT�:��<�NA�5���
��ItOf�jP��=���>��\������6VL<Ah�2�6B^
	0�D��>(�$�4�ZOP����gwN@����)�<f���xM	��@��IlRIlV]�zL-��yK��I��d��c=1�����G����]���i�6�aU3�}�G������g�Q&)G>�M�/P�+���aI
r�^�
��+	Oc��j.����Pc3��\H"�!S���B:�4/��\3?�Oc�yl����v�^YGm���|[v�E��Pe��
�E���!�������@gcQ'W���t���z�����u%<���s��}�
y�����%����;���J�+��zWi��g^�1w���]�&�Sn ��:E]���Em�n68Og^N��Q,�� ��1>��0C��re��s���$�u�(vn
';����0!<��-�SI�<�2NmB��1������$F�$��Avd2\��x��$H��d�o��pX�.���U�����q�l"��Y��v��q\t�\�i�-Y^[=l?D�x���8S~�����k��:�%�>��=j��nzR����0�$�G8 1�-;	z^_��l�Yhf���#������3A�t_LB�m|��O���{h�G�����5�����\S�Z?2!�Fk����.�c�����z���t��uG��mk�D��w	�����N����v.z�'��q���s�������E�HW��Yv�r�����r���&�B�������R��A	Z�m$$���#V��"3.	���"���3j	1�Yr�r%y��Y .cG9���sH�^�TsR�eT�������XE���vu}�p��B�j*m�
!�Wx�!��E�:yj������e��B���9�/�����T8�8hN�S���)������ ����6y^�!	�KQ�j&y�@*�e0q�Z	E�j�U]�)��Nt��RU��&
O�@��;3�Y"�< )�/�:�,�����g�l��F���D�����!��
�.���]t�*��70��n�E���8�r��W������NZ�R�&��=�B��c*�1G+�QV�$�����|XS�|��W
�d�e����0���E(��5R������lR�������W��o�>�������b����������!Q������t�2��{\b3�X���g`��J,�*9�DUca�G
�uo�Ei�p��)G��D7������EaR-��SA`����0H?G�@v�?g�K�vt4
L�P�P�����Q�;'q����LK��^��_ZJ�8���$�w"�,6��K��&��2����2{.����8����<_^S�����"���HPv:��xJRskC%�"w�UoF����	W��<Z�cOg����\!���\���������K���O��nH<���s3�V96���6�[�#6��UZ��?����G�6���A�L�XO�w@�����jv����Y��i��D��up��8U�*<&t�7���j�!��	�bdk���F^�A�@���C�?�0���O�
��R�]��6�T5|*�������p�����p��K�#�!)�W�������r�y��R�t���\O
���b�~AB-������'o�?�(�x���IeK�����=�����u.:g=��c�-O�,�5��N
|:��e�r�r�V��a+=\�.;b>���E����8��?b�������Ur�J�^�8yy���1���\+�|��%�1�)
Ef.�����EH��)e� +��MJi>�g��gH1��:/;C������|p����y���~)���S�kro��5!U����a���UZ��yMjb&��n�&�T�ce�C��
�OoY��J�����8���u�8���t�Di*��>k};|��6���>�������!-�]�MU�/1\~�.c��'e��!�
YVc;'�3�({UIpKi��|T��Xq���6�b0�T������pCJ���l��t����h>D>q�+u�m9����7�jD��@$�%�C�4���
C�����uN�tZb����Z���l���>��g�>X��l��p�d�
t{��OFL$���� }�@@�=v���d�B�L^GQL�ED2/��:%w)�Q��
��e:�!����6��NgE	�Qfl�
��9A�,����;"�*�����C���r�ee��l��s7����K�K�J:m�#
|8mS�X/a���rZoac�������<�Ca�nX��T����!]I�������Up�SqP�n�$���^����?���#���\-�
%���
]������E�x�:����.�)���x��e�����W�([g���LI�������w<����Q�bK+���!��*_���#/h�c7,�D�:���Z�7�S��;S*F�(�2OS��{m�P��-�Q��s���i�^}z�B�U����.�H��z�@����V+0�&'V�q*i_���U}�6��&"
}�M���w����:D���Y4l��2��3�X������X`HT�����~�;w��n�*�(���u���;�:�%�e�Rb�Y�\�C����O&3�Ux�u����|X���w�ZW�-A��_(��m20Q�x���6p����B����RY�4T\�U��W��kn42���J&���K��!���.	Z��6��$uEn�y]���M�^���N��]X��+mV�T���"�)	�s�l���&�i��i%��P+d�J�Sp�	���X/&��<����y�>�Q\Z]�������P6��qd�����3������i��pj���gQW��QW��������E<�E���o�'�,��TI�VuU��W������'���L�a]�K�"�j �m�kg2Q��[�q`��Z��6�1�����{3V�J\�����I��o����(�)7��\�F���8������A� 5.�����)���7|BSv��.�\{�c�_���7V�>�o�iW������J�����Z��D���$A��!�!����:I��Tv��wAe��"�s���Uj��D����~%%���E~�>�L��i����b�?����4�#qQ	���Ia��k�mn��u�\z-L0�j����D�])$��cx�dn���K8Z�%�����Y���EZ���l��r���d�{���!�HrI�@��kJ�����w���ne.�y����Z~nB�f���_������{��_��5~��*�!������Gi�$�{�:}�{�����{r��?{w��_�p�A">e��{����������9|���HR!�GYNe�_�V���
�&<�hk�gx1�����K�l:���Ks��#xG���#�#q��aS��@�n{����:���9x������:�T�5�l~���$cOj2�����=�T�(�88��K��S��I �o�����<n�`'�.�`M�o;�+�u���������0L����Su��KHb+%��.�Z�E��,�i�����_�M��C��������Q�R*	��k�����L[%Q��4a+�?�~,��$�@)K]�����jntj
�3���%�?�I��v��'^/������������u���io`�����k���0��!��Ie�5=2g�m�,�r Z�?���qR<AwS����D\��"m����D���^#%zW:���M���6��~�o�����X`�2��
��L~#x���[�%e�`������P�w ��
 �@���|� ��p2�~WI5a�X_���3~F�\*�����u��d6�m
�������������W$0�tY������DJ��^h:;^2���� �`������Q0�sD3�<������j������&U1I���J��E����7����QqW����E@E�	�-���]A8j��~�*I�-�s����T��������d!�����������Z���D	l��zqg����	�:����E�����b�BO��D��"��k���Z�I��%*d���@�+ke�u���sJ����_Jv����=i:#���������p�;�f�r8�������X���%D�����w���������M��IM�iX���������x��pA��l05���f���b�D�q�/���5��
7����^����g�1�i8�XK_x������BzmRY?�3e��G�Q�E�UO�2Y?c�O?��K�;�H)�q�`��_���]����	1�9���-iy<s�G �B�jI�)eF,�N�����;�GUa���L��B3t�Ja������wS���M�Q?a
�TrP��k"pCD����Y�o6�mX��pBmf�<CE�b�w��"��'i1���-Z�*�L�s:����J�v(N��7���-��H��;W.�w5���E�������B��)j�`��:
���;�
<KV�����.��0u0yvsY��eI��	[ �7DQ��w�A�I�&���&w�hH: c����H��c����]({�xU���M��nh�I�����S�{�.����*�\�
jR�/~�}�4��\A�lm�0��G.���>��;�'jz�aRS���?����"�CGc��/�9��-:7k��fU���M9��6O9mZ�{�qD���pS��"5�
���q^J�W��+�u�Wav!�g���m�o"|@�}�#uxvr�����[�'&��8"��D�%�mI��4�%�#���R^��E�r6�'�F�x�kL2ao�$���i�E��"�p�%�1��
�^����7��P/eIj<0��O1�^�5k��Y����`��E���?��;��f�^��
Z�	�[6fM)�Glx�W��luG�����u~����8m%����V�qmG�����������fx���h�`)���N0��qI��1��u���eJ�k.w��Nj�F+)?�M�L+M<0�3@M���/}���5#��6-hmz�;��R�O�R��O�)x�=[��|3���/��%��-�������&C&��d�Dr�s��2��
��87����	��:7vD�1{�d;7E8���Q����F�+��)�>�c����[�$���%L�J��)#6�s��I�����[~����f��z�@��
��b�=^�yA!x�g
k��l?�<y.%q+=�f����0�.��-$��{1�Y���FlZ����I$�
���&�.�m��HJZ�[��>��X�l��b�|��f%���������a�<A�$��G�_M��%z~��p`6%1���)�1�(�&�:���l�����5R��KSA]��<�<>�0gD��"���#�����W�;�Tk��b�b|7�|�[���v����I�Xj�8 q�
�*����d@V����wS;�6y��S�9��#�UfS���f���O?Z��������$�)=hk�wkuW�d�bP��G]����:�����*Y�yt��\��=&�	aJwU��������J�D����Q��(_��.U�#0I���:���F��<�������S��{Z}�aR1D	_�"lh��B���S��-��l���HFi���s�8����>�����g�_}���D�K)�\��L5.Wj��u��9�9�z)�������j���������Eb��>���Uk�	�P�R��:@��v}/��2���Zx�UG��WoJ��^R���e�i-|l�Q-����������U�C'�<~	�[�Js���9�g�U���M��?rgs(�� !�����z1SJ�G�K�*r!`IR�R3����z0����g�
�6gj�w�f��[j��x.5�,���YO&F(��-��N�K���Z�������9�W?o1S�I��/h�lv�:�5��t�����<g@��zu���B&���0�����s��F���c6��`�]����$��,�V�0o^���T2=L�8���=LeC��������z����g����������rpb�����B��&Q+��"���H�u>_t�k�����H�HF���P����kF����W�8��wL�4<�6$��Hq ��N����b�$�C$b�CSL�3P�O�%��E�]�;+��ylS.�����p�Fk��h ���q�j(�${�s.����	|�����LF����cm�h��.�e���&�}�F�a�4���"�@�����t�����T�hm���nGh#H1.�K��?����X���{m�4�v6������HfS����
�4���I�"g��],����_��_�*�q�
���1�e��S���@��n����v��n?��{�'��^_mmn���,���/����������?������Z����?�yY�K��3��+|_"��q�R�
�;��i����b��BJo�w'��il$)
o)���Dcl�+|������kTo����Q'_jY-u��8M��}��O��~���C�O��3�)�F�]*�N���A���:I�RgW�<�
����qq������ 5���\~�����X_����%�hj&������S��1�������U��)�����6�������l��B�w>t.T+���}��8�K���|�f}�>c�Y��m2���r���?'��m[�W7������4�<����t;��	���j�P���=�������H���u��F�Dj��E�v3�����6��N�G�K�%i��}��BN �b6�xt��O�3��a��90�N0�\Av*���6-{;z������z6��=ZjG���vt����~����H����@v'��/���C5�a���v�?���<�ko�%@�,%�8������b�l��X��U*�Q�AV��F��8J�lG�\�XT��5Z�^���*��3��x.��(�`#�y6���U\=g���'V:%W�R��-I�d
���`��m�
%)S��O��-j����bb=��X��Wqq�Vb)O~c�57fjLEl�����.��	��S�A�tRl�������A����@b��]�LEpu�Syd��C�1��G�I����,��=
�n'F�<�	_e��t�n���'@v���q>�!k"1VT�D�����:���Y�������]�0S�n�;�������3�����5�J�&8��o�0�44�u��d�a�%���|Fi�����"���7�+9tu2�����RH�R6���%Ht9�����!��E?����&R3���:��8���%�:]�B�tg�����������V3���J��Z	�|F����dZ�{b������i����l��u���u�����_:���O'����;�������.�Z�v3~�+�)y��C��,.��KjURnx���������']��=Q��#�}8��_��+�z�9C������~���������o��bC�>�������?�����t�-����$��=���8^u�j`p�y���s���u>t�;����������V����(��1x�������
�@����E��1z���YU���L��`�������F�������f��8P946%Z�duk�/<��nvP�2�(��)�����'�a����l�g�/a�4�O�c�4����)<f�����0{��.A�br"���[�����������#��9U�X�"$�g_�x+v���[�����?i3��pr���k�~+T
I�)�olS�(���/��(*�8����_r;o,�f�A���ej�&�i�k%%������4/�q��m�n�����13���o�����7mo#9�E�W�W�y�[@���d�/EB���,��>x
@����Q�(����o��YYv��3����HVe��UXeZ���vn�������4q2������Fy��HPY����-h�Jd#�����g�'�z_��\����T}^��^D�T���������w���i{zmM�}���a����C��
�:<"����R��^��&��.����''wv�v�U�{�<���s����*����_c���C���+�cG��'���oI7d�@Q�rh�!�n��@�_x�d��!����;~�>�	�{�����->Hr����+��]!�s)t�2���ZT�m�+��/����K�+�����}��:�oo:gr������U��-�t2o��.[��~��<N�?�l@�=����������V�P�T�h�T<�uh�~��D~Ysn�x�������T������Q�sq�����4G%��np�Cs�^���,�.����DYI&�����B�%p=e �v08���Yk���q����z6"xq-4��k1�Q*
��~;�T�5w�|U�}��Q��/l����`f����
*���15~�fEM�[21�V���w�L+�����9
�9�H�8}���vi9#1/���6����;
�A�;P;�f���������!]�������A�r��0v���N��Y���\��T�~��
�v��C�Dh������6��L���_j�N����������~}���V��s|y�9�:�I��E1jz�������&K��:���Zx�!#�0��D��<�0'��Y_|@E���T��Bf�����3Vx�qqo��z�
,+Ic�s{g����� D������(@sw��%��OLc�9)MI~���9-��B�n ��}O�^�rbO��Rc
2�A	]�9���LQb����gHQ���a�����BF��?�.�d���g�U�4�����R����������6���-w�[���-G,Rl���0�����A�y%T�k�Q$u~��(�H���Z5AV�hN�mm�GG���9�	4��c����|��	���H���M@\;'t�O�g����$��+���~�9�4�����%��3���|�'��W��K��~����C�?*���p7��>�����!dF����rC���
��f.�p����/��#:�637O;u�F���qNB��\���s<�Y��9��� @x�����p�Ckk���njI#�E�~D�z����|-B�T�[T��e����O�8�ee��1]"�[�������I��l���.m�E]�(��'Y��BpGj�6,DO-�����\V[z�uAS��]@�{Z��\a��<<����i37����/%YeDf3���v��������#Kr �N}���s�^������X����K���z���9c�sg�/�x�/O�[�������`��4XR��//�n�t�n�Yr9����y�5�����U�����m��������������������of�_qr�
�L�����G��?p��E2�3�����`�es�&Z��(l���&��X����"���������[J�l2sI�#V"��H6��I��d�?6&�������VJ���$[�AfS���eXH� ���C	c��Sq>2&�Mk���0m�$5�+4��lYi}R8�'����y:h�����_u8��K��A����5����6��^�}]�8�����	�������"?�����������!�����|�����$�0/����^��T�#P�����>�������Z��;�;�@�*n������������o�����o+�M��~n��;<�u���	�D�!�p
m$���(�~��xf�g��.�
�������������Z��PJ^���r������'��ji[��gr��l��� ��8�=�i���<����:U�3Y!;p�m8c?`���7%^#�������c�u`rs����N�5���b�j�vp���CW_�w�<�p��g�]$��!��=��C���)��q���2�D��=�I]<��qTs�s���i�@�!�a��y����6��`�{�u=b�O��������x��1�
�&N��8����9�Gu2��sWW%�����}o��}�{���G��+�2���@f�y���:'�b��Y�p�S�p|<������>����.���f�4����A*M�������L�nC(A��2�r��"�	z�~��s�wp��[Y����T���eo�vy���m�p[0�Ri�	����4��\����e�iq8
�$�1���f��)I.-8 �L4�\Da~a�$�wtG@�����%VC��_�H
���&�m��l��]ybu
�,]��)?]!�=#6p�(�:oD�BYO�hs�M
��W9%��_!�X�����!���J�X�YA�q�(�\yH��W�&�&��~���<&)U����6k��9 �?5"��h����#_i��E���p��WS���� �J�x����H1��ud]X����2W'�i�>5
5��(po������N�uq����I���w�����S"s8�dg ����n�����mg��#2'�L�:�,�����ys���)oO8���_y��Bo������������Y��g�Wm��j9�w�����H���q�
��m���3gk�����p��	��s:���K�^������;�����������c2���xL��Xl��@w�������n��=`H��` l�c����
�n>��y
���Ee��	��M}�[�j8[sz�H�e5��wc�*�[WI_��a���5Eugz&�fs�Uz�S���y�,��I� ��
��PA^<��X�Q��"�?�q�Jqg��7���-�N�r�W0�����n������A������;����
Ft���
�bA�5z�oIh��;#i�$�f�������}JX�
��V��#����0J���L��a����/�(���)�!����`�O��t�a����Q��������j|.�Ip���u�*�v�����n��x��ULS�n����}�>��Y���Ct��M�j
E�=}������(+����#�5]�u�85�����?��Y����<$������
e�9U�\${
�?<�������������|S��q����q(V�;(���t�|bkuQRH_W�?`�N��:�������y����uyZ�]�#�G0����O���a�����.��X���(�V�����[�>�2��1�FJ�V��zc�bF����[�l��:;���@5�w�8���R+�������D�q}�e��VlQs�tww�$�uww�F�I�T��&�%��Dd����������7ZQ�Gw����oP�G�O���u��&���k��k�����u��
��W�owo����`���[<'�2&{�p�{�����`/X\[\K�
����)��J���d�,�x�nn�'J����s����9������rWL��;>K!�^����b��G|����=��3�K����A5��H�h\}Zq��E.�N	������z���o�W'^�������W��<i����2��yC�W���OKd�������Y8����&�������Y�<���QrGi�.�PQ�������
BF��cV_����{-y;�}�k���>���(`���kM	I 2��G?����� _��������>�l�"��S:7��v^��Q����[@�Hn	~����[D�8E�)"OR��$��O�"���)d�9��
�)�����u���l6�g��S0![���f��R	�l�J��	�p��W�st�1���������H�~ns�K��U�b�&�h!{S^JC�[AI�����$��?�
�q�[c5!����U�S�1��c��-.nu���nZ�^d�����V��\q�x+)G��fC��x���1�':������V\�u���/E���qT��I����$���xv�,�O�^8`����h�	_V���&���w����[^���IJ�w��v��G��m��^������>�������wf�y��2��m�v�I�"@%G�5�(z�����J���`��>��?��
�-�F���j6F���������iG��;��,�7
que����]�$2�E����Wl��c�u�)�A	R1����������<�d ���!���!&`Gb�Z��u��G�A���a��}���4���v�!�$�<N��]�U�������v������-�Sh��	���4��5�b��1��.�����%v�� t����(���6�Q�(��"��C��_�oR���"�P�q�b���aS&
�%/V��6���(�/:��X��|(�#�C� I���.��b�f�N����I��"�F
����d��/�n<���aU����q�+q0�|_�����I���o��*=D)z[�'��{�+��ty�<=o�|��X���K�mP1mW�e�S:e�<)���wI���F��4aP�����F�_
�I����
It{?��c0lk���e��;7����0|:q���s�r�eg�t�&0O���'~5'�k����:MQ6���A?V�5;�����(�)�jO\��e�:�	�x��.5����Ip�U����K����"0fn3D&h���L� ���sYD��(�O0�0e�?c{��F��������[����E8��;���j�P�tc`��q����Mg��>9�S�6�m2e��G0	=�Lc��������|�3���Vn�Ij��q�%X��Ra�k���W'7���i�����[g����L������������gF�1����[S���G0�*�4X��L4����i���|�*Z�����YV"��R-����Q��i�f����|(+l N'��=�a�\8�k����r(�!��&,�r�<���)���4���d���h����Y��\��B������]f�c��x�6�Z
gJf���zY.�qQg�w�5�M�s����Q�D� �9�N���d�g�|f�n�����Z��������!��tG=�S�]��fMO��h0H�H�{i�����Yej��6���������������1,��Z�����{��@�Q
-@���;U�`����w^������&�5F��}��\�2Xj���0H�*��5O�����SZ��^���Rs^o���a�+�s�����	s�:��$�N��3������n���Q�y��h���wz|{�9�j��_��Nshy�v��r�wu�X<MS��E=��dnm�N5���e|�y
��
�
K-dY���o?���U������7��t���	��{��T�x��q�����p�5a{rM�JL
��m���~��,%�����n~�\�6��Iy��}�������	�2����&vb�?'o�������X��/Xt���
S�T]h��j��nJ��-�)�K�kK���O��Jy\*�h���i���Q�O�;�����Y�'��p.�%�Tv>s�� �H�qRW�_�g�^�]���V8���LJ��L��m��b����k�,�TW�1�Rew��v��X�h�6K�������"�J�q��!"��I�sw+��).pDk��04X�l�8)0����Y*C��C@�H0\��p0C<�wH�LG�F�R?����^Y.Z��1��_#���G�����V�X+�7��&f�������"����
��$����LW<��K9�9C�h8h�")��:��m���m����q�g�R8������q$T~�t�t��#���+���|�Y��eCxNm�=�.~�Y��<{F+�JAG��6�y�����rp`o�@#S1g����n;�sc2��o��+�pA��<�zyU��s,b�)]�y�P>��6��U|
��s�s�[?E+�*,��XT���fr�1�4��&��%!%�p���YHI����6-����f:���I���1��=c�\0���r��\S�7l�%��%�LY����	l�e�$5M0(���C9��[
L���>j4��}����BVr01M9$N��
�Lo3��4���d;-���IH])n��
^g>�B�aZ����������KT�ke+�M��Ik�,~��{����R
|�P���Y��W�Of����=�l3[lhIj�9�*�F�����J�����j
T���0�.�>�<���N�y�z��E`�Qw{�Qb2$�J�o�Tb(M��#��'�,N�G��$��5��LY���	�pK��Q���^U�G �u2�Y���^�8[���VL�4_&.ENi��R2�4��<�|knB&;5X��D\�T6?��>J�74N��N��7�����;�04way�U�it
�f#9���.$I�������&&:���
e��	�>�m�;Ho���e<�� �cq:}�������x����X�������G8/�����P���I����+p�
f�D�����:�&{���
v��y�]�m�����geH;i]��H�OsnH�����2�e������G3��|/x��=7M�c�e�!�N�l��������	B��m�I�# !p�yG�cb
@�7�P#LJI�GIX���[Ie�SB`�0K�����*3�1foy�4P-�|����wM$�{���[	��SM�M&�,�*���H2������7������&�z�?h���!%1�{KH��U�m�6��$��*���`sH%�m8.�6N�'s�K:+E$gw�����p����d��uC?�L��\B��,�hdX2�A#e���J#�Qw��s[.��2���������yGBR���P�R"�$�6�����NUL��r�oGHr��
���p���IM���������\��aE��jVJ�j=4���"���?'%���`{o��sT�o���D��:xZ;�8���#{K#{C�8�gY�����q�U�8L��G�F#���������q?�����ZRJ�n���dZ����G���������w��������K$'�����D�3���_r�\X�D
v�{�z����h�)���<M�c����4�3�����$���6��>G���K$�������_S�	�M�������NS7�-�X��N�
GgL�e�?��w�<S��6�,����?��w����������l��"?*���!�V-@��g��G_��M��
|'�D��)2�#3[�[�kx���sTv����E���Zs>S
{�~��B*��U661a��}f�����p�N�C4��A����D8���	�c�rM��j��������s��������i�.�
����K��xLg>�����lgE���M0�37�J\50��������`���{���s~v�,���o�_<�����n[�k���g;�O5��o'W���/����~�<���A���. ��F���}nR�n����F]2!��	����0���iSB�dN�������;6a���+�M2�Q���13�~W$;:�F7�N0h5h3�[/���:����`vl��������-{J~�����H���pf�'�c0&<��^�������{�����M4��i�+��o����j
h�H�����&=������dw�S��nA)��w��z�z���0j/��z�K��$s�}�`�7�q>�V����z9���[�z���9�������j_��L������B w8�e2�)�k&E03�����
�zS~`�O��'��$���Z�t����y �k���_�O��gi��T����>����uq���v.���yD�	�{��)���-{�NMv�b����Gg���:S�r��$�t�/}
/6wh��\FEd�(\)cHN�v�F}�ll�l���H�;i2YQ����5�0�W����S���H�EEf��=�?��������T�2w��?Hn����7V�%nt�4:�=9I��I��+�3����H���En�����5��1!��7��w�����Vd�O3J~��A���u�$�\�l�R���f�Y:���'�a���sM��o�J�5��}M�a3>����^h��t���w�"�_���W\�c�.~8�l��4P[Un:g7�gm��:}5b��_~�����'�q&���=�=�C����P\�Hy����]�{�Q�]�����pPR��Z8k���+'?/bD�7�8�J��t��s��j�?!�A\X���;�t1n�n�������-�8�����X7�x>)�<%�:
s�����.�Z���5]a�������7����Xg����k��x����O�z���~����V2���������gm�pf2U���Z���y������#����6g���+���C�!_�6��0��'���a��kJ}12!�=L.��y��.���X���}�����b�<u5�m���L/�<&��M&g��D=��bc�f�����O�W��V@�qE���'
~ ^������`:�1;�Tg��:��2y��D��ot�:f,)D���D���A������������0�D�$iK�V������k�� sB��&��&3�XzgbM��9{��e��|O�8d���(�|<�GW|����A����t���3�+`��w��Nk�H/o���K&=��i2!^����A����E��g�k������%/���i�2���n���C��*��Q�e_�����@�Lp<�`k*YR�P/����U��g�����n�oxg�����s��9k����*�xf*$�����t���Oms��mA[��rs�>�W��?��g�,��&\a�<-�%vL�hM/s)��F�7/���W�YJ<��:'���-���$>L>_���ES]��{Q5.�2V 0����I��t!5F�ZM��0M���0�HeIM��Jz<D��56�
�l���!Ze����[���)�}�L��6��_�J��U�r���������Cy s��sX��v$;����>�VQ7+/VV*��'wS���.��9��=�9�������v�\
��>'��J�A7�K�4\JO1(�[�###��B���	�����^�P��:R�"���u�M��A�����u����qp��~�\������E�0��������M���!�*���f��4j8�O�nsr}����������uzz~����������o�G�/����U~����� 'Un����O��1�V���z�����63y[��� ��M��;��^F��p�b"���@�8�9�����n��/��-���M��z�� ��	:$w������v���������g��-��:W�B���3�0�iU!���\qrO��K��+�;�&"j��D1�����������/��L9�e8���q�������L�?�U|gsw����$�����1/��a�w�4
c��X�l��2;Hu]��bL���Zk������kq�8��&��5�x��w��+_�"e�n��HI}�����ip��v�%E���{A��K��P-�0}�tg���L{o-�=�0zS6)�b�'%��M��-���������O$n�"�t��&�t� ����3�E��%�_Ldb$F���.s�������j���+�#��"bW�u��������k�9
% �����1��l��j�5|e�!��������)��#l����$n���~���	���.+��G�R�z�������g_D�&"d����
�����m���Y
��R�rW68>NC��iJ��^Id>�8��W����]����q�:o������<{���`�O}]��)S�p�N��Z��Yh�i����[��8��'��*��*�l��Eu����*7B��������8�CQ`����C�����7T
6��t-��l�Z$t����a�Oy/�� �1'��3���S���F�^�]�7l�����Tr`0�)��E����,{��]~���c�������+��	Fn���;���F��������UR.$�����U�{*��oV��oh�/��C���] <z�:�-�'W���E�E�L�l:E�L�XE�S���
{4��PjP4��[^��w�hM?���7��}�v�k;��6okk�v�Gwq��(�Mw���L���hB�����M��R�HE�,�
?3<��a�x$?��v�����^lT�����l���e��a��jk��6�y�^<3al��&&�>T�djB�U�RP��]��{'� �0 9���q������]�:��ds��*rfU��S'T�t%�	k�Nn|���w����C�g��w�vH��Ib��N�^�ge����J�"��w����k�T�_<���� ��)�K�-Y���b>��g�V"��{���}�����J%���@�����(>���M���+���q27n^���t[L�D�S�����Juz��0 ����~���~��/t� ���#�����,;#X�Qr8�;9��o6�����Nsw��/=����@�����`�hl'B0�D�������,��Qr"��k�{���yaiy�HH�F��p&Z�d��-d��_%R���w����:��%tK�B�Gx��}�Ynk.��t9S��.���?K��l��J�4�q��L@�m���N,���eM�^"��`&��8.[�����:	�w�*f�K�4��K��^'{��l�m�����Q�W�����U&�^��H�u�Hc5[���Yg0�|X;���gx_�������q3��@���r�.zr/e�e�3>�k�>��������8 ywq,Q��b�x:e�=����Q��c)���@#5J_�D�
&OD��O�>p���2
����4����c��B??���%��l�f	U=����T���n�������vy������;G;NZ�5-`	7�$WU�;7�3��J�xR����N�E9:�,��e���8Q��Er��E+��`�H6g{?S������" EGES���'@B�0c'XD���&���
O���U[�0�]����
�b�p�A��5������-�-�|7p7
����!cG�)Td���<��K$6?6|\k��6������%�;#�X�#.�u����H��P�Q� ����|&����b�h�^����&TP�H��!��}E����'H"�^s���+]d&�����{���npb�'Z+m�c[1�����tYnK��YS�u�c0��g�Z���jt�N%f�U���
���������o����'E�D.�h:�O4�\/B��
�����%o�,������Aofi��o�b���T�a!.bP��jd�*������a�S��j��������N&�B�\�=c�������'U��@�}�p������[��m�\��PE	18
=�f�����\pX9h>����g�7Y��U�I������Jf�n��08U�e�R,a���)n�E�B�-�
RH��T{J��BOi��"��%��BB��q	���������D��o0_��'����9TmG^z��aJ����Z�z�9V�v��c�S��2-!G������6����������D��QE#c�?Uv`mi5�����{�eW-P���t)�|Z0:�3(��l�&���A�&��o>"��:~��$�g7����`K�Wo9^��M�����O-L��~�;n#	���)�ED=���yk���-M��:}j���h1�,�gfU��s�,X$��&8Jb$J~%v��tI�#�$>g�`�K�|t�O�HTH��c�d+��{�gp[��O���ha*����#�]D>��>ja�������	1Rx�:�]v=r���FCX�!Jj4S����R,�j&RG�M���s��# ��Z��O���9���mM7�� 9������*����\�M�eu�!���kSI���/=,��
�Ag0��%9��t�j__�.o
5��$z~[B���mL�����^�G~��{��-���:;�e��@��i�g�V�mM��a�^�:�= �W%�
g�5�(~zI���&���ToP��h���e�A�<Uow��0��q'����B,^F�e��1�{d���i���A�a�L�aL)�w�S�F���e�f�������9����^P�{*on"b�%���!~e��NA�s����$��~�����������m�W�
�����j��5B�{�#"}�{���D4aWm�OE����������Pj�6�a|�*5o�d��s�T���������`
73&�ijn�F����2C�5����o�|�rk����j��2C���|�t�i�0��?��	c�2�c���v0�����=I	L;Y8��9�,HL��N]�����}>������te�i�L�GA��Xy��c{�YE�%G���$+����c�"�#'�;
,�����o��*�_'���������Mi�x���p���YYs5+g�K����~����;O�����n����n$�~�uC��eM��A�b��� ��DB�&��~�����5���|�5�p����1��-���I�
�YV~{��wq�j��Y#����(��~���;�LV��������1,�*�]��w�AaL�j!+n�Q*O�BQ�F�F56P�yE�	VI���)~��*��]v��)�����7��)~��*7�4���3�����m�D!�oF��'Z�@��>c*[�� ��TI��P��@����L��D�'u$>8�H��^
F��3���7�Ae!�B
���Y��
�eC�8���-�H��l��l>�����8���c�&����SI�&
a6!�EA�:��OP�����*#�����!�T��Rp�r�#](!���'�;c����u���b-46 Q����%��u|�����'v���VV(�Z8��C1����bw�7�+�P�AY2�Kd��E`Igb�9�����'�J6{��vu!��x�`��p�����6p�j��a��%M�+���
�c�&erL�������{�>i���5=���v���sN;v��Ola�-W�q���������L�e��`<�]�J:r��9v��1�����3��^�T\>'ne���j��K���|>��K��"J�0k�(�Gd�lk);[�s�����^�5nuP�fT#zPq�q�e�����KD^���RVU��1����)����csqa���y�I�hA�E
! ��=�����4����E���
M+R�Nt�
�j������c���zU�O%������i���Xi�����5U�#z�
�d&x>�~��U���"K]��t.YW����@s�]�{��"#Nev���Du�ZCJ�������@l3Uw��V�"5���2;v�i������pw^j��Z��F�D7��2�l���6J��
����G��"���ttG�q�����W������a���������~��bA
9w��2��N��eh�����W_N���Gxrg>+����W�/��
���^+���?�0��sg�0�Y'���&�������������Y�S^C�0�e�5��=3j�������x���/:��.���������
��F��ufqG�~��������[c�u}~�>{K[��l�a(*�+��S�@�s�V�<���!�����`���VS
�m]��g'���`L��u'�iC�/i��U��y;��O��������_����������]�Q3'�(��v����`�7�Va������c��2@2I+��EM,#h�]Li���3����X����������TPJ�I	���s(Q8L[>b]�������,p��|s�h�6n'��/�
����wo��3�NZ\����]�|��4��l���n��G��D,���C~{fPr�zb�
c�k��}8�Id�X�&$�%��@�UB��'�����d����F^��S�������G&!��|iS�ig��^D�{�7F���~?�;4�>"����Z���������G��o��a�{jy��=53�a��r�8���_��iW������7���Y���GtgA�m�A�S��zuz���h&�%��#>�����K������a�yf_$�Y�m���<�������rO���g����:�o}���+z����<����;�bx3
L1�a����p�{�@����d�M����d��[N�ew'��y�XE�()k�����jb����k*��i��������|���[C���p�x?��5{����'��C�vk!
h�o���!�	��9���#�;������u.���u>FK�}MRM4~>
���K2�t�@9k(�������m��������:�4����������Am�3M�z&-
b�nLz`��%Z������,l�B��}��f�q��������-��{���2���H�B3�y�/���N�L���kj��CX���D:�$XuE�����@����b
�F���n�<��U���I���W����Q�#M"�N�~+\��}D���>���^8��G4�c�z�v����+��u9��T�39:�YL�a����V���������
�l`�G��m(���;�����Ci�dz��A�61*�i��&�` NW��Wt�>��L�����h����[I��������0S�
���b�m���u�{�5�Cx�������q��4b;I����<v�T����~a`2�U�M���gs�3{�y=h�3mq-�<GS3���I�l�����tR�����*�6�v��^7��NZ�L���L���+��N��������Z6!��&�@v���';��wpE�b�e��9�'�2���=
o!>�8��7�H`�C�$���������B7�yeY����=m�)�|�[�����,�������4ah?~��0�'"I������,nz)im3�U������e}2�����~��~�,g�B�X+�N��������q���h3S���d"���{&s�J;C�}�t�6N�'o�6�o�~�B`���U�����T�wN�+;��9�����5�G����,i""&r{��s���o���2��'4d{�o�����KCo�h�o��K�a�	0X����X���vKc�\��u�]"8�0e�����]R!�x��Q�&�JE@����?���:��C��36�j�#��dg������_?��kAOd�^xg�ij�	G��'�2��k������*��>r-T�<�l<j,J������kHO(Z/�O��}��f5W�����ST�/�e�dbk���$kHM���fSv����*������
��E�.<�4f@[b; Q!�h�X.Q��;�����V#�lV�Es��
\�W4�M�?�Y��}��4��!&�sY=� �y>�:���
H�K��%��������ds�N(0&�bt�fLM��r-�8v��f�{�����@�Qen�=!���c���������h�uZ������������O%o?��qWv���q��vT���u�eE�d�����H��n��U�K�9$�B��<f5��]��O]�P"���,���H�'�<�|O2B�����]�U���6aT��{��5>�S
y��[%�r���)Q1q�vm�v��FB�b�~� k���+8�s�|%�%A��c�A����d���"Cz����E�C?���[k�gn�Bg���,C0��N������h\�=�D������BVP�V�4_M��+/���E��$+@(Kg����B��.���6,a����.G)��X��$U�U-^Q�w����8>������������$i�9`.��
�T}%>c�c�9M+�y���Q6�f�l�6����kX ������|pq��+�Tp�,"�X������B���}J<�����&��N���5�n�*=��h�~��p������5�z��v�8o)�7����xx���j��Q���J�S��Wy�f�Y+5�~�W���������0B�Lfh1���1Gs���yY0��(5��42C�'�,��1F�2YB�~X0>�q��=?K!D[��t��L��9�����)E�nj���y��_1������Ic���6���.�u4�%v�� ������b�1�p9�V�H�eE)�/���h-���G��5TT�����UZ$�p�$�-a���nM�n�"�L}Y��T����x�~f�?0�g�U*_3����]�w#G�������:����*;�6�!�H����	�H�����p>���L�
k��U��p����
��*��1{����o@pQ�*:fUc$#���>
���_z}m���.S&J701���}��-��Z��{3S-�cx�����*�-g?������K�S�$����� ��,b&�*�c�lA5��e3�cqe���:5c�%V�@�-C�<�����v��3F����KT�g��Y�v��pE�]�B.�z��I��>��n����R
�����}��r���
�����?U���l�f�����!6����f��1m��l����<��A2~����I��g�-(v��l��4�c�P ���-�o#7��z��)�L���k���OnSC�����'�1\^U��*�h'RmQ�LP��x���1])F�K7i����e{��w
��N�����qEq���7_�(��B�:HB���-�xq�h���e�� ^3�?�EI�v�xo��4G�d������N�k%�b�{N�N��A�EM��s8��Q`�c�S�xn���������
;"2?J������9���1�0��|H����P����@�v&%�%W�����&�2R��5�{V@����]��2�89���\kM�h1�q���(u�B�9�z6}t 5%^[�[�_��]��N��V�A�P��/���?6NO�����/X��*"]���V���$���N�TE��j��	���1N��`#����=N�9��wFU{
f	Z�L���qT>���m0�|!v3er'�?�#N6���1l�U��\�n�;���������
,�&}>�X?�N8�7�K���	��n�R�abB�z���\���|-"���n')�r8�R�k����5)6���3t�?��������{���\Y6��a����������hNCyF��&a��?��a_��(�\����"�������e� j*�I^�4����s0�1r9�(\EaS��{Q	1�a��D@*p!��(+8���"�	2@s�i,c.,h��%7s�m���C�y�p_�d�EO���}��$B�N�uh\�l����l��Y���wr��U���5���2
�2=�ixw��?�F��b�mD\��'.���,��N0�z�,G�
�2���ZZVB��.hV.��/�_��K�6S�|�x�\�Sm��	���J�~��x�p+�����A�W�])�a�����$�����.��[���;r���c]���)��CG��t���@\��X�R������q�%
M�G4���0�KQ�d�I��#/�����3��d���8CY	��xfq,�F�VE]����e��qX���� 
 [�#��AE�z��A�}���Pr_}�G[?b/[OH ��X*T?0��A���J�������9c��>b�X��T��%��NF�� Rab�Y�,��r���u�����,��(���	O=� dTIn�c�������%y�����c����C��V- d@a���us��S����V����|���	�utPZS^8���q8"���F��$Cpw�����09���q�������]
�$~�3 �#SN��&#;�O��bA��G���K 7��
�f8cE�I	v7�:������FB������[����������t����9UL6[D�3��������5�S�X�*�7��?�?>���0���BU�V�oN��
�*+�z�:9?n�2���]�\g�a�}�P�z/+�~{z~~vq}��MM\G&����K,o�2����S�J����|�~*n��T���x	�
����|����%:q���(O�:$�06_����#R"}��`����</:����N���A�to�u� �@@o_��C�O*���ik�!�D����T*/��
4sh���e&'m�����R#D��k������������Tt���3{1a&>�� 2��k�6�%v���������&������)������9���,�3���?q�2M~.�g�o�I�+���^�IHua�-��x>S�7L�I$��e���nl��]8>cm���z�	'������VmB_��"=�#M���P�`�bODSd�C���^$������iT	���"�sL��5_�[�i���m��\^W���(�:l	\a�g����V�����&��x������k�|�2�SIYc�(!���g�,�O�j���zH������z�K�eU�����n-���u���&��tj�p�^��������2M���2�2nk��SH��r�������Q�
S�)�A,�w��
A7<��E����n�&t
O���y8���G��P����_|��/@�����G;������s�M@y9@{f����i���0t���&-�.��"��9s\�S�3��c�l��l�]��\r���y�M4���*�4��j��d�����3I�L!�-r�A��[SQ%��S�����OdJ��i�rJQ'��d�`N�[}���������6��v��q�"��,��6n4��.[9�Fv����,���������k. L����"9<l�����&����l�'��*���-@��X��#�iF��-rO��$�$��yHN6�hGs
�s:�>O�}�
J�K������O����]n��5��VH��y����i��\r���������H��qf�B����������vw�"FiEJMC6y���#��Tr�n"�"����4Z���D{�k�������oGa����i�U
��0
�I���o���eb�+���B���Jp
�����>����`6����!L��5���vQ ~<�z���*�Y��O g��
4��%��q���PZ��	�9� @�����t�����rLwN������������"���Q�QVD����NY��g�'�PKs��fn*Cy�"���a���h��n|�{��|�N��X��O"��g��t��Y���kzB�I�$�������_��
C�G��i��X�B�0�xY��=l�.��d����]9�<�:�&������Si7����p��<k����)s�A������&n�Kg���/)`�3���\�r�LhX��7:�����G�V�J��&�cR}��T^��+���a\vb:���~��������;;����cn���[H�N�������4�U������*~3��9��#��R��SD7���=�$RW�O��(���t9��$���T	��^����4��Y�L�Jd�t������WE=7�����]�Wh������)��G�b<L�0��#�|��dK�%/��|u�%����[�l^3��1��|�N$����~�m*��u����~o�O���Jn���!����I�z����f9HK�����Yf��0 �����n�~�t�����E�Jp{�����K�����J�8�����3|��jV8N�h�����`��J(2
�L�YM?��wxg��U�d���z_k���Or��s��r���vs�j���L�E���\��-���X ��}
y�P��cA�>��m�S�f�������pj���?�^��Rc�o�?P������z�5�����}M���a�ZP������U������F�������e�kn�
f�D{����?��g���<679����~��8j���
ys�	��55wq8�G���=2����������>|��~w|��p�	���En~�<YX�C��������dieaiga�������\�����7�J�����������������e��������s��R����������uIuN�k)�9Xt������kCs�����������~9��*��^�h�yX;�6���,�qu�r	����@�X���?h^��/:�@�9��Y��5�#(	�I���2��j_u�/O;�W'?����I���G���;o8\H8�����3��:=�{�p�P��������F6KI���;2�x��$*��S^��������k�,[U����.�/%.iC��
�?����������v��a��i���o����������L��@E�����wG1i�Bt�m���B�eJD.$3���G��5�A�Z]l��z���[��"!pi�7����c��e0�2c�zR���(\��Bi�~�
�m�����H�g�(B*z�����?�$N���1W��w����G��xvM�R�K8��G��������ts�*�\\
�7c�G��*�//�_j�K�&���P�cN�7�N�{9�������4���J�U�����t`,y�Lz�d��m0K?��1}��_t�X�wK,=!�����d�������z������n,?���S�_3��m2^9�8p��FH%W��G�����Qa���35�(�P<_�'E���^r5J����l�{�KP���KW�S�B~���a���@����@\������b;
�13��|���� ��h���Z��<D�$�/�s��OE�\�c|�����cK�� �P�ym���`��;��w�n�����zm�l��DV�M�H��hg��W:9��1��H��$��s����zp}�>>?o�w���I�N���x|r��2y���}C��
����|�7����k��j3H�h�>�]f���>}%�����?,_�x�X�B��n3������n��8X�x�2�<N���B���_�YL��4r��p�I0FV	PT\#o�k��e����
�t!������F#|L#��,4��Ec�"��L�����-��=�>�����?��1�������:8��u�knD,8d�}�/\;E�r��M���m�sv����3o5j����]��<��Q.�@�"?C���Er�~��s�����r�#�\N�R�����Cg_��w4h�������O
��p|��=�O#�j�k��y
�����}�\a���p���K�y�L��N4O��u�TmI)�PF����X�\oVfv���$)���/�5�K���;������_t���������C�g4>S��7�w����7@�d�\X?�q0�Vv���$QA��T�����D�9��^1K�(2=��NX�!hK���Y���R������4(-.y�r�sW������`3�j�$�1��3�����|��o��
���������W�e��x���d����,ws�'�����Ni��^`��J�^aJ������[�Bz���IV������M#��)�-n�}:�o�Y��m]��Fz�9�5E����U�Q�����=o$N1�q����
�M1�y����;�lb��w����;:�C���O���gjI?��l������"�)�bb0��
�����Ex0�N����������.lf���_���q������D�N���EF����<��J�����n����7*F�G���z�pw�y,������K�-{�4�C?�����O:L9�`:�r�]��6�?���%���g�\�g���������n�9�_c�Q\+�������N��>_�r��|����O��7������N��]�\=���8���{�r��^���z$q�<O2������9R?#���J)�ailG�~�NdO_��]VOB��JJ��f����}��K?���x7�����}E���V)��M���?�E����'��\.��}uQP�>�k�[^�RPWLk���F������q�}������K���Q�>�{�gE>�w�V��������=��T�:��C�zp,���:Y��&��Q7a���K8����R�@������2�R�������%$���d�{qb�)ZH��TK��!ig.�a�@c/k���X=�Kq�8�jR���I���v�F�T>���		�IH�����l���m��F����7�������������Bq��/Ao�E�"AoK��N@[��-��wSb,����>���b�����:��v�;��Xos��ln��������������c���p�����a8�4���-{/8���o���=�n�{4z�����mv}��y����zG��^���������q@�����vh9�Q��xD��O�$���}�������	&���7�z/��~J���w{?���{�>{P�y�����G���^s{�f�HS/�?_���������^=K���Q	��nQ	��[P�������a�����w�#�9���Bf�u���?�4��$���*�%I�k-�TS�D��T���\r��s4�=4S�*�;�`zx�'�����I=B��uP����ctP�RX��`���`�r^�����+��P�C_��4�>� ����L`(bE���M�7b�Ts�~�L^}�y�����8�-�b�**"���Ia
x��������
X���+��$�Ps4����,V��&�T����������g���,�����2NP������0����.�)�'�K�����T���*�s�D��@��L��y�������w
�����S�������(�����x2��I���e�fP���v�E��w�/~{�����x�!B��\��L��tt7��2P��5����v����dJF�?��x>I>���{��ChL�+i��[�4���v�J>�|?G�%6��Z+���V��:����=�g��.��������������'��w����Gv>���=<4�n��x�{;�f�(+I��8:���t<3U���N���c3$�':����qf��5����.��w�>�g������;����I�3�kv��[3��p����\c��~�gC���"������,���5��c0����Q0���X����l�E�M�(�mH��xP����T'31_�}v�mm��3�%��(���`���v�|&���5|V40/��
"gH���^�3�bZ�p����5-���N���.�]���|�[���Uas��1�UO���^�h	�;�bUS�����&���y���F�	�	�}e�J��~0��Vu�@7:��BO�6u�<���M.���~���O��6��f� uW,h�]�����f����[�AA��
U%j�
����<�����R4����a�2�1��w�����?����%i7�G�����3)�)���FR����/q�8}-�����y����8x@Y�y�"&n4�����s�j+��t>�l9(�]�����
m�Jp~V�FvV����d��t2eoC2c��y��$�9@d��?����ZR$["��}d�[���p�����[4�����:��q�I:����?��������bR�1_*?:a����]h8�������F����{�X�����@cL_�p#����=��pY���m��
��c�m@�����cVKWu��x{uvZ]�jr����-�Lf��n����nc�(FqY=�&W�}�wj
���'�M�!���r3��)��`:�w�
��Us���	�Yy����M�a�O|VI:�!2���u�e���/N�Q�:������=0�6nf���&�_�?���|�I�l'��Qe
s>g�o8Aj?GsE�c]���S>���h�Y�A,u����~��r�)u�x���H8�E}�ZMB��e��{��i���afF��l�V-��)=�+�}#KP��tV��5�\c���'W���u��m���s~v����'��i�4�����X��D������n�Cc�W��s��SL�M�J��i�osY�
e��:>�6��|�"�lo���l/��"B{D�������tkh�O��H5���y���\n:>y��B���Y�SX���������J�"���lJ�4���rX�o�������W��h�������g��������f����l�vv��n�Y��0�y���`�=3��K[�������q|q�/��Z��������/i���������B��1c�1����8�z�R0��xJ%)�"��Q<�#>g�A&��|��6��Q�~�&�1����
at�
�\	���
z>B�f�B�I���	�8%��:3��c���f��<�!K��qN��X�����D�Q�e\2��+sn�9���n�3sD���N��9=�F��X�j�{�`�=�����[IWm~��j�	���!��T@�_��"���e�BN{&��d��I]����9'E��L�0n�{$H�:������G%1{3�4�wL�P�P�G�O|<;�{4f,z���,8��T�4H>�"�Wv��=���jF<-�;�Yz���M�j��x�r��^C��-KAg/#8��g�0i�'d����*���P���V�z�����Z|��;F{��'��;�C`��z����z 	�����a{/�fX��)�_N�1q�.��8�e��$c�C��>~���� R�� ��ZX�y��K��<���R�<�a�FZo|$\�Fb�FxL����t����bgAX1SQ5#f�����^$�����c�vw_��oLe���"��a����/j��`���b%��[��%���P�G�!��k2�b���ZJ)c�B���������������Z��D���3���(���\4g<��W����&������V���}�:g�Q���S�8`�:������O(I">!��ZWP~t���{<W��
�x�����#<C`r���]� gF�Y}��q��
�"O���S����;�I���V	�2��)���pX@��I��$���aL��b��O=?��6��Fg*R%x�������"�JW�����.��1@��vw�u�%��������@��H��X�?���:�f���b�#I:Q�Y�hF4�qJ�x������@���U�X�dV3e3V��6���p�Zr�jr��?��.I'@*�������O��}�r��WE�]�6�����IogO%yI,��{{(w809�6r���V�Sc��U����MV�9o�[��z���������X���t,�,Dp���
��^d����b��jK^�s�J����n[��U�����/�Nt�����o9y����TRY��mY��]���k�lA0>|Oft��}��B���a"v�����71��r���s���ZA�u�{�(��+x����<�s��N�2R7����q�e��_���(�H%8�C�`S="��5w�i����W\��%��#
d�Y�Hj&1���\���h���@p�4Gq���o!w*�/�����N�u~|{vu�9�<�A3�V	H��8�j�sW�VA���.���l 5����.����z�D�:���G����3(�'C���w�;�:��L/A;��px�<2��
%���m����K���|�1eZ�h[^24�yz�����OT�y����W�1a_����q�������%|����p�e�%�����7�xO��/K�z�I�3(����8�!/Bq����"���|SV#@tf�6�
�G,������C0��b
���>�PHi-�X���\���6����3�}���T������(;�l
�V]75k��3*0�<��DO2>�L3(���tM����Q_M��\\�j��);����� 4x�=��[�A8���Bh�p��D#�����=��(T�����/����=��Nj6������9Ko���h>�?��d���(�-8H'x�n�����c��(�0�{4���CL�`�71!$����Y�.�����R8|��z�B����G��"�\�4qo���p�Pp�d>�kw����J�7�Z�:g����o:���.�*�������n�^��]����������.&�p��#W�x������|�N�;�t���/�������=�����[��1��E^&���z��7��]��"��Y����#�DH��yY����j���M�>��=�9��B+����>��W��4=34v^�0 ����j$U�U����
R�v�Dl����`5b+�q1������I�6�Q�.r@%(J%����yj����'qB|�Y���F�g�uv^�p$'z�hJ��
�y���>���M����+s�6M%H��
h<+��B���a�,���>`J�e�m�rq��'&�ws)f�m��)����`@=�q��DC���P��!��i����&���7y$�����������X�!���	T�>���`"��F�2����&q8�O=�}n2�����^����uO]�{t@[U�J�J55�KU�X��12D�n��tD\�+�
K����b\HLh`2;c���"R��R�5q����Y��w����4����m�fg�����3Z;��,x���;6)��� �I��g�����~�X�[���X�Gn���P��j.ZUj�&5v>���[������:�M��@�=��,�b5B;U����M�(�D�	|�r�����;����T�G�� ��S�K8�H6y����]�����,���P{Yi��������{E�M�%�5k��^���"&��lY�`Du�������O�5gj���kh���Ai��8~tR��d��;�P���!��7]���a�I��uf A�S���]��f�����1m�)",F�?N`�����$�m����p����rg"s��4S=�������3��>���yz�y->h�=^;��l�<����2|���Pzu}�p�U"��&H�S���aE�����e����<0������/D\�������#x�d+b����q��C#
��i�<��>�X%q�"fH�����4��h�F�gC�k��o�������%]�81�����V��)I'2%�������I�=��Il\���?�c&B�MI7�����hbp�Y�n�g�D������W
=l��z��`�5l��6����wt�9oQS>j�I��2N|������2]�����sT�&�5
|�|.��qD0}n-�%
:+��Q\�HB����&�0����"�<f�j*���#��C0fv)���Bg��1��N�����S5�V"J�IF�8X�e`U�Jp���'�o��x�����q��#�:�0A"5$�4���.�p����
�?K0K���sruq}��m�R#}� �!\����Vsyu���=nT4��uc����H�L<N�������f���)������YW��/so�)v!��i�,�	���������4�7�
�7��V���
"9�W�%E���5)�������C+�7�*���k�������>�k��(b/.��F�V���*���|�E��h������P�:
uH��so����4����x�U�������:+:����C*��A�������S��9?�|��7�&�0�"x�bZK�,mb*���#��I�������"�������M�k���<����������?��]��ds����V�'�s�O4�o
o^�BGo��=�K�/X���j�#L�Q�3i����w9+.�p;���|�B3!��ufV�����0�4U�������6��d��������%e����X!Nw��$��]A�Vd�sh�gN�������H��~���7S��IL��`f��(}��c@�����%3���0
+U����|���/��a*;�w�G8�g m.
��������kcD���S�������'j�;!Q#- L3N�(�����;�)U���~���d�=�l�f)Y�']	���Y�J��J����r[fm@�&FEET2��0���������.o�����[���U�`j��[�8�O8�g�T�M��++�d?�LJ�Q���$����O�`���M���u,HYz�3����g���;�t������S#~���������hC��7=���o|�	I����
�\��>���s���L'19�~�	��-%��o?��V��}�s��cN(����'dXr]�
�\�S���1��~����<K�}�@�*U�u~�3�
>�F�{Y���$L��W������=,H
%h����8%�2�f�h�����q:�&
������d
����z%��;hZ:ry�Vn��^�w��ool���pD�FQ�}���!`B��\����)�4����e���9�;�t3<������<)s�:?��uj�����z�?�[��3��B�m]B8��C��m�/�W���������&��?�����$I>_��({�`a�� ��� cv��$����WOI*A�u�f������Ue�v����~fTefeFFF�=�>8I/��{T4?�!EX@�k���I�!�"�1.����z���$P��+��K�T����4�?�7��l���/���.#q�@U�)x��O���Q&2r���
��q�Z!����F!�R�2,�����#�� `�U=��7�R<��*���`�=����P�L�&�A���p�2hSO���N���xP�J���Fy�'���_���=�r9:e�H��E���<>nu:��X�J�:j��uy�;iv�G�N�w|uy�:N�oF�^G�43�]�I���Fw��E��B�'�
�S�
���8;�������K\A�6��`���w7��
dk��9~iA�u@rQ����l�{s��+�o�����[L9�����G�g�����~�d��
��g��R�=
��?M���d���\�����'�$r"��V&�v�6B&�3,�����y>��"�;��
���(Q���"P��W)`�!���u,�H=+��r��������
-�����&F�e��x<�;�5���{��dr}z���;�����^G)qV\��-����8���V:��X��SH<�[�0D��qv����TF2��,�X�fL�#�����72�>���@#��}�IbV�.�Z�	8h`2�M��-7���O�}�������a����� �<Qg[�I��q�P ��p;
�\�?�DbCJ����9���[h����Bb��p�v
"��(f��X�\0S���Fl
�P
m�&5s`9WA�<P,`�<g��"Z(���a�Z������8���R��x~��:��G����N�y�����_�Rt����7<;)����;�E���X�t���t,�#
dJ��-�<�;��(4�����"c{^��(��m���z�����`|����B����_�g��bu��x,P���h�����h���Ldr��Gl���3"Tm�P��RFre����M��R��Q�����>���0���h�"@��]��l���0��BM"9!)������pH�������7��5���m"Q�86����Tz������nbxMl���#���pI�#IR���t�;��3�f�z������������hv�������FO5_s�&����u��r������'�bx�<�W7�@Lt��������N��Ye!2�wv)�����v�;6�$}���-#gJ�]���V���v�STS
���Cw#�X.�(�bt�<����D1u����00(���B,�w��{a��e��1��
+���-���=�B�R'J���������p��Xe'��A	wp�B�j1wP"���g�5m����`��_?&��*�/E��C���F���4p5W��Q[��E���`+A
.�fd��!�$`��f�%�����IA�$�����ny�xq�Z��a�e%d������Y�W3t 'u��8�Jt`AU(�rP�>�8��F��\q�8qP�l��?a
���*�,�Ynd�3�(�Lzf36�5_��/�G��`}V��@��=�)�v�'��'�$�jK#b�Qq��(F����Ry��h^�*EQc��a�"��<��f�x6Ao^�H A7��������c*����%s��Eq�S��`�)�U�����9 ���q(}�zX`#���kw)�D7r��9�E!��?N2����EnS���}u~j���.�x�����D*J�%��@�����1��4w�J$^����t�,n�����;0�����]�V�14B���o��OH`.���K0]/�D	��?�lb��1����A�3,������z��������q�B����^{��x�^c������q&���3/�/,�FQ�X�4�Lr��aG��rd@��H)�G)t��'733@�`��������B8V0:v�P0(����*����'�
C�4N ��`�J)���%�|y�K����y������q������X`�`t�Y-����-z�~��&�%8g�!����k��_\��}����i�����E�Nq�_�u���\2�BMqm��D�-)h*�2�t�������5������(3�&��c�B�^a�|�\S��_RP�����bCHt�(�>��b��|�.��R����t�}�/�:4���LE���,x������{��9��8�if��a;�;:P��#�'�/��.e�y c,���r�ib
�H�i�!� �
>Zjo
���#@�\����Pl�_J��(\�T�0����i�/�4�V��P4oP���|����h5S�N_�QK5����D�j|q@hft�y	`.Z�I@1�r�=�]��H�Is F:"u�5�:�^��%����.�GG�����g�O��H������g�g ��[����q�#��"�+����?E���Io����������>����:Y��A��	��B*�$��R�$q�0L��~��K�}r}�%�N*�+
LpQ�����xsf��|���&�������������`��/�^@����h,�}�Q�����3���$:��������;�Z��^����h����
����>��tz������W���cFW���L�@��{� l���P� ���[�Z '�������b�����X��]���������=�1M�G��d�K��n6T�yl0��������/�����w=�������@>�j!�^Z��))KW����tA�M\:+}�����i�p��"���C��Qrp���J�����P��wc����I��t��|��7J�OOD�������i70*[RF��7t�R���(V[����$\NQ��k��������:�z�=���G����>��x��*] �N���l��\�c�o|�]d}#��/}|��*�b��@�N�J�J����=����J�$J-��@d������,v�
����X�
�!���kS��������/�
�8z3��.�[��YQ;�T7��q�G���+U����&0�ZD��*i������YL�GcE7r�WK~�kfT"s8������87�`�9��)�����t	+���P���0����w�Z�����F%���q����e��M!��4Sf6l9���e���b��FCV��#!y.�t,����x�8c
|$��;���K��k8��h7��(N?%5����d��K���-���f�#� N��{P�
�	���Zi�d�;�D'em�}6l)w9q����5������2���h�<���19iU�t�<�f�b`�U������K��`tQ��H�5�9+)�

�O�-��K�2L�6�/��������n'��k�tk��UOSx�~���u���W�������������HJ�=T���^3��):n��C������)��C����5�����j��W#��@C�>�� w�3�h[?�s
�5�&�u\3�������c|��oS�����o���87�V���Rk�����o5e�B|&�9M����;�]&�&�>�����y�# [����v���/'�S�����/=���������R�D}�@x����t+��eR���;�-0���Y,����$�\�0�m��Js)�</��6�7R�~�8��n+q�I��;������1���1�*:>$�8���}H����R0Y@0��J����n����)R:d�����^(#�L��YL_U^Bm�j�����N�Gk���3�^`�B�K�I�#"#E�j9c_7�I��YlQ�)�K@G��#\�QI��
�G�Y6���
T'����������M�\��1�[2��"�����F��~���1��R�q'��Ow
�E��?�w�K���|��b"Uz��3dI�8��M�y�6�M�jF�����5y*)���������l���QLgbc5��������rC��Y�i>Ai/q������:����i�9.Kf
b������$����d�[4����������\��������v��ph�����m�Id��T��J���U��L�F���79��@1�<,��trK����7�����\��w���DT��T�������O�����R���r;��}��;��r�b���sS��r9S��'?�9?����2���p|�d�)��K�{H���`aj4�I���$��@�`����w��Oe���C����A�4�lA���?-��0�M�y,=�����_D���_^pc��j�|������s�!dC�!	�7��Ib!w�����a>K�����I��t���O�kW�Z�����+W�HQf��L^G&i9�?u�!���D�p)�[>�k�CP�I�9j���b���*
L������X����ivE�8S�����GN�6��X&{���d�� � ��sk� �J�.���)���V��@Oc�G�!��y)[h�i��.}U����H^J1]���	uU�%�����nR-Zs�@�f^�2�;��Ph�8�;��d3�M����M������?
*���T��^0����3�~aj����z�j0�N#v16��b���)�a�����\�/��/��#����Pi��+�ir��A<���P"w�����Uy�2@�z�u(����G�VSz�����u�L!��%����9s��;6��~�/�n������N�|������u�����T�w��*��f|Q���@���R����A��B"Fi�f�8�I2��9�����w��D�<S-hz7gD�)�J
���x��%��xR_��F1�=|&�g�2n��������T�^P��Gf���+ �o��$�x�b�vR�d��+��f0��,���9��A�d��+��gRa����R�S����NTr[���^#�t�- +Kq�^D%���d1�� �bq"�;��WJ���6q�������x����Y�5��oi��'W��P��6�"w���e=,
���we��Ea?p��
�=�L/N1���7^[N�*�i���8`r�Y+���t�@Le����-Og�=
�Ld:�|��?����J��V��'�;&m{���N��m�q�@Y�������+</��.3@yX��:�c�w
���*Am}�A��U�������v"d�f#��\%�C����|R�H�S�_"�-'��q�%\j���
y*��}�3(��������1�[�=�
�c�ar�gZ@�M O���5��7��f�=K]nx;��l������"�2����Ci�6�l����D#i���
HW�����F�2������d��[���e��"RQ�w��5"�*��?��dE�������K�{��F�������F��}���9z�T�^�Z/�������3���y�����)��b�P��b�G1�:� �3����*5[Rw�/�]�Q!�u�^.�&}_K�(��}U�z���x;��R�:��g�:�����r����E.)�K�)5����m��r��������8`�9o]��6:F�2�5e�X�#[�%+����u]�2>gxq���H���`hs+���J���l�8C�l���w%\�����pu��\J��k?����7�E�pUb&E�E�Y�u!:0�.�,K��-�45�H������l7�9,��3p��
�q��gf���q&8m3'I�*g2g����q�q�������0�D�Ip����nT��L���B�B�i5+���h�������PG�RJ�P����&�e��xA�Qq���B���XUB-�%�E&�������Q��,���s�U�7�p*�2R&Y�2�5���|��}����r*�&B��wm��I��w������D�iU)�`�r�g�(��
��l|�d�Sz)�Shz��=]�L;���/m����[�Q�$�U�OW\��H�rC�pj�>�=�n��h�k���������nX��-��8J����&�})U���+h�FQW
�8�_$(���������������\���mH�6r��I�����Lhe�+�	$��@�X�������f]"�"�J��f�&�*j��DdNP�J�#��nmY���V���A0c�B�u!T�dY���TGH��n��fF��V�E0���}��Oa��1�J���eR��T�JE��T9��B����0w����xR9�k���t�e��{�lHJ
�dgj�������C��G���k���VK�j�E2��F���ec�r�&#�$�lbEe:*���i ���_������I��N�dP��Y
r.j��V�eL�����Q���"2f(j`sB��F��xa��@���A�7^�-���5���y�^�d������ik2��Q;�^��\�	9�;��������tp���=��;E�Xd��ZN*�����(�=!+Ln]@*����a�qx8���FP?V6�GD��L�v�]���/��.����BD�.���yeZt�����-L�v~u
A��e�����	n����~����w�B�60{~3����M/u�����n�O����7���To������or�ZkT��b�T��|��|��H�7Z���S�B�����J,�����j���^��m0�'�Ww�?O{G:�����Rc`�&%w�dp,�D��_�R�~-��
��jT U�b���tx��g��c�
gB�����l�7N��uL���T��J���4��gW#&���6��� q�)nTp�g����a��u$AIg������Hy#(9A�TWe@�%����	�`U_{���TW#f���W-�	����5j�� FP��#-�iH (�I���2D�|
�c`�\q��n�����DS��qA�����j����&��1��Tg+���U��,>���h"UK!#�he4Y���t�p#
em��>��Ql=Y�n/R��MI���u�1���l�+�a!�N]��)T?�P�x�Pr������C�m���?'G����B����5*MWK������#�X3�t���]I���A^;��q�%����	h%���g9�gi�M���lZW.�M�Vcy��f����e��C��/_���M��H��������u���H�Jf,Gn����jX�n�{+�G��N�E"&i.81a�7{a��e*�~�VFT\����ba�.�)D���@�^��^�5Yj��4Z
������K?����}j�������H����{i�����r��O��&v�vfR��$7	5�!n�>� ]Q���F�'JU��~6m��]���jf��Qx'�ZZV��w����n���`�Ll�u�������
0���i��UrKF(�P��[��2<�3�=��;9���������d��(�
_S�&�H�l��x�s���gA�s��!TxW�0RD\:I�G�'�,���7W�[#��"�k9D$@:&�
$����0-U.�)��n{7��q����U$�N��$�.�W�2���UK��-q���$�������G�R/�*�E���]��+u	���� ��6XEtI�H>�}���*��Z�����5O�.
������{x�3��X� �l���\���YxP���Dh����Y3�M�V��
�X����:a����x�v%$�Ezq�g�2�I^��f��$���#����2�J����U�cb�E�%OV^��b����Q����P��M3��l������d#�<�g��0;�0�F�hq���+J���,%�H0[�c1F�1���
�,/�y���<U�x�<�����������J����O]�4��-K������<B����r��W�T�,g�����OG3j `tT������[.�*5P���k1|;�kwK��dNuf�4S]
�E����������~�l��BC��rk9v��W`Yq�b�|�!�jAj�"s�k��Z��9FEq�8i��q2r�Bn��
�o��7�<��
�)(:Pd�Cl&v�W��Y����M	�B)��)qm�}`��?q���-�&#����s2�Z�3RW�/�]J%z����A<���)xEsX&�!Lb������<Pa$� ��#�jEUP�dC��K0n�RQ��H��BW��J�(W	���e��k)w1+pK�U�Q��������]!�J������~�A��xX��}��/�c0��i���w7{�v?*wLc������	���6�A�Y�v�5Q�����������0���<���QAR�*X��T�."�y#(gs���BR�k�I������%A��k9�����\�O��������@�	���2Z]�qh|1"���an�i�H��2.��X����x���k�1uVh�C(�������o�(C��Y�r2����?�O���O-_�pwz3v�C"�W5�L�F������)���,g�lZ��Y&K��~�3G�����o����5�F�e��c��f�{J0;��4A(A�H�-�iR!iN>���O�?�i���9��#J|�a�i-2�����)
�p�ap����6���M;�V����oe/��F4�P�B�5yMP��84
F�z�+K0
�Y������C<25��;I�����3+I�l�v��Y���!�,��
�<����X������."�����]A�|�=j5A9w��
�������m��|���\�j[n��L����C�>�#!�$��ji��lAPn}�Z(c�}`TF�{�1��d�����xh,��'�K���EWP���SZ�D�c��O?�
�5vA0����j���p�g����I+�0�IJ�3�G��	����B��;��.D%i�P�>A6Af����c����3�)F5p�����G�������Yc�u�@kx�^"���+���U�����V����<��r,B��
#XX�Q��1��
�A�88T�T� G ��i��y<^T�S�����|� �w{�U�����W��V��VE�FF��[���3n��jw��H�K0��P���J���F�{6Et���Atw�kNW��EmO�b�
���	V�8������p��	;�*%�����(��B
�'���&c~7�U����o��ec�+��uW��Fj��F����
�#F��z��-���I��������F�ckb�{��pv"���g�M����L����o�@�n,0���C2v����I�(���9+
��C�|�^\�T�J%��L
�����/�����OwF�C�
:�eI�Py�����j��������S&>��I��)��g��'

���ek����H��4I~I�+p}����"Cyr_��B�"�=�p(=6��r��\Kh�G c	�	))���r���
4�����j��x�]�u�R�9�f]f�32�������2����g��P��#���P�$/�i3�SJb��-��DW����%�����
k���L+��<��(���Jj��':wZS���_�������JLE����b���$Mm�M|m��!�[o���|�xj��q�<	�i�P��K�>����>���l����t��T��.
����Y�eL��M���X���c��1��	�����W{kV83�E�d�IJ����#8^�\��1�`�����eA���	�kp����W��b:;������^EtU('V����;�����#C9�%I%���KpD���7��x.�cU�=i}h��w��E�s�]�K�	�����i��JZK4���$�`������C�����~�����@N��rW0g�5�Y��e~%S0����\^N��U��c����`��c�W�@G���M��e*Rq	`{����7�F�Mzt[�+�+w���N����9�4���y1����b�s��9)������_P����C�V[90�o���pq��:�������E�#P}����
��Ih)d�����3�.���\NW���@���l�9C=�,/�����:N��xz���A
t�X��4�H���H.x��p(5j��YE�
�^�� ���Z��)��0�P�9���#���
TkZ.C�AW�@����$�8��3�����MC����>R���zD�a4�2�t{K�XI�}on\���%2�"�?��!��n��@�Y
u���W3�����?8o}>;:o���6����Q���
2�Lj�1��������9
��iK�<�`�i��P�aq��6���8���y>�Y����g����<f��;#�=;#A�o�!���,�Z`QK�04�Nx+���#����k��s����y��������u��+��XF;��}�Z��[e��t�;���DWv���p �>9���1q�����]0 �/����2�S��B������%���N��+�2�����j>�95�*(d�����x��;���%g/j@�QX�(�]q�'-R��X��@����U����,X��"�����]<��3���������H'�������Q�N���f�������l�p�0]��K�`�f@-�RT����~�yd`	fM��6I��]d��Y�������h�L}	���X���>��D�,��_�������o�ou���?�����p����=�}s����`1k�^�y�*��|��r��aH�0�����-p�R��u��*����O��G>�K�\cOp�4�����:�R����I�\���(�` @1DV�B�T�������p��}-x�s�I��U����O{����5g/5��;�\�bQv���0�o����<�tz���aafr[�SN�����-��}3s3	��E�'fM+��!)����l�}���qt&�������:���T��(�A���b ���E���{������V����"P����N�����I���h�,��Vk���B�X��6���kk�I�@�_X8�\�#O�l4��&&���m%�8�b���M������l��F�e3���x��h*��#����XTXrg;���
�Fz�Tl�u~��br����H���3u@�f��D.%{��h����n=aI^���������V����[�����{���:g�V�k����$���4YE�O��.��d�|�z,����R�U��F{1����I����8��z!����z�Og�<�X��Xxr�. K,��9�3z���i�;/�,��Wb�Z,�|Hp)��6��m4����|�Y����
�dI�U���@�`C�X2����1�L�D�q��������^\�)���Z����~4��
��`��ww�^o�@��7o�)9sKL��q�2t(��!�������O�j����OBa�m=�6]R���Q�YZ��<MT��-�M�D��@�{��t�\�U��^.����{"��l�*���Rh��'2�+��g�`��)=IL��q�B��X��o�:�KQP�����6MW���Qg�fl���C�'��`��/[�^A�<L�'�kh,�Z�e�?*��}<������@���/q5�{����A��������#�Tg��������p.��nsiK�H�s��6���{�a��Us�����������`�f�[�.��-������H;�z��W�4��--��R�CH}��%�&V����q�O�1��5���������
�r>���D�/E8�#���@������E�y|��t<�Q����l]��N���Q���_]^�0�����PK(���5 q��9��W��n�t.���a�q�����)f�_�]4?��.�������TH��jpB9���X�g�bpJW�'�]
H������|)��v��B1��*�Y�M\�5!�l�x=q�9b����4�}����5.1��'N�i�5I�2z~ �����d��
'��}�z([e���,�H�%�(�	U	� HJ��F��5���)��vHk��G�E1��U�F�@c�2��g�[`�=��%�]��T3������&�1�/���p��+�U$���m�}����o�^�K4������:K[��������,��o�5g��G�@�/���!_�M�� pE�az$����tV��k�n���Y�L!�e�)���2lv
(x!�'��6�����Ff��/��p��y�Z��]<J��d��U�-w}����D�#���e�[���R�1���;<rW�����0%YYE��cP��_g�k����aVS�����������������{���3��r-w�!)�9���|2���!m����Q`(Op0��S|���;O�����;���
�Po9k��&�^rJ(���B��f�I���]*���aC�/��{�J��g��/���FLy��:?q�P�$C�hE�;T2K����J�E��`6��J����&���&mB"�J��q�h���fN�j��w�s��pW����9}4�[	>�W�������`�n.Z	�`��a�lB�r���������c��`!�t0������*� ��Mf������/��n~� x���q��"���V�c�����1���Y�>;��S#�l7��N/�`�^���1t���<����m�q��d6�d&���V��H������A
��`\��p����V�P.�A��s�p�p��{������y���\~]��:��*�}NE*����ESs>(�������!P�[�����	g>�W�	n9�	��/6�-�~)K�?Et��.��!�'z"�y/]�����v��}?UD/���lRc���-��|���&gs�}�ID��3'B�84
ON�`��*���;E��I����L�[JxM���������F��/im4�_�������������V��������x���<���hz��g�v��WHu��q)�o�}��;8FdT?���Q��*MI�����l�����c�;OQ��oi����Cq��`E����xm�)<0����������Dl!
#�[��y���6�	����pkJ���)0S��0<'���v<�v,C�������d���i�HW*�y��K)�
�n;6��	����7�C��n�L�����hb��}0*��yu����y7������/��*"�s��1z�~���>=�����F�W`SRDv*d�����4�����0��CS���J.vJ������k�Td'�=�o>��cs!N���j��[FX�>{��~�,&�\����I���0�\�-�@KP��F������#9f��V-##����.�@�e��l��60��8�:�w�
�3�J%����	��;fjel'u�V2���z0�ZM}���AS����l�K���Ji?�T�����:�����q��|,��V����
]���i����b�������d��.��x�9����l�A
 �2����*�2�-#�U�!���E%%1�&q�����S������,�����I���s�C���{���W������4����H�"�p��k��^c<�AU,��A�4�k�R���3d�z1�����m��9����IC>z<���.
����*�3��JY���4�&����!3���r|9�0j��7auk1���'\/~@�`�5�6���9�od�x.,g��Mzz���3b���L�?���*3��.��dtz&>m�P��(�P�0�q�����4�������Uta�L��S�c�;�Y������<��M�pK&I�9'�Vi��T�3���m���qr�?����s�a�$Cm,�����O0��6#��B�\���'������3.INb!�Gx!*��^�F8��U����
gk��o�6��\c�)�����c�A1V^�gHy�y.9/C��x�*�e�z
7��8M��@�#"����8������o-��YRme���B�G�}>�����'c�K��S��+�9���J��L����K��,����EZ�l(�0L���$>��/@zi������u��k��P0���:���f���Q]s�M���u��BNY<z����[�Da�#�NJl�Z�)q��<R�)���8�#��;wG=���v<�ZLv�	$�sgbF��e�p���m�����sBC5���<�Z���"��H��Z���M�B�$<$qQ�����4����=9@QQs#��b	�)�7_;k�"&[fQ��
Y���/`��-�A�)@h�B*y3�d8�������D����n< o��5��D*FW��X���~�����������A��� c�:+5L���=F�r�RK�L��cB�EFz2������|~(����S��-��k
.��_�4����" ���l��o��Jr���������������|����^�j�8c,1���y�U�^c��=��~lt��.�-�@,������NW�y����E��Y��02��b�+\��m�����a�����~:�GI"��e��$������?��a�8���f��{N�fH�6)��)������V����
���fsN�j�P��f?I��Y�CloA��xip���� �I���~����`
o?�3vZ��={�!�2�2T��%���Ai0(k�F�`�Af]cd"�j{U=8�����[��w,{�����>\>�Am�'_��xH��`����y�S����"�&tD�A�^~�������h�&A�Q��
A���=�A_�$����
2ml�6v�	���#j;����z�l:T���q\���)%k��r�/�P�h��yP���Z�XG�������FQ�%�r���)��Z�DB�d��s�����Q�>y��g���u����-� =��.fcIA+��}��FO��`
�i�?���lex�Cy��G�W�[�9u�>G���G5��_���5��|������`:�h����.����~�[�$+n�|}��L�1QE���b,`����-��w�^4����Z���!��_�
�z������?��zx�$|�vd�u������������#��7\�v���c�>a�+{]FNf��������WQI�I����������@�J����0A�
�LPT+���	O5,����c�$!p4�C�!����X�q������L�[�Z>�����<�R�������|���=e"���-�*[/��*�~'&���of�3�Qw��lj���wg�O7l��u��o��^�
���=�]|�F�����u,�X3}����Pn�Cw�[��������������0lU�9�v�������jm�=]��	D�fAS�����O��`�:��=A��=��=�H��n7���1}RNf��X�4]����p��$Ri�?\�*�����"�j*�Z#����b�<:�/�g�����8gs�?q�����+r�C@��r��������%�|����b������-�Q��<�R@��������)��n���^����_,�k���hC��k@����4����Zz���b����#�o@p�� *z��ADN0P#�N���Heg���	�bvd�2BC�GouP�o�e�f����z��{$�P�INI4K�TT�5(��,__�Js� L�/}�9��������-E�Ac����F���;� ���&(�b��j5M�hp�����l�A��8$�@
������
(�uh(]8~����0���S��0��x����wp]�^��/ki�j���.N����O�
;l�;�������!x�C���`	�M{��l�y"��a�3|-�CX,�W�%��������c������>o�Su89o�5Iz�>/�[b�����kM�{���X0��	f�7RY�;������?�c+���R��8Y�U�^�s��>�w�l��M��JL����#��h�:W7��V�������c������#��p��x���`5L�<�$�������q5�E������i��P~����{�ZMp�����8U�v�� Q�^��?6��1AS�#�����/�b=��M���a����Y4�S)��=���rPkT�l5h��z�l�~�L��������� R�W�����_����^i�	�a�`19goY���a�=���$�U.D��{m�B������Z=�����u��t��t��$��i��B��%��F��������(���Oe�*���
Z��T-��dYE�(�f7���B������*���)��R[�����J���������t�ne��o3Z�B7<�k���i$�32N��q�I���Fi������S������@����(��� (+�A=(���6u��C����:D��P2	g��[���N�����5�0/�G�j�}%��C��^�����t�c���g�v�_��|�����*��DS�-�:���T���3�t��X���}�!�-����_���tL������=�-�`9~DvLb�5�&���i'�q���e-�X6�6ZM6"��z�
J�c�&)>�]RR�kE�����E�!��@�kC�w&�`��V��v�)�q����}yP���f��~����w�v�����������II��4��� �.�8�g�����WD���{]�|�2��[��&W��o����R��~�\,V����4X{�2�65A�S�@�QF���������$�`<��N�X�a��7��8�Au�U������E���4*8.�?>0�A���=�P�LL�d�I�l%!by���z���3T��sp�T2`���_KX��h���k��,�-G#wR�H@:���|g����uUwUJ[N3�bsb##Az�������(x����1N����0�����
Qa���
��>��"�n|��F�;.�2R�H5E-�%��:F�au�a�)�E���o�`��Z.��1���@	�QL7/�n�����H��>&1�>��~8
>�q���`���T���9G]�C�`��P��JT	D<���b1$oN1�O�s�~�=�" �`��L��_6"(����b#V������6k|�Sh�w��vJl��H-���U>�;�!���)����7���a�2AE����.VPG�\(2������c����0�y4��������u-3e���U\�t4��IEp����er%.���)w5a�15���P�~08p�^e��(�f��#���H&V����d��8��L:i��E��cs�tG�7a�Y���(�+|�A,�%%b�5�C���w6�!�������A ���0�9��ouY�T7���^����6X�sf�e�@8���
ZO�fC��������'�-��p���P�UsK����c���e[9�B@� �����(���#G?Q�9��L?Z
�p�-����#0�~.��	J%�����|~u�9z	����E�)���2z#@3��G$�^r�%�^������q>�#$G�
��Z�����y0���2WSr�o"��+O��������wu��O�o�Ny�CP�G�v�N��z�nuZ��Z����NG���K^f�r�:ou�c�+At��Y�<i��x�Et�v�M�����sF�Z~���m�iZn������|�(S�F��bq�J��Q���^����.���;L�#��SG��S�.�/����:n�������<Qo��o
Gl��/��4�kW3�o����{��#���`��w��%��`w+I"�a�������_����q�����LP�o�3���[��h���+��W�$w������������o)��>�]V+j�������������Q\�U�5�J������`l*��_�O�����H4!��*y��[�E��V�xN���8F���/>�����;+������� <)`��|&���I����8kw�Y�`�������F�'�8�/����N/�I��F}q�� ���X�t�s��!8����r��BB������W�a�p�o�1�1�>����5�'Wz�X!.cB�x����tZa�p
�H�q��J�(�s-�9�����N6`��	Pn>$�����XJ���������j�~^��O��@2����
����E�^��$��Y���'�|��G�!L5�OO���.���]��{���6 ������en!���z���R,���J�Q����X-[j#%9dgt���Qn�7@]�L
!H��g�;\�Y�:��?�VT�������?c������~�������nKjr9�N���7�����77����\���T�2�G�!S�����1������8��������?{��e��O��2����|y������������{���w���#+e)��I���P}��}:#d4����yRt(.!d���a���OXIG0C�?��Nz�zq�bF�����?�9[�`�h&���c�H����Mxy��w@/f������X���fr����$����Fb
;2Ei�C_�5�R?-V�j���I1����K����A�����f�\��?�-�������}!^�ub!{��})n�7�������b��\^T���6dR���R�/J��`?H��
������u��,�G�����Q@�SQ.��;����9��+b���J\�������&����Z�
jS8��[-�a�X{���`���]�$���/�����p��I��X(h��6�(����|*��EpaaD$�8
$���-�������Qu'��J�M_	�9Q��u]�[g�����b���r�^��[[�������_�T*�5�����vq6�[��"��Lp�{��������9�Iy�7�.~��K�O�_�T�?�x��~�Z-�J5�T������W��ku��F\Le�����D���o�{�2z��?����������:,4K�Z0:��������q*�a����C�BHj�`�	�V*����*b;�`����l�G��j��y?.���������$��l�~��_?xa��{��#�W��P��x��j���Y������!��W*�)�~��z�����15��Q���Vw�!��6���`$4p�6Lp[@j7�2{�|���l����%$(H%(}���
��+��R����`4����-YKL���N�&^^O�@ �"%/�=?5�zA;�"�~&>f1V/=��@�/��E}XR�������-��J��I'<+p�I]$�� q�����-�,���y��V������'��%����BH�_��6��^�r��Kq��W���-�/�2��#�����mQ,��
��,T��Go8<��I	�={[<?��1>�b0�2�?�Qa���+q_���[K�����^p�|G��Z��B�P������<�V,��n��G?�y���P4�f��7�:�����)g���������O���Vi2	���b����������?��a�����>������hW���?��V�3�N�i[����D�M����8��R���*������7��������ft����++E�\){�I�+����?59��^U�D���0��1�Z�5������F�����fDr�~���<�p6@�:���E?���(es=������E8�z�k#�!j���������#����b���[��
�s�]��:�r�C���������')��<���)�A�L��,��w<@~�=>���O��E���S���������=�}Sq���vw���4��I���{?e8��M�g����� `
��hr�f4�@��V&
�A�U�-�W`��,�!�_�p�.9�dW�D'8�H����b���)|��0��>:�������}���0N$�*kj�F1��yW��t�al (D8�;���9�Pv���<���"s
F�h�$��}��z\W����w!�5P�"obU�  �qW
�B^+�w��]���?�&s����������6��n�%8���2�ES���:;l���"PN��;FH�G�;����
��r�}2L������]���&�]v���$n����9����07�m0,x�J������B�(�7�a0�����ey%?�/�"IC�Bs��6
a��S�~�+�������b1�6����V+�v&Er�kL�PG����n����a0�/�m��=��(�	��T�&gS����`�%����E���m^7O[���?^^a�;d��8������
���Y�������=�{^��Lv<�=�_���!����������oo�r�)�����k��E����<i4G9���0�r�G���_�B�����a�8<<���[����&�Z6��I9j.�Nn�[�~A�N[����w�YT�y�7��=�V������e��A
��c-;���������!���~g��k��9z��fX���WS�i|�bc"L����['��1$������1
��@�ajB��?�s����0
����F����bO�`X������i�+H�
q%B@��������N0��\ �)*=C��"�c��h�Z(6k.����N.���}���jv[�����f�Mx/�/ch����|�c�+z��*\������qqk���psI� \0����nuo�����^}��������^����c}��~�l^�^�W�A^m��7/Oo�����f�y~�:�:���<G����&1��4�0�dz�?��>���������y���]_{�W)+m�k�:O�B��ep:[����|{3��/PJp��G�>#a������pl��h��^�0�Gtj,x+�Tf+�F�C�C����iZ���H8�`M��d7
;�e�UT���->(���t���'�
�k�� v}st~v�6�7l�����A3�@$�k�5�0��|g��s�iD�Ue|�s�G����}>�h�w[m�\��"�.�w���u��Wb6�2���!:�;���d��]����������d�H>7���>������V����8���|��/�qN��k��:Z�O���\Y�#*�	��&6��$��lhL)����|�I3�
����'��o ���T1��v����ao�	��[�8�7������K~/��y$���{s�������7�����L���N�w�����9��w��w�N�n�I��n:{Gs���[w���7I�:����0�Z�����-�1�k�xn������F��WAn�;@�w���b;��|��NH������1`H���H|�4"n��?��w�����!5��U0Z������W�`��)e�'�t��$�} ���������0z��
���V�k�z!�r��fb�M�����s������b'�w\����}������P�L|��T��{�|6��l/����acT,V�k�r���S#>a�T��T*pjN��t�7���Z��&�������8����p����������Sd�*�����=��.��p~=NG��2���N6rdd�,,��Bg�\����)^.'�6<�
�X�PQk��&�o	^T`u-�9*�IG�,%��5���!��������I���uW�qpD�j�M|�/��P<*x��r���W^���D��C���`}v��XC�m�Q�B���S������e!�wfG�;L�������=a
���%�W�K�YK�d"��Z|�L���r���]�C@n4�*�b0JP�#p@W��_���[��R:bOA0]RQ�w�{?Z�>�*?��xF������E���c7J�����i�u"����v�Q+�\���Q�������#�_��rv6�!W�h>���&W�a��"%)Qa$���'�=�����(Y�j�������nB�j��
C�@2���#d
��zT��;
��c��������kO}W���f���<$���f�M�Bf�'��;��<q	g@��b��||�����Pp4jd7@��<��A������2�/k"���O�:X����Na6HF��H�un��u-d ��W��i,��� ��`��j�k�!|XGF��5��Hv���b��%+�O���C?@���H��(g�=��7���P�x?�D������
������}��CV���N��WBZ���?���9c��w�����54e��c�3���X�1F���U�O�C������>����I+[�/�j��t�j�:���O'��>�%�2�f��/!�������P0�?��n�������5�c�p����_P�����X}������y���^N�C����������Kq�������\3��<N�B~����	��d���!����-��/VF�A�������sP������������k���~ �.������j�)$n�%O�ZF��?*xyN�����/�Y����^}����j����$O�#y�}�/��B�YD�yw
�l4�\������hW���TY�#)������8���xRz�}�-x�9��csI�|�A�`���6y�l��7�C���+���"�1���������G��>�<A4� I�}���N�?����XD�L���|��'~-��y��~
~Y�d}�Ru��B���ucl����g�!���B��1:ZJ���F)D=��(&�,c2h�������d�b�����\���g~,�h�8,?������Gw3�bp����m���'�l_���SV�:R|�a�]����[���l+9}�����E��z�	*�#���g��G�0�	4�����������Gl=�
BhB,�*#��g�����i�%�8kY�-O
 +�?��c���X�Ms5���
�J����`�$�9�KH$"�#�(�y���\�a��u���A���% �qR)H�%�<������-����%����d�\�WUY�F�_ �t$=ri��R�c�h%_�|3t������Ir"2�g&3�)��o���[Jg���)^��)���Utw5
X��W����l.`��%p�����Md��n��Aq�~�jA�[�@%�{��H����%�{�hJ���_|a�X�,���.�@}� ���Z�f�1���h�v��7��;���
x�\�r!~����:�i���N�����.����|a������Cq�!^J���v�:����=��}��?
�Vl����
��k=S��&g�O E�y�yJ%�"���Vr�<���n3�����F:�#�	���z������ ���#q�WGv�5�����!��WP�Q�,s^w���rRA�+��
�@6���i�������I��4fk�����!����$�����Xs6
�D��$����8
�'V`��0�s�w���Qz���2���iG�"Z�kE��RZSJ���t�,��{v�'��������+4���N�*e���e�O�s���Y6�2���������\?�[��	X�hdT�t����l�X��z��/�a��^�c���V3��|X�����Z���
_0��iN���g�0�Shnr�����N�������
^A.�>d��r����o�;�zg��t:�?�w_AV8�������MWJ�nZP����Mp+?n�'��ld���l`&���2%e��"HS�nE@k���Z@������D��o��oK����j���^]w��T�!��OXe��`&�A���Tt��+�	r��%�F��Qc�����!��ux:)�m=�������=X�?�X�Jp.�e�?-8S,i�3��
���9(Wj(E�O���xnk�y��%���
������k�b����Y~�y�}0��#��ut����	s�Z���^i0d[� @����P\�E*������5�����+=~����$0qO&������@���������2������.�K�rnX�f(�>q��(�-��+����I��P�
�i���	Zv��4e!k~w�g\z�8�s�=��C�?�r���W�}�l�n8h�O���)p�S�~'����uL��p���C�)V��5�{��b��Z'<���=fPn�j�b�V�����
���b4��YU�����h$}Mz����[Lb�u����kA9����:��g	NsLB���R�UW��E��������0d��~^�N`�F���`a�d`����I��A��Q��A��?�l[�����n���*2d��7��nzn*��Gg�V������-@Qr�I��E���1Y�Y���3���cT����|���~�`]�� 01'��R��(��I*�V
����X����*XA�
�x�����p�s�9q4�Hf������3��Re��X��I=����;��:�W����t���J����5V���5��7�Y�I��5�H��Z��o���b�Q-6��K���pK��7��������F#H���4�x��T�cT�+�$�}���_>�As��.��"�e��]�~L�v��6����V��Z��c�2kS�����de#{ ?	I������[�~�X,J���z���8 ���yPh���y�cb�#�l���8�����m��D��`R����������i1�S��rJ}��9�X�3�XM��3����w�:���A��_�y����l)*�n-LqE?�}_�`���fnn�D�T��u��v��u��d;>��Z�?��{�����k��c���hK,Y�Y�A���y���k����4x����)'����`&���K�F����p�%��@JQX�����3�W�3vE���c���������3
������X����lg���v�	47����bz��?�ZuT�G��U��������/Z����@�!Id{w��x{6�%��9r2���p�m�	�����Ofo�H��.g	�RArPo�F��f��B\�0V��4CZ&'�z���a.:>�_3z0F�x�[\�G�*F�T�gg�C���v���,�U=��>��2x��-s:�o�i�����{���.��9J����7�-&�X��z��0[����0��@��l��S>��%eI��O�������p����������������\��na��������
���3R���g��"AYP�m�,��0���x�k��&Q��e�0�����,6�,be$�7��F&E��a�Z,�R%���b���o�vH���L�P���0<o�RyM�Y���sR3*����^����|���qt��<�aa����o�J�hi��]r�4}U����?�Qs�xD��~��0�:����r?�~�)+A�����Q��/����X��lG�"�p�P��U#��K�=���R�7��Q@z�8��K��^~����H���������C0�	���U%KY�L2��� ���^��p<�R�����x�d�Gy��{9|�������0��S�����$�S�k����.z�W�����U{u����}��+!�B"����l�����`���!h0����L�XQ%K�A>F\����H�I�SPY�%�W�:�p���%b���\�Vs�v����C�5��F|Hj�x`*b��@'?�"�x0IF��9�vN/���\j���%�,�@t�>��<�)W�3�"�TBc���!0.�����5��,��������
�l���,���h�;r�F")+�7-�x��4���/{d�����������T;��w������g7���c(��m����'���F������Ml	�PX����W�M�7�����uiRC��t�2�����?��T�< ���!�y�=�[z����Nv�dT������������IJ{:�Fs�����L�V����;1�VN�9Xm�a1(B5���8[qh#I=r8�\�9)�j���f��i��X�3�X�
{�d��h�ew�����<���H4p�i�ddO������p������K�J�8��F0ws�c����/,�o^�;#���<�?���q.Tb�(8����m��$@�:,�W���i�C�r'���S����������!9
G=�������_S��|������4�(��%|�@+������x{7�yV���=r��i��_��"lI�n�_���������?�:��V��:��r(8)A#��g��7���4xb��I0���`��|M�-���D=wXU����w�����AV�����Ll62P�QnH��&�qI������C��-I�#}���r���qx;
�H�O;��`>���z�o�^�T�F4Lqo��
)��f�]��C
|�v��5�Htb?�)�d��m^�4�'o��Yj��K1lRy��/;���y
���u��H���M�E��f��/�`�\�r������d���/��0���e���o`|F���M�!�qFJ�IA"�]����FC�]
����1��X3vLg����=����U|�b�f��L.�yq	��q-���(�������"�#z���|����h���;��>j4�#��SF,��)���^je/\��#�����m��)w�Tn�/b�	P>kN��m�VG����<��hA.Su����+�������+�I���"!�����C@���6|o��Pf���yAZ'f6����8�r�9�r�2� ���
8]����vW��X�\
2�J��-
"��.��9�����	8�_��!���<@���b������YS�Xi/UkB��:L2��	���bR���9�D��:X,f�It���S�/��B��[�!\������k)(Rl`���{e9P���x8P,i
(�YU*�'��j	�������[���v�7���Px;���S�c�y�j�>���O�p"2�h/��ZF9'!l��ln:����[cm2!5g�.Wjd������ng�y����
u_��*�q��HS�A�Ur�j��Jw���*�L������CC��������BpN$&c����
���,���J�`�����7p�5*�S0�k��}�kR�`x)X��������d�.)�#�=�[��cR���k����}���W�:���;LL%�R�?���me�L
b������9L��Y���]0�^0�$t��G_�$G�i_���*��z�g�E�/U�.U���N���~�'�� �5��M�
e��h'O$5S���~���K�j�op�rC��P���bq�?���O�r�P���1�����a���i��)m�&�4ec�4A'=y!�z�t�1�P����=���a��=�(MRbEdbm���Z�B1Q�u����JT<��A���A����������L�����~�,��9F���pl����H�z�	�� ����C^?�- ���~����M�9F�{�����j_����;>o5/o�{��x?���1#N��h��&2`B�;��T����o�J������ ��N�:���a$���k�y��b@�Z��wI���������o�����o����(*�aB��H�ns�nJ����UJj��G.`��LE���U�6�)������S�DuJ)���Z�t����\?�o�Tuo31�3��MvF�t�mE}��3\�k�`�*c*�S���]��L��_�hY��.����e�	J?_%@��{���}�s�����|�*������+g,���305�/�gd�d���1D�(�R/J�
aV�x�Z�����;�
8����PM2$��KM��@a����( ��*���D���@4����S�C�D��;���m��T��<Ro��	��w����\t��_=�K��{O���m{��������������$���%���{�]8���b��_j��#�#$e�dN��P	wd����u�jL��C�MtUe���P�F$m�#G5�C���}&ky����)e�MT�V�-��Q��|��o��w���V�[7����'��}��T���%$�T9�/�<����M����+z:"�R����-�SF��&p�C����R	�)���_=�]�-	��T|$�s����d.�1���C��m����Zjc�^�OL|q�Tp�h����<�����c1��7[x\�)\��d
�l�$��*>Qy
.z����zY���t�Yn�_����D����������1HY�8���g�g�>
�k;{9��R6/�����C�|���Q��3A~����<nv[��$�����;[������.qO���Q�rP|���"���1�����ml$(}3.'q$��0z�u6�aR��j"�(?*�:���
j���+�5���r5#��A��3�/�!�!5��g���g��w���;[�q8��o (`���Qi�����R�_T�`T�������^�`�]�l��z����Co�N=��������u��,���6P�B+��P:����i\�) =�l��T=��B&��i3����Hk�11�si2��N(g�����r����ke��
|T����K���T��;e��I��
���|/S����0�7*T�b��-xr���nw��u�v�XW&����(���E$:�`���?{�����%��������  �����%9��-i$y��l>���@,+�������]������}�%�w��S�}���7�8��?7���(�"��h&������*��~�=l7��A�7X������K�mE�1
���9�(|(��'�xr���bF��R����KI���]�!��C]3DQ���xp��w�d��7~�3�c��I����MD_�Jf�^��Y4\��@bp��pg�)*���g��v�=�LVec���a�M>D�]�GJuJ�\$��)�����)o������G�
������o�X�-�A�Q
�abt�{���2��#46t,����G,S�YdU�<G�s����Zr��wv��^��u�vvJ+���5E5�
�������HWh�2���\��5W����-�t}QVgx�bN�� M0��(IPm�M��������!P@L��$�r�k�$T���c��w��DT�p���J3�����Rn�_4�����yL�J��Su���h-�FK�����O9�0�����fu������x�
/�amM��+�i�����v*9&�t3���=��9��i����������,m��:P���D�71��y���aY����������UV�xK��P����i�e+�H��6[+�a�S�}k�.�Z�N���5�#��[���hU}�ST&�69;Pzl>���I�
�l;o��&������g'�_]�_�v/��N�N�\�r������J���A����%�dk�g#cM��Fx=����k��\��2W��E��<��7Wr��ug���[]���}��G���������3�M1/G�eh��BO�idM��Z��1`O���Q��"M�~��9qW�Hv�n�( #k����>2�f���!m2.�H���o%�U��0��Y{]�L�4J���Xh7l d�{��o���\@�	�8Dp����)P>B2��eP��-����{�:��i�=���P���x��b���E�Re���[�/�������D�D��oQ^Q���������)�M��W���b#��h�3O��/���z�n���VA�R��B78�����������w�g7~#OE���w7��������%!6���Z�`����N����F�m�����.j��hBP��$xw�g�P%+��=���
���d6��\�3�^�`|���F��F=&��4�-��$H���C�M���7�0H���![FWe���O�$�p��`�G
.��4$�e	$h@6���T�;�����"�+E�G�%��?����`���C����m���x����G<0
�G�I4�N�\�;�'�o�-F#�k��
�K�y�D��%�^�����S��U��+��������PSOi�[���^����QX�������U�yV�0��3�������"�	�6�Ds�7�1U�,�e�?�)��A��"tI�s?���Hx��!��9���_#2�i�G���XmA0��I���B��`�W�D��8�n�m���d
.U��a��lW8�I��IM��a[����'!>4q1A�q��N0�hIA���z��Q���c��6�H��������k^�l�T��P�p�Ci�*D������������h�����
rAo�H�_����d kfC���������>Q�
[��z9���lX�"���������?��w5,E�8��$b]SNS�<^�������������k����~�nM�N�A����:=��(�����f���"���8O}��%5$����a�1wz����se}���6�?H�o�o� ��.���W8�����QB��f����(�Rm��0� bLA|�|���������K#o�%�`��6��������Z��D�l���9\X+������(�Lu�������T�D���El�������:a��0������V ?M��e���g�P��������h�Ppi�:�N>��0X�z�06���p���$����sN2����%�y<����3����b2�'��md)��*�p�NE�"48W���<��+��{��Q�����C�Po4�O��Y�
�����c7���@���������>
G�������%h���>�j��hX[�a6M�����o�=�I�-�F�u,�����P����X��1�?g�Q��~�X���"���|�y�?|����[�����T�k������d��)�I*�����~�lU�X+dO^�i"Ap����?T���c>�m/�y���Z��gs2����s�z�Z�5���s@G��b������Dr���|D%��x�%���U�NP����S���x{vqsO������J���F���������h�b��.�K�E#���m�#�MRC�3��}��?�����7��H�wOTf~�r�8*ZD��l�0��T`u��xO:_vO`�17�
�D�+�����^���(�R���.�'cX���`��1�MREDo��%�ak�h�}-�8���m��u�q�j��
��
>�����{y^����7��<c�VGf������&cD�4�A�9pf�����hY��4-G�*����ux �s�08b�����n�4@�����4��l����X.$��:��n��Q�i7�=�W(����	_q����\���)��H�Z�\3cK�	���Y�
������VG���x��w�N���������B�t���p`���e�^ZF���/�*6Ug����^�`
���P���3�2�T6�h���v��j.�WA�E$�>N�H4��Ub=	B80N����x,�B�'(&7�
9���?����e�������Wg��2�2���p��;��F{�}��f��X�R�����"U�H#�T������B�wc�Jz�.���S�GL��}�&�zz����IY�-�	�T�S�G���6�U��]��d6�j{��q�:���a"y�=eH��$�K~Bb�o��o������j�$lI�z�4��Z�~P�e�����V��x��t��:�8���75%bd�tmN�9E�������~���s�3g����~��\��F���P4�}�2�[��V���]u�)��|���9�����i�����vk�Ykp������5&��?`&�
6�E)%����b�$Y"��~�%b�w�E���S�6�����La���1�����Q\L�mcH{��;�-t���]��W��<���(:��d���U�;��3�\��_(M��B��,N����E�����o0��._�X����{��gua���U8�����"�%���
��*�)@���Z��1�vc0��.h0�|��b�U]�!�)�h�����O��wP����i8K#7[Q��}���p��l4:�a��iy��������v(f�ju(�����
����*u������ �������7&+��@�������u������'�����������H51�P�"tX6�����G�oR{� )� 89�4��	���C8,�S��,t�j��������~�q�;�uv�����#��Z���{Vby<�S(>���(|������uuGf7���0�$���8�
���Q����O����@���l+����(��_
�1����'���G��Wk����:�������:�#�m���|���\�ps�k��������22c:��V)�������9�6Z��F��@���z"c�����`I�/#�Tn�4������+���#��:����8��W��O^�����_1�������[��-�8��\��uZ�1_�����*��w'BY�*�7�rOk<���HE���J���WRC�=��N�����	��A��Z��0OkY-VM������R?x�-qW�O�e�aA��s�5��5H��6�P���<���h�v������:�&�����31��;B< �H��-�&�b�Uf; -���4cV*_k�$���Yj��_�,���X����.nagd��BR4���$�S��;��;���&�J#,���u��E<3�J��gf\v.t��9�|�i4g8�T�|���(�0���Y_`t���8�ED�>�����X��0�|3�G���^"Ur�&���c��������\����U2Gg7���Y+�Q�YKfO�V�6��6����$Vy<$�G���j������Q��ngX���}�>���S�z<�=1p�5�^x�(�`x�#D���JV���2���;4v:>i~%��Yjr��G'�C���
�������DAmN4�sl���s�m�x'e�K�����~���8�]� $��j~�����\T�q��&=0�n=WDi�s<���U����^��@�����/�W�������_HA�Vs������^�`���P]�bU�[pZA�h��K�)��g�A�u�!�9
6S�.����j������cJ�������?O1i?���d��g�X_���
$#'�(�n�~��:I�s��W�{ ��Q8��g����=�@u)��$�\��Z�C��F#j����G�����)�I1��I���M�A��b<d��� �<b����8�R������nCl��z!�I� ��G���G���%a�����8����sQ<gCN�<�&����X�.a"tN���|
�P��6�s����(M8�R�1�P	����;�{�qJV�!ff%���b� !�v��T��p3������������������o�����)�K=��\�`7#�u��Q�EM5�����A�w�a��!u`���A����d3���7���U�gk>4��8�!�/�r�c@*v$'Fy����#�����t���!uT�y�)t
N�zS��U����7^W;�q�k?�#��hz�S{I���5T�B7�}��63�#���L��-�����[�Q��/�a����.jc
����t�����
,�W���?=}JS�������Qu^�����;���:z����kl��I�����v����Z@�I����c���K4��@�q�i�"������,1S�:P�<D���a�Q���<�%;(}��
���0�B�BA ��'��WL�-L'�T��,�<�D�{��V\���U�a�U��_#����~�(lT%�(K#�#KsC�2�����K�.����^�]E��f��|��Y���`#'�������g�o���
O�)W�]�9�Js�V +��zon.���8���z���+�<�4�$���K�<wts?���Y�uD�kF�q��e�Wa��7�z|y�!��}�?b|,�C���F��6w������S1;K����:9�`#�p��-&��5g9{kl7��Z��W�(�{9�l��i�V��v�W��^Z<��'��������{��~����D���U��y�9��=��D��/[|)��^'������nYz��d���%��A'W�:v�	�����25|o�i��@(tM}�?7kl��v��U�.����0*�,��]��h'�~8��jF���������hN��n��;_t-O(����I!�k�-�V�����N��[��������6�v)������`�4rdhW��<z�^���[%��R��x��{$]�9�����9��b�e�z��!Z���aV4�/-��9��e���L�����;,[.R���@��}���aw��G�%�.�������N�!~X�9I�7���bL�d�#�@-�2 �k�Y3�2S,������G���8�h��h0E�������yBe5��������L������F�R���V?M����h��u�z4���5w=���Y�>J���b:^�1
��������������O����tg�����o6;A����������Y���@�0Le�����>x���b����+����	ww������a�7l�mXE�wF���pop�i�D�[��7�4h���s�/���
v�<8�=
��M
��
k���)Ba�t�����'�o+������Y\�����@7��o�ys��`���N�r�������oO^���V�����b�X���t
���k_�J�I����-��!F���7���T���~~�����-4ns�T�N�0,(�1V�'iuk�������������{3��u��6{�mdM������^
��g���p��2��7|����=�\r&u�.�e�is{��q��
�u"�iNWA�Tdcx(QF<�����l6� a���V�����!�2����Z�|��.��a�Uc1�R8�F����&��.f�%�U5�a/�7���a�����j+3"|<,�R-mo���z�m������
l������V-�y�/,[)+���]R��o�7o7��
�E��D��02��@�_��k�x|<��w��A`�4�Mq�z�����_�,�>�5,����7?8q���xb�['��eG�������l�����01�~��;*4�PDt�5[-�������,evKQ�&�U���������~���Z����A��g�����/Q�)��J����	d��7|����,��}-x�L�2��sQ��}Tg4�L�*0P/�����3���������l��d���|c$���aX��d�X����"���|(��2�&Cy��t�rN��6�^��9�zK��L�6M�
L�d����<���;9���7,>�����a�v��M� �]0���4$i����]A�oA��{�C,�vH�W����<���d�����#z�����qx�?���b��?�� �����C8������rd@�!P$��Qrf�����e��t:B���������n�/�!�K�.���\�4�	QC�6�&dm���N�k����4E��>v���Px1ox.
��o�%���L���Q|'Y]����x@}�6r�\yF���������������C���^����m	�:��5p

1�����������(��m=��~8Sf9�h��)��d�����&��)��� x�A������"�����
{�D�Z�����X��?���l��@��B���YT�'u��d�)��gZ�c��_�������vg'#��ou�����W�����������(l�:�fo���������a��?��;�e���1����i<
�oB��xO
�����#lA�{�V��.�r��WN����8����"���J�b�������1�6A�O"�D���X@�Vg�\N���4��&���U��#�&����1}4�%3T�D1�h<���!	{������5���C�
Z�f6U�,C2��h;�@�q���.C�R�~�M���1��.���bb����L�=65����@��bZ����kv�#��,���0�:��$��8�"�q(g�*�c������i.B����-(h�g'��i��,�y�D4�&"MaM1�$-��#dP"B�"��*����u�d��M�`U��Y��"�j��kJhCi)�XvMK���1���b�+���n�:�X�OlA~N����qG8?$�f6�����F�
�!���}~��y7	��	����������=L�WX}��,�OE��;����`q���-�;�yu�(���������]�E0�0��lN��a0��08C�_��&u�	��GH��
���.�E��{�BW	��N�H�����<Y��=����t���i�!��l����X�D�-����[`�����x��4����x�
`������?$���6��
�/&�p|w����2���J>D$s<���E0J�\C��RR8;?��?�`k�j�?7�n������p��oA&K���q�SF5���l{�@�d1H@cd�Y�W������r��3�5�6��9��i��Z�1����"?��k�G�p�;8�_F
m�H�	�y�v9	�X)�r�k���4������SGz�k��a��������d�����M�������e5tmWA�0>Z���j5X.lY�[+��5-V��*�	�0������6P�C��N�������<]�p��7�)���m����[���k4��m<��v�M���q7Jz�\�/��
ta�m������9'�����	%�������I��;N&������{�+]U59�p?l�{�F��~t���Ww�\�������w�1JxV���r�U���� ����1�.P@RP"��K�A�	�e����w%���,���H6��C$%�R�����X��/�������F�����������i`L�A�����|u�Q�rki��On��WWo����F�������-g�����.H��W�b�A�g�!Iaww0$)l�����um�p:�UM�b6�da�^?��P�2j!�O*E�H�bXTy	�'��cWx��b�tT&��oL���d�Un6�%b���C4��8��d�0�����`O^aN��]g��0�3�,2���Hf����� ��"x�S6��8y ~���v����l'76$���+���k��=��{�0J���8Md�W'��Omdx�D������/]U+�#��h�S�LX��*�>�<�<=SF�������g�g�]���lSE�������$f����32?�D��##=N���6CeS� -$Q��2G4�~wqz�8o�XY���r����/��=�~{��f�2[�����r��2�Q��!�v���+��R������T-J'��+[7�����9f��'�EF�P����\#�Tg	�7���'��1��
���p���C[3
GI8���ne�@�UO��@�B���z���g ]|�+`������M��n'g�{�|s��������y6���-�����O���J�
�����~N��ER�7�X���lA+SM�u,'aY>��������?���N�p'�%*j{}��,F�%3
>r�p:�q�@*�R�R?�`������uP��$�
��1)���M-����*a�(8I��3�V���7��Q�[������M���i�!%S��}�O���A��;L��n��	����8�.`�x��&q�������d�?C���������"�"n���|"W�����ZR_��>8�%t,;$��L)]M��e~	Qv���u����-W.��Q���m=0t���F��1��Q
�Z�K8�H'�w�,+
Q����=Y����7][vi�(�G��S���(
/��>J�;���~���������EJkB"7�0����n������I�m��[��
��}��	�>�\!Q�@q �X��S�m��[A���X�/�H-�QjP��A`��a���bU2t��{N]_#^"l���X��@��D��4U)�)����Z������b+e]��h5���!��Y�?����F@/�(s���1��sv]�OAA{N�!��3$�3�,5�t"�c�����
����
Zon,�>3%�����U�	��Z��%G���������<�'A��N�;�P(��=]Y�l�4�73��JNFy����B�h���+r`��%Vb0����kw��	^�v��r��uq�=���1C��	(K�f�*�� ��o��y0RM)!�kAH����Y����r���5��SU�4B�hu-�t���*(3_��aHO����j�Ju���\)�!v�����F7�JW-�KIU�N&f�t���U��`�];�(��s�(k��~U[��n�s�8������_�O���p�hN�����(�����"T@K��,d,h6��Nk����$�3��)�7����`~�Ew,��;��	_'"wQ���Q$T�4=��5N������/J��]��B*�NF�i�E��
���5;>1)�H/*�*����
J�j���7l#XP<�����������'��#�� �Ly�ArQP���f�c��5k�)w������U'����d�8�.���������������������]����h53��T���f���+�zs�����T�M�ee�4���J�@�:G����%��.]U��lw�X����"��j����4\3q[IthN,x��G��b��XJr2�D�L�>R���J�2�}H!N,Sz����o�������'q%lF�������yC���K��
Ck�Ch��%Y�������k����&�uf����'8�L��89	JT�c�tK���xH����0������NU�F�Wmy�w_h�(|�
�M������a�)l������>Sh�����u!5���l<�?V79`���nD���k����H��$|�>q?�>��3XK$]��?��?�}�L�{�P���-������>/�v��W&��� Rpi7����|1��9O�&�;���������L<����9�������o"��Z�h&�=��h�T_�5������;j��J-�������Y����t�[�=�v����
�.��zBk�|wn�;�;^��������dO��_w��w���xv��;��[z�?eb�[�N�������C�e��s��W����Z-�h.5��_I�Y"�,�04�jh�4���	����V�)���F������zmd�z�K�����#3�����|*F�u�C)f�Kq���?�dfV�U<W�Vf�|��EiC�M����G����x��co4����vf�e���uPK��A/$%yix@�����d�tP	OB��F�6J�,��2mns�o��IW�m�XUR5����G?{F�������zm-eW`�7�,���G���W���iLR^�A9~�W���AN�������cJ9C�:l�"*��M��x�HB��p+�W��C����
�xcX �im`���`U(���~�+�c��;
4��o�w����K;�f�N������V�0?�,k�����b���b�d6�0GB�"6�����F�(����v��P��&�����@!
"t��yW�(
���� 1F]���Jw5����F���F0U�����4X��z�.\��v
�P>���$��	
4���P%z?��r�o<�H)��9V�E�x�����&�f�*��Ma[�6���SQG��:���3����!8�g���Si����)�'O��'67�L��g��87����n�c��p�Gm�"��,���$����[���d��b��u�,�F�&C����\2��]@N�����r}�I�N���Bz����KM������JH�����C���l�NEi�\k����g��X�4_���{��*��A{�Cp�������E�P�(�T[�S��<tZ^{8���XA�2U��MRs�����9K���2�AC��r�-��rM��&��r����|S���l�&��3�p�8m6�RR�n8���jz*�t�x-Rs������2k���;1`+�!�#!��V�
T�c�a)*DK}���-���..�g
��~'��m(�;n!'�j4/�/y\������6 .�^�P[�nM?h���z��ep��0T��.Te4�`�m7��F��8�T#��K�s������
�5����c�)�[)��v���#S#����32����r��R�����-s��S�d�����qG8,��TT�m!�`�DZU�z��Sp�L�	`9���e�vyf��d�UQ�f�Z��-/S���@bb�����5YQy�C(D�e��Y���fe,aQ+�	M(����+q�'D�^�B4��aT`�(
���
��g1������G��>u�������Buc1=��W�1�A��s���S�vM��
����*Z��@���RF1P0���rX���h���mt>��9UE�	���������.�i��fS���1x6#����1�=j�s��j�n�G���6�+G^
���*yE
Ut���3�iT=���������pk_B�Uf�7��8t����������Sv�R+!ZI��!��34�q���!f��If�J�Y�S���<Q]����&
�h[a��`k~�-����|r%����=��
�_z�D�GV�ulEH��+ ����^��0���\�h��������6}��������c�Mb"OF'���QP�ro0�< ����+��nn���:;�����GEU�'������Y��\��~8����������E�s�����Ky�NC�����Q`�������D�e�����G�i���g��D����+g��l�O��m��[�(�v���0�=�pr c}����u�6A���F�_$��&���gj��HdM���;�E-xQ�2r��gf�"i�V�����:g�)�	y�"*���Jh��n}HPN=���WN���� �����������:��2OU��S;T!1�Q�rPI*���k�����r8FOB�s�QDr>ITHa���0G�8�G��Z@���8�H_�]R��j��A��=)��`�+ ?'�hV�m���d���h����o�����&y#�]87�k�Jv��m����+����Ig�Y��B+��#�FQ�#�15i����6s.p���]��[XlLy�V��ke^2t�E&^K8:jT+R�Y��Rr�g�����=��������D�������=�yy��5X*��g��-��8y�5��\��1�|���&��"�����Y���]��^�T ������Y��%L���#4��9i������jJm���H��w/Lx�������E�js�4(,�G���,��q�1H�
3o!^���j9�����ba+V}��2T�}��r�A����m�K��)����.����+���
�:{a�i�,�+���W�����d�v���e�)��'�P�f����-\���zi���C�n�s�����B~���X���m�6���\���{��I�l��310G.�uBq��=�U������9�(�D�p�
�S.�'Y%[�<G$a;�i��T�/
����- �2dU9C7&Y!3C�d��g8{���� <�!P���h{�C��m�m�'L`�}��yA��HH�d�5���MM
:Y@.f	�z�����-G�_f��>�+��RA�������3�sC&M����9��A����9g��wynf���|�'Id������)�J�������G���QK#$�v�^��+"C��<&.\��L)��;CF��8��%@0b���f��1�%m�A���&�M�Y�	�;��|w��W�q4G�)
����f���Ib�A��&���4� 8���I�&os��,��E�5���l�Z�����2������������
x�2�c�	*��k5c���Cg��Ih,'l�'J����>�c�����N,��UA�����[M��L�����A���3����@����u0O�7�~�YF��w��C�J���e& ��_+�Z�l�%������`5b��
v9?,��"L�h�^�����Q��A	]��0����cP�A� L��S�Q�]�u6A5R�1%���h�i�j\�N.S2����W�/
��JR���R���3���DB�
��O�&����������b��L��GD��E�}�3����p%]�"z��!b���I�����|�,\�(w�l���I��C�
��S��p<4��B>v�9����I�TX� E�@UL�[L�9&��v�(F{����xBa��&�7����������f�a����b�,����y���E���3���T��g�>[(�;������=x���3e�3S&�O�	ejY�+�{o�����!���F��b�d�����Y����[WR�o�C��4��X2�LXj��xd��O��d����Jt�L<��6�J&��Z:!�	�JY�@�I��#p!�K�>D55�PE��k�i�j6M���,��������S�]�h�y}�0b������U=��n������>
U�!A��,���}e:�z�-�R�U�_|�6Q]�_p����rx���xC��(�F�����������ol���Q)��{�6,�v&q�s���o���/�9Q'n�f���aU����2A���k�/#=Op3���}�2
s����_Fw���Z��rCE�D�S�J�U���<qz��*�.���6���I!�&�i�i���*v)���"��.A"!��9yV3��������A,A�J�����S����E����b��s�^�����n��6�v����	!Fc�9���Yr��rRx�z��I����N���.����7�'������`����"Fe�C�[�b9a�I!j�o1T_�8����i�{N����P��rr��4c���P���+}�E2�A|
�f�3*mz"������Eg���sx����F��Q�{���<C�5�H8���^	km��)x������a�����c�Z�3o��Fr��V�U��(�,���"�hU�:)�f��&O�W��v��
����x�TX�~�M��j�FC���
5X<*����k��J�������W������7���F��b�c�61����ck��?���]<�~��Jf�|���N�oFp�3��G�s[8� �=������.��)P�:C����f��j��q7���-�5g������@�P�L�������������-�2;��uJ9li��|���F��F�f���YK���w���������I�b5�,�I�v�s����c��#��t

&'��4:����o�mG���:?�M��F������W���N�"�L�<aq��x�u�p��g��P���7����QI����PO�K$~9����S5���V��S�O�]����P����[o���i��[_
jt�SE��"~}Y����ub`�E/&*��2c�xC����P"���0�H�'��Do��XiJ����8j�A�LE��,���FG����,��pR?+�y��#�y��Q�����$�aa<��Z"�-��U�X����l�2&""���Q~S�y"��Y�>��%F"Y��p����/������)a�-�����,����xYM�����'����/�[�c��vL��n�;����@��H�nquc��E��-g�I2��W9��T�ik������v���af�jSQV�^�e��_��P��z)�L��ot����FE2�\�#���[�S+���2���l*�3�W��g-��5Tl0OT��=������q�0�WT�M��h�g����%���U�ZI��T{�v'��Ee�1r4����OP�(j��f'	���`gEc�z��G���
)n�R`2@{�>���d1U(��t������C[p��l��y���U/����j-���&,1/uZ�����,B�����7����mm��������o]Y��>K���y��?�t��H\��q�2�x�d,!vG�U*�X��G�<����������QE�N����&i�W��P�M�T-,S�'9�\�~���t�A��@����Q���������31�"��9'&���p0��8.������x�}�(3Ui����Am�G'0��F�x��ZD�T����x�mH8�d��z�&���JQ���(��J���>D,����S��iPp6��������a��F�lc���i[���bc6�/���
�tX����+c������
o�^D��S\Z-hjp&�ZE����������p]��T=z4�����kv�����x�]
������'���X$!o���b�u|b��2�(����xS����u���m���f�� ���|���i�y�������I}B,�cY
&x��������f������.����$[9Ch��7����W9c��IF�Q�V�5�:7f�1q/�o�_���s4����S{Z����c��yU��h{�
�sI�t��O����G����6����d���f���2$����El47���>��z��h����[x����'��n>�(=�������c��?R�f��n�����O3oQ�L�y}��Zk^�T�y�L��w���$��#���HX%��`X�b�-�����v�Xp>1�����evu�N/$�{:��/#{&��h����1'��aY��/&�nD�Ii��K%A�`��LU��L3�x3G�]������Z��o���%+<��<x�;����t_]^�=�
�����O���'d�A������X�l2�����u����T�!(6k�pt��?$�ia7��$*<N6|{r@H�YCbI<"�"@��1d<[0����v_����*���x+LN��O����|�a1�*����@�`�G��������
��,�aY�:�`DK�������]�H	c���[���s��dJO9�����7����{�4a����o�b�G�.8vH�d�,o��cA����vx+CQ�G�pC3��t�����w����
���Z�xN�!�X�!��p+��
��_���M�R�EvYy'�5I+�E�V���I�+w	<a���#`��A��u�%,��0���K���Y�
�F�`��&C0*�5��z��3��m��(z��#j?�����x�"��3Ds �����<�10�B�
����!b���=�r��c4�����k.I#���<����8��h�dT�T��O���8�1����YN��.�+&��x_����T���&oY����f��5�-�K��;���~���A�f��M@���A������#"~ek$$7��b=���Te�A���X'z�!��y*�!���X@V#ijE1��S�g�M����tts�2<���1�u����`y�
"p����K�n#��'���,��
FBlC�c����Lt�y->�R�Z��tcE0O{������#�� ���1H�'�v�(F�*��f9u�l���-D�G��6��UY�+>p���V������W�(:MG/jWz�,����Yq�
���*����O���qF:a�=�d��B����`	S��E�,��z�hc>Ld�hfh|�jA��=����F���8��J/J��E_�$�1LC���2��]�TEB?Kl�Q��Z8�qe�@=�X;o�����dU7����E3��%@�-��h^k���q�[�fX�nDx�6��	��y�Qx�����E�-|Ca�
2dq7��Wr��g(��f������kM�Xn�7�\k4��3v�V#n��oD}��L��<R&�$�x4�#>���wL[�����0��c�r��oO����2���WvGn���:(�����c��G*Y����<����%�\Z��|+�'�?�X$b	�C^�.0rf����T�+�Q����w�g77G����l�uYA����p;T7FBco��+b���E�P�������u_��X�N�@�������D+�xKB���2��p.��8U���!FqIVZ����G���Tc�b�I���{FGh��$����iE�7��'Q��0�^w�j_������u�#
������\�=6�|��_���."����#�����u,����A�B��z���W���E����l��
��������gW����9!�����k���X�M���z��ap�@l&�}����dd��2e��FW�������Y���L�n_��4����jk�_��X���V���2�Q�v���	��n�I���'�>KE��O��^���L*J_��(�2Zj95eN��)*����(��L{���`-km���,w?��2��*!���YNn�W�ye�*O^	�e�XD��	#���
�"H�@���k}��PF'r���Ew1���^���g�b*��<9��,���h����%}i��Z��Y���S��Xo�B���f`��� 
�Rn�U�`j��L\/j�Y���:�(_nq��FJy�6��glwl��/��!r;���(�!�������c=��?��)�Jh�|t`�������ud5��o���)�
��a���
>���A�������:���|�C��A����� d����a�QJ�s����_!�ghSj��J��������Z���������f;�e���$����*KC�YZ������7�X5�
*�v�����T���-+`d*[���1�m�G�I�`�����)9�=-G���)c
�0
������L,������������Z��5s�Z9u����r����:���y���	�	�W'��p�V������������,�<�+c��1>UW����CZ�/5c���X����(0������6���(�a����%�w*/0�������\�$��<W�].n��������6s��H�f1��&8x�����v�K	��e��[���,��$SIaD1���>�J������_�����m����(S@�������4���������P�7�^�:?9����������22F��qzW}�{����F���������w��v��8<�4x��g��t�``H��SDl���1�=�k2|p��<��f]���((�-%�����oK��Y����e~�K�/�bb��6��(�n���Q�[���?@u�5���Q����b[\0�X���S���S��*{�Gv�������9��q9u���������9e��
��=(3�����T�cBw+f�:��b�r�\];�7,b��{u�YJ\���L����.���I�����	`�fX��5��������_������,���L�:[��"e�5��[��'3�,}h�	g��q�E�#k���}�'gPzW��+\�5o����a�5\��q	���|Y��#[���n�z�!6��#o�-/���n{s���o$P�����/�u�2t����$#��go/�r�h.�u�V��?�����P`����
������_�8o��N[���CW$��}��/��#�����[����A�a�����������Iw��T�L�
��,3���P��|�@��n4��8\�{*�:�������p��h� ���4��QPk���]�u����M���t������<h��/�����_�5,��e��^1�fj�}Ks\��V��J�~]��jgu������R����E�.z����u4����8���d:FQ��>sG~����$������8G��5�,�NF���8�|��4wM3����`��O�����0P�3�zCZ�4=a]����?won�o��ll0��T�	��Ss����;�.�i��S~�G����cX����Z`���$���C.�D�C+��(��6d��M;H����������$��=W�
�w�2&��%+��3��>�Ds������w�������-|�v:Of�]�=���G(m���(m�?��r�J<D�N��[�F�uu��a�j6�:�J�^_c�����:c��OA}�Y�	���N���L�iz�P����7�N�����:��\X��T�(��-&�PP%�*���W����`�w���� +���m�P'px���3l4:���x�����Z������^���p�1z���u	���"L���X�60���cA�;�F���kt�:8�1F��J)D�l�@w��b)���X����-��Tx-
�g1�G�6���i�����y��
Z-<��^�������!���1��tN����'�P�S���s�0#�AoU-`}h������,�`vi+�"o������\b�vm�C-%F��9O-�
��\��
��++���/a�*�bS|%�<��("CU_�=,��x������p0�q:�(���I�!����Q/i4G�!��DF�NG�o1
(��O�����q���[��A�X ���9�?h6{�����RH�:(2�
��N�����P�X��}��m�Q�q���������!���,�n�J�lQ����x�J��&��+S�~�������i��N�V���km\l AsSB4xM�1����@�_`/���O?/G�^�;�d�����
���\}w�}wsv}s<�Vpu���e�+L4��$���D�/ipGxV�H�\���3Q���_������������8���}Iu���zT��!oD���������JAVL��t��-�5��x�W�7�o�on��WX�M47�
��g�����l�\�6�xYlY�%�K�=��-:�q�J\)P_�T(��6�)]���_/N^__^\���r+FBFy���\%�E�!Zw8�r�.��F�li�T�4�	�V7pG�CMh��fx�%���`�Z�������2j�i��GI!n����E�Q����mE����J���RW�Q�^����:�����C.�����X�k�m�SRW��n?A�?�s�[�����s�������eA���^�9��!������
T!��?���������"����Y��D\���&��h��\^\�q����W������o�<;���P���X�B3���w������^�.v`@��
Bz��������0)776��o����iQ�8.��MhJ4��Vh���\��lv��<��]mJ�K��]�IV�����x�r��4M�������� �����
��Ykv
��~���D���rps�(�8�q,���.0,��Kx��R�eG8p�~�	���B�0&��Q%�j�L%n������H�uV5�%U� �DV��lZxA�sq�Q6
3�"���(�}AY�����$�^p&�1p�*�A��u(��]��������k/K�9@M����R��`C���I�@��4�i4�z�Z�A�� ��=Y`��2(2# 5kM�Z{y^����W����
���G�`�X�X��Z��__A52�;I�������Z��{�v�u�"� 7~��"8�>D�dJZ��f�b�{[�%R��n�6�eC8�a@.�J������N�b�l��N9����6z$'6�f*0-7�F�8u*���F���@G�u����b�R��O���Dx�0�������]__^�W���cv�g=�$����+	���������W���h���'��
�NG�>��d�G�����r��;�1�0'�M����,X^*,�K�u)���4��^�[�����
���N;�(�y[�q�?`;P���R����&]� De�*k�I��H������6)l�A����T|HVv����e:m�����{�$�H��.�!9�V��� H@���t��b�K
�b��	�Jw&OW�!�L�C��v#�cR���]���f����/�Q�D�y�>�����Q�������o�R��he��M���Z����&�
�@&���]���pn�T]>$�&�
�c�<i��_Q�>���ci�����c���zM��$�H���`�E'L� ���G+����	@hP�Uf�����S�3am�[C��n�;+>��J��O���x��MKY�D�;���zf��]� ��q)�)���}��O��T,E�}R�^3~{���YV)\�fP�S!���	���5M4;S��N���kml��M��|/���B^��o���|�-V����ky��5WJ5Q��I����b�o���p]>����V��H�s�u&�-������g�����
R���*�Z�.���Z�1���*7wi��2@}�"�I�$�������;��n��r{M���h�!�m��V�h��mU��^��~d�����.�/���S������fBa�a��_x1�@L�
.���������vx��g��4���^u��4����\aT/(�4T�����j�;}�
�|/\�1�e�`;�j#v�6=����@����\R�w1��hq���z
@��Au��/�
������3���c7�g1'��(������5�x�H-�������}a�Z$>�:�@�G�����,�-��U~B����H[�4Y�u�m�q?YZ
�lS�>1���
�f�<w*����*���p����>�O	lb�3�=c=Ws*��:�6X�<�V�$�!����[�;���aL�C���R~�q�������A�S�)��/F�,k[�js_0���d��:Uf�V����)���m��p3�;�r��g����2]#b�[���:���?��v��������������7g�g��I�����ES�kl����?�B�1��bAN�)��W�wQ(}gc��/�����.�9y|�P�B��GQ7jZU�7!b&	�,"�D�dq�L#�$�Oq�DS��gp��=)��Z�����=��'���GS����(�`�,VOXeT0Wi�,hp9X2c$xI������jd���+'e7�K��d��&2_��1�8����}�K��}W�����,��U��}���	�a��}�+��T�2E�z)u"7k���D,����d�
�<s���y�K>&	��(8�Yt�M�
�W��p6kJ$������&~O7�&{�Y4J�o��<�7�	����}��/�/�zV�*.K��h/��r�o�D�{������V8�w;�Fv����.%K�6Yy������0�J�n2��U6�~��>������k��5�y6U�d$�$N������ob.g��[���[�u?t�������/��&�~����_b�K�� Sw�b-C{<1�/��?A�YqF��~���;�������yL2�oc�,����d�@��t_�^��s�d1SGS�+d���C,_��"�����e�.���0��4��D5�t�[�����^���	|8'��'���XRd�)k�������8;4���>����=�tU<3^$Gp A��8h�4Z���������l���38����=�����J@J���si}��=�Iq�����Z��v'h���vv���f'h�wvvv�-h~�u�,���T�(��_����gY9/&������4�p��?l�����~������:��a�}�����iF;����d��4h���s�/h�V���������4��7!|h����'d:����5�<��r����_-hC��c��YA����~��	���;��������<~�:�=y
�z���9`x�3�.�V*�/-7"��2E;
1� �|���N���$���s��A[����
��PJ����F�=��S�"����#���R��b~����/&s�w�O(���X|�*�m[?�{�&6<����A���O��r��w��6�#���P�@���@� a��	'��H#�\��;3���+-V�x�p���h��;��A��B���gB�
Y��g�^����q8�o�K��� .�a���"R���OTE�7A��C����I�t<����$@{%�!�J�v���VD�����R&����c-������f�0���(C�������X���������5L}
�J�G����Dk��H��5P�q���=!�-�?���m���]g���N���t��~E�*{4G'�����|�����'`!mBUM�=��/4��z�g������H�4�����Vy����m���e/�F9��n,���b�(�y0�W+����>L��^ h��@s����Tv�� �vM��%��*5�#��Qd�,G���M�����Q�/��h�D}�)��Nm�0sqf�t;�%�s��[��A���%g<�Ay$^>?�f���o���6�|+���IS��G��VC��jkSk������0���7L�(�&���Q�d���$�<��I�1�E���95j����9�[���~�D-V#�\�tb*O�o���Y���?M~��2q������_����\�1�'�	g�t9v�9a|%�+�=����W����q����pYE��d��D]��i�Q�e��`�������4�����E'���A_P�!�
������4��V���2����pf,��K[�~��B^w1Ifd}��0�����8�	�M�V|���@���y)r%���$�0���������:��d���I���!����t�g��$0G��nEo��M�.�l����������-0G���P6���G+j��{��
���!���<(
{H�S	��h�xBa#(v!G��h5��a���V��Q*�>`|5\7��r����;,t�T��3��l�R�'*C���/�����<1��D� �:L
��dh-�v�`��q}	�B ,���T4�'�Q!o�+-V�2Z����a�
���,�����5W����/��jyD��:�$�'[(iC�R��R�UY�,�[��G�a,i���f��x
&g�)8"�,%��h%
��(Z�>�"VT�f{����V����n�>K{R��{�^����Jh��a~_Wc6p��.#�J��@��z�����x��v�${��qN����@���M���]��~1����h��������������X��?Va�l6�W��C<��D���<���*�cd&J�XGae�c��kiJ��;;{;����o����������������a�s88<������`w����~go/jB�~o�\*�?�=
��M
�1�'|�F�I:o�_�d�����/PblT������"@��T��YJX;O$�<���YY�@��QS��(	�]�S�SW�cO�!�
N�i
�u,Pg�:��G�E�&���k]�ER,�x1�S�$�SA�s�d�peN�-�)l���,�G�����*�wN���N��R4X��aB@��q3����B����&?�t���I��`M�0L���1t��j� >��pP�����[LO��"]h�Q|7�-�L1j��D�	z�9|0�t.�4Ev�Z���.��@����w�$e��-�-����u��.x�i����**���=��4x����Z�Xg��'���p��
��}�:�G���{|�3��{'�����l1�nN=���o�UL�Q:�>.�{���qX9���"������4}�������":%���q�����x�}r�*���/���c2�4���^��;������wv�����N'/��I
~���.z���N0��qG��WzYQ����#
{a��������v�g��o���F#��}l7�"'����M+S���������me�s;�6(���mu�{�>2��\L��m]���7"����)�C�����'�
�A������j_�����;M\ ��Qg���v������%�ee-[��r
�wwW���B��FP���{a{K�h	�a�p��"%����U��N�j_o5R�����E���7�)���mY�Nr�Fc[�W	���T�Z�45h��l����_��7���)Ie��F�%1����	2)�p0�L�-�i�_����x���JL�N+j��QaH����Rt"��1�"�`���(<��1�$*���'S���
�x���E�OM�"�W�R�b8��A�{���&�q������-��>���l
2�S�+�[���S[vY����&j��&5�W��b���m����HI���r��:D����fJQo��R��`�-�;�����@�9i}�a�.���3��:]����z^�B�;��M��bM��P^D�����XG>����6�����1uF�g]�M���R�xV���s�E�#��#:N�d���T���6{������@4?�}�)\N�����$��t�_�#���-�d"�����-�=������dD��!����q��������NB��n�����HbG�v���6�V����������}�m����@�����������bU81l���1x]����W��3�J���2J/�?�� 
�C�^��t��J�G:�!���{��s�W���{�t�����r �JgS�a(�y�_��c�qY�Fk+��'P�T9$�*�a�������Cg�$����O�a�8(�}���*���A_:l_IO�z�i��=:������J0�I:������|qy{v�\��Q7l@��u�/%s�t�_@���tY����4tNQViH2Am�2oqy�����PvMHP�������4L�r;�`��T����1���
�%��N��g!"e23�u�{�u��+>��/��jT�&y�QP����"�X�Y-( O��&��(����UD�����`a�\vG�q�?�����@�b�&i#(h{JG���n�d+#�R��V� M�HZF}U����R>S<='��2����Aa���#�F�� 
'��f�?8�d�S�C�?M���a�=��Xo��h��`U����3�����u?�k��Q�d��w�-�����pg�=��*��'�]4X�)�9��F!�|�d�z)gVD!��6n)^���1���EPQ��=���IW#�=��7@/�;
 
+�0Sn����@}������/��z�������N0�g�}Q�� �J�*Vs\�E�����4��B<k��uey��$���P9B<%�m���3�*s���K��:{�]�s�HV��x��D��(*[�Kt^������,Shy�>�,��-���I"Ti)}K�3@
@������\���������u=�L�:�\���s�?����=������9zh�G�|���>��
��C�S~�0l!���8|�z�����z�z�0A� BM�/F���/��<!f���$3��)pZU���16��;���j�F�����p�d�m6N�g���Y!�'u�^��"�$��ru�o8Rizu�������n��e!����{I���	���g��Y������Nv�`Q���B���'��qfu���nb�	k��q���b	\��y�����&�Da�=d��wJF�bL������`IDz_%���N��P���h�
��d'gh����Q����g�R"C[m`9�0�b	�(`��(E�����OD�V�6=���b��5����yG����G�����((F\k�t$��g��
P�S�%�x  ���x����7�&��cZ{v����hR��!�L��J���9��\
2��s�Q9��4{=�YW�b�Xk*�9Af�V'��
B��F�2���?��)c�[������
I��5�����������e[Qm>��
���L�,w}Bt�n���{����������9~�I����88w�w]Q��\�J�;�����F���F���6���gF��	��$��FQH,|��E�.��.^J�Dco�^�QieA	!��'��K"A����a��.��'�kIq��DD9@I�t��I�
G��Af��<�%��k�	���p��'��Q*�	��g������z4���E���=�!��1�b��_�Y"�P���>__,��<��,��KM1p��m�
.�jw��~��yv��&��i���"cA�C��-���%��Y�>���N��1#�`�,�B��W�Ya<Q�9w����]�x���7��cz���y�����u!`�K�M�"����o�&�;�
��s"C���
�Wj�E1|?I��R����}YH�Dd��n�x^$��B�`K���|'n
�3�������H!s�J>0���w�9*��no��^A]���u\\g	�k]�+���C^Ne	��F� j�Pz�taF5�k9�(\�@qL�������V��^$>�h�
������2��9�"fk����?bW���V��D@����1�+����Z�)_,���8�U�:G��g��9cpK���b�6��A_i�J�C��l` J{yH ��c�wC�����9A3�Z#�F?q�����c ��&���Bj�$�����5�	[���S���lDg���N#�Zt�D/y T|y��e�������7���=u���53(���)��;D�Y��5Tv�$aRQ	8fs��R��P�0��/;�o^xc_>���-��m&p��
v�T�Z�w/3����mD�4�6�������"*���L�e~����l8R�n�x�hm����K*b���I{��������"^6Z_���2�~(�$�T���e]�@��
I�l	����d�9����	�'_|9:��S�I�sr����|����~{��l+_R��Z^���v-�>���om��a(4>�Y�e6�D�w�` ��h�Gn5O�Q�U$7�(�A8���4�k\-��\�z��U��0��M[�a}C��f.����@����L^�"
@e����������;U���}�=�����������5�W�*��n����*�W��\��u����l��+GW�'qV�pXi+0V/�H����$����(RQAo��n;+��I"�U�U��8*x����d���@y�K)d[���-"�A�ghQ�b��]q<X���b��5N��#A	�?���}��2�Ih��7���xc�hl���1	�zb����~��q�����)������j��4U�LR�
����e���O�-z������/���@�����S��Oq��oe�.oa���7?to^_��nJ`�������
�T��
������rw���$�o��jk>>�L�+�J��y���E��<�e��������5E����0�l�k�}SlE���R�k�kU��hbA�-��]Xd���F����h�u��_E�����H��#�^��n(����C�0+bNh�?7�0��I�X������E�����>���
����M\���2���[��.Obm���U�m����1FiW1�XFU*U��
0�h���*�P��b��^�mD����=[k+r���#�76Hd�"�x��������������� 	x]���b����R>����y�l@�>Bv2C.2y7�������(�� H=s��A6�������A�1D��3��j+
��e8H�W�#\�B�>&�>KE��l^�9��i��a�5���-��e�\-(�v�"$L&��#�i�n��0��Jv)\��C6sh���7m�u���R7�c)0��4�T�������GF�(�
�������1�q��^F��"�A����7�9E�������'U�v�����i�zX���d������
��c����Y�>������iV�
0i�H��U�t�����1��+�F�%����j"���*I���d	/D�2��:m�ZR�[A��>�����w�j#��E�_�)������x�E��1�.N�<��=^�����J��S���o�W���H)�����au�%ed<wD��o����ZqQ���~kt�,����A�W��l ���yw<�U����F����8(>���kO����>
�����UB���xz�1�L��PQ��+�����X��E�T`���Y:���u n�x���~^��P6d�m��y�#�8v���\+���z���1��K��$�~g����;��fv��t)��C�����G�6a/�1��,a�q����3��&�:�p�=�NH^-��{t�F��Q��dmI�U7��'�����@3���)�����f@}7�^�����jI~-I<m�u
�c�����6,k'���2��h|������^��N 1�y����K�������l��L�E������|}t�����o�d�j��Et��
��C�����K��.%,c�\�y2C��7n���O���
��|��o��7&��G��A��3eVW�e��c�g�}��C��l0�^]�!"���SI��������}{!��#��M��S>+�d9[����\STb��>�Z{o��V��T�'t|��=��bV�r�EO�`�~2�z(��r�)Uj�C�_7 ���Z!��
=D?wj�L�A�t�}��t��!uV��68�%��&�)!�����~v{�`Sq����T��.�C�3���JDW��=�{��Q%��9�������������y��W�\@r[�x�_@X&�I���r���Qr?	�C5�G_�v<������>���E���1/xh-��#~9�hL5y�'/����\�iZ���������������W����a������������x�
!����b=7O��C-�B)����LwN!.��Z�%�S��u�s�6�0m���JCe����5����(
u_���z����O��Uh���7/����'7oxE�<������gN*��[����c�tI+�����A�1����/
NYP��Rfz��r3{��Y7P�}�v��c�Pgzytz)�4A���>U��64�
�|�
2n����o0�v w�Y	���Q�g�i)��+TGj���p�9�P)uO��p*����%jDe�*������w��]��6��%�����mf
�Xr`J:�C�����,���i�it�s�
a���;V�K�a�%�@����H�XM�DYQ��k=N�����'��7��e�����Iy�������4PM�K��x^a�h��*$�;����R��k |n,���8G�Oy)���U�k������=;:�<~���������!_6,�"�����i�/�*�bO#��	�����]���*����n��*9z���+�J�h�t�U�������su�+���!�F4���E����D!���{J�@�#���3�9����$����O2T����Z�@�Z'���T�����`�����s���Q�mC<z�D9�7���������p���\B������ fK�I�����g�_����%d�<�P�v�f'��q����3�VC��_�J��!{�B����(k1`�g��}��
���7�3�9��JE,��1R|�:���G�;��gc��-\��c*rKr���X����L�(���3	x����[:�1��x�=r���L�vz ��jm&�����*����)	��xq����C�h+(����pHs;�O+!/���Q�P��
���m"PG�b����@��]��;�A��4Y/K~9x���d����5����
�O?��k�5��~��p�}�
����4��.���Xy�+����%��(w��9d�J*�
p�nm�j��Z�`��/����m
�T���Gz��Kax�|tv����|��j�C���������K��0�D
�h=�3�T�W`���%z>Jcu��������'��ma'��}
�m�x���A��1�������!�����F�
�@��-���	��e~�)����������c��� ��^	9B?\[��[����5v���N����H��U�p��YN
�0��e�y�/�@��,d��f���sI�t��Q����f63p���NI����*���*��H<U&�x>E ��u�bi���NA�+���y�GK�$��+�=�
�������v@���������;w�!\<��k�1j$h�3}%�r�O�>C����p�:3��+�.�k��#�������W�� [����+������F�T�B���Z�����_R��&[��!9�b�ZF���Z�����9�
IBi���/F���$���nK�A4L!��*�F�t-	$+�ja�fF�E��]@)���w��X��v������h,v�A���K�����co�� O"��-P6�����/I=X���#������Y
U�M��G�l��{uF��`�{p�����i��Q|��%��2�������i�m�'}r�����)�D�����
l�)l	���v2pf������\��,�4�@�yn��9���4QBf�\u?��������$^���*34�wx�������X\��F����{��ou���8���-K���ph���������ZL����'��`��n��n$����N"yY��������d���da�Y����
������E�:����cU�0h���iL�6�E�[|�}K�k���ca���M����z�@������T/�^�F����yE_�yy�������
��<�<|sr�u.�c��>�pEQ_#�x!+���EC������t�8f���S����9J%��Y�	�
���u�vs��@���T��G�1�/��N0��+�9���D����d�)��I`�/�NN����U_{����K�Q��2�nJKeFO�y�mC���YH��_C��_�o��qpcO�7v��x�Z�8�i�g�R(��
�E���Cq��6�#�v?:6$���p����4=������v��q�Bs�����g���U�rm����D7������������|�(n��&
T��l�J4��p�*�m%?���csZ���4�Hb�*iv��y.b���HB�p#�~�R0��C��
�:�xq���|��V	�~��c+��s�q5�$V�P�������L����)��!4r���/�`(~=B���������f6�3km�X
]�`�D��ac~��4��TT���`m6��f�+5�I-Q�]J.��6��RKH������o~Um>a�h� ��B���m5R�d�!Vg<)�f4���U�����$��q*D�\�-�8��)�R�&��!������F\�uD��B"����;��������y=�������h?�k	+�$��0������G��dCOV�>,�c��3�n�o�Rr��q���H2�u8�^E��}.%v�|D�w5z�d���~dV���a���1WX���G4
�q�0�����Wr�����g��U��fU5�2�Q�L���"�E�c��4�<�N��T��1t<9i�+�4��kS�hj��:���G  ���3U�?�e�H�[3��,���k��	���xK�q��)�,���(������q}�����]<��.�xi�=�	�$Q�T���"�)�-\J�
���k<�H:@w�Tx~c�dq�V\��NK��f.��������R��[��!��v���������4����t� �������z���D�w�*�4@.��m9��4A��6��J�
$r�jT�BI�D�c�����0t&?�'��7��.��l�Z����yHl�_U^����W�mS��/TN�hB;��@Y�{��L����q�S ��.%�p��|��=�x�}?����%�eq���= �#c�1�3I���$�5ex*����q����||��o�f7
�d����M�$�*b4_��s�L{��$'|@@1�����)S�����l�Q��1T�P�#��
�HCnp��IHQ
�$��t'�3BV�����w<�6�H�����E���"��#���F�X�S.��/�E$�i�8���"I:Td����_�s��m"'(mE<h�Bn�G�Aq�/�%`�e�/�:���Bz:�I�Q��$�r���!��A�������]�Sa��j��("�8�C$Y2���G��p�����d4��Q	���i��o@}��D��JHd�9�+�cOGfQ��i,���#���|(�27����,Sr
1]�����).�T�v-�8��-���Kir��$DCA?
�u�#����J��1�+��:��j��|b.������������H��e�����(��Ssq��������_7>���0
i&���(��[��@>K�e��kf�n�����wy90Uq�5����K~��:��Z�L~�-94�S����b��s0S� �8�?��s�P����@ZQ�I^@�s�j3�u�x�z�n�~\O����Y>s���T��'�����u83�������S{y�f�5q�BA��
fG/��
X�kU�$ab��s�B�^C*)
�s���$]g!���M����P;���)��33�
�5�#�y�HM1�%I^w���0�6}E?bj�# ����`��#��.tW��a��[:=���[�������8'�J
�?��@v�IoD2������"�N�qy~����>Zari�%�o�/)����
t���c_�'�}��r1'�M�n.�ug$%�.Pq�l}sg��`	i��)����z���t^saZ0�e��
D�R��}� �?.
5����aj��^6�T��V@���\����+���LC�)�� ���(qQ��y�9����N`������!��,�y3
�
�7���o|v�vfS]*9G�
�M���9_��+���C�A��K5'�����7����2��0�M9?J��P��;�p7��.l)��d#��9����E�5�����X�%%����#��:#8!*�����BM���b,���}md��
K=s��<'��rT�E���E���;3���"CM��:��JF�"��]�)������8��)�Y�~0��T{�h���Q)1�?��@P������ew��E6Q�����W��2i���g���u�YV���E����E[r/z������h2��`-J�fuC���U0��9���M6�����T�eyi1�����C��b���]��g�Ik�UpUr�vg�����R��|XX�+y�8'p�$�`��h�(��R��\��{9��+�C��[��-�O$Z������x��b��u{�i~w��R�e{G"�(X�lPRm���*��0����;`��6�2T��0Nw���}\,^e��V���ZeV����6�G�e
�A{������NBL����VPK��\@8�+��A�����nX����e(���$.0���z�(GB���JY��Ye~+o��+�M����e���l/���v��g������<og�%8Q�C���s:a�B�GrA�Wp�|�D��2/�1�����X�-DA���@Y�	���-���G���q���M�l�i��i�jcFxP�{]�N ��9������A�n�s�2)����b��A��m�x{�$�*D�8�jW/����|��������e���#(o�.u~`g�;���w����#N]D����?���Z�)���N0\-*so�������v�����y��-�t���!sU7=��zH�QH�Qb���YD��%��U�j�N�<��U��R�(��b�����A���X,P��\p]Z����	#�a��^9@Y���NL?4&�m�����f��
aj��RX��P����w��U����HnM�����%�;#��U�>����
3ew���@�����9&��	��>���� ���k�����=�<a8$<l`�<T^L�e�o��WT{��/���]�iC�I���I����)��00N������������"�[]U��]F���<U�X�D��aU"��DI��Bw�H����t�BcQiY-J���A�N�*�?�u�,�(hm6�}`S��s�m
G]�7-�����h�zY�l�����u�zt2/�����,���P�[���{��0�������w�"	��f��^|Kp��(-^�[�.���
�%l	�$���mx�!�Cl��?V
�>���b���3��������J���KT���-]���r�d/���<^�0�
��@�	���z�{h��jv��S���;�g�l.*�q���\���xaC$l!���6�D�K�2�2P ���&��|�S�w�[��2�,�Ma�3����J�R����eF��wYj��(���c��s�.�0���s:�f6�:�M
�J�:M&ws?�G9��~��p�!*���4��C��~F�x�p>��n5���6��LA���W���������b=Q�E��W��A;��TwqD�/	~�`Z�E?g��oH����h���f����6=���kRi`*���A~C��*3��������1�`B�D����CUu(+����[�|���O��YX+��O���8Ns�Q1��D���C�u�V6#gsC�6	�pTP�1����.w<�� �������f?@>(z�����K/H����"���e� ��4r\�A����\O���iVZ�����	�xq�i��B&�S�5��<��R]��/��q%"��Jc�ta-�M'u���S���`�q�h_�)	M���"�+)��r���8`NSJ�n���-	�Nq���j�p�1x<i�%��'��l-=���J�L�^�T���s^H���o�N=��y�Aw��������P��	�c����!�0�H~�
N_���j���A�g�-���=����zj��eG�9��K��D���RYn���"W�b�g&�u���Y��K����)��u���[���p�q�:p�,�Z�m������v��(�sc�z+���J���Qp[=��EvU����8�x������b�x�[\��@�C��f4j�}�@�V���G�H.�)
-w]Xf�3����#�K�X���Xx�������}��W%����x4�^�;���
b���DF�
��S��5F����f_W&�H��<T_xI����E�'�$
O�j{�(QL�����M_4�����,"�����J�sPR�W1���(�]������u��*U�[�����'q��������/���K�\BJ
OM��.�49��6��j6��T��"Th��`^�.�}�&}���a�-h�Z�C\u!EiE)R0h�V���K�#%���.���I:6|u�iF*�|�����.����v4��dx�����+\p���Tf	�|��bh�A*��'�&oem�(�aA�\	����St�i�s(������(Y'�6*~�6�� ����Q�1XxE�A~|j�Z����*s���xzW� ��/����*(��L�)a;�f�����[I3���4,Y���um�rB��RB�'��/_�9����'���(�Z]�����
��8��`fV����b��G!��,){��Mn2F/����
��6�|��:��k�V�z��f��u��%J���B�,����[5�^��3dJA�6�g��`.�|�wL��q-�	�jB+QW�t�z���ZHf+�y �1(T�����&�$9@��iCPc���*�@b����E�C��%�'sCr\B�`�X���T�=8)t�B���*�/Eg�V3�H��}���]�aEyz���W��N�����'4�����A��
K�|��k�$�d#r"�<��NV�N�u�8-�0BY�p��S�O�`z�1����7�W�UX1�x���d3�(�w�BR�zX��_��`��A����0�e�Fc�\9}
X`%&���T�������\�(��	��:f���<K���<�f`)������rME�<
B�\}z��A#_]b�c���?��iccs���/��C�!�y�N�������
p�-<T��B�Zd��=����+A��zly��x�R2e��z�����	eX��!�)�'���@?��� ��]�3����A)�[����Jy�S����mmU�u�u�=�������q���3�H�����Et��$�R�um*�:��S����m1'r[�T/��q�7P%��Z�����7=����}��L�#�����&�
�[�&R�M�p./oZv����g_0���R�Q��WRHDCa������Yn
]\�w(8:��3���o\������Z�H?N�v��[����CV��X����W����Z�'GT�]s,|Lug���:��eR�
�6����
�kK�>,�R�[g�}��R��g������+��b�
�m/Vw}lO95��Z�a1��*IN�m{�(F��@vdZ�:���7�=z��h��^>����+��<bM1��}�����+��Ft���.
*V�
���KN} �-O{ee�rX��0cg���\|6�P�O���>kkQ������:�u��w�����2�Y7B�)�y��?�}�lR���7>N
���CE�U�Y�D��jNV��!����5����B�����	�<���
���X��!n\O`3�.���s���3�	&�:L?�v1��N��z{td���Z�k��8�,2{�i��|?MJ���P:�1�vRm���Gv�,;9skLsAA������Sf5wHe!�sk�cD�"��{��d����Ej������]��0�Ee;zqBaJ���[�V.�'�$=/���,��b���P-1}�P��tP
�����Pd2�<�\=��y�Lj�\��%�3��B�) '�!+�S��Z<y+���/��0<1�:�����k�����\�9��h�$'�t�\v����k��r����*�F�����Y�M���	P�t�B?=��:����Xd�#���7��p�N�N��9�V��
a��cn�X(��O�zY����K�g����p��V�I��0{����D,
�V2Q�����m:���]�YD��pK�����-���C��tw���D��3�����������~�����g��l=\xA��c�D�r���54Z��k�<'�C��Vi����c�	n_G�X]�OOC��;���������@2�F�d�/��l��sj����Z�	������!�_���i������t�%��Li�#AW%���:�������B�U��k��9.�8�c�8TPd���3��xU��j�0�1��se��p-1����t���f��{�����Al����H�����=e���r�b������!��H�^��8�����(�+��y��YE[(�* ���u^k��CO�z.q�+����1k�	v��
�q6
��}����Z�m<�))��@�C>?Ms)�sl;��.m*�"-��aU�X����l���cw�������������/.�qttz�������������i��<����+s{_�+?�����L��/�?�*�\!�W�2F|���j�~{f�UZF�Qp�41�����������9�1�M�
DD3�&J>���$����#?2��b�X�(�da�A$���+�������),���ogAHEK�,�v��.���2��V,��z�Zb�@<�P�p����q�J�f�3�s���^IV�3w=�<������K��L��T?���^7� 5���(����	�`�@���;
�8�F��=��Q�<�����t�Y�i�zi�������t���\�>Z�T&QD��4��tQ����1�"b��\����n�������7+�s����.�����^B��+�k�{MvYa����QU��J-
�&%�v�T�&z���_�����%����$J�#������������j��B���3�]�}.�=M!���o�}����Q�n�5��%���N��)����O�]�9��6m�[LAw�����Dr�x$��4��Q��������3{�i������ p ���a]I�#�y���8��E�@�j�@���sM��@�k�i����
r��t<�N]���'�������vqg���'$��HC
�V���#�(����C',7�C��`�-�$����ZXYI����8N�hX�+N����I��M�\��XZs��SV��+�	���,��4��5���o����8E��m2Q�w�S3Y(�v�����X|�=
��t�qM~��Y:����b���h�}�N������%�n��Vdt%c>�������!���T]2�9�feI�9(Q���Xl�	0��d(��u"}^jw;�dm�
��G��������(E|�G�����J���-��p�doll���,��t�������f��^Z[[K����S�^^]�������m�7��F����H��w�jk��g���(�7'�ka���;�?ju�}�t8���Kvt;F����J�x�dwms���nJ���qv������<����#�4x9NGsj��+�;�"�������5�����nU�]S�^����_��������_�?��}�O����0�6��?U����=����htu9��2Xe��5�#��s� Jnh������l/*���_�����xB3��p:nehe�t��e��r���f�tBtwHZa�.�#]H��b5��u��;��[p1�N%������SIR��t	~�C.�{1��x�r%��/����^G�:&@�/����A��^�;��t����h��Y7��`{�5��e��\��s�{�X�#32�G�����u�@t-�b��5>n��#���c���tKZC7���z7����B������j���[��fN��i�!�p�@I���@�M1�/�����bF���?o�E"�i�<C��{����;M_�@/lF�
=�i�K{#!�w�����zs#��#���'��!��.|B��K�2�3���
����K X�T~l�\vX�o�O���T#"��dNK|0
���\�O��k�G�t]Z'�4��{�����F���W��$J�V�5]������ mW��HR2@![�
g�Y�N�S3��Z���c'L�tO4���W]��F���0�������:��]Z~_���eHI~)$�K����eMoc���{S�8����6V	���"��(SJ7��F�p
��4������'>�U4r^�T�~�DA��`Y�x1����N=���)t,?�^EU%8�����7�H�e`�����G�������l�f���`��g�Y�wfd�������]���~yq>�<R+�@���]���jl�7���O�z��f�+~�E�X�{�(O�a_w�8l�V�X��P�(����VI9�q�/j���vK�U��;4y8����b+lPS�����I2�=�)0��7���#t��i7����Tt��;qO�����)�v-�W~����������f��J_�n������q��?�.&��K�W��_$�R���N+��HO��e���R�[��y��mKf����<]R�o���.�F��a,����%���T�!�V��B��H�U����If�N�w������L11�}�}~��?��bvX 6��Z#9��s�=w�ru)���P��t+bBs�ogy�2b�0in�0r�)�����U�s����g�u �q6
TZ{56_��Do�������|�H�yE��lZ�H�1%�R��D����,�
]�u��[�v�%��+����N���k������/ax%�����jXm�>���t�"����5�kJfU������)�����n�W��7�[p���u�Z\4�{�f-b�U����:~����m�a�5�5~��8�e����+�e\AH�����3��{����C�V��I���N(��e���p�Pt�J�
Rw]/��[1���i������]a�>}�UzwK����r[��f���\���j}��:���j��r6�?�*1�V����h&f��� ,���P�7�E���f�D�r��W��b���U�������}b�����UC
�����z��y�����bA����O�^Viv�5���a�b��o%����u���?�Z���.&�y�����1{}���OM�e���F��	V��������G������>� B�7o:X:�T�n�>A��^�8��Vc-�l��[8^�*J�������}^|�\wZ<��Q}�\���.�U���
��:z����;���#��������l7��X���[�w+c�3���f'��pB�g��V��=��v�I��	�v���.o���"t���>tG��%�^�"����1�������A���LY#=?����p&	T����v��O+aY�t�L�MI�<p��12C~���y,��bv�"8-x�O���y�m-_l��*N~,�]x�D���W�~@�vC�M��p��v��aMU�W�����b�=s���<Ps���������-�f��Y�-
+�`M
a�����u�b�=!\e+�v���&,�=�����[	�v�o���P��Y`9�Q��U���
P�����-����"����3fT�������W���T_R�b3�t��n��(�����-��8�^�w`�Y��?�R��;�o?�:0p�3�~���X /Q��R��*���wX������+���Ta~+��
����{����_���7��g�Kg���;��AX}���/��.��L�M%�,�&	����C7�����;f�:�����d4�0���d���M6�@�X��b
��w������y@�4��OP+Z��X,���oq�����(�Q�x��u��]p���j��Ms���xE(9/�����B��	&r	����a��<O���A�b9k0�z�8�ruq�$�t�!W�^��GY�)Ju�2R5C>��w[����m2ar������d<m�=���Mi��Ye�vK�g$(��$��.T�Y�w�(�j�6���������L��v��M���[�1\�.���}����Ff^��f�=`6��!�@� �!d��bG��������������6�1�	B�/gi5����I�`�,��A�[��~��?Y��~:�r���B�	o)�
s�S7�������MI��n���%�v�Y� w,(U�$�^439�T�jD�F^t\k5���O.��8�L-�]�d5��
Z5���H�>|���E�@�/�<�E��>M����nXH��Ap�\��*I7��������\Y[�?�B��������^�=v3�]�:9���4~[�5v����S��������p(w-H8�T�T8<����w��w�G�{�=�
SS�k|��n�k���.�0?�y
om���x��S�����H�7���w��p����0��_�u���'��
�cp�?`��K��q�B�"��6&�B��>��jN~�o�;���N��Y�x|�e�"�}W���W�j�kg�����3�lVJ�����=��\��|�fU�-��Y"���S>��<�||_�bg�7��yf��q�g���Y��+�����J&=��G�����oi
�L�~����h��q��[��i��"�	�r+���#����\u�����U�'����w�9W]�XP�,�?�b��JW�I�6����+�Q����#���+4�r�-����@�j�����=����c����)�����1-��j�&v�~j��E�������G�"��DU��UBWT��	��AY.��J!���*
�V���c��Q������*E9��������N���U76����=S6�%��<���b��*j��Y@s��K'j�B
TO�*����;oW#��������a�������o��������k���ZHL�7
�;�*?��%����7��t���5;>���?��gA�:@�TS�N��������	��7''0e��?�������������)����[�gy�Z�R�j����t;��[����sC|�����!�z�����P���+*����9�����x�(��9��<����M�w#�����P��O���w�3zn�D���;q4��[�3F����Y���q���C.v�r�L'���V{�����+����t����;-r���.���Q����"��asX|1������V���u�m��t��z��_W
I{U�VT����[\�n�Tg5@WW���~���n� N���`�����a��^z����A
��0��0�V�8��,Kih�����r:��P�]hAP�i���g������U���@����~��C���}����P�Fg��P��F|���;������%���4�j=��E��7��d�
�X��B|k�wa����[���9^�����,;��Y�����F��hn���4�X�0FI)�C���;	0�=A�V[���b=.�����0��aS���Y'��r����{�<��3������i6/S696����I%%W���,@�}����N5���Ez�Q�ss�g�e�v����/v�2��4��,����yB���@��z����/6���;����������nr�����;����u����i���s�(�I��G]�� ����L�`��d�a@����&�������H����l-K3�XK2�f��v0B��:^��U�6��<nl��wv[[�=�e����I����g]R�Y�<��&���[������%���HA(��������eG�������~�
!O�%�?�L<W��Z
��p�����,9��pp��J>/%�0%� �`����u���9�8q����k:�
7~�������cu�]�Z�i���8���tA?�.�.��p7�c��x�`��r���`��'~d�@[����3UZ'o��H����Q�6!���W������ �{�y�k���wG�h�3�G�Owb
����eF)�i��.���F���AC����O��M6�2!�A<g��JNG#,)5�'L��gZ��r��o�KT������/B�^�@����8����	����e"G�/��~u�_o�^\�����1)-��H� �5d�FR�M-]�6������%�:�����|4�=�C��ln���o�����V�*��?�E��������w��;K�_�YR}����F�,x�-Z�-#���J�l���D��O��e�����.�AW!5�W��n�z���*o��kj�����4���t����f������je��/��c�
U���]�>�1?�^>��j��OP.��kS������e.��)N�U�Xd7�d2�B���D��7����{�N�������}|4��T\F�^�@���d�Qol6�N���v_�����'1��d|+
/���N6i]��Nmr:�`�EH�\n�$�'Ov�67O�3�'W���?^%/{��9$�g��p�����p:�S�v�TWr�R2��G8�&����Yy��A7���7�\�^��\@�v5t�6{pxxt~~������P�z�|���^�_W��MT�������<x�27�y�htu���9=�|��w���C�M��Wss�
T��bT'�6���}�RJ��F�8��^�HK&�J�G� j��y�l���H�"}�F&�������/^�]�����%�oSu�$���V�ic��wf�;9�X�c���I:�Ml`n�R���j��(f��>xEU�y����`���6%i�zc=I�M���}P���}����i?#���/�G�^/��:�DS�t85r3���u�g�����+�����3
u����Jb������@���"��
�q���r�9j���|����?�+�y��2�e}���eg�,.B#Wp2�7��0:5�:N)�|�S4%4���N�3�!�/�%�V=�b�.�:�)w]|��K�f;�+7��^'����W!u��o�k��m��U��C%EZz����Z�w�1���xr����|�V|@��&�^�,w����w�R��Q%�8��C�-l!���1�e�L�v_���7W�k�-P��&��
z��v��_�5gqj7�-��t4���{h����R$��c	��'�k������mM���[�����[��������J6~X6��Y&�-p�('A��2Ou���)��JK�H��H�z�*3�3��2���Q�!X��#�I>`�t2�q��=W�8	�!]�u����!��5�t1�&�f�.����D��<� �O�����?#��{�"�����D�rMI��(|u�>����=��-����)J�h��d�+
����!yG�g���}�	�\	��2GX�3�C����"��'���u��g�z{�O���o���
Go���j���"���
h����~���<��2�Z�S���x(x�4�'�^[��� K�u��]���i���6g�p���(�^0��u�m��|����Y&J��.����a�"2��k��fo
d��A�X����8��?u�t ��^ �6����.���	L������,-�L[�P�*�y��$	���+fZK��w��3������sI����MD���H���i���1��nvu�8GT�>��6V!���sX�]-\������
��C?�������1�Y>�E�����g�]6�O�h��H"xz>���*/y�(���xCS�&�h	�e<����Q��~>?�����%�)�@�����+o"������L��.�D�7p�M^��^���8/4'��	_f�[>�����O,u����+L�4!��_�A�����A�����������p�U�
�2�\q��Y�x/������gY������v�TR�6����U��� �O�s�KIm�����+���b0\I~�Q��A~�Yw��$�%���.���4P��4G�S���@��)��5��b"�'dKA�Y|����z�h��wXw�=Y�����$h�-��H��
j���J�J:�B���������0�Q���d��5Y������ze�����zFL���S�����U�c6�
�<��������*G:d�k�Ed�C�J�5���f�./��Cq��Unv�L<a'7C�K)�&v{���^P�=pE���<�7O���� �g��/4����
����Z���$���`q77����ll��t����50��*k�d��(������Y�b�
��r�����"��5	^���*@��a����@3C0�*�8�a�BxV���W��q }�>���x�
�o(�Ll�����ymkgmk�OJ�zN_>�������E�G+j�.�������Tn���GW���q�i��e��Ios#]���q������h���}����Ne�T��h��w+��g	�B������+��1��?��p�a:Z.��=��t#�������e�;[��{�X�l��Z�����Z�l�b-k����?��Z"�������m��������6�~]�6fM���^��TX(���G���g��T������|3p(M��7�����hE&����J4���(92�|��y��i6�K[o�����:���"|�H�33��,f�������� ��jh������@O�\
�V�w�f��r�Y.���],��Z���Y���y����)9~N��t�����s��{#�,�Q�eK��7i�slf���������:9�Fb'�f��7���B�J���������N��f_�Q���F��W:S�J��^���zSM^��9k��]~�_�V������z��4�2(<(kKy%�U:Z��R�T����b�����_�v9e,�a���������FU��h���:%si:e�XN8�n'�d�&�	{Z�n4t������kD(���[�zj����I�\n|��h���� %���
!e�ce��Z�J���X	�k[d3ZD�XJmGK!���G�X"�b���6H���6�P��H������\z?����:����	�O ���O�$�;B�+��=!	*���vH
�
�|��B�^S���k0q�b{���p_P��
�2w��>��Q��m1��8�P��N��Gi���or]�k����n6)���F?I?�S;lk��S*��V�����W6�>�����L�[�lc��a������as�:r�C��P�@����xm��u�sk:
,vI��P6���\��*�S�)(��3B�`#�kg���,)�nm��� 8��]�m1�1.�:y�d��BZ��������3Hy5��>��R$#�g�+j,P��&T6�B�����������on[����.�m�A���h�7���_�q�P/�jHz� �9q�+H*�����Vz�����D�|�V����s�(�<��B����������o}1��
���������m�������^�)��L>��q�� �;��y�N9M/�����9����M!��F���~��8E&v�v�2� '���EO<�
����-��
f�%a��Hx,���g���!�����j����������$x������%��*�4�l����UP^Dt��6���K��L�rL}�9D��0���U�Z�.y�����xc9���������//������pJ,���
"5��@,	�Q�
��u���C��S�J�}�m��Bx�byA�W=+�r��qA9��������WN"�����.&Av�8o#������Z����a4)B���-
��m@J��#C��}"����e_s�Ir��!���kb��KP����������]��Q��c����i�������x]{G�_��~�������i�q.�@k�80�.�(����T ��������4���Li%�Ef�I\�j�:M[�}�1�W�j�a�����#���f����f6���w=%�{.~YP�zRy�������G�������
�K/%-~�2
L����}Y��*�b�����X�%����0l��h-���	7 #tu�Y*��q�Nu����W�����b���}w�V�~�gu�"�)IO��1���/|7��F8�(�>��������^����\rp5Os���H/(@�i��g�����t2QU6��u�;����)��V�u��,�w@�6�@�a�1�W�/��a������=�;U8N7����v^P����u��$�yZ8tl~��Y�����'8"�
Cz%�`!x��\��7�xV(�H������x���s5�Y"���'!���D{up���a�D��M��G�b6D�4N��;���"S/'E�`E���T��8Ko�n�Q��@�a�!x����a������6����5|t��I�)*Pk��S�&Y�w9���y�r�F`��
r^��wo��L�k@Q"�Lc��6@�0Q����:p��)s�00�0JL��3z&w�/���-"�N��E�F���m���r��R���`&�|����K&��Px�p5tJi<����=�>��}4�
��x���%�����p�r��P�M��f�u�8�m����E�h�����^ ,"�{�-v��\�8��F�	��`%nv������pB�2������&5bQ�+D���h�D��P�/���9������Da
�+�D�
�<�/�K(.������4���
����#R�r=�SY?�(B�b-����q��!#F�0m�Pg�3�t��[��q}��<��e���~U�XP����:�����vP�mH�a.WixL��t�Ym�q9@R~��;G��yl�J|����vX���.���'3`��<U��UZ=����$�4�A���[?Y�B3ap�!N�������TH�[��X<��"�^�����4h"`ih�8�
)8n���������}������vcgk�<8�XC��X=����loiWu?f"y���x�X\F�����z�Y��HbF��y�T�J���B_��&����hvH���|���8~wr���	��9��J�\��}X?H�QC���� Ha8xD�O����YJ
?��SI���s���g"A=L��m�Q��Wlm8�J�r!�/|�Z[As	�_�@z+d��AS�D��P���*h�����Ls�D�A����!�a���R��0�F8��+��bt
B�������{�t�3�2� ���k��X�s_�e�/i\��Tj�0S`���H�k�n��<�ka�xe8�U	X�����;D|�
�
���r�e�A[m$p��x�A�����k�$�G��7�S��n+��T@?F�����RCjp6��.DC�t���

�~�>$~��?G��}m�+>��T}dGM��w�a�Cs4��k�"������-|���P����w�AzPq63��Y|G"q��BP���n���!��_������n~i���J����������������G�w���w���\PZ�������o���>����V)��������A�}lV��<6�W���&�C��?����B��x���
'��X����XZ������ml46��:N�Fpm2\�>e��$[c:�Z��n�1��G���jccww;���v6���������I[�������ll�6vv�-��j�VS�BMW��^w�a�7_���SYb����re��xc�q����i����w���������N���k�l�5��,91���a`{�����d�������P�����4�15_�?���g������o��k��%s�g?$���Y��M	�%��6�~�y��n�d�|�����!���D465��'>�"E��0�j/�|}i�B�Z��Z�^��^�yK����3�E2�*�l�|�������� ��8���}�:����qv��XP��1����O'��Ika��
5I�(n��[r�N��R�E�i�f~�H
PiJ��0��ao��E�7�$0�����fH����W�nVt�7w��t0���c:�n������+/G����	��?�i��R�Q�9�t�f\�G����������V�:�5�|]o�k�{�c��q��o
�?hn��9h�������������m=��j��k;��0���*��$�u�n�n'k�
$��)k�����K�����8���w
W�g��`�6���������GvE���)bIq!��W��E^��]S|��g-��Z��bs�2%��`:/?�G@������IS�"��:�{���x�3b�@B9 ��]O�v.�?_^]�'[{I;3���f8`6s����f�

���K_��FK�iJ��f�b��{�H��������������B(�����(b�tH�p
$���Y�G��i��nw�+X���~���K��g��<y�4�]Z�a�B`�8��kb���6�.��*q���@�2�xe+���?,�|4R����_�U���4��8����p�
�]�X���V���r��g#i���[�6i���th��Z����Tw�Xm�!J���*
��_I�~v�,(|���:Qn���V��UZ[Ovwo�2TnL�3l�}e(s�,4��~wG�x���V��q�����n ���G�/�_��4�x���j�0����(��w�c1�k��Aw��Q0��E@��9/�u������D�2��Qkl����ss�rh=�r����}����NO�9�������7/�������]���yyvt~n3�}�!�%��Y����up���
���m7�8����d��{o�D��~�L������l����4��W��9���F�L���e�CC0��zs�@
x�5���9��?�vv0�y�Rbr������0��g���'��xF����=��2��4���90w��V�9�Uj)=Q�r�$<F&���.	w��U����\�����ehw`�mZ��o�%��p��zS�nx��M�_����c
9����J�����Q���A��_p�B�8j����r��������������au��8k�p�e3��4�[�,���a��_|'����'��D�K���L>��^�K�P������d��gxq������-�W�Ml��w�t���Y����K@I>~��&��f7�!'�}7w���tyEx��mm��ll������fk���+)��~1����c7����M���q����hv��[_$�Y�9B���J,�\2b��|!i���eo����|r�E�U��������"�|it1&��f���'���7��v��j��U4���@���T3��E� X�2�]�{dgE[�B��45�u���+������M�.u�)3��5�xX��="C�������5�A��C	a������mR��glbk��D���#lV�V��7��(z�<�A���[�m�������r��@EN���i&
)��@5�\��etK��R��"���'�0o��tu��d�j+�/�E�
����`�<���[���vk5-�X���y��T�k:����K:/���!�,v�Yy����S���%Y��;-��l����/z���Ld�h��r�m-�'�I��,�*�t%�	-�����8��'�����D"���Pr��MB��%�Du������9�E�#9s�)���gqt��X�)�)�S�~�����
�r@�O��D6�/��I�
�I����Kn��N�'����C��P�)HxvtG�%/.&��a���~�G�5���4�������0�����A�����]eV^�}a��<V��UtxOv�vw���������u��+���N��h���U3�H��3�S���2���}���4�k�^J�l��S�@��6�c�(����(W)����C7w�����9*l[�Cb�`�b7���5�~s��g%u
�����xRn]�aVa�cU��UX/�B0���AK��w@=X���;p����"����[�
�ET��H]Y
u���l��&�dU<�&�a��{*,Ud`��L������=��A������{h�fpk@���(Z$��<x����J���Y����p����
	��#j/L���R�������#x8n	}$�
:�h�,G�~�/�'�V���r���q�u()���1P�XB�U��t��)m��A�U3�c�[^��$���KesJS%������Q����-L��N�c{ma���=�#��|�r�!��n|���E�t�4�=Y&�"N��^�A(`���65}Q_�32
X[�u\I�����_����MjQ^e!����s������\��5�����7��f���5�?Uk���i��e/Sx�n��0����w�^e1T�k���o���i����e�C���T����5���3��73�K��*�H��}�n�u�\O��\0��N�y�[�/�.�t�C�1��:���0U�y��,|�2�����<���t�B@�LXM��Z���x��k�r�.�Adk"~��W�m���3��/�[����	H����I��.V��������.~�y93��$?HL\h�����n|�L����<�f�ZjT��<m�.:��~a�X$�[%$��?J���9��mbD��Z�4o���A�W��Q����~o�x�}0i�Qn$�S������U��n)��}L](�\���A���b�md�g�:*C\�6\���]���:�X���r�tM`���n��d�Sj2;O\4���q��K���n!�����|�5�E�����k%���5�z3L�(�V�N����
/�����F�������fL���a�{��y!p>�X���$���Jx�Xls��e������mBy����}�����I��R'1���g�9
l*���q�x7H,��(���&E9kg����d�zk��}�F=�����V�XuD����a""k���$�L�,�o���;�����A��(�<f��JL+�z���8c��/����#'�r�Xx #G��q������;@Y�i/[nn�i �Obb�#���CId��%S����8����1���������5�C�[58	�(k���GPB����cR�����d""9����p��tO,~
��l-�E�658VN�I	���"���D�M���������l��G`{!��)��l���� �FF	sk�#��������7�E�mu��v��'��g����[������=q�a����Yp�PL���=����"�;�.�cT[���8�K����'�_��4�Dd�;"��t8v5��O[+m���]�O������,{���:���I��"u���!�F����#MW�x�3��":(#�D�y������5{��Q���MO��w`�R��-<o~.�5jS	���������N�ZH����]���m�#� ������g8�I�{Q\zI8�6��c�0f���ww���*A�J,����oz�d��t�x�����o�$?f(�M�jVg7et"0\���Z3����
��V
x�S�L��x������w���l�����#��`;��x�Z��5�	����t�&��h��9��4_�O����'��i�6����73�7��EI���Vw���P8��HV@����~lZ
)��K�����������}��:�k��[s������_��%w`�(������.�zA'��B#���A:he�7����>�L�M�P#��q�����;:�W�}�������9s
<�M�	4�	V$��"�������V����"� M9������.6XSt<lOM�4���i��.v�&��h�?UfJ���0illn��g��?������v�s2���#c�$�����a0�6b�?��f��e�,�M�L�n�B
�UW-b�(lf�j	�KS��9�!�2�f�V�`f��		�e���g���O���
��]3�-���.e��
�{�����>����+�_��X{��D�zK��m���$�Tl"�J�LR��je����k���~��E��I�V�����rm�Ss=F�:`{��uweqE�D5$[��n����yY+�������S8-�<{(9���0T��B(���]8Jp[�Y5dN����lM��\��	ox�8&;l�<5�,��'��`6���?E�����g��4l����:R53SV:kV�����;���Y"��n�'m^ZV@I�
*��%]yfb�9*�_Q	.NdS��r��^V��%e9����@4^ ������6�>J��n���M8`�"N*��G�,������g�4!u�]��[�%Z5���g�z��Z�Zp�����"�a[_��ca�5�$�bTW��U�� ~��S>X�pv*���N�&G�&���#�Y���]_���lY+,�����dq�.d��q56����r��Tj{N���ye0�9��8tr#�&��f�Z��,5�a��cF��>�m&��������p��fA$����&��.�����%�8%�y�;�Z��K�#W~Zj�h��&�Q��$�$U+���$f������<�	��,����������.���B_��_�L'��2��1u�&aw���G�G��$�G��sA�3u��rl�� �.�i�q����
�Y�q06e�0���,���<��'���R����������Y�r���)(���������d�J�3stx�~8����+�p���Z�����s4��?)�����,��u,7��Y6P�f�0"��<|b�-�#e����
�Z|�l0uH�t��y�	��
�]������ yZ0���>jFgN����Y)Y-z
��~�2�)8K��\G ]�o���B���8Z���
����}D�@po��b�.�r��C��2y������&�(��Vh#'%���k�@n!�YQ�G��W��LY�U�kz
��g5	�+F�+�B@DE�A��(�~a�_������������Cf�E61i�*ld�>���FF$g9���B>�&_M���"��s��+��
�fz����;�����w�/��v��?�����u��an%i�H���!$���C�Ut��&��1�)���5�9Mv���y1�M8�bni�0��cU�[�C3yP�a�e���XF����Rmwl���|�����)��2�o[w�������T]pb�e.7��M�r���J���$�w`-9�@�P��$�kL�X�:�ysiL��R�F����1�	�2��`�1}�����)v��v:���N\D8����+WM�a�g�j������=�~�d_�M��XJ|�c.�a��R-������h���&�2�.��o��/�1�
\��YZt��~F�b����o�%���nU���AQ]��{�=�J�e�����_�A,	��O�j�Q��r�5:���(����7U�w�nk[���PB���#H������kY���B�����U��i�e6��PF�U%O4����K/:�z�K�H#k2�v����S>���t������f#����Lt�?�B�ITm�5���q���Xg���w�|�A�m�RD��q7�>��Hu�T4hKb��%7���{=U��kX��+X���2+�2�-���c<��0���\�r<;[�s�Qz�m�������S�G�UNs����=N���e�����@
��(���+�z���(,0m�#���N����o�S�e����t����.P_>5%B���PZ�s,�[o@vBm7����Rh����U���#���Z5�L,�sQ��]/�5o���_�S�Bu��j����,)�Q�'��p�l�i��Q����������}g�P	�Sa�4�1����p6���M�_o�l4����������)UY��&�P_��������]2���n� pY���!Z7XG��P9E��n�b=��n�0��De��,Ln���7� ���C++�t4��2v��sb��
J��f����G�H������Sb(K�W�EMZ�� �n�jr��������htX�����U&��;���&�!�2���
@&���6��
������.�X�C�G���
_�?$lI!x/�/����>K�g���&B�bu(�a����<��������p���q����A
�s#Z
C��g-D��	��p/�$1l���#��!���H�sH���5P�"����B��Rb�8����^-��>> c�?���0�Y#cj�4NDkGx�����L��E���lf�����z{co'M���D���x3�,��;� ��+*��tW���Y��VM>�f�!���������7g�/�!:�����������g 
 �y���F9��%�����������`�h����bg��(������^6X�iL������olH0@�R�FJ��4g?gj���l��
5�v[O�v�JkP$PZ�!mmS�����_�_�E�TEM�k��{�:��p��1X��TB>J�+������b��{�=���������oh
Ang*9\F
�t7���%[�����F{}���n��FVJ
�
�)������e��3������@��B����H-��$���A�Q\�?][Tn���m�Zpm�R
�]������,�T�z��i����o�[{O�����������s������U�j�2#<n����z���(�\AqR��-f�H��9SiIRs�px�3X���T��<kR)P�L[VZ�����s���E��L�O�w��l}}���<����X�LR�2x���7��3������������mgu{\dYCI��}1�'|��N)�.�H�Qek�Pz���N�X�:�!�;F����#W
�V}n�����84�BJ#�?R���j��viZnZnZ����g�GT)U�y�T���f�LU���u_<{u~z�s��"��m���M\?��K�N���8vyJn�0m�4���N-��U;;zurpJ-��$}W��Y���Ke��8x���	����]]�^	��7&�V�	���I
����MS=d
����3UM����K<A����nU��y���]�1�>��3 �&��u�g��>�k�g�~����N�k���G�
��������X1	h���]8W���>Q����	�>Y�6����~�5u~!m�l,�����m.�x���a���j��Tl���?�V�n1�}�g�B5��H������H��n0�3
'�9����>���?��	�!�3Y'��5�+�����Zy�5�.���������s>���"�������\o��Op�����tx>Y��L��G�������A�,�C���������'!A��T���M���|�;������9_��F9!��4����]�5�~�:@�<1M�����$S����"�f5�z����V�������q@���h^+��%�k�k�M������������K�_�����}�_U��'�l^S������qm����|����g7w���7������%����^^_�C��~3�q.�M.r^���������iViTA�b���^�����;[��T���$���%X�����O����{����V$���:J�^)��o��w�Z?:���\B����WV-���riP�������Pj���	�3\}�S�5��d<mMT�%
�����N�}2�zH~�zP$�Kj��I0�f�AI.����SN��o0��n����g�2c�U��k�$q�-e�C^!D�������2�"wU���a�I���n�:�mD9n=���.������U
�?EJ�����������3~v����E3���C�?�u
ua�up8*Iz3�t|�Br'��n��S�����X����2hY��Um����G�"��Oa�z>SI�N'Q':h&8���a����g?��M 
>k�j��L���D' Gqm�f��V���W�U	H�>����V���/}
8��s\|�$lN;e�S���a_�����N�
�!>s��1$gX�*G�MiU~"�"|��i=n��J�x��Z����Zf���s7� �1����V1P�s��3�0�B��U,<������-���~4�A�vp���B�A����o&��7C������ ��KH""S�.���x�)1�M�L�6/����.D�@��zd�]�)���w��"�s��c
-�p%�)������x{d��J�8�	*(����j=im��j�v�o�%���R��Jc=T�cN
��Gg�^i���`6{����"z�����s���p;<�|��GA���b!��zqtU?�E���u"nl�5�no����8�=I	��QO���WI���0�Z��,���]��Nt��z�o�m	7��������_�/.�������I��)���9��Q���rB�/������=���b�Cly	>�>���'��z���#�K
�Fs��k��2k ���Y�����B�X�W���P�`�W�6���������Zm���/J���h����p+yJ�W���O �z�TC�q�,�
��O��w�*����]�6;g�@X\$q�A���C	�
z��]���f���v>���<~D�$����YN_>�����������q�:�>�(1:LG�
�	��^�������vs,'K����[������?#�n���jmb�pk��x
h�D�`�����Z�������pMicww;1�6�v6������an����������v�������o��W����V7]iw{���i�|���{NcI�����C��{��[�������l��66��:�����f���to����s�<%��dc��_�i�s	�����Cs�%'��x��8�o�}����~������%��3Sr4mm$�q��ds��F�C
�
S��������������?�x��~��`��%URH�Ti��b������R!.�^L+��������nFa�KF<FW��� 8�%h�i��j'�t2p��Gn�.��}9��Y�3���$i=��P�(�$�-1���7�%�|)Pj$����A>\AF!S�
������`�0����T�T�js�sq�m���f�m�n=ioo?.�c*�P^*����]�R�B8���0�����[U:hs�h$��)�����|b���K��W7c�a-a=;m�8OvQ3�d��@w�M�|~r5>c�:54jH_X�}dQ���e����T�r�����m�����������{Z�@y�Kf�Oo	SV�m��!Op;	P�+�-��TM��M0���Py��	������$���-&�9���#L�IJ:iwlv�����cw�����a{
�m|M�e�����",4�^��QF��S��G�b=IN�[S���b��7�U��cn���;Hb4���1���5�.gm �:�zC��'�N����N��'p�����d]�p��%���'�%p�?�������R���M�S��I��
n��O���B�$��:��yjJB��C�/�a����7�g�93���������t�����`$�g����+g7���q�P	���n>ntRsb5���4p0��9g��
��1({f����x;�-p�������l����Q�Fmo�������}D��|
�gy�������"��E�`y�i�i�~N��/<��}���N���;����DZ��i
O����Hk�N���DZ�`%S8���z����g�d"^K����3��o�H�������8C��5������J���,�bB�������n�����l����-�P��d��4{�in=���m?��mw���fs�������oon������������{�\Z�m�H��$���J�@��!	AJV�������@�d%��W2��F��}��y�����6[��_������9?�$9?���w~#N�����,����4��"�h�ju_5�N��iY�*��fI���#��T����5C��Q5C+{��}Pva�oq�-|P��hQ��O��7�;S0��+x����oN+r���
�r��9�Pt348B���1jq�g�3��L	��>�Ol7������8�0��S6'��q���T4��������h��r���L���}��{��t�-���H�n�V ���(g��3�=����� ,}?G��rs|��(����4�|E���O����������|D�><7�4S!$c ��2n^�/��^c�����������b�a$����:��������5b�V�����
]����Na=��bt���.w{�foP�
:�������d���@����g�����8<� ��{���x_�8^��%��@\��������C6�
�5�A�Xn�������t���[ �CY.c@{z��R/�y"��AW
���m�Y�K
t����������q�T����M��O�Xg�.C%|>P�K�0����a�xxt ${��\�N!������/v�]+�T���Qp;��v��;!�o7���y�\���;��;���� ��(�����x���y�;�jg\��+PVF+�
�is,���"��I-	M��g,zM��������=�j�����s�^�����G1$�{��
��*��9�!H[x
!� :8qZ�?j~��D<
Cy).�Cd.����XV��z�&�E,&cQC�n���	�J�l�"�+�n���c�����d��|�0��~,��(oKW*�i���S�^���8�h&"Fa�>�#����y��S���y$�*=���.9=���]�}I�S��Q	M�NU�zi�<�@#��������ZJ����|_~^p���Np��TmL��D�Ov�x&US.��,I�d
E���#�}���F%�	��F�c}�^�K���8����E�������2���+�'����( �z�6��
#��f����v%�Gvc8���/:175����%+�1��C�;��r��vA[`��cD��#���*�R?���oa�^������bx��bu��w�eg*�����+�k��xN�D2�[D���
Cg���}�����kA�YU���X��p~K��\M;����3�n`��l�M4�1���z^@�=�=���}�fb�'XKf�����:�:!�����f��V�c��ig3����?�}X�3%�ci����p�N�O{Pi��3hVZ-�~�������	g�����S�!�W���6���N6��{��j!q�U��7�D��f��q���L��&��i
m3/���#�Z��L�v?��~*�U��X�r.����B�"�X&s��&c�v3$��r@W�H�8����vF���2l���W�]������"���8�a+�_�M��J�J��y��#����t�(s�f��3Y�4H�lJm$����r!�j��Z)�A&VH������������j��h�7��tN����,���*G./��T�}
a��yi�:�nq��yy�:�n���y��E���U-<��E�|��U����E�
X0;�����b��9��N��M@��K�;Y�*�b����s�����w4	9{z�5�q�;n%����t��Z�=�N;�|�]����k-�{��s���	T�C�����C-d��:���p�x����*AM�..�.j�x�dr����g��V��
���c�QA�c!.
N��*��kU3��D~:<�<�pv���/�(P`����K�V����7��	��bz��������F�� �q�����.�Kl��z�N��������1^�����/�"G���#L����W�M��?�g�82�J�eb��;�1hu��Vk�f�n/'qD=�NQ�0������V��/�\�B������&���������d��Y�u�\�7����N�\~��N��&��y�z����wk5��4��Ju�^�T�V]
�?u-%M�G��y����?;��<N��H���9�V�e�v�!9���TRs��l6���3�xNXo�}������Gx��fV���Z��0CR��R��{����j,�6�����>�����o�����F���=��_��?�v�1�M�������{������;mw��On����Q�roV� ����p��8m�?�s�����NY�=���k1~�,6D����D�C2E_�2@$�5h��e�;d�>��:Uz��>l�)K�A�7�K�����ht�m����(TH+%�! �<��\�����"�7��p��v�C\]Up��P,]Y������Cx	�&0GQ�o5���m��*��PWe����+����2�������w����7���
����e�W����RB����v�>�'�Z������L����$��������P-���P����_AU�����������.9�\%�:��?��2���U���@(z���$~�f����<�8 ��l@���p�B�D~���:���T�D�,ncD]�*K��A^E��u����k�6XQ�
["���G�T���BL�|����m�qvh�)����$���-��J��J�_�5u@�Uv�u�}���@��}n!.ZUF���_~�o�'d���B�P2oI��bL����-����w������v�����(���~��o������v��y}���8qf]���M�Y��/�5^u��:�,�o��2Y WG#W���X<zU��n�����ef�T��	UmQ}F�QsqQv�G����,�8����0��4}tE�A��@#>b�pm�����{�w���N!O��#/Q�O��)S�]�M��^�uxb/���8])�v����8�����w���;	nv��	���Y��_�@.�E���<�F�� ����K���db�Lf�V�t�FE�^���(�:�-���(����
�H��F�,b(�K.�hi�n�2�J�Z}+�3^�����js���������>\E�^]�j��x�|�Q�����O���1�4T��E�	K�2���i�p���5T�D�S�*���:�}�����W5��8������
j3=C�1�M�';�h�PR�8�����~�$����7O�w]N���_;_pqz�>�gV��r����F'g{tw����{t�6�[�V���C�*N��8��\�|���0����_��lL�;�p"
D�2�V����B����qJ���#���ah\�m'���N���X��:0�C�����B��TB�@Dv����{��L��)�����a�@�)���iBZI�0R<�'-���!G*q�vKek�\X�bU�2uu�B�"���[U���'A9L�}�|�mJ��������>�#w>��e�]�4��L��}����4%�7�L������Udnc`ye��0�kU���ahJ��cvnl�#c�hD!F�����qU���
w�rC5�n�B��T+�;����d��9���l�4��-{��/��K����~�G2�{��^��s@�-�����#%B|��Fs�s_������gZ�3QR�`S�&�(�F��$W(H�E���vE,�i���Uu,��\
���^��E���/;����J;����@�������E�Q���������{�X�(�������(Mm�k����,`W����m�Y����GG2a�7���y������w�����F&~3�4�j�G>�� ���eBmrz��~�}�������9@��O��>qw��6"g��B�7�R�
$�P�������C-�?��V�$L�s�������&Ro� ��c���G��[�������c;h�r�5T���!�-S	���wZ7���C
.�Y�-�I5I��%�@r����U9���B"aw;�f�W�u���c����cR+�"T�n�
��]����h=(80,NMQX��������|E*���/��N��F����{�(�7�4��F�:���;H��������&��#}c��c��Y0���h�{����������+������}�5�uZ��i�Z����7��Y�_o6�Y�_ou��]�?���`�:?����/�G����r�s�d`�U�����06Z�����������(D�:�}TU���k�M�4�����H5]3��l���
���	�)gV��\,E����Z���7=��N)����=X,|/���"��]:�z-��Qk��g0���Y�x��^�q0�6�8�h�GE	[-�\�^��� $�5��u@�!��$-��.����^�j5�@����(��a���,9� *8�fA^�b��]�W{e����t^��c��W��/�xeg����1��p\������N�0=�����`��$e4�^��q>�n�i�dG���.\	��gx6S�N���������+�9|��N�C$��b��S�\g��6���k!��tT��4v�q�Y�P�I����wzN�1��;D?�)�!
2���j{�J���D�|^��������Z�b�O��$0��� 3�0�c���1���u���w�0���i�����u��c�Z|K�(���������xi6������Py����[6��x�n�"l�1�z2�z^������S������>�����8���K�4�ww�f��,|v��`
�@��C���!jP���|��W]K$��]x#��N���xN��B�:�����.���77�������	����}�?�5�
�,P��y�a��'���,�#j�@P)�p%!����7�
uS5!��+Vx�:�
%_�5��'��"������W7M0D5C�R�2��t�RK���>;!u���PZ�:O�=S�iD����Ad���ECysH/V�uEIVJf���2�����M�u(q5�T��ls��/� i*��X���o����R�����^t�����B�t��o�&���
&�D�����>Z�J^�J�K2�Q��C�4��`��\h�"
��2�+������4�:qw�&�����$���q�a��43*��4g��F}:1��)�<�8;"������9�wz_��������u���kh�a���P���������d��qZ���F�����]\�or�����)l�7#U}+/PX 4�D>v�����/�v(m�-��������H�1]������8�����nJv'�'}�*��������
���3��������Pq����sO�I��S��@N���z
Vq*���6��~����n�+���e�4�o����O��L�.�A7��W� S���-���!�K��^����B
�7�(�����\�@�h����n:/�������d�V.�
����x��n|/��6T!��]�5�P��y�!��?����>���8�r���W`�'���<��Pq0�^������|Q���S���:��d�4��7�����\B��2����8�](�Gs����Ad��`nx���l�0((�M��gq�����sGnz��o��#'x���h&%K�8:�`h������m}/��>���X��#���Vb���aD�7x0��D���pl�Xo�tF�*�0p�����TM!�]�����FoO��X;�n�y
��s��}Uk1�<�VQ�������4��I��Ar

c�n�!����B��#�p�#����Rn_7��)��<��Ha����H��Q��mB�*��FV4�G�]�6G����9K���~p���\���k��� u��v��qS`!6D�$=��`�ov��W�[����K��_�}v��5�,0 �b��Ne��_�)��_�hv]���|�>�����O�����N���I�b�s�;b`���5��:��jN	(�+�p=:������)-���
���j��6�7"����!\C�?�Y'� O;�.u��.=x;d��ME`�Su���5"�)V�3(�q��q��A!^HzD�c]�
�	�a*twL���Ga��,�|�5-h�f�5�;���PC�1�]
0��	���8!.������!�G:���v����P��,_����IFt�FM�����H��H�
S��5�T�����&�q�}>��0�y�����uZ�)1�����
�����!�o��B)/��S������6H�����f�;��x��=$��G���H:	C@?���������p��|���q�5�%i����8a�C#�
�r5Iw��D~����ng\���S�����[������h�I�bHOd�q@�pKq�6:�n�f����tL���U#0�cBSIK{�D�p����:v� �,w�+	�it��tD�(/��_]����
R�*�vG��$�+[I�����S�����`o���g��W�D@�n���G�������;C���'�&c�_B���S��d��B�FK�Z��8�L5
�E��U8�hD�\���;I���A�3:!+���;��!L�'�R���tC�>!d����@�o��.����Eyp(z��T4�r���B7b!�a����=���U}������-�������%7j#��{�V}Z���^��t�&o�e'qs;���\�f7��!�/|*�o�������h`��r�9 3�Qn)%x ]@p5Y�B\StG�G(��)9��������
��Y�*�������jJ�d�e�[�_���#n*���e�qe�� ��&:>QBM�Wm��y�Ej|'�Q�\!����w'gG?�W�G�����!G������w����b����k>z.�=������E���M����1+��`z�3�H]PU�q���r���aBA�Bd�����w��X���������H�k!�#�B��	o��<�Iw��z^M����;��jH�������|55�%����z�<��c,��"�_�2���AU�u���5&|�+��<����Y��a�'����2�����D��mh�19<s���st���Ry8�l���JO-�K����W������oz����k5�L*}�(�.$j�~s�#<�T����_l��k�}������w���
)���`/N�k�#��P�t��L�G�
��C6�B��V0��1,�������~$��e���RZ
�p�J��Mx�|��0��@q���X��c�����j��O�{oQ����`/s�����yuI�Y/��4������\��&������.�9�R�-�\��	e��>h,��b�:�n�/�S���*C�a{�.l0���u\+���*��w�
�!"��������^��~C�4����#��O�*
��y���3.������"[�3��cqHy��bK��
��&�E�f������/��\�i�k�`A{����v�+I��[���L.4���[���
��	Ce���s����$��>�>"�Ivd�W���W ���0�z��XDf���/��/�w����&I���!�]����hr����(���:=�(H�O���UC�$'����A��������d�K�������/�m>�����[�����^�,
����%�h�xu4���wq*#b�%7�4�Q�_�|1�P�o�pe���]��w�bZ�Lb�
�@��h]����?��C��j%6�����"g��_l��y"wd:�<��������_I�?��9��
�f�L�D/�h�G�M<��D)�w�j�J��)�` ����Ky�e2����5�{�����c*LB�8�!�
�w�/�D�~ru�h��js/�|��{����ha�e)4v$y�Y#B=�[�����54���@E�Y�Hm��BY~����Q24��4)o�kn���['�cl�_�Fgh�p�F�*-`}������z�W��������-V
�����f������EJ6�2l��kW�*m����[J�������+/���h��k�����ld�a���Q�=e r?����7���:��F����p�}��������9.7���,�������@�m�+�
��G�2Z�=R���-wrw�6��A6��yK�e���Qj�v�5�v�T���j�8��5��p
�SV�pq�|.y�T�0
��D"�����,�4�3��v�h�,^D+��F�%�T�L_N,�L|s(���D�U�#Fj���&�X��"u]]�Ei��4�"�8b#KS��g�%����c{��bxr2:1%��yZ�`���?w��y]�������Z�����e��:���:�Yr��1*�M�3�4��o�k�S��K����v=�?�q��qB��v���ds
��ow����m����*\U,��2����2���LX�U������K.���-��+!O��V�I��������S���Q�����Y+E�9h�*
���+����B�1DN�������Z��5�gV�������������3Zz���"1\�Fd��Dy���C��$V���Az0
�E�zN�=�M�}�����X�V)�
c�\��|�J�`�i�B�
WU��� ��A���Z�}`p$UnN��C�Y��d�HB���@:�:\�VLs �������
��D��iBi5��������e��������jx"6K��vK���dB�-h�v�>�����������`
��C��I&�	WA����46�
�E��������'�����
^����-�	�v�������A��4gA��EZ��AJ���
jk$
��B�8�&i��eL�-�<�Cd{�Q)��M%A����
���U�^G�w���f�VF+�F��|D����N Xe�x+�)}'�MV�����I����;��j����^['����M����(DF��	|Grw�0��hQQOCb@��������IX�n�}��P1t>��c{�?�zA����t�?'��l1{�3��<��e;T=�_�l@�ik�-��< dU-��X=��Ti���'*(�7��a}�������}�,+�7��/6����VA�7]���4_d��q8�W���@�WP2�(��XAh���I�V��!�� &�a�fW��rE�?(GS�"��|�{�^��W�e</���Pz)j?f=�`���E��t���q�U�<3������1�u��il�s�N��������9�u��CX�����f�(�
L��x�`�5R7��(k��f)s�r�����J�r�%M/o5�����J��!�w|��<�<�yA���$��C���F3X�(o�V��i
l���%���cKh�r,&U!L\)j�PF�����?��7cahICg�N_V���h�\v5�_�B�I����c�dh�z�2���@�X;>H��$G���|�Pq\�is���?��ARcV`�ZQ:��F�f�Y���LJ���V�1����X+P�����(��:��|	t�/�&�[M��>~�z������� 3���)K���{�VnP����5Kf���X3Y4Rz��������UR��m�#:cn�P���e���D�; �1�<�Fh5�l�'U�D�B�M������_�r�u6�� �.���#�a���<�����	$��-��KK���������AlI�Y�A�:��i�y*���J�^��b�fK�H�6��@�PRB6���k������� �,�[������<���Q������\�rK��GX����t�0t��iAw
�?�G�z���!���<���<�c<LY��T�>��P�A��V���k��P�*[�W�_��e}�Eck����j����k�GO��$B���-2\/�>�K��%�����;</v��|z_�������</�X���{B0���$c���'$���<)k{,,2�r.�i��D������iSL��������P�R 4�);��z��cp�����������@2!`�C!B�{E��)����������I
{`
$?��4��pt��Q(�G�(E��eY��?f`��
�,~�O����$���vM�
�O����%g��2Jx^�M�P*����=�q�]�s*$
����CIzrp���8W|��8K�?��A�Q�����i�)������3g���;�<kX�%Q:�
��87e�>�@�	"2#�\�
>#A6����d��5�2�
�f�"�.���gV(`C����Y�^0d����>�W��WW�����r����k�0��(^.2����yb~��T�D���O��A��B(uX��-F�k�n
� �lB�Mp\�T�9�����I���#��x+�T�@�Wt�.����� ��t{����
��2@=r;1����Y-O��+�?��G�|T���%5~���xI@=�?uW����k,��(V`����W��4��<�<��m,��?ao�t�����N�dIrX�?�" ��j�*��e�HRU���m��L�h������PH���+�PR�f�%�wW�e���*=]b���������mZ	]q���}U�,%s\���10��q\���k�~E�����T�1����/I���[���$�]��	|�t#���"������M*�	Np{G��zl\B
�����W(�t��4����9	��$>��t��1C��$p���+��������:@��� �'���N�4���!��!h�����A��G������1�&�)M)?JVR��;�a�������� �q��������������u�J/������7�+|)^�3�&�8��sg!�{}��nFg�:�������2��:�4�dac��q%�7���
[>Mn�,��R��)���������F��R��]��Hq����y�V2l��QIKf����������������'����l5�:$������C�jv���0��,�AN�ls0�������f�=�3w�J�������s{>a|(��1j[=j�j�s1�<�rq4�:����hDw�5�]D�YH��C������6w1YkOFa�����l�p�u�-��62�Lj�p���+�[������:��A��S����JVu���"T���R�z���]lq��� h�U��p�$AR�Q��t*��p�z�bZ�A����	E ���\�����}���V�������GK/5G������������i�F��v��9_�D�d��f+s�O>g�z���:p��QC:��E��+1��,
4K�f�����D�<=0`*�^�3���sgu~`�����9�����C�B0���
��r�"���%��x(�pd��k%��
Zkc�SSc��2NP7�����\Q,����e�(��	3j��$��5BYO���H��H���,�w=
�+[�����f.!�9��p)rfg��
��$K�!�7:X���n3c���k�!9��g

<��<��Vo��Y��r��6?0|������ZN�K���c��H����o-M�x��%�=;b�$Oh��WoI��������g<"m��f�s�����V�����J�(&��w�u����WRG[�SH%Y�dr%n����g��0(��I�������H���w��,���
b�m������@!������l��
��������;���������y[{����}fm��������C�~�u��&�V��C�t,�&f��� ;O*���M��v��lj�v���?}q�Q"���p
�h �����p����M�~?y�O|`���=6�/���#���I~������������V���~�^i5�3j����g���j�����:\�C9w9�A��+W}F�!�i%�����N|�=a��%�C�*�F�o}t`�wC��|r��o5� �#/
�W�{�$�����82+9��=��Vf��
!��:]���Y�)'�U��?��g���A�cKh-�bBpB"�=����[���s�/�F-��V&$�P��1��ty=]��%�=a_V�T�s��3�8�83���i*�*�=�/o�����l����������������������OV�i}�y}��=��A�Y�ss2T���)��8�	v��p��$���3����Tc�aO�(����z`#e_����F�����2^�Z��-����}.���N��:��5"�y�x�������Q�@�s?�$�&����y�N�t:�L'�[���A��8zK ���qs�H���Y�:�e<�p�5�������$����6���F���y�S��G?�����Q�����������/F/$���GB��R��Q���	���T�O2���5�[��-���?�w����h�t���
���	�	aCWk,��F+�
��4��w���8_!}�
!:vZZ���M��Zx����g�����Ks�I� Q4���*�
�i��+��
6�]�C�
����u��A�/>{���TS���U�2��W���|W��N�&��}3_�{���I�nz>�A�15+��O������������2�;{���~�$�S]�
-�%�?N�������3v�O�zPR��Bdx���)����`nX��>�%�J�i���|���H�%�$��~YB5qXO�p���*hL:xmV��h�N�q��8qE�@EK2j��PZ���@�+�@�iDfj�f��M����
���h��9��go���#��(Vt��{��[+r�'�^�"�S���3��s�'���]�c�1��{��%�[�����e�J�����@$c�l}�w,IQU0�.�k����m��L��P�]&�@Q��M����5���h�����p��	�����p�u�TY�)
9>���g��x�-^x�p��Fw���-�6}iO@�r-m)����l���Z��n8�e�^@���s/�����g�rr���J�DY����Zv�������:��>w��=��j��.A�At�q|u�gi�{J�g����b��i�0��S�f�4��� ���M��f�Lk�������Hc�U
?���80�o���@�TtH<��-��B6*��S)PBb ;9��pa�����,�d���f��\�������{4�:��X^3����v,�QW���67��n�Fc���K���1�<�Q�d*���������|=*��:�`f���V�;�d`G@P���3� 1�.0g+GEhA�\@����f�b4:���n�{W^~�����rHw�;�@�a����\�GD�4�O <$w�������/��������=r)��������|;�y���������G1�HC��`US4�A�A���L�ie�"C�
��VX��*x��g<:�rQr���X��������}]��pj��[��s����zn7�M8��AX%-�P-�
N�V���P|�h�b��������3hgw��"3�73�W�����:�}~��C,}k�MWB`�TE��t����Q���{51���o��$���������/,�$Hk�h8:�6����X���-�2bjJ������?&hZ^���|>�!dtK���T���q����6�Z������Q
>�\�X�"`2�`�,@�o���v�,=��S���_����_�V�����.�yU.?�m`��W�N�Qi�Y��������=��	j�:z���-���RAP$R!>�:W,[j� ��C�_ru?�?X�'X,|�j���.��k��%�%�����8M"q"�)@8���(\�L{�X��!lP��i��,��}p���8~�S�����=>�,0�o�6A<�h�S"'�/���a��k�'�;�CS�X���b��<�rGc8��?)��(�?���h�>	��P_<s4HD��s��!��I�`�p���]B����ea H!��A�}��������SQs}����w>�a�V3������j�@U�bY��:��S��*�c�H7c��f�J,=��^������JA�J�G�o�	���e�j�������4��A�`�+D��IE�w#��eK�!WH;�������-d���$,E�LY���R����������G��E�����Bl�����<��-�/?`�VD�yWq����*p���J�b:Y<����e=b��}&�����ki�Q�WJC1���g\20�L����#/,��n
�N�"�`���U�Ni�Pf�-=�)�@$bYuEA�����
�}D���2'�KF�9������g��1���V�VG�5�u��B�y�Yp�Z���U��`�������oir�B���\l���N��L�/���@��1�u�>�")���j���K(+=�JT(�T�9Quh|%����B��pt�YK���vbs��V���h+���g������B��m+1���h�?�wm�#Z��XT�7�������8��)������v��FS,�\o`
<�l �,Y*���F�0	=�nrR��r���d�q���7�G�t�����F�s��x�kQ�ax��Bm��%�;#��Agw�G3zkJ6"kS�R�3�O
������J�<q���[`��1�YS�$�BP�w0M�S�iS�����h"i�5uf����,��m<N��>N��$��&�G������"�tI��v�Y�k�i��iv�k�"M��FX��3�J	e���mi�(X������g��.Yl���Kb����VM7Iy���Z��a�j�_oQn^o��J�!�8��
W��V �N�y�a��/���^�G�t� S+��j	�Hb��kP~8 "d
n[G�����J_%��e2����j�"�W&�{��hNev����X�U�'�����91���<W�r��
���9[��h���X�J��8XJ}�m��[������Ur�$N����F���q�F���v�-�Ug������+&Xs�Z���B�e
�8�C�)e��mQ��^\+#mc�J�M���3.��'����*�����&����o�cO,��_T6��k&�����5�#�)�����}Q��=��$
����[�<m:���6<�������-���p�,;�X���9���`:�0rFL:9�6��
��m�m��O7�����#���O0XT��������o�0��;���u1��������;g��#�����OF�qE�vr�Y�Fq,�2�Z\A����Lc�|js'i^����M��o�:�y�������������W�
�[`�?�d��i��d�TPz�wV�uzA�����^�^G�J�����p�����	���l���a��{��W�uX&:�:.�����,?�����������x�/��6��~[��l�-.�|��7N������$98�n�u@���`!~'�~���|"������5�)��t��
�;o�h�q����or���#�r2U�!-�6�N=��]����IcZ��w�Pl.@<��%
q�nAuBU����6wCBa���<�|��2��cZ�M�Fc���Z����Ov����,���E1��5�%s&����|+d�g����[�yl,���bA����_���'���������^g�VS��1�
���8	��(�J�M'$\D
mT�9W7 	���x��.��*ITI�L�C5;����/�:Xsc}�	�]��:\ur_Y0�Y���DW�
�Cj��ow��i����J�q�2|^���ltQY�+��S~�	L�/��K'0����e�I��	�|S(�d�2[���N����0X�U��w*�G���5$��B<�KVq����������2P�Vt6{F���q{�$!�tR���jA�}Q�aN�u[���7�Mj�A}��~��evs��&��Z(��\!H� U
�,F �I ��d��Q����lf��o�8�^X�?��GB�9�vw���� /X��O���������'�� ���������ts��enA�v(\�����==*���.�������]�#�x1
7�
����/���]I���x9�h7����u3	E�gJ��N�1���74g�)%�!�T�6(�a�A���3���s�������5��k���/�C��L�����&���>7A�����Cp��	^��Y���\m��?�l�;�!$i��P%�t`c�����+������*wc6��S�f���=&�������Rz��`�������_����FKo��-*F��&���G�����vmZn;y�=�n��kv�Z�;�z��E�?�I�|?��]�D/�~(�+�����U���@�s���$��f���x�(������
Y�K:��6����/�aJfs�RVs5%���=�)}��F��Fj��� w{��D����D����!�xNd~��c�I
��N��J�G������$��?�,�5��OK\Mq�O�2mj��^��0^�6'{�3y��T=���8�v��C��M�^Kh���7�����l�;�wQ�j��?��~;�����������������-l�����f
�B�i���[���O�t�]	��\h���������Bf��7�9���d��I�o��y����g�Z}�l�\��o}8���m��2i��
�������0�`�
������
����l���Pv����6�����k�4�T;���`=ZOUv�D�o�7�1|����JJGN���l����J���L�`�u7b�HbX�d�.%;������$��,���)���Y����	��i���H��Z���5�����������������y�
#97Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#93)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 2:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

Reviewing Amit's 0016:

performUndoActions appears to be badly-designed. For starters, it's
sometimes wrong: the only place it gets set to true is in
UndoActionsRequired (which is badly named, because from the name you
expect it to return a Boolean and to not have side effects, but
instead it doesn't return anything and does have side effects).
UndoActionsRequired() only gets called from selected places, like
AbortCurrentTransaction(), so the rest of the time it just returns a
wrong answer. Now maybe it's never called at those times, but there's
no guard to prevent a function like CanPerformUndoActions() (which is
also badly named, because performUndoActions tells you whether you
need to perform undo actions, not whether it's possible to perform
undo actions) from being called before the flag is set. I think that
this flag should be either (1) maintained eagerly - so that wherever
we set start_urec_ptr we also set the flag right away or (2) removed -
so when we need to know, we just loop over all of the undo categories
on the spot, which is not that expensive because there aren't that
many of them.

I would prefer to go with (2). So, I will change the function
CanPerformUndoActions() to loop over categories and return whether
there is a need to perform undo actions. Also, rename
CanPerformUndoActions as NeedToPerformUndoActions or
UndoActionsRequired, any other better suggestion?

It seems pointless to make PrepareTransaction() take undo pointers as
arguments, because those pointers are just extracted from the
transaction state, to which PrepareTransaction() has a pointer.

Agreed, will remove.

Thomas has already objected to another proposal to add functions that
turn 32-bit XIDs into 64-bit XIDs. Therefore, I feel confident in
predicting that he will likewise object to GetEpochForXid. I think
this needs to be changed somehow, maybe by doing what the XXX comment
you added suggests.

We can do what the comment says, but there is one more similar usage
in undodiscard.c as well, so not sure if that is the right thing. I
think Thomas is suggesting to open code its usage where it is safe to
do so and required. I have responded to his email, let us see what he
has to say, based on that we can modify this patch.

This patch has some problems with naming consistency. There's a
function called PushUndoRequest() which calls a function called
RegisterRollbackReq() to do the heart of the work. So, is it undo or
rollback? Are we pushing or registering? Is it a request or a req?

I think we can rename PushUndoRequest as RegisterUndoRequest and
RegisterRollbackReq as RegisterUndoRequestGuts.

For bonus points, the flag that the function sets is called
undo_req_pushed, which is halfway in between the two competing
terminologies. Other gripes about PushUndoRequest: push is vague and
doesn't really explain what's happening, "apllying" is a typo,
per_level is a poor variable name and shouldn't be declared volatile.
This function has problems with naming in other places, too; please go
through all of the names carefully and make them consistent and
adequately descriptive.

Okay, will change as per suggestion.

I am not a fan of applying_subxact_undo. I think we should look for a
better design there. A couple of things occur to me. One is that we
don't necessarily need to go to FATAL; we could just force the current
transaction and all of its subtransactions fail all the way out to the
top level, but then perhaps allow new transactions to be started
afterwards. I'm not sure that's worth it, but it would work, and I
think it has precedent in SxactIsDoomed. Assuming we're going to stick
with the current FATAL plan, I think we should do something like
invent a new kind of critical section that forces ERROR to be promoted
to FATAL and then use it here. We could call it a semi-critical or
locally-critical section, and the undo machinery could use it, but
then also so could other things. I've wanted that sort of concept
before, so I think it's a good idea to try to have something general
and independent of undo. The same concept could be used in
PerformUndoActions() instead of having to invent
pg_rethrow_as_fatal(), so we'd have two uses for this mechanism right
away.

Okay, I will investigate on the lines of the semi-critical section.

FinishPreparedTransactions() tries to apply undo actions while
interrupts are still held. Is that necessary?

I don't think so. I'll think some more and update back if I see any
problem, otherwise, will do RESUME_INTERRUPTS before performing
actions.

Can we avoid it?

It seems highly likely that the logic added to the TBLOCK_SUBCOMMIT
case inside CommitTransactionCommand and also into
ReleaseCurrentSubTransaction should have been added to
CommitSubTransaction instead. If that's not true, then we have to
believe that the TBLOCK_SUBRELEASE call to CommitSubTransaction needs
different treatment from the other two cases, which sounds unlikely;
we also have to explain why undo is somehow different from all of
these other releases that are already handled in that function, not in
its callers.

Yeah, it is better to move that code from ReleaseSavepoint to here or
rather move it to CommitSubTransaction as suggested by you.

I also strongly suspect it is altogether wrong to do
this before CommitSubTransaction sets s->state to TRANS_COMMIT; what
if a subxact callback throws an error?

Are you worried that it might lead to the execution of actions twice?
If so, I think we prevent that during replay of actions and also that
can happen in other ways too. I am not telling that we should not
move that code block to the location you are suggesting, but I think
the current code is also not wrong.

For related reasons, I don't think that the change ReleaseSavepoint()
are right either. Notice the header comment: "As above, we don't
actually do anything here except change blockState." The "as above"
part of the comment probably didn't originally refer to
DefineSavepoint(), which definitely does do other stuff, but to
something like EndImplicitTransactionBlock() or EndTransactionBlock(),
and DefineSavepoint() got stuck in the middle later. Anyway, your
patch makes the comment false by doing actual state changes in this
function, rather than just marking the subtransactions for commit.
But why should that be right? If none of the many other bits of state
are manipulated here rather than in CommitSubTransaction(), why is
undo the one thing that is different? I guess this is basically just
compensation for the lack of any of this code in the TBLOCK_SUBRELEASE
path which I noted in the previous paragraph, but I still think the
right answer is to put it all in CommitSubTransaction() *after* we set
TRANS_COMMIT.

Agreed, will change accordingly.

There are a number of things I either don't like or don't understand
about PerformUndoActions. One is that undo_req_pushed gets passed to
this function. That just looks really odd from an abstraction point
of view. Basically, we have a function whose job is to "perform undo
actions," and it gets a flag as an argument that tells it to not
actually perform some of the undo actions: that's odd. I think the
reason it's like that is because of the issue we've been discussing
elsewhere that there's a separate undo request for each category.

The reason was that if we don't have that check here, then we need to
do the same in both the callers. As there are just two places, so
moving it to the caller should be okay. I think if we do that then
probably looping for each persistence level can also be moved into the
caller.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#98Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

1. Renamed UndoPersistence to UndoLogCategory everywhere, and add a
fourth category UNDO_SHARED where transactions can write 'out of band'
data that relates to more than one transaction.

2. Introduced a new RMGR callback rm_undo_status. It is used to
decide when record sets in the UNDO_SHARED category should be
discarded (instead of the usual single xid-based rules). The possible
answers are "discard me now!", "ask me again when a given XID is all
visible", and "ask me again when a given XID is no longer running".

3. Recognise UNDO_SHARED record set boundaries differently. Whereas
undolog.c recognises transaction boundaries automatically for the
other categories (UNDO_PERMANENT, UNDO_UNLOGGED, UNDO_TEMP), for
UNDO_SHARED the

4. Add some quick-and-dirty throw-away test stuff to demonstrate
that. SELECT test_multixact([1234, 2345]) will create a new record
set that will survive until the given array of transactions is no
longer running, and then it'll be discarded. You can see that with
SELECT * FROM undoinspect('shared'). Or look at SELECT
pg_stat_undo_logs. This test simply writes all the xids into its
payload, and then has an rm_undo_status function that returns the
first xid it finds in the list that is still running, or if none are
running returns UNDO_STATUS_DISCARD.

Currently you can only return UNDO_STATUS_WAIT_XMIN so wait for an xid
to be older than the oldest xmin; presumably it'd be useful to be able
to discard as soon as an xid is no longer active, which could be a bit
sooner.

Another small change: several people commented that
UndoLogIsDiscarded(ptr) ought to have some kind of fast path that
doesn't acquire locks since it'll surely be hammered. Here's an
attempt at that that provides an inlined function that uses a
per-backend recent_discard to avoid doing more work in the (hopefully)
common case that you mostly encounter discarded undo pointers. I hope
this change will show up in profilers in some zheap workloads but this
hasn't been tested yet.

Another small change/review: the function UndoLogGetNextInsertPtr()
previously took a transaction ID, but I'm not sure if that made sense,
I need to think about it some more.

I pulled the latest patches pulled in from the "undoprocessing" branch
as of late last week, and most of the above is implemented as fixup
commits on top of that.

Next I'm working on DBA facilities for forcing undo records to be
discarded (which consists mostly of sorting out the interlocking to
make that work safely). And also testing facilities for simulating
undo log switching (when you fill up each log and move to another one,
which are rare code paths run, so we need a good way to make them not
rare).

In 0003-Add-undo-log-manager

/* If we discarded everything, the slot can be given up. */
+ if (entirely_discarded)
+ free_undo_log_slot(slot);

I have noticed that when the undo log was detached and it was full
then if we discard complete log we release its slot. But, what is
bothering me is should we add that log to the free list? Or I am
missing something?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#99Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#97)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 4:43 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 16, 2019 at 2:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

This patch has some problems with naming consistency. There's a
function called PushUndoRequest() which calls a function called
RegisterRollbackReq() to do the heart of the work. So, is it undo or
rollback? Are we pushing or registering? Is it a request or a req?

I think we can rename PushUndoRequest as RegisterUndoRequest and
RegisterRollbackReq as RegisterUndoRequestGuts.

One thing I am not sure about the above suggestion is whether it is a
good idea to expose a function which ends with 'Guts'. I have checked
and found that there are a few similar precedents like
ExecuteTruncateGuts. Another idea could be to rename
RegisterRollbackReq as RegisterUndoRequestInternal. We have few
precedents for that as well.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#100Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#99)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 10:02 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 16, 2019 at 4:43 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 16, 2019 at 2:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

This patch has some problems with naming consistency. There's a
function called PushUndoRequest() which calls a function called
RegisterRollbackReq() to do the heart of the work. So, is it undo or
rollback? Are we pushing or registering? Is it a request or a req?

I think we can rename PushUndoRequest as RegisterUndoRequest and
RegisterRollbackReq as RegisterUndoRequestGuts.

One thing I am not sure about the above suggestion is whether it is a
good idea to expose a function which ends with 'Guts'. I have checked
and found that there are a few similar precedents like
ExecuteTruncateGuts. Another idea could be to rename
RegisterRollbackReq as RegisterUndoRequestInternal. We have few
precedents for that as well.

I don't personally like Guts, not only because bringing human (or
animal) body parts into this seems unnecessary, but more importantly
because it's not at all descriptive. Internal is no better. The point
is that you need to give the functions names that make it clear how
what one function does is different from what another function does,
and neither Guts nor Internal is going to help with that.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#101Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#94)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 12:32 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

The idea is that the queues can get full, but not rollback hash table.
In the case where the error queue gets full, we mark the entry as
Invalid in the hash table and later when discard worker again
encounters this request, it adds it to the queue if there is a space
available and marks the entry in the hash table as valid. This allows
us to keep the information of all xacts having pending undo in shared
memory.

I don't understand. How is it OK to have entries in the hash table
but not the queues? And why would that ever happen, anyway? If you
make the queues as big as the hash table is, then they should never
fill up (or, if using binary heaps with lazy removal rather than
rbtrees, they might fill up, but if they do, you can always make space
by cleaning out the stale entries).

I think this can regress the performance when there are many
concurrent sessions unless there is a way to add/remove request
without a lock. As of now, we don't enter any request or block any
space in shared memory related to pending undo till there is an error
or user explicitly Rollback the transaction. We can surely do some
other way as well, but this way we won't have any overhead in the
commit or successful transaction's path.

Well, we're already incurring some overhead to attach to an undo log,
and that probably involves some locking. I don't see why this would
be any worse, and maybe it could piggyback on the existing work.
Anyway, if you don't like this solution, propose something else. It's
impossible to correctly implement a hard limit unless the number of
aborted-but-not-yet-undone transaction is bounded to (HARD_LIMIT -
ENTRIES_THAT_WOULD_BE_ADDED_AFTER_RECOVERY_IF_THE_SYSTEM_CRASHED_NOW).
If there are 100 transactions each bound to 2 undo logs, and you
crash, you will need to (as you have it designed now) add another 200
transactions to the hash table upon recovery, and that will make you
exceed the hard limit unless you were at least 200 transactions below
the limit before the crash. Have you handled that somehow? If so,
how? It seems to me that you MUST - at a minimum - keep a count of
undo logs attached to in-progress transactions, if not the actual hash
table entries.

Again coming to question of whether we need single or multiple entries
for one-request-per-persistence level, the reason for the same we have
discussed so far is that discard worker can register the requests for
them while scanning undo logs at different times.

Yeah, but why do we need that in the first place? I wrote something
about that in a previous email, but you haven't responded to it here.

However, there are
few more things like what if while applying the actions, the actions
for logged are successful and unlogged fails, keeping them separate
allows better processing. If one fails, register its request in error
queue and try to process the request for another persistence level. I
think the requests for the different persistence levels are kept in a
separate log which makes their processing separately easier.

I don't find this convincing. It's not really an argument, just a
vague list of issues. If you want to convince me, you'll need to be
much more precise.

It seems to me that it is generally undesirable to undo the unlogged
part of a transaction separately from the logged part of the
transaction. But even if we want to support that, having one entry
per XID rather than one entry per <XID, persistence level> doesn't
preclude that. Even if you discover the entries at different times,
you can still handle that by updating the existing entry rather than
making a new one.

There might be a good reason to do it the way you are describing, but
I don't see that you've made the argument for it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#102Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#97)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 7:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I also strongly suspect it is altogether wrong to do
this before CommitSubTransaction sets s->state to TRANS_COMMIT; what
if a subxact callback throws an error?

Are you worried that it might lead to the execution of actions twice?

No, I'm worried that you are running code that is part of the commit
path before the transaction has actually committed.
CommitSubTransaction() is full of stuff which basically propagates
whatever the subtransaction did out to the parent transaction, and all
of that code runs after we've ruled out the possibility of an abort,
but this very-similar-looking code runs while it's still possible for
an abort to happen. That seems unlikely to be correct, and even if it
is, it seems needlessly inconsistent.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#103Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

Here's a new version.

Here's a relatively complete review of 0019 and 0020 and a remark or
two on the beginning of 0003.

Regarding 0020:

The documentation claims that undo data exists in a 64-bit address
space divided into 2^34 undo logs, each with a theoretical capacity of
1TB, but that would require 74 bits.

I am mildly suspicious that, on a busy system, the use of 1MB segment
files could result in slowdowns due to frequent filesystem operations.
We just recently made it more convenient to change the WAL segment
size, mostly so that people on very busy systems could crank it up
from 16MB to, say, 64MB or 256MB. It's true that the considerations
are a bit different here, because undo logs don't have to be archived,
and because we might be using many undo logs simultaneously rather
than only 1 for the whole system, but it's still true that if you've
got a bunch of backends blasting out undo at top speed, you're going
to have to recycle files *extremely* quickly. How much performance
testing have you done to assess the effect of segment size? Do you
think there's an argument for making this 1MB size configurable at
initdb-time? Or even variable at runtime, so that we use larger files
if we're filling them up in < 100ms or whatever?

I don't think the last paragraph is entirely accurate. The access
method gets to control what records are written, but the general
format of the records is fixed by the undo system. Perhaps the undo
log code isn't what cares about that, but whether it's the undo log
code or the undo access code or the undo processing code isn't likely
to seem relevant to developers.

Regarding 0019:

I think there's a substantial amount of duplication between 0019 and
0020, and I'm not sure that we ought to have both. They both talk
about the purpose of undo, the way the adddress space is divided, etc.
I understand that it would be a little weird to include all of the
information from 0019 in the user-facing documentation, and I also
understand that it won't work to have no user-facing documentation at
all, but it still seems a little odd to me. Possibly 0019 could refer
to the SGML documentation for preliminaries and then add only those
details that are not covered there.

How could we avoid the limit on the total size of an active
transaction mentioned here? And what would be the cost of such a
scheme? If we've filled an undo log and moved on to another one, why
can't we evict the one that's full and reuse the shared memory slot,
bringing it back in later when required? I suspect the answer is that
there is a locking rule involved. I think this README would be a good
place to document things like locking rules, or a least to refer to
where they are documented. I also think we should mull over whether we
could relax the rule without too much pain. I expect that at least
part of the problem is that somebody might have a pointer to an
UndoLogSlot which could become stale if we recycle a slot, but that
can already happen at least when the log is fully discarded, so maybe
allowing it to happen in other cases wouldn't be too bad.

I know you're laughing at me on the inside, worrying about a
transaction that touches so many TB of data that it manages to exhaust
all the undo log slots, but I don't think that's a completely crazy
scenario. There are PB-scale databases out there, and it would be nice
to think that PostgreSQL could capture more of those workloads. They
will probably become more common over time.

Reading the section on persistence levels and tablespaces makes me
wonder what happens to address space that gets allocated to temporary
and unlogged undo logs. It seems pretty important to make sure that we
at least don't leak anything significant, and maybe that we actually
recycle the address space or share it across backends. That is, if
several backends are all writing temporary undo, there's no intrinsic
reason why they can't all be using the same temporary undo logs, as
long as the file naming works OK for that (e.g. if it follows the same
pattern we use for relation names). Any undo logs that get allocated
to unlogged undo can be recycled - either for unlogged undo or
otherwise - after a crash, and any that are partially filled can be
rewound. I don't know how much effort we're expending on any of that
right now, but it seems like it would be worth discussing in this
README, and possibly improving.

When the undo log contents section mentions that "client code is
responsible for stepping over the page headers and advancing to the
next page," that's again a somewhat middle-of-the-patch stack
perspective. I am not sure exactly how this should be phrased, but the
point is that the client code we're talking about is not the AM but
the next patch in the stack. I think developers will view the AM as
the client and our wording probably ought to reflect that.

"keepign" is not spelled correctly. A little later on, "checkpoin" is
missing a letter.

I think it would be worth mentioning how you solved the problem of
inferring during recovery the position within the page where the
record needs to be placed.

The bit about checkpoint files written to pg_undo being potentially
inconsistent is confusing. If the files are written before the
checkpoint is completed, fsync'd, and not modified afterwards, how can
they be inconsistent?

Regarding 0003:

UndoLogSharedData could use a more extensive comment. It's not very
clear what low_logno and next_logno are, and it also seems like it
would be worth mentioning how the free lists are linked. On a similar
note, I think the file header comment ought to reference the undo
README added by 0019 and perhaps also the documentation added by 0020,
and I think 0019 and 0020 ought to be flattened into 0003.

I meant to write more about 0003 before sending this, but I am out of
time and it seems more useful to send what I have now than to wait
until I have more...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#104Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#91)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-07-13 15:55:51 +0530, Amit Kapila wrote:

On Fri, Jul 12, 2019 at 7:08 PM Robert Haas <robertmhaas@gmail.com> wrote:

I think even if we currently go with a binary heap, it will be
possible to change it to rbtree later, but I am fine either way.

Well, I don't see much point in revising all of this logic twice. We
should pick the way we want it to work and make it work that way.

Yeah, I agree. So, I am assuming here that as you have discussed this
idea with Andres offlist, he is on board with changing it as he has
originally suggested using binary_heap. Andres, do let us know if you
think differently here. It would be good if anyone else following the
thread can also weigh in.

Yes, I think using an rbtree makes sense.

I'm not yet sure whether we'd want the rbtree nodes being pointed to
directly by the hashtable, or whether we'd want one indirection.

e.g. either something like:

typedef struct UndoWorkerQueue
{
/* priority ordered tree */
RBTree *tree;
....
}

typedef struct UndoWorkerQueueEntry
{
RBTNode tree_node;

/*
* Reference hashtable via key, not pointers, entries might be
* moved.
*/
RollbackHashKey rollback_key
...
} UndoWorkerQueueEntry;

typedef struct RollbackHashEntry
{
...
UndoWorkerQueueEntry *queue_memb_size;
UndoWorkerQueueEntry *queue_memb_age;
UndoWorkerQueueEntry *queue_memb_error;
}

and call rbt_delete() for any non-NULL queue_memb_* whenever an entry is
dequeued via one of the queues (after setting the one already dequeued
from to NULL, of course). Which requires - as Robert mentioned - that
rbtree pointers remain stable after insertions.

Alternatively we can have a more complicated arrangement without the
"stable pointer" requirement (which'd also similarly work for a binary
heap):

typedef struct UndoWorkerQueue
{
/* information about work needed, not meaningfully ordered */
UndoWorkerQueueEntry *entries;

/*
* Priority ordered references into 0<entries, using
* UndoWorkerQueueTreeEntry as members.
*/
RBTree tree;

/* unused elements in ->entries, UndoWorkerQueueEntry members */
slist_head freelist;

/*
* Number of entries in ->entries and tree that can be pruned by
* doing a scan of both.
*/
int num_prunable_entries;
}

typedef struct UndoWorkerQueueEntry
{
/*
* Reference hashtable via key, not pointers, entries might be
* moved.
*/
RollbackHashKey rollback_key

/*
* As members of UndoWorkerQueue->tree can be moved in memory,
* RollbackHashEntry cannot directly point to them. Instead
*/
bool already_processed;
...

slist_node freelist_node;
} UndoWorkerQueueEntry;

typedef struct UndoWorkerQueueTreeEntry
{
RBTree tree;

/* offset into UndoWorkerQueue->entries */
int off;
} UndoWorkerQueueEntry;

and again

typedef struct RollbackHashEntry
{
RBTNode tree_node;
...
UndoWorkerQueueEntry *queue_memb_size;
UndoWorkerQueueEntry *queue_memb_age;
UndoWorkerQueueEntry *queue_memb_error;
}

Because the tree entries are not members of the tree itself, pointers to
them would be stable, regardless of rbtree (or binary heap) moving them
around. The cost of that would be more complicated datastructures, and
insertion/deletion/dequeing operations:

insertion:
if (slist_is_empty(&queue->freelist))
prune();
if (slist_is_empty(&queue->freelist))
elog(ERROR, "full")

UndoWorkerQueueEntry *entry = slist_pop_head_node(&queue->freelist)
UndoWorkerQueueTreeEntry tree_entry;

entry->already_processed = false;
entry->... = ...;

tree_entry.off = entry - queue->entries; // calculate offset
rbt_insert(queue->tree, &tree_entry, NULL);

prune:
if (queue->num_prunable_entries > 0)
RBTreeIterator iter;
slist_node *pending_freelist;
rbt_begin_iterate(queue->tree, &iter, LeftRightWalk);
while ((tnode = rbt_iterate(&iter)) != 0)
node = (UndoWorkerQueueTreeEntry *) tnode;
if (queue->entries[node->off]->already_processed)
rbt_delete(tnode);
/* XXX: Have to stop here, the iterator is invalid -
* probably should add a rbt_delete_current(iterator);
*/
break;

dequeue:
while (node = rbt_leftmost(queue->tree))
node = (UndoWorkerQueueTreeEntry *) tnode;
entry = &queue->entries[node->off];

rbt_delete(tnode);

/* check if the entry has already been processed via another queue */
if (entry->already_processed)
slist_push(&queue->freelist, &entry->freelist_node);
else
/* found it */
return entry;
return NULL;

delete (i.e. processed in another queue):

/*
* Queue entry will only be reusable when the corresponding tree
* entry has been removed. That'll happen either when new entries
* are needed (cf prune), or when the entry is dequeued (cf dequeue).
*/
entry->already_processed = true;

I think the first approach is clearly preferrable from a simplicity POV,
but the second approach would be a bit more generic (applicable to heap
as well) and wouldn't require adjusting the rbtree code.

Greetings,

Andres Freund

#105Thomas Munro
thomas.munro@gmail.com
In reply to: Dilip Kumar (#98)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 11:33 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:
/* If we discarded everything, the slot can be given up. */
+ if (entirely_discarded)
+ free_undo_log_slot(slot);

I have noticed that when the undo log was detached and it was full
then if we discard complete log we release its slot. But, what is
bothering me is should we add that log to the free list? Or I am
missing something?

Stepping back a bit: The free lists are for undo logs that someone
might want to attach to and insert into. If it's full, we probably
can't insert anything into it again (well, technically someone else
who wants to insert something a bit smaller might be able to, but
that's not an interesting case to worry about). So it doesn't need to
go back on a free list, but it still needs to exist (= occupy a slot)
as long as there is undiscarded data in it, because that data is
needed and we need to be able to test URPs against its discard
pointer. But once its data is entirely discarded, it ceases to exist
-- there is no reason to waste a slot on it, and any URP in this undo
log will be considered to be discarded (because we can't find a slot,
and we also cache that fact in recent_discard so lookups are fast and
lock-free), and therefore it'll not be checkpointed or reloaded at
next startup; then we couldn't put it on a free list even if we wanted
to, because there is nothing left of it ("logs" don't really exist in
memory, only "slots", currently holding the meta-data for a log, which
is why I renamed UndoLog to UndoLogSlot to reduce confusion on that
point). One of the goals here is to make a system that doesn't
require an increasing amount of memory as time goes on -- hence desire
to completely remove state relating to entirely discarded undo logs
(you might point out that the recent_discard cache would get
arbitrarily large after we chew through millions of undo logs, but
there is another defence against that in the form of low_logno which
isn't used in that test yet but could be used to miminise that
effect). Does this make sense, and do you see a problem?

--
Thomas Munro
https://enterprisedb.com

#106Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#92)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-07-15 12:26:21 -0400, Robert Haas wrote:

Yeah. I didn't understand that explanation. It seems to me that one
of the fundamental design questions for this system is whether we
should allow there to be an unbounded number of transactions that are
pending undo application, or whether it's OK to enforce a hard limit.
Either way, there should certainly be pressure applied to try to keep
the number low, like forcing undo application into the foreground when
a backlog is accumulating, but the question is what to do when that's
insufficient. My original idea was that we should not have a hard
limit, in which case the shared memory data on what is pending might
be incomplete, in which case we would need the discard workers to
discover transactions needing undo and add them to the shared memory
data structures, and if those structures are full, then we'd just skip
adding those details and rediscover those transactions again at some
future point.

But, my understanding of the current design being implemented is that
there is a hard limit on the number of transactions that can be
pending undo and the in-memory data structures are sized accordingly.

My understanding is that that's really just an outcome of needing to
maintain oldestXidHavingUndo accurately, right? I know I asked this
before, but I didn't feel like the answer was that clear (probably due
to my own haziness). To me it seems very important to understand whether
/ how much we can separate the queuing/worker logic from the question of
how to maintain oldestXidHavingUndo.

In such a system, we cannot rely on the discard worker(s) to
(re)discover transactions that need undo, because if there can be
transactions that need undo that we don't know about, then we can't
enforce a hard limit correctly. The exception, I suppose, is that
after a crash, we'll need to scan all the undo logs and figure out
which transactions are pending, but that doesn't preclude using a
single queue entry covering both the logged and the unlogged portion
of a transaction that has written undo of both kinds. We've got to
scan all of the undo logs before we allow any new undo-using
transactions to start, and so we can create one fully-up-to-date entry
that reflects the data for both persistence levels before any
concurrent activity happens.

Yea, that seems like a question independent of the "completeness"
requirement. If desirable, it seems trivial to either have
RollbackHashEntry have per-persistence level status (for one entry per
xid), or not (for per-persistence entries).

Greetings,

Andres Freund

#107Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#106)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 3:53 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-07-15 12:26:21 -0400, Robert Haas wrote:

Yeah. I didn't understand that explanation. It seems to me that one
of the fundamental design questions for this system is whether we
should allow there to be an unbounded number of transactions that are
pending undo application, or whether it's OK to enforce a hard limit.
Either way, there should certainly be pressure applied to try to keep
the number low, like forcing undo application into the foreground when
a backlog is accumulating, but the question is what to do when that's
insufficient. My original idea was that we should not have a hard
limit, in which case the shared memory data on what is pending might
be incomplete, in which case we would need the discard workers to
discover transactions needing undo and add them to the shared memory
data structures, and if those structures are full, then we'd just skip
adding those details and rediscover those transactions again at some
future point.

But, my understanding of the current design being implemented is that
there is a hard limit on the number of transactions that can be
pending undo and the in-memory data structures are sized accordingly.

My understanding is that that's really just an outcome of needing to
maintain oldestXidHavingUndo accurately, right?

Yes.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#108Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#105)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 3:48 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Tue, Jul 16, 2019 at 11:33 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:
/* If we discarded everything, the slot can be given up. */
+ if (entirely_discarded)
+ free_undo_log_slot(slot);

I have noticed that when the undo log was detached and it was full
then if we discard complete log we release its slot. But, what is
bothering me is should we add that log to the free list? Or I am
missing something?

Stepping back a bit: The free lists are for undo logs that someone
might want to attach to and insert into. If it's full, we probably
can't insert anything into it again (well, technically someone else
who wants to insert something a bit smaller might be able to, but
that's not an interesting case to worry about). So it doesn't need to
go back on a free list, but it still needs to exist (= occupy a slot)
as long as there is undiscarded data in it, because that data is
needed and we need to be able to test URPs against its discard
pointer. But once its data is entirely discarded, it ceases to exist
-- there is no reason to waste a slot on it,

Right, actually I got that point. But, I was thinking that we are
wasting one logno from undo log addressing space no?. Instead, if we
can keep it attached to the slot and somehow manage to add to the free
list then the same logno can be used by someone else?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#109Thomas Munro
thomas.munro@gmail.com
In reply to: Dilip Kumar (#108)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 3:44 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Right, actually I got that point. But, I was thinking that we are
wasting one logno from undo log addressing space no?. Instead, if we
can keep it attached to the slot and somehow manage to add to the free
list then the same logno can be used by someone else?

We can never reuse log numbers. UndoRecPtr values containing that log
number could exist in permanent storage anywhere (zheap, zedstore etc)
and must appear to be discarded forever if anyone asks. Now, it so
happens that the current coding in zheap has fxid + urp for each
transaction slot and always checks the fxid first so it probably
wouldn't ask about discarded urps too much, but I don't think that's
policy is a requirement and the undo layer can't count on it. I think
I heard that zedstore is planning to check urp only.

--
Thomas Munro
https://enterprisedb.com

#110Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#109)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 9:27 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Wed, Jul 17, 2019 at 3:44 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Right, actually I got that point. But, I was thinking that we are
wasting one logno from undo log addressing space no?. Instead, if we
can keep it attached to the slot and somehow manage to add to the free
list then the same logno can be used by someone else?

We can never reuse log numbers. UndoRecPtr values containing that log
number could exist in permanent storage anywhere (zheap, zedstore etc)
and must appear to be discarded forever if anyone asks.

Yeah right. I knew that we can not reuse UndoRecPtr but forget to
think that if we reuse logno then it is same as reusing UndoRecPtr.
Sorry for the noise.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#111Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#101)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 9:44 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jul 16, 2019 at 12:32 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

The idea is that the queues can get full, but not rollback hash table.
In the case where the error queue gets full, we mark the entry as
Invalid in the hash table and later when discard worker again
encounters this request, it adds it to the queue if there is a space
available and marks the entry in the hash table as valid. This allows
us to keep the information of all xacts having pending undo in shared
memory.

I don't understand. How is it OK to have entries in the hash table
but not the queues? And why would that ever happen, anyway?

We add entries in queues only when we want them to be processed by
background workers whereas hash table will contain the entries for all
the pending undo requests irrespective of whether they are executed by
foreground-transaction or by background workers. Once the request is
processed, we remove it from the hash table. The reasons for keeping
all the pending abort requests in hash table is that it allows us to
compute oldestXidHavingUnappliedUndo and second is it avoids us to
have duplicate undo requests by backends and discard worker. In
short, there is no reason to keep all the entries in queues, but there
are reasons to keep all the aborted xact entries in hash table.

There is some more explanation about queues and hash table in
README.UndoProcessing which again might not be sufficient to get all
the details, but it can still help.

If you
make the queues as big as the hash table is, then they should never
fill up (or, if using binary heaps with lazy removal rather than
rbtrees, they might fill up, but if they do, you can always make space
by cleaning out the stale entries).

I think this can regress the performance when there are many
concurrent sessions unless there is a way to add/remove request
without a lock. As of now, we don't enter any request or block any
space in shared memory related to pending undo till there is an error
or user explicitly Rollback the transaction. We can surely do some
other way as well, but this way we won't have any overhead in the
commit or successful transaction's path.

Well, we're already incurring some overhead to attach to an undo log,
and that probably involves some locking. I don't see why this would
be any worse, and maybe it could piggyback on the existing work.

We attach to the undo log only once per backend (unless user changes
tablespace of undo in-between or probably when the space in current
log is finished) and then use it for all transactions via that
backend. For each transaction, we don't take any global lock for
undo, so here we need something different. Also, we need it at commit
time as well.

Anyway, if you don't like this solution, propose something else. It's
impossible to correctly implement a hard limit unless the number of
aborted-but-not-yet-undone transaction is bounded to (HARD_LIMIT -
ENTRIES_THAT_WOULD_BE_ADDED_AFTER_RECOVERY_IF_THE_SYSTEM_CRASHED_NOW).
If there are 100 transactions each bound to 2 undo logs, and you
crash, you will need to (as you have it designed now) add another 200
transactions to the hash table upon recovery, and that will make you
exceed the hard limit unless you were at least 200 transactions below
the limit before the crash. Have you handled that somehow? If so,
how?

Yeah, we have handled it by reserving the space of MaxBackends. It is
UndoRollbackHashTableSize() - MaxBackends. There is a bug in the
current patch which is that it should reserve space for 2 *
MaxBackends so that after recovery, we are safe, but that can be
fixed.

It seems to me that you MUST - at a minimum - keep a count of
undo logs attached to in-progress transactions, if not the actual hash
table entries.

Again coming to question of whether we need single or multiple entries
for one-request-per-persistence level, the reason for the same we have
discussed so far is that discard worker can register the requests for
them while scanning undo logs at different times.

Yeah, but why do we need that in the first place? I wrote something
about that in a previous email, but you haven't responded to it here.

I have responded to it as a separate email, but let's discuss it here.
So, you are right that only time we need to scan the undo logs to find
all pending aborted xacts is immediately after startup. But, we can't
create a fully update-to-date entry from both the logs unless we make
undo launcher to also wait to process anything till we are done. We
are not doing this in the current patch but we can do it if we want.
This will be an additional restriction we have to put which is not
required for the current approach.

Another related thing is that to update the existing entry for queues,
we need to delete and re-insert the entry after we find the request in
a different log category. Again it depends if we point queue entries
to hash table, then we might not have this additional work but that
has its own set of complexities.

However, there are
few more things like what if while applying the actions, the actions
for logged are successful and unlogged fails, keeping them separate
allows better processing. If one fails, register its request in error
queue and try to process the request for another persistence level. I
think the requests for the different persistence levels are kept in a
separate log which makes their processing separately easier.

I don't find this convincing. It's not really an argument, just a
vague list of issues. If you want to convince me, you'll need to be
much more precise.

I think it is implementation wise simpler to have one entry per
persistence level. It is not that we can't deal with all the
problems being discussed.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#112Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#106)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 3:53 AM Andres Freund <andres@anarazel.de> wrote:

On 2019-07-15 12:26:21 -0400, Robert Haas wrote:

Responding again with some more details.

But, my understanding of the current design being implemented is that
there is a hard limit on the number of transactions that can be
pending undo and the in-memory data structures are sized accordingly.

My understanding is that that's really just an outcome of needing to
maintain oldestXidHavingUndo accurately, right?

Yes.

I know I asked this
before, but I didn't feel like the answer was that clear (probably due
to my own haziness). To me it seems very important to understand whether
/ how much we can separate the queuing/worker logic from the question of
how to maintain oldestXidHavingUndo.

I am not sure if there is any tight coupling between queuing/worker
logic and computing oldestXid* value. The main thing to compute
oldestXid* value is that we need to know the xids of all the pending
abort transactions. We have already decided from the very beginning
that the hash table will have all the abort requests irrespective of
whether it is being processed by the foreground process or background
process. This will help us to avoid duplicate entries by backend and
background workers. Later, we decided that if we can have a hard
limit on how many pending undo requests can be present in a system,
then we can find the value of oldestXid* from the hash table.

I don't know how much it helps and you might already know all of this,
but I thought it is better to summarize to avoid any confusion.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#113Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#102)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 9:52 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jul 16, 2019 at 7:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

I also strongly suspect it is altogether wrong to do
this before CommitSubTransaction sets s->state to TRANS_COMMIT; what
if a subxact callback throws an error?

Are you worried that it might lead to the execution of actions twice?

No, I'm worried that you are running code that is part of the commit
path before the transaction has actually committed.
CommitSubTransaction() is full of stuff which basically propagates
whatever the subtransaction did out to the parent transaction, and all
of that code runs after we've ruled out the possibility of an abort,
but this very-similar-looking code runs while it's still possible for
an abort to happen. That seems unlikely to be correct, and even if it
is, it seems needlessly inconsistent.

Fair point, will change as per your suggestion.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#114Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#104)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 3:37 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-07-13 15:55:51 +0530, Amit Kapila wrote:

On Fri, Jul 12, 2019 at 7:08 PM Robert Haas <robertmhaas@gmail.com> wrote:

I think even if we currently go with a binary heap, it will be
possible to change it to rbtree later, but I am fine either way.

Well, I don't see much point in revising all of this logic twice. We
should pick the way we want it to work and make it work that way.

Yeah, I agree. So, I am assuming here that as you have discussed this
idea with Andres offlist, he is on board with changing it as he has
originally suggested using binary_heap. Andres, do let us know if you
think differently here. It would be good if anyone else following the
thread can also weigh in.

Yes, I think using an rbtree makes sense.

Okay.

I'm not yet sure whether we'd want the rbtree nodes being pointed to
directly by the hashtable, or whether we'd want one indirection.

e.g. either something like:

typedef struct UndoWorkerQueue
{
/* priority ordered tree */
RBTree *tree;
....
}

I think we also need the size of rbtree (aka how many nodes/undo
requests it has) to know whether we can add more. This information is
available in binary heap, but here I think we need to track it in
UndoWorkerQueue. Basically, at each enqueue/dequeue, we need to
increment/decrement the same.

typedef struct UndoWorkerQueueEntry
{
RBTNode tree_node;

/*
* Reference hashtable via key, not pointers, entries might be
* moved.
*/
RollbackHashKey rollback_key
...
} UndoWorkerQueueEntry;

In UndoWorkerQueueEntry, we might also want to include some other info
like dbid, request_size, next_retry_at, err_occurred_at so that while
accessing queue entry in comparator functions or other times, we don't
always need to perform hash table search. OTOH, we can do hash_search
as well, but may be code-wise it will be better to keep additional
information.

Another thing is we need some freelist/array for
UndoWorkerQueueEntries equivalent to size of three queues?

typedef struct RollbackHashEntry
{
...
UndoWorkerQueueEntry *queue_memb_size;
UndoWorkerQueueEntry *queue_memb_age;
UndoWorkerQueueEntry *queue_memb_error;
}

and call rbt_delete() for any non-NULL queue_memb_* whenever an entry is
dequeued via one of the queues (after setting the one already dequeued
from to NULL, of course). Which requires - as Robert mentioned - that
rbtree pointers remain stable after insertions.

Right.

BTW, do you have any preference for using dynahash or simplehash for
RollbackHashTable?

Alternatively we can have a more complicated arrangement without the
"stable pointer" requirement (which'd also similarly work for a binary
heap):

I think the first approach is clearly preferrable from a simplicity POV,
but the second approach would be a bit more generic (applicable to heap
as well) and wouldn't require adjusting the rbtree code.

+1 for the first approach, the second one appears to be quite
complicated as compared to first.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#115Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#114)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-07-18 11:15:05 +0530, Amit Kapila wrote:

On Wed, Jul 17, 2019 at 3:37 AM Andres Freund <andres@anarazel.de> wrote:

I'm not yet sure whether we'd want the rbtree nodes being pointed to
directly by the hashtable, or whether we'd want one indirection.

e.g. either something like:

typedef struct UndoWorkerQueue
{
/* priority ordered tree */
RBTree *tree;
....
}

I think we also need the size of rbtree (aka how many nodes/undo
requests it has) to know whether we can add more. This information is
available in binary heap, but here I think we need to track it in
UndoWorkerQueue. Basically, at each enqueue/dequeue, we need to
increment/decrement the same.

typedef struct UndoWorkerQueueEntry
{
RBTNode tree_node;

/*
* Reference hashtable via key, not pointers, entries might be
* moved.
*/
RollbackHashKey rollback_key
...
} UndoWorkerQueueEntry;

In UndoWorkerQueueEntry, we might also want to include some other info
like dbid, request_size, next_retry_at, err_occurred_at so that while
accessing queue entry in comparator functions or other times, we don't
always need to perform hash table search. OTOH, we can do hash_search
as well, but may be code-wise it will be better to keep additional
information.

The dots signal that additional fields are needed in those places.

Another thing is we need some freelist/array for
UndoWorkerQueueEntries equivalent to size of three queues?

I think using the slist as I proposed for the second alternative is
better?

BTW, do you have any preference for using dynahash or simplehash for
RollbackHashTable?

I find simplehash nicer to use in code, personally, and it's faster in
most cases...

Greetings,

Andres Freund

#116Thomas Munro
thomas.munro@gmail.com
In reply to: Robert Haas (#93)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 8:39 AM Robert Haas <robertmhaas@gmail.com> wrote:

Thomas has already objected to another proposal to add functions that
turn 32-bit XIDs into 64-bit XIDs. Therefore, I feel confident in
predicting that he will likewise object to GetEpochForXid. I think
this needs to be changed somehow, maybe by doing what the XXX comment
you added suggests.

Perhaps we should figure out how to write GetOldestFullXmin() and friends.

For FinishPreparedTransaction(), the XXX comment sounds about right
(TwoPhaseFileHeader should hold an fxid).

--
Thomas Munro
https://enterprisedb.com

#117Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#96)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 2:20 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Few comments on the new patch:

1.
Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.

Which mechanism are you talking about here? By any chance is this
related to some old code?

2.
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.

I think this is out-dated information. You seem to forget updating
README after latest changes in API.

3.
+ * The cid/xid/reloid/rmid information will be added in the undo record header
+ * in the following cases:
+ * a) The first undo record of the transaction.
+ * b) First undo record of the page.
+ * c) All subsequent record for the transaction which is not the first
+ *   transaction on the page.
+ * Except above cases,  If the rmid/reloid/xid/cid is same in the subsequent
+ * records this information will not be stored in the record, these information
+ * will be retrieved from the first undo record of that page.
+ * If any of the member rmid/reloid/xid/cid has changed, the changed
information
+ * will be stored in the undo record and the remaining information will be
+ * retrieved from the first complete undo record of the page
+ */
+UndoCompressionInfo undo_compression_info[UndoLogCategories];

a. Do we want to compress fork_number also? It is an optional field
and is only include when undo record is for not MAIN_FORKNUM. For
zheap, this means it will never be included, but in future, it could
be included for some other AM or some other use case. So, not sure if
there is any benefit in compressing the same.

b. cid/xid/reloid/rmid - I think it is better to write it as rmid,
reloid, xid, cid in the same order as you declare them in
UndoPackStage.

c. Some minor corrections. /Except above/Except for above/; /, If
the/, if the/; /is same/is the same/; /record, these
information/record rather this information/

d. I think there is no need to start the line "If any of the..." from
a new line, it can be continued where the previous line ends. Also,
at the end of that line, add a full stop.

4.
/*
+ * Copy the compression global compression info to our context before
+ * starting prepare because this value might get updated multiple time in
+ * case of multi-prepare but the global value should be updated only after
+ * we have successfully inserted the undo record.
+ */

In the above comment, the first 'compression' is not required. /time/times/

5.
+/*
+ * The below common information will be stored in the first undo
record of the page.
+ * Every subsequent undo record will not store this information, if
required this information
+ * will be retrieved from the first undo record of the page.
+ */
+typedef struct UndoCompressionInfo

The line length in the above comments exceeds the 80-char limit. You
might want to run pgindent to avoid such problems.

6.
+/*
+ * Exclude the common info in undo record flag and also set the compression
+ * info in the context.
+ *

'flag' seems to be a redundant word here?

7.
+UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+   UnpackedUndoRecord *urec, UndoRecPtr urp,
+   Buffer buffer)
+{
+
+ /*
+ * If we have valid compression info and the for the same transaction and
+ * the current undo record is on the same block as the last undo record
+ * then exclude the common information which are same as first complete
+ * record on the page.
+ */
+ if (compressioninfo->valid &&
+ FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+ UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))

Here the comment is just a verbal for of if-check. How about writing
it as: "Exclude the common information from the record which is same
as the first record on the page."

8.
UndoSetCommonInfo()
{
..
if (compressioninfo->valid &&
+ FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+ UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))
+ {
+ urec->uur_info &= ~UREC_INFO_XID;
+
+ /* Don't include rmid if it's same. */
+ if (urec->uur_rmid == compressioninfo->rmid)
+ urec->uur_info &= ~UREC_INFO_RMID;
+
+ /* Don't include reloid if it's same. */
+ if (urec->uur_reloid == compressioninfo->reloid)
+ urec->uur_info &= ~UREC_INFO_RELOID;

In all the checks except for transaction id, urec's info is on the
left side. I think all the checks can be consistent.

These are some of the things I noticed while skimming through this
patch. I will do some more detailed review later.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#118Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#71)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Fri, Jun 28, 2019 at 6:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

I happened to open up 0001 from this series, which is from Thomas, and
I do not think that the pg_buffercache changes are correct. The idea
here is that the customer might install version 1.3 or any prior
version on an old release, then upgrade to PostgreSQL 13. When they
do, they will be running with the old SQL definitions and the new
binaries. At that point, it sure looks to me like the code in
pg_buffercache_pages.c is going to do the Wrong Thing. [...]

Yep, that was completely wrong. Here's a new version.

One comment/question related to
0022-Use-undo-based-rollback-to-clean-up-files-on-abort.patch.

+make_undo_smgr_create(RelFileNode *rnode, FullTransactionId fxid,
+   XLogReaderState *xlog_record)
+{
+ UnpackedUndoRecord undorecord = {0};
+ UndoRecordInsertContext context;
+
+ undorecord.uur_rmid = RM_SMGR_ID;
+ undorecord.uur_type = UNDO_SMGR_CREATE;
+ undorecord.uur_info = UREC_INFO_PAYLOAD;
+ undorecord.uur_dbid = rnode->dbNode;
+ undorecord.uur_xid = XidFromFullTransactionId(fxid);
+ undorecord.uur_cid = InvalidCommandId;
+ undorecord.uur_fork = InvalidForkNumber;

While reviewing Dilip's patch(undo-record-interface), I noticed that
we include Fork_Num in undo record, if it is not a MAIN_FORKNUM. So,
in this patch's case, we will always include it as you are passing
InvalidForkNumber. I also see that the patch doesn't use uur_fork in
the undo record handler, so I think you don't care what is its value.
I am not sure what is the best thing to do here, but it might be
better if we can avoiding adding fork_num in each undo record.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#119Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#111)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 17, 2019 at 2:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

We add entries in queues only when we want them to be processed by
background workers whereas hash table will contain the entries for all
the pending undo requests irrespective of whether they are executed by
foreground-transaction or by background workers. Once the request is
processed, we remove it from the hash table. The reasons for keeping
all the pending abort requests in hash table is that it allows us to
compute oldestXidHavingUnappliedUndo and second is it avoids us to
have duplicate undo requests by backends and discard worker. In
short, there is no reason to keep all the entries in queues, but there
are reasons to keep all the aborted xact entries in hash table.

I think we're drifting off on a tangent here. That does make sense,
but my original comment that led to this discussion was
"PerformUndoActions() also thinks that there is a possibility of
failing to insert a failed request into the error queue, and makes
reference to such requests being rediscovered by the discard worker,
..." and none of what you've written explains why there is or should
be a possibility of failing to insert a request into the error queue.
I feel like we've discussed this point to death. You just make the
maximum size of the queue equal to the maximum size of the hash table,
and it can't ever fail to have room for a new entry. If you remove
entries lazily, then it can, but any time it does, you can just go and
clean out all of the dead entries and you're guaranteed to then have
enough room. And if we switch to rbtree then we won't do lazy removal
any more, and it won't matter anyway.

Anyway, if you don't like this solution, propose something else. It's
impossible to correctly implement a hard limit unless the number of
aborted-but-not-yet-undone transaction is bounded to (HARD_LIMIT -
ENTRIES_THAT_WOULD_BE_ADDED_AFTER_RECOVERY_IF_THE_SYSTEM_CRASHED_NOW).
If there are 100 transactions each bound to 2 undo logs, and you
crash, you will need to (as you have it designed now) add another 200
transactions to the hash table upon recovery, and that will make you
exceed the hard limit unless you were at least 200 transactions below
the limit before the crash. Have you handled that somehow? If so,
how?

Yeah, we have handled it by reserving the space of MaxBackends. It is
UndoRollbackHashTableSize() - MaxBackends. There is a bug in the
current patch which is that it should reserve space for 2 *
MaxBackends so that after recovery, we are safe, but that can be
fixed.

One of us is REALLY confused here. Nothing you do in
UndoRollbackHashTableSize() can possibly fix the problem that I'm
talking about. Suppose the system gets to a point where all of the
rollback hash table entries are in use - there are some entries that
are used because work was pushed into the background, and then there
are other entries that are present because those transactions are
being rolled back in the foreground. Now at this point you crash. Now
when you start up, all the hash table entries, including the reserved
ones, are already in use before any running transactions start. Now
if you allow transactions to start before some of the rollbacks
complete, you have got big problems. The system might crash again,
and if it does, when it restarts, the total amount of outstanding
requests will no longer fit in the hash table, which was the whole
premise of this design.

Maybe that doesn't make sense, so think about it this way. Suppose
the following happens repeatedly: the system starts, someone begins a
transaction that writes an undo record, the rollback workers start up
but don't make very much progress because the system is heavily loaded
or whatever reason, the system crashes, rinse, repeat. Since no
transactions got successfully rolled back and 1 new transaction that
needs roll back got added, the number of transactions pending rollback
has increased by one. Now, however big you made the hash table, just
repeat this process that number of times plus one, and the hash table
overflows. The only way you can prevent that is if you stop the
transaction from writing undo when the hash table is already too full.

I have responded to it as a separate email, but let's discuss it here.
So, you are right that only time we need to scan the undo logs to find
all pending aborted xacts is immediately after startup. But, we can't
create a fully update-to-date entry from both the logs unless we make
undo launcher to also wait to process anything till we are done. We
are not doing this in the current patch but we can do it if we want.
This will be an additional restriction we have to put which is not
required for the current approach.

I mean, that is just not true. There's no fundamental difference
between having two possible entries each of which looks like this:

struct entry { txn_details d; };

And having a single entry that looks like this:

struct entry { txn_details permanent; txn_details unlogged; bool
using_permanent; bool using_unlogged; };

I mean, I'm not saying you would actually want to do exactly the
second thing, but arguing that something cannot be done with one
design or the other is just not correct.

Another related thing is that to update the existing entry for queues,
we need to delete and re-insert the entry after we find the request in
a different log category. Again it depends if we point queue entries
to hash table, then we might not have this additional work but that
has its own set of complexities.

I don't follow this. If you have a hash table where the key is XID,
there is no need to delete and reinsert anything just because you
discover that the XID has not only permanent undo but also unlogged
undo, or something of that sort.

I think it is implementation wise simpler to have one entry per
persistence level. It is not that we can't deal with all the
problems being discussed.

It's possible that it's simpler, but I'm not finding the arguments
you're making very convincing.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#120Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#119)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 12:28 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Jul 17, 2019 at 2:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Anyway, if you don't like this solution, propose something else. It's
impossible to correctly implement a hard limit unless the number of
aborted-but-not-yet-undone transaction is bounded to (HARD_LIMIT -
ENTRIES_THAT_WOULD_BE_ADDED_AFTER_RECOVERY_IF_THE_SYSTEM_CRASHED_NOW).
If there are 100 transactions each bound to 2 undo logs, and you
crash, you will need to (as you have it designed now) add another 200
transactions to the hash table upon recovery, and that will make you
exceed the hard limit unless you were at least 200 transactions below
the limit before the crash. Have you handled that somehow? If so,
how?

Yeah, we have handled it by reserving the space of MaxBackends. It is
UndoRollbackHashTableSize() - MaxBackends. There is a bug in the
current patch which is that it should reserve space for 2 *
MaxBackends so that after recovery, we are safe, but that can be
fixed.

One of us is REALLY confused here. Nothing you do in
UndoRollbackHashTableSize() can possibly fix the problem that I'm
talking about. Suppose the system gets to a point where all of the
rollback hash table entries are in use - there are some entries that
are used because work was pushed into the background, and then there
are other entries that are present because those transactions are
being rolled back in the foreground.

We are doing exactly what you have written in the last line of the
next paragraph "stop the transaction from writing undo when the hash
table is already too full.". So we will
never face the problems related to repeated crash recovery. The
definition of too full is that we stop allowing the new transactions
that can write undo when we have the hash table already have entries
equivalent to (UndoRollbackHashTableSize() - MaxBackends). Does this
make sense?

Now at this point you crash. Now
when you start up, all the hash table entries, including the reserved
ones, are already in use before any running transactions start. Now
if you allow transactions to start before some of the rollbacks
complete, you have got big problems. The system might crash again,
and if it does, when it restarts, the total amount of outstanding
requests will no longer fit in the hash table, which was the whole
premise of this design.

Maybe that doesn't make sense, so think about it this way.

All you are saying makes sense and I think I can understand the
problem you are trying to describe, but we have thought about the same
thing and have the algorithm/code in place which won't allow such
situations.

Another related thing is that to update the existing entry for queues,
we need to delete and re-insert the entry after we find the request in
a different log category. Again it depends if we point queue entries
to hash table, then we might not have this additional work but that
has its own set of complexities.

I don't follow this. If you have a hash table where the key is XID,
there is no need to delete and reinsert anything just because you
discover that the XID has not only permanent undo but also unlogged
undo, or something of that sort.

The size of the total undo to be processed will be changed if we find
anyone (permanent or unlogged) later. Based on the size, the entry
location should be changed in size queue.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#121Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#87)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 11, 2019 at 9:17 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

This information won't be shared across transactions, so can't we keep
it in top transaction's state? It seems to me that will be better
than to maintain it as a global state.

Few more comments on this patch:
1.
PrepareUndoInsert()
{
..
+ if (logswitched)
+ {
..
+ }
+ else
+ {
..
+ resize = true;
..
+ }
+
..
+
+ do
+ {
+ bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
..
+ rbm = RBM_ZERO;
+ cur_blk++;
+ } while (cur_size < size);
+
+ /*
+ * Set/overwrite compression info if required and also exclude the common
+ * fields from the undo record if possible.
+ */
+ if (UndoSetCommonInfo(compression_info, urec, urecptr,
+   context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf))
+ resize = true;
+
+ if (resize)
+ size = UndoRecordExpectedSize(urec);

I see that in some cases where resize is possible are checked before
buffer allocation and some are after. Isn't it better to do all these
checks before buffer allocation? Also, isn't it better to even
compute changed size before buffer allocation as that might sometimes
help in lesser buffer allocations?

Can you find a better way to write
:context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf)?
It makes the line too long and difficult to understand. Check for
similar instances in the patch and if possible, change them as well.

2.
+InsertPreparedUndo(UndoRecordInsertContext *context)
{
..
/*
+ * Try to insert the record into the current page. If it
+ * doesn't succeed then recall the routine with the next page.
+ */
+ InsertUndoData(&ucontext, page, starting_byte);
+ if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+ {
+ MarkBufferDirty(buffer);
+ break;
+ }
+ MarkBufferDirty(buffer);
..
}

Can't we call MarkBufferDirty(buffer) just before 'if' check? That
will avoid calling it twice.

3.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer

/thse/these

4.
+ /*
+ * If we are writing first undo record for the page the we can set the
+ * compression so that subsequent records from the same transaction can
+ * avoid including common information in the undo records.
+ */
+ if (first_complete_undo)

/page the we/page then we

5.
PrepareUndoInsert()
{
..
After
+ * allocation We'll only advance by as many bytes as we turn out to need.
+ */
+ UndoRecordSetInfo(urec);

Change the beginning of comment as: "After allocation, we'll .."

6.
PrepareUndoInsert()
{
..
* TODO:  instead of storing this in the transaction header we can
+ * have separate undo log switch header and store it there.
+ */
+ prevlogurp =
+ MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+    (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+

I don't think this TODO is valid anymore because now the patch has a
separate log-switch header.

7.
/*
+ * If undo log is switched then set the logswitch flag and also reset the
+ * compression info because we can use same compression info for the new
+ * undo log.
+ */
+ if (UndoRecPtrIsValid(prevlog_xact_start))

/can/can't

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#122Amit Khandekar
amitdkhan.pg@gmail.com
In reply to: Dilip Kumar (#27)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, 9 May 2019 at 12:04, Dilip Kumar <dilipbalaut@gmail.com> wrote:

Patches can be applied on top of undo branch [1] commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

[1] https://github.com/EnterpriseDB/zheap/tree/undo

Below are some review points for 0009-undo-page-consistency-checker.patch :

+ /* Calculate the size of the partial record. */
+ partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+ phdr->tuple_len + phdr->payload_len -
+ phdr->record_offset;

There is already an UndoPagePartialRecSize() function which calculates
the size of partial record, which seems to do the same as above. If
this is same, you can omit the above code, and instead down below
where you increment next_record, you can do "next_record +=
UndoPagePartialRecSize()".

Also, I see an extra sizeof(uint16) added in
UndoPagePartialRecSize(). Not sure which one is correct, and which one
wrong, unless I am wrong in assuming that the above calculation and
the function definition do the same thing.

------------------

+ * We just want to mask the cid in the undo record header.  So
+ * only if the partial record in the current page include the undo
+ * record header then we need to mask the cid bytes in this page.
+ * Otherwise, directly jump to the next record.
Here, I think you mean : "So only if the partial record in the current
page includes the *cid* bytes", rather than "includes the undo record
header"
May be we can say :
We just want to mask the cid. So do the partial record masking only if
the current page includes the cid bytes from the partial record
header.

----------------

+ if (phdr->record_offset < (cid_offset + sizeof(CommandId)))
+ {
+    char    *cid_data;
+    Size mask_size;
+
+    mask_size = Min(cid_offset - phdr->record_offset,
+    sizeof(CommandId));
+
+    cid_data = next_record + cid_offset - phdr->record_offset;
+    memset(&cid_data, MASK_MARKER, mask_size);
+
Here, if record_offset lies *between* cid start and cid end, then
cid_offset - phdr->record_offset will be negative, and so will be
mask_size. Probably abs() should do the work.

Also, an Assert(cid_data + mask_size <= page_end) would be nice. I
know cid position of a partial record cannot go beyond the page
boundary, but it's better to have this Assert to do sanity check.

+ * Process the undo record of the page and mask their cid filed.
filed => field

--
Thanks,
-Amit Khandekar
EnterpriseDB Corporation
The Postgres Database Company

#123Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#120)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 12:10 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

We are doing exactly what you have written in the last line of the
next paragraph "stop the transaction from writing undo when the hash
table is already too full.". So we will
never face the problems related to repeated crash recovery. The
definition of too full is that we stop allowing the new transactions
that can write undo when we have the hash table already have entries
equivalent to (UndoRollbackHashTableSize() - MaxBackends). Does this
make sense?

Oops, I was looking in the wrong place. Yes, that makes sense, but:

1. It looks like the test you added to PrepareUndoInsert has no
locking, and I don't see how that can be right.

2. It seems like this is would result in testing for each new undo
insertion that gets prepared, whereas surely we would want to only
test when first attaching to an undo log. If you've already attached
to the undo log, there's no reason not to continue inserting into it,
because doing so doesn't increase the number of transactions (or
transaction-persistence level combinations) that need undo.

3. I don't think the test itself is correct. It can fire even when
there's no problem. It is correct (or would be if it said 2 *
MaxBackends) if every other backend in the system is already attached
to an undo log (or two). But if they are not, it will block
transactions from being started for no reason. For instance, suppose
max_connections = 100 and there are another 100 slots for background
rollbacks. Now suppose that the system crashes when 101 slots are in
use -- 100 pushed into the background plus 1 that was aborted by the
crash. On recovery, this test will refuse to let any new transaction
start. Actually it is OK for up to 99 transactions to write undo, just
not 100. Or, given that you have a slot per persistence level, it's
OK to have up to 199 transaction-persistence-level combinations in
flight, just not 200. And that is the difference between the system
being unusable after the crash until a rollback succeeds and being
almost fully usable immediately.

I don't follow this. If you have a hash table where the key is XID,
there is no need to delete and reinsert anything just because you
discover that the XID has not only permanent undo but also unlogged
undo, or something of that sort.

The size of the total undo to be processed will be changed if we find
anyone (permanent or unlogged) later. Based on the size, the entry
location should be changed in size queue.

OK, true. But that's not a significant cost, either in runtime or code
complexity.

I still don't really see any good reason to the hash table key be
anything other than XID, or really, FXID. I mean, sure, the data
structure manipulations are a little different, but not in any way
that really matters. And it seems to me that there are some benefits,
the biggest of which is that the system becomes easier for users to
understand. We can simply say that there is a limit on the number of
transactions that either (1) are in progress and have written undo or
(2) have aborted and not all of the undo has been processed. If the
key is XID + persistence level, then it's a limit on the number of
transaction-and-persistence-level combinations, which I feel is not so
easy to understand. In most but not all scenarios, it means that the
limit is about double what you think the limit is, and as the mistake
in the current version of the patch makes clear, even the people
writing the code can forget about that factor of two.

It affects a few other things, too. If you made the key XID and fixed
problems (2) and (3) from above, then you'd have a situation where a
transaction could fail at only one times: either it bombs the first
time it tries to write undo, or it works. As it is, there is a second
failure scenario: you do a bunch of work on permanent (or unlogged)
tables and then try to write to an unlogged (or permanent) table and
it fails because there are not enough slots. Is that the end of the
world? No, certainly not. The situation should be rare. But if we have
to fail transactions, it's best to fail them before they've started
doing any work, because that minimizes the amount of work we waste by
having to retry. Of course, a transaction that fails midway through
when it tries to write at a second persistence level is also consuming
an undo slot in a situation where we're short of undo slots.

Another thing which Andres pointed out to me off-list is that we might
want to have a function that takes a transaction ID as an argument and
tells you the status of that transaction from the point of view of the
undo machinery: does it have any undo, and if so how much? As you have
it now, such a function would require searching the whole hash table,
because the user won't be able to provide an UndoRecPtr to go with the
XID. If the hash table key were in fact <XID, undo persistence level>
rather than <XID, UndoRecPtr>, then you could it with two lookups; if
it were XID alone, you could do it with one lookup. The difference
between one lookup and two is not significant, but having to search
the whole hash table is.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#124Robert Haas
robertmhaas@gmail.com
In reply to: Amit Khandekar (#122)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 7:54 AM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

+ * We just want to mask the cid in the undo record header.  So
+ * only if the partial record in the current page include the undo
+ * record header then we need to mask the cid bytes in this page.
+ * Otherwise, directly jump to the next record.
Here, I think you mean : "So only if the partial record in the current
page includes the *cid* bytes", rather than "includes the undo record
header"
May be we can say :
We just want to mask the cid. So do the partial record masking only if
the current page includes the cid bytes from the partial record
header.

Hmm, but why is it correct to mask the CID at all? Shouldn't that match?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#125Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#106)
should there be a hard-limit on the number of transactions pending undo?

On Tue, Jul 16, 2019 at 6:23 PM Andres Freund <andres@anarazel.de> wrote:

Yea, that seems like a question independent of the "completeness"
requirement. If desirable, it seems trivial to either have
RollbackHashEntry have per-persistence level status (for one entry per
xid), or not (for per-persistence entries).

I want to talk more about the "completeness" issue, which is basically
a question of whether we should (a) put a hard limit on the number of
transactions that have unprocessed undo and that are either aborted or
in progress (and thus capable of aborting) as proposed by Andres or
(b) not have such a hard limit, as originally proposed by me. I think
everyone who is not working on this code has installed an automatic
filter rule to send the original thread to /dev/null, so I'm changing
the subject line in the hopes of getting some of those people to pay
attention. If it doesn't work, at least the concerns will be
memorialized in case it comes up later.

I originally proposed (b) because if undo application is failing for
some reason, it seemed better not to bring the whole system to a
screeching halt, but rather just cause incremental performance
degradation or something. However, Andres has pointed out this may
postpone remedial action and let bugs go undetected, so it might not
actually be better. Also, some of the things we need to do, like
computing the oldest XID whose undo has not been retired, are
tricky/expensive if you don't have complete data in memory, and you
can't be sure of having complete data in shared memory without a hard
limit. No matter which way we go, failures to apply undo had better
be really rare, or we're going to be in really serious trouble, so
we're only concerned here with how to handle what is hopefully a very
rare scenario, not the common case.

I want to consider three specific scenarios that could cause undo
application to fail, and then offer some observations about them.

Scenario #1:

1. Sessions 1..N each begin a transaction and write a bunch of data to
a table (at least enough that we'll try to perform undo in the
background).
2. Session N+1 begins a transaction and tries to lock the same table.
It blocks.
3. Sessions 1..N abort, successfully pushing the undo work into the background.
4. Session N+1 now acquires the lock and sits on it.
5. Optionally, repeat steps 1-4 K times, each time for a different table.

Scenario #2:

1. Any number of sessions begin a transaction, write a bunch of data,
and then abort.
2. They all try to perform undo in the foreground.
3. They get killed using pg_terminate_backend().

Scenario #3:

1. A transaction begins, does some work, and then aborts.
2. When undo processing occurs, 1% of such transactions fail during
undo apply because of a bug in the table AM.
3. When undo processing retries after a failure, it fails again
because the bug is triggered by something about the contents of the
undo record, rather than by, say, concurrency.

In scenario one, the problem is mostly self-correcting. When we decide
that we've got too many things queued up for background processing,
and start to force undo processing to happen in the foreground, it
will start succeeding, because the foreground process will have
retained the lock that it took before writing any data and can
therefore undo those writes without having to wait for the lock.
However, this will do nothing to help the requests that are already in
the background, which will just sit there until they can get the lock.
I think there is a good argument that they should not actually wait
for the lock, or only for a certain time period, and then give up and
put the transaction on the error queue for reprocessing at a later
time. Otherwise, we're pinning down undo workers, which could easily
lead to starvation, just as it does for autovacuum. On the whole, this
doesn't sound too bad. We shouldn't be able to fill up the queue with
small transactions, because of the restriction that we only push undo
work into the background when the transaction is big enough, and if we
fill it up with big transactions, then (1) back-pressure will prevent
the problem from crippling the system and (2) eventually the problem
will be self-correcting, because when the transaction in session N+1
ends, the undo will all go through and everything will be fine. The
only real user impact of this scenario is that unrelated work on the
system might notice that large rollbacks are now happening in the
foreground rather than the background, and if that causes a problem,
the DBA can fix it by terminating session N+1. Even if she doesn't,
you shouldn't ever hit the hard cap.

However, if prepared transactions are in use, we could have a variant
of scenario #1 in which each transaction is first prepared, and then
the prepared transaction is rolled back. Unlike the ordinary case,
this can lead to a nearly-unbounded growth in the number of
transactions that are pending undo, because we don't have a way to
transfer the locks held by PGPROC used for the prepare to some running
session that could perform the undo. It's not necessary to have a
large value for max_prepared_transactions; it only has to be greater
than 0, because we can keep reusing the same slots with different
tables. That is, let N = max_prepared_xacts, and let K be anything at
all; session N+1 can just stay in the same transaction and keep on
taking new locks one at a time until the lock table fills up; not sure
exactly how long that will take, but it's probably a five digit number
of transactions, or maybe six. In this case, we can't force undo into
the foreground, so we can exceed the number of transactions that are
supposed to be backgrounded. We'll eventually have to just start
refusing new transactions permission to attach to an undo log, and
they'll error out. Although unpleasant, I don't think that this
scenario is a death sentence for the idea of having a hard cap on the
table size, because if we can the cap is 100k or so, you shouldn't
really hit it unless you specifically make it your goal to do so. At
least, not this way. But if you have a lower cap, like 1k, it doesn't
seem crazy to think that you could hit this in a non-artificial
scenario; you just need lots of rolled-back prepared transactions plus
some long-running DDL.

We could mitigate the prepared transaction scenario by providing a way
to transfer locks from the prepared transaction to the backend doing
the ROLLBACK PREPARED and then make it try to execute the undo
actions. I think that would bring this scenario into parity with the
non-prepared case. We could still try to background large rollbacks,
but if the queue gets too full then ROLLBACK PREPARED would do the
work instead, and, with the hypothetical lock transfer mechanism, that
would dodge the locking issues.

In scenario #2, the undo work is going to have to be retried in the
background, and perforce that means reacquiring locks that have been
released, and so there is a chance of long lock waits and/or deadlock
that cannot really be avoided. I think there is basically no way at
all to avoid an unbounded accumulation of transactions requiring undo
in this case, just as in the similar case where the cluster is
repeatedly shut down or repeatedly crashes. Eventually, if you have a
hard cap on the number of transactions requiring undo, you're going to
hit it, and have to start refusing new undo-using transactions. As
Thomas pointed out, that might still be better than some other systems
which use undo, where the system doesn't open for any transactions at
all after a restart until all undo is retired, and/or where undo is
never processed in the background. But it's a possible concern. On the
other hand, if you don't have a hard cap, the system may just get
further and further behind until it eventually melts, and that's also
a possible concern.

How plausible is this scenario? For most users, cluster restarts and
crashes are uncommon, so that variant isn't likely to happen unless
something else is going badly wrong. As to the scenario as written,
it's not crazy to think that a DBA might try to kill off sessions that
sitting there stuck in undo processing for long periods of time, but
that doesn't make it a good idea. Whatever problems it causes are
analogous to the problems you get if you keep killing of autovacuum
processes: the system is trying to make you do the right thing, and if
you fight it, you will have some kind of trouble no matter what design
decisions we make.

In scenario #3, the hard limit is likely to bring things to a
screeching halt pretty quickly; you'll just run out of space in the
in-memory data structures. Otherwise, the problem will not be obvious
unless you're keeping an eye on error messages in your logs, the first
sign of trouble may be that the undo logs fill up the disk. It's not
really clear which is better. There is value in knowing about the
problem sooner (because then you can file a bug report right away and
get a fix sooner) but there is also value in having the system limp
along instead of grinding to a halt (because then you might not be
totally down while you're waiting for that bug fix to become
available).

One other thing that seems worth noting is that we have to consider
what happens after a restart. After a crash, and depending on exactly
how we design it perhaps also after a non-crash restart, we won't
immediately know how many outstanding transactions need undo; we'll
have to grovel through the undo logs to find out. If we've got a hard
cap, we can't allow new undo-using transactions to start until we
finish that work. It's possible that, at the moment of the crash, the
maximum number of items had already been pushed into the background,
and every foreground session was busy trying to undo an abort as well.
If so, we're already up against the limit. We'll have to scan through
all of the undo logs and examine each transaction to get a count on
how many transactions are already in a needs-undo-work state; only
once we have that value do we know whether it's OK to admit new
transactions to using the undo machinery, and how many we can admit.
In typical cases, that won't take long at all, because there won't be
any pending undo work, or not much, and we'll very quickly read the
handful of transaction headers that we need to consult and away we go.
However, if the hard limit is pretty big, and we're pretty close to
it, counting might take a long time. It seems bothersome to have this
interval between when we start accepting transactions and when we can
accept transactions that use undo. Instead of throwing an ERROR, we
can probably just teach the system to wait for the background process
to finish doing the counting; that's what Amit's patch does currently.
Or, we could not even open for connections until the counting has been
completed.

When I first thought about this, I was really concerned about the idea
of a hard limit, but the more I think about it the less problematic it
seems. I think in the end it boils down to a question of: when things
break, what behavior would users prefer? You can either have a fairly
quick, hard breakage which will definitely get your attention, or you
can have a long, slow process of gradual degradation that doesn't
actually stop the system until, say, the XIDs stuck in the undo
processing queue become old enough to threaten wraparound, or the disk
fills up. Which is less evil?

Thanks,

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#126Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#125)
Re: should there be a hard-limit on the number of transactions pending undo?

Hi,

On 2019-07-19 13:28:14 -0400, Robert Haas wrote:

I want to consider three specific scenarios that could cause undo
application to fail, and then offer some observations about them.

Scenario #1:

1. Sessions 1..N each begin a transaction and write a bunch of data to
a table (at least enough that we'll try to perform undo in the
background).
2. Session N+1 begins a transaction and tries to lock the same table.
It blocks.
3. Sessions 1..N abort, successfully pushing the undo work into the background.
4. Session N+1 now acquires the lock and sits on it.
5. Optionally, repeat steps 1-4 K times, each time for a different table.

Scenario #2:

1. Any number of sessions begin a transaction, write a bunch of data,
and then abort.
2. They all try to perform undo in the foreground.
3. They get killed using pg_terminate_backend().

Scenario #3:

1. A transaction begins, does some work, and then aborts.
2. When undo processing occurs, 1% of such transactions fail during
undo apply because of a bug in the table AM.
3. When undo processing retries after a failure, it fails again
because the bug is triggered by something about the contents of the
undo record, rather than by, say, concurrency.

However, if prepared transactions are in use, we could have a variant
of scenario #1 in which each transaction is first prepared, and then
the prepared transaction is rolled back. Unlike the ordinary case,
this can lead to a nearly-unbounded growth in the number of
transactions that are pending undo, because we don't have a way to
transfer the locks held by PGPROC used for the prepare to some running
session that could perform the undo.

It doesn't seem that hard - and kind of required for robustness
independent of the decision around "completeness" - to find a way to use
the locks already held by the prepared transaction.

It's not necessary to have a
large value for max_prepared_transactions; it only has to be greater
than 0, because we can keep reusing the same slots with different
tables. That is, let N = max_prepared_xacts, and let K be anything at
all; session N+1 can just stay in the same transaction and keep on
taking new locks one at a time until the lock table fills up; not sure
exactly how long that will take, but it's probably a five digit number
of transactions, or maybe six. In this case, we can't force undo into
the foreground, so we can exceed the number of transactions that are
supposed to be backgrounded.

I'm not following, unfortunately.

I don't understand what exactly the scenario is you refer to. You say
"session N+1 can just stay in the same transaction", but then you also
reference something taking "probably a five digit number of
transactions". Are those transactions the prepared ones?

Aloso, if someobdy fills up the entire lock table, then the system is
effectively down - independent of UNDO, and no meaningful amount of UNDO
is going to be written. Perhaps we need some better resource control,
but that's really independent of UNDO.

Perhaps you can just explain the scenario in a few more words? My
comments regarding it probably make no sense, given how little I
understand what the scenario is.

In scenario #2, the undo work is going to have to be retried in the
background, and perforce that means reacquiring locks that have been
released, and so there is a chance of long lock waits and/or deadlock
that cannot really be avoided. I think there is basically no way at
all to avoid an unbounded accumulation of transactions requiring undo
in this case, just as in the similar case where the cluster is
repeatedly shut down or repeatedly crashes. Eventually, if you have a
hard cap on the number of transactions requiring undo, you're going to
hit it, and have to start refusing new undo-using transactions. As
Thomas pointed out, that might still be better than some other systems
which use undo, where the system doesn't open for any transactions at
all after a restart until all undo is retired, and/or where undo is
never processed in the background. But it's a possible concern. On the
other hand, if you don't have a hard cap, the system may just get
further and further behind until it eventually melts, and that's also
a possible concern.

You could force new connections to complete the rollback processing of
the terminated connection, if there's too much pending UNDO. That'd be a
way of providing back-pressure against such crazy scenarios. Seems
again that it'd be good to have that pressure, independent of the
decision on completeness.

One other thing that seems worth noting is that we have to consider
what happens after a restart. After a crash, and depending on exactly
how we design it perhaps also after a non-crash restart, we won't
immediately know how many outstanding transactions need undo; we'll
have to grovel through the undo logs to find out. If we've got a hard
cap, we can't allow new undo-using transactions to start until we
finish that work.

Couldn't we record the outstanding transactions in the checkpoint, and
then recompute the changes to that record during WAL replay?

When I first thought about this, I was really concerned about the idea
of a hard limit, but the more I think about it the less problematic it
seems. I think in the end it boils down to a question of: when things
break, what behavior would users prefer? You can either have a fairly
quick, hard breakage which will definitely get your attention, or you
can have a long, slow process of gradual degradation that doesn't
actually stop the system until, say, the XIDs stuck in the undo
processing queue become old enough to threaten wraparound, or the disk
fills up. Which is less evil?

Yea, I think that's what it boils down to... Would be good to have a few
more opinions on this.

Greetings,

Andres Freund

#127Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#126)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 2:04 PM Andres Freund <andres@anarazel.de> wrote:

It doesn't seem that hard - and kind of required for robustness
independent of the decision around "completeness" - to find a way to use
the locks already held by the prepared transaction.

I'm not wild about finding more subtasks to put on the must-do list,
but I agree it's doable.

I'm not following, unfortunately.

I don't understand what exactly the scenario is you refer to. You say
"session N+1 can just stay in the same transaction", but then you also
reference something taking "probably a five digit number of
transactions". Are those transactions the prepared ones?

So you begin a bunch of transactions. All but one of them begin a
transaction, insert data into a table, and then prepare. The last one
begins a transaction and locks the table. Now you roll back all the
prepared transactions. Those sessions now begin new transactions,
insert data into a second table, and prepare the second set of
transactions. The last session, which still has the first table
locked, now locks the second table in addition. Now you again roll
back all the prepared transactions. At this point you have 2 *
max_prepared_transactions that are waiting for undo, all blocked on
that last session that holds locks on both tables. So now you go have
all of those sessions begin a third transaction, and they all insert
into a third table, and prepare. The last session now attempts AEL on
that third table, and once it's waiting, you roll back all the
prepared transactions, after which that last session successfully
picks up its third table lock.

You can keep repeating this, locking a new table each time, until you
run out of lock table space, by which time you will have roughly
max_prepared_transactions * size_of_lock_table transactions waiting
for undo processing.

You could force new connections to complete the rollback processing of
the terminated connection, if there's too much pending UNDO. That'd be a
way of providing back-pressure against such crazy scenarios. Seems
again that it'd be good to have that pressure, independent of the
decision on completeness.

That would definitely provide a whole lot of back-pressure, but it
would also make the system unusable if the undo handler finds a way to
FATAL, or just hangs for some stupid reason (stuck I/O?). It would be
a shame if the administrative action needed to fix the problem were
prevented by the back-pressure mechanism.

One thing I've thought about, which I think would be helpful for a
variety of scenarios, is to have a facility that forces a computed
delay at the each write transaction (when it first writes WAL, or when
an XID is assigned), or we could adapt that to this case and say the
beginning of each undo-using transaction. So for example if you are
about to run out of space in pg_wal, you can slow thinks down to let
the checkpoint complete, or if you are about to run out of XIDs, you
can slow things down to let autovacuum complete, or if you are about
to run out of undo slots, you can slow things down to let some undo to
complete. The trick is to make sure that you only wait when it's
likely to do some good; if you wait because you're running out of XIDs
and the reason you're running out of XIDs is because somebody left a
replication slot or a prepared transaction around, the back-pressure
is useless.

Couldn't we record the outstanding transactions in the checkpoint, and
then recompute the changes to that record during WAL replay?

Hmm, that's not a bad idea. So the transactions would have to "count"
the moment they insert their first undo record, which is exactly the
right thing anyway.

Hmm, but what about transactions that are only touching unlogged tables?

Yea, I think that's what it boils down to... Would be good to have a few
more opinions on this.

+1.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

In reply to: Robert Haas (#125)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 10:28 AM Robert Haas <robertmhaas@gmail.com> wrote:

In scenario #2, the undo work is going to have to be retried in the
background, and perforce that means reacquiring locks that have been
released, and so there is a chance of long lock waits and/or deadlock
that cannot really be avoided.

I haven't studied the UNDO or zheap stuff in any detail, but I am
concerned about rollbacks that deadlock. I'd feel a lot better about
it if forward progress was guaranteed, somehow. That seems to imply
that locks are retained, which is probably massively inconvenient to
ensure. Not least because it probably requires cooperation from
underlying access methods.

--
Peter Geoghegan

#129Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#127)
Re: should there be a hard-limit on the number of transactions pending undo?

Hi,

On 2019-07-19 14:50:22 -0400, Robert Haas wrote:

On Fri, Jul 19, 2019 at 2:04 PM Andres Freund <andres@anarazel.de> wrote:

It doesn't seem that hard - and kind of required for robustness
independent of the decision around "completeness" - to find a way to use
the locks already held by the prepared transaction.

I'm not wild about finding more subtasks to put on the must-do list,
but I agree it's doable.

Isn't that pretty inherently required? How are otherwise ever going to
be able to roll back a transaction that holds an AEL on a relation it
also modifies? I might be standing on my own head here, though.

You could force new connections to complete the rollback processing of
the terminated connection, if there's too much pending UNDO. That'd be a
way of providing back-pressure against such crazy scenarios. Seems
again that it'd be good to have that pressure, independent of the
decision on completeness.

That would definitely provide a whole lot of back-pressure, but it
would also make the system unusable if the undo handler finds a way to
FATAL, or just hangs for some stupid reason (stuck I/O?). It would be
a shame if the administrative action needed to fix the problem were
prevented by the back-pressure mechanism.

Well, then perhaps that admin ought not to constantly terminate
connections... I was thinking that new connections wouldn't be forced
to do that if there were still a lot of headroom regarding
#transactions-to-be-rolled-back. And if undo workers kept up, you'd
also not hit this.

Couldn't we record the outstanding transactions in the checkpoint, and
then recompute the changes to that record during WAL replay?

Hmm, that's not a bad idea. So the transactions would have to "count"
the moment they insert their first undo record, which is exactly the
right thing anyway.

Hmm, but what about transactions that are only touching unlogged tables?

Wouldn't we throw all that UNDO away in a crash restart? There's no
underlying table data anymore, after all.

And for proper shutdown checkpoints they could just be included.

Greetings,

Andres Freund

#130Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#128)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 2:57 PM Peter Geoghegan <pg@bowt.ie> wrote:

On Fri, Jul 19, 2019 at 10:28 AM Robert Haas <robertmhaas@gmail.com> wrote:

In scenario #2, the undo work is going to have to be retried in the
background, and perforce that means reacquiring locks that have been
released, and so there is a chance of long lock waits and/or deadlock
that cannot really be avoided.

I haven't studied the UNDO or zheap stuff in any detail, but I am
concerned about rollbacks that deadlock. I'd feel a lot better about
it if forward progress was guaranteed, somehow. That seems to imply
that locks are retained, which is probably massively inconvenient to
ensure. Not least because it probably requires cooperation from
underlying access methods.

Right, that's definitely a big part of the concern here, but I don't
really believe that retaining locks is absolutely required, or even
necessarily desirable. For instance, suppose that I create a table,
bulk-load a whole lotta data into it, and then abort. Further support
that by the time we start trying to process the undo in the
background, we can't get the lock. Well, that probably means somebody
is performing DDL on the table. If they just did LOCK TABLE or ALTER
TABLE SET STATISTICS, we are going to need to execute that same undo
once the DDL is complete. However, if the DDL is DROP TABLE, we're
going to find that once we can get the lock, the undo is obsolete, and
we don't need to worry about it any more. Had we made it 100% certain
that the DROP TABLE couldn't go through until the undo was performed,
we could avoid having to worry about the undo having become obsolete
... but that's hardly a win. We're better off allowing the drop and
then just chucking the undo.

Likely, something like CLUSTER or VACUUM FULL would take care of
removing any rows created by aborted transactions along the way, so
the undo could be thrown away afterwards without processing it.

Point being - there's at least some chance that the operations which
block forward progress also represent progress of another sort.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#131Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#129)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 3:12 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-07-19 14:50:22 -0400, Robert Haas wrote:

On Fri, Jul 19, 2019 at 2:04 PM Andres Freund <andres@anarazel.de> wrote:

It doesn't seem that hard - and kind of required for robustness
independent of the decision around "completeness" - to find a way to use
the locks already held by the prepared transaction.

I'm not wild about finding more subtasks to put on the must-do list,
but I agree it's doable.

Isn't that pretty inherently required? How are otherwise ever going to
be able to roll back a transaction that holds an AEL on a relation it
also modifies? I might be standing on my own head here, though.

I think you are. If a transaction holds an AEL on a relation it also
modifies, we still only need something like RowExclusiveLock to roll
it back. If we retain the transaction's locks until undo is complete,
we will not deadlock, but we'll also hold AccessExclusiveLock for a
long time. If we release the transaction's locks, we can perform the
undo in the background with only RowExclusiveLock, which is full of
win. Even you insist that the undo task should acquire the same lock
the relation has, which seems entirely excessive to me, that hardly
prevents undo from being applied. Once the original transaction has
released its locks, those locks are released, and the undo system can
acquire those locks the next time the relation isn't busy (or when it
gets to the head of the lock queue).

As far as I can see, the only reason why you would care about this is
to make the back-pressure system effective against prepared
transactions. Different people may want that more or less, but I have
a little trouble with the idea that it is a hard requirement.

Well, then perhaps that admin ought not to constantly terminate
connections... I was thinking that new connections wouldn't be forced
to do that if there were still a lot of headroom regarding
#transactions-to-be-rolled-back. And if undo workers kept up, you'd
also not hit this.

Sure, but cascading failure scenarios suck.

Hmm, that's not a bad idea. So the transactions would have to "count"
the moment they insert their first undo record, which is exactly the
right thing anyway.

Hmm, but what about transactions that are only touching unlogged tables?

Wouldn't we throw all that UNDO away in a crash restart? There's no
underlying table data anymore, after all.

And for proper shutdown checkpoints they could just be included.

On thirty seconds thought, that sounds like it would work.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#132Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#131)
Re: should there be a hard-limit on the number of transactions pending undo?

On 2019-07-19 15:57:45 -0400, Robert Haas wrote:

On Fri, Jul 19, 2019 at 3:12 PM Andres Freund <andres@anarazel.de> wrote:

Isn't that pretty inherently required? How are otherwise ever going to
be able to roll back a transaction that holds an AEL on a relation it
also modifies? I might be standing on my own head here, though.

I think you are. If a transaction holds an AEL on a relation it also
modifies, we still only need something like RowExclusiveLock to roll
it back. If we retain the transaction's locks until undo is complete,
we will not deadlock, but we'll also hold AccessExclusiveLock for a
long time. If we release the transaction's locks, we can perform the
undo in the background with only RowExclusiveLock, which is full of
win. Even you insist that the undo task should acquire the same lock
the relation has, which seems entirely excessive to me, that hardly
prevents undo from being applied. Once the original transaction has
released its locks, those locks are released, and the undo system can
acquire those locks the next time the relation isn't busy (or when it
gets to the head of the lock queue).

Good morning, Mr Freund. Not sure what you were thinking there.

In reply to: Robert Haas (#130)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 12:52 PM Robert Haas <robertmhaas@gmail.com> wrote:

Right, that's definitely a big part of the concern here, but I don't
really believe that retaining locks is absolutely required, or even
necessarily desirable. For instance, suppose that I create a table,
bulk-load a whole lotta data into it, and then abort. Further support
that by the time we start trying to process the undo in the
background, we can't get the lock. Well, that probably means somebody
is performing DDL on the table.

I believe that the primary reason why certain other database systems
retain locks until rollback completes (or release their locks in
reverse order, as UNDO processing progresses) is that application code
will often repeat exactly the same actions on receiving a transient
error, until the action finally completes successfully. Just like with
serialization failures, or with manually implemented UPSERT loops that
must sometimes retry. This is why UNDO is often (or always) processed
synchronously, blocking progress of the client connection as its xact
rolls back.

Obviously these other systems could easily hand off the work of
rolling back the transaction to an asynchronous worker process, and
return success to the client that encounters an error (or asks to
abort/roll back) almost immediately. I have to imagine that they
haven't implemented this straightforward optimization because it makes
sense that the cost of rolling back the transaction is primarily borne
by the client that actually rolls back. And, as I said, because a lot
of application code will immediately retry on failure, which needs to
not deadlock with an asynchronous roll back process.

If they just did LOCK TABLE or ALTER
TABLE SET STATISTICS, we are going to need to execute that same undo
once the DDL is complete. However, if the DDL is DROP TABLE, we're
going to find that once we can get the lock, the undo is obsolete, and
we don't need to worry about it any more. Had we made it 100% certain
that the DROP TABLE couldn't go through until the undo was performed,
we could avoid having to worry about the undo having become obsolete
... but that's hardly a win. We're better off allowing the drop and
then just chucking the undo.

I'm sure that there are cases like that. And, I'm pretty sure that at
least one of the other database systems that I'm thinking of isn't as
naive as I suggest, without being sure of the specifics. The classic
approach is to retain the locks, even though that sucks in some cases.
That doesn't mean that you have to do it that way, but it's probably a
good idea to present your design in a way that compares and contrasts
with the classic approach.

I'm pretty sure that this is related to the way in which other systems
retain coarse-grained locks when bitmap indexes are used, even though
that makes them totally unusable with OLTP apps. It seems like it
would help users a lot if their bitmap indexes didn't come with that
problem, but it's a price that they continue to have to pay.

Point being - there's at least some chance that the operations which
block forward progress also represent progress of another sort.

That's good, provided that there isn't observable lock starvation. I
don't think that you need to eliminate the theoretical risk of lock
starvation. It deserves careful, ongoing consideration, though. It's
difficult to codify exactly what I have in mind, but I can give you an
informal definition now: It's probably okay if there is the occasional
implementation-level deadlock because the user got unlucky once.
However, it's not okay for there to be *continual* deadlocks because
the user got unlucky just once. Even if the user had *extraordinarily*
bad luck that one time. In short, my sense is that it's never okay for
the system as a whole to "get stuck" in a deadlock or livelock loop.
Actually, it might even be okay if somebody had a test case that
exhibits "getting stuck" behavior, provided the test case is very
delicate, and looks truly adversarial (i.e. it goes beyond being
extraordinarily unlucky).

I know that this is all pretty hand-wavy, and I don't expect you to
have a definitive response. These are some high level concerns that I
have, that may or may not apply to what you're trying to do.

--
Peter Geoghegan

#134Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#133)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 6:47 PM Peter Geoghegan <pg@bowt.ie> wrote:

I believe that the primary reason why certain other database systems
retain locks until rollback completes (or release their locks in
reverse order, as UNDO processing progresses) is that application code
will often repeat exactly the same actions on receiving a transient
error, until the action finally completes successfully. Just like with
serialization failures, or with manually implemented UPSERT loops that
must sometimes retry. This is why UNDO is often (or always) processed
synchronously, blocking progress of the client connection as its xact
rolls back.

I don't think this matters here at all. As long as there's only DML
involved, there won't be any lock conflicts anyway - everybody's
taking RowExclusiveLock or less, and it's all fine. If you update a
row in zheap, abort, and then try to update again before the rollback
happens, we'll do a page-at-a-time rollback in the foreground, and
proceed with the update; when we get around to applying the undo,
we'll notice that page has already been handled and skip the undo
records that pertain to it. To get the kinds of problems I'm on about
here, somebody's got to be taking some more serious locks.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

In reply to: Robert Haas (#134)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 4:14 PM Robert Haas <robertmhaas@gmail.com> wrote:

I don't think this matters here at all. As long as there's only DML
involved, there won't be any lock conflicts anyway - everybody's
taking RowExclusiveLock or less, and it's all fine. If you update a
row in zheap, abort, and then try to update again before the rollback
happens, we'll do a page-at-a-time rollback in the foreground, and
proceed with the update; when we get around to applying the undo,
we'll notice that page has already been handled and skip the undo
records that pertain to it. To get the kinds of problems I'm on about
here, somebody's got to be taking some more serious locks.

If I'm not mistaken, you're tacitly assuming that you'll always be
using zheap, or something sufficiently similar to zheap. It'll
probably never be possible to UNDO changes to something like a GIN
index on a zheap table, because you can never do that with sensible
concurrency/deadlock behavior.

I don't necessarily have a problem with that. I don't pretend to
understand how much of a problem it is. Obviously it partially depends
on what your ambitions are for this infrastructure. Still, assuming
that I have it right, ISTM that UNDO/zheap/whatever should explicitly
own this restriction.

--
Peter Geoghegan

#136Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#124)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 6:37 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jul 19, 2019 at 7:54 AM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

+ * We just want to mask the cid in the undo record header.  So
+ * only if the partial record in the current page include the undo
+ * record header then we need to mask the cid bytes in this page.
+ * Otherwise, directly jump to the next record.
Here, I think you mean : "So only if the partial record in the current
page includes the *cid* bytes", rather than "includes the undo record
header"
May be we can say :
We just want to mask the cid. So do the partial record masking only if
the current page includes the cid bytes from the partial record
header.

Hmm, but why is it correct to mask the CID at all? Shouldn't that match?

We don't write CID in the WAL. Because In hot-standby or after
recovery we don't need actual CID for the visibility. So during REDO
while generating the undo record we set CID as 'FirstCommandId' which
is different from the DO time. That's the reason we mask it.
--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#137Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#123)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 6:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jul 19, 2019 at 12:10 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

We are doing exactly what you have written in the last line of the
next paragraph "stop the transaction from writing undo when the hash
table is already too full.". So we will
never face the problems related to repeated crash recovery. The
definition of too full is that we stop allowing the new transactions
that can write undo when we have the hash table already have entries
equivalent to (UndoRollbackHashTableSize() - MaxBackends). Does this
make sense?

Oops, I was looking in the wrong place. Yes, that makes sense, but:

1. It looks like the test you added to PrepareUndoInsert has no
locking, and I don't see how that can be right.

+if (ProcGlobal->xactsHavingPendingUndo >
+(UndoRollbackHashTableSize() - MaxBackends))

Actual HARD_LIMIT is UndoRollbackHashTableSize(), but we only allow a
new backend to prepare the undo record if we have MaxBackends number
of empty slots in the hash table. This will guarantee us to always
have at least one slot in the hash table for our current prepare, even
if all the backend which running their transaction has aborted and
inserted an entry in the hash table.

I think the problem with this check is that for any backend to prepare
an undo there must be MaxBackend number of empty slots in the hash
table for any concurrent backend to insert the request and this seems
too restrictive.

Having said that I think we must ensure MaxBackends*2 empty slots in
the hash table as each backend can enter 2 requests in the hash table.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#138Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#125)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 10:58 PM Robert Haas <robertmhaas@gmail.com> wrote:

One other thing that seems worth noting is that we have to consider
what happens after a restart. After a crash, and depending on exactly
how we design it perhaps also after a non-crash restart, we won't
immediately know how many outstanding transactions need undo; we'll
have to grovel through the undo logs to find out. If we've got a hard
cap, we can't allow new undo-using transactions to start until we
finish that work. It's possible that, at the moment of the crash, the
maximum number of items had already been pushed into the background,
and every foreground session was busy trying to undo an abort as well.
If so, we're already up against the limit. We'll have to scan through
all of the undo logs and examine each transaction to get a count on
how many transactions are already in a needs-undo-work state; only
once we have that value do we know whether it's OK to admit new
transactions to using the undo machinery, and how many we can admit.
In typical cases, that won't take long at all, because there won't be
any pending undo work, or not much, and we'll very quickly read the
handful of transaction headers that we need to consult and away we go.
However, if the hard limit is pretty big, and we're pretty close to
it, counting might take a long time. It seems bothersome to have this
interval between when we start accepting transactions and when we can
accept transactions that use undo. Instead of throwing an ERROR, we
can probably just teach the system to wait for the background process
to finish doing the counting; that's what Amit's patch does currently.

Yeah, however, we wait for a certain threshold period of time (one
minute) for counting to finish and then error out. We can wait till
the counting is finished but I am not sure if that is a good idea
because anyway user can try again after some time.

Or, we could not even open for connections until the counting has been
completed.

When I first thought about this, I was really concerned about the idea
of a hard limit, but the more I think about it the less problematic it
seems. I think in the end it boils down to a question of: when things
break, what behavior would users prefer?

One minor thing I would like to add here is that we are providing some
knobs wherein the systems having more number of rollbacks can
configure to have a much higher value of hard limit such that it won't
hit in their systems. I know it is not always easy to find the right
value, but I guess they can learn from the behavior and then change it
to avoid the same in future.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#139Amit Kapila
amit.kapila16@gmail.com
In reply to: Peter Geoghegan (#133)
Re: should there be a hard-limit on the number of transactions pending undo?

On Sat, Jul 20, 2019 at 4:17 AM Peter Geoghegan <pg@bowt.ie> wrote:

On Fri, Jul 19, 2019 at 12:52 PM Robert Haas <robertmhaas@gmail.com> wrote:

Right, that's definitely a big part of the concern here, but I don't
really believe that retaining locks is absolutely required, or even
necessarily desirable. For instance, suppose that I create a table,
bulk-load a whole lotta data into it, and then abort. Further support
that by the time we start trying to process the undo in the
background, we can't get the lock. Well, that probably means somebody
is performing DDL on the table.

I believe that the primary reason why certain other database systems
retain locks until rollback completes (or release their locks in
reverse order, as UNDO processing progresses) is that application code
will often repeat exactly the same actions on receiving a transient
error, until the action finally completes successfully. Just like with
serialization failures, or with manually implemented UPSERT loops that
must sometimes retry. This is why UNDO is often (or always) processed
synchronously, blocking progress of the client connection as its xact
rolls back.

Obviously these other systems could easily hand off the work of
rolling back the transaction to an asynchronous worker process, and
return success to the client that encounters an error (or asks to
abort/roll back) almost immediately. I have to imagine that they
haven't implemented this straightforward optimization because it makes
sense that the cost of rolling back the transaction is primarily borne
by the client that actually rolls back.

It is also possible that there are some other disadvantages or
technical challenges in those other systems due to which they decided
not to have such a mechanism. I think one such database prepares the
consistent copy of pages during read operation based on SCN or
something like that. It might not be as easy for such a system to
check if there is some pending undo which needs to be consulted. I am
not telling that there are no ways to overcome such things but that
might have incurred much more cost or has some other disadvantages.
I am not sure if it is straight-forward to imagine why some other
system does the things in some particular way unless there is some
explicit documentation about the same.

Having said that, I agree that there are a good number of advantages
of performing the actions in the client that actually rolls back and
we should try to do that where it is not a good idea to transfer to
background workers like for short transactions.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#140Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#123)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 6:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jul 19, 2019 at 12:10 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

We are doing exactly what you have written in the last line of the
next paragraph "stop the transaction from writing undo when the hash
table is already too full.". So we will
never face the problems related to repeated crash recovery. The
definition of too full is that we stop allowing the new transactions
that can write undo when we have the hash table already have entries
equivalent to (UndoRollbackHashTableSize() - MaxBackends). Does this
make sense?

Oops, I was looking in the wrong place. Yes, that makes sense, but:

1. It looks like the test you added to PrepareUndoInsert has no
locking, and I don't see how that can be right.

2. It seems like this is would result in testing for each new undo
insertion that gets prepared, whereas surely we would want to only
test when first attaching to an undo log. If you've already attached
to the undo log, there's no reason not to continue inserting into it,
because doing so doesn't increase the number of transactions (or
transaction-persistence level combinations) that need undo.

I agree that it should not be for each undo insertion rather whenever
any transaction attached to an undo log.

3. I don't think the test itself is correct. It can fire even when
there's no problem. It is correct (or would be if it said 2 *
MaxBackends) if every other backend in the system is already attached
to an undo log (or two). But if they are not, it will block
transactions from being started for no reason.

Right, we should find a way to know the exact number of transactions
that are attached to undo-log at any point in time, then we can have a
more precise check.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#141Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#140)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 20, 2019 at 12:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Jul 19, 2019 at 6:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Jul 19, 2019 at 12:10 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

We are doing exactly what you have written in the last line of the
next paragraph "stop the transaction from writing undo when the hash
table is already too full.". So we will
never face the problems related to repeated crash recovery. The
definition of too full is that we stop allowing the new transactions
that can write undo when we have the hash table already have entries
equivalent to (UndoRollbackHashTableSize() - MaxBackends). Does this
make sense?

Oops, I was looking in the wrong place. Yes, that makes sense, but:

1. It looks like the test you added to PrepareUndoInsert has no
locking, and I don't see how that can be right.

2. It seems like this is would result in testing for each new undo
insertion that gets prepared, whereas surely we would want to only
test when first attaching to an undo log. If you've already attached
to the undo log, there's no reason not to continue inserting into it,
because doing so doesn't increase the number of transactions (or
transaction-persistence level combinations) that need undo.

I agree that it should not be for each undo insertion rather whenever
any transaction attached to an undo log.

3. I don't think the test itself is correct. It can fire even when
there's no problem. It is correct (or would be if it said 2 *
MaxBackends) if every other backend in the system is already attached
to an undo log (or two). But if they are not, it will block
transactions from being started for no reason.

Right, we should find a way to know the exact number of transactions
that are attached to undo-log at any point in time, then we can have a
more precise check.

Maybe we can make ProcGlobal->xactsHavingPendingUndo an atomic
variable. We can increment its value atomically whenever
a) A transaction writes the first undo record for each persistence level.
b) For each abort request inserted by the 'StartupPass'.

And, we will decrement it when
a) The transaction commits (decrement by 1 for each persistence level
it has written undo for).
b) Rollback request is processed.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#142Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#116)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 18, 2019 at 4:41 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Tue, Jul 16, 2019 at 8:39 AM Robert Haas <robertmhaas@gmail.com> wrote:

Thomas has already objected to another proposal to add functions that
turn 32-bit XIDs into 64-bit XIDs. Therefore, I feel confident in
predicting that he will likewise object to GetEpochForXid. I think
this needs to be changed somehow, maybe by doing what the XXX comment
you added suggests.

Perhaps we should figure out how to write GetOldestFullXmin() and friends.

For FinishPreparedTransaction(), the XXX comment sounds about right
(TwoPhaseFileHeader should hold an fxid).

I think we can do that, but what about subxids in TwoPhaseFileHeader?
Shall we store them as fxids as well? If we don't do that then it
will appear inconsistent and if we want to store subxids as fxids,
then we need to track them as fxids in TransactionStateData. It might
not be a very big change, but certainly, more work as compared to if
we just store top-level fxid or use GetEpochForXid as we are currently
using in the patch. Another thing is changing subxids to fxids can
increase the size of two-phase file for a xact having many
sub-transactions which again might be okay, but not completely sure.

Thoughts?

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#143Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#142)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 2:21 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

I have reviewed 0012-Infrastructure-to-execute-pending-undo-actions,
Please find my comment so far.

1.
+ /* It shouldn't be discarded. */
+ Assert(!UndoRecPtrIsDiscarded(xact_urp));

I think comments can be added to explain why it shouldn't be discarded.

2.
+ /* Compute the offset of the uur_next in the undo record. */
+ offset = SizeOfUndoRecordHeader +
+ offsetof(UndoRecordTransaction, urec_progress);
+
in comment /uur_next/uur_progress
3.
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
Function header formating is not in sync with other functions.
4.
+void
+undoaction_redo(XLogReaderState *record)
+{
+ uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+ switch (info)
+ {
+ case XLOG_UNDO_APPLY_PROGRESS:
+ undo_xlog_apply_progress(record);
+ break;

For HotStandby it doesn't make sense to apply this wal as this
progress is only required when we try to apply the undo action after
restart
but in HotStandby we never apply undo actions.

5.
+ Assert(from_urecptr != InvalidUndoRecPtr);
+ Assert(to_urecptr != InvalidUndoRecPtr);

we can use macros UndoRecPtrIsValid instead of checking like this.

6.
+ if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+ slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+ Assert(slot != NULL);
We are passing missing_ok as false in UndoLogGetSlot.  But, not sure
why we are expecting that undo lot can not be dropped.  In multi-log
transaction it's possible
that the tablespace in which next undolog is there is already dropped?
7.
+ */
+ do
+ {
+ BlockNumber progress_block_num = InvalidBlockNumber;
+ int i;
+ int nrecords;
                      .....
    + */
+ if (!UndoRecPtrIsValid(urec_ptr))
+ break;
+ } while (true);

I think we can convert above loop to while(true) instead of do..while,
because there is no need for do while loop.

8.
+ if (last_urecinfo->uur->uur_info & UREC_INFO_LOGSWITCH)
+ {
+ UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;

IMHO, the caller of UndoFetchRecord should directly check
uur->uur_logswitch instead of uur_info & UREC_INFO_LOGSWITCH.
Actually, uur_info is internally set
for inserting the tuple and check there to know what to insert and
fetch but I think caller of UndoFetchRecord should directly rely on
the field because ideally all
the fields in UnpackUndoRecord must be set and uur_txt or
uur_logswitch will be allocated when those headers present. I think
this needs to be improved in undo interface patch
as well (in UndoBulkFetchRecord).

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#144Thomas Munro
thomas.munro@gmail.com
In reply to: Peter Geoghegan (#135)
Re: should there be a hard-limit on the number of transactions pending undo?

On Sat, Jul 20, 2019 at 11:28 AM Peter Geoghegan <pg@bowt.ie> wrote:

On Fri, Jul 19, 2019 at 4:14 PM Robert Haas <robertmhaas@gmail.com> wrote:

I don't think this matters here at all. As long as there's only DML
involved, there won't be any lock conflicts anyway - everybody's
taking RowExclusiveLock or less, and it's all fine. If you update a
row in zheap, abort, and then try to update again before the rollback
happens, we'll do a page-at-a-time rollback in the foreground, and
proceed with the update; when we get around to applying the undo,
we'll notice that page has already been handled and skip the undo
records that pertain to it. To get the kinds of problems I'm on about
here, somebody's got to be taking some more serious locks.

If I'm not mistaken, you're tacitly assuming that you'll always be
using zheap, or something sufficiently similar to zheap. It'll
probably never be possible to UNDO changes to something like a GIN
index on a zheap table, because you can never do that with sensible
concurrency/deadlock behavior.

I don't necessarily have a problem with that. I don't pretend to
understand how much of a problem it is. Obviously it partially depends
on what your ambitions are for this infrastructure. Still, assuming
that I have it right, ISTM that UNDO/zheap/whatever should explicitly
own this restriction.

I had a similar thought: you might regret that choice if you were
wanting to implement an AM with lock table-based concurrency control
(meaning that there are lock ordering concerns for row and page locks,
for DML statements, not just DDL). That seemed a bit too far fetched
to mention before, but are you saying the same sort of concerns might
come up with indexes that support true undo (as opposed to indexes
that still need VACUUM)?

For comparison, ARIES[1]https://cs.stanford.edu/people/chrismre/cs345/rl/aries.pdf has no-deadlock rollbacks as a basic property
and reacquires locks during restart before new transactions are allow
to execute. In its model, the locks in question can be on things like
rows and pages. We don't even use our lock table for those (except
for non-blocking SIREAD locks, irrelevant here). After crash
recovery, if zheap encounters a row with pending rollback from an
aborted transaction, as usual it either needs to read an older version
from an undo log (for reads) or help execute the rollback before
updating (for writes). That only requires page-at-a-time LWLocks
("latching"), so it's deadlock-free. The only deadlock risk comes
from the need to acquire heavyweight locks on relations which
typically only conflict when you run DDL, so yeah, it's tempting to
worry a lot less about those than the fine grained lock traffic from
DML statements that DB2 and others have to deal with.

So spell out the two options again:

A. Rollback can't deadlock. You have to make sure you reliably hold
locks until rollback is completed (including some tricky new lock
transfer magic), and then reacquire them after recovery before new
transactions are allowed. You could trivially achieve the restart
part by simply waiting until all rollback is executed before you allow
new transactions, but other systems including DB2 first acquire all
the locks in an earlier scan through the log, then allow new
connections, and then execute the rollback. Acquiring them before new
transactions are allowed means that they must fit in the lock table and
there must be no conflicts among them if they were all granted as at
the moment you crashed or shut down.

B. Rollback can deadlock or exhaust the lock table because we release
and reacquire some arbitrary time later. No choice but to keep
retrying if anything goes wrong, and rollback is theoretically not
guaranteed to complete and you can contrive a workload that will never
make progress. This amounts to betting that these problems will be
rare enough that it doesn't matter and eventually make progress, and
it should be fairly clear what's happening and why.

I might as well put the quote marks on now: "Perhaps we could
implement A later."

[1]: https://cs.stanford.edu/people/chrismre/cs345/rl/aries.pdf

--
Thomas Munro
https://enterprisedb.com

#145Amit Khandekar
amitdkhan.pg@gmail.com
In reply to: Amit Kapila (#142)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, 22 Jul 2019 at 14:21, Amit Kapila <amit.kapila16@gmail.com> wrote:

I have started review of
0014-Allow-execution-and-discard-of-undo-by-background-wo.patch. Below
are some quick comments to start with:

+++ b/src/backend/access/undo/undoworker.c
+#include "access/xact.h"
+#include "access/undorequest.h"
Order is not alphabetical

+ * Each undo worker then start reading from one of the queue the requests for
start=>starts
queue=>queues

-------------

+ rc = WaitLatch(MyLatch,
+    WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+    10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+ /* emergency bailout if postmaster has died */
+ if (rc & WL_POSTMASTER_DEATH)
+ proc_exit(1);
I think now, thanks to commit cfdf4dc4fc9635a, you don't have to
explicitly handle postmaster death; instead you can use
WL_EXIT_ON_PM_DEATH. Please check at all such places where this is
done in this patch.

-------------

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+ MyUndoWorker = &UndoApplyCtx->workers[slot];
Not sure why MyUndoWorker is used here. Can't we use a local variable
? Or do we intentionally attach to the slot as a side-operation ?

-------------

+ * Get the dbid where the wroker should connect to and get the worker
wroker=>worker

-------------

+ BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
0, 0 => InvalidOid, 0

+ * Set the undo worker request queue from which the undo worker start
+ * looking for a work.
start => should start
a work => work

--------------

+ if (!InsertRequestIntoErrorUndoQueue(urinfo))
I was thinking what happens if for some reason
InsertRequestIntoErrorUndoQueue() itself errors out. In that case, the
entry will not be marked invalid, and so there will be no undo action
carried out because I think the undo worker will exit. What happens
next with this entry ?

#146Amit Khandekar
amitdkhan.pg@gmail.com
In reply to: Amit Khandekar (#122)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, 19 Jul 2019 at 17:24, Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Thu, 9 May 2019 at 12:04, Dilip Kumar <dilipbalaut@gmail.com> wrote:

Patches can be applied on top of undo branch [1] commit:
(cb777466d008e656f03771cf16ec7ef9d6f2778b)

[1] https://github.com/EnterpriseDB/zheap/tree/undo

Below are some review points for 0009-undo-page-consistency-checker.patch :

Another point that I missed :

+    * Process the undo record of the page and mask their cid filed.
+    */
+   while (next_record < page_end)
+   {
+       UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+       /* If this undo record has cid present, then mask it */
+       if ((header->urec_info & UREC_INFO_CID) != 0)

Here, even though next record starts in the current page, the
urec_info itself may or may not lie on this page.

I hope this possibility is also considered when populating the
partial-record-specific details in the page header.

--
Thanks,
-Amit Khandekar
EnterpriseDB Corporation
The Postgres Database Company

#147Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#142)
Re: POC: Cleaning up orphaned files using undo logs

On 2019-07-22 14:21:36 +0530, Amit Kapila wrote:

Another thing is changing subxids to fxids can increase the size of
two-phase file for a xact having many sub-transactions which again
might be okay, but not completely sure.

I can't see that being a problem.

#148Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Khandekar (#145)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 8:39 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Mon, 22 Jul 2019 at 14:21, Amit Kapila <amit.kapila16@gmail.com> wrote:

-------------

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+ MyUndoWorker = &UndoApplyCtx->workers[slot];
Not sure why MyUndoWorker is used here. Can't we use a local variable
? Or do we intentionally attach to the slot as a side-operation ?

-------------

I think here, we can use a local variable as well. Do you see any
problem with the current code or do you think it is better to use a
local variable here?

--------------

+ if (!InsertRequestIntoErrorUndoQueue(urinfo))
I was thinking what happens if for some reason
InsertRequestIntoErrorUndoQueue() itself errors out. In that case, the
entry will not be marked invalid, and so there will be no undo action
carried out because I think the undo worker will exit. What happens
next with this entry ?

The same entry is present in two queues xid and size, so next time it
will be executed from the second queue based on it's priority in that
queue. However, if it fails again a second time in the same way, then
we will be in trouble because now the hash table has entry, but none
of the queues has entry, so none of the workers will attempt to
execute again. Also, when discard worker again tries to register it,
we won't allow adding the entry to queue thinking either some backend
is executing the same or it must be part of some queue.

The one possibility to deal with this could be that we somehow allow
discard worker to register it again in the queue or we can do this in
critical section so that it allows system restart on error. However,
the main thing is it possible that InsertRequestIntoErrorUndoQueue
will fail unless there is some bug in the code? If so, we might want
to have an Assert for this rather than handling this condition.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#149Amit Khandekar
amitdkhan.pg@gmail.com
In reply to: Amit Kapila (#148)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, 23 Jul 2019 at 08:48, Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Jul 22, 2019 at 8:39 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Mon, 22 Jul 2019 at 14:21, Amit Kapila <amit.kapila16@gmail.com> wrote:

-------------

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+ MyUndoWorker = &UndoApplyCtx->workers[slot];
Not sure why MyUndoWorker is used here. Can't we use a local variable
? Or do we intentionally attach to the slot as a side-operation ?

-------------

I think here, we can use a local variable as well. Do you see any
problem with the current code or do you think it is better to use a
local variable here?

I think, even though there might not be a correctness issue with the
current code as it stands, we should still use a local variable.
Updating MyUndoWorker is a big side-effect, which the caller is not
supposed to be aware of, because all that function should do is just
get the slot info.

--------------

+ if (!InsertRequestIntoErrorUndoQueue(urinfo))
I was thinking what happens if for some reason
InsertRequestIntoErrorUndoQueue() itself errors out. In that case, the
entry will not be marked invalid, and so there will be no undo action
carried out because I think the undo worker will exit. What happens
next with this entry ?

The same entry is present in two queues xid and size, so next time it
will be executed from the second queue based on it's priority in that
queue. However, if it fails again a second time in the same way, then
we will be in trouble because now the hash table has entry, but none
of the queues has entry, so none of the workers will attempt to
execute again. Also, when discard worker again tries to register it,
we won't allow adding the entry to queue thinking either some backend
is executing the same or it must be part of some queue.

The one possibility to deal with this could be that we somehow allow
discard worker to register it again in the queue or we can do this in
critical section so that it allows system restart on error. However,
the main thing is it possible that InsertRequestIntoErrorUndoQueue
will fail unless there is some bug in the code? If so, we might want
to have an Assert for this rather than handling this condition.

Yes, I also think that the function would error out only because of
can't-happen cases, like "too many locks taken" or "out of binary heap
slots" or "out of memory" (this last one is not such a can't happen
case). These cases happen probably due to some bugs, I suppose. But I
was wondering : Generally when the code errors out with such
can't-happen elog() calls, worst thing that happens is that the
transaction gets aborted. Whereas, in this case, the worst thing that
could happen is : the undo action would never get executed, which
means selects for this tuple will keep on accessing the undo log ?
This does not sound like any data consistency issue, so we should be
fine after all ?

--------------------

Some further review comments for undoworker.c :

+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
The function name sounds like "is the worker lingering ?". Can we
rename it to something like "UndoWorkerSetLingering" ?

-------------

+ errmsg("undo worker slot %d is empty, cannot attach",
+ slot)));
+ }
+
+ if (MyUndoWorker->proc)
+ {
+ LWLockRelease(UndoWorkerLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("undo worker slot %d is already used by "
+ "another worker, cannot attach", slot)));

These two error messages can have a common error message "could not
attach to worker slot", with errdetail separate for each of them :
slot %d is empty.
slot %d is already used by another worker.

--------------

+static int
+IsUndoWorkerAvailable(void)
+{
+ int i;
+ int alive_workers = 0;
+
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);

Should have bool return value.

Also, why is it keeping track of number of alive workers ? Sounds like
earlier it used to return number of alive workers ? If it indeed needs
to just return true/false, we can do away with alive_workers.

Also, *exclusive* lock is unnecessary.

--------------

+if (UndoGetWork(false, false, &urinfo, NULL) &&
+    IsUndoWorkerAvailable())
+    UndoWorkerLaunch(urinfo);

There is no lock acquired between IsUndoWorkerAvailable() and
UndoWorkerLaunch(); that means even though IsUndoWorkerAvailable()
returns true, there is a small window where UndoWorkerLaunch() does
not find any worker slot with in_use false, causing assertion failure
for (worker != NULL).
--------------

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
*Exclusive* lock is unnecessary.
-------------
+ LWLockRelease(UndoWorkerLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("undo worker slot %d is empty",
+ slot)));
I believe there is no need to explicitly release an lwlock before
raising an error, since the lwlocks get released during error
recovery. Please check all other places where this is done.
-------------
+ * Start new undo apply background worker, if possible otherwise return false.
worker, if possible otherwise => worker if possible, otherwise
-------------
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
We don't check UndoWorkerLaunch() return value. Can't we make it's
return value type void ?
Also, it would be better to have urinfo as pointer to UndoRequestInfo
rather than UndoRequestInfo, so as to avoid structure copy.
-------------
+{
+ BackgroundWorker bgw;
+ BackgroundWorkerHandle *bgw_handle;
+ uint16 generation;
+ int i;
+ int slot = 0;
We can remove variable i, and use slot variable in place of i.
-----------
+ snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
I think it would be trivial to also append the worker->generation in
the bgw_name.
-------------
+ if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+ {
+ /* Failed to start worker, so clean up the worker slot. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+ UndoWorkerCleanup(worker);
+ LWLockRelease(UndoWorkerLock);
+
+ return false;
+ }

Is it intentional that there is no (warning?) message logged when we
can't register a bg worker ?
-------------

--
Thanks,
-Amit Khandekar
EnterpriseDB Corporation
The Postgres Database Company

#150Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#143)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 3:51 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Please find my review comments for
0013-Allow-foreground-transactions-to-perform-undo-action

+ /* initialize undo record locations for the transaction */
+ for (i = 0; i < UndoLogCategories; i++)
+ {
+ s->start_urec_ptr[i] = InvalidUndoRecPtr;
+ s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+ s->undo_req_pushed[i] = false;
+ }

Can't we just memset this memory?

+ * We can't postpone applying undo actions for subtransactions as the
+ * modifications made by aborted subtransaction must not be visible even if
+ * the main transaction commits.
+ */
+ if (IsSubTransaction())
+ return;

I am not completely sure but is it possible that the outer function
CommitTransactionCommand/AbortCurrentTransaction can avoid
calling this function in the switch case based on the current state,
so that under subtransaction this will never be called?

+ /*
+ * Prepare required undo request info so that it can be used in
+ * exception.
+ */
+ ResetUndoRequestInfo(&urinfo);
+ urinfo.dbid = dbid;
+ urinfo.full_xid = fxid;
+ urinfo.start_urec_ptr = start_urec_ptr[per_level];
+

I see that we are preparing urinfo before execute_undo_actions so that
in case of an error in CATCH we can use that to
insert into the queue, but can we just initialize urinfo right there
before inserting into the queue, we have all the information
Am I missing something?

+
+ /*
+ * We need the locations of the start and end undo record pointers when
+ * rollbacks are to be performed for prepared transactions using undo-based
+ * relations.  We need to store this information in the file as the user
+ * might rollback the prepared transaction after recovery and for that we
+ * need it's start and end undo locations.
+ */
+ UndoRecPtr start_urec_ptr[UndoLogCategories];
+ UndoRecPtr end_urec_ptr[UndoLogCategories];

it's -> its

+ bool undo_req_pushed[UndoLogCategories]; /* undo request pushed
+ * to worker? */
+ bool performUndoActions;
+
  struct TransactionStateData *parent; /* back link to parent */

We must have some comments to explain how performUndoActions is used,
where it's set. If it's explained somewhere else then we can
give reference to that code.

+ for (i = 0; i < UndoLogCategories; i++)
+ {
+ if (s->latest_urec_ptr[i])
+ {
+ s->performUndoActions = true;
+ break;
+ }
+ }

I think we should chek UndoRecPtrIsValid(s->latest_urec_ptr[i])

+ PG_TRY();
+ {
+ /*
+ * Prepare required undo request info so that it can be used in
+ * exception.
+ */
+ ResetUndoRequestInfo(&urinfo);
+ urinfo.dbid = dbid;
+ urinfo.full_xid = fxid;
+ urinfo.start_urec_ptr = start_urec_ptr[per_level];
+
+ /* for subtransactions, we do partial rollback. */
+ execute_undo_actions(urinfo.full_xid,
+ end_urec_ptr[per_level],
+ start_urec_ptr[per_level],
+ !isSubTrans);
+ }
+ PG_CATCH();

Wouldn't it be good to explain in comments that we are not rethrowing
the error in PG_CATCH but because we don't want the main
transaction to get an error if there is an error while applying to
undo action for the main transaction and we will abort the transaction
in the caller of this function?

+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.

I think we need to give detail reasoning why subtransaction changes
will be visible if we don't apply it's undo and the main
the transaction commits by mentioning that we don't use separate
transaction id for the subtransaction and that will make all the
changes of the transaction id visible when it commits.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#151Rushabh Lathia
rushabh.lathia@gmail.com
In reply to: Dilip Kumar (#150)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

I have stated review of
0008-Provide-interfaces-to-store-and-fetch-undo-records.patch, here are few
quick comments.

1) README.undointerface should provide more information like API details or
the sequence in which API should get called.

2) Information about the API's in the undoaccess.c file header block would
good. For reference please look at heapam.c.

3) typo

+ * Later, during insert phase we will write actual records into thse
buffers.
+ */

%s/thse/these

4) UndoRecordUpdateTransInfo() comments says that this must be called under
the critical section, but seems like undo_xlog_apply_progress() do call it
outside of critical section? Is there exception, then should add comments?
or Am I missing anything?

5) In function UndoBlockGetFirstUndoRecord() below code:

/* Calculate the size of the partial record. */
partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
phdr->tuple_len + phdr->payload_len -
phdr->record_offset;

can directly use UndoPagePartialRecSize().

6)

+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+                  RelFileNode rnode,
+                  BlockNumber blk,
+                  ReadBufferMode rbm)
+{
+    int            i;

In the above code variable "i" is mean "block index". It would be good
to give some valuable name to the variable, maybe "blockIndex" ?

7)

* We will also keep a previous undo record pointer to the first and last
undo
* record of the transaction in the previous log. The last undo record
* location is used find the previous undo record pointer during rollback.

%s/used fine/used to find

8)

/*
* Defines the number of times we try to wait for rollback hash table to get
* initialized. After these many attempts it will return error and the user
* can retry the operation.
*/
#define ROLLBACK_HT_INIT_WAIT_TRY 60

%s/error/an error

9)

* we can get the exact size of partial record in this page.
*/

%s/of partial/of the partial"

10)

* urecptr - current transaction's undo record pointer which need to be set
in
* the previous transaction's header.

%s/need/needs

11)

/*
* If we are writing first undo record for the page the we can set the
* compression so that subsequent records from the same transaction can
* avoid including common information in the undo records.
*/

%s/the page the/the page then

12)

/*
* If the transaction's undo records are split across the undo logs. So
* we need to update our own transaction header in the previous log.
*/

double space between "to" and "update"

13)

* The undo record should be freed by the caller by calling
ReleaseUndoRecord.
* This function will old the pin on the buffer where we read the previous
undo
* record so that when this function is called repeatedly with the same
context

%s/old/hold

I will continue further review for the same patch.

Regards,
--
Rushabh Lathia
www.EntepriseDB.com

#152Dilip Kumar
dilipbalaut@gmail.com
In reply to: Rushabh Lathia (#151)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 11:28 AM Rushabh Lathia
<rushabh.lathia@gmail.com> wrote:

Hi,

I have stated review of
0008-Provide-interfaces-to-store-and-fetch-undo-records.patch, here are few
quick comments.

Thanks for the review, I will work on them soon and post the updated
patch along with other comments. I have noticed some comments are
pointing to the code which is not part of this patch
for example

5) In function UndoBlockGetFirstUndoRecord() below code:

/* Calculate the size of the partial record. */
partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
phdr->tuple_len + phdr->payload_len -
phdr->record_offset;

can directly use UndoPagePartialRecSize().

UndoBlockGetFirstUndoRecord is added under 0014 patch, I think you got
confused because this code is in undoaccess.c file. But actually
later patch set added some code under undoaccess.c. Basically, this
comment needs to be fixed but under another patch. I am pointing out
so that we don't miss this.

8)

/*
* Defines the number of times we try to wait for rollback hash table to get
* initialized. After these many attempts it will return error and the user
* can retry the operation.
*/
#define ROLLBACK_HT_INIT_WAIT_TRY 60

%s/error/an error

This macro also added under 0014.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#153Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#118)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Fri, Jun 28, 2019 at 6:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

I happened to open up 0001 from this series, which is from Thomas, and
I do not think that the pg_buffercache changes are correct. The idea
here is that the customer might install version 1.3 or any prior
version on an old release, then upgrade to PostgreSQL 13. When they
do, they will be running with the old SQL definitions and the new
binaries. At that point, it sure looks to me like the code in
pg_buffercache_pages.c is going to do the Wrong Thing. [...]

Yep, that was completely wrong. Here's a new version.

One comment/question related to
0022-Use-undo-based-rollback-to-clean-up-files-on-abort.patch.

I have done some more review of undolog patch series and here are my comments:
0003-Add-undo-log-manager.patch
1.
allocate_empty_undo_segment()
{
..
..
if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+ {
+ char    *parentdir;
+
+ if (errno != ENOENT || !InRecovery)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create directory \"%s\": %m",
+ undo_path)));
+
+ /*
+ * In recovery, it's possible that the tablespace directory
+ * doesn't exist because a later WAL record removed the whole
+ * tablespace.  In that case we create a regular directory to
+ * stand in for it.  This is similar to the logic in
+ * TablespaceCreateDbspace().
+ */
+
+ /* create two parents up if not exist */
+ parentdir = pstrdup(undo_path);
+ get_parent_directory(parentdir);
+ get_parent_directory(parentdir);
+ /* Can't create parent and it doesn't already exist? */
+ if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)

All of this code is almost same as we have code in
TablespaceCreateDbspace still we have small differences like here you
are using mkdir instead of MakePGDirectory which as far as I can see
use similar permissions for creating directory. Also, it checks
whether the directory exists before trying to create it. Is there a
reason why we need to do a few things differently here if not, they
can both the places use one common function?

2.
allocate_empty_undo_segment()
{
..
..
/* Flush the contents of the file to disk before the next checkpoint. */
+ undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
..
}

+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+ char path[MAXPGPATH];
+ FileTag tag;
+
+ INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+ /* Try to send to the checkpointer, but if out of space, do it here. */
+ if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))

The comment in allocate_empty_undo_segment indicates that the code
wants to flush before checkpoint, but the actual function tries to
register the request with checkpointer. Shouldn't this be similar to
XLogFileInit where we use pg_fsync to flush the contents immediately?
I guess that will avoid what you have written in comments in the same
function (we just want to make sure that the filesystem has allocated
physical blocks for it so that non-COW filesystems will report ENOSPC
now rather than later when space is needed). OTOH, I think it is
performance-wise better to postpone the work to checkpointer. If we
want to push this work to checkpointer, then we might need to change
comments or alternatively, we might want to use bigger segment sizes
to mitigate the performance effect.

If my above understanding is correct and the reason to fsync
immediately is to reserve space now, then we also need to think
whether we are always safe in postponing the work? Basically, if this
means that it can fail when we are actually trying to write undo, then
it could be risky because we could be in the critical section at that
time. I am not sure about this point, rather it is just to discuss if
there are any impacts of postponing the fsync work.

Another thing is that recently in commit 475861b261 (commit by you),
we have introduced a mechanism to not fill the files with zero's for
certain filesystems like ZFS. Do we want similar behavior for undo
files?

3.
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+ UndoLogSlot *slot;
+ size_t end;
+
+ slot = find_undo_log_slot(logno, false);
+
+ /* TODO review interlocking */
+
+ Assert(slot != NULL);
+ Assert(slot->meta.end % UndoLogSegmentSize == 0);
+ Assert(new_end % UndoLogSegmentSize == 0);
+ Assert(InRecovery ||
+    CurrentSession->attached_undo_slots[slot->meta.category] == slot);

Can you write some comments explaining the above Asserts? Also, can
you explain what interlocking issues are you worried about here?

4.
while (end < new_end)
+ {
+ allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+ end += UndoLogSegmentSize;
+ }
+
+ /* Flush the directory entries before next checkpoint. */
+ undofile_request_sync_dir(slot->meta.tablespace);

I see that at two places after allocating empty undo segment, the
patch performs undofile_request_sync_dir whereas it doesn't perform
the same in UndoLogNewSegment? Is there a reason for the same or is it
missed from one of the places?

5.
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
{
..
/*
+ * We didn't need to acquire the mutex to read 'end' above because only
+ * we write to it.  But we need the mutex to update it, because the
+ * checkpointer might read it concurrently.

Is this assumption correct? It seems patch also modified
slot->meta.end during discard in function UndoLogDiscard. I am
referring below code:

+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
{
..
+ /* Update shmem to show the new discard and end pointers. */
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ slot->meta.discard = discard;
+ slot->meta.end = end;
+ LWLockRelease(&slot->mutex);
..
}
6.
extend_undo_log()
{
..
..
if (!InRecovery)
+ {
+ xl_undolog_extend xlrec;
+ XLogRecPtr ptr;
+
+ xlrec.logno = logno;
+ xlrec.end = end;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+ ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+ XLogFlush(ptr);
+ }

It is not obvious to me why we need to perform XLogFlush here, can you explain?

7.
+attach_undo_log(UndoLogCategory category, Oid tablespace)
{
..
if (candidate->meta.tablespace == tablespace)
+ {
+ logno = *place;
+ slot = candidate;
+ *place = candidate->next_free;
+ break;
+ }

Here, the code is breaking from the loop, so why do we need to set
*place? Am I missing something obvious?

8.
+ /* WAL-log the creation of this new undo log. */
+ {
+ xl_undolog_create xlrec;
+
+ xlrec.logno = logno;
+ xlrec.tablespace = slot->meta.tablespace;
+ xlrec.category = slot->meta.category;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));

Here and in most other places in this patch you are using
sizeof(xlrec) for xlog static data. However, as far as I know in
other places in the code we define the size using offset of the last
parameter of corresponding structure to avoid any inconsistency in WAL
record size across different platforms. Is there a reason to go
differently with this patch? See below one for example:

typedef struct xl_hash_add_ovfl_page
{
uint16 bmsize;
bool bmpage_found;
} xl_hash_add_ovfl_page;

#define SizeOfHashAddOvflPage
\
(offsetof(xl_hash_add_ovfl_page, bmpage_found) + sizeof(bool))

9.
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+ xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+ UndoLogSlot *slot;
+
+ /* Create meta-data space in shared memory. */
+ LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+ /* TODO: assert that it doesn't exist already? */
+
+ slot = allocate_undo_log_slot();
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);

Why do we need to acquire locks during recovery?

10.
I think UndoLogAllocate can leak allocation of slots. It first
allocates the slot for a new log from the free pool in there is no
existing slot/log, writes a WAL record and then at a later point of
time it actually creates the required physical space in the log via
extend_undo_log which also writes a separate WAL. Now, if there is a
error between these two operations, then we will have a redundant slot
allocated. What if there are repeated errors for similar thing from
multiple backends after which system crashes. Now, after restart, we
will allocate multiple slots for different lognos which don't have any
actual (physical) logs. This might not be a big problem in practice
because the chances of error between two operations are less, but
can't we delay the WAL logging for allocation of a slot for a new log.

11.
+UndoLogAllocate()
{
..
..
+ /*
+ * Maintain our tracking of the and the previous transaction start
+ * locations.
+ */
+ if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+ {
+ slot->meta.unlogged.last_xact_start =
+ slot->meta.unlogged.this_xact_start;
+ slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+ }

".. of the and the ..", after first the, something is missing.

12.
UndoLogAllocate()
{
..
..
+ /*
+ * We don't need to acquire log->mutex to read log->meta.insert and
+ * log->meta.end, because this backend is the only one that can
+ * modify them.
+ */
+ if (unlikely(new_insert > slot->meta.end))

I might be confused but slot->meta.end is modified by discard process
also, so how is it safe? If so, may be adding a comment to explain
the same would be good. Also, I think in the comments log should be
replaced with the slot.

13.
UndoLogAllocate()
{
..
+ /* This undo log is entirely full.  Get a new one. */
+ if (logxid == GetTopTransactionId())
+ {
+ /*
+ * If the same transaction is split over two undo logs then
+ * store the previous log number in new log.  See detailed
+ * comments in undorecord.c file header.
+ */
..
}

The undorecord.c should be renamed to undoaccess.c

14.
UndoLogAllocate()
{
..
+ if (logxid != GetTopTransactionId())
+ {
+ /*
+ * While we have the lock, check if we have been forcibly detached by
+ * DROP TABLESPACE.  That can only happen between transactions (see
+ * DropUndoLogsInsTablespace()).
+ */

/DropUndoLogsInsTablespace/DropUndoLogsInTablespace

15.
UndoLogSegmentPath()
{
..
/*
+ * Build the path from log number and offset.  The pathname is the
+ * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+ * period inserted between the components.
+ */
+ snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+ segno * UndoLogSegmentSize);
..
}

a. It is not very clear from the above code why are we multiplying
segno with UndoLogSegmentSize? I see that many of the callers pass
segno as segno/UndoLogSegmentSize. Won't it be better if the caller
take care of passing correct value of segno?
b. In the comment above, instead of offset, shouldn't there be segment number.

16. UndoLogGetLastXactStartPoint is not used any where. I think this
was required in previous version of patchset, now, we can remove it.

17.
Discussion: /messages/by-id/CAEepm=2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ@mail.gmail.com

This discussion link seems to be from old discussion/thread, not this one.

0019-Add-developer-documentation-for-the-undo-log-storage
18.
+each undo log, a set of meta-data properties is tracked:
+tracked, including:
+
+* the tablespace that holds its segment files
+* the persistence level (permanent, unlogged or temporary)

Here, don't we want to refer to UndoLogCategory rather than
persistence level? "tracked, including:" seems bit confusing.

0020-Add-user-facing-documentation-for-undo-logs
19.
<row>
+     <entry><structfield>persistence</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Persistence level of data stored in this undo log; one of
+      <literal>permanent</literal>, <literal>unlogged</literal> or
+      <literal>temporary</literal>.</entry>
+    </row>

Don't we want to cover the new (shared) undolog category here?

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#154Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#153)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 2:45 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Jul 1, 2019 at 1:24 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Yep, that was completely wrong. Here's a new version.

10.
I think UndoLogAllocate can leak allocation of slots. It first
allocates the slot for a new log from the free pool in there is no
existing slot/log, writes a WAL record and then at a later point of
time it actually creates the required physical space in the log via
extend_undo_log which also writes a separate WAL. Now, if there is a
error between these two operations, then we will have a redundant slot
allocated. What if there are repeated errors for similar thing from
multiple backends after which system crashes. Now, after restart, we
will allocate multiple slots for different lognos which don't have any
actual (physical) logs. This might not be a big problem in practice
because the chances of error between two operations are less, but
can't we delay the WAL logging for allocation of a slot for a new log.

After sending this email, I was browsing the previous comments raised
by me for this patch and it seems this same point was raised
previously [1]/messages/by-id/CAA4eK1LDctrYeZ8ev1N1v-8KwiigAmNMx=t-UTs9qgEFt+P0XQ@mail.gmail.com as well and there were few additional questions related
to same (See point-1 in email [1]/messages/by-id/CAA4eK1LDctrYeZ8ev1N1v-8KwiigAmNMx=t-UTs9qgEFt+P0XQ@mail.gmail.com.)

[1]: /messages/by-id/CAA4eK1LDctrYeZ8ev1N1v-8KwiigAmNMx=t-UTs9qgEFt+P0XQ@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#155Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#153)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 2:45 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

7.
+attach_undo_log(UndoLogCategory category, Oid tablespace)
{
..
if (candidate->meta.tablespace == tablespace)
+ {
+ logno = *place;
+ slot = candidate;
+ *place = candidate->next_free;
+ break;
+ }

Here, the code is breaking from the loop, so why do we need to set
*place? Am I missing something obvious?

I think I know what I was missing. It seems here you are removing an
element from the freelist.

One point related to detach_current_undo_log.

+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ slot->pid = InvalidPid;
+ slot->meta.unlogged.xid = InvalidTransactionId;
+ if (full)
+ slot->meta.status = UNDO_LOG_STATUS_FULL;
+ LWLockRelease(&slot->mutex);

If I read the comments in structure UndoLogMetaData, it is mentioned
that 'status' is changed by explicit WAL record whereas there is no
WAL record in code to change the status. I see the problem as well if
we don't WAL log this change. Suppose after changing the status of
this log, we allocate a new log and insert some records in that log as
well for the same transaction for which we have inserted records in
the log which we just marked as FULL. Now, here we form the link
between two logs as the same transaction has overflowed into a new
log. Say, we crash after this. Now, after recovery the log won't be
marked as FULL which means there is a chance that it can be used for
some other transaction, if that happens, then our link for a
transaction spanning to different log will break and we won't be able
to access the data in another log. In short, I think it is important
to WAL log this status change unless I am missing something.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#156vignesh C
vignesh21@gmail.com
In reply to: Amit Kapila (#155)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

I have done some review of undolog patch series
and here are my comments:
0003-Add-undo-log-manager.patch

1) As undo log is being created in tablespace,
 if the tablespace is dropped later, will it have any impact?
+void
+UndoLogDirectory(Oid tablespace, char *dir)
+{
+ if (tablespace == DEFAULTTABLESPACE_OID ||
+ tablespace == InvalidOid)
+ snprintf(dir, MAXPGPATH, "base/undo");
+ else
+ snprintf(dir, MAXPGPATH, "pg_tblspc/%u/%s/undo",
+ tablespace, TABLESPACE_VERSION_DIRECTORY);
+}
2) Header file exclusion
a) The following headers can be excluded in undolog.c
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "catalog/catalog.h"
+#include "nodes/execnodes.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "storage/standby.h"
+#include "storage/sync.h"
+#include "utils/memutils.h"
b) The following headers can be excluded from undofile.c
+#include "access/undolog.h"
+#include "catalog/database_internal.h"
+#include "miscadmin.h"
+#include "postmaster/bgwriter.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "utils/memutils.h"

3) Some macro replacement.
a)Session.h

+++ b/src/include/access/session.h
@@ -17,6 +17,9 @@
 /* Avoid including typcache.h */
 struct SharedRecordTypmodRegistry;

+/* Avoid including undolog.h */
+struct UndoLogSlot;
+
/*
* A struct encapsulating some elements of a user's session. For now this
* manages state that applies to parallel query, but it principle it could
@@ -27,6 +30,10 @@ typedef struct Session
dsm_segment *segment; /* The session-scoped DSM segment. */
dsa_area *area; /* The session-scoped DSA area. */

+ /* State managed by undolog.c. */
+ struct UndoLogSlot *attached_undo_slots[4]; /* UndoLogCategories */
+ bool need_to_choose_undo_tablespace;
+
Should we change 4 to UndoLogCategories or suitable macro?
b)
+static inline size_t
+UndoLogNumSlots(void)
+{
+ return MaxBackends * 4;
+}
Should we change 4 to UndoLogCategories  or suitable macro
c)
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+ UndoLogOffset end)
+{
+ struct stat stat_buffer;
+ off_t size;
+ char path[MAXPGPATH];
+ void   *zeroes;
+ size_t nzeroes = 8192;
+ int fd;

should we use BLCKSZ instead of 8192?

4) Should we add a readme file for undolog as it does a fair amount of work
and is core part of the undo system?

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

On Wed, Jul 24, 2019 at 5:44 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 24, 2019 at 2:45 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

7.
+attach_undo_log(UndoLogCategory category, Oid tablespace)
{
..
if (candidate->meta.tablespace == tablespace)
+ {
+ logno = *place;
+ slot = candidate;
+ *place = candidate->next_free;
+ break;
+ }

Here, the code is breaking from the loop, so why do we need to set
*place? Am I missing something obvious?

I think I know what I was missing. It seems here you are removing an
element from the freelist.

One point related to detach_current_undo_log.

+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ slot->pid = InvalidPid;
+ slot->meta.unlogged.xid = InvalidTransactionId;
+ if (full)
+ slot->meta.status = UNDO_LOG_STATUS_FULL;
+ LWLockRelease(&slot->mutex);

If I read the comments in structure UndoLogMetaData, it is mentioned
that 'status' is changed by explicit WAL record whereas there is no
WAL record in code to change the status. I see the problem as well if
we don't WAL log this change. Suppose after changing the status of
this log, we allocate a new log and insert some records in that log as
well for the same transaction for which we have inserted records in
the log which we just marked as FULL. Now, here we form the link
between two logs as the same transaction has overflowed into a new
log. Say, we crash after this. Now, after recovery the log won't be
marked as FULL which means there is a chance that it can be used for
some other transaction, if that happens, then our link for a
transaction spanning to different log will break and we won't be able
to access the data in another log. In short, I think it is important
to WAL log this status change unless I am missing something.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
Regards,
vignesh
Have a nice day

#157Amit Kapila
amit.kapila16@gmail.com
In reply to: vignesh C (#156)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 11:04 PM vignesh C <vignesh21@gmail.com> wrote:

Hi,

I have done some review of undolog patch series
and here are my comments:
0003-Add-undo-log-manager.patch

1) As undo log is being created in tablespace,
if the tablespace is dropped later, will it have any impact?

Yes, it drops the undo logs present in tablespace being dropped. See
DropUndoLogsInTablespace() in the same patch.

4) Should we add a readme file for undolog as it does a fair amount of work
and is core part of the undo system?

The Readme is already present in the patch series posted by Thomas.
See 0019-Add-developer-documentation-for-the-undo-log-storage.patch in
email [1]/messages/by-id/CA+hUKGKni7EEU4FT71vZCCwPeaGb2PQOeKOFjQJavKnD577UMQ@mail.gmail.com.

[1]: /messages/by-id/CA+hUKGKni7EEU4FT71vZCCwPeaGb2PQOeKOFjQJavKnD577UMQ@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#158vignesh C
vignesh21@gmail.com
In reply to: Amit Kapila (#157)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 25, 2019 at 7:48 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 24, 2019 at 11:04 PM vignesh C <vignesh21@gmail.com> wrote:

Hi,

I have done some review of undolog patch series
and here are my comments:
0003-Add-undo-log-manager.patch

1) As undo log is being created in tablespace,
if the tablespace is dropped later, will it have any impact?

Thanks Amit, that clarifies the problem I was thinking.
I have another question regarding drop table space failure, but I
don't have a better solution for that problem.
Let me think more about it and discuss.

Yes, it drops the undo logs present in tablespace being dropped. See
DropUndoLogsInTablespace() in the same patch.

4) Should we add a readme file for undolog as it does a fair amount of work
and is core part of the undo system?

Thanks Amit, I could get the details of readme.

The Readme is already present in the patch series posted by Thomas.
See 0019-Add-developer-documentation-for-the-undo-log-storage.patch in
email [1].

[1] - /messages/by-id/CA+hUKGKni7EEU4FT71vZCCwPeaGb2PQOeKOFjQJavKnD577UMQ@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

#159Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: vignesh C (#158)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

Here are some review comments on 0003-Add-undo-log-manager.patch. I've
tried to avoid duplicate comments as much as possible.

1. In UndoLogAllocate,
+ * time this backend as needed to write to an undo log at all or because
s/as/has

+ * Maintain our tracking of the and the previous transaction start
Do you mean current log's transaction start as well?

2. In UndoLogAllocateInRecovery,
we try to find the current log from the first undo buffer. So, after a
log switch, we always have to register at least one buffer from the
current undo log first. If we're updating something in the previous
log, the respective buffer should be registered after that. I think we
should document this in the comments.

3. In UndoLogGetOldestRecord(UndoLogNumber logno, bool *full),
it seems the 'full' parameter is not used anywhere. Do we still need this?

+ /* It's been recycled. SO it must have been entirely discarded. */
s/SO/So

4. In CleanUpUndoCheckPointFiles,
we can emit a debug2 message with something similar to : 'removed
unreachable undo metadata files'

+ if (unlink(path) != 0)
+ elog(ERROR, "could not unlink file \"%s\": %m", path);
according to my observation, whenever we deal with a file operation,
we usually emit a ereport message with errcode_for_file_access().
Should we change it to ereport? There are other file operations as
well including read(), OpenTransientFile() etc.
5. In CheckPointUndoLogs,
+ /* Capture snapshot while holding each mutex. */
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ serialized[num_logs++] = slot->meta;
+ LWLockRelease(&slot->mutex);
why do we need an exclusive lock to read something from the slot? A
share lock seems to be sufficient.

pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC) is called
after pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE)
without calling pgstat_report_wait_end(). I think you've done the
same to avoid an extra function call. But, it differs from other
places in the PG code. Perhaps, we should follow this approach
everywhere.

6. In StartupUndoLogs,
+ if (fd < 0)
+ elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
assuming your agreement upon changing above elog to ereport, the
message should be more user friendly. May be something like 'cannot
open pg_undo file'.

+ if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
The usage of size of doesn't look like a problem. But, we can save
some extra padding bytes at the end if we use (offsetof + sizeof)
approach similar to other places in PG.

7. In free_undo_log_slot,
+ /*
+ * When removing an undo log from a slot in shared memory, we acquire
+ * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+ * hold any one of those locks to prevent the slot from being recycled.
+ */
+ LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ Assert(slot->logno != InvalidUndoLogNumber);
+ slot->logno = InvalidUndoLogNumber;
+ memset(&slot->meta, 0, sizeof(slot->meta));
+ LWLockRelease(&slot->mutex);
+ LWLockRelease(UndoLogLock);
you've not taken the discard_lock as mentioned in the comment.

8. In find_undo_log_slot,
+ * 1. If the calling code knows that it is attached to this lock or is the
s/lock/slot

+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
Perhaps, you meant '..check that the logno remains same. If it is not..'.
+ /*
+ * If we didn't find it, then it must already have been entirely
+ * discarded.  We create a negative cache entry so that we can answer
+ * this question quickly next time.
+ *
+ * TODO: We could track the lowest known undo log number, to reduce
+ * the negative cache entry bloat.
+ */
This is an interesting thought. But, I'm wondering how we are going to
search the discarded logno in the simple hash. I guess that's why it's
in the TODO list.
9. In attach_undo_log,
+ * For now we have a simple linked list of unattached undo logs for each
+ * persistence level.  We'll grovel though it to find something for the
+ * tablespace you asked for.  If you're not using multiple tablespaces
s/though/through
+ if (slot == NULL)
+ {
+ if (UndoLogShared->next_logno > MaxUndoLogNumber)
+ {
+ /*
+ * You've used up all 16 exabytes of undo log addressing space.
+ * This is a difficult state to reach using only 16 exabytes of
+ * WAL.
+ */
+ elog(ERROR, "undo log address space exhausted");
+ }
looks like a potential unlikely() condition.

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

#160Thomas Munro
thomas.munro@gmail.com
In reply to: Amit Kapila (#153)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 9:15 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

I have done some more review of undolog patch series and here are my comments:

Hi Amit,

Thanks! There a number of actionable changes in your review. I'll be
posting a new patch set soon that will address most of your complaints
individually. In this message want to respond to one topic area,
because the answer is long enough already:

2.
allocate_empty_undo_segment()
{
..
..
/* Flush the contents of the file to disk before the next checkpoint. */
+ undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
..
}

+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+ char path[MAXPGPATH];
+ FileTag tag;
+
+ INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+ /* Try to send to the checkpointer, but if out of space, do it here. */
+ if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))

The comment in allocate_empty_undo_segment indicates that the code
wants to flush before checkpoint, but the actual function tries to
register the request with checkpointer. Shouldn't this be similar to
XLogFileInit where we use pg_fsync to flush the contents immediately?
I guess that will avoid what you have written in comments in the same
function (we just want to make sure that the filesystem has allocated
physical blocks for it so that non-COW filesystems will report ENOSPC
now rather than later when space is needed). OTOH, I think it is
performance-wise better to postpone the work to checkpointer. If we
want to push this work to checkpointer, then we might need to change
comments or alternatively, we might want to use bigger segment sizes
to mitigate the performance effect.

In an early version I was doing the fsync() immediately. While
testing zheap, Mithun CY reported that whenever segments couldn't be
recycled in the background, such as during a bit long-running
transaction, he could measure ~6% of the time time spent waiting for
fsync(), and throughput increased with bigger segments (and thus fewer
files to fsync()). Passing the work off to the checkpointer is better
not only because it's done in the background but also because there is
a chance that the work can be consolidated with other sync requests,
and perhaps even avoided completely if the file is discarded and
unlinked before the next checkpoint.

I'll update the comment to make it clearer.

If my above understanding is correct and the reason to fsync
immediately is to reserve space now, then we also need to think
whether we are always safe in postponing the work? Basically, if this
means that it can fail when we are actually trying to write undo, then
it could be risky because we could be in the critical section at that
time. I am not sure about this point, rather it is just to discuss if
there are any impacts of postponing the fsync work.

Here is my theory for why this arrangement is safe, and why it differs
from what we're doing with WAL segments and regular relation files.
First, let's review why those things work the way they do (as I
understand it):

1. WAL's use of fdatasync(): The reason we fill and then fsync()
newly created WAL files up front is because we want to make sure the
blocks are definitely on disk. The comment doesn't spell out exactly
why the author considered later fdatasync() calls to be insufficient,
but they were: it was many years after commit 33cc5d8a4d0d that Linux
ext3/4 filesystems began flushing file size changes to disk in
fdatasync()[1]https://lkml.org/lkml/2012/9/3/83[2]https://github.com/torvalds/linux/commit/b71fc079b5d8f42b2a52743c8d2f1d35d655b1c5. I don't know if its original behaviour was
intentional or not. So, if you didn't use the bigger fsync() hammer
on that OS, you might lose the end of a recently extended file in a
power failure even though fdatasync() had returned success.

By my reading of POSIX, that shouldn't be necessary on a conforming
implementation of fdatasync(), and that was fixed years ago in Linux.
I'm not proposing any changes there, and I'm not proposing to take
advantage of that in the new code. I'm pointing out that that we
don't have to worry about that for these undo segments, because they
are already flushed with fsync(), not fdatasync().

(To understand POSIX's descriptions of fsync() and fdatasync() you
have to find the meanings of "Synchronized I/O Data Integrity
Completion" and "Synchronized I/O File Integrity Completion" elsewhere
in the spec. TL;DR: fdatasync() is only allowed to skip flushing
attributes like the modified time, it's not allowed to skip flushing a
file size change since that would interfere with retrieving the data.)

2. Time of reservation: Although they don't call fsync(), regular
relations and these new undo files still write zeroes up front
(respectively, for a new block and for a new segment). One reason for
that is that most popular filesystems reserve space at write time, so
you'll get ENOSPC when trying to allocate undo space, and that's a
non-fatal ERROR. If we deferred until writing back buffer contents,
we might get file holes, and deferred ENOSPC is much harder to report
to users and for users to deal with.

You can still get a ENOSPC at checkpoint write-back time on COW
systems like ZFS, and there is not much I can do about that. You can
still get ENOSPC at checkpoint fsync() time on NFS, and there's not
much we can do about that for now except panic (without direct IO, or
other big changes).

3. Separate size tracking: Another reason that regular relations
write out zeroes at relation-extension time is that that's the only
place that the size of a relation is recorded. PostgreSQL doesn't
track the number of blocks itself, so we can't defer file extension
until write-back from our buffer pool. Undo doesn't rely on the
filesystem to track the amount of undo data, it has its own crash-safe
tracking of the discard and end pointers, which can be used to know
which segment files exist and what ranges contain data. That allows
us to work in whole files at a time, like WAL logs, even though we
still have checkpoint-based flushing rules.

To summarise, we write zeroes so we can report ENOSPC errors as early
as possible, but we defer and consolidate fsync() calls because the
files' contents and names don't actually have to survive power loss
until a checkpoint says they existed at that point in the WAL stream.

Does this make sense?

BTW we could probably use posix_fallocate() instead of writing zeroes;
I think Andres mentioned that recently. I see also that someone tried
that for WAL and it got reverted back in 2013 (commit
b1892aaeaaf34d8d1637221fc1cbda82ac3fcd71, I didn't try to hunt down
the discussion).

[1]: https://lkml.org/lkml/2012/9/3/83
[2]: https://github.com/torvalds/linux/commit/b71fc079b5d8f42b2a52743c8d2f1d35d655b1c5

--
Thomas Munro
https://enterprisedb.com

#161Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#160)
Re: POC: Cleaning up orphaned files using undo logs

Hi Thomas,

I have started reviewing 0003-Add-undo-log-manager, I haven't yet
reviewed but some places I noticed that instead of UndoRecPtr you are
directly
using UndoLogOffset. Which seems like bugs to me

1.
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+   TransactionId xid,
+   uint16 size,
+   bool *need_xact_header,
+   UndoRecPtr *last_xact_start,
....
+ *need_xact_header =
+ context->try_location == InvalidUndoRecPtr &&
+ slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+ *last_xact_start = slot->meta.unlogged.last_xact_start;

the output parameter last_xact_start is of type UndoRecPtr whereas
slot->meta.unlogged.last_xact_start is of type UndoLogOffset
shouldn't we use MakeUndoRecPtr(logno, offset) here?

2.
+ slot = find_undo_log_slot(logno, false);
+ if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+ {
+ *need_xact_header = false;
+ return try_offset;
+ }

Here also you are returning directly try_offset instead of UndoRecPtr

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#162Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#153)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 2:45 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

Yep, that was completely wrong. Here's a new version.

One comment/question related to
0022-Use-undo-based-rollback-to-clean-up-files-on-abort.patch.

I have done some more review of undolog patch series and here are my comments:
0003-Add-undo-log-manager.patch

Some more review of the same patch:
1.
+typedef struct UndoLogSharedData
+{
+ UndoLogNumber free_lists[UndoLogCategories];
+ UndoLogNumber low_logno;

What is the use of low_logno? I don't see anywhere in the code this
being assigned any value. Is it for some future use?

2.
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
{
..
+ /* Compute header checksum. */
+ INIT_CRC32C(crc);
+ COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+ COMP_CRC32C(crc, &UndoLogShared->next_logno,
sizeof(UndoLogShared->next_logno));
+ COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+ FIN_CRC32C(crc);
+
+ /* Write out the number of active logs + crc. */
+ if ((write(fd, &UndoLogShared->low_logno,
sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno))
||
+ (write(fd, &UndoLogShared->next_logno,
sizeof(UndoLogShared->next_logno)) !=
sizeof(UndoLogShared->next_logno)) ||

Is it safe to read UndoLogShared without UndoLogLock? All other
places accessing UndoLogShared uses UndoLogLock, so if this usage is
safe, maybe it is better to add a comment.

3.
UndoLogAllocateInRecovery()
{
..
/*
+ * Otherwise we need to do our own transaction tracking
+ * whenever we see a new xid, to match the logic in
+ * UndoLogAllocate().
+ */
+ if (xid != slot->meta.unlogged.xid)
+ {
+ slot->meta.unlogged.xid = xid;
+ if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+ slot->meta.unlogged.last_xact_start =
+ slot->meta.unlogged.this_xact_start;
+ slot->meta.unlogged.this_xact_start =
+ slot->meta.unlogged.insert;

The code doesn't follow the comment. In UndoLogAllocate, both
last_xact_start and this_xact_start are assigned in if block, so the
should be the case here.

4.
UndoLogAllocateInRecovery()
{
..
+ /*
+ * Just as in UndoLogAllocate(), the caller may be extending an existing
+ * allocation before committing with UndoLogAdvance().
+ */
+ if (context->try_location != InvalidUndoRecPtr)
+ {
..
}

I am not sure how will this work because unlike UndoLogAllocate, this
function doesn't set try_location initially. It will be set later by
UndoLogAdvance which can easily go wrong because that doesn't include
UndoLogBlockHeaderSize.

5.
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+ context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+ size);
+}

Here, you are using UndoRecPtr whereas UndoLogOffsetPlusUsableBytes
expects offset.

6.
UndoLogAllocateInRecovery()
{
..
+ /*
+ * At this stage we should have an undo log that can handle this
+ * allocation.  If we don't, something is screwed up.
+ */
+ if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) >
slot->meta.end)
+ elog(ERROR,
+ "cannot allocate %d bytes in undo log %d",
+ (int) size, slot->logno);
..
}

Similar to point-5, here you are using a pointer instead of offset.

7.
UndoLogAllocateInRecovery()
{
..
+ /* We found a reference to a different (or first) undo log. */
+ slot = find_undo_log_slot(logno, false);
..
+ /* TODO: check locking against undo log slot recycling? */
..
}

I think it is better to have an Assert here that slot can't be NULL.
AFAICS, slot can't be NULL unless there is some bug. I don't
understand this 'TODO' comment.

8.
+ {
+ {"undo_tablespaces", PGC_USERSET,
CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the
tablespace(s) to use for undo logs."),
+ NULL,
+
GUC_LIST_INPUT | GUC_LIST_QUOTE
+ },
+
&undo_tablespaces,
+ "",
+ check_undo_tablespaces,
assign_undo_tablespaces, NULL
+ },

It seems you need to update variable_is_guc_list_quote for this variable.

9.
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
{
..
+ if (!InRecovery)
+ {
+ xl_undolog_extend xlrec;
+ XLogRecPtr ptr;
+
+ xlrec.logno = logno;
+ xlrec.end = end;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+ ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+ XLogFlush(ptr);
+ }
..
}

Do we need it for temporary/unlogged persistence level? Similarly,
there is a WAL logging in attach_undo_log which I can't understand why
it would be required for temporary/unlogged persistence levels.

10.
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
{
..
+ oid = get_tablespace_oid(name, true);
+ if (oid == InvalidOid)
..
}

Do we need to check permissions to see if the current user is allowed
to create in this tablespace?

11.
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+ char   *rawname;
+ List   *namelist;
+ bool
need_to_unlock;
+ int length;
+ int
i;
+
+ /* We need a modifiable copy of string. */
+ rawname =
pstrdup(undo_tablespaces);

I don't see the usage of rawname outside this function, isn't it
better to free it? I understand that this function won't be called
frequently enough to matter, but still, there is some theoretical
danger if the user continuously changes undo_tablespaces.

12.
+find_undo_log_slot(UndoLogNumber logno, bool locked)
{
..
+ * TODO: We could track the lowest known undo log
number, to reduce
+ * the negative cache entry bloat.
+ */
+ if (result == NULL)
+ {
..
}

Do we have any mechanism to clear this bloat or will it stay till the
end of the session? If it is later, then I think it might be good to
take care of this TODO. I think this is not a blocker, but good to
have kind of stuff.

13.
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+ UndoLogOffset end)
{
..
}

What will happen if the transaction creating undolog segment rolls
back? Do we want to have pendingDeletes stuff as we have for normal
relation files? This might also help in clearing the shared memory
state (undo log slots) if any.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#163vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#158)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hi Thomas,

Few review comments on 0003-Add-undo-log-manager.patch:
1) Upgrade may fail
+/*
+ * Compute the new redo, and move the pg_undo file to match if necessary.
+ * Rather than renaming it, we'll create a new copy, so that a failure that
+ * occurs before the controlfile is rewritten won't be fatal.
+ */
+static void
+AdjustRedoLocation(const char *DataDir)
+{
+ uint64 old_redo = ControlFile.checkPointCopy.redo;
+ char old_pg_undo_path[MAXPGPATH];
+ char new_pg_undo_path[MAXPGPATH];
+ int old_fd;
+ int new_fd;
+ ssize_t nread;
+ ssize_t nwritten;
+ char buffer[1024];
+
+ /*
+ * Adjust fields as needed to force an empty XLOG starting at
+ * newXlogSegNo.
+ */

During the upgrade we delete the undo files present in the new cluster
and copy the undo files from the old cluster to the new cluster.
Then we try to readjust the redo location using pg_resetwal.
While trying to readjust we get the current control file details
from current cluster. We try to open the current undo file
present in the cluster using the details from the current cluster.
As the undo files from the current cluster have been removed
and replaced with the old cluster contents, the file open will fail.

Attached a patch to solve this problem.

2) drop table space failure in corner case.

+ else
+ {
+ /*
+ * There is data we need in this undo log.  We can't force it to
+ * be detached.
+ */
+ ok = false;
+ }
+ LWLockRelease(&slot->mutex);
+ /* If we failed, then give up now and report failure. */
+ if (!ok)
+ return false;

One thought, can we discard the current tablespace entries
and try not to fail.

3) There will be a problem if some files deletion is successful and some
file deletion fails, the meta contents having end details also need to be
applied or to handle the case where the undo is created further after
rollback

+ while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+ {
+ char segment_path[MAXPGPATH];
+
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ continue;
+ snprintf(segment_path, sizeof(segment_path), "%s/%s",
+ undo_path, de->d_name);
+ if (unlink(segment_path) < 0)
+ elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+ }
4)  In error case unlinked undo segment message will be logged
+ while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+ {
+ char segment_path[MAXPGPATH];
+
+ if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+ continue;
+ snprintf(segment_path, sizeof(segment_path), "%s/%s",
+ undo_path, de->d_name);
+ elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+ if (unlink(segment_path) < 0)
+ elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+ }
+ FreeDir(dir);

In error case the success message will be logged.

5) UndoRecPtrIsValid can be used to check InvalidUndoRecPtr
+ /*
+ * 'size' is expressed in usable non-header bytes.  Figure out how far we
+ * have to move insert to create space for 'size' usable bytes, stepping
+ * over any intervening headers.
+ */
+ Assert(slot->meta.unlogged.insert % BLCKSZ >= UndoLogBlockHeaderSize);
+ if (context->try_location != InvalidUndoRecPtr)

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Show quoted text

On Thu, Jul 25, 2019 at 9:30 AM vignesh C <vignesh21@gmail.com> wrote:

On Thu, Jul 25, 2019 at 7:48 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 24, 2019 at 11:04 PM vignesh C <vignesh21@gmail.com> wrote:

Hi,

I have done some review of undolog patch series
and here are my comments:
0003-Add-undo-log-manager.patch

1) As undo log is being created in tablespace,
if the tablespace is dropped later, will it have any impact?

Thanks Amit, that clarifies the problem I was thinking.
I have another question regarding drop table space failure, but I
don't have a better solution for that problem.
Let me think more about it and discuss.

Yes, it drops the undo logs present in tablespace being dropped. See
DropUndoLogsInTablespace() in the same patch.

4) Should we add a readme file for undolog as it does a fair amount of work
and is core part of the undo system?

Thanks Amit, I could get the details of readme.

The Readme is already present in the patch series posted by Thomas.
See 0019-Add-developer-documentation-for-the-undo-log-storage.patch in
email [1].

[1] - /messages/by-id/CA+hUKGKni7EEU4FT71vZCCwPeaGb2PQOeKOFjQJavKnD577UMQ@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-pg_upgrade-failure-fix.patchapplication/x-patch; name=0001-pg_upgrade-failure-fix.patchDownload
From 66578336861e02b9dc5544b7d2fed46261681998 Mon Sep 17 00:00:00 2001
From: Vigneshwaran c <vignesh21@gmail.com>
Date: Mon, 8 Jul 2019 22:49:18 +0530
Subject: [PATCH]     pg_upgrade failure fix.

    For adjusting undo the old cluster redo need to be passed.

    Patch by Vigneshwaran C, reviewed by Dilip Kumar.
---
 src/bin/pg_resetwal/pg_resetwal.c | 34 ++++++++++++++++++++++++++++------
 src/bin/pg_upgrade/pg_upgrade.c   |  4 ++--
 2 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 0610a8c..396613b 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -85,7 +85,7 @@ static bool ReadControlFile(void);
 static void GuessControlValues(void);
 static void PrintControlValues(bool guessed);
 static void PrintNewControlValues(void);
-static void AdjustRedoLocation(const char *DataDir);
+static void AdjustRedoLocation(const char *DataDir, uint64 prev_redo);
 static void RewriteControlFile(void);
 static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
@@ -109,6 +109,7 @@ main(int argc, char *argv[])
 		{"next-oid", required_argument, NULL, 'o'},
 		{"multixact-offset", required_argument, NULL, 'O'},
 		{"next-transaction-id", required_argument, NULL, 'x'},
+		{"old-redo", required_argument, NULL, 'r'},
 		{"wal-segsize", required_argument, NULL, 1},
 		{NULL, 0, NULL, 0}
 	};
@@ -121,6 +122,7 @@ main(int argc, char *argv[])
 	char	   *endptr2;
 	char	   *DataDir = NULL;
 	char	   *log_fname = NULL;
+	uint64	   old_redo = 0;
 	int			fd;
 	char		latest_undo_checkpoint_file[MAXPGPATH];
 	char		new_undo_checkpoint_file[MAXPGPATH];
@@ -145,7 +147,7 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:x:", long_options, NULL)) != -1)
+	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:r:O:x:", long_options, NULL)) != -1)
 	{
 		switch (c)
 		{
@@ -239,6 +241,21 @@ main(int argc, char *argv[])
 				}
 				break;
 
+			case 'r':
+				old_redo = strtoull(optarg, &endptr, 0);
+				if (endptr == optarg || *endptr != '\0')
+				{
+					pg_log_error("invalid argument for option %s", "-r");
+					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					exit(1);
+				}
+				if (old_redo == 0)
+				{
+					pg_log_error("Old Redo (-r) must not be 0");
+					exit(1);
+				}
+				break;
+
 			case 'm':
 				set_mxid = strtoul(optarg, &endptr, 0);
 				if (endptr == optarg || *endptr != ',')
@@ -521,7 +538,7 @@ main(int argc, char *argv[])
 	/*
 	 * Else, do the dirty deed.
 	 */
-	AdjustRedoLocation(DataDir);
+	AdjustRedoLocation(DataDir, old_redo);
 	RewriteControlFile();
 
 	/*
@@ -800,6 +817,9 @@ PrintControlValues(bool guessed)
 		   ControlFile.catalog_version_no);
 	printf(_("Database system identifier:           %s\n"),
 		   sysident_str);
+	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
+		   (uint32) (ControlFile.checkPointCopy.redo >> 32),
+		   (uint32) ControlFile.checkPointCopy.redo);
 	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
 		   ControlFile.checkPointCopy.ThisTimeLineID);
 	printf(_("Latest checkpoint's full_page_writes: %s\n"),
@@ -940,9 +960,10 @@ PrintNewControlValues(void)
  * occurs before the controlfile is rewritten won't be fatal.
  */
 static void
-AdjustRedoLocation(const char *DataDir)
+AdjustRedoLocation(const char *DataDir, uint64 prev_redo)
 {
-	uint64		old_redo = ControlFile.checkPointCopy.redo;
+	uint64		old_redo = (prev_redo != 0) ? prev_redo:
+				  ControlFile.checkPointCopy.redo;
 	char		old_pg_undo_path[MAXPGPATH];
 	char		new_pg_undo_path[MAXPGPATH];
 	int			old_fd;
@@ -985,7 +1006,7 @@ AdjustRedoLocation(const char *DataDir)
 	new_fd = open(new_pg_undo_path, O_RDWR | O_CREAT, 0644);
 	if (new_fd < 0)
 	{
-		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		pg_log_error("could not open \"%s\": %m", new_pg_undo_path);
 		exit(1);
 	}
 	while ((nread = read(old_fd, buffer, sizeof(buffer))) > 0)
@@ -1399,6 +1420,7 @@ usage(void)
 	printf(_("  -n, --dry-run                  no update, just show what would be done\n"));
 	printf(_("  -o, --next-oid=OID             set next OID\n"));
 	printf(_("  -O, --multixact-offset=OFFSET  set next multitransaction offset\n"));
+	printf(_("  -r, --old-redo=REDO            use old redo for adjusting the UNDO\n"));
 	printf(_("  -V, --version                  output version information, then exit\n"));
 	printf(_("  -x, --next-transaction-id=XID  set next transaction ID\n"));
 	printf(_("      --wal-segsize=SIZE         size of WAL segments, in megabytes\n"));
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index c611203..3a6ade9 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -484,9 +484,9 @@ copy_xact_xlog_xid(void)
 	/* set the next transaction id and epoch of the new cluster */
 	prep_status("Setting next transaction ID and epoch for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
-			  "\"%s/pg_resetwal\" -f -x %u \"%s\"",
+			  "\"%s/pg_resetwal\" -f -x %u  -r "UINT64_FORMAT" \"%s\"",
 			  new_cluster.bindir, old_cluster.controldata.chkpnt_nxtxid,
-			  new_cluster.pgdata);
+			  old_cluster.controldata.redo_location, new_cluster.pgdata);
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
 			  "\"%s/pg_resetwal\" -f -e %u \"%s\"",
 			  new_cluster.bindir, old_cluster.controldata.chkpnt_nxtepoch,
-- 
1.8.3.1

#164Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#160)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 25, 2019 at 11:22 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Wed, Jul 24, 2019 at 9:15 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

I have done some more review of undolog patch series and here are my comments:

Hi Amit,

Thanks! There a number of actionable changes in your review. I'll be
posting a new patch set soon that will address most of your complaints
individually. In this message want to respond to one topic area,
because the answer is long enough already:

2.
allocate_empty_undo_segment()
{
..
..
/* Flush the contents of the file to disk before the next checkpoint. */
+ undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
..
}

+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+ char path[MAXPGPATH];
+ FileTag tag;
+
+ INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+ /* Try to send to the checkpointer, but if out of space, do it here. */
+ if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))

The comment in allocate_empty_undo_segment indicates that the code
wants to flush before checkpoint, but the actual function tries to
register the request with checkpointer. Shouldn't this be similar to
XLogFileInit where we use pg_fsync to flush the contents immediately?
I guess that will avoid what you have written in comments in the same
function (we just want to make sure that the filesystem has allocated
physical blocks for it so that non-COW filesystems will report ENOSPC
now rather than later when space is needed). OTOH, I think it is
performance-wise better to postpone the work to checkpointer. If we
want to push this work to checkpointer, then we might need to change
comments or alternatively, we might want to use bigger segment sizes
to mitigate the performance effect.

In an early version I was doing the fsync() immediately. While
testing zheap, Mithun CY reported that whenever segments couldn't be
recycled in the background, such as during a bit long-running
transaction, he could measure ~6% of the time time spent waiting for
fsync(), and throughput increased with bigger segments (and thus fewer
files to fsync()). Passing the work off to the checkpointer is better
not only because it's done in the background but also because there is
a chance that the work can be consolidated with other sync requests,
and perhaps even avoided completely if the file is discarded and
unlinked before the next checkpoint.

I'll update the comment to make it clearer.

Okay, that makes sense.

If my above understanding is correct and the reason to fsync
immediately is to reserve space now, then we also need to think
whether we are always safe in postponing the work? Basically, if this
means that it can fail when we are actually trying to write undo, then
it could be risky because we could be in the critical section at that
time. I am not sure about this point, rather it is just to discuss if
there are any impacts of postponing the fsync work.

Here is my theory for why this arrangement is safe, and why it differs
from what we're doing with WAL segments and regular relation files.
First, let's review why those things work the way they do (as I
understand it):

1. WAL's use of fdatasync():

I was referring to function XLogFileInit which doesn't appear to be
directly using fdatasync.

3. Separate size tracking: Another reason that regular relations
write out zeroes at relation-extension time is that that's the only

..

To summarise, we write zeroes so we can report ENOSPC errors as early
as possible, but we defer and consolidate fsync() calls because the
files' contents and names don't actually have to survive power loss
until a checkpoint says they existed at that point in the WAL stream.

Does this make sense?

Yes, this makes sense. However, I wonder if we need to do some
special handling for ENOSPC while writing to file in this function
(allocate_empty_undo_segment). Basically, unlink/remove the file if
fail to write because of disk full, something similar to what we do in
XLogFileInit.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#165Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#161)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 25, 2019 at 11:25 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Hi Thomas,

I have started reviewing 0003-Add-undo-log-manager, I haven't yet
reviewed but some places I noticed that instead of UndoRecPtr you are
directly
using UndoLogOffset. Which seems like bugs to me

1.
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+   TransactionId xid,
+   uint16 size,
+   bool *need_xact_header,
+   UndoRecPtr *last_xact_start,
....
+ *need_xact_header =
+ context->try_location == InvalidUndoRecPtr &&
+ slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+ *last_xact_start = slot->meta.unlogged.last_xact_start;

the output parameter last_xact_start is of type UndoRecPtr whereas
slot->meta.unlogged.last_xact_start is of type UndoLogOffset
shouldn't we use MakeUndoRecPtr(logno, offset) here?

2.
+ slot = find_undo_log_slot(logno, false);
+ if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+ {
+ *need_xact_header = false;
+ return try_offset;
+ }

Here also you are returning directly try_offset instead of UndoRecPtr

+UndoLogRegister(UndoLogAllocContext *context, uint8 block_id,
UndoLogNumber logno)
+{
+ int i;
+
+ for (i = 0; i < context->num_meta_data_images; ++i)
+ {
+ if (context->meta_data_images[i].logno == logno)
+ {
+ XLogRegisterBufData(block_id,
+ (char *) &context->meta_data_images[i].data,
+ sizeof(context->meta_data_images[i].data));
+ return;
+ }
+ }
+}

I have observed one more thing that you are registering
"meta_data_images" with each buffer of that log. Suppose, if one undo
record is spread across 2 undo blocks then both the blocks will
include a duplicate copy of this metadata image if this first changes
after the checkpoint? It will not cause any issue but IMHO we can
avoid including 2 copies of the same meta_data_image.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#166Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Khandekar (#149)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 23, 2019 at 8:12 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Tue, 23 Jul 2019 at 08:48, Amit Kapila <amit.kapila16@gmail.com> wrote:

--------------

+ if (!InsertRequestIntoErrorUndoQueue(urinfo))
I was thinking what happens if for some reason
InsertRequestIntoErrorUndoQueue() itself errors out. In that case, the
entry will not be marked invalid, and so there will be no undo action
carried out because I think the undo worker will exit. What happens
next with this entry ?

The same entry is present in two queues xid and size, so next time it
will be executed from the second queue based on it's priority in that
queue. However, if it fails again a second time in the same way, then
we will be in trouble because now the hash table has entry, but none
of the queues has entry, so none of the workers will attempt to
execute again. Also, when discard worker again tries to register it,
we won't allow adding the entry to queue thinking either some backend
is executing the same or it must be part of some queue.

The one possibility to deal with this could be that we somehow allow
discard worker to register it again in the queue or we can do this in
critical section so that it allows system restart on error. However,
the main thing is it possible that InsertRequestIntoErrorUndoQueue
will fail unless there is some bug in the code? If so, we might want
to have an Assert for this rather than handling this condition.

Yes, I also think that the function would error out only because of
can't-happen cases, like "too many locks taken" or "out of binary heap
slots" or "out of memory" (this last one is not such a can't happen
case). These cases happen probably due to some bugs, I suppose. But I
was wondering : Generally when the code errors out with such
can't-happen elog() calls, worst thing that happens is that the
transaction gets aborted. Whereas, in this case, the worst thing that
could happen is : the undo action would never get executed, which
means selects for this tuple will keep on accessing the undo log ?

Yeah, or in zheap, we have page-wise rollback facility which rollbacks
the transaction for a particular page (this gets triggers whenever we
try to update/delete a tuple which was last updated by aborted xact or
when we try to reuse slot of aborted xact) and we don't need to
traverse undo chain.

This does not sound like any data consistency issue, so we should be
fine after all ?

I will see if we can have an Assert in the code for this.

--------------

+if (UndoGetWork(false, false, &urinfo, NULL) &&
+    IsUndoWorkerAvailable())
+    UndoWorkerLaunch(urinfo);

There is no lock acquired between IsUndoWorkerAvailable() and
UndoWorkerLaunch(); that means even though IsUndoWorkerAvailable()
returns true, there is a small window where UndoWorkerLaunch() does
not find any worker slot with in_use false, causing assertion failure
for (worker != NULL).
--------------

Yeah, I think UndoWorkerLaunch should be able to return without
launching worker in such a case.

+ if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+ {
+ /* Failed to start worker, so clean up the worker slot. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+ UndoWorkerCleanup(worker);
+ LWLockRelease(UndoWorkerLock);
+
+ return false;
+ }

Is it intentional that there is no (warning?) message logged when we
can't register a bg worker ?
-------------

I don't think it was intentional. I think it will be good to have a
warning here.

I agree with all your other comments.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#167Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#150)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 10:00 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 22, 2019 at 3:51 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Please find my review comments for
0013-Allow-foreground-transactions-to-perform-undo-action

+ * We can't postpone applying undo actions for subtransactions as the
+ * modifications made by aborted subtransaction must not be visible even if
+ * the main transaction commits.
+ */
+ if (IsSubTransaction())
+ return;

I am not completely sure but is it possible that the outer function
CommitTransactionCommand/AbortCurrentTransaction can avoid
calling this function in the switch case based on the current state,
so that under subtransaction this will never be called?

We can do that and also can have an additional check similar to "if
(!s->performUndoActions)", but such has to be all places from where
this function is called. I feel that will make code less readable at
many places.

+ bool undo_req_pushed[UndoLogCategories]; /* undo request pushed
+ * to worker? */
+ bool performUndoActions;
+
struct TransactionStateData *parent; /* back link to parent */

We must have some comments to explain how performUndoActions is used,
where it's set. If it's explained somewhere else then we can
give reference to that code.

I am planning to remove this variable in the next version and have an
explicit check as we have in UndoActionsRequired.

I agree with your other comments and will address them in the next
version of the patch.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#168Amit Khandekar
amitdkhan.pg@gmail.com
In reply to: Amit Kapila (#166)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, 26 Jul 2019 at 12:25, Amit Kapila <amit.kapila16@gmail.com> wrote:

I agree with all your other comments.

Thanks for addressing the comments. Below is the continuation of my
comments from 0014-Allow-execution-and-discard-of-undo-by-background-wo.patch
:

+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
for first request and that is required => for the first request. This
is required

---------------

+UndoLauncherShmemSize(void)
+{
+    Size        size;
+
+    /*
+     * Need the fixed struct and the array of LogicalRepWorker.
+     */
+    size = sizeof(UndoApplyCtxStruct);

The fixed structure size should be offsetof(UndoApplyCtxStruct,
workers) rather than sizeof(UndoApplyCtxStruct)

---------------

In UndoWorkerCleanup(), we set individual fields of the
UndoApplyWorker structure, whereas in UndoLauncherShmemInit(), for all
the UndoApplyWorker array elements, we just memset all the
UndoApplyWorker structure elements to 0. I think we should be
consistent for the two cases. I guess we can just memset to 0 as you
do in UndoLauncherShmemInit(), but this will cause the
worker->undo_worker_queue to be 0 i.e. XID_QUEUE , whereas in
UndoWorkerCleanup(), it is set to -1. Is the -1 value essential, or we
can just set it to XID_QUEUE initially ?
Also, if we just use memset in UndoWorkerCleanup(), we need to first
save generation into a temp variable, and then after memset(), restore
it back.

That brought me to another point :
We already have a macro ResetUndoRequestInfo(), so UndoWorkerCleanup()
can just call ResetUndoRequestInfo().
------------

+        bool        allow_peek;
+
+        CHECK_FOR_INTERRUPTS();
+
+        allow_peek = !TimestampDifferenceExceeds(started_at,
Some comments would be good about what is allow_peek  used for. Something like :
"Arrange to prevent the worker from restarting quickly to switch databases"
-----------------
+++ b/src/backend/access/undo/README.UndoProcessing
-----------------
+worker then start reading from one of the queues the requests for that
start=>starts
---------------
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
As per the latest definition, it is 20s. IMHO, there's no need to
mention the default value in the readme.
---------------
+++ b/src/backend/access/undo/discardworker.c
---------------

+ * portion of transaction that is overflowed into a separate log can
be processed
80-col crossed.

+#include "access/undodiscard.h"
+#include "access/discardworker.h"
Not in alphabetical order
+++ b/src/backend/access/undo/undodiscard.c
---------------
+        next_insert = UndoLogGetNextInsertPtr(logno);
I checked UndoLogGetNextInsertPtr() definition. It calls
find_undo_log_slot() to get back the slot from logno. Why not make it
accept slot as against logno ? At all other places, the slot->logno is
passed, so it is convenient to just pass the slot there. And in
UndoDiscardOneLog(), first call find_undo_log_slot() just before the
above line (or call it at the end of the do-while loop). This way,
during each of the UndoLogGetNextInsertPtr() calls in undorequest.c,
we will have one less find_undo_log_slot() call. My suggestion is of
course valid only under the assumption that when you call
UndoLogGetNextInsertPtr(fooslot->logno), then inside
UndoLogGetNextInsertPtr(), find_undo_log_slot() will return back the
same fooslot.
-------------

In UndoDiscardOneLog(), there are at least 2 variable declarations
that can be moved inside the do-while loop : uur and next_insert. I am
not sure about the other variables viz : undofxid and
latest_discardxid. Values of these variables in one iteration continue
across to the second iteration. For latest_discardxid, it looks like
we do want its value to be carried forward, but is it also true for
undofxid ?

+ /* If we reach here, this means there is something to discard. */
+     need_discard = true;
+ } while (true);

Also, about need_discard; there is no place where need_discard is set
to false. That means, from 2nd iteration onwards, it will never be
false. So even if the code that explicitly sets need_discard to true
does not get run, still the undolog will be discarded. Is this
expected ?
-------------

+            if (request_rollback && dbid_exists(uur->uur_txn->urec_dbid))
+            {
+                (void) RegisterRollbackReq(InvalidUndoRecPtr,
+                                           undo_recptr,
+                                           uur->uur_txn->urec_dbid,
+                                           uur->uur_fxid);
+
+                pending_abort = true;
+            }
We can get rid of request_rollback variable. Whatever the "if" block
above is doing, do it in this upper condition :
if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))

Something like this :

if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
{
if (dbid_exists(uur->uur_txn->urec_dbid))
{
(void) RegisterRollbackReq(InvalidUndoRecPtr,
undo_recptr,
uur->uur_txn->urec_dbid,
uur->uur_fxid);

pending_abort = true;
}
}
-------------

+            UndoRecordRelease(uur);
+            uur = NULL;
+        }
.....
.....
+        Assert(uur == NULL);
+
+        /* If we reach here, this means there is something to discard. */
+        need_discard = true;
+    } while (true);

Looks like it is neither necessary to set uur to NULL, nor is it
necessary to have the Assert(uur == NULL). At the start of each
iteration uur is anyway assigned a fresh value, which may or may not
be NULL.
-------------

+ * over undo logs is complete, new undo can is allowed to be written in the
new undo can is allowed => new undo is allowed

+ * hash table size.  So before start allowing any new transaction to write the
before start allowing => before allowing any new transactions to start
writing the
-------------
+    /* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+    oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+   ....
+   ....
+    if (FullTransactionIdIsValid(oldestXidHavingUndo))
+        pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+                            U64FromFullTransactionId(oldestXidHavingUndo));

Is it possible that the FullTransactionId returned by
RollbackHTGetOldestFullXid() could be invalid ? If not, then the if
condition above can be changed to an Assert().
-------------

+         * If the log is already discarded, then we are done.  It is important
+         * to first check this to ensure that tablespace containing this log
+         * doesn't get dropped concurrently.
+         */
+        LWLockAcquire(&slot->mutex, LW_SHARED);
+        /*
+         * We don't have to worry about slot recycling and check the logno
+         * here, since we don't care about the identity of this slot, we're
+         * visiting all of them.
I guess, it's accidental that the LWLockAcquire() call is *between*
the two comments ?
-----------
+            if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)
+            {
+                /*
+                 * For the "shared" category, we only discard when the
+                 * rm_undo_status callback tells us we can.
+                 */
+                status = RmgrTable[uur->uur_rmid].rm_undo_status(uur,
&wait_xid);
status variable could be declared in this block itself.
-------------

Some variable declaration alignments and comments spacing need changes
as per pgindent.

--
Thanks,
-Amit Khandekar
EnterpriseDB Corporation
The Postgres Database Company

#169Andres Freund
andres@anarazel.de
In reply to: Thomas Munro (#160)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-07-25 17:51:33 +1200, Thomas Munro wrote:

1. WAL's use of fdatasync(): The reason we fill and then fsync()
newly created WAL files up front is because we want to make sure the
blocks are definitely on disk. The comment doesn't spell out exactly
why the author considered later fdatasync() calls to be insufficient,
but they were: it was many years after commit 33cc5d8a4d0d that Linux
ext3/4 filesystems began flushing file size changes to disk in
fdatasync()[1][2]. I don't know if its original behaviour was
intentional or not. So, if you didn't use the bigger fsync() hammer
on that OS, you might lose the end of a recently extended file in a
power failure even though fdatasync() had returned success.

By my reading of POSIX, that shouldn't be necessary on a conforming
implementation of fdatasync(), and that was fixed years ago in Linux.
I'm not proposing any changes there, and I'm not proposing to take
advantage of that in the new code. I'm pointing out that that we
don't have to worry about that for these undo segments, because they
are already flushed with fsync(), not fdatasync().

(To understand POSIX's descriptions of fsync() and fdatasync() you
have to find the meanings of "Synchronized I/O Data Integrity
Completion" and "Synchronized I/O File Integrity Completion" elsewhere
in the spec. TL;DR: fdatasync() is only allowed to skip flushing
attributes like the modified time, it's not allowed to skip flushing a
file size change since that would interfere with retrieving the data.)

Note that there's very good performance reasons trying to avoid metadata
changes at e.g. commit time. They're commonly journaled at the FS level,
which can add a good chunk of IO and synchronization to an operations
that we commonly want to be as fast as possible. Basically you often at
least double the amount of synchronous writes.

And for the potential future where use async direct IO - writes that
change the file size take considerably slower codepaths, and add a lot
of synchronization.

I suspect that's much more likely to be the reason for the preallocation
in 33cc5d8a4d0d, than avoiding an ext* bug (I doubt the bug you
reference existed back then, it IIUC didn't apply to ext2, and ext3 was
was introduced after 33cc5d8a4d0d).

2. Time of reservation: Although they don't call fsync(), regular
relations and these new undo files still write zeroes up front
(respectively, for a new block and for a new segment). One reason for
that is that most popular filesystems reserve space at write time, so
you'll get ENOSPC when trying to allocate undo space, and that's a
non-fatal ERROR. If we deferred until writing back buffer contents,
we might get file holes, and deferred ENOSPC is much harder to report
to users and for users to deal with.

FWIW, the hole bit I don't quite buy - we could zero the hole at that
time (and not be worse than today, except that it might be done by
somebody that didn't cause the extension), or even better just look up
the buffers between the FS end of the relation, and the block currently
written, and write them out in order.

The whole thing with deferred ENOSPC being harder to report to users is
obviously true regardless of htat.

BTW we could probably use posix_fallocate() instead of writing zeroes;
I think Andres mentioned that recently. I see also that someone tried
that for WAL and it got reverted back in 2013 (commit
b1892aaeaaf34d8d1637221fc1cbda82ac3fcd71, I didn't try to hunt down
the discussion).

IIRC the problem from back then was that while the space is reserved on
the FS level, the actual blocks don't contain zeroes at that time. Which
means that

a) Small writes need to write more, because the surrounding data also
needs to be zeroed (annoying but not terrible).

b) Writes into the fallocated but not written range IIRC effectively
cause metadata writes, because while the "allocated file ending"
doesn't change anymore, the new "non-zero written to" fileending does
need to be journaled to disk before an f[data]sync - otherwise you
could end up with the old value after a crash, and would read
spurious zeroes.

That's quite bad.

Those don't necessarily apply to e.g. extending relations as we
e.g. don't granularly fsync them. Although even there the performance
picture is mixed - it helps a lot in certain workloads, but there's
others were it mildly regresses performance on ext4. Not sure why yet,
possibly it's due to more heavyweight locking needed when later changing
the "non-zero size", or it's the additional metadata changes. I suspect
those would be mostly gone if we didn't write back blocks in random
order under memory pressure.

Note that neither of those mean that it's not a good idea to
posix_fallocate() and *then* write zeroes, when initializing. For
several filesystems that's more likely to result in more optimally sized
filesystem extents, reducing fragmentation. And without an intervening
f[data]sync, there's not much additional metadata journalling. Although
that's less of an issue on some newer filesystems, IIRC (due to delayed
allocation).

Greetings,

Andres Freund

#170Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#169)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Jul 27, 2019 at 2:27 PM Andres Freund <andres@anarazel.de> wrote:

Note that neither of those mean that it's not a good idea to
posix_fallocate() and *then* write zeroes, when initializing. For
several filesystems that's more likely to result in more optimally sized
filesystem extents, reducing fragmentation. And without an intervening
f[data]sync, there's not much additional metadata journalling. Although
that's less of an issue on some newer filesystems, IIRC (due to delayed
allocation).

Interesting. One way to bring back posix_fallocate() without
upsetting people on some filesystem out there would be to turn the new
wal_init_zero GUC into a choice: write (current default, and current
behaviour for 'on'), pwrite_hole (write just the final byte, current
behaviour for 'off'), posix_fallocate (like that 2013 patch that was
reverted) and posix_fallocate_and_write (do both as you said, to try
to solve that problem you mentioned that led to the revert).

I suppose there'd be a parallel GUC undo_init_zero. Or some more
general GUC for any fixed-sized preallocated files like that (for
example if someone were to decide to do the same for SLRU files
instead of growing them block-by-block), called something like
file_init_zero.

--
Thomas Munro
https://enterprisedb.com

#171Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#68)
Re: POC: Cleaning up orphaned files using undo logs

Hi

On 2019-06-26 01:29:57 +0530, Amit Kapila wrote:

From 67845a7afa675e973bd0ea9481072effa1eb219d Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/14] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

+#ifdef USE_PREFETCH
/*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.

* No-op if prefetching isn't compiled in.

This isn't true for the this function, as you've defined it?

diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 2aa4952..14ccc52 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -117,7 +117,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
void
undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
{
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
}

This looks like it should be part of the commit that introduces
undofile_prefetch(), rather than separately? Afaics there's no reason to
have it in this commit.

From 7206c40e4cee3391c537cdb22c854889bb417d0e Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 6 Mar 2019 16:46:04 +1300
Subject: [PATCH 03/14] Add undo log manager.

+/*
+ * If the caller doesn't know the the block_id, but does know the RelFileNode,
+ * forknum and block number, then we try to find it.
+ */
+XLogRedoAction
+XLogReadBufferForRedoBlock(XLogReaderState *record,
+						   SmgrId smgrid,
+						   RelFileNode rnode,
+						   ForkNumber forknum,
+						   BlockNumber blockno,
+						   ReadBufferMode mode,
+						   bool get_cleanup_lock,
+						   Buffer *buf)

I find that a somewhat odd function comment. Nor does the function name
tell me much. A buffer is always block sized. And you pass in a block
number.

@@ -347,7 +409,8 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
* Make sure that if the block is marked with WILL_INIT, the caller is
* going to initialize it. And vice versa.
*/
-	zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
+	zeromode = (mode == RBM_ZERO || mode == RBM_ZERO_AND_LOCK ||
+				mode == RBM_ZERO_AND_CLEANUP_LOCK);
willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
if (willinit && !zeromode)
elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
@@ -463,7 +526,7 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
{
/* page exists in file */
buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-										   mode, NULL);
+										   mode, NULL, RELPERSISTENCE_PERMANENT);
}
else
{
@@ -488,7 +551,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
ReleaseBuffer(buffer);
}
buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum,
-											   P_NEW, mode, NULL);
+											   P_NEW, mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
}
while (BufferGetBlockNumber(buffer) < blkno);
/* Handle the corner case that P_NEW returns non-consecutive pages */
@@ -498,7 +562,8 @@ XLogReadBufferExtended(SmgrId smgrid, RelFileNode rnode, ForkNumber forknum,
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buffer);
buffer = ReadBufferWithoutRelcache(smgrid, rnode, forknum, blkno,
-											   mode, NULL);
+											   mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
}
}

Not this patches fault, but it strikes me as a bad idea to just hardcode
RELPERSISTENCE_PERMANENT. E.g. it can totally make sense to WAL log some
records for an unlogged table, e.g. to create the init fork.

+/*
+ * Main control structure for undo log management in shared memory.
+ * UndoLogSlot objects are arranged in a fixed-size array, with no particular
+ * ordering.
+ */
+typedef struct UndoLogSharedData
+{
+	UndoLogNumber	free_lists[UndoPersistenceLevels];
+	UndoLogNumber	low_logno;
+	UndoLogNumber	next_logno;
+	UndoLogNumber	nslots;
+	UndoLogSlot		slots[FLEXIBLE_ARRAY_MEMBER];
+} UndoLogSharedData;

Would be good to document at least low_logno - at least to me it's not
obvious what that means by name. Also some higher level comments about
what the shared memory layout is wouldn't hurt.

+/*
+ * How many undo logs can be active at a time?  This creates a theoretical
+ * maximum amount of undo data that can exist, but if we set it to a multiple
+ * of the maximum number of backends it will be a very high limit.
+ * Alternative designs involving demand paging or dynamic shared memory could
+ * remove this limit but would be complicated.
+ */
+static inline size_t
+UndoLogNumSlots(void)
+{
+	return MaxBackends * 4;
+}

I'd put this factor in a macro (or named integer constant
variable). It's a) nice to have all such numbers defined in one place b)
it makes it easier to understand where the four comes from.

+/*
+ * Initialize the undo log subsystem.  Called in each backend.
+ */
+void
+UndoLogShmemInit(void)
+{
+	bool found;
+
+	UndoLogShared = (UndoLogSharedData *)
+		ShmemInitStruct("UndoLogShared", UndoLogShmemSize(), &found);
+
+	/* The postmaster initialized the shared memory state. */
+	if (!IsUnderPostmaster)
+	{
+		int		i;
+
+		Assert(!found);

I don't quite understand putting this under IsUnderPostmaster, rather
than found (and then potentially having an IsUnderPostmaster assert). I
know that a few other places do it this way too.

+/*
+ * Iterate through the set of currently active logs.  Pass in NULL to get the
+ * first undo log.

Not a fan of APIs like this. Harder to understand at callsites.

NULL indicates the end of the set of logs.

+ "A return value of"? Right now this sounds a bit like it's referencing
the NULL argument.

The caller
+ * must lock the returned log before accessing its members, and must skip if
+ * logno is not valid.
+ */
+UndoLogSlot *
+UndoLogNextSlot(UndoLogSlot *slot)
+{
+/*
+ * Create a new empty segment file on disk for the byte starting at 'end'.
+ */
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+							UndoLogOffset end)
+{
+	struct stat	stat_buffer;
+	off_t	size;
+	char	path[MAXPGPATH];
+	void   *zeroes;
+	size_t	nzeroes = 8192;
+	int		fd;
+
+	UndoLogSegmentPath(logno, end / UndoLogSegmentSize, tablespace, path);
+
+	/*
+	 * Create and fully allocate a new file.  If we crashed and recovered
+	 * then the file might already exist, so use flags that tolerate that.
+	 * It's also possible that it exists but is too short, in which case
+	 * we'll write the rest.  We don't really care what's in the file, we
+	 * just want to make sure that the filesystem has allocated physical
+	 * blocks for it, so that non-COW filesystems will report ENOSPC now
+	 * rather than later when the space is needed and we'll avoid creating
+	 * files with holes.
+	 */
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);

As I said somewhere nearby, I think it might make sense to optionally
first fallocate, and then zero.

Is there agood reason to not just use O_TRUNC here, and enter the
zeroing path without a stat? We could potentially end up with holes this
way, I think (if the writes didn't make it to disk, but the metadata
operation did). Also seems better to just start from a consistently
zeroed out block, rather than sometimes having old data in there.

+	/*
+	 * If we're not in recovery, we need to WAL-log the creation of the new
+	 * file(s).  We do that after the above filesystem modifications, in
+	 * violation of the data-before-WAL rule as exempted by
+	 * src/backend/access/transam/README.  This means that it's possible for
+	 * us to crash having made some or all of the filesystem changes but
+	 * before WAL logging, but in that case we'll eventually try to create the
+	 * same segment(s) again, which is tolerated.
+	 */

Perhaps explain *why* the rule is violated here?

+/*
+ * Advance the insertion pointer in this context by 'size' usable (non-header)
+ * bytes.  This is the next place we'll try to allocate a record, if it fits.
+ * This is not committed to shared memory until after we've WAL-logged the
+ * record and UndoLogAdvanceFinal() is called.
+ */
+void
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+	context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+														 size);
+}
+
+/*
+ * Advance the insertion pointer to 'size' usable (non-header) bytes past
+ * insertion_point.
+ */
+void
+UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size)

Think this should differentiate from UndoLogAdvance().

+	/*
+	 * We acquire UndoLogLock to prevent any undo logs from being created or
+	 * discarded while we build a snapshot of them.  This isn't expected to
+	 * take long on a healthy system because the number of active logs should
+	 * be around the number of backends.  Holding this lock won't prevent
+	 * concurrent access to the undo log, except when segments need to be
+	 * added or removed.
+	 */
+	LWLockAcquire(UndoLogLock, LW_SHARED);

s/the undo log/undo logs/?

+	/* Dump into a file under pg_undo. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE);
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not create file \"%s\": %m", path)));
+
+	/* Compute header checksum. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the number of active logs + crc. */
+	if ((write(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno)) ||
+		(write(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno)) != sizeof(UndoLogShared->next_logno)) ||
+		(write(fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));

I'd prefix it with some magic value. It provides a way to do version
bumps if really necessary (or just provide an explicit version), makes
it easier to distinguish proper checksum failures from zeroed out files,
and helps identify the files after FS corruption.

+	/* Write out the meta data for all active undo logs. */
+	data = (char *) serialized;
+	INIT_CRC32C(crc);
+	serialized_size = num_logs * sizeof(UndoLogMetaData);
+	while (serialized_size > 0)
+	{
+		ssize_t written;
+
+		written = write(fd, data, serialized_size);
+		if (written < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write to file \"%s\": %m", path)));
+		COMP_CRC32C(crc, data, written);
+		serialized_size -= written;
+		data += written;
+	}
+	FIN_CRC32C(crc);
+
+	if (write(fd, &crc, sizeof(crc)) != sizeof(crc))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+

The number of small writes here makes me wonder if this shouldn't either
use fopen/write or a manual buffer.

+	/* Flush file and directory entry. */
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC);
+	pg_fsync(fd);
+	if (CloseTransientFile(fd) < 0)
+		ereport(data_sync_elevel(ERROR),
+				(errcode_for_file_access(),
+				 errmsg("could not close file \"%s\": %m", path)));
+	fsync_fname("pg_undo", true);
+	pgstat_report_wait_end();

Is there a risk in crashing during this, and leaving an incomplete file
in place? Presumably not, because the checkpoint wouldn't exist?

+/*
+ * Find the UndoLogSlot object for a given log number.
+ *
+ * The caller may or may not already hold UndoLogLock, and should indicate
+ * this by passing 'locked'.  We'll acquire it in the slow path if necessary.
+ * If it is not held by the caller, the caller must deal with the possibility
+ * that the returned UndoLogSlot no longer contains the requested logno by the
+ * time it is accessed.
+ *
+ * To do that, one of the following approaches must be taken by the calling
+ * code:
+ *
+ * 1.  If the calling code knows that it is attached to this lock or is the

*this "log", not "lock", right?

+static void
+attach_undo_log(UndoPersistence persistence, Oid tablespace)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber logno;
+	UndoLogNumber *place;
+
+	Assert(!InRecovery);
+	Assert(CurrentSession->attached_undo_slots[persistence] == NULL);
+
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/*
+	 * For now we have a simple linked list of unattached undo logs for each
+	 * persistence level.  We'll grovel though it to find something for the
+	 * tablespace you asked for.  If you're not using multiple tablespaces
+	 * it'll be able to pop one off the front.  We might need a hash table
+	 * keyed by tablespace if this simple scheme turns out to be too slow when
+	 * using many tablespaces and many undo logs, but that seems like an
+	 * unusual use case not worth optimizing for.
+	 */
+	place = &UndoLogShared->free_lists[persistence];
+	while (*place != InvalidUndoLogNumber)
+	{
+		UndoLogSlot *candidate = find_undo_log_slot(*place, true);
+
+		/*
+		 * There should never be an undo log on the freelist that has been
+		 * entirely discarded, or hasn't been created yet.  The persistence
+		 * level should match the freelist.
+		 */
+		if (unlikely(candidate == NULL))
+			elog(ERROR,
+				 "corrupted undo log freelist, no such undo log %u", *place);
+		if (unlikely(candidate->meta.persistence != persistence))
+			elog(ERROR,
+				 "corrupted undo log freelist, undo log %u with persistence %d found on freelist %d",
+				 *place, candidate->meta.persistence, persistence);
+
+		if (candidate->meta.tablespace == tablespace)
+		{
+			logno = *place;
+			slot = candidate;
+			*place = candidate->next_free;
+			break;
+		}
+		place = &candidate->next_free;
+	}

I'd replace the linked list with ilist.h ones.

< more tomorrow >

Greetings,

Andres Freund

#172Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#170)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, Jul 28, 2019 at 9:38 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Interesting. One way to bring back posix_fallocate() without
upsetting people on some filesystem out there would be to turn the new
wal_init_zero GUC into a choice: write (current default, and current
behaviour for 'on'), pwrite_hole (write just the final byte, current
behaviour for 'off'), posix_fallocate (like that 2013 patch that was
reverted) and posix_fallocate_and_write (do both as you said, to try
to solve that problem you mentioned that led to the revert).

I suppose there'd be a parallel GUC undo_init_zero. Or some more
general GUC for any fixed-sized preallocated files like that (for
example if someone were to decide to do the same for SLRU files
instead of growing them block-by-block), called something like
file_init_zero.

I think it's pretty sane to have a GUC for how we extend files, but to
me it seems like overkill to have one for every separate kind of file.
It's not theoretically impossible that you could have the data and WAL
on separate partitions on separate mount points with, consequently,
separate needs, and the data (including undo) could be split among
multiple tablespaces each of which uses a different filesystem.
Probably, the right design would be a per-tablespace storage option
plus an overall default that is always used for WAL. However, that
strikes me as a lot of complexity for a pretty marginal use case: most
people have a favorite filesystem and stick with it.

And all of that seems like something a bit separate from coming up
with a good undo framework. Why doesn't undo just do this like we do
it elsewhere, and leave the question of changing the way we do
extend-and-zero for another thread?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#173Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#135)
Re: should there be a hard-limit on the number of transactions pending undo?

On Fri, Jul 19, 2019 at 7:28 PM Peter Geoghegan <pg@bowt.ie> wrote:

If I'm not mistaken, you're tacitly assuming that you'll always be
using zheap, or something sufficiently similar to zheap. It'll
probably never be possible to UNDO changes to something like a GIN
index on a zheap table, because you can never do that with sensible
concurrency/deadlock behavior.

I mean, essentially any well-designed framework intended for any sort
of task whatsoever is going to have a design center where one can
foresee that it will work well, and then as a result of working well
for the thing for which it was designed, it will also work well for
other things that are sufficiently similar. So, I think you're
correct, but I also don't think that's really saying very much. The
trick is to figure out whether and how the ideas you have could be
generalized with reasonable effort to handle other cases, and that's
easier with some projects than others. I think when it comes to UNDO,
it's actually really hard. The system has some assumptions built into
it which are probably required for good performance and reasonable
complexity, and it's probably got other assumptions in it which are
unnecessary and could be eliminated if we only realized that we were
making those assumptions in the first place. The more involvement we
get from people who aren't coming at this from the point of view of
zheap, the more likely it is that we'll be able to find those
assumptions and wipe them out before they get set in concrete.
Unfortunately, we haven't had many takers so far -- thanks for chiming
in.

I don't really understand your comments about GIN. My simplistic
understanding of GIN is that it's not very different from btree in
this regard. Suppose we insert a row, and then the insert aborts;
suppose also that the index wants to use UNDO. In the case of a btree
index, we're going to go insert an index entry for the new row; upon
abort, we should undo the index insertion by removing that index tuple
or at least marking it dead. Unless a page split has happened,
triggered either by the insertion itself or by subsequent activity,
this puts the index in a state that is almost perfectly equivalent to
where we were before the now-aborted transaction did any work. If a
page split has occurred, trying to undo the index insertion is going
to run into two problems. One, we probably can't undo the page split,
so the index will be logically equivalent but not physically
equivalent after we get rid of the new tuple. Two, if the page split
happened after the insertion of the new tuple rather than at the same
time, the index tuple may not be on the page where we left it.
Possibly we can walk right (or left, or sideways, or diagonally at a
35 degree angle, my index-fu is not great here) and be sure of finding
it, assuming the index is not corrupt.

Now, my mental model of a GIN index is that you go find N>=0 index
keys inside each value and do basically the same thing as you would
for a btree index for each one of them. Therefore it seems to me,
possibly stupidly, that you're going to have basically the same
problems, except each problem will now potentially happen up to N
times instead of up to 1 time. I assume here that in either case -
GIN or btree - you would tentatively record where you left the tuple
that now needs to be zapped and that you can jump to that place
directly to try to zap it. Possibly those assumptions are bad and
maybe that's where you're seeing a concurrency/deadlock problem; if
so, a more detailed explanation would be very helpful.

To me, based on my more limited knowledge of indexing, I'm not really
seeing a concurrency/deadlock issue, but I do see that there's going
to be a horrid efficiency problem if page splits are common. Suppose
for example that you bulk-load a bunch of rows into an indexed table
in descending order according to the indexed column, with all the new
values being larger than any existing values in that column. The
insertion point basically doesn't change: you're always inserting
after what was the original high value in the column, and that point
is always on the same page, but that page is going to be repeatedly
split, so that, at the end of the load, almost none of the
newly-inserted rows are going to be on the page into which they were
originally inserted. Now if you abort, you're going to either have to
walk right a long way from the original insertion point to find each
tuple, or re-find each tuple by traversing from the root of the tree
instead of remembering where you left it. Doing the first for N tuples
is O(N^2), and doing the second is O(N*H) where H is the height of the
btree. The latter is almost like O(N) given the high fanout of a
btree, but with a much higher constant factor than the
remember-where-you-put-it strategy would be in cases where no splits
have occurred. Neither seems very good. This seems to be a very
general problem with making undo and indexes work nicely together:
almost any index type has to sometimes move tuple around to different
pages, which makes finding them a lot more expensive than re-finding a
heap tuple.

I think that most of the above is a bit of a diversion from the
original topic of the thread. I think I see the connection you're
making between the two topics: the more likely undo application is to
fail, the more worrying a hard limit is, and deadlocks are a way for
undo application to fail, and if that's likely to be common when undo
is applied to indexes, then undo failure will be common and a hard
limit is bad. However, I think the solution to that problem is
page-at-a-time undo: if foreground process needs to modify a page with
pending undo, and if the modification it wants to make can't be done
sensibly unless the undo is applied first, it should be prepared to
apply that undo itself - just for that page - rather than wait for
somebody else to get it done. That's important not only for deadlock
avoidance - though deadlock avoidance is certainly a legitimate
concern - but also because the change might be part of some gigantic
rollback that's going to take an hour, and waiting for the undo to hit
all the other pages before it gets to this one will make users very
sad. Assuming page-at-a-time undo is possible for all undo-using AMs,
which I believe to be more or less a requirement if you want to have
something production-grade, I don't really see what common deadlock
scenario could exist. Either we're talking about LWLocks -- in which
case we've got a bug in the code -- or we're talking about heavyweight
locks -- in which case we're dealing with a rare scenario where undo
work is piling up behind strategically-acquired AELs.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

In reply to: Thomas Munro (#144)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 22, 2019 at 4:15 AM Thomas Munro <thomas.munro@gmail.com> wrote:

I had a similar thought: you might regret that choice if you were
wanting to implement an AM with lock table-based concurrency control
(meaning that there are lock ordering concerns for row and page locks,
for DML statements, not just DDL). That seemed a bit too far fetched
to mention before, but are you saying the same sort of concerns might
come up with indexes that support true undo (as opposed to indexes
that still need VACUUM)?

Yes. It doesn't really make any difference with B-Trees, because the
locks there are very similar to row locks (you still need forwarding
UNDO metadata in index pages, probably for checking the visibility of
index tuples that have their ghost bit set). But when you need to undo
changes to an indexes with coarse grained index tuples (e.g. in a GIN
index), the transaction needs to roll back the index tuple as a whole,
necessitating that locks be held. Heap TIDs need to be completely
stable to avoid a VACUUM-like mechanism -- you cannot just create a
new HOT chain. You even have to be willing to store a single heap row
across two heap pages in extreme cases where an UPDATE makes it
impossible to fit a new row on the same heap page as the original --
this is called row forwarding.

Once heap TIDs are guaranteed to be associated with a logical row for
the lifetime of that row, and once you lock index entries, you're
always able to cleanly undo the changes in the index (i.e. remove new
tuples on abort). Then you have indexes that don't need VACUUMING, and
that have cheap index-only scans.

For comparison, ARIES[1] has no-deadlock rollbacks as a basic property
and reacquires locks during restart before new transactions are allow
to execute. In its model, the locks in question can be on things like
rows and pages. We don't even use our lock table for those (except
for non-blocking SIREAD locks, irrelevant here).

Right. ARIES has plenty to say about concurrency control, even though
we often think of it as something that is only concerned with crash
recovery. The undo phase is tied to how concurrency control works in
general in ARIES. There is something called ARIES/KVL, and something
else called ARIES/IM [1]https://15721.courses.cs.cmu.edu/spring2016/papers/a16-graefe.pdf.

After crash
recovery, if zheap encounters a row with pending rollback from an
aborted transaction, as usual it either needs to read an older version
from an undo log (for reads) or help execute the rollback before
updating (for writes). That only requires page-at-a-time LWLocks
("latching"), so it's deadlock-free. The only deadlock risk comes
from the need to acquire heavyweight locks on relations which
typically only conflict when you run DDL, so yeah, it's tempting to
worry a lot less about those than the fine grained lock traffic from
DML statements that DB2 and others have to deal with.

I think that DB2 index deletes are synchronous, and immediately remove
space from a leaf page. Rollbacks will re-insert the deleted tuple.
Systems that use a limited form of MVCC based on 2PL [2]http://db.cs.berkeley.edu/papers/fntdb07-architecture.pdf -- See "6.7 Standard Practice" -- Peter Geoghegan set a ghost
bit instead of physically removing the tuple immediately. But I don't
think that that's actually very different to the DB2 classic 2PL
approach, since there is forwarding undo information that makes it
possible to reclaim tuples with the ghost bit set at the earliest
possible opportunity. And because you can immediately do an in-place
update of an index tuple's heap TID in the case of unique indexes,
which can be optimized as a special case. Queries like "UPDATE tab set
tab_pk = tab_pk + 1" work per the SQL standard (no duplicate
violation), and don't even bloat the index, because the changes in the
index can happen almost entirely in-place.

I might as well put the quote marks on now: "Perhaps we could
implement A later."

I don't claim to have any real answers here. I don't claim to
understand how much of a problem this is.

[1]: https://15721.courses.cs.cmu.edu/spring2016/papers/a16-graefe.pdf
[2]: http://db.cs.berkeley.edu/papers/fntdb07-architecture.pdf -- See "6.7 Standard Practice" -- Peter Geoghegan
"6.7 Standard Practice"
--
Peter Geoghegan

#175Robert Haas
robertmhaas@gmail.com
In reply to: Amit Khandekar (#149)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 23, 2019 at 10:42 AM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

I think, even though there might not be a correctness issue with the
current code as it stands, we should still use a local variable.
Updating MyUndoWorker is a big side-effect, which the caller is not
supposed to be aware of, because all that function should do is just
get the slot info.

Absolutely right. It's just routine good practice to avoid using
global variables when there is no compelling reason to do otherwise.
The reason you state here is one of several good ones.

Yes, I also think that the function would error out only because of
can't-happen cases, like "too many locks taken" or "out of binary heap
slots" or "out of memory" (this last one is not such a can't happen
case). These cases happen probably due to some bugs, I suppose. But I
was wondering : Generally when the code errors out with such
can't-happen elog() calls, worst thing that happens is that the
transaction gets aborted. Whereas, in this case, the worst thing that
could happen is : the undo action would never get executed, which
means selects for this tuple will keep on accessing the undo log ?
This does not sound like any data consistency issue, so we should be
fine after all ?

I don't think so. Every XID present in undo has to be something we
can look up in CLOG to figure out which transactions are aborted and
which transactions are committed, so that we know which transactions
need undo. If we forget to undo the transaction, we can't discard it,
which means we can't advance the CLOG transaction horizon, which means
we'll eventually start failing to assign XIDs, leading to a refusal of
all write transactions. Oops.

More generally, it's not OK for the generic undo layer to make
assumptions about whether the operations performed by the undo
handlers are essential or not. We don't want to impose a design
constraint the undo can only be used for things that are not actually
critical, because that will make it hard to write AMs that use it.
And there's no reason to live with such a design constraint anyway,
because, as noted above, CLOG truncation requires it.

More generally still, some can't-happen situations should be checked
via Assert() and others via elog(). For example, consider some code
that looks up a syscache tuple and pulls data from the returned tuple.
If the code that handles DDL is written in such a way that the tuple
should always exist, then this is a can't-happen situation, but
generally the code checks this via elog(), not Assert(), because it
could also happen due to the catalog contents being corrupted. If
Assert() were used, the checks would not run in production builds, and
a corrupt catalog would lead to a seg fault. An elog() is much
friendlier. As a general principle, when a certain thing ought to
always be true, but it being true depends on a whole lot of
assumptions elsewhere in the code, and especially if it also depends
on assumptions like "the database is not corrupted," I think elog() is
preferable. Assert() is better for things that are more localized and
that really can't go wrong for any reason other than a bug. In this
case, I think I would tend towards elog(PANIC), but it's arguable.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#176Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#174)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 2:24 PM Peter Geoghegan <pg@bowt.ie> wrote:

Yes. It doesn't really make any difference with B-Trees, because the
locks there are very similar to row locks (you still need forwarding
UNDO metadata in index pages, probably for checking the visibility of
index tuples that have their ghost bit set). But when you need to undo
changes to an indexes with coarse grained index tuples (e.g. in a GIN
index), the transaction needs to roll back the index tuple as a whole,
necessitating that locks be held. Heap TIDs need to be completely
stable to avoid a VACUUM-like mechanism -- you cannot just create a
new HOT chain. You even have to be willing to store a single heap row
across two heap pages in extreme cases where an UPDATE makes it
impossible to fit a new row on the same heap page as the original --
this is called row forwarding.

I find this hard to believe, because an UPDATE can always be broken up
into a DELETE and an INSERT. If that were to be done, you would not
have a stable heap TID and you would have a "new HOT chain," or your
AM's equivalent of that concept. So if we can't handle an UPDATE that
changes the TID, then we also can't handle a DELETE + INSERT. But
surely handling that case is a hard requirement for any AM.

Sorry if I'm being dense here, but I feel like you're making some
assumptions that I'm not quite following.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

In reply to: Robert Haas (#173)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 9:35 AM Robert Haas <robertmhaas@gmail.com> wrote:

I mean, essentially any well-designed framework intended for any sort
of task whatsoever is going to have a design center where one can
foresee that it will work well, and then as a result of working well
for the thing for which it was designed, it will also work well for
other things that are sufficiently similar. So, I think you're
correct, but I also don't think that's really saying very much.

I agree that it's quite unclear how important this is. I don't
necessarily think it matters if zheap doesn't do that well with GIN
indexes. I think it's probably going to be useful to imagine how GIN
indexing might work for zheap because it clarifies the strengths and
weaknesses of your design. It's perfectly fine for there to be
weaknesses, provided that they are well understood.

The
trick is to figure out whether and how the ideas you have could be
generalized with reasonable effort to handle other cases, and that's
easier with some projects than others. I think when it comes to UNDO,
it's actually really hard.

I agree.

Unfortunately, we haven't had many takers so far -- thanks for chiming
in.

I don't have the ability to express my general concerns here in a very
crisp way. This is complicated stuff. Thanks for tolerating the
hand-wavy nature of my feedback about this.

I don't really understand your comments about GIN. My simplistic
understanding of GIN is that it's not very different from btree in
this regard.

GIN is quite similar to btree from a Postgres point of view -- GIN is
simply a btree that is good at storing duplicates (and has higher
level infrastructure to make things like FTS work). So I'd say that
your understanding is fairly complete, at least as far as traditional
Postgres goes. But if we imagine a system in which we have to roll
back in indexes, it's quite a different story. See my remarks to
Thomas just now about that.

Suppose we insert a row, and then the insert aborts;
suppose also that the index wants to use UNDO. In the case of a btree
index, we're going to go insert an index entry for the new row; upon
abort, we should undo the index insertion by removing that index tuple
or at least marking it dead. Unless a page split has happened,
triggered either by the insertion itself or by subsequent activity,
this puts the index in a state that is almost perfectly equivalent to
where we were before the now-aborted transaction did any work. If a
page split has occurred, trying to undo the index insertion is going
to run into two problems. One, we probably can't undo the page split,
so the index will be logically equivalent but not physically
equivalent after we get rid of the new tuple. Two, if the page split
happened after the insertion of the new tuple rather than at the same
time, the index tuple may not be on the page where we left it.

Actually, page splits are the archetypal case where undo cannot
restore the original physical state. In general, we cannot expect the
undo process to reverse page splits. Undo might be able to merge the
pages together, but it also might not be able to. It won't be terribly
different to the situation with deletes where the transaction commits,
most likely.

Some other systems have something called "system transactions" for
things like page splits. They don't need to have their commit record
flushed synchronously, and occur in the foreground of the xact that
needs to split the page. That way, rollback doesn't have to concern
itself with rolling back things that are pretty much impossible to
roll back, like page splits.

Now, my mental model of a GIN index is that you go find N>=0 index
keys inside each value and do basically the same thing as you would
for a btree index for each one of them. Therefore it seems to me,
possibly stupidly, that you're going to have basically the same
problems, except each problem will now potentially happen up to N
times instead of up to 1 time. I assume here that in either case -
GIN or btree - you would tentatively record where you left the tuple
that now needs to be zapped and that you can jump to that place
directly to try to zap it. Possibly those assumptions are bad and
maybe that's where you're seeing a concurrency/deadlock problem; if
so, a more detailed explanation would be very helpful.

Imagine a world in which zheap cannot just create a new TID (or HOT
chain) for the same logical tuple, which is something that I believe
should be an important goal for zheap (again, see my remarks to
Thomas). Simplicity for rollbacks in access methods like GIN demands
that you lock the entire index tuple, which may point to hundreds of
logical rows (or TIDs, since they have a 1:1 correspondence with
logical rows in this imaginary world). Rolling back with more granular
locking seems very hard for the same reason that rolling back a page
split would be very hard -- you cannot possibly have enough book
keeping information to make that work in a sane way in the face of
concurrent insertions that may also commit or abort unpredictably. It
seems necessary to bake concurrency control in to roll back at the
index access method level in order to get significant benefits from a
design like zheap.

Now, maybe zheap should be permitted to not work particularly well
with GIN, while teaching btree to take advantage of the common case
where we can roll everything back, even in indexes (so zheap behaves
much more like heapam when you have a GIN index, which is hopefully
not that common). That could be a perfectly reasonable restriction.
But ISTM that you need to make heap TIDs completely stable for the
case that zheap is expected to excel at. You also need to teach nbtree
to take advantage of this by rolling back if and when it's safe to do
so (when we know that heap TIDs are stable for the indexed table).

In general, the only way that rolling back changes to indexes can work
is by making heap TIDs completely stable. Any design for rollback in
nbtree that allows there to be multiple entries for the same logical
row in the index seems like a disaster to me. Are you really going to
put forwarding information in the index that mirrors what has happened
in the table?

To me, based on my more limited knowledge of indexing, I'm not really
seeing a concurrency/deadlock issue, but I do see that there's going
to be a horrid efficiency problem if page splits are common.

I'm not worried about rolling back page splits. That seems to present
us with exactly the same issues as rolling back in GIN indexes
reliably (i.e. problems that are practically impossible to solve, or
at least don't seem worth solving).

This seems to be a very
general problem with making undo and indexes work nicely together:
almost any index type has to sometimes move tuple around to different
pages, which makes finding them a lot more expensive than re-finding a
heap tuple.

Right. That's why undo is totally logical in indexes. And it's why you
cannot expect to roll back page splits.

I think that most of the above is a bit of a diversion from the
original topic of the thread. I think I see the connection you're
making between the two topics: the more likely undo application is to
fail, the more worrying a hard limit is, and deadlocks are a way for
undo application to fail, and if that's likely to be common when undo
is applied to indexes, then undo failure will be common and a hard
limit is bad.

This is an awkward thing to discuss, because it involves so many
interrelated moving parts. And because I know that I could easily miss
quite a bit about the zheap design. Forgive me if I've hijacked the
thread.

However, I think the solution to that problem is
page-at-a-time undo: if foreground process needs to modify a page with
pending undo, and if the modification it wants to make can't be done
sensibly unless the undo is applied first, it should be prepared to
apply that undo itself - just for that page - rather than wait for
somebody else to get it done. That's important not only for deadlock
avoidance - though deadlock avoidance is certainly a legitimate
concern - but also because the change might be part of some gigantic
rollback that's going to take an hour, and waiting for the undo to hit
all the other pages before it gets to this one will make users very
sad.

It's something that users in certain other systems (though certainly
not all other systems) have had to live with for some time.

SQL Server 2019 has something called "instantaneous transaction
rollback", which seems to make SQL Server optionally behave a lot more
like Postgres [1]https://www.microsoft.com/en-us/research/uploads/prod/2019/06/p700-antonopoulos.pdf -- Peter Geoghegan, apparently with many of the same disadvantages as
Postgres. I agree that there is probably a middle way that more or
less has the advantages of both approaches. I don't really know what
that should look like, though.

[1]: https://www.microsoft.com/en-us/research/uploads/prod/2019/06/p700-antonopoulos.pdf -- Peter Geoghegan
--
Peter Geoghegan

In reply to: Robert Haas (#176)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 12:11 PM Robert Haas <robertmhaas@gmail.com> wrote:

I find this hard to believe, because an UPDATE can always be broken up
into a DELETE and an INSERT. If that were to be done, you would not
have a stable heap TID and you would have a "new HOT chain," or your
AM's equivalent of that concept. So if we can't handle an UPDATE that
changes the TID, then we also can't handle a DELETE + INSERT. But
surely handling that case is a hard requirement for any AM.

I'm not saying you can't handle it. But that necessitates "write
amplification", in the sense that you must now create new index tuples
even for indexes where the indexed columns were not logically altered.
Isn't zheap supposed to fix that problem, at least at in version 2 or
version 3? I also think that stable heap TIDs make index-only scans a
lot easier and more effective.

I think that indexes (or at least B-Tree indexes) will ideally almost
always have tuples that are the latest versions with zheap. The
exception is tuples whose ghost bit is set, whose visibility varies
based on the MVCC snapshot in use. But the instant that the
deleting/updating xact commits it becomes legal to recycle the old
heap TID. We don't need to go back to the index to permanently zap the
tuple whose ghost bit we already set, because there is an undo pointer
in the same leaf page, so nobody is in danger of getting confused and
following the now-recycled heap TID.

This ghost bit design owes plenty to 2PL (which will fully remove the
index tuple synchronously, rather than just setting a ghost bit). You
could say that it's a 2PL/MVCC hybrid, while classic Postgres is
"pure" MVCC because it uses explicit row versioning -- it doesn't need
to impose restrictions on TID stability. Which seems to be why we
offer such a large variety of index access methods -- it's relatively
straight forward for Postgres to add niche index AMs, such as SP-GiST.

--
Peter Geoghegan

In reply to: Peter Geoghegan (#178)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 12:39 PM Peter Geoghegan <pg@bowt.ie> wrote:

I think that indexes (or at least B-Tree indexes) will ideally almost
always have tuples that are the latest versions with zheap. The
exception is tuples whose ghost bit is set, whose visibility varies
based on the MVCC snapshot in use. But the instant that the
deleting/updating xact commits it becomes legal to recycle the old
heap TID.

Sorry, I meant the instant the ghost bit index tuple cannot be visible
to any possible MVCC snapshot. Which, in general, will be pretty soon
after the deleting/updating xact commits.

--
Peter Geoghegan

#180Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#178)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 3:39 PM Peter Geoghegan <pg@bowt.ie> wrote:

I'm not saying you can't handle it. But that necessitates "write
amplification", in the sense that you must now create new index tuples
even for indexes where the indexed columns were not logically altered.
Isn't zheap supposed to fix that problem, at least at in version 2 or
version 3? I also think that stable heap TIDs make index-only scans a
lot easier and more effective.

I think there's a cost-benefit analysis here. You're completely
correct that inserting new index tuples causes write amplification
and, yeah, that's bad. On the other hand, row forwarding has its own
costs. If a row ends up persistently moved to someplace else, then
every subsequent access to that row has an extra level of indirection.
If it ends up split between two places, every read of that row incurs
two reads. The "someplace else" where moved rows or ends of split rows
are stored has to be skipped by sequential scans, which is complex and
possibly inefficient if it breaks up a sequential I/O pattern. Those
things are bad, too.

It's a little difficult to compare the kinds of badness. My thought
is that in the short run, the redirect strategy probably wins, because
there could be and likely are a bunch of indexes and it's cheaper to
just insert one redirect. But in the long term, the redirect thing
seems like a loser, because you have to keep following it. That
(perhaps naive) analysis is why zheap doesn't try to maintain TID
stability. Instead it wants to do in-place updates (no new TID) as
often as possible, but the fallback strategy is simply to do a
non-in-place update (new TID) rather than a redirect.

I think that indexes (or at least B-Tree indexes) will ideally almost
always have tuples that are the latest versions with zheap. The
exception is tuples whose ghost bit is set, whose visibility varies
based on the MVCC snapshot in use. But the instant that the
deleting/updating xact commits it becomes legal to recycle the old
heap TID. We don't need to go back to the index to permanently zap the
tuple whose ghost bit we already set, because there is an undo pointer
in the same leaf page, so nobody is in danger of getting confused and
following the now-recycled heap TID.

I haven't run across the "ghost bit" terminology before. Is there a
good place to read about the technique you're assuming here? A major
question is how you handle inserted rows, that are new now and thus
not yet visible to everyone, but which will later become all-visible.
One idea is: if the undo pointer is new enough that a write
transaction which modified the page could still be in-flight, check
the undo log to ascertain visibility of index tuples. If not, then
any potentially-deleted index tuples are in fact deleted, and any
others are all-visible. With this design, you don't set the ghost bit
on new tuples, but are still able to stop following the undo pointers
for them after a while.

To put that another way, there seems to be pretty clearly a need for a
bit, but what does the bit mean? It could mean "please check the undo
log," in which case it'd have to be set on insert, eventually cleared,
and then reset on delete, but I think that's likely to suck. I think
therefore that the bit should mean
is-deleted-but-not-necessarily-all-visible-yet, which avoids that
problem.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

In reply to: Robert Haas (#180)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 1:04 PM Robert Haas <robertmhaas@gmail.com> wrote:

I think there's a cost-benefit analysis here. You're completely
correct that inserting new index tuples causes write amplification
and, yeah, that's bad. On the other hand, row forwarding has its own
costs. If a row ends up persistently moved to someplace else, then
every subsequent access to that row has an extra level of indirection.

The devil is in the details. It doesn't seem that optimistic to assume
that a good implementation could practically always avoid it, by being
clever about heap fillfactor. It can work a bit like external TOAST
pointers. The oversized datums can go on the other heap page, which
presumably not be in the SELECT list of most queries. It won't be one
of the indexed columns in typical cases, so index scans will generally
only have to visit one heap page.

It occurs to me that the zheap design is still sensitive to heap
fillfactor in much the same way as it would be with reliably-stable
TIDs, combined with some amount of row forwarding. It's not essential
for correctness that you avoid creating a new HOT chain (or whatever
it's called in zheap) with new index tuples, but it is still quite
preferable on performance grounds. It's still worth going to a lot of
work to avoid having that happen, such as using external TOAST
pointers with some of the larger datums on the existing heap page.

If it ends up split between two places, every read of that row incurs
two reads. The "someplace else" where moved rows or ends of split rows
are stored has to be skipped by sequential scans, which is complex and
possibly inefficient if it breaks up a sequential I/O pattern. Those
things are bad, too.

It's a little difficult to compare the kinds of badness.

I would say that it's extremely difficult. I'm not going to speculate
about how the two approaches might compare today.

I haven't run across the "ghost bit" terminology before. Is there a
good place to read about the technique you're assuming here?

"5.2 Key Range Locking and Ghost Records" from "A Survey of B-Tree
Locking Techniques" seems like a good place to start. As I said
earlier, the paper is available from:
https://15721.courses.cs.cmu.edu/spring2016/papers/a16-graefe.pdf

This description won't define the term ghost record/bit in a precise
way that you can just adopt, since the details will vary somewhat
based on considerations like whether or not MVCC is used. But you'll
get the general idea from the paper, I think.

To put that another way, there seems to be pretty clearly a need for a
bit, but what does the bit mean? It could mean "please check the undo
log," in which case it'd have to be set on insert, eventually cleared,
and then reset on delete, but I think that's likely to suck. I think
therefore that the bit should mean
is-deleted-but-not-necessarily-all-visible-yet, which avoids that
problem.

That sounds about right to me.

--
Peter Geoghegan

#182Thomas Munro
thomas.munro@gmail.com
In reply to: Peter Geoghegan (#177)
Re: should there be a hard-limit on the number of transactions pending undo?

On Tue, Jul 30, 2019 at 7:12 AM Peter Geoghegan <pg@bowt.ie> wrote:

SQL Server 2019 has something called "instantaneous transaction
rollback", which seems to make SQL Server optionally behave a lot more
like Postgres [1], apparently with many of the same disadvantages as
Postgres. I agree that there is probably a middle way that more or
less has the advantages of both approaches. I don't really know what
that should look like, though.

[1] https://www.microsoft.com/en-us/research/uploads/prod/2019/06/p700-antonopoulos.pdf

Thanks for sharing that. I see they're giving that paper at VLDB next
month in LA... I hope the talk video will be published on the web.
While we've been working on a hybrid vaccum/undo design, they've built
a hybrid undo/vacuum system. I've only skimmed this, but one of their
concerns that caught my eye is log volume in the presence of long
running transactions ("3.6 Aggressive Log Truncation"). IIUC they
have only a single log for both redo and undo, so a long running
transaction requires them to keep all log data around as long as it
might be needed for that transaction, in traditional SQL Server.
That's basically the flip side of the problem we're trying to solve,
in-heap bloat. I think we might have a different solution to that
problem, with our finer grained undo logs. Our undo data is not mixed
in with redo data (though redo can recreated it, it's not needed after
that), and we have multiple undo logs with their own discard pointers,
so a long running transaction only prevents only one single undo log
from being truncated, while other undo logs holding other transactions
can be truncated as soon as those transactions are committed/rolled
back and are either all visible (admittedly tracked with a system-wide
xmin approach for now, but could probably be made more granular) or a
snapshot-too-old threshold is reached (not implemented yet).

--
Thomas Munro
https://enterprisedb.com

#183Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#171)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

I realize that this might not be the absolutely newest version of the
undo storage part of this patchset - but I'm trying to understand the
whole context, and that's hard without reading through the whole stack
in a situation where the layers actually fit together

On 2019-07-29 01:48:30 -0700, Andres Freund wrote:

< more tomorrow >

+		/* Move the high log number pointer past this one. */
+		++UndoLogShared->next_logno;

Fwiw, I find having "next" and "low" as variable names, and then
describing "next" as high in comments somewhat confusing.

+/* check_hook: validate new undo_tablespaces */
+bool
+check_undo_tablespaces(char **newval, void **extra, GucSource source)
+{
+	char	   *rawname;
+	List	   *namelist;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/*
+	 * Parse string into list of identifiers, just to check for
+	 * well-formedness (unfortunateley we can't validate the names in the
+	 * catalog yet).
+	 */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}

Why can't you validate the catalog here? In a lot of cases this will be
called in a transaction, especially when changing it in a
session. E.g. temp_tablespaces does so?

+	/*
+	 * Make sure we aren't already in a transaction that has been assigned an
+	 * XID.  This ensures we don't detach from an undo log that we might have
+	 * started writing undo data into for this transaction.
+	 */
+	if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 (errmsg("undo_tablespaces cannot be changed while a transaction is in progress"))));

Hm. Is this really a great proxy? Seems like it'll block changing the
tablespace unnecessarily in a lot of situations, and like there could
even be holes in the future - it doesn't seem crazy that we'd want to
emit undo without assigning an xid in some situations (e.g. for deleting
files in error cases, or for more aggressive cleanup of dead index
entries during reads or such).

It seems like it'd be pretty easy to just check
CurrentSession->attached_undo_slots[i].slot->meta.unlogged.this_xact_start
or such?

+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+	else
+	{
+		/*
+		 * Choose an OID using our pid, so that if several backends have the
+		 * same multi-tablespace setting they'll spread out.  We could easily
+		 * do better than this if more serious load balancing is judged
+		 * useful.
+		 */

We're not really choosing an oid, we're choosing a tablespace. Obviously
one can understand it as is, but it confused me for a second.

+ int index = MyProcPid % length;

Hm. Is MyProcPid a good proxy here? Wouldn't it be better to use
MyProc->pgprocno or such? That's much more guaranteed to space out
somewhat evenly?

+		int		first_index = index;
+		Oid		oid = InvalidOid;
+
+		/*
+		 * Take the tablespace create/drop lock while we look the name up.
+		 * This prevents the tablespace from being dropped while we're trying
+		 * to resolve the name, or while the called is trying to create an
+		 * undo log in it.  The caller will have to release this lock.
+		 */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);

Why exclusive?

I think any function that acquires a lock it doesn't release (or the
reverse) ought to have a big honking comment in its header warning of
that. And an explanation as to why that is.

+		for (;;)
+		{
+			const char *name = list_nth(namelist, index);
+
+			oid = get_tablespace_oid(name, true);
+			if (oid == InvalidOid)
+			{
+				/* Unknown tablespace, try the next one. */
+				index = (index + 1) % length;
+				/*
+				 * But if we've tried them all, it's time to complain.  We'll
+				 * arbitrarily complain about the last one we tried in the
+				 * error message.
+				 */
+				if (index == first_index)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", name),
+							 errhint("Create the tablespace or set undo_tablespaces to a valid or empty list.")));
+				continue;

Wouldn't it be better to simply include undo_tablespaces in the error
messages? Something roughly like 'none of the tablespaces in undo_tablespaces =
\"%s\" exists"?

+	/*
+	 * If we came here because the user changed undo_tablesaces, then detach
+	 * from any undo logs we happen to be attached to.
+	 */
+	if (force_detach)
+	{
+		for (i = 0; i < UndoPersistenceLevels; ++i)
+		{
+			UndoLogSlot *slot = CurrentSession->attached_undo_slots[i];
+
+			if (slot != NULL)
+			{
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				slot->pid = InvalidPid;
+				slot->meta.unlogged.xid = InvalidTransactionId;
+				LWLockRelease(&slot->mutex);

Would it make sense to re-assert here that the current transaction
didn't write undo?

+bool
+DropUndoLogsInTablespace(Oid tablespace)
+{
+	DIR *dir;
+	char undo_path[MAXPGPATH];
+	UndoLogSlot *slot = NULL;
+	int		i;
+
+	Assert(LWLockHeldByMe(TablespaceCreateLock));

IMO this ought to be mentioned in a function header comment.

+	/* First, try to kick everyone off any undo logs in this tablespace. */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		bool ok;
+		bool return_to_freelist = false;
+
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/* Check if this undo log can be forcibly detached. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		if (slot->meta.discard == slot->meta.unlogged.insert &&
+			(slot->meta.unlogged.xid == InvalidTransactionId ||
+			 !TransactionIdIsInProgress(slot->meta.unlogged.xid)))
+		{

Not everyone will agree, but this looks complicated enough that I'd put
it just in a simple wrapper function. If this were if
(CanDetachUndoForcibly(slot)) you'd not need a comment either...

Also, isn't the slot->meta.discard == slot->meta.unlogged.insert a
separate concern from detaching? My understanding is that it'll be
perfectly normal to have undo logs with undiscarded data that nobody is
attached to? In fact, I got confused below, because I initially didn't
spot any place that implemented the check referenced in the caller:

+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));
+
+	/*
+		else
+		{
+			/*
+			 * There is data we need in this undo log.  We can't force it to
+			 * be detached.
+			 */
+			ok = false;
+		}

Seems like we ought to return more information here. An error message
like:

/*
+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));

doesn't really allow a DBA to do anything about the issue. Seems we
ought to at least include the pid in the error message? I'd perhaps
just move the error message from DropTableSpace() into
DropUndoLogsInTablespace(). I don't think that's worse from a layering
perspective, and allows to raise a more precise error, and simplifies
the API.

+		/*
+		 * Put this undo log back on the appropriate free-list.  No one can
+		 * attach to it while we hold TablespaceCreateLock, but if we return
+		 * earlier in a future go around this loop, we need the undo log to
+		 * remain usable.  We'll remove all appropriate logs from the
+		 * free-lists in a separate step below.
+		 */
+		if (return_to_freelist)
+		{
+			LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+			slot->next_free = UndoLogShared->free_lists[slot->meta.persistence];
+			UndoLogShared->free_lists[slot->meta.persistence] = slot->logno;
+			LWLockRelease(UndoLogLock);
+		}

There's multiple places that put logs onto the freelist. I'd put them
into one small function. Not primarily because it'll be easier to read,
but because it makes it easier to search for places that do so.

+	/*
+	 * We detached all backends from undo logs in this tablespace, and no one
+	 * can attach to any non-default-tablespace undo logs while we hold
+	 * TablespaceCreateLock.  We can now drop the undo logs.
+	 */
+	slot = NULL;
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/*
+		 * Make sure no buffers remain.  When that is done by
+		 * UndoLogDiscard(), the final page is left in shared_buffers because
+		 * it may contain data, or at least be needed again very soon.  Here
+		 * we need to drop even that page from the buffer pool.
+		 */
+		forget_undo_buffers(slot->logno, slot->meta.discard, slot->meta.discard, true);
+
+		/*
+		 * TODO: For now we drop the undo log, meaning that it will never be
+		 * used again.  That wastes the rest of its address space.  Instead,
+		 * we should put it onto a special list of 'offline' undo logs, ready
+		 * to be reactivated in some other tablespace.  Then we can keep the
+		 * unused portion of its address space.
+		 */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		slot->meta.status = UNDO_LOG_STATUS_DISCARDED;
+		LWLockRelease(&slot->mutex);
+	}

Before I looked up forget_undo_buffers()'s implementation I wrote:

Hm. Iterating through shared buffers several times, especially when
there possibly could be a good sized numbers of undo logs, seems a bit
superfluous. This probably isn't going to be that frequently used in
practice, so it's perhaps ok. But it seems like this might be used when
things are bad (i.e. there's a lot of UNDO).

But I still wonder about that. Especially when there's a lot of UNDO
(most of it not in shared buffers), this could end up doing a *crapton*
of buffer lookups. I'm inclined to think that this case - probably in
contrast to the discard case, would be better served using
DropRelFileNodeBuffers().

+	/* Remove all dropped undo logs from the free-lists. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	for (i = 0; i < UndoPersistenceLevels; ++i)
+	{
+		UndoLogSlot *slot;
+		UndoLogNumber *place;
+
+		place = &UndoLogShared->free_lists[i];
+		while (*place != InvalidUndoLogNumber)
+		{
+			slot = find_undo_log_slot(*place, true);
+			if (!slot)
+				elog(ERROR,
+					 "corrupted undo log freelist, unknown log %u", *place);
+			if (slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+				*place = slot->next_free;
+			else
+				place = &slot->next_free;
+		}
+	}
+	LWLockRelease(UndoLogLock);

Hm, shouldn't this check that the log is actually in the being-dropped
tablespace?

+void
+ResetUndoLogs(UndoPersistence persistence)
+{

This imo ought to explain why one would want/need to do that. As far as
I can tell this implementation for example wouldn't be correct in all
that many situations, because it e.g. doesn't drop the relevant buffers?

Seems like this would need to assert that persistence isn't PERMANENT?

This is made more "problematic" by the fact that there's no caller for
this in this commit, only being used much later in the series. But I
think the comment should be there anyway. Hard to review (and
understand) otherwise.

Why is it correct not to take any locks here? The caller in 0014 afaict
is when we're already in hot standby, which means people will possibly
read undo?

+	UndoLogSlot *slot = NULL;
+
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		DIR	   *dir;
+		struct dirent *de;
+		char	undo_path[MAXPGPATH];
+		char	segment_prefix[MAXPGPATH];
+		size_t	segment_prefix_size;
+
+		if (slot->meta.persistence != persistence)
+			continue;
+
+		/* Scan the directory for files belonging to this undo log. */
+		snprintf(segment_prefix, sizeof(segment_prefix), "%06X.", slot->logno);
+		segment_prefix_size = strlen(segment_prefix);
+		UndoLogDirectory(slot->meta.tablespace, undo_path);
+		dir = AllocateDir(undo_path);
+		if (dir == NULL)
+			continue;
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+				continue;

I'm perfectly fine with using MAXPGPATH buffers. But I do find it
confusing that in some places you're using dynamic allocations (in some
cases quite repeatedly, like in allocate_empty_undo_segment(), but here
you don't?

Hm, isn't this kinda O(#slot*#total_size_of_undo) due to going over the
whole tablespace for each log?

+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);

I think the LOG should be done alternatively do to the DEBUG1, otherwise
it's going to be confusing. Should this really only be a LOG? Going to
be hard to cleanup for a DBA later.

+Datum
+pg_stat_get_undo_logs(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_UNDO_LOGS_COLS 9
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	char *tablespace_name = NULL;
+	Oid last_tablespace = InvalidOid;
+	int			i;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");

I wish we'd encapsulate this in one place instead of copying it over and
over.

Imo it's bad style to break error messages over multiple lines, makes it
harder to grep for.

+	/* Scan all undo logs to build the results. */
+	for (i = 0; i < UndoLogShared->nslots; ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+		char buffer[17];
+		Datum values[PG_STAT_GET_UNDO_LOGS_COLS];
+		bool nulls[PG_STAT_GET_UNDO_LOGS_COLS] = { false };
+		Oid tablespace;

Uncommented numbers like '17' for buffer lengths make me nervous.

+		values[0] = ObjectIdGetDatum((Oid) slot->logno);
+		values[1] = CStringGetTextDatum(
+			slot->meta.persistence == UNDO_PERMANENT ? "permanent" :
+			slot->meta.persistence == UNDO_UNLOGGED ? "unlogged" :
+			slot->meta.persistence == UNDO_TEMP ? "temporary" : "<uknown>");

s/uknown/unknown/

+		tablespace = slot->meta.tablespace;
+
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.discard));
+		values[3] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert));
+		values[4] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.end));
+		values[5] = CStringGetTextDatum(buffer);

Makes me wonder if we shouldn't have a type for undo pointers.

+		if (slot->meta.unlogged.xid == InvalidTransactionId)
+			nulls[6] = true;
+		else
+			values[6] = TransactionIdGetDatum(slot->meta.unlogged.xid);
+		if (slot->pid == InvalidPid)
+			nulls[7] = true;
+		else
+			values[7] = Int32GetDatum((int32) slot->pid);
+		switch (slot->meta.status)
+		{
+		case UNDO_LOG_STATUS_ACTIVE:
+			values[8] = CStringGetTextDatum("ACTIVE"); break;
+		case UNDO_LOG_STATUS_FULL:
+			values[8] = CStringGetTextDatum("FULL"); break;
+		default:
+			nulls[8] = true;
+		}

Don't think this'll survive pgindent.

+		/*
+		 * Deal with potentially slow tablespace name lookup without the lock.
+		 * Avoid making multiple calls to that expensive function for the
+		 * common case of repeating tablespace.
+		 */
+		if (tablespace != last_tablespace)
+		{
+			if (tablespace_name)
+				pfree(tablespace_name);
+			tablespace_name = get_tablespace_name(tablespace);
+			last_tablespace = tablespace;
+		}

If we need to do this repeatedly, I think we ought to add a syscache for
tablespace names.

+		if (tablespace_name)
+		{
+			values[2] = CStringGetTextDatum(tablespace_name);
+			nulls[2] = false;
+		}
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);

Seems like a CHECK_FOR_INTERRUPTS() in this loop wouldn't hurt.

+	}
+
+	if (tablespace_name)
+		pfree(tablespace_name);

That seems a bit superflous, given we're leaking plenty other memory
(which is perfectly fine).

+/*
+ * replay the creation of a new undo log
+ */
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+	xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	/* Create meta-data space in shared memory. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* TODO: assert that it doesn't exist already? */
+	slot = allocate_undo_log_slot();

Doesn't this need some error checking? allocate_undo_log_slot() will
return NULL if there's no slots left. E.g. restarting a server with a
lower max_connections could have one run into this easily?

+/*
+ * Drop all buffers for the given undo log, from the old_discard to up
+ * new_discard.  If drop_tail is true, also drop the buffer that holds
+ * new_discard; this is used when discarding undo logs completely, for example
+ * via DROP TABLESPACE.  If it is false, then the final buffer is not dropped
+ * because it may contain data.
+ *
+ */
+static void
+forget_undo_buffers(int logno, UndoLogOffset old_discard,
+					UndoLogOffset new_discard, bool drop_tail)
+{
+	BlockNumber old_blockno;
+	BlockNumber new_blockno;
+	RelFileNode	rnode;
+
+	UndoRecPtrAssignRelFileNode(rnode, MakeUndoRecPtr(logno, old_discard));
+	old_blockno = old_discard / BLCKSZ;
+	new_blockno = new_discard / BLCKSZ;
+	if (drop_tail)
+		++new_blockno;
+	while (old_blockno < new_blockno)
+	{

Hm. This'll be quite bad if you have a lot more undo than
shared_buffers. Taking the partition lwlocks this many times will
hurt. OTOH, scanning all of shared buffers everytime we truncate a few
hundred bytes of undo away is obviously also not going to work.

+		ForgetBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno);
+		ForgetLocalBuffer(SMGR_UNDO, rnode, UndoLogForkNum, old_blockno++);

This seems odd to me - why do we need to scan both? We ought to know
which one is needed, right?

+ }

+}
+/*
+ * replay an undo segment discard record
+ */

Missing newline between functions.

+static void
+undolog_xlog_discard(XLogReaderState *record)
+{
+	/*
+	 * We're about to discard undologs. In Hot Standby mode, ensure that
+	 * there's no queries running which need to get tuple from discarded undo.

nitpick: s/undologs/undo logs/? I think most other comments split it?

+	 * XXX we are passing empty rnode to the conflict function so that it can
+	 * check conflict in all the backend regardless of which database the
+	 * backend is connected.
+	 */
+	if (InHotStandby && TransactionIdIsValid(xlrec->latestxid))
+		ResolveRecoveryConflictWithSnapshot(xlrec->latestxid, rnode);

Hm. Perhaps it'd be better to change
ResolveRecoveryConflictWithSnapshot's API to just accept the
database (OTOH, we perhaps ought to be more granular in conflict
processing). Or just mention that it's ok to pass in an invalid rnode?

+	/*
+	 * See if we need to unlink or rename any files, but don't consider it an
+	 * error if we find that files are missing.  Since UndoLogDiscard()
+	 * performs filesystem operations before WAL logging or updating shmem
+	 * which could be checkpointed, a crash could have left files already
+	 * deleted, but we could replay WAL that expects the files to be there.
+	 */

Or we could have crashed/restarted during WAL replay and processing the
same WAL again. Not sure if that's worth mentioning.

+	/* Unlink or rename segments that are no longer in range. */
+	while (old_segment_begin < new_segment_begin)
+	{
+		char	discard_path[MAXPGPATH];
+
+		/* Tell the checkpointer that the file is going away. */
+		undofile_forget_sync(slot->logno,
+							 old_segment_begin / UndoLogSegmentSize,
+							 slot->meta.tablespace);
+
+		UndoLogSegmentPath(xlrec->logno, old_segment_begin / UndoLogSegmentSize,
+						   slot->meta.tablespace, discard_path);
+
+		/* Can we recycle the oldest segment? */
+		if (end < xlrec->end)
+		{
+			char	recycle_path[MAXPGPATH];
+
+			UndoLogSegmentPath(xlrec->logno, end / UndoLogSegmentSize,
+							   slot->meta.tablespace, recycle_path);
+			if (rename(discard_path, recycle_path) == 0)
+			{
+				elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+					 discard_path, recycle_path);
+				end += UndoLogSegmentSize;
+			}
+			else
+			{
+				elog(LOG, "could not rename \"%s\" to \"%s\": %m",
+					 discard_path, recycle_path);
+			}
+		}
+		else
+		{
+			if (unlink(discard_path) == 0)
+				elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+			else
+				elog(LOG, "could not unlink \"%s\": %m", discard_path);
+		}
+		old_segment_begin += UndoLogSegmentSize;
+	}

The code to recycle or delete one segment exists in multiple places (at
least also in UndoLogDiscard()). Think it's long enough that it's easily
worthwhile to share.

+/*

@@ -1418,12 +1418,18 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
segmentpath = strstr(filename, ".");
if (segmentpath != NULL)
{
-				segmentno = atoi(segmentpath + 1);
-				if (segmentno == 0)
+				char	   *end;
+				if (strstr(readfilename, "undo"))
+					first_blkno = strtol(segmentpath + 1, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath + 1, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
ereport(ERROR,
-							(errmsg("invalid segment number %d in file \"%s\"",
-									segmentno, filename)));
+							(errmsg("invalid segment number in file \"%s\"",
+									filename)));
}
+			else
+				first_blkno = 0;
}
}

Hm. Not a fan of just using strstr() here. Can't quite articulate
why. Just somehow rubs me wrong.

/*
* ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
*		a relcache entry for the relation.
- *
- * NB: At present, this function may only be used on permanent relations, which
- * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary or unlogged relations, we could pass additional
- * parameters.
*/
Buffer
ReadBufferWithoutRelcache(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
BlockNumber blockNum, ReadBufferMode mode,
-						  BufferAccessStrategy strategy)
+						  BufferAccessStrategy strategy,
+						  char relpersistence)
{
bool		hit;
-	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
-
-	Assert(InRecovery);
+	SMgrRelation smgr = smgropen(smgrid, rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
-	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+	return ReadBuffer_common(smgr, relpersistence, forkNum, blockNum,
mode, strategy, &hit);
}

Hm. Using this for undo access means that we don't do any buffer
read/hit counting associatable with the relation causing undo to be
read/written. That seems like a sizable monitoring defficiency.

/*
+ * ForgetBuffer -- drop a buffer from shared buffers
+ *
+ * If the buffer isn't present in shared buffers, nothing happens.  If it is
+ * present, it is discarded without making any attempt to write it back out to
+ * the operating system.  The caller must therefore somehow be sure that the
+ * data won't be needed for anything now or in the future.  It assumes that
+ * there is no concurrent access to the block, except that it might be being
+ * concurrently written.
+ */
+void
+ForgetBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+			 BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, InvalidBackendId);
+	BufferTag	tag;			/* identity of target block */
+	uint32		hash;			/* hash value for tag */
+	LWLock	   *partitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgrid, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	hash = BufTableHashCode(&tag);
+	partitionLock = BufMappingPartitionLock(hash);
+
+	/* see if the block is in the buffer pool */
+	LWLockAcquire(partitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&tag, hash);
+	LWLockRelease(partitionLock);
+
+	/* didn't find it, so nothing to do */
+	if (buf_id < 0)
+		return;
+
+	/* take the buffer header lock */
+	bufHdr = GetBufferDescriptor(buf_id);
+	buf_state = LockBufHdr(bufHdr);
+	/*
+	 * The buffer might been evicted after we released the partition lock and
+	 * before we acquired the buffer header lock.  If so, the buffer we've
+	 * locked might contain some other data which we shouldn't touch. If the
+	 * buffer hasn't been recycled, we proceed to invalidate it.
+	 */
+	if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+		bufHdr->tag.blockNum == blockNum &&
+		bufHdr->tag.forkNum == forkNum)
+		InvalidateBuffer(bufHdr);		/* releases spinlock */
+	else
+		UnlockBufHdr(bufHdr, buf_state);

Phew, I don't like this code one bit. It imo is a bad idea / unnecessary
to look up the buffer, unlock the partition lock, and then recheck
identity. And do exactly the same thing again in InvalidateBuffer()
(including making a copy of the tag while holding the buffer header
lock).

Seems like this should be something roughly like

ReservePrivateRefCountEntry();

LWLockAcquire(partitionLock, LW_SHARED);
buf_id = BufTableLookup(&tag, hash);
if (buf_id >= 0)
{
bufHdr = GetBufferDescriptor(buf_id);

buf_state = LockBufHdr(bufHdr);

/*
* Temporarily acquire pin - that prevents the buffer
* from being replaced with one that we did not intend
* to target.
*
* XXX:
*/
ref = PinBuffer_Locked(bufHdr, strategy);

/* release partition lock, acquire exclusively so we can drop */
LWLockRelease(partitionLock);

/* loop until nobody else has the buffer pinned */
while (true)
{
LWLockAcquire(partitionLock, LW_EXCLUSIVE);

buf_state = LockBufHdr(buf);

/*
* Check if somebody else is busy writing the buffer (we
* have one pin).
*/
if (BUF_STATE_GET_REFCOUNT(buf_state) == 1)
break;

// XXX: Should we assert IO_IN_PROGRESS? Ought to be the
// only way to get here.

/* wait for IO to finish, without holding locks */
UnlockBufHdr(buf, buf_state);
LWLockRelease(partitionLock);

Assert(GetPrivateRefCount(BufferDescriptorGetBuffer(buf)) == 1);
WaitIO(buf);

/* buffer identity can't change, we've a pin */

// XXX: Assert that the buffer isn't dirty anymore? There
// ought to be no possibility for it to get dirty now.
}

Assert(!(buf_state & BM_PIN_COUNT_WAITER));

/*
* Clear out the buffer's tag and flags. We must do this to ensure that
* linear scans of the buffer array don't think the buffer is valid.
*/
oldFlags = buf_state & BUF_FLAG_MASK;
CLEAR_BUFFERTAG(buf->tag);
buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
/* remove our refcount */
buf_state -= BUF_REFCOUNT_ONE;
UnlockBufHdr(buf, buf_state);

/*
* Remove the buffer from the lookup hashtable, if it was in there.
*/
if (oldFlags & BM_TAG_VALID)
BufTableDelete(&oldTag, oldHash);

/*
* Done with mapping lock.
*/
LWLockRelease(oldPartitionLock);

Assert(ref->refcount == 1);
ForgetPrivateRefCountEntry(ref);
ResourceOwnerForgetBuffer(CurrentResourceOwner, BufferDescriptorGetBuffer(buf));
}

or something in that vein. Now you can validly argue that this is more
complicated - but I also think that this is going to be a much hotter
path than normal relation drops.

<more after some errands>

Greetings,

Andres Freund

In reply to: Thomas Munro (#182)
Re: should there be a hard-limit on the number of transactions pending undo?

On Mon, Jul 29, 2019 at 2:52 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Thanks for sharing that. I see they're giving that paper at VLDB next
month in LA... I hope the talk video will be published on the web.
While we've been working on a hybrid vaccum/undo design, they've built
a hybrid undo/vacuum system.

It seems that this will be in a stable release soon, so it's not
pie-in-the-sky stuff. AFAICT, they have indexes that always point to
the latest row version. Getting an old version always required working
backwards from the latest. Perhaps the constant time recovery stuff is
somewhat like Postgres heapam when it comes to SELECTs, INSERTs, and
DELETEs, but much less similar when it comes to UPDATEs. This seems
like it might be an important distinction.

As the MVCC survey paper out of CMU [1]http://www.vldb.org/pvldb/vol10/p781-Wu.pdf -- Peter Geoghegan from a couple of years back says:

"The main idea of using logical pointers is that the DBMS uses a fixed
identifier that does not change for each tuple in its index entry.
Then, as shown in Fig. 5a, the DBMS uses an indirection layer that
maps a tuple’s identifier to the HEAD of its version chain. This
avoids the problem of having to update all of a table’s indexes to
point to a new physical location whenever a tuple is modified. (even
if the indexed attributes were not changed)."

To me, this suggests that zheap ought to make heap TIDs "more logical"
than they are with heapam today (heap TIDs are hybrid physical/logical
identifiers today). "Row forwarding" across heap pages is the
traditional way of ensuring that TIDs in indexes are stable even in
the worst case, apparently, but other approaches also seem possible.

[1]: http://www.vldb.org/pvldb/vol10/p781-Wu.pdf -- Peter Geoghegan
--
Peter Geoghegan

#185Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#175)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 12:18 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Jul 23, 2019 at 10:42 AM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

Yes, I also think that the function would error out only because of
can't-happen cases, like "too many locks taken" or "out of binary heap
slots" or "out of memory" (this last one is not such a can't happen
case). These cases happen probably due to some bugs, I suppose. But I
was wondering : Generally when the code errors out with such
can't-happen elog() calls, worst thing that happens is that the
transaction gets aborted. Whereas, in this case, the worst thing that
could happen is : the undo action would never get executed, which
means selects for this tuple will keep on accessing the undo log ?
This does not sound like any data consistency issue, so we should be
fine after all ?

I don't think so. Every XID present in undo has to be something we
can look up in CLOG to figure out which transactions are aborted and
which transactions are committed, so that we know which transactions
need undo. If we forget to undo the transaction, we can't discard it,
which means we can't advance the CLOG transaction horizon, which means
we'll eventually start failing to assign XIDs, leading to a refusal of
all write transactions. Oops.

More generally, it's not OK for the generic undo layer to make
assumptions about whether the operations performed by the undo
handlers are essential or not. We don't want to impose a design
constraint the undo can only be used for things that are not actually
critical, because that will make it hard to write AMs that use it.
And there's no reason to live with such a design constraint anyway,
because, as noted above, CLOG truncation requires it.

More generally still, some can't-happen situations should be checked
via Assert() and others via elog(). For example, consider some code
that looks up a syscache tuple and pulls data from the returned tuple.
If the code that handles DDL is written in such a way that the tuple
should always exist, then this is a can't-happen situation, but
generally the code checks this via elog(), not Assert(), because it
could also happen due to the catalog contents being corrupted. If
Assert() were used, the checks would not run in production builds, and
a corrupt catalog would lead to a seg fault. An elog() is much
friendlier. As a general principle, when a certain thing ought to
always be true, but it being true depends on a whole lot of
assumptions elsewhere in the code, and especially if it also depends
on assumptions like "the database is not corrupted," I think elog() is
preferable. Assert() is better for things that are more localized and
that really can't go wrong for any reason other than a bug. In this
case, I think I would tend towards elog(PANIC), but it's arguable.

Agreed, elog(PANIC) seems like a better way for this as compared to Assert.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#186Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#121)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 2:28 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 11, 2019 at 9:17 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

This information won't be shared across transactions, so can't we keep
it in top transaction's state? It seems to me that will be better
than to maintain it as a global state.

I think this idea is good for the DO time but during REDO time it will
not work as we will not have the transaction state. Having said that
the current idea of keeping in the global variable will also not work
during REDO time because the WAL from the different transaction can be
interleaved. There are few ideas to handle this issue

1. At DO time keep in TopTransactionState as you suggested and during
recovery time read from the first complete record on the page.
2. Just to keep the code uniform always read from the first complete
record of the page.

After putting more though I am more inclined towards idea-2. Because
we are anyway inserting our current record into that page basically we
have read the buffer and also holds the exclusive lock on the buffer.
So reading a few extra bytes from the buffer will not hurt us IMHO.

If someone has a better solution please suggest.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#187Thomas Munro
thomas.munro@gmail.com
In reply to: Dilip Kumar (#186)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 5:03 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Fri, Jul 19, 2019 at 2:28 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 11, 2019 at 9:17 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

This information won't be shared across transactions, so can't we keep
it in top transaction's state? It seems to me that will be better
than to maintain it as a global state.

I think this idea is good for the DO time but during REDO time it will
not work as we will not have the transaction state. Having said that
the current idea of keeping in the global variable will also not work
during REDO time because the WAL from the different transaction can be
interleaved. There are few ideas to handle this issue

1. At DO time keep in TopTransactionState as you suggested and during
recovery time read from the first complete record on the page.
2. Just to keep the code uniform always read from the first complete
record of the page.

After putting more though I am more inclined towards idea-2. Because
we are anyway inserting our current record into that page basically we
have read the buffer and also holds the exclusive lock on the buffer.
So reading a few extra bytes from the buffer will not hurt us IMHO.

If someone has a better solution please suggest.

Hi Dilip,

Here's some initial review of the following patch (from your public
undo_interface_v1 branch as of this morning). I haven't tested this
version yet, because my means of testing this stuff involves waiting
for undoprocessing to be rebased, so that I can test it with my
orphaned files stuff and other test programs. It contains another
suggestion for that problem you just mentioned (and also me pointing
out what you just pointed out, since I wrote it earlier) though I'm
not sure if it's better than your options above.

commit 2f3c127b9e8bc7d27cf7adebff0a355684dfb94e
Author: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu May 2 11:28:13 2019 +0530

Provide interfaces to store and fetch undo records.

+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"

"miscadmin.h" comes before "storage...".

+/*
+ * Compute the size of the partial record on the undo page.
+ *
+ * Compute the complete record size by uur_info and variable field length
+ * stored in the page header and then subtract the offset of the record so that
+ * we can get the exact size of partial record in this page.
+ */
+static inline Size
+UndoPagePartialRecSize(UndoPageHeader phdr)
+{
+    Size        size;

We decided to use size_t everywhere in new code (except perhaps
functions conforming to function pointer types that historically use
Size in their type).

+    /*
+     * Compute the header size from undo record uur_info, stored in the page
+     * header.
+     */
+    size = UndoRecordHeaderSize(phdr->uur_info);
+
+    /*
+     * Add length of the variable part and undo length. Now, we know the
+     * complete length of the undo record.
+     */
+    size += phdr->tuple_len + phdr->payload_len + sizeof(uint16);
+
+    /*
+     * Subtract the size which is stored in the previous page to get the
+     * partial record size stored in this page.
+     */
+    size -= phdr->record_offset;
+
+    return size;

This is probably a stupid question but why isn't it enough to just
store the offset of the first record that begins on this page, or 0
for none yet? Why do we need to worry about the partial record's
payload etc?

+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+                  UnpackedUndoRecord *urec,
+                  Oid dbid)
+{
...
+    /* Fetch compression info for the transaction. */
+    compression_info = GetTopTransactionUndoCompressionInfo(category);

How can this work correctly in recovery? [Edit: it doesn't, as you
just pointed out]

I had started reviewing an older version of your patch (the version
that had made it as far as the undoprocessing branch as of recently),
before I had the bright idea to look for a newer version. I was going
to object to the global variable you had there in the earlier version.
It seems to me that you have to be able to reproduce the exact same
compression in recovery that you produced as "do" time, no? How can
TopTranasctionStateData be the right place for this in recovery?

One data structure that could perhaps hold this would be
UndoLogTableEntry (the per-backend cache, indexed by undo log number,
with pretty fast lookups; used for things like
UndoLogNumberGetCategory()). As long as you never want to have
inter-transaction compression, that should have the right scope to
give recovery per-undo log tracking. If you ever wanted to do
compression between transactions too, maybe UndoLogSlot could work,
but that'd have more complications.

+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set
of records
+ * and call this function again.
+ *
+ * from_urecptr        - Where to start fetching the undo records.
If we can not
+ *                      read all the records because of memory limit then this
+ *                      will be set to the previous undo record
pointer from where
+ *                      we need to start fetching on next call.
Otherwise it will
+ *                      be set to InvalidUndoRecPtr.
+ * to_urecptr        - Last undo record pointer to be fetched.
+ * undo_apply_size    - Memory segment limit to collect undo records.
+ * nrecords            - Number of undo records read.
+ * one_page            - Caller is applying undo only for one block not for
+ *                      complete transaction.  If this is set true then instead
+ *                      of following transaction undo chain using
prevlen we will
+ *                      follow the block prev chain of the block so that we can
+ *                      avoid reading many unnecessary undo records of the
+ *                      transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+                    int undo_apply_size, int *nrecords, bool one_page)

Could you please make it clear in comments and assertions what the
relation between from_urecptr and to_urecptr is and what they mean
(they must be in the same undo log, one must be <= the other, both
point to the *start* of a record, so it's not the same as the total
range of undo)?

undo_apply_size is not a good parameter name, because the function is
useful for things other than applying records -- like the
undoinspect() extension (or some better version of that), for example.
Maybe max_result_size or something like that?

+{
...
+        /* Allocate memory for next undo record. */
+        uur = palloc0(sizeof(UnpackedUndoRecord));
...
+
+        size = UnpackedUndoRecordSize(uur);
+        total_size += size;

I see, so the unpacked records are still allocated one at a time. I
guess that's OK for now. From some earlier discussion I had been
expecting an arrangement where the actual records were laid out
contiguously with their subcomponents (things they point to in
palloc()'d memory) nearby.

+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+                     UndoLogCategory category)
+{
...
+    char        prevlen[2];
...
+    prev_rec_len = *(uint16 *) (prevlen);

I don't think that's OK, and might crash on a non-Intel system. How
about using a union of uint16 and char[2]?

+    /* Copy undo record transaction header if it is present. */
+    if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+        memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);

I was wondering why you don't use D = S instead of mempcy(&D, &S,
size) wherever you can, until I noticed you use these SizeOfXXX macros
that don't include trailing padding from structs, and that's also how
you allocate objects. Hmm. So if I were to complain about you not
using plain old assignment whenever you can, I'd also have to complain
about that.

I think that that technique of defining a SizeOfXXX macro that
excludes trailing bytes makes sense for writing into WAL or undo log
buffers using mempcy(). I'm not sure it makes sense for palloc() and
copying into typed variables like you're doing here and I think I'd
prefer the notational simplicity of using the (very humble) type
system facilities C gives us. (Some memory checker might not like it
you palloc(the shorter size) and then use = if the compiler chooses to
implement it as memcpy sizeof().)

+/*
+ * The below common information will be stored in the first undo record of the
+ * page.  Every subsequent undo record will not store this information, if
+ * required this information will be retrieved from the first undo
record of the
+ * page.
+ */
+typedef struct UndoCompressionInfo

Shouldn't this say "Every subsequent record will not store this
information *if it's the same as the relevant fields in the first
record*"?

+#define UREC_INFO_TRANSACTION                0x001
+#define UREC_INFO_RMID                        0x002
+#define UREC_INFO_RELOID                    0x004
+#define UREC_INFO_XID                        0x008

Should we call this UREC_INFO_FXID, since it refers to a FullTransactionId?

+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */

I think you mean "All structures are packed into undo pages without
considering alignment and without trailing padding bytes"? This comes
from the definition of the SizeOfXXX macros IIUC. There might still
be padding between members of some of those structs, no? Like this
one, that has the second member at offset 2 on my system:

+typedef struct UndoRecordHeader
+{
+    uint8        urec_type;        /* record type code */
+    uint16        urec_info;        /* flag bits */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader    \
+    (offsetof(UndoRecordHeader, urec_info) + sizeof(uint16))
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+    /*
+     * Undo block number where we need to start reading the undo for applying
+     * the undo action.   InvalidBlockNumber means undo applying hasn't
+     * started for the transaction and MaxBlockNumber mean undo completely
+     * applied. And, any other block number means we have applied partial undo
+     * so next we can start from this block.
+     */
+    BlockNumber urec_progress;
+    Oid            urec_dbid;        /* database id */
+    UndoRecPtr    urec_next;        /* urec pointer of the next transaction */
+} UndoRecordTransaction;

I propose that we rename this to UndoRecordGroupHeader (or something
like that... maybe "Set", but we also use "set" as a verb in various
relevant function names):

1. We'll also use these for the new "shared" records we recently
invented that don't relate to a transaction. This is really about
defining the unit of discarding; we throw away the whole set of
records at once, which is why it's basically about proividing a space
for "urec_next".

2. Though it also holds rollback progress information, which is a
transaction-specific concept, there can be more than one of these sets
of records for a single transaction anyway. A single transaction can
write undo stuff in more than one undo log (different categories
perm/temp/unlogged/shared and also due to log switching when they are
full).

So really it's just a header for an arbitrary set of records, used to
track when and how to discard them.

If you agree with that idea, perhaps urec_next should become something
like urec_next_group, too. "next" is a bit vague, especially for
something as untyped as UndoRecPtr: someone might think it points to
the next record.

More soon.

--
Thomas Munro
https://enterprisedb.com

#188Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#183)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

Amit, short note: The patches aren't attached in patch order. Obviously
a miniscule thing, but still nicer if that's not the case.

Dilip, this also contains the start of a review for the undo record
interface further down.

On 2019-07-29 16:35:20 -0700, Andres Freund wrote:

<more after some errands>

Here we go.

I'm a bit worried about expanding the use of
ReadBufferWithoutRelcache(). Not so much because of the relcache itself,
but because it requires doing separate smgropen() calls. While not
crazily expensive, it's also not free. Especially combined with closing
all such relations at transaction end (c.f. AtEOXact_SMgr).

I'm somewhat inclined to think that this requires a slightly bigger
refactoring than done in this patch. Imo at the very least the smgr
entries ought not to be unowned. But working towards not haven to
re-open the smgr entry for every single trival request ought to be part
of this too.

/*
+ * ForgetLocalBuffer - drop a buffer from local buffers
+ *
+ * This is similar to bufmgr.c's ForgetBuffer, except that we do not need
+ * to do any locking since this is all local.  As with that function, this
+ * must be used very carefully, since we'll cheerfully throw away dirty
+ * buffers without any attempt to write them.
+ */
+void
+ForgetLocalBuffer(SmgrId smgrid, RelFileNode rnode, ForkNumber forkNum,
+				  BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(smgrid, rnode, BackendIdForTempRelations());
+	BufferTag	tag;					/* identity of target block */
+	LocalBufferLookupEnt *hresult;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/*
+	 * If somehow this is the first request in the session, there's nothing to
+	 * do.  (This probably shouldn't happen, though.)
+	 */
+	if (LocalBufHash == NULL)
+		return;

Given that the call to ForgetLocalBuffer() currently is unconditional,
rather than checking the persistence of the undo log, I don't see why
this wouldn't happen?

+	/* mark buffer invalid */
+	bufHdr = GetLocalBufferDescriptor(hresult->id);
+	CLEAR_BUFFERTAG(bufHdr->tag);
+	buf_state = pg_atomic_read_u32(&bufHdr->state);
+	buf_state &= ~(BM_VALID | BM_TAG_VALID | BM_DIRTY);
+	pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);

Shouldn't this also clear out at least the usagecount? I'd probably just
use
buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
like InvalidateBuffer() does.

I'd probably also add an assert ensuring that the refcount is zero.

@@ -97,7 +116,6 @@ static dlist_head unowned_relns;
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);

-
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
* managers.

spurious change.

+/*
+ * While md.c expects random access and has a small number of huge
+ * segments, undofile.c manages a potentially very large number of smaller
+ * segments and has a less random access pattern.  Therefore, instead of
+ * keeping a potentially huge array of vfds we'll just keep the most
+ * recently accessed N.
+ *
+ * For now, N == 1, so we just need to hold onto one 'File' handle.
+ */
+typedef struct UndoFileState
+{
+	int		mru_segno;
+	File	mru_file;
+} UndoFileState;

IMO N==1 gotta change before this is committable. There's too many
design issues that could creep in without fixing this (e.g. not being
careful enough about closing cached file handles after certain
operations etc), that will be harder to fix later.

+void
+undofile_open(SMgrRelation reln)
+{
+	UndoFileState *state;
+
+	state = MemoryContextAllocZero(UndoFileCxt, sizeof(UndoFileState));
+	reln->private_data = state;
+}

Hm, I don't quite like this 'private_data' design. Was that design
discussed anywhere?

Intuitively ISTM that it'd be better if SMgrRelation were embedded in a
per-SMGR type struct. Obviously that'd not quite work as things are set
up, because the size has to be constant due to SMgrRelationHash. But I
think it'd might be good anyway if that hash just stored a pointer to
the relevant SMgrRelation.

+void
+undofile_close(SMgrRelation reln, ForkNumber forknum)
+{
+}

Hm, aren't we leaking private_data right now?

+void
+undofile_create(SMgrRelation reln, ForkNumber forknum, bool isRedo)
+{
+	/*
+	 * File creation is managed by undolog.c, but xlogutils.c likes to call
+	 * this just in case.  Ignore.
+	 */
+}

Phew, this is not pretty.

+bool
+undofile_exists(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_exists is not supported");
+
+	return false;		/* not reached */
+}

This one I actually find bad. It seems pretty reasonable to just be able
for SMGR-kind agnostic code to be able to know whether a file exists or
not.

+void
+undofile_extend(SMgrRelation reln, ForkNumber forknum,
+				BlockNumber blocknum, char *buffer,
+				bool skipFsync)
+{
+	elog(ERROR, "undofile_extend is not supported");
+}

This one I have much less problems with.

+void
+undofile_read(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+			  char *buffer)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);

I'd not name this seekpos, given that we're not seeking..

+BlockNumber
+undofile_nblocks(SMgrRelation reln, ForkNumber forknum)
+{
+	/*
+	 * xlogutils.c likes to call this to decide whether to read or extend; for
+	 * now we lie and say the relation is big as possible.
+	 */
+	return UndoLogMaxSize / BLCKSZ;
+}

That's imo not ok.

/*
+ *	check_for_live_undo_data()
+ *
+ *	Make sure there are no live undo records (aborted transactions that have
+ *	not been rolled back, or committed transactions whose undo data has not
+ *	yet been discarded).
+ */
+static void
+check_for_undo_data(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1200)
+		return;

Needs to be updated now.

--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -351,6 +351,10 @@ check_data_dir(ClusterInfo *cluster)
check_single_dir(pg_data, "pg_clog");
else
check_single_dir(pg_data, "pg_xact");
+
+	/* pg_undo is new in v13 */
+	if (GET_MAJOR_VERSION(cluster->major_version) >= 1200)
+		check_single_dir(pg_data, "pg_undo");
}

The comment talks about v13, but code checks for v12?

+++ b/src/bin/pg_upgrade/undo.c
@@ -0,0 +1,292 @@
+/*
+ *	undo.c
+ *
+ *	Support for upgrading undo logs.\
+ *	Copyright (c) 2019, PostgreSQL Global Development Group
+ *	src/bin/pg_upgrade/undo.c
+ */

A small design note here seems like a good idea.

+/* Undo log statuses. */
+typedef enum
+{
+	UNDO_LOG_STATUS_UNUSED = 0,
+	UNDO_LOG_STATUS_ACTIVE,
+	UNDO_LOG_STATUS_FULL,
+	UNDO_LOG_STATUS_DISCARDED
+} UndoLogStatus;

An explanation of what these mean would be good.

+/*
+ * Convert from relpersistence ('p', 'u', 't') to an UndoPersistence
+ * enumerator.
+ */
+#define UndoPersistenceForRelPersistence(rp)						\
+	((rp) == RELPERSISTENCE_PERMANENT ? UNDO_PERMANENT :			\
+	 (rp) == RELPERSISTENCE_UNLOGGED ? UNDO_UNLOGGED : UNDO_TEMP)
+
+/*
+ * Convert from UndoPersistence to a relpersistence value.
+ */
+#define RelPersistenceForUndoPersistence(up)				\
+	((up) == UNDO_PERMANENT ? RELPERSISTENCE_PERMANENT :	\
+	 (up) == UNDO_UNLOGGED ? RELPERSISTENCE_UNLOGGED :		\
+	 RELPERSISTENCE_TEMP)

We shouldn't add macros with multiple evaluation hazards without need.

+/*
+ * Properties of an undo log that don't have explicit WAL records logging
+ * their changes, to reduce WAL volume.  Instead, they change incrementally
+ * whenever data is inserted as a result of other WAL records.  Since the
+ * values recorded in an online checkpoint may be out of the sync (ie not the
+ * correct values as at the redo LSN), these are backed up in buffer data on
+ * first change after each checkpoint.
+ */

s/on first/on the first/?

+/*
+ * Instantiate fast inline hash table access functions.  We use an identity
+ * hash function for speed, since we already have integers and don't expect
+ * many collisions.
+ */
+#define SH_PREFIX undologtable
+#define SH_ELEMENT_TYPE UndoLogTableEntry
+#define SH_KEY_TYPE UndoLogNumber
+#define SH_KEY number
+#define SH_HASH_KEY(tb, key) (key)
+#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_SCOPE static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+extern PGDLLIMPORT undologtable_hash *undologtable_cache;

Why isn't this defined in a .c file? I've a bit of a hard time believing
that making UndoLogGetTableEntry() an extern function would be a
meaningful overhead compared to the operations this is used for. Not
exposing those details seems nicer to me.

+/* Create a new undo log. */
+typedef struct xl_undolog_create
+{
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoPersistence persistence;
+} xl_undolog_create;
+
+/* Extend an undo log by adding a new segment. */
+typedef struct xl_undolog_extend
+{
+	UndoLogNumber logno;
+	UndoLogOffset end;
+} xl_undolog_extend;
+
+/* Discard space, and possibly destroy or recycle undo log segments. */
+typedef struct xl_undolog_discard
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	TransactionId latestxid;	/* latest xid whose undolog are discarded. */
+	bool		  entirely_discarded;
+} xl_undolog_discard;
+
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;

I'd add flags to these. Perhaps I'm overly cautious, but I found that
extremely valuable when having to fix bugs in already released
versions. And these aren't so frequent that that'd hurt. Obviously
entirely_discarded would then be a flag.

+extern void undofile_init(void);
+extern void undofile_shutdown(void);
+extern void undofile_open(SMgrRelation reln);
+extern void undofile_close(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_create(SMgrRelation reln, ForkNumber forknum,
+							bool isRedo);
+extern bool undofile_exists(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum,
+							bool isRedo);
+extern void undofile_extend(SMgrRelation reln, ForkNumber forknum,
+		 BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_prefetch(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber blocknum);
+extern void undofile_read(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, char *buffer);
+extern void undofile_write(SMgrRelation reln, ForkNumber forknum,
+		BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+			BlockNumber blocknum, BlockNumber nblocks);
+extern BlockNumber undofile_nblocks(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_truncate(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber nblocks);
+extern void undofile_immedsync(SMgrRelation reln, ForkNumber forknum);
+
+/* Callbacks used by sync.c. */
+extern int undofile_syncfiletag(const FileTag *tag, char *path);
+extern bool undofile_filetagmatches(const FileTag *tag, const FileTag *candidate);
+
+/* Management of checkpointer requests. */
+extern void undofile_request_sync(UndoLogNumber logno, BlockNumber segno,
+								  Oid tablespace);
+extern void undofile_forget_sync(UndoLogNumber logno, BlockNumber segno,
+								 Oid tablespace);
+extern void undofile_forget_sync_tablespace(Oid tablespace);
+extern void undofile_request_sync_dir(Oid tablespace);

Istm that it'd be better to only have mdopen/... referenced from
smgrsw() and then have f_smgr be included (as a const f_smgr* const) as
part of SMgrRelation. For one, that'll allow us to hide a lot more of
this into md.c/undofile.c. It's also a pathway to being more extensible.
I think performance ought to be at least as good (as we currently have
to read SMgrRelation->smgr_which, then read the callback from smgrsw
(which is probably combined into one op in many platforms), and then
call it). Could then also just make the simple smgr* functions static
inline wrappers, as that doesn't require exposing f_smgr anymore.

From 880f25a543783f8dc3784a51ab1c29b72f6b5b27 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Fri, 7 Jun 2019 15:03:37 +0530
Subject: [PATCH 06/14] Defect and enhancement in multi-log support

That's imo not a good thing to have in patch series intended to be
reviewed, especially relatively early in the series. At least the commit
message ought to include an explanation.

Subject: [PATCH 07/14] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs. We
also provide the capability to fetch the undo records. This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.

Maybe "In each und log undo records are stored in sequential order."?

+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.

The reference to "undo log storage" kinda seems like a reference into
nothingness...

+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.

I'm not sure whta this is telling me. Who is "it"?

To me the filename ("interface"), and the title of this section,
suggests this provides documentation on how to write code to insert undo
records. But I don't think this does.

+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.

There's a lot of terminology in this file that's not been introduced. I
think this needs to be greatly expanded and restructured to allow people
unfamiliar with the code to benefit.

+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.

Is that actually true? There's records without tuples, no?

The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start
header.

Seems like this needs to reference different persistence levels,
otherwise it seems misleading, given there can be multiple first records
in multiple undo logs?

+ * This allows us to discard the entire transaction's log at one-shot
rather

s/at/in/

+ * than record-by-record. The callers are not aware of transaction header,

s/of/of the/

+ * this is entirely maintained and used by undo record layer. See

s/this/it/

+ * undorecord.h for detailed information about undo record header.

s/undo record/the undo record/

I think at the very least there's explanations missing for:
- what is the locking protocol for multiple buffers
- what are the contexts for insertion
- what phases an undo insertion happens in
- updating previous records in general
- what "packing" actually is

+
+/* Prototypes for static functions. */

Don't think we commonly include that...

+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+				 UndoRecPtr urp, RelFileNode rnode,
+				 UndoPersistence persistence,
+				 Buffer *prevbuf);
+static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+						  int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+									UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode, BlockNumber blk,
+				  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoPersistence upersistence);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};

Most files define datatypes before function prototypes, because
functions may reference the datatypes.

+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress

This doesn't really explain much. PrepareUpdateUndoActionProgress
doesnt' exist. I assume it's UndoRecordPrepareApplyProgress from 0012?

+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */

These comments seem redundant with the parameter names.

+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	XactUndoRecordInfo *xact_info;
+
+	xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Is there some byte of the urec_next on the current block, if not then
+	 * start from the next block.
+	 */

This comment needs rephrasing.

+	/* Loop until we have fetched all the buffers in which we need to write. */
+	while (size > 0)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}

So, this locks a very large number of undo buffers at the same time, do
I see that correctly? What guarantees that there are no deadlocks due
to multiple buffers locked at the same time (I guess the order inside
the log)? What guarantees that this is a small enough number that we can
even lock all of them at the same time?

Why do we need to lock all of them at the same time? That's not clear to
me.

Also, why do we need code to lock an unbounded number here? It seems
hard to imagine we'd ever want to update more than something around 8
bytes? Shouldn't that at the most require two buffers?

+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr xact_urp)

That name imo is confusing - it's not clear that it's not actually about
the next record or such.

+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend

*indicates

+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */

*the discard worker

+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoLogIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}

Ho, hum. I don't quite remember what we decided in the discussion about
not having to use the discard lock for this purpose.

+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_next);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the next pointer in xact_urec_info, this will be overwritten in
+	 * actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].next = urecptr;

What does "this will be overwritten mean"? It sounds like "context->xact_urec_info[index].next"
would be overwritten, but that can't be true.

+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&slot->discard_update_lock);
+}

Hm. Because you expect it to be blocked behind the content lwlocks for
the buffers?

+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.

It doesn't actually appear to insert any records. At least not a record
in the way the rest of the file uses that term?

+ * This must be called under the critical section.

s/under the/in a/

Think that should be asserted.

+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */

Why do we do offset calculations multiple times? Seems like all the
offsets, and the split, should be computed in exactly one place.

+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoPersistence persistence = context->alloc_context.persistence;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */

As the code stands, it's locked, not just pinned.

+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{

How large do we expect this to get at most?

+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											SMGR_UNDO,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);

Why is not locking the buffer correct here? Can't there be concurrent
reads during hot standby?

+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.

How can a function be called "before all the undo records"?

+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoPersistence persistence,
+					  int nprepared,
+					  XLogReaderState *xlog_record)

There definitely needs to be explanation about xlog_record. But also
about memory management etc. Looks like one e.g. can't call this from a
short lived memory context.

+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.

Again, how is deadlocking / max number of buffers handled, and why do
they all need to be locked at the same time?

+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  We'll only advance
+	 * by as many bytes as we turn out to need.
+	 */

Why can we only find this out by allocating? This seems like an API
deficiency of the storage layer to me. The information is in the und log
slot's metadata, no?

+	urec->uur_next = InvalidUndoRecPtr;
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.
+		 */
+		Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(txid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */

That's basically the same comment as before the if.

+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a

"is switch" is right.

+/*
+ * Insert a previously-prepared undo records.

s/a//

More tomorrow.

Greetings,

Andres Freund

#189Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#187)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 12:21 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Tue, Jul 30, 2019 at 5:03 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I think this idea is good for the DO time but during REDO time it will
not work as we will not have the transaction state. Having said that
the current idea of keeping in the global variable will also not work
during REDO time because the WAL from the different transaction can be
interleaved. There are few ideas to handle this issue

1. At DO time keep in TopTransactionState as you suggested and during
recovery time read from the first complete record on the page.
2. Just to keep the code uniform always read from the first complete
record of the page.

It contains another

suggestion for that problem you just mentioned (and also me pointing
out what you just pointed out, since I wrote it earlier) though I'm
not sure if it's better than your options above.

Thanks, Thomas for your review, Currently, I am replying to the
problem which both of us has identified and found a different set of
solutions. I will go through other comments soon and work on those.

+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+                  UnpackedUndoRecord *urec,
+                  Oid dbid)
+{
...
+    /* Fetch compression info for the transaction. */
+    compression_info = GetTopTransactionUndoCompressionInfo(category);

How can this work correctly in recovery? [Edit: it doesn't, as you
just pointed out]

One data structure that could perhaps hold this would be
UndoLogTableEntry (the per-backend cache, indexed by undo log number,
with pretty fast lookups; used for things like
UndoLogNumberGetCategory()). As long as you never want to have
inter-transaction compression, that should have the right scope to
give recovery per-undo log tracking. If you ever wanted to do
compression between transactions too, maybe UndoLogSlot could work,
but that'd have more complications.

I think this could be a good idea. I had thought of keeping in the
slot as my 3rd option but later I removed it thinking that we need to
expose the compression field to the undo log layer. I think keeping
in the UndoLogTableEntry is a better idea than keeping in the slot.
But, I still have the same problem that we need to expose undo
record-level fields to undo log layer to compute the cache entry size.
OTOH, If we decide to get from the first record of the page (as I
mentioned up thread) then I don't think there is any performance issue
because we are inserting on the same page. But, for doing that we
need to unpack the complete undo record (guaranteed to be on one
page). And, UnpackUndoData will internally unpack the payload data
as well which is not required in our case unless we change
UnpackUndoData such that it unpacks only what the caller wants (one
input parameter will do).

I am not sure out of these two which idea is better?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#190Thomas Munro
thomas.munro@gmail.com
In reply to: Amit Kapila (#185)
Re: POC: Cleaning up orphaned files using undo logs

Hi Amit

I've been testing some undo worker workloads (more on that soon), but
here's a small thing: I managed to reach an LWLock self-deadlock in
the undo worker launcher:

diff --git a/src/backend/access/undo/undorequest.c
b/src/backend/access/undo/undorequest.c
...
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
...
+       /* Search the queues under lock as they can be modified concurrently. */
+       LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
...
+                               RollbackHTRemoveEntry(rh->full_xid,
rh->start_urec_ptr);

^ but that function acquires the same lock, leading to:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
* frame #0: 0x00007fff5d110106 libsystem_kernel.dylib`semop + 10
frame #1: 0x0000000104bbf24c
postgres`PGSemaphoreLock(sema=0x000000010e216a08) at pg_sema.c:428:15
frame #2: 0x0000000104c90186
postgres`LWLockAcquire(lock=0x000000010e218300, mode=LW_EXCLUSIVE) at
lwlock.c:1246:4
frame #3: 0x000000010487463d
postgres`RollbackHTRemoveEntry(full_xid=(value = 89144),
start_urec_ptr=20890721090967) at undorequest.c:1717:2
frame #4: 0x0000000104873dbe
postgres`UndoGetWork(allow_peek=false, remove_from_queue=false,
urinfo=0x00007ffeeb4d3e30, in_other_db_out=0x0000000000000000) at
undorequest.c:1388:5
frame #5: 0x0000000104876211 postgres`UndoLauncherMain(main_arg=0)
at undoworker.c:607:7
...
(lldb) print held_lwlocks[0]
(LWLockHandle) $0 = {
lock = 0x000000010e218300
mode = LW_EXCLUSIVE
}

--
Thomas Munro
https://enterprisedb.com

#191Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#188)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 1:32 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

Amit, short note: The patches aren't attached in patch order. Obviously
a miniscule thing, but still nicer if that's not the case.

Noted, I will try to ensure that patches are in order in future posts.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#192Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#190)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 5:26 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Hi Amit

I've been testing some undo worker workloads (more on that soon),

One small point, there is one small bug in the error queues which is
that the element pushed into error queue doesn't have an updated value
of to_urec_ptr which is important to construct the hash key. This
will lead to undolauncher/worker think that the action for the same is
already processed and it removes the same from the hash table. I have
a fix for the same which I will share in next version of the patch
(which I am going to share in the next day or two).

but
here's a small thing: I managed to reach an LWLock self-deadlock in
the undo worker launcher:

I could see the problem, will fix in next version.

Thank you for reviewing and testing this.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#193Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#93)
14 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 16, 2019 at 2:09 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Jul 1, 2019 at 3:54 AM Thomas Munro <thomas.munro@gmail.com> wrote:

2. Introduced a new RMGR callback rm_undo_status. It is used to
decide when record sets in the UNDO_SHARED category should be
discarded (instead of the usual single xid-based rules). The possible
answers are "discard me now!", "ask me again when a given XID is all
visible", and "ask me again when a given XID is no longer running".

From the minor nitpicking department, the patches from this stack that
are updating rmgrlist.h are consistently failing to update the comment
line preceding the list of PG_RMGR() lines. This looks to be patches
0014 and 0015 in this stack; 0015 seems to need to be squashed into
0014.

Fixed. You can verify in patch
0011-Infrastructure-to-execute-pending-undo-actions. The 'mask' was
missing in the list as well which I have added here, we might want to
commit that separately.

Reviewing Amit's 0016:

performUndoActions appears to be badly-designed. For starters, it's
sometimes wrong: the only place it gets set to true is in
UndoActionsRequired (which is badly named, because from the name you
expect it to return a Boolean and to not have side effects, but
instead it doesn't return anything and does have side effects).
UndoActionsRequired() only gets called from selected places, like
AbortCurrentTransaction(), so the rest of the time it just returns a
wrong answer. Now maybe it's never called at those times, but there's
no guard to prevent a function like CanPerformUndoActions() (which is
also badly named, because performUndoActions tells you whether you
need to perform undo actions, not whether it's possible to perform
undo actions) from being called before the flag is set. I think that
this flag should be either (1) maintained eagerly - so that wherever
we set start_urec_ptr we also set the flag right away or (2) removed -
so when we need to know, we just loop over all of the undo categories
on the spot, which is not that expensive because there aren't that
many of them.

I have taken approach-2 to fix this.

It seems pointless to make PrepareTransaction() take undo pointers as
arguments, because those pointers are just extracted from the
transaction state, to which PrepareTransaction() has a pointer.

Fixed.

Thomas has already objected to another proposal to add functions that
turn 32-bit XIDs into 64-bit XIDs. Therefore, I feel confident in
predicting that he will likewise object to GetEpochForXid. I think
this needs to be changed somehow, maybe by doing what the XXX comment
you added suggests.

I will fix this later. I think we can separately write a patch to
extend Two-phase file to use fulltransactionid and then use it here.

This patch has some problems with naming consistency. There's a
function called PushUndoRequest() which calls a function called
RegisterRollbackReq() to do the heart of the work. So, is it undo or
rollback? Are we pushing or registering? Is it a request or a req?
For bonus points, the flag that the function sets is called
undo_req_pushed, which is halfway in between the two competing
terminologies. Other gripes about PushUndoRequest: push is vague and
doesn't really explain what's happening, "apllying" is a typo,
per_level is a poor variable name and shouldn't be declared volatile.
This function has problems with naming in other places, too; please go
through all of the names carefully and make them consistent and
adequately descriptive.

I have changed the namings to make them consistent. If you see
anything else, then do let me know.

I am not a fan of applying_subxact_undo. I think we should look for a
better design there. A couple of things occur to me. One is that we
don't necessarily need to go to FATAL; we could just force the current
transaction and all of its subtransactions fail all the way out to the
top level, but then perhaps allow new transactions to be started
afterwards. I'm not sure that's worth it, but it would work, and I
think it has precedent in SxactIsDoomed. Assuming we're going to stick
with the current FATAL plan, I think we should do something like
invent a new kind of critical section that forces ERROR to be promoted
to FATAL and then use it here. We could call it a semi-critical or
locally-critical section, and the undo machinery could use it, but
then also so could other things. I've wanted that sort of concept
before, so I think it's a good idea to try to have something general
and independent of undo. The same concept could be used in
PerformUndoActions() instead of having to invent
pg_rethrow_as_fatal(), so we'd have two uses for this mechanism right
away.

Okay, I have developed the concept of semi-critical section and used
it for sub-transactions and temp tables. Kindly check if this is
something that you have in mind?

FinishPreparedTransactions() tries to apply undo actions while
interrupts are still held. Is that necessary? Can we avoid it?

Fixed.

It seems highly likely that the logic added to the TBLOCK_SUBCOMMIT
case inside CommitTransactionCommand and also into
ReleaseCurrentSubTransaction should have been added to
CommitSubTransaction instead. If that's not true, then we have to
believe that the TBLOCK_SUBRELEASE call to CommitSubTransaction needs
different treatment from the other two cases, which sounds unlikely;
we also have to explain why undo is somehow different from all of
these other releases that are already handled in that function, not in
its callers. I also strongly suspect it is altogether wrong to do
this before CommitSubTransaction sets s->state to TRANS_COMMIT; what
if a subxact callback throws an error?

For related reasons, I don't think that the change ReleaseSavepoint()
are right either. Notice the header comment: "As above, we don't
actually do anything here except change blockState." The "as above"
part of the comment probably didn't originally refer to
DefineSavepoint(), which definitely does do other stuff, but to
something like EndImplicitTransactionBlock() or EndTransactionBlock(),
and DefineSavepoint() got stuck in the middle later. Anyway, your
patch makes the comment false by doing actual state changes in this
function, rather than just marking the subtransactions for commit.
But why should that be right? If none of the many other bits of state
are manipulated here rather than in CommitSubTransaction(), why is
undo the one thing that is different? I guess this is basically just
compensation for the lack of any of this code in the TBLOCK_SUBRELEASE
path which I noted in the previous paragraph, but I still think the
right answer is to put it all in CommitSubTransaction() *after* we set
TRANS_COMMIT.

Changed as per suggestion.

There are a number of things I either don't like or don't understand
about PerformUndoActions. One is that undo_req_pushed gets passed to
this function. That just looks really odd from an abstraction point
of view. Basically, we have a function whose job is to "perform undo
actions," and it gets a flag as an argument that tells it to not
actually perform some of the undo actions: that's odd. I think the
reason it's like that is because of the issue we've been discussing
elsewhere that there's a separate undo request for each category. If
you didn't have that, you wouldn't need to do this here. I'm not
saying that proves that the one-request-per-persistence-level design
is definitely wrong, but this is certainly not a point in its favor,
at least IMHO.

I think we have discussed in detail about
one-request-per-persistence-level design and I will investigate it to
see if we can make it one-request-per-transaction and if not what are
the challenges and can we overcome them without significantly more
work and complexity. So for now, I have not changed anything related
to this point.

Apart from these comments, I have changed a few more things:
a. Changed TWOPHASE_MAGIC define as we are changing TwoPhaseFileHeader.
b. Fixed comments by Dilip on same patch [1]/messages/by-id/CAFiTN-tObs5BQZETqK12QuOz7nPSXb90PdG49AzK2ZJ4ts1c5g@mail.gmail.com. I will respond to them
separately.
c. Fixed the problem reported by Thomas [2]/messages/by-id/CA+hUKGLv016-1y=Cwx+mme+cFRD5Bn03=2JVFnRB7JMLsA35=w@mail.gmail.com and one similar problem in
an error queue noticed by me.

I have still not addressed all the comments raised. This is mainly to
unblock Thomas's test and share whatever is done until now. I am
posting all the patches, but have not modified anything related to
undo-log and undo-interface patches (aka from 0001 to 0008).

[1]: /messages/by-id/CAFiTN-tObs5BQZETqK12QuOz7nPSXb90PdG49AzK2ZJ4ts1c5g@mail.gmail.com
[2]: /messages/by-id/CA+hUKGLv016-1y=Cwx+mme+cFRD5Bn03=2JVFnRB7JMLsA35=w@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0001-Move-some-md.c-specific-logic-from-smgr.c-to-md.c.patchapplication/octet-stream; name=0001-Move-some-md.c-specific-logic-from-smgr.c-to-md.c.patchDownload
From dfd0121dc73aab491bcaad2d2b7a2a749389add8 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Wed, 17 Jul 2019 12:14:08 +1200
Subject: [PATCH 01/13] Move some md.c-specific logic from smgr.c to md.c.

Potential future SMGR implementations may not want to create
tablespace directories when creating an SMGR relation.  Move that
logic to mdcreate().  Move the initialization of md-specific
data structures from smgropen() to a new callback mdopen().

Author: Thomas Munro
Reviewed-by: Shawn Debnath (as part of an earlier patch set)
Discussion: https://postgr.es/m/CA%2BhUKG%2BOZqOiOuDm5tC5DyQZtJ3FH4%2BFSVMqtdC4P1atpJ%2Bqhg%40mail.gmail.com
---
 src/backend/storage/smgr/md.c   | 37 +++++++++++++++++++++++++++++++------
 src/backend/storage/smgr/smgr.c | 33 ++++-----------------------------
 src/include/storage/md.h        |  1 +
 3 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 58c94e9257..52136ad558 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -28,6 +28,7 @@
 #include "miscadmin.h"
 #include "access/xlogutils.h"
 #include "access/xlog.h"
+#include "commands/tablespace.h"
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
@@ -120,7 +121,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 /* local routines */
 static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum,
 						 bool isRedo);
-static MdfdVec *mdopen(SMgrRelation reln, ForkNumber forknum, int behavior);
+static MdfdVec *mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior);
 static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum,
 								   MdfdVec *seg);
 static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum,
@@ -165,7 +166,7 @@ mdexists(SMgrRelation reln, ForkNumber forkNum)
 	 */
 	mdclose(reln, forkNum);
 
-	return (mdopen(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
+	return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
 }
 
 /*
@@ -185,6 +186,19 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 
 	Assert(reln->md_num_open_segs[forkNum] == 0);
 
+	/*
+	 * We may be using the target table space for the first time in this
+	 * database, so create a per-database subdirectory if needed.
+	 *
+	 * XXX this is a fairly ugly violation of module layering, but this seems
+	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
+	 * should be here and not in commands/tablespace.c?  But that would imply
+	 * importing a lot of stuff that smgr.c oughtn't know, either.
+	 */
+	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
+							reln->smgr_rnode.node.dbNode,
+							isRedo);
+
 	path = relpath(reln->smgr_rnode, forkNum);
 
 	fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
@@ -425,7 +439,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 }
 
 /*
- *	mdopen() -- Open the specified relation.
+ *	mdopenfork() -- Open one fork of the specified relation.
  *
  * Note we only open the first segment, when there are multiple segments.
  *
@@ -435,7 +449,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
  * invent one out of whole cloth.
  */
 static MdfdVec *
-mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
+mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 {
 	MdfdVec    *mdfd;
 	char	   *path;
@@ -474,6 +488,17 @@ mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
 	return mdfd;
 }
 
+/*
+ *  mdopen() -- Initialize newly-opened relation.
+ */
+void
+mdopen(SMgrRelation reln)
+{
+	/* mark it not open */
+	for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++)
+		reln->md_num_open_segs[forknum] = 0;
+}
+
 /*
  *	mdclose() -- Close the specified relation, if it isn't closed already.
  */
@@ -713,7 +738,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 BlockNumber
 mdnblocks(SMgrRelation reln, ForkNumber forknum)
 {
-	MdfdVec    *v = mdopen(reln, forknum, EXTENSION_FAIL);
+	MdfdVec    *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
 	BlockNumber nblocks;
 	BlockNumber segno = 0;
 
@@ -1137,7 +1162,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1];
 	else
 	{
-		v = mdopen(reln, forknum, behavior);
+		v = mdopenfork(reln, forknum, behavior);
 		if (!v)
 			return NULL;		/* if behavior & EXTENSION_RETURN_NULL */
 	}
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c397fe..b0d9f21e68 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,7 +17,6 @@
  */
 #include "postgres.h"
 
-#include "commands/tablespace.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -41,6 +40,7 @@ typedef struct f_smgr
 {
 	void		(*smgr_init) (void);	/* may be NULL */
 	void		(*smgr_shutdown) (void);	/* may be NULL */
+	void		(*smgr_open) (SMgrRelation reln);
 	void		(*smgr_close) (SMgrRelation reln, ForkNumber forknum);
 	void		(*smgr_create) (SMgrRelation reln, ForkNumber forknum,
 								bool isRedo);
@@ -68,6 +68,7 @@ static const f_smgr smgrsw[] = {
 	{
 		.smgr_init = mdinit,
 		.smgr_shutdown = NULL,
+		.smgr_open = mdopen,
 		.smgr_close = mdclose,
 		.smgr_create = mdcreate,
 		.smgr_exists = mdexists,
@@ -170,8 +171,6 @@ smgropen(RelFileNode rnode, BackendId backend)
 	/* Initialize it if not present before */
 	if (!found)
 	{
-		int			forknum;
-
 		/* hash_search already filled in the lookup key */
 		reln->smgr_owner = NULL;
 		reln->smgr_targblock = InvalidBlockNumber;
@@ -179,9 +178,8 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = 0;	/* we only have md.c at present */
 
-		/* mark it not open */
-		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
-			reln->md_num_open_segs[forknum] = 0;
+		/* implementation-specific initialization */
+		smgrsw[reln->smgr_which].smgr_open(reln);
 
 		/* it has no owner yet */
 		dlist_push_tail(&unowned_relns, &reln->node);
@@ -330,33 +328,10 @@ smgrclosenode(RelFileNodeBackend rnode)
  *		Given an already-created (but presumably unused) SMgrRelation,
  *		cause the underlying disk file or other storage for the fork
  *		to be created.
- *
- *		If isRedo is true, it is okay for the underlying file to exist
- *		already because we are in a WAL replay sequence.
  */
 void
 smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
 {
-	/*
-	 * Exit quickly in WAL replay mode if we've already opened the file. If
-	 * it's open, it surely must exist.
-	 */
-	if (isRedo && reln->md_num_open_segs[forknum] > 0)
-		return;
-
-	/*
-	 * We may be using the target table space for the first time in this
-	 * database, so create a per-database subdirectory if needed.
-	 *
-	 * XXX this is a fairly ugly violation of module layering, but this seems
-	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
-	 * should be here and not in commands/tablespace.c?  But that would imply
-	 * importing a lot of stuff that smgr.c oughtn't know, either.
-	 */
-	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
-							reln->smgr_rnode.node.dbNode,
-							isRedo);
-
 	smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
 }
 
diff --git a/src/include/storage/md.h b/src/include/storage/md.h
index df24b93161..c0f05e23ff 100644
--- a/src/include/storage/md.h
+++ b/src/include/storage/md.h
@@ -21,6 +21,7 @@
 
 /* md storage manager functionality */
 extern void mdinit(void);
+extern void mdopen(SMgrRelation reln);
 extern void mdclose(SMgrRelation reln, ForkNumber forknum);
 extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
 extern bool mdexists(SMgrRelation reln, ForkNumber forknum);
-- 
2.16.2.windows.1

0002-Prepare-to-support-multiple-SMGR-implementations.patchapplication/octet-stream; name=0002-Prepare-to-support-multiple-SMGR-implementations.patchDownload
From e3f1fb93eb7a8c5fad276c7dd6d82087135ae015 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 17 Jul 2019 14:15:27 +1200
Subject: [PATCH 02/13] Prepare to support multiple SMGR implementations.

Move the "which" decision into a function that later patches can add
to.

Author: Thomas Munro
---
 src/backend/storage/smgr/smgr.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index b0d9f21e68..d00b275e46 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -97,6 +97,18 @@ static dlist_head unowned_relns;
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
 
+/*
+ * Which implementation should handle a given RelFileNode?
+ */
+static int
+smgrwhich(RelFileNode rnode)
+{
+	switch (rnode.dbNode)
+	{
+		default:
+			return 0;			/* md.c */
+	}
+}
 
 /*
  *	smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -176,7 +188,7 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_targblock = InvalidBlockNumber;
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
-		reln->smgr_which = 0;	/* we only have md.c at present */
+		reln->smgr_which = smgrwhich(rnode);
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
-- 
2.16.2.windows.1

0003-Add-undo-log-manager.patchapplication/octet-stream; name=0003-Add-undo-log-manager.patchDownload
From 3da534dc6c28b2b3127d7400ab524f12b9faa0ae Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 6 Mar 2019 16:46:04 +1300
Subject: [PATCH 03/13] Add undo log manager.

Add a new subsystem to manage undo logs.  Undo logs allow data to be appended
efficiently, like logs.  They also allow data to be discarded efficiently from
the other end, like a queue.  Thirdly, they allow efficient buffered random
access, like a relation.

Undo logs physically consist of a set of 1MB segment files under
$PGDATA/base/undo (or per-tablespace equivalent) that are created, deleted or
renamed as required, similarly to the way that WAL segments are managed.
Meta-data about the set of undo logs is stored in shared memory, and written
to per-checkpoint files under $PGDATA/pg_undo.

Provide access to the undo files managed by undolog.c through bufmgr.c.
A new SMGR implementation allows bufmgr.c to access files created by
undolog.c.

Author: Thomas Munro, with contributions from Dilip Kumar, Rafia Sabih,
        Robert Haas and Amit Kapila
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ%40mail.gmail.com
---
 src/backend/access/Makefile               |    2 +-
 src/backend/access/rmgrdesc/Makefile      |    2 +-
 src/backend/access/rmgrdesc/undologdesc.c |   81 +
 src/backend/access/transam/rmgr.c         |    1 +
 src/backend/access/transam/xlog.c         |    5 +
 src/backend/access/transam/xlogutils.c    |   70 +-
 src/backend/access/undo/Makefile          |   17 +
 src/backend/access/undo/undolog.c         | 2627 +++++++++++++++++++++++++++++
 src/backend/bootstrap/bootstrap.c         |    3 +
 src/backend/catalog/system_views.sql      |    4 +
 src/backend/commands/tablespace.c         |   23 +
 src/backend/postmaster/pgstat.c           |   21 +
 src/backend/replication/basebackup.c      |   18 +-
 src/backend/replication/logical/decode.c  |    1 +
 src/backend/storage/buffer/bufmgr.c       |   80 +-
 src/backend/storage/buffer/localbuf.c     |   43 +
 src/backend/storage/file/fd.c             |    3 +-
 src/backend/storage/ipc/ipci.c            |    3 +
 src/backend/storage/lmgr/lwlock.c         |    2 +
 src/backend/storage/lmgr/lwlocknames.txt  |    1 +
 src/backend/storage/smgr/Makefile         |    2 +-
 src/backend/storage/smgr/smgr.c           |   23 +
 src/backend/storage/smgr/undofile.c       |  422 +++++
 src/backend/storage/sync/sync.c           |    6 +
 src/backend/utils/init/postinit.c         |    2 +
 src/backend/utils/misc/guc.c              |   12 +
 src/bin/initdb/initdb.c                   |    2 +
 src/bin/pg_checksums/pg_checksums.c       |   23 +-
 src/bin/pg_resetwal/pg_resetwal.c         |   76 +
 src/bin/pg_upgrade/Makefile               |    2 +-
 src/bin/pg_upgrade/check.c                |   43 +
 src/bin/pg_upgrade/controldata.c          |   25 +
 src/bin/pg_upgrade/exec.c                 |    4 +
 src/bin/pg_upgrade/pg_upgrade.c           |    2 +
 src/bin/pg_upgrade/pg_upgrade.h           |    5 +
 src/bin/pg_upgrade/undo.c                 |  292 ++++
 src/bin/pg_waldump/rmgrdesc.c             |    1 +
 src/include/access/rmgrlist.h             |    1 +
 src/include/access/session.h              |    7 +
 src/include/access/undolog.h              |  489 ++++++
 src/include/access/undolog_xlog.h         |   62 +
 src/include/access/xlogutils.h            |   14 +
 src/include/catalog/database_internal.h   |   21 +
 src/include/catalog/pg_proc.dat           |    7 +
 src/include/pgstat.h                      |    7 +
 src/include/storage/bufmgr.h              |   14 +-
 src/include/storage/fd.h                  |    1 +
 src/include/storage/lwlock.h              |    5 +-
 src/include/storage/smgr.h                |    3 +
 src/include/storage/sync.h                |    3 +-
 src/include/storage/undofile.h            |   59 +
 src/include/utils/guc.h                   |    2 +
 src/test/regress/expected/rules.out       |   10 +
 53 files changed, 4613 insertions(+), 41 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undologdesc.c
 create mode 100644 src/backend/access/undo/Makefile
 create mode 100644 src/backend/access/undo/undolog.c
 create mode 100644 src/backend/storage/smgr/undofile.c
 create mode 100644 src/bin/pg_upgrade/undo.c
 create mode 100644 src/include/access/undolog.h
 create mode 100644 src/include/access/undolog_xlog.h
 create mode 100644 src/include/catalog/database_internal.h
 create mode 100644 src/include/storage/undofile.h

diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0880e0a8bb..bf6d3fa1bd 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -9,6 +9,6 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  table tablesample transam
+			  table tablesample transam undo
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 5514db1dda..91ad1ef8a3 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,6 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undologdesc.c b/src/backend/access/rmgrdesc/undologdesc.c
new file mode 100644
index 0000000000..f89fcb31c6
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undologdesc.c
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * undologdesc.c
+ *	  rmgr descriptor routines for access/undo/undolog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undologdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+
+void
+undolog_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDOLOG_CREATE)
+	{
+		xl_undolog_create *xlrec = (xl_undolog_create *) rec;
+
+		appendStringInfo(buf, "logno %u", xlrec->logno);
+	}
+	else if (info == XLOG_UNDOLOG_EXTEND)
+	{
+		xl_undolog_extend *xlrec = (xl_undolog_extend *) rec;
+
+		appendStringInfo(buf, "logno %u end " UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_DISCARD)
+	{
+		xl_undolog_discard *xlrec = (xl_undolog_discard *) rec;
+
+		appendStringInfo(buf, "logno %u discard " UndoLogOffsetFormat " end "
+						 UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->discard, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_SWITCH)
+	{
+		xl_undolog_switch *xlrec = (xl_undolog_switch *) rec;
+
+		appendStringInfo(buf, "logno %u start " UndoLogOffsetFormat " last " UndoLogOffsetFormat,
+						 xlrec->logno,
+						 xlrec->prevlog_xact_start,
+						 xlrec->prevlog_last_urp);
+	}
+
+}
+
+const char *
+undolog_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			id = "CREATE";
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			id = "EXTEND";
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			id = "DISCARD";
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			id = "SWITCH";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9368b56c4c..8b0537405a 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b6c9353cbd..5dbe485af2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/tuptoaster.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "access/xloginsert.h"
@@ -6710,6 +6711,9 @@ StartupXLOG(void)
 	 */
 	restoreTwoPhaseData();
 
+	/* Recover undo log meta data corresponding to this checkpoint. */
+	StartupUndoLogs(ControlFile->checkPointCopy.redo);
+
 	lastFullPageWrites = checkPoint.fullPageWrites;
 
 	RedoRecPtr = XLogCtl->RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
@@ -8977,6 +8981,7 @@ static void
 CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointCLOG();
+	CheckPointUndoLogs(checkPointRedo, ControlFile->checkPointCopy.redo);
 	CheckPointCommitTs();
 	CheckPointSUBTRANS();
 	CheckPointMultiXact();
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663bae6..c227c03854 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -293,6 +293,65 @@ XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
 										 false, buf);
 }
 
+/*
+ * Find the block ID of the first block that matches the given rnode forknum
+ * and blockno.  If blockno is InvalidBlockNumber, then match any block
+ * number.  Return true if found.
+ */
+bool
+XLogFindBlockId(XLogReaderState *record,
+				RelFileNode rnode,
+				ForkNumber forknum,
+				BlockNumber blockno,
+				uint8 *block_id)
+{
+	uint8	i;
+
+	for (i = 0; i <= record->max_block_id; ++i)
+	{
+		DecodedBkpBlock *block = &record->blocks[i];
+
+		if (block->in_use &&
+			RelFileNodeEquals(block->rnode, rnode) &&
+			block->forknum == forknum &&
+			(block->blkno == blockno || blockno == InvalidBlockNumber))
+		{
+			*block_id = i;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * If the caller doesn't know the the block_id, but does know the RelFileNode,
+ * forknum and block number, then we try to find it.
+ */
+XLogRedoAction
+XLogReadBufferForRedoBlock(XLogReaderState *record,
+						   RelFileNode rnode,
+						   ForkNumber forknum,
+						   BlockNumber blockno,
+						   ReadBufferMode mode,
+						   bool get_cleanup_lock,
+						   Buffer *buf)
+{
+	uint8  	block_id;
+
+	if (XLogFindBlockId(record, rnode, forknum, blockno, &block_id))
+		return XLogReadBufferForRedoExtended(record,
+											 block_id,
+											 mode,
+											 get_cleanup_lock,
+											 buf);
+
+	elog(ERROR, "failed to find block reference rel %u/%u/%u, forknum = %u, block = %u",
+		 rnode.spcNode, rnode.dbNode, rnode.relNode, forknum, blockno);
+
+	return BLK_NOTFOUND;	/* not reached */
+}
+
 /*
  * Pin and lock a buffer referenced by a WAL record, for the purpose of
  * re-initializing it.
@@ -346,7 +405,8 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	 * Make sure that if the block is marked with WILL_INIT, the caller is
 	 * going to initialize it. And vice versa.
 	 */
-	zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
+	zeromode = (mode == RBM_ZERO || mode == RBM_ZERO_AND_LOCK ||
+				mode == RBM_ZERO_AND_CLEANUP_LOCK);
 	willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
 	if (willinit && !zeromode)
 		elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
@@ -462,7 +522,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	{
 		/* page exists in file */
 		buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
-										   mode, NULL);
+										   mode, NULL, RELPERSISTENCE_PERMANENT);
 	}
 	else
 	{
@@ -487,7 +547,8 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 				ReleaseBuffer(buffer);
 			}
 			buffer = ReadBufferWithoutRelcache(rnode, forknum,
-											   P_NEW, mode, NULL);
+											   P_NEW, mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
 		/* Handle the corner case that P_NEW returns non-consecutive pages */
@@ -497,7 +558,8 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
 			buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
-											   mode, NULL);
+											   mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 	}
 
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
new file mode 100644
index 0000000000..219c6963cf
--- /dev/null
+++ b/src/backend/access/undo/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/undo
+#
+# IDENTIFICATION
+#    src/backend/access/undo/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/undo
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = undolog.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
new file mode 100644
index 0000000000..f2e0272ab1
--- /dev/null
+++ b/src/backend/access/undo/undolog.c
@@ -0,0 +1,2627 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.c
+ *	  management of undo logs
+ *
+ * PostgreSQL undo log manager.  This module is responsible for managing the
+ * lifecycle of undo logs and their segment files, associating undo logs with
+ * backends, and allocating space within undo logs.
+ *
+ * For the code that reads and writes blocks of data, see undofile.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undolog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/session.h"
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogreader.h"
+#include "access/xlogutils.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablespace.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "pgstat.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "storage/standby.h"
+#include "storage/sync.h"
+#include "storage/undofile.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/varlena.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+/*
+ * Main control structure for undo log management in shared memory.
+ * UndoLogSlot objects are arranged in a fixed-size array, with no particular
+ * ordering.
+ */
+typedef struct UndoLogSharedData
+{
+	UndoLogNumber	free_lists[UndoLogCategories];
+	UndoLogNumber	low_logno;
+	UndoLogNumber	next_logno;
+	UndoLogNumber	nslots;
+	UndoLogSlot		slots[FLEXIBLE_ARRAY_MEMBER];
+} UndoLogSharedData;
+
+/* The shared memory region that all backends are attach to. */
+UndoLogSharedData *UndoLogShared;
+
+undologtable_hash *undologtable_cache;
+
+/* GUC variables */
+char	   *undo_tablespaces = NULL;
+
+static UndoLogSlot *find_undo_log_slot(UndoLogNumber logno, bool locked);
+static UndoLogSlot *allocate_undo_log_slot(void);
+static void free_undo_log_slot(UndoLogSlot *log);
+static void attach_undo_log(UndoLogCategory category, Oid tablespace);
+static void detach_current_undo_log(UndoLogCategory category, bool full);
+static void extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end);
+static void undo_log_before_exit(int code, Datum value);
+static void forget_undo_buffers(int logno, UndoLogOffset old_discard,
+								UndoLogOffset new_discard,
+								bool drop_tail);
+static bool choose_undo_tablespace(bool force_detach, Oid *oid);
+
+PG_FUNCTION_INFO_V1(pg_stat_get_undo_logs);
+
+/*
+ * How many undo logs can be active at a time?  This creates a theoretical
+ * maximum amount of undo data that can exist, but if we set it to a multiple
+ * of the maximum number of backends it will be a very high limit.
+ * Alternative designs involving demand paging or dynamic shared memory could
+ * remove this limit but would be complicated.
+ */
+static inline size_t
+UndoLogNumSlots(void)
+{
+	return MaxBackends * 4;
+}
+
+/*
+ * Return the amount of traditional shmem required for undo log management.
+ */
+Size
+UndoLogShmemSize(void)
+{
+	return sizeof(UndoLogSharedData) +
+		UndoLogNumSlots() * sizeof(UndoLogSlot);
+}
+
+/*
+ * Initialize the undo log subsystem.  Called in each backend.
+ */
+void
+UndoLogShmemInit(void)
+{
+	bool found;
+
+	UndoLogShared = (UndoLogSharedData *)
+		ShmemInitStruct("UndoLogShared", UndoLogShmemSize(), &found);
+
+	/* The postmaster initialized the shared memory state. */
+	if (!IsUnderPostmaster)
+	{
+		int		i;
+
+		Assert(!found);
+
+		/*
+		 * We start with no active undo logs.  StartUpUndoLogs() will recreate
+		 * the undo logs that were known at the last checkpoint.
+		 */
+		memset(UndoLogShared, 0, sizeof(*UndoLogShared));
+		UndoLogShared->nslots = UndoLogNumSlots();
+		for (i = 0; i < UndoLogCategories; ++i)
+			UndoLogShared->free_lists[i] = InvalidUndoLogNumber;
+		for (i = 0; i < UndoLogShared->nslots; ++i)
+		{
+			memset(&UndoLogShared->slots[i], 0, sizeof(UndoLogShared->slots[i]));
+			UndoLogShared->slots[i].logno = InvalidUndoLogNumber;
+			LWLockInitialize(&UndoLogShared->slots[i].mutex,
+							 LWTRANCHE_UNDOLOG);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
+							 LWTRANCHE_UNDODISCARD);
+		}
+	}
+	else
+		Assert(found);
+
+	/* All backends prepare their per-backend lookup table. */
+	undologtable_cache = undologtable_create(TopMemoryContext,
+											 UndoLogNumSlots(),
+											 NULL);
+}
+
+void
+UndoLogInit(void)
+{
+	before_shmem_exit(undo_log_before_exit, 0);
+}
+
+/*
+ * Figure out which directory holds an undo log based on tablespace.
+ */
+void
+UndoLogDirectory(Oid tablespace, char *dir)
+{
+	if (tablespace == DEFAULTTABLESPACE_OID ||
+		tablespace == InvalidOid)
+		snprintf(dir, MAXPGPATH, "base/undo");
+	else
+		snprintf(dir, MAXPGPATH, "pg_tblspc/%u/%s/undo",
+				 tablespace, TABLESPACE_VERSION_DIRECTORY);
+}
+
+/*
+ * Compute the pathname to use for an undo log segment file.
+ */
+void
+UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace, char *path)
+{
+	char		dir[MAXPGPATH];
+
+	/* Figure out which directory holds the segment, based on tablespace. */
+	UndoLogDirectory(tablespace, dir);
+
+	/*
+	 * Build the path from log number and offset.  The pathname is the
+	 * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+	 * period inserted between the components.
+	 */
+	snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+			 segno * UndoLogSegmentSize);
+}
+
+/*
+ * Iterate through the set of currently active logs.  Pass in NULL to get the
+ * first undo log.  NULL indicates the end of the set of logs.  The caller
+ * must lock the returned log before accessing its members, and must skip if
+ * logno is not valid.
+ */
+UndoLogSlot *
+UndoLogNextSlot(UndoLogSlot *slot)
+{
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+	for (;;)
+	{
+		/* Advance to the next log. */
+		if (slot == NULL)
+		{
+			/* Start at the beginning. */
+			slot = &UndoLogShared->slots[0];
+		}
+		else if (++slot == &UndoLogShared->slots[UndoLogShared->nslots])
+		{
+			/* Past the end. */
+			slot = NULL;
+			break;
+		}
+		/* Have we found a slot with a valid log? */
+		if (slot->logno != InvalidUndoLogNumber)
+			break;
+	}
+	LWLockRelease(UndoLogLock);
+
+	/* XXX: erm, which lock should the caller hold!? */
+	return slot;
+}
+
+/*
+ * Check if an undo log position has been discarded.  'pointer' must be an
+ * undo log pointer that was allocated at some point in the past, otherwise
+ * the result is undefined.
+ */
+bool
+UndoLogRecPtrIsDiscardedSlowPath(UndoRecPtr pointer)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(pointer);
+	UndoLogSlot *slot;
+	UndoRecPtr discard;
+
+	slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL)
+	{
+		/*
+		 * If we couldn't find the undo log number, then it must be entirely
+		 * discarded.  Set this backend's recent_discard value to the highest
+		 * possible value, so that all records appear to be discarded to the
+		 * fast-path code.  Technically this value is too low by 1, but
+		 * assuming only pointers to records are tested, and no record can
+		 * have size 1, this value suffices.
+		 */
+		discard = MakeUndoRecPtr(logno, UndoLogMaxSize - 1);
+	}
+	else
+	{
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if (unlikely(logno != slot->logno))
+		{
+			/*
+			 * The undo log has been entirely discarded since we looked it up
+			 * above, and the UndoLogSlot is now unused or being used for some
+			 * other undo log.  This is the same as not finding it.
+			 */
+			discard = MakeUndoRecPtr(logno, UndoLogMaxSize - 1);
+		}
+		else
+			discard = MakeUndoRecPtr(logno, slot->meta.discard);
+		LWLockRelease(&slot->mutex);
+	}
+
+	/*
+	 * Remember this discard pointer in this backend so that future lookups
+	 * via UndoLogRecPtrIsDiscarded() have a chance of avoiding the slow path.
+	 */
+	UndoLogGetTableEntry(logno)->recent_discard = discard;
+
+	return pointer < discard;
+}
+
+/*
+ * Fetch the previous transaction's start undo record point.
+ */
+UndoRecPtr
+UndoLogGetLastXactStartPoint(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	uint64 last_xact_start = 0;
+
+	if (unlikely(slot == NULL))
+		return InvalidUndoRecPtr;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: review */
+	last_xact_start = slot->meta.unlogged.last_xact_start;
+	LWLockRelease(&slot->mutex);
+
+	if (last_xact_start == 0)
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, last_xact_start);
+}
+
+/*
+ * Detach from the undo log we are currently attached to, returning it to the
+ * appropriate free list if it still has space.
+ */
+static void
+detach_current_undo_log(UndoLogCategory category, bool full)
+{
+	UndoLogSlot *slot;
+
+	slot = CurrentSession->attached_undo_slots[category];
+
+	Assert(slot != NULL);
+
+	CurrentSession->attached_undo_slots[category] = NULL;
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = InvalidPid;
+	slot->meta.unlogged.xid = InvalidTransactionId;
+	if (full)
+		slot->meta.status = UNDO_LOG_STATUS_FULL;
+	LWLockRelease(&slot->mutex);
+
+	/* Push back onto the appropriate free list, unless it's full. */
+	if (!full)
+	{
+		LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+		slot->next_free = UndoLogShared->free_lists[category];
+		UndoLogShared->free_lists[category] = slot->logno;
+		LWLockRelease(UndoLogLock);
+	}
+}
+
+/*
+ * Exit handler, detaching from all undo logs.
+ */
+static void
+undo_log_before_exit(int code, Datum arg)
+{
+	int		i;
+
+	if (!CurrentSession)
+		return;
+
+	for (i = 0; i < UndoLogCategories; ++i)
+	{
+		if (CurrentSession->attached_undo_slots[i] != NULL)
+			detach_current_undo_log(i, false);
+	}
+}
+
+/*
+ * Create a new empty segment file on disk for the byte starting at 'end'.
+ */
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+							UndoLogOffset end)
+{
+	struct stat	stat_buffer;
+	off_t	size;
+	char	path[MAXPGPATH];
+	void   *zeroes;
+	size_t	nzeroes = 8192;
+	int		fd;
+
+	UndoLogSegmentPath(logno, end / UndoLogSegmentSize, tablespace, path);
+
+	/*
+	 * Create and fully allocate a new file.  If we crashed and recovered
+	 * then the file might already exist, so use flags that tolerate that.
+	 * It's also possible that it exists but is too short, in which case
+	 * we'll write the rest.  We don't really care what's in the file, we
+	 * just want to make sure that the filesystem has allocated physical
+	 * blocks for it, so that non-COW filesystems will report ENOSPC now
+	 * rather than later when the space is needed and we'll avoid creating
+	 * files with holes.
+	 */
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0 && tablespace != 0)
+	{
+		char undo_path[MAXPGPATH];
+
+		/* Try creating the undo directory for this tablespace. */
+		UndoLogDirectory(tablespace, undo_path);
+		if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+		{
+			char	   *parentdir;
+
+			if (errno != ENOENT || !InRecovery)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+
+			/*
+			 * In recovery, it's possible that the tablespace directory
+			 * doesn't exist because a later WAL record removed the whole
+			 * tablespace.  In that case we create a regular directory to
+			 * stand in for it.  This is similar to the logic in
+			 * TablespaceCreateDbspace().
+			 */
+
+			/* create two parents up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			/* create one parent up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+		}
+
+		fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	}
+	if (fd < 0)
+		elog(ERROR, "could not create new file \"%s\": %m", path);
+	if (fstat(fd, &stat_buffer) < 0)
+		elog(ERROR, "could not stat \"%s\": %m", path);
+	size = stat_buffer.st_size;
+
+	/* A buffer full of zeroes we'll use to fill up new segment files. */
+	zeroes = palloc0(nzeroes);
+
+	while (size < UndoLogSegmentSize)
+	{
+		ssize_t written;
+
+		written = write(fd, zeroes, Min(nzeroes, UndoLogSegmentSize - size));
+		if (written < 0)
+			elog(ERROR, "cannot initialize undo log segment file \"%s\": %m",
+				 path);
+		size += written;
+	}
+
+	/* Flush the contents of the file to disk before the next checkpoint. */
+	undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
+
+	CloseTransientFile(fd);
+
+	pfree(zeroes);
+
+	elog(DEBUG1, "created undo segment \"%s\"", path);
+}
+
+/*
+ * Create a new undo segment, when it is unexpectedly not present.
+ */
+void
+UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno)
+{
+	Assert(InRecovery);
+	allocate_empty_undo_segment(logno, tablespace, segno * UndoLogSegmentSize);
+}
+
+/*
+ * Create and zero-fill a new segment for a given undo log number.
+ */
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+	UndoLogSlot *slot;
+	size_t		end;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/* TODO review interlocking */
+
+	Assert(slot != NULL);
+	Assert(slot->meta.end % UndoLogSegmentSize == 0);
+	Assert(new_end % UndoLogSegmentSize == 0);
+	Assert(InRecovery ||
+		   CurrentSession->attached_undo_slots[slot->meta.category] == slot);
+
+	/*
+	 * Create all the segments needed to increase 'end' to the requested
+	 * size.  This is quite expensive, so we will try to avoid it completely
+	 * by renaming files into place in UndoLogDiscard() instead.
+	 */
+	end = slot->meta.end;
+	while (end < new_end)
+	{
+		allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/*
+	 * If we're not in recovery, we need to WAL-log the creation of the new
+	 * file(s).  We do that after the above filesystem modifications, in
+	 * violation of the data-before-WAL rule as exempted by
+	 * src/backend/access/transam/README.  This means that it's possible for
+	 * us to crash having made some or all of the filesystem changes but
+	 * before WAL logging, but in that case we'll eventually try to create the
+	 * same segment(s) again, which is tolerated.
+	 */
+	if (!InRecovery)
+	{
+		xl_undolog_extend xlrec;
+		XLogRecPtr	ptr;
+
+		xlrec.logno = logno;
+		xlrec.end = end;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+		XLogFlush(ptr);
+	}
+
+	/*
+	 * We didn't need to acquire the mutex to read 'end' above because only
+	 * we write to it.  But we need the mutex to update it, because the
+	 * checkpointer might read it concurrently.
+	 *
+	 * XXX It's possible for meta.end to be higher already during
+	 * recovery, because of the timing of a checkpoint; in that case we did
+	 * nothing above and we shouldn't update shmem here.  That interaction
+	 * needs more analysis.
+	 */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (slot->meta.end < end)
+		slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * This function must be called before all of the undo log activity that will
+ * be covered by a single WAL record.
+ */
+void
+UndoLogBeginInsert(UndoLogAllocContext *context,
+				   UndoLogCategory category,
+				   XLogReaderState *xlog_record)
+{
+	context->try_location = InvalidUndoRecPtr;
+	context->category = category;
+
+	/*
+	 * Tell UndoLogAllocate() to capture undo log meta-data before-change
+	 * images, so that UndoLogRegister() can find them and they can be written
+	 * to the WAL once per checkpoint.
+	 */
+	context->num_meta_data_images = 0;
+
+	/*
+	 * Tell UndoLogAllocateInRecovery() that we don't know which undo log to
+	 * allocate in yet, and to start its search for registered blocks at
+	 * the lowest-numbered block_id.
+	 */
+	context->xlog_record = xlog_record;
+	context->recovery_logno = InvalidUndoLogNumber;
+	context->recovery_block_id = 0;
+
+	/*
+	 * For UNDO_SHARED, this always denotes the beginning of a new record set.
+	 * For other categories, the boundaries are detected by transaction ID
+	 * changes.
+	 */
+	context->new_shared_record_set = category == UNDO_SHARED;
+}
+
+/*
+ * Get an insertion point that is guaranteed to be backed by enough space to
+ * hold 'size' bytes of data.  To actually write into the undo log, client
+ * code should call this first and then use bufmgr routines to access buffers
+ * and provide WAL logs and redo handlers.  In other words, while this module
+ * looks after making sure the undo log has sufficient space and the undo meta
+ * data is crash safe, the *contents* of the undo log and (indirectly) the
+ * insertion point are the responsibility of client code.
+ *
+ * A suggested insertion point can optionally be passed in as 'try_location',
+ * and will be returned if possible.  If not InvalidUndoRecPtr, it must fall
+ * with, or exactly one byte after, the most recent allocation for the same
+ * persistence level.  This interface allows for a series of allocation to be
+ * made without committing to using the space yet; call UndoLogAdvance() to
+ * actually advance the insert pointer.
+ *
+ * Return an undo log insertion point that can be converted to a buffer tag
+ * and an insertion point within a buffer page.
+ */
+UndoRecPtr
+UndoLogAllocate(UndoLogAllocContext *context,
+				uint16 size,
+				bool *need_xact_header,
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
+{
+	Session *session = CurrentSession;
+	UndoLogSlot *slot;
+	UndoLogOffset new_insert;
+	TransactionId logxid;
+
+	slot = CurrentSession->attached_undo_slots[context->category];
+
+	/*
+	 * We may need to attach to an undo log, either because this is the first
+	 * time this backend as needed to write to an undo log at all or because
+	 * the undo_tablespaces GUC was changed.  When doing that, we'll need
+	 * interlocking against tablespaces being concurrently dropped.
+	 */
+
+ retry:
+	/* See if we need to check the undo_tablespaces GUC. */
+	if (unlikely(session->need_to_choose_undo_tablespace || slot == NULL))
+	{
+		Oid		tablespace;
+		bool	need_to_unlock;
+
+		need_to_unlock =
+			choose_undo_tablespace(session->need_to_choose_undo_tablespace,
+								   &tablespace);
+		attach_undo_log(context->category, tablespace);
+		if (need_to_unlock)
+			LWLockRelease(TablespaceCreateLock);
+		slot = CurrentSession->attached_undo_slots[context->category];
+		session->need_to_choose_undo_tablespace = false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	logxid = slot->meta.unlogged.xid;
+
+	if (logxid != GetTopTransactionId())
+	{
+		/*
+		 * While we have the lock, check if we have been forcibly detached by
+		 * DROP TABLESPACE.  That can only happen between transactions (see
+		 * DropUndoLogsInsTablespace()).
+		 */
+		if (slot->pid == InvalidPid)
+		{
+			LWLockRelease(&slot->mutex);
+			slot = NULL;
+			goto retry;
+		}
+		/* Record that we are attached to this log. */
+		slot->meta.unlogged.xid = GetTopTransactionId();
+		/*
+		 * Maintain our tracking of the and the previous transaction start
+		 * locations.
+		 */
+		if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+		{
+			slot->meta.unlogged.last_xact_start =
+				slot->meta.unlogged.this_xact_start;
+			slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+		}
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * 'size' is expressed in usable non-header bytes.  Figure out how far we
+	 * have to move insert to create space for 'size' usable bytes, stepping
+	 * over any intervening headers.
+	 */
+	Assert(slot->meta.unlogged.insert % BLCKSZ >= UndoLogBlockHeaderSize);
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+
+		Assert(UndoRecPtrGetLogNo(context->try_location) == slot->logno);
+		Assert(try_offset <= slot->meta.end);
+		new_insert = UndoLogOffsetPlusUsableBytes(try_offset, size);
+	}
+	else
+	{
+		new_insert = UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert,
+												  size);
+	}
+	Assert(new_insert % BLCKSZ >= UndoLogBlockHeaderSize);
+
+	/*
+	 * We don't need to acquire log->mutex to read log->meta.insert and
+	 * log->meta.end, because this backend is the only one that can
+	 * modify them.
+	 */
+	if (unlikely(new_insert > slot->meta.end))
+	{
+		if (new_insert > UndoLogMaxSize)
+		{
+			/* This undo log is entirely full.  Get a new one. */
+			if (logxid == GetTopTransactionId())
+			{
+				/*
+				 * If the same transaction is split over two undo logs then
+				 * store the previous log number in new log.  See detailed
+				 * comments in undorecord.c file header.
+				 */
+				*prevlog_xact_start =
+					MakeUndoRecPtr(slot->logno,
+								   slot->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+			}
+			elog(DEBUG1, "undo log %u is full, switching to a new one", slot->logno);
+			slot = NULL;
+			detach_current_undo_log(context->category, true);
+			context->try_location = InvalidUndoRecPtr;
+			goto retry;
+		}
+		/*
+		 * Extend the end of this undo log to cover new_insert (in other words
+		 * round up to the segment size).
+		 */
+		extend_undo_log(slot->logno,
+						new_insert + UndoLogSegmentSize -
+						new_insert % UndoLogSegmentSize);
+		Assert(new_insert <= slot->meta.end);
+	}
+
+	/*
+	 * Create a back-up image of the unlogged part of the undo log's
+	 * meta-data, if we haven't already done so since UndoLogBeginInsert() (ie
+	 * for the WAL record that this undo allocation will be replayed by).
+	 */
+	if (context->num_meta_data_images == 0 ||
+		context->meta_data_images[context->num_meta_data_images - 1].logno != slot->logno)
+	{
+		if (context->num_meta_data_images >= MAX_META_DATA_IMAGES)
+			elog(ERROR, "too many undo log meta data images");
+		context->meta_data_images[context->num_meta_data_images].logno = slot->logno;
+		context->meta_data_images[context->num_meta_data_images++].data = slot->meta.unlogged;
+	}
+
+	/*
+	 * If no try_location was passed in, or if we switched logs, then we'll
+	 * return the current insertion point.
+	 */
+	if (context->try_location == InvalidUndoRecPtr)
+		context->try_location = MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+
+	/*
+	 * Is this location the first in this undo log for a transaction or a
+	 * shared record set?
+	 */
+	if (context->new_shared_record_set)
+	{
+		context->new_shared_record_set = false;
+		*need_xact_header = true;
+	}
+	else
+	{
+		*need_xact_header =
+			UndoRecPtrGetOffset(context->try_location) ==
+			slot->meta.unlogged.this_xact_start;
+	}
+	*last_xact_start =
+		MakeUndoRecPtr(slot->logno, slot->meta.unlogged.last_xact_start);
+
+	return context->try_location;
+}
+
+void
+UndoLogRegister(UndoLogAllocContext *context, uint8 block_id, UndoLogNumber logno)
+{
+	int		i;
+
+	for (i = 0; i < context->num_meta_data_images; ++i)
+	{
+		if (context->meta_data_images[i].logno == logno)
+		{
+			XLogRegisterBufData(block_id,
+								(char *) &context->meta_data_images[i].data,
+								sizeof(context->meta_data_images[i].data));
+			return;
+		}
+	}
+}
+
+/*
+ * In recovery, we expect exactly the same sequence of allocation sizes, but
+ * we also need the WAL record that is being replayed so we can figure out
+ * where the undo space was allocated.
+ */
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+						  TransactionId xid,
+						  uint16 size,
+						  bool *need_xact_header,
+						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	Assert(InRecovery);
+
+	/*
+	 * Just as in UndoLogAllocate(), the caller may be extending an existing
+	 * allocation before committing with UndoLogAdvance().
+	 */
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+		UndoLogNumber logno = UndoRecPtrGetLogNo(context->try_location);
+
+		/*
+		 * You can only have a try_location on your second or later allocation
+		 * for a given WAL record.  It had better be in the same log as the
+		 * previous allocation for this WAL record (though it may not turn out
+		 * to have enough space, below).
+		 */
+		Assert(logno == context->recovery_logno);
+
+		/*
+		 * Any log extension triggered by UndoLogAllocate() must have been
+		 * replayed by now, so we can just check if this log has enough space,
+		 * and if so, return.
+		 */
+		slot = find_undo_log_slot(logno, false);
+		if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+		{
+			*need_xact_header = false;
+			return try_offset;
+		}
+
+		/* Full.  Ignore try_location and find the next log that was used. */
+		Assert(slot->meta.status == UNDO_LOG_STATUS_FULL);
+	}
+	else
+	{
+		/*
+		 * For now we only support one allocation per WAL record that doesn't
+		 * have a try_location (ie the first one).  We'll have to find out
+		 * which log was used first.
+		 */
+		Assert(context->recovery_logno == InvalidUndoLogNumber);
+	}
+
+	/*
+	 * In order to find the undo log that was used by UndoLogAllocate(), we
+	 * consult the list of registered blocks to figure out which undo logs
+	 * should be written to by this WAL record.
+	 */
+	while (context->recovery_block_id <= context->xlog_record->max_block_id)
+	{
+		DecodedBkpBlock *block;
+
+		/* We're looking for the first block referencing a new undo log. */
+		block = &context->xlog_record->blocks[context->recovery_block_id];
+		if (block->rnode.dbNode == UndoDbOid &&
+			block->rnode.relNode != context->recovery_logno)
+		{
+			UndoLogNumber logno = block->rnode.relNode;
+			const void *backup;
+			size_t backup_size;
+
+			/* We found a reference to a different (or first) undo log. */
+			slot = find_undo_log_slot(logno, false);
+
+			/*
+			 * Since on-line checkpoints capture an inconsistent snapshot of
+			 * undo log meta-data, we'll restore the unlogged part of the
+			 * meta-data image if one was attached to the WAL record (that is,
+			 * the members that don't have WAL records for every change
+			 * already).
+			 */
+			backup =
+				XLogRecGetBlockData(context->xlog_record,
+									context->recovery_block_id,
+									&backup_size);
+			if (unlikely(backup))
+			{
+				Assert(backup_size == sizeof(UndoLogUnloggedMetaData));
+
+				/* Restore the unlogged members from the backup-imaged. */
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				memcpy(&slot->meta.unlogged, backup, sizeof(UndoLogUnloggedMetaData));
+				LWLockRelease(&slot->mutex);
+			}
+			else
+			{
+				/*
+				 * Otherwise we need to do our own transaction tracking
+				 * whenever we see a new xid, to match the logic in
+				 * UndoLogAllocate().
+				 */
+				if (xid != slot->meta.unlogged.xid)
+				{
+					slot->meta.unlogged.xid = xid;
+					if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+						slot->meta.unlogged.last_xact_start =
+							slot->meta.unlogged.this_xact_start;
+					slot->meta.unlogged.this_xact_start =
+						slot->meta.unlogged.insert;
+				}
+			}
+
+			/* TODO: check locking against undo log slot recycling? */
+
+			/*
+			 * At this stage we should have an undo log that can handle this
+			 * allocation.  If we don't, something is screwed up.
+			 */
+			if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) > slot->meta.end)
+				elog(ERROR,
+					 "cannot allocate %d bytes in undo log %d",
+					 (int) size, slot->logno);
+
+			*need_xact_header =
+				context->try_location == InvalidUndoRecPtr &&
+				slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+			*last_xact_start = slot->meta.unlogged.last_xact_start;
+			context->recovery_logno = slot->logno;
+
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+
+			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
+			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+		}
+		++context->recovery_block_id;
+	}
+
+	/*
+	 * If we've run out of blocks to inspect, then we must have replayed a
+	 * different sequence of allocation sizes, or screwed up the
+	 * XLOG_UNDOLOG_EXTEND records, indicating a bug somewhere.
+	 */
+	elog(ERROR, "cannot determine undo log to allocate from");
+
+	return 0;		/* not reached */
+}
+
+/*
+ * Advance the insertion pointer in this context by 'size' usable (non-header)
+ * bytes.  This is the next place we'll try to allocate a record, if it fits.
+ * This is not committed to shared memory until after we've WAL-logged the
+ * record and UndoLogAdvanceFinal() is called.
+ */
+void
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+	context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+														 size);
+}
+
+/*
+ * Advance the insertion pointer to 'size' usable (non-header) bytes past
+ * insertion_point.
+ */
+void
+UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(insertion_point) ;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(InRecovery ||
+		   AmAttachedToUndoLogSlot(slot) ||
+		   slot->meta.status == UNDO_LOG_STATUS_FULL);
+
+	/*
+	 * The caller has the current insertion point, as returned by
+	 * UndoLogAllocate[InRecovery]().
+	 */
+	Assert(UndoRecPtrGetOffset(insertion_point) == slot->meta.unlogged.insert);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.insert =
+		UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size);
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * Advance the discard pointer in one undo log, discarding all undo data
+ * relating to one or more whole transactions.  The passed in undo pointer is
+ * the address of the oldest data that the called would like to keep, and the
+ * affected undo log is implied by this pointer, ie
+ * UndoRecPtrGetLogNo(discard_pointer).
+ *
+ * The caller asserts that there will be no attempts to access the undo log
+ * region being discarded after this moment.  This operation will cause the
+ * relevant buffers to be dropped immediately, without writing any data out to
+ * disk.  Any attempt to read the buffers (except a partial buffer at the end
+ * of this range which will remain) may result in IO errors, because the
+ * underlying segment file may have been physically removed.
+ *
+ * Return true if the discard point was updated, and false if nothing was done
+ * because the log precending the given point was already discarded.
+ *
+ * TODO: The return value is not yet reliable and the code still doesn't work
+ * correctly if called for the same undo log in two backends; more
+ * interlocking work required here.
+ */
+bool
+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(discard_point);
+	UndoLogOffset discard = UndoRecPtrGetOffset(discard_point);
+	UndoLogOffset old_discard;
+	UndoLogOffset end;
+	UndoLogSlot *slot;
+	int			segno;
+	int			new_segno;
+	bool		need_to_flush_wal = false;
+	bool		entirely_discarded = false;
+
+	slot = find_undo_log_slot(logno, false);
+	if (unlikely(slot == NULL))
+	{
+		/* Already discarded (entirely). */
+		return false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (unlikely(slot->logno != logno || discard <= slot->meta.discard))
+	{
+		/*
+		 * Already discarded entirely and the slot has been recycled, or up
+		 * to this point).
+		 */
+		LWLockRelease(&slot->mutex);
+		return false;
+	}
+	if (discard > slot->meta.unlogged.insert)
+		elog(ERROR, "cannot move discard point past insert point");
+	old_discard = slot->meta.discard;
+	end = slot->meta.end;
+	/* Are we discarding the last remaining data in a log marked as full? */
+	if (slot->meta.status == UNDO_LOG_STATUS_FULL &&
+		discard == slot->meta.unlogged.insert)
+	{
+		/*
+		 * Adjust the discard and insert pointers so that the final segment is
+		 * deleted from disk, and remember not to recycle it.
+		 */
+		entirely_discarded = true;
+		/* TODO: Check if the following line is replayed correctly */
+		slot->meta.unlogged.insert = slot->meta.end;
+		discard = slot->meta.end;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * TODO: I think we need a new lock just for this phase, so that buffer
+	 * dropping and IO are done by only one backend if a superuser command and
+	 * a discard worker both run this!
+	 */
+
+	/*
+	 * Drop all buffers holding this undo data out of the buffer pool (except
+	 * the last one, if the new location is in the middle of it somewhere), so
+	 * that the contained data doesn't ever touch the disk.  The caller
+	 * promises that this data will not be needed again.  We have to drop the
+	 * buffers from the buffer pool before removing files, otherwise a
+	 * concurrent session might try to write the block to evict the buffer.
+	 */
+	forget_undo_buffers(logno, old_discard, discard, entirely_discarded);
+
+	/*
+	 * Check if we crossed a segment boundary and need to do some synchronous
+	 * filesystem operations.
+	 */
+	segno = old_discard / UndoLogSegmentSize;
+	new_segno = discard / UndoLogSegmentSize;
+	if (segno < new_segno)
+	{
+		int		recycle;
+		UndoLogOffset pointer;
+
+		/*
+		 * We always WAL-log discards, but we only need to flush the WAL if we
+		 * have performed a filesystem operation.
+		 */
+		need_to_flush_wal = true;
+
+		/*
+		 * XXX When we rename or unlink a file, it's possible that some
+		 * backend still has it open because it has recently read a page from
+		 * it.  smgr/undofile.c in any such backend will eventually close it,
+		 * because it considers that fd to belong to the file with the name
+		 * that we're unlinking or renaming and it doesn't like to keep more
+		 * than one open at a time.  No backend should ever try to read from
+		 * such a file descriptor; that is what it means when we say that the
+		 * caller of UndoLogDiscard() asserts that there will be no attempts
+		 * to access the discarded range of undo log.  In the case of a
+		 * rename, if a backend were to attempt to read undo data in the range
+		 * being discarded, it would read entirely the wrong data.
+		 */
+
+		/*
+		 * How many segments should we recycle (= rename from tail position to
+		 * head position)?  For now it's always 1 unless there is already a
+		 * spare one, but we could have an adaptive algorithm that recycles
+		 * multiple segments at a time and pays just one fsync().
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if ((slot->meta.end - slot->meta.unlogged.insert) < UndoLogSegmentSize &&
+			slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+			recycle = 1;
+		else
+			recycle = 0;
+		LWLockRelease(&slot->mutex);
+
+		/* Rewind to the start of the segment. */
+		pointer = segno * UndoLogSegmentSize;
+
+		while (pointer < new_segno * UndoLogSegmentSize)
+		{
+			char	discard_path[MAXPGPATH];
+
+			/* Tell the checkpointer that the file is going away. */
+			undofile_forget_sync(logno, pointer / UndoLogSegmentSize,
+								 slot->meta.tablespace);
+
+			UndoLogSegmentPath(logno, pointer / UndoLogSegmentSize,
+							   slot->meta.tablespace, discard_path);
+
+			/* Can we recycle the oldest segment? */
+			if (recycle > 0)
+			{
+				char	recycle_path[MAXPGPATH];
+
+				/*
+				 * End points one byte past the end of the current undo space,
+				 * ie to the first byte of the segment file we want to create.
+				 */
+				UndoLogSegmentPath(logno, end / UndoLogSegmentSize,
+								   slot->meta.tablespace, recycle_path);
+				if (rename(discard_path, recycle_path) == 0)
+				{
+					elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+						 discard_path, recycle_path);
+					end += UndoLogSegmentSize;
+					--recycle;
+				}
+				else
+				{
+					elog(ERROR, "could not rename \"%s\" to \"%s\": %m",
+						 discard_path, recycle_path);
+				}
+			}
+			else
+			{
+				if (unlink(discard_path) == 0)
+					elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+				else
+					elog(ERROR, "could not unlink \"%s\": %m", discard_path);
+			}
+			pointer += UndoLogSegmentSize;
+		}
+	}
+
+	/* WAL log the discard. */
+	{
+		xl_undolog_discard xlrec;
+		XLogRecPtr ptr;
+
+		xlrec.logno = logno;
+		xlrec.discard = discard;
+		xlrec.end = end;
+		xlrec.latestxid = xid;
+		xlrec.entirely_discarded = entirely_discarded;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_DISCARD);
+
+		if (need_to_flush_wal)
+			XLogFlush(ptr);
+	}
+
+	/* Update shmem to show the new discard and end pointers. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (entirely_discarded)
+		free_undo_log_slot(slot);
+
+	return true;
+}
+
+/*
+ * Return an UndoRecPtr to the oldest valid data in an undo log, or
+ * InvalidUndoRecPtr if it is empty.
+ */
+UndoRecPtr
+UndoLogGetOldestRecord(UndoLogNumber logno, bool *full)
+{
+	UndoLogSlot *slot;
+	UndoRecPtr	result;
+
+	/* Try to find the slot for this undo log number. */
+	slot = find_undo_log_slot(logno, false);
+	if (slot == NULL)
+	{
+		/* It's unknown to us, so we assume it's been entirely discarded. */
+		if (full)
+			*full = true;
+		return InvalidUndoRecPtr;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->logno != logno)
+	{
+		/* It's been recycled.  SO it must have been entirely discarded. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = true;
+	}
+	else if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		/* It's empty, so there is no oldest record pointer to return. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	else
+	{
+		/* There is a record here! */
+		result = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLogSlot(slot));
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&slot->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
+ * Return the next insert location.
+ */
+UndoRecPtr
+UndoLogGetNextInsertPtr(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	UndoRecPtr	insert;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: what if the slot has been recycled? */
+	insert = slot->meta.unlogged.insert;
+	LWLockRelease(&slot->mutex);
+
+	return MakeUndoRecPtr(logno, insert);
+}
+
+/*
+ * Delete unreachable files under pg_undo.  Any files corresponding to LSN
+ * positions before the previous checkpoint are no longer needed.
+ */
+static void
+CleanUpUndoCheckPointFiles(XLogRecPtr checkPointRedo)
+{
+	DIR	   *dir;
+	struct dirent *de;
+	char	path[MAXPGPATH];
+	char	oldest_path[MAXPGPATH];
+
+	/*
+	 * If a base backup is in progress, we can't delete any checkpoint
+	 * snapshot files because one of them corresponds to the backup label but
+	 * there could be any number of checkpoints during the backup.
+	 */
+	if (BackupInProgress())
+		return;
+
+	/* Otherwise keep only those >= the previous checkpoint's redo point. */
+	snprintf(oldest_path, MAXPGPATH, "%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	dir = AllocateDir("pg_undo");
+	while ((de = ReadDir(dir, "pg_undo")) != NULL)
+	{
+		/*
+		 * Assume that fixed width uppercase hex strings sort the same way as
+		 * the values they represent, so we can use strcmp to identify undo
+		 * log snapshot files corresponding to checkpoints that we don't need
+		 * anymore.  This assumption holds for ASCII.
+		 */
+		if (!(strlen(de->d_name) == UNDO_CHECKPOINT_FILENAME_LENGTH))
+			continue;
+
+		if (UndoCheckPointFilenamePrecedes(de->d_name, oldest_path))
+		{
+			snprintf(path, MAXPGPATH, "pg_undo/%s", de->d_name);
+			if (unlink(path) != 0)
+				elog(ERROR, "could not unlink file \"%s\": %m", path);
+		}
+	}
+	FreeDir(dir);
+}
+
+/*
+ * Write out the undo log meta data to the pg_undo directory.  The actual
+ * contents of undo logs is in shared buffers and therefore handled by
+ * CheckPointBuffers(), but here we record the table of undo logs and their
+ * properties.
+ */
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
+{
+	UndoLogMetaData *serialized = NULL;
+	size_t	serialized_size = 0;
+	char   *data;
+	char	path[MAXPGPATH];
+	UndoLogNumber num_logs;
+	int		fd;
+	int		i;
+	pg_crc32c crc;
+
+	/*
+	 * We acquire UndoLogLock to prevent any undo logs from being created or
+	 * discarded while we build a snapshot of them.  This isn't expected to
+	 * take long on a healthy system because the number of active logs should
+	 * be around the number of backends.  Holding this lock won't prevent
+	 * concurrent access to the undo log, except when segments need to be
+	 * added or removed.
+	 */
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+
+	/*
+	 * Rather than doing the file IO while we hold locks, we'll copy the
+	 * meta-data into a palloc'd buffer.
+	 */
+	serialized_size = sizeof(UndoLogMetaData) * UndoLogNumSlots();
+	serialized = (UndoLogMetaData *) palloc0(serialized_size);
+
+	/* Scan through all slots looking for non-empty ones. */
+	num_logs = 0;
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+
+		/* Skip empty slots. */
+		if (slot->logno == InvalidUndoLogNumber)
+			continue;
+
+		/* Capture snapshot while holding each mutex. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		serialized[num_logs++] = slot->meta;
+		LWLockRelease(&slot->mutex);
+	}
+
+	LWLockRelease(UndoLogLock);
+
+	/* Dump into a file under pg_undo. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE);
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not create file \"%s\": %m", path)));
+
+	/* Compute header checksum. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the number of active logs + crc. */
+	if ((write(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno)) ||
+		(write(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno)) != sizeof(UndoLogShared->next_logno)) ||
+		(write(fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+	/* Write out the meta data for all active undo logs. */
+	data = (char *) serialized;
+	INIT_CRC32C(crc);
+	serialized_size = num_logs * sizeof(UndoLogMetaData);
+	while (serialized_size > 0)
+	{
+		ssize_t written;
+
+		written = write(fd, data, serialized_size);
+		if (written < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write to file \"%s\": %m", path)));
+		COMP_CRC32C(crc, data, written);
+		serialized_size -= written;
+		data += written;
+	}
+	FIN_CRC32C(crc);
+
+	if (write(fd, &crc, sizeof(crc)) != sizeof(crc))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+
+	/* Flush file and directory entry. */
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC);
+	pg_fsync(fd);
+	if (CloseTransientFile(fd) < 0)
+		ereport(data_sync_elevel(ERROR),
+				(errcode_for_file_access(),
+				 errmsg("could not close file \"%s\": %m", path)));
+	fsync_fname("pg_undo", true);
+	pgstat_report_wait_end();
+
+	if (serialized)
+		pfree(serialized);
+
+	CleanUpUndoCheckPointFiles(priorCheckPointRedo);
+}
+
+void
+StartupUndoLogs(XLogRecPtr checkPointRedo)
+{
+	char	path[MAXPGPATH];
+	int		i;
+	int		fd;
+	int		nlogs;
+	pg_crc32c crc;
+	pg_crc32c new_crc;
+
+	/* If initdb is calling, there is no file to read yet. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/* Open the pg_undo file corresponding to the given checkpoint. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_READ);
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
+
+	/* Read the active log number range. */
+	if ((read(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno))
+		 != sizeof(UndoLogShared->low_logno)) ||
+		(read(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno))
+		 != sizeof(UndoLogShared->next_logno)) ||
+		(read(fd, &nlogs, sizeof(nlogs)) != sizeof(nlogs)) ||
+		(read(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+
+	/* Verify the header checksum. */
+	INIT_CRC32C(new_crc);
+	COMP_CRC32C(new_crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(new_crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(new_crc, &nlogs, sizeof(UndoLogShared->next_logno));
+	FIN_CRC32C(new_crc);
+
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	/*
+	 * We'll acquire UndoLogLock just because allocate_undo_log() asserts we
+	 * hold it (we don't actually expect concurrent access yet).
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* Initialize all the logs and set up the freelist. */
+	INIT_CRC32C(new_crc);
+	for (i = 0; i < nlogs; ++i)
+	{
+		ssize_t size;
+		UndoLogSlot *slot;
+
+		/*
+		 * Get a new UndoLogSlot.  If this checkpoint was created on a system
+		 * with a higher max_connections setting, it's theoretically possible
+		 * that we don't have enough space and cannot start up.
+		 */
+		slot = allocate_undo_log_slot();
+		if (!slot)
+			ereport(ERROR,
+					(errmsg("not enough undo log slots to recover from checkpoint: need at least %d, have %zu",
+							nlogs, UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections")));
+
+		/* Read in the meta data for this undo log. */
+		if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
+			elog(ERROR, "short read of pg_undo meta data in file \"%s\": %m (got %zu, wanted %zu)",
+				 path, size, sizeof(slot->meta));
+		COMP_CRC32C(new_crc, &slot->meta, sizeof(slot->meta));
+
+		/*
+		 * At normal start-up, or during recovery, all active undo logs start
+		 * out on the appropriate free list.
+		 */
+		slot->logno = slot->meta.logno;
+		slot->pid = InvalidPid;
+		slot->oldest_data = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+		{
+			slot->next_free = UndoLogShared->free_lists[slot->meta.category];
+			UndoLogShared->free_lists[slot->meta.category] = slot->logno;
+		}
+	}
+	FIN_CRC32C(new_crc);
+
+	LWLockRelease(UndoLogLock);
+
+	/* Verify body checksum. */
+	if (read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	CloseTransientFile(fd);
+	pgstat_report_wait_end();
+}
+
+/*
+ * Allocate a new UndoLogSlot object.
+ */
+static UndoLogSlot *
+allocate_undo_log_slot(void)
+{
+	UndoLogSlot *slot;
+	UndoLogNumber i;
+
+	Assert(LWLockHeldByMeInMode(UndoLogLock, LW_EXCLUSIVE));
+
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		slot = &UndoLogShared->slots[i];
+		if (slot->logno == InvalidUndoLogNumber)
+		{
+			memset(&slot->meta, 0, sizeof(slot->meta));
+			slot->pid = 0;
+			slot->wait_fxmin = InvalidFullTransactionId;
+			slot->oldest_data =0;
+			slot->next_free = -1;
+			slot->logno = -1;
+			return slot;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Free an UndoLogSlot object in shared memory, so that it can be reused.
+ * This is a rare event, and has complications for all code paths that access
+ * slots.  Unless the current session is attached to the slot, it must be
+ * prepared for it to be freed and then potentially recycled for use by
+ * another log.  See UndoLogGetSlot().
+ */
+static void
+free_undo_log_slot(UndoLogSlot *slot)
+{
+	/*
+	 * When removing an undo log from a slot in shared memory, we acquire
+	 * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+	 * hold any one of those locks to prevent the slot from being recycled.
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno != InvalidUndoLogNumber);
+	slot->logno = InvalidUndoLogNumber;
+	memset(&slot->meta, 0, sizeof(slot->meta));
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * Find the UndoLogSlot object for a given log number.
+ *
+ * The caller may or may not already hold UndoLogLock, and should indicate
+ * this by passing 'locked'.  We'll acquire it in the slow path if necessary.
+ * If it is not held by the caller, the caller must deal with the possibility
+ * that the returned UndoLogSlot no longer contains the requested logno by the
+ * time it is accessed.
+ *
+ * To do that, one of the following approaches must be taken by the calling
+ * code:
+ *
+ * 1.  If the calling code knows that it is attached to this lock or is the
+ * recovery process, then there is no way for the slot to be recycled, so it's
+ * not necessary to check that the log number hasn't changed.  The slot cannot
+ * be recycled while a backend is attached.  It should probably assert that it
+ * is attached, however.
+ *
+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
+ *
+ * Return NULL if the undo log has been entirely discarded.  It is an error to
+ * ask for undo logs that have never been created.
+ */
+static UndoLogSlot *
+find_undo_log_slot(UndoLogNumber logno, bool locked)
+{
+	UndoLogSlot *result = NULL;
+	UndoLogTableEntry *entry;
+	bool	   found;
+
+	Assert(locked == LWLockHeldByMe(UndoLogLock));
+
+	/* First see if we already have it in our cache. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		result = entry->slot;
+	else
+	{
+		UndoLogNumber i;
+
+		/* Nope.  Linear search for the slot in shared memory. */
+		if (!locked)
+			LWLockAcquire(UndoLogLock, LW_SHARED);
+		for (i = 0; i < UndoLogNumSlots(); ++i)
+		{
+			if (UndoLogShared->slots[i].logno == logno)
+			{
+				/* Found it. */
+
+				/*
+				 * TODO: Should this function be usable in a critical section?
+				 * Would it make sense to detect that we are in a critical
+				 * section and just return the pointer to the log without
+				 * updating the cache, to avoid any chance of allocating
+				 * memory?
+				 */
+
+				entry = undologtable_insert(undologtable_cache, logno, &found);
+				entry->number = logno;
+				entry->slot = &UndoLogShared->slots[i];
+				entry->tablespace = entry->slot->meta.tablespace;
+				entry->category = entry->slot->meta.category;
+				entry->recent_discard =
+					MakeUndoRecPtr(logno, entry->slot->meta.discard);
+				result = entry->slot;
+				break;
+			}
+		}
+
+		/*
+		 * If we didn't find it, then it must already have been entirely
+		 * discarded.  We create a negative cache entry so that we can answer
+		 * this question quickly next time.
+		 *
+		 * TODO: We could track the lowest known undo log number, to reduce
+		 * the negative cache entry bloat.
+		 */
+		if (result == NULL)
+		{
+			/*
+			 * Sanity check: the caller should not be asking about undo logs
+			 * that have never existed.
+			 */
+			if (logno >= UndoLogShared->next_logno)
+				elog(ERROR, "undo log %u hasn't been created yet", logno);
+			entry = undologtable_insert(undologtable_cache, logno, &found);
+			entry->number = logno;
+			entry->slot = NULL;
+			entry->tablespace = 0;
+		}
+		if (!locked)
+			LWLockRelease(UndoLogLock);
+	}
+
+	return result;
+}
+
+/*
+ * Get a pointer to an UndoLogSlot object corresponding to a given logno.
+ *
+ * In general, the caller must acquire the UndoLogSlot's mutex to access
+ * the contents, and at that time must consider that the logno might have
+ * changed because the undo log it contained has been entirely discarded.
+ *
+ * If the calling backend is currently attached to the undo log, that is not
+ * possible, because logs can only reach UNDO_LOG_STATUS_DISCARDED after first
+ * reaching UNDO_LOG_STATUS_FULL, and that only happens while detaching.
+ */
+UndoLogSlot *
+UndoLogGetSlot(UndoLogNumber logno, bool missing_ok)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL && !missing_ok)
+		elog(ERROR, "unknown undo log number %d", logno);
+
+	return slot;
+}
+
+/*
+ * Attach to a free undo log, creating a new one if required.
+ */
+static void
+attach_undo_log(UndoLogCategory category, Oid tablespace)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber logno;
+	UndoLogNumber *place;
+
+	Assert(!InRecovery);
+	Assert(CurrentSession->attached_undo_slots[category] == NULL);
+
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/*
+	 * For now we have a simple linked list of unattached undo logs for each
+	 * persistence level.  We'll grovel though it to find something for the
+	 * tablespace you asked for.  If you're not using multiple tablespaces
+	 * it'll be able to pop one off the front.  We might need a hash table
+	 * keyed by tablespace if this simple scheme turns out to be too slow when
+	 * using many tablespaces and many undo logs, but that seems like an
+	 * unusual use case not worth optimizing for.
+	 */
+	place = &UndoLogShared->free_lists[category];
+	while (*place != InvalidUndoLogNumber)
+	{
+		UndoLogSlot *candidate = find_undo_log_slot(*place, true);
+
+		/*
+		 * There should never be an undo log on the freelist that has been
+		 * entirely discarded, or hasn't been created yet.  The persistence
+		 * level should match the freelist.
+		 */
+		if (unlikely(candidate == NULL))
+			elog(ERROR,
+				 "corrupted undo log freelist, no such undo log %u", *place);
+		if (unlikely(candidate->meta.category != category))
+			elog(ERROR,
+				 "corrupted undo log freelist, undo log %u with persistence %d found on freelist %d",
+				 *place, candidate->meta.category, category);
+
+		if (candidate->meta.tablespace == tablespace)
+		{
+			logno = *place;
+			slot = candidate;
+			*place = candidate->next_free;
+			break;
+		}
+		place = &candidate->next_free;
+	}
+
+	/*
+	 * If all existing undo logs for this tablespace and persistence level are
+	 * busy, we'll have to create a new one.
+	 */
+	if (slot == NULL)
+	{
+		if (UndoLogShared->next_logno > MaxUndoLogNumber)
+		{
+			/*
+			 * You've used up all 16 exabytes of undo log addressing space.
+			 * This is a difficult state to reach using only 16 exabytes of
+			 * WAL.
+			 */
+			elog(ERROR, "undo log address space exhausted");
+		}
+
+		/* Allocate a slot from the UndoLogSlot pool. */
+		slot = allocate_undo_log_slot();
+		if (unlikely(!slot))
+			ereport(ERROR,
+					(errmsg("could not create new undo log"),
+					 errdetail("The maximum number of active undo logs is %zu.",
+							   UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections.")));
+		slot->logno = logno = UndoLogShared->next_logno;
+
+		/*
+		 * The insert and discard pointers start after the first block's
+		 * header.  XXX That means that insert is > end for a short time in a
+		 * newly created undo log.  Is there any problem with that?
+		 */
+		slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+		slot->meta.discard = UndoLogBlockHeaderSize;
+
+		slot->meta.logno = logno;
+		slot->meta.tablespace = tablespace;
+		slot->meta.category = category;
+		slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+
+		/* Move the high log number pointer past this one. */
+		++UndoLogShared->next_logno;
+
+		/* WAL-log the creation of this new undo log. */
+		{
+			xl_undolog_create xlrec;
+
+			xlrec.logno = logno;
+			xlrec.tablespace = slot->meta.tablespace;
+			xlrec.category = slot->meta.category;
+
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+			XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_CREATE);
+		}
+
+		/*
+		 * This undo log has no segments.  UndoLogAllocate will create the
+		 * first one on demand.
+		 */
+	}
+	LWLockRelease(UndoLogLock);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = MyProcPid;
+	LWLockRelease(&slot->mutex);
+
+	CurrentSession->attached_undo_slots[category] = slot;
+}
+
+/* check_hook: validate new undo_tablespaces */
+bool
+check_undo_tablespaces(char **newval, void **extra, GucSource source)
+{
+	char	   *rawname;
+	List	   *namelist;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/*
+	 * Parse string into list of identifiers, just to check for
+	 * well-formedness (unfortunateley we can't validate the names in the
+	 * catalog yet).
+	 */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	/*
+	 * Make sure we aren't already in a transaction that has been assigned an
+	 * XID.  This ensures we don't detach from an undo log that we might have
+	 * started writing undo data into for this transaction.
+	 */
+	if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 (errmsg("undo_tablespaces cannot be changed while a transaction is in progress"))));
+	list_free(namelist);
+
+	return true;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_undo_tablespaces(const char *newval, void *extra)
+{
+	/*
+	 * This is normally called only when GetTopTransactionIdIfAny() ==
+	 * InvalidTransactionId (because you can't change undo_tablespaces in the
+	 * middle of a transaction that's been asigned an xid), but we can't
+	 * assert that because it's also called at the end of a transaction that's
+	 * rolling back, to reset the GUC if it was set inside the transaction.
+	 */
+
+	/* Tell UndoLogAllocate() to reexamine undo_tablespaces. */
+	if (CurrentSession)
+		CurrentSession->need_to_choose_undo_tablespace = true;
+}
+
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+	char   *rawname;
+	List   *namelist;
+	bool	need_to_unlock;
+	int		length;
+	int		i;
+
+	/* We need a modifiable copy of string. */
+	rawname = pstrdup(undo_tablespaces);
+
+	/* Break string into list of identifiers. */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+		elog(ERROR, "undo_tablespaces is unexpectedly malformed");
+
+	length = list_length(namelist);
+	if (length == 0 ||
+		(length == 1 && ((char *) linitial(namelist))[0] == '\0'))
+	{
+		/*
+		 * If it's an empty string, then we'll use the default tablespace.  No
+		 * locking is required because it can't be dropped.
+		 */
+		*tablespace = DEFAULTTABLESPACE_OID;
+		need_to_unlock = false;
+	}
+	else
+	{
+		/*
+		 * Choose an OID using our pid, so that if several backends have the
+		 * same multi-tablespace setting they'll spread out.  We could easily
+		 * do better than this if more serious load balancing is judged
+		 * useful.
+		 */
+		int		index = MyProcPid % length;
+		int		first_index = index;
+		Oid		oid = InvalidOid;
+
+		/*
+		 * Take the tablespace create/drop lock while we look the name up.
+		 * This prevents the tablespace from being dropped while we're trying
+		 * to resolve the name, or while the called is trying to create an
+		 * undo log in it.  The caller will have to release this lock.
+		 */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		for (;;)
+		{
+			const char *name = list_nth(namelist, index);
+
+			oid = get_tablespace_oid(name, true);
+			if (oid == InvalidOid)
+			{
+				/* Unknown tablespace, try the next one. */
+				index = (index + 1) % length;
+				/*
+				 * But if we've tried them all, it's time to complain.  We'll
+				 * arbitrarily complain about the last one we tried in the
+				 * error message.
+				 */
+				if (index == first_index)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", name),
+							 errhint("Create the tablespace or set undo_tablespaces to a valid or empty list.")));
+				continue;
+			}
+			if (oid == GLOBALTABLESPACE_OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("undo logs cannot be placed in pg_global tablespace")));
+			/* If we got here we succeeded in finding one. */
+			break;
+		}
+
+		Assert(oid != InvalidOid);
+		*tablespace = oid;
+		need_to_unlock = true;
+	}
+
+	/*
+	 * If we came here because the user changed undo_tablesaces, then detach
+	 * from any undo logs we happen to be attached to.
+	 */
+	if (force_detach)
+	{
+		for (i = 0; i < UndoLogCategories; ++i)
+		{
+			UndoLogSlot *slot = CurrentSession->attached_undo_slots[i];
+
+			if (slot != NULL)
+			{
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				slot->pid = InvalidPid;
+				slot->meta.unlogged.xid = InvalidTransactionId;
+				LWLockRelease(&slot->mutex);
+
+				LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+				slot->next_free = UndoLogShared->free_lists[i];
+				UndoLogShared->free_lists[i] = slot->logno;
+				LWLockRelease(UndoLogLock);
+
+				CurrentSession->attached_undo_slots[i] = NULL;
+			}
+		}
+	}
+
+	return need_to_unlock;
+}
+
+bool
+DropUndoLogsInTablespace(Oid tablespace)
+{
+	DIR *dir;
+	char undo_path[MAXPGPATH];
+	UndoLogSlot *slot = NULL;
+	int		i;
+
+	Assert(LWLockHeldByMe(TablespaceCreateLock));
+	Assert(tablespace != DEFAULTTABLESPACE_OID);
+
+	/* First, try to kick everyone off any undo logs in this tablespace. */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		bool ok;
+		bool return_to_freelist = false;
+
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/* Check if this undo log can be forcibly detached. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		if (slot->meta.discard == slot->meta.unlogged.insert &&
+			(slot->meta.unlogged.xid == InvalidTransactionId ||
+			 !TransactionIdIsInProgress(slot->meta.unlogged.xid)))
+		{
+			slot->meta.unlogged.xid = InvalidTransactionId;
+			if (slot->pid != InvalidPid)
+			{
+				slot->pid = InvalidPid;
+				return_to_freelist = true;
+			}
+			ok = true;
+		}
+		else
+		{
+			/*
+			 * There is data we need in this undo log.  We can't force it to
+			 * be detached.
+			 */
+			ok = false;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* If we failed, then give up now and report failure. */
+		if (!ok)
+			return false;
+
+		/*
+		 * Put this undo log back on the appropriate free-list.  No one can
+		 * attach to it while we hold TablespaceCreateLock, but if we return
+		 * earlier in a future go around this loop, we need the undo log to
+		 * remain usable.  We'll remove all appropriate logs from the
+		 * free-lists in a separate step below.
+		 */
+		if (return_to_freelist)
+		{
+			LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+			slot->next_free = UndoLogShared->free_lists[slot->meta.category];
+			UndoLogShared->free_lists[slot->meta.category] = slot->logno;
+			LWLockRelease(UndoLogLock);
+		}
+	}
+
+	/*
+	 * We detached all backends from undo logs in this tablespace, and no one
+	 * can attach to any non-default-tablespace undo logs while we hold
+	 * TablespaceCreateLock.  We can now drop the undo logs.
+	 */
+	slot = NULL;
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/*
+		 * Make sure no buffers remain.  When that is done by
+		 * UndoLogDiscard(), the final page is left in shared_buffers because
+		 * it may contain data, or at least be needed again very soon.  Here
+		 * we need to drop even that page from the buffer pool.
+		 */
+		forget_undo_buffers(slot->logno, slot->meta.discard, slot->meta.discard, true);
+
+		/*
+		 * TODO: For now we drop the undo log, meaning that it will never be
+		 * used again.  That wastes the rest of its address space.  Instead,
+		 * we should put it onto a special list of 'offline' undo logs, ready
+		 * to be reactivated in some other tablespace.  Then we can keep the
+		 * unused portion of its address space.
+		 */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		slot->meta.status = UNDO_LOG_STATUS_DISCARDED;
+		LWLockRelease(&slot->mutex);
+	}
+
+	/* Forget about all sync requests relating to this tablespace. */
+	undofile_forget_sync_tablespace(tablespace);
+
+	/* Unlink all undo segment files in this tablespace. */
+	UndoLogDirectory(tablespace, undo_path);
+
+	dir = AllocateDir(undo_path);
+	if (dir != NULL)
+	{
+		struct dirent *de;
+
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strcmp(de->d_name, ".") == 0 ||
+				strcmp(de->d_name, "..") == 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+	}
+
+	/* Remove all dropped undo logs from the free-lists. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	for (i = 0; i < UndoLogCategories; ++i)
+	{
+		UndoLogSlot *slot;
+		UndoLogNumber *place;
+
+		place = &UndoLogShared->free_lists[i];
+		while (*place != InvalidUndoLogNumber)
+		{
+			slot = find_undo_log_slot(*place, true);
+			if (!slot)
+				elog(ERROR,
+					 "corrupted undo log freelist, unknown log %u", *place);
+			if (slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+				*place = slot->next_free;
+			else
+				place = &slot->next_free;
+		}
+	}
+	LWLockRelease(UndoLogLock);
+
+	return true;
+}
+
+void
+ResetUndoLogs(UndoLogCategory category)
+{
+	UndoLogSlot *slot = NULL;
+
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		DIR	   *dir;
+		struct dirent *de;
+		char	undo_path[MAXPGPATH];
+		char	segment_prefix[MAXPGPATH];
+		size_t	segment_prefix_size;
+
+		if (slot->meta.category != category)
+			continue;
+
+		/* Scan the directory for files belonging to this undo log. */
+		snprintf(segment_prefix, sizeof(segment_prefix), "%06X.", slot->logno);
+		segment_prefix_size = strlen(segment_prefix);
+		UndoLogDirectory(slot->meta.tablespace, undo_path);
+		dir = AllocateDir(undo_path);
+		if (dir == NULL)
+			continue;
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+
+		/*
+		 * We have no segment files.  Set the pointers to indicate that there
+		 * is no data.  The discard and insert pointers point to the first
+		 * usable byte in the segment we will create when we next try to
+		 * allocate.  This is a bit strange, because it means that they are
+		 * past the end pointer.  That's the same as when new undo logs are
+		 * created.
+		 *
+		 * TODO: Should we rewind to zero instead, so we can reuse that (now)
+		 * unreferenced address space?
+		 */
+		slot->meta.unlogged.insert = slot->meta.discard = slot->meta.end +
+			UndoLogBlockHeaderSize;
+	}
+}
+
+Datum
+pg_stat_get_undo_logs(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_UNDO_LOGS_COLS 9
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	char *tablespace_name = NULL;
+	Oid last_tablespace = InvalidOid;
+	int			i;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Scan all undo logs to build the results. */
+	for (i = 0; i < UndoLogShared->nslots; ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+		char buffer[17];
+		Datum values[PG_STAT_GET_UNDO_LOGS_COLS];
+		bool nulls[PG_STAT_GET_UNDO_LOGS_COLS] = { false };
+		Oid tablespace;
+
+		/*
+		 * This won't be a consistent result overall, but the values for each
+		 * log will be consistent because we'll take the per-log lock while
+		 * copying them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+
+		/* Skip unused slots and entirely discarded undo logs. */
+		if (slot->logno == InvalidUndoLogNumber ||
+			slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+
+		values[0] = ObjectIdGetDatum((Oid) slot->logno);
+		values[1] = CStringGetTextDatum(
+			slot->meta.category == UNDO_PERMANENT ? "permanent" :
+			slot->meta.category == UNDO_UNLOGGED ? "unlogged" :
+			slot->meta.category == UNDO_TEMP ? "temporary" :
+			slot->meta.category == UNDO_SHARED ? "shared" : "<unknown>");
+		tablespace = slot->meta.tablespace;
+
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.discard));
+		values[3] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert));
+		values[4] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.end));
+		values[5] = CStringGetTextDatum(buffer);
+		if (slot->meta.unlogged.xid == InvalidTransactionId)
+			nulls[6] = true;
+		else
+			values[6] = TransactionIdGetDatum(slot->meta.unlogged.xid);
+		if (slot->pid == InvalidPid)
+			nulls[7] = true;
+		else
+			values[7] = Int32GetDatum((int32) slot->pid);
+		switch (slot->meta.status)
+		{
+		case UNDO_LOG_STATUS_ACTIVE:
+			values[8] = CStringGetTextDatum("ACTIVE"); break;
+		case UNDO_LOG_STATUS_FULL:
+			values[8] = CStringGetTextDatum("FULL"); break;
+		default:
+			nulls[8] = true;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/*
+		 * Deal with potentially slow tablespace name lookup without the lock.
+		 * Avoid making multiple calls to that expensive function for the
+		 * common case of repeating tablespace.
+		 */
+		if (tablespace != last_tablespace)
+		{
+			if (tablespace_name)
+				pfree(tablespace_name);
+			tablespace_name = get_tablespace_name(tablespace);
+			last_tablespace = tablespace;
+		}
+		if (tablespace_name)
+		{
+			values[2] = CStringGetTextDatum(tablespace_name);
+			nulls[2] = false;
+		}
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	if (tablespace_name)
+		pfree(tablespace_name);
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * replay the creation of a new undo log
+ */
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+	xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	/* Create meta-data space in shared memory. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* TODO: assert that it doesn't exist already? */
+
+	slot = allocate_undo_log_slot();
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->logno = xlrec->logno;
+	slot->meta.logno = xlrec->logno;
+	slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+	slot->meta.category = xlrec->category;
+	slot->meta.tablespace = xlrec->tablespace;
+	slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+	slot->meta.discard = UndoLogBlockHeaderSize;
+	UndoLogShared->next_logno = Max(xlrec->logno + 1, UndoLogShared->next_logno);
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * replay the addition of a new segment to an undo log
+ */
+static void
+undolog_xlog_extend(XLogReaderState *record)
+{
+	xl_undolog_extend *xlrec = (xl_undolog_extend *) XLogRecGetData(record);
+
+	/* Extend exactly as we would during DO phase. */
+	extend_undo_log(xlrec->logno, xlrec->end);
+}
+
+/*
+ * Drop all buffers for the given undo log, from the old_discard to up
+ * new_discard.  If drop_tail is true, also drop the buffer that holds
+ * new_discard; this is used when discarding undo logs completely, for example
+ * via DROP TABLESPACE.  If it is false, then the final buffer is not dropped
+ * because it may contain data.
+ *
+ */
+static void
+forget_undo_buffers(int logno, UndoLogOffset old_discard,
+					UndoLogOffset new_discard, bool drop_tail)
+{
+	BlockNumber old_blockno;
+	BlockNumber new_blockno;
+	RelFileNode	rnode;
+
+	UndoRecPtrAssignRelFileNode(rnode, MakeUndoRecPtr(logno, old_discard));
+	old_blockno = old_discard / BLCKSZ;
+	new_blockno = new_discard / BLCKSZ;
+	if (drop_tail)
+		++new_blockno;
+	while (old_blockno < new_blockno)
+	{
+		ForgetBuffer(rnode, UndoLogForkNum, old_blockno);
+		ForgetLocalBuffer(rnode, UndoLogForkNum, old_blockno++);
+	}
+}
+/*
+ * replay an undo segment discard record
+ */
+static void
+undolog_xlog_discard(XLogReaderState *record)
+{
+	xl_undolog_discard *xlrec = (xl_undolog_discard *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	UndoLogOffset old_segment_begin;
+	UndoLogOffset new_segment_begin;
+	RelFileNode rnode = {0};
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+	if (slot == NULL)
+		elog(ERROR, "unknown undo log %d", xlrec->logno);
+
+	/*
+	 * We're about to discard undologs. In Hot Standby mode, ensure that
+	 * there's no queries running which need to get tuple from discarded undo.
+	 *
+	 * XXX we are passing empty rnode to the conflict function so that it can
+	 * check conflict in all the backend regardless of which database the
+	 * backend is connected.
+	 */
+	if (InHotStandby && TransactionIdIsValid(xlrec->latestxid))
+		ResolveRecoveryConflictWithSnapshot(xlrec->latestxid, rnode);
+
+	/*
+	 * See if we need to unlink or rename any files, but don't consider it an
+	 * error if we find that files are missing.  Since UndoLogDiscard()
+	 * performs filesystem operations before WAL logging or updating shmem
+	 * which could be checkpointed, a crash could have left files already
+	 * deleted, but we could replay WAL that expects the files to be there.
+	 */
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno == xlrec->logno);
+	discard = slot->meta.discard;
+	end = slot->meta.end;
+	LWLockRelease(&slot->mutex);
+
+	/* Drop buffers before we remove/recycle any files. */
+	forget_undo_buffers(xlrec->logno, discard, xlrec->discard,
+						xlrec->entirely_discarded);
+
+	/* Rewind to the start of the segment. */
+	old_segment_begin = discard - discard % UndoLogSegmentSize;
+	new_segment_begin = xlrec->discard - xlrec->discard % UndoLogSegmentSize;
+
+	/* Unlink or rename segments that are no longer in range. */
+	while (old_segment_begin < new_segment_begin)
+	{
+		char	discard_path[MAXPGPATH];
+
+		/* Tell the checkpointer that the file is going away. */
+		undofile_forget_sync(slot->logno,
+							 old_segment_begin / UndoLogSegmentSize,
+							 slot->meta.tablespace);
+
+		UndoLogSegmentPath(xlrec->logno, old_segment_begin / UndoLogSegmentSize,
+						   slot->meta.tablespace, discard_path);
+
+		/* Can we recycle the oldest segment? */
+		if (end < xlrec->end)
+		{
+			char	recycle_path[MAXPGPATH];
+
+			UndoLogSegmentPath(xlrec->logno, end / UndoLogSegmentSize,
+							   slot->meta.tablespace, recycle_path);
+			if (rename(discard_path, recycle_path) == 0)
+			{
+				elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+					 discard_path, recycle_path);
+				end += UndoLogSegmentSize;
+			}
+			else
+			{
+				elog(LOG, "could not rename \"%s\" to \"%s\": %m",
+					 discard_path, recycle_path);
+			}
+		}
+		else
+		{
+			if (unlink(discard_path) == 0)
+				elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+			else
+				elog(LOG, "could not unlink \"%s\": %m", discard_path);
+		}
+		old_segment_begin += UndoLogSegmentSize;
+	}
+
+	/* Create any further new segments that are needed the slow way. */
+	while (end < xlrec->end)
+	{
+		allocate_empty_undo_segment(xlrec->logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/* Update shmem. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = xlrec->discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (xlrec->entirely_discarded)
+		free_undo_log_slot(slot);
+}
+
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
+void
+undolog_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			undolog_xlog_create(record);
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			undolog_xlog_extend(record);
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			undolog_xlog_discard(record);
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
+		default:
+			elog(PANIC, "undo_redo: unknown op code %u", info);
+	}
+}
+
+/*
+ * For assertions only.
+ */
+bool
+AmAttachedToUndoLogSlot(UndoLogSlot *slot)
+{
+	/*
+	 * In general, we can't access log's members without locking.  But this
+	 * function is intended only for asserting that you are attached, and
+	 * while you're attached the slot can't be recycled, so don't bother
+	 * locking.
+	 */
+	return CurrentSession->attached_undo_slots[slot->meta.category] == slot;
+}
+
+/*
+ * For testing use only.  This function is only used by the test_undo module.
+ */
+void
+UndoLogDetachFull(void)
+{
+	int		i;
+
+	for (i = 0; i < UndoLogCategories; ++i)
+		if (CurrentSession->attached_undo_slots[i])
+			detach_current_undo_log(i, true);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9238fbe98d..4671838dd2 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -20,6 +20,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/session.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -519,6 +520,8 @@ BootstrapModeMain(void)
 
 	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
 
+	InitializeSession();
+
 	/* Initialize stuff for bootstrap-file processing */
 	for (i = 0; i < MAXATTR; i++)
 	{
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ea4c85e395..cbe04e4dcc 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1062,6 +1062,10 @@ GRANT SELECT (subdbid, subname, subowner, subenabled, subslotname, subpublicatio
     ON pg_subscription TO public;
 
 
+CREATE VIEW pg_stat_undo_logs AS
+    SELECT *
+    FROM pg_stat_get_undo_logs();
+
 --
 -- We have a few function definitions in here, too.
 -- At some point there might be enough to justify breaking them out into
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 84efb414d8..17c28b2c66 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -55,6 +55,7 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -496,6 +497,20 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 	 */
 	LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
 
+	/*
+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));
+
 	/*
 	 * Try to remove the physical infrastructure.
 	 */
@@ -1517,6 +1532,14 @@ tblspc_redo(XLogReaderState *record)
 	{
 		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
 
+		/* This shouldn't be able to fail in recovery. */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		if (!DropUndoLogsInTablespace(xlrec->ts_id))
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("tablespace cannot be dropped because it contains non-empty undo logs")));
+		LWLockRelease(TablespaceCreateLock);
+
 		/*
 		 * If we issued a WAL record for a drop tablespace it implies that
 		 * there were no files in it at all when the DROP was done. That means
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b4f2b28b51..c742861dae 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4070,6 +4070,27 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_TWOPHASE_FILE_WRITE:
 			event_name = "TwophaseFileWrite";
 			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_READ:
+			event_name = "UndoCheckpointRead";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_WRITE:
+			event_name = "UndoCheckpointWrite";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
+			event_name = "UndoCheckpointSync";
+			break;
+		case WAIT_EVENT_UNDO_FILE_READ:
+			event_name = "UndoFileRead";
+			break;
+		case WAIT_EVENT_UNDO_FILE_WRITE:
+			event_name = "UndoFileWrite";
+			break;
+		case WAIT_EVENT_UNDO_FILE_FLUSH:
+			event_name = "UndoFileFlush";
+			break;
+		case WAIT_EVENT_UNDO_FILE_SYNC:
+			event_name = "UndoFileSync";
+			break;
 		case WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ:
 			event_name = "WALSenderTimelineHistoryRead";
 			break;
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index d5f9b617c8..abcb5e5ad2 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1368,7 +1368,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	char	   *page;
 	size_t		pad;
 	PageHeader	phdr;
-	int			segmentno = 0;
+	BlockNumber	first_blkno = 0;
 	char	   *segmentpath;
 	bool		verify_checksum = false;
 
@@ -1406,12 +1406,18 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 			segmentpath = strstr(filename, ".");
 			if (segmentpath != NULL)
 			{
-				segmentno = atoi(segmentpath + 1);
-				if (segmentno == 0)
+				char	   *end;
+				if (strstr(readfilename, "undo"))
+					first_blkno = strtol(segmentpath + 1, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath + 1, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 					ereport(ERROR,
-							(errmsg("invalid segment number %d in file \"%s\"",
-									segmentno, filename)));
+							(errmsg("invalid segment number in file \"%s\"",
+									filename)));
 			}
+			else
+				first_blkno = 0;
 		}
 	}
 
@@ -1451,7 +1457,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + first_blkno);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 151c3ef882..d3a9c4d64c 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -154,6 +154,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_COMMIT_TS_ID:
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
+		case RM_UNDOLOG_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 6f3a402854..2b1d60680e 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -177,6 +177,7 @@ static PrivateRefCountEntry *NewPrivateRefCountEntry(Buffer buffer);
 static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move);
 static inline int32 GetPrivateRefCount(Buffer buffer);
 static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref);
+static void InvalidateBuffer(BufferDesc *buf);
 
 /*
  * Ensure that the PrivateRefCountArray has sufficient space to store one more
@@ -620,10 +621,12 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
  * valid, the page is zeroed instead of throwing an error. This is intended
  * for non-critical data, where the caller is prepared to repair errors.
  *
- * In RBM_ZERO_AND_LOCK mode, if the page isn't in buffer cache already, it's
+ * In RBM_ZERO mode, if the page isn't in buffer cache already, it's
  * filled with zeros instead of reading it from disk.  Useful when the caller
  * is going to fill the page from scratch, since this saves I/O and avoids
  * unnecessary failure if the page-on-disk has corrupt page headers.
+ *
+ * In RBM_ZERO_AND_LOCK mode, the page is zeroed and also locked.
  * The page is returned locked to ensure that the caller has a chance to
  * initialize the page before it's made visible to others.
  * Caution: do not use this mode to read a page that is beyond the relation's
@@ -674,24 +677,20 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 /*
  * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
  *		a relcache entry for the relation.
- *
- * NB: At present, this function may only be used on permanent relations, which
- * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary or unlogged relations, we could pass additional
- * parameters.
  */
 Buffer
 ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
-						  BufferAccessStrategy strategy)
+						  BufferAccessStrategy strategy,
+						  char relpersistence)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
-
-	Assert(InRecovery);
+	SMgrRelation smgr = smgropen(rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+	return ReadBuffer_common(smgr, relpersistence, forkNum, blockNum,
 							 mode, strategy, &hit);
 }
 
@@ -885,7 +884,9 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * Read in the page, unless the caller intends to overwrite it and
 		 * just wants us to allocate a buffer.
 		 */
-		if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
+		if (mode == RBM_ZERO ||
+			mode == RBM_ZERO_AND_LOCK ||
+			mode == RBM_ZERO_AND_CLEANUP_LOCK)
 			MemSet((char *) bufBlock, 0, BLCKSZ);
 		else
 		{
@@ -1339,6 +1340,61 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	return buf;
 }
 
+/*
+ * ForgetBuffer -- drop a buffer from shared buffers
+ *
+ * If the buffer isn't present in shared buffers, nothing happens.  If it is
+ * present, it is discarded without making any attempt to write it back out to
+ * the operating system.  The caller must therefore somehow be sure that the
+ * data won't be needed for anything now or in the future.  It assumes that
+ * there is no concurrent access to the block, except that it might be being
+ * concurrently written.
+ */
+void
+ForgetBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
+	BufferTag	tag;			/* identity of target block */
+	uint32		hash;			/* hash value for tag */
+	LWLock	   *partitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	hash = BufTableHashCode(&tag);
+	partitionLock = BufMappingPartitionLock(hash);
+
+	/* see if the block is in the buffer pool */
+	LWLockAcquire(partitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&tag, hash);
+	LWLockRelease(partitionLock);
+
+	/* didn't find it, so nothing to do */
+	if (buf_id < 0)
+		return;
+
+	/* take the buffer header lock */
+	bufHdr = GetBufferDescriptor(buf_id);
+	buf_state = LockBufHdr(bufHdr);
+
+	/*
+	 * The buffer might been evicted after we released the partition lock and
+	 * before we acquired the buffer header lock.  If so, the buffer we've
+	 * locked might contain some other data which we shouldn't touch. If the
+	 * buffer hasn't been recycled, we proceed to invalidate it.
+	 */
+	if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+		bufHdr->tag.blockNum == blockNum &&
+		bufHdr->tag.forkNum == forkNum)
+		InvalidateBuffer(bufHdr);		/* releases spinlock */
+	else
+		UnlockBufHdr(bufHdr, buf_state);
+}
+
 /*
  * InvalidateBuffer -- mark a shared buffer invalid and return it to the
  * freelist.
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index f5f6a29222..a6fe3dbf82 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -272,6 +272,49 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	return bufHdr;
 }
 
+/*
+ * ForgetLocalBuffer - drop a buffer from local buffers
+ *
+ * This is similar to bufmgr.c's ForgetBuffer, except that we do not need
+ * to do any locking since this is all local.  As with that function, this
+ * must be used very carefully, since we'll cheerfully throw away dirty
+ * buffers without any attempt to write them.
+ */
+void
+ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(rnode, BackendIdForTempRelations());
+	BufferTag	tag;					/* identity of target block */
+	LocalBufferLookupEnt *hresult;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/*
+	 * If somehow this is the first request in the session, there's nothing to
+	 * do.  (This probably shouldn't happen, though.)
+	 */
+	if (LocalBufHash == NULL)
+		return;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* see if the block is in the local buffer pool */
+	hresult = (LocalBufferLookupEnt *)
+	hash_search(LocalBufHash, (void *) &tag, HASH_REMOVE, NULL);
+
+	/* didn't find it, so nothing to do */
+	if (!hresult)
+		return;
+
+	/* mark buffer invalid */
+	bufHdr = GetLocalBufferDescriptor(hresult->id);
+	CLEAR_BUFFERTAG(bufHdr->tag);
+	buf_state = pg_atomic_read_u32(&bufHdr->state);
+	buf_state &= ~(BM_VALID | BM_TAG_VALID | BM_DIRTY);
+	pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
+}
+
 /*
  * MarkLocalBufferDirty -
  *	  mark a local buffer dirty
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 315c74c745..3c4f0537d4 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -322,7 +322,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
 static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
 static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
 
-static int	fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
 static int	fsync_parent_path(const char *fname, int elevel);
 
 
@@ -3345,7 +3344,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
  *
  * Returns 0 if the operation succeeded, -1 otherwise.
  */
-static int
+int
 fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
 {
 	int			fd;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index d7d733530f..12c324925c 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/subtrans.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -125,6 +126,7 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, ProcGlobalShmemSize());
 		size = add_size(size, XLOGShmemSize());
 		size = add_size(size, CLOGShmemSize());
+		size = add_size(size, UndoLogShmemSize());
 		size = add_size(size, CommitTsShmemSize());
 		size = add_size(size, SUBTRANSShmemSize());
 		size = add_size(size, TwoPhaseShmemSize());
@@ -213,6 +215,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	 */
 	XLOGShmemInit();
 	CLOGShmemInit();
+	UndoLogShmemInit();
 	CommitTsShmemInit();
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index bc1aa88322..5df658d349 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -522,6 +522,8 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append");
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_HASH_JOIN, "parallel_hash_join");
 	LWLockRegisterTranche(LWTRANCHE_SXACT, "serializable_xact");
+	LWLockRegisterTranche(LWTRANCHE_UNDOLOG, "undo_log");
+	LWLockRegisterTranche(LWTRANCHE_UNDODISCARD, "undo_discard");
 
 	/* Register named tranches. */
 	for (i = 0; i < NamedLWLockTrancheRequests; i++)
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index db47843229..4b42a1cf0b 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -49,3 +49,4 @@ MultiXactTruncationLock				41
 OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
+UndoLogLock                                      45
diff --git a/src/backend/storage/smgr/Makefile b/src/backend/storage/smgr/Makefile
index e486b7c0d1..ff2e5e2db4 100644
--- a/src/backend/storage/smgr/Makefile
+++ b/src/backend/storage/smgr/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/storage/smgr
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = md.o smgr.o
+OBJS = md.o smgr.o undofile.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index d00b275e46..93df85cba9 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,11 +17,13 @@
  */
 #include "postgres.h"
 
+#include "catalog/database_internal.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/smgr.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
 
@@ -81,6 +83,24 @@ static const f_smgr smgrsw[] = {
 		.smgr_nblocks = mdnblocks,
 		.smgr_truncate = mdtruncate,
 		.smgr_immedsync = mdimmedsync,
+	},
+	/* undo logs */
+	{
+		.smgr_init = undofile_init,
+		.smgr_shutdown = undofile_shutdown,
+		.smgr_open = undofile_open,
+		.smgr_close = undofile_close,
+		.smgr_create = undofile_create,
+		.smgr_exists = undofile_exists,
+		.smgr_unlink = undofile_unlink,
+		.smgr_extend = undofile_extend,
+		.smgr_prefetch = undofile_prefetch,
+		.smgr_read = undofile_read,
+		.smgr_write = undofile_write,
+		.smgr_writeback = undofile_writeback,
+		.smgr_nblocks = undofile_nblocks,
+		.smgr_truncate = undofile_truncate,
+		.smgr_immedsync = undofile_immedsync,
 	}
 };
 
@@ -105,6 +125,8 @@ smgrwhich(RelFileNode rnode)
 {
 	switch (rnode.dbNode)
 	{
+		case UndoDbOid:
+			return 1;			/* undofile.c */
 		default:
 			return 0;			/* md.c */
 	}
@@ -189,6 +211,7 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = smgrwhich(rnode);
+		reln->private_data = NULL;
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
new file mode 100644
index 0000000000..04d4514e35
--- /dev/null
+++ b/src/backend/storage/smgr/undofile.c
@@ -0,0 +1,422 @@
+/*
+ * undofile.h
+ *
+ * PostgreSQL undo file manager.  This module provides SMGR-compatible
+ * interface to the files that back undo logs on the filesystem, so that undo
+ * log data can use the shared buffer pool.  Other aspects of undo log
+ * management are provided by undolog.c, so the SMGR interfaces not directly
+ * concerned with reading, writing and flushing data are unimplemented.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/storage/smgr/undofile.c
+ */
+
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/xlog.h"
+#include "catalog/database_internal.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgwriter.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "storage/undofile.h"
+#include "utils/memutils.h"
+
+/* Populate a file tag describing an undo segment file. */
+#define INIT_UNDOFILETAG(a,xx_logno,xx_tbspc,xx_segno) \
+( \
+	memset(&(a), 0, sizeof(FileTag)), \
+	(a).handler = SYNC_HANDLER_UNDO, \
+	(a).rnode.dbNode = UndoDbOid, \
+	(a).rnode.spcNode = (xx_tbspc), \
+	(a).rnode.relNode = (xx_logno), \
+	(a).segno = (xx_segno) \
+)
+
+/*
+ * While md.c expects random access and has a small number of huge
+ * segments, undofile.c manages a potentially very large number of smaller
+ * segments and has a less random access pattern.  Therefore, instead of
+ * keeping a potentially huge array of vfds we'll just keep the most
+ * recently accessed N.
+ *
+ * For now, N == 1, so we just need to hold onto one 'File' handle.
+ */
+typedef struct UndoFileState
+{
+	int		mru_segno;
+	File	mru_file;
+} UndoFileState;
+
+static MemoryContext UndoFileCxt;
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok);
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno);
+
+void
+undofile_init(void)
+{
+	UndoFileCxt = AllocSetContextCreate(TopMemoryContext,
+										"UndoFileSmgr",
+										ALLOCSET_DEFAULT_SIZES);
+}
+
+void
+undofile_shutdown(void)
+{
+}
+
+void
+undofile_open(SMgrRelation reln)
+{
+	UndoFileState *state;
+
+	state = MemoryContextAllocZero(UndoFileCxt, sizeof(UndoFileState));
+	reln->private_data = state;
+}
+
+void
+undofile_close(SMgrRelation reln, ForkNumber forknum)
+{
+}
+
+void
+undofile_create(SMgrRelation reln, ForkNumber forknum, bool isRedo)
+{
+	/*
+	 * File creation is managed by undolog.c, but xlogutils.c likes to call
+	 * this just in case.  Ignore.
+	 */
+}
+
+bool
+undofile_exists(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_exists is not supported");
+
+	return false;		/* not reached */
+}
+
+void
+undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo)
+{
+	elog(ERROR, "undofile_unlink is not supported");
+}
+
+void
+undofile_extend(SMgrRelation reln, ForkNumber forknum,
+				BlockNumber blocknum, char *buffer,
+				bool skipFsync)
+{
+	elog(ERROR, "undofile_extend is not supported");
+}
+
+void
+undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
+{
+	elog(ERROR, "undofile_prefetch is not supported");
+}
+
+void
+undofile_read(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+			  char *buffer)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileRead(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_READ);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+}
+
+void
+undofile_write(SMgrRelation reln, ForkNumber forknum,
+			   BlockNumber blocknum, char *buffer,
+			   bool skipFsync)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileWrite(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_WRITE);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		/*
+		 * short write: unexpected, because this should be overwriting an
+		 * entirely pre-allocated segment file
+		 */
+		ereport(ERROR,
+				(errcode(ERRCODE_DISK_FULL),
+				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+
+	/* Tell checkpointer this file is dirty. */
+	if (!skipFsync && !SmgrIsTemp(reln))
+	{
+		undofile_request_sync(reln->smgr_rnode.node.relNode,
+							  blocknum / UNDOSEG_SIZE,
+							  reln->smgr_rnode.node.spcNode);
+	}
+}
+
+void
+undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+				   BlockNumber blocknum, BlockNumber nblocks)
+{
+	while (nblocks > 0)
+	{
+		File	file;
+		int		nflush;
+
+		file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+
+		/* compute number of desired writes within the current segment */
+		nflush = Min(nblocks,
+					 1 + UNDOSEG_SIZE - (blocknum % UNDOSEG_SIZE));
+
+		FileWriteback(file,
+					  (blocknum % UNDOSEG_SIZE) * BLCKSZ,
+					  nflush * BLCKSZ, WAIT_EVENT_UNDO_FILE_FLUSH);
+
+		nblocks -= nflush;
+		blocknum += nflush;
+	}
+}
+
+BlockNumber
+undofile_nblocks(SMgrRelation reln, ForkNumber forknum)
+{
+	/*
+	 * xlogutils.c likes to call this to decide whether to read or extend; for
+	 * now we lie and say the relation is big as possible.
+	 */
+	return UndoLogMaxSize / BLCKSZ;
+}
+
+void
+undofile_truncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
+{
+	elog(ERROR, "undofile_truncate is not supported");
+}
+
+void
+undofile_immedsync(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_immedsync is not supported");
+}
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok)
+{
+	File		file;
+	char		path[MAXPGPATH];
+
+	UndoLogSegmentPath(relNode, segno, spcNode, path);
+	file = PathNameOpenFile(path, O_RDWR | PG_BINARY);
+
+	if (file <= 0 && (!missing_ok || errno != ENOENT))
+		elog(ERROR, "cannot open undo segment file '%s': %m", path);
+
+	return file;
+}
+
+/*
+ * Get a File for a particular segment of a SMgrRelation representing an undo
+ * log.
+ */
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno)
+{
+	UndoFileState *state = (UndoFileState *) reln->private_data;
+
+	/* If we have a file open already, check if we need to close it. */
+	if (state->mru_file > 0 && state->mru_segno != segno)
+	{
+		/* These are not the blocks we're looking for. */
+		FileClose(state->mru_file);
+		state->mru_file = 0;
+	}
+
+	/* Check if we need to open a new file. */
+	if (state->mru_file <= 0)
+	{
+		state->mru_file =
+			undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+									   reln->smgr_rnode.node.spcNode,
+									   segno, InRecovery);
+		if (InRecovery && state->mru_file <= 0)
+		{
+			/*
+			 * If in recovery, we may be trying to access a file that will
+			 * later be unlinked.  Tolerate missing files, creating a new
+			 * zero-filled file as required.
+			 */
+			UndoLogNewSegment(reln->smgr_rnode.node.relNode,
+							  reln->smgr_rnode.node.spcNode,
+							  segno);
+			state->mru_file =
+				undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+										   reln->smgr_rnode.node.spcNode,
+										   segno, false);
+			Assert(state->mru_file > 0);
+		}
+		state->mru_segno = segno;
+	}
+
+	return state->mru_file;
+}
+
+/*
+ * Callback to handle a queued sync request.
+ */
+int
+undofile_syncfiletag(const FileTag *tag, char *path)
+{
+	SMgrRelation reln = smgropen(tag->rnode, InvalidBackendId);
+	File		file;
+
+	if (tag->rnode.relNode == (Oid) InvalidUndoLogNumber)
+	{
+		/* Sync parent directory for this tablespace. */
+		UndoLogDirectory(tag->rnode.spcNode, path);
+
+		/* The caller (sync.c) will do appropriate error reporting. */
+		return fsync_fname_ext(path, true, false, WARNING);
+	}
+	else
+	{
+		/* Sync a segment file. */
+		UndoLogSegmentPath(tag->rnode.relNode, tag->segno, tag->rnode.spcNode,
+						   path);
+
+		file = undofile_get_segment_file(reln, tag->segno);
+		if (file <= 0)
+		{
+			/* errno set by undofile_get_segment_file() */
+			return -1;
+		}
+
+		return FileSync(file, WAIT_EVENT_UNDO_FILE_SYNC);
+	}
+}
+
+/*
+ * Filtering callback used by SYNC_FILTER_REQUEST to forget some requests.
+ */
+bool
+undofile_filetagmatches(const FileTag *tag, const FileTag *candidate)
+{
+	/*
+	 * We use SYNC_FILTER_REQUEST to forget requests for a given tablespace,
+	 * before removing all undo files in the tablespace.
+	 */
+	return tag->rnode.spcNode == candidate->rnode.spcNode;
+}
+
+/*
+ * Tell the checkpointer to sync a segment file.
+ */
+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync file \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about any sync requests for a given segment
+ * file, because it's about to go away.
+ */
+void
+undofile_forget_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Send, and keep retrying if out of space. */
+	(void) RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
+}
+
+/*
+ * Tell the checkpointer to fsync the undo directory in a given tablespace,
+ * because we have created or renamed files inside it.
+ */
+void
+undofile_request_sync_dir(Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	/* We use a special logno and segno to mean "the directory". */
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync directory \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about all sync requests for a given
+ * tablespace, because it's about to go away.
+ */
+void
+undofile_forget_sync_tablespace(Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/*
+	 * Tell checkpointer to forget about any request for this tag, and keep
+	 * waiting if there is not enough space.
+	 */
+	(void) RegisterSyncRequest(&tag, SYNC_FILTER_REQUEST, true);
+}
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index f329c3fd66..db7b417d83 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -28,6 +28,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/inval.h"
@@ -96,6 +97,11 @@ static const SyncOps syncsw[] = {
 		.sync_syncfiletag = mdsyncfiletag,
 		.sync_unlinkfiletag = mdunlinkfiletag,
 		.sync_filetagmatches = mdfiletagmatches
+	},
+	/* undo log segment files */
+	{
+		.sync_syncfiletag = undofile_syncfiletag,
+		.sync_filetagmatches = undofile_filetagmatches
 	}
 };
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 43b9f17f72..5d9af89036 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -25,6 +25,7 @@
 #include "access/session.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -561,6 +562,7 @@ BaseInit(void)
 	InitSync();
 	smgrinit();
 	InitBufferPoolAccess();
+	UndoLogInit();
 }
 
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fc463601ff..296fb77166 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -121,6 +121,7 @@ extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
+extern char *undo_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
 
@@ -3667,6 +3668,17 @@ static struct config_string ConfigureNamesString[] =
 		check_temp_tablespaces, assign_temp_tablespaces, NULL
 	},
 
+	{
+		{"undo_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the tablespace(s) to use for undo logs."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&undo_tablespaces,
+		"",
+		check_undo_tablespaces, assign_undo_tablespaces, NULL
+	},
+
 	{
 		{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
 			gettext_noop("Sets the path for dynamically loadable modules."),
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 04d77ad700..bbcbdfdb87 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -210,11 +210,13 @@ static const char *const subdirs[] = {
 	"pg_snapshots",
 	"pg_subtrans",
 	"pg_twophase",
+	"pg_undo",
 	"pg_multixact",
 	"pg_multixact/members",
 	"pg_multixact/offsets",
 	"base",
 	"base/1",
+	"base/undo",
 	"pg_replslot",
 	"pg_tblspc",
 	"pg_stat",
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 8c00ec9a3b..8e83be9d52 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -167,7 +167,7 @@ skipfile(const char *fn)
 }
 
 static void
-scan_file(const char *fn, BlockNumber segmentno)
+scan_file(const char *fn, BlockNumber first_blkno)
 {
 	PGAlignedBlock buf;
 	PageHeader	header = (PageHeader) buf.data;
@@ -208,7 +208,7 @@ scan_file(const char *fn, BlockNumber segmentno)
 		if (PageIsNew(header))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + first_blkno);
 		current_size += r;
 		if (mode == PG_MODE_CHECK)
 		{
@@ -310,7 +310,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			char		fnonly[MAXPGPATH];
 			char	   *forkpath,
 					   *segmentpath;
-			BlockNumber segmentno = 0;
+			BlockNumber first_blkno;
 
 			if (skipfile(de->d_name))
 				continue;
@@ -325,15 +325,22 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			segmentpath = strchr(fnonly, '.');
 			if (segmentpath != NULL)
 			{
+				char	   *end;
+
 				*segmentpath++ = '\0';
-				segmentno = atoi(segmentpath);
-				if (segmentno == 0)
+				if (strstr(segmentpath, "undo"))
+					first_blkno = strtol(segmentpath, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 				{
-					pg_log_error("invalid segment number %d in file name \"%s\"",
-								 segmentno, fn);
+					pg_log_error("invalid segment number in file name \"%s\"",
+								 fn);
 					exit(1);
 				}
 			}
+			else
+				first_blkno = 0;
 
 			forkpath = strchr(fnonly, '_');
 			if (forkpath != NULL)
@@ -350,7 +357,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			 * the items in the data folder.
 			 */
 			if (!sizeonly)
-				scan_file(fn, segmentno);
+				scan_file(fn, first_blkno);
 		}
 #ifndef WIN32
 		else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index ff0f8ea5e7..ad96fe75af 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -80,6 +80,7 @@ static bool ReadControlFile(void);
 static void GuessControlValues(void);
 static void PrintControlValues(bool guessed);
 static void PrintNewControlValues(void);
+static void AdjustRedoLocation(const char *DataDir);
 static void RewriteControlFile(void);
 static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
@@ -510,6 +511,7 @@ main(int argc, char *argv[])
 	/*
 	 * Else, do the dirty deed.
 	 */
+	AdjustRedoLocation(DataDir);
 	RewriteControlFile();
 	KillExistingXLOG();
 	KillExistingArchiveStatus();
@@ -889,6 +891,80 @@ PrintNewControlValues(void)
 }
 
 
+/*
+ * Compute the new redo, and move the pg_undo file to match if necessary.
+ * Rather than renaming it, we'll create a new copy, so that a failure that
+ * occurs before the controlfile is rewritten won't be fatal.
+ */
+static void
+AdjustRedoLocation(const char *DataDir)
+{
+	uint64		old_redo = ControlFile.checkPointCopy.redo;
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	int			old_fd;
+	int			new_fd;
+	ssize_t		nread;
+	ssize_t		nwritten;
+	char		buffer[1024];
+
+	/*
+	 * Adjust fields as needed to force an empty XLOG starting at
+	 * newXlogSegNo.
+	 */
+	XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
+							ControlFile.checkPointCopy.redo);
+
+	/* If the redo location didn't move, we don't need to do anything. */
+	if (old_redo == ControlFile.checkPointCopy.redo)
+		return;
+
+	/*
+	 * Otherwise we copy the pg_undo file, because its name must match the redo
+	 * location.
+	 */
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "pg_undo/%016" INT64_MODIFIER "X",
+			 old_redo);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "pg_undo/%016" INT64_MODIFIER "X",
+			 ControlFile.checkPointCopy.redo);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	new_fd = open(new_pg_undo_path, O_RDWR | O_CREAT, 0644);
+	if (new_fd < 0)
+	{
+		pg_log_error("could not create \"%s\": %m", new_pg_undo_path);
+		exit(1);
+	}
+	while ((nread = read(old_fd, buffer, sizeof(buffer))) > 0)
+	{
+		do
+		{
+			nwritten = write(new_fd, buffer, nread);
+			if (nwritten < 0)
+			{
+				pg_log_error("could not write to \"%s\": %m", new_pg_undo_path);
+				exit(1);
+			}
+			nread -= nwritten;
+		} while (nread > 0);
+	}
+	if (nread < 0)
+	{
+		pg_log_error("could not read from \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	close(old_fd);
+	close(new_fd);
+}
+
 /*
  * Write out the new pg_control file.
  */
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 2cb829acd5..82c80336c5 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -9,7 +9,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = check.o controldata.o dump.o exec.o file.o function.o info.o \
        option.o parallel.o pg_upgrade.o relfilenode.o server.o \
-       tablespace.o util.o version.o $(WIN32RES)
+       tablespace.o undo.o util.o version.o $(WIN32RES)
 
 override CPPFLAGS := -DDLSUFFIX=\"$(DLSUFFIX)\" -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 617270f101..4a431dce83 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -21,6 +21,7 @@ static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb);
 static bool equivalent_locale(int category, const char *loca, const char *locb);
 static void check_is_install_user(ClusterInfo *cluster);
 static void check_proper_datallowconn(ClusterInfo *cluster);
+static void check_for_undo_data(ClusterInfo *cluster);
 static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
@@ -97,6 +98,7 @@ check_and_dump_old_cluster(bool live_check)
 	 */
 	check_is_install_user(&old_cluster);
 	check_proper_datallowconn(&old_cluster);
+	check_for_undo_data(&old_cluster);
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
@@ -171,6 +173,7 @@ check_new_cluster(void)
 
 	check_is_install_user(&new_cluster);
 
+	check_for_undo_data(&new_cluster);
 	check_for_prepared_transactions(&new_cluster);
 }
 
@@ -800,6 +803,46 @@ check_for_prepared_transactions(ClusterInfo *cluster)
 }
 
 
+/*
+ *	check_for_live_undo_data()
+ *
+ *	Make sure there are no live undo records (aborted transactions that have
+ *	not been rolled back, or committed transactions whose undo data has not
+ *	yet been discarded).
+ */
+static void
+check_for_undo_data(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn;
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1300)
+		return;
+
+	conn = connectToServer(cluster, "template1");
+	prep_status("Checking for undo data");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_stat_undo_logs "
+							"WHERE discard != insert");
+
+	if (PQntuples(res) != 0)
+	{
+		if (cluster == &old_cluster)
+			pg_fatal("The source cluster contains live undo data\n");
+		else
+			pg_fatal("The target cluster contains live undo data\n");
+	}
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
+
+
 /*
  *	check_for_isn_and_int8_passing_mismatch()
  *
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 38236415be..523e86eeff 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -59,6 +59,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	bool		got_redo_location = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -485,6 +486,23 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "Latest checkpoint's REDO location:")) != NULL)
+		{
+			uint32		hi;
+			uint32		lo;
+
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			p++;				/* remove ':' char */
+
+			if (sscanf(p, "%X/%X", &hi, &lo) != 2)
+				pg_fatal("%d: controldata cannot parse REDO location\n", __LINE__);
+			cluster->controldata.redo_location = (((uint64) hi) << 32) | lo;
+			got_redo_location = true;
+		}
 	}
 
 	pclose(output);
@@ -528,6 +546,13 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		}
 	}
 
+	/*
+	 * If we used pg_resetwal instead of pg_controldata, there is no REDO
+	 * location.
+	 */
+	if (!got_redo_location)
+		cluster->controldata.redo_location = 0;
+
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi ||
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 0363309328..f23451a876 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -351,6 +351,10 @@ check_data_dir(ClusterInfo *cluster)
 		check_single_dir(pg_data, "pg_clog");
 	else
 		check_single_dir(pg_data, "pg_xact");
+
+	/* pg_undo is new in v13 */
+	if (GET_MAJOR_VERSION(cluster->major_version) >= 1300)
+		check_single_dir(pg_data, "pg_undo");
 }
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index d1975aab2b..09b786f472 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -139,6 +139,8 @@ main(int argc, char **argv)
 
 	/* New now using xids of the old system */
 
+	merge_undo_logs();
+
 	/* -- NEW -- */
 	start_postmaster(&new_cluster, true);
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 5d31750d86..d0d93c0682 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -227,6 +227,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	uint64		redo_location;
 } ControlData;
 
 /*
@@ -465,3 +466,7 @@ void		parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr
 										  char *old_pgdata, char *new_pgdata,
 										  char *old_tablespace);
 bool		reap_child(bool wait_for_child);
+
+/* undo.c */
+
+void		merge_undo_logs(void);
diff --git a/src/bin/pg_upgrade/undo.c b/src/bin/pg_upgrade/undo.c
new file mode 100644
index 0000000000..48397b0141
--- /dev/null
+++ b/src/bin/pg_upgrade/undo.c
@@ -0,0 +1,292 @@
+/*
+ *	undo.c
+ *
+ *	Support for upgrading undo logs.\
+ *	Copyright (c) 2019, PostgreSQL Global Development Group
+ *	src/bin/pg_upgrade/undo.c
+ */
+
+
+#include "postgres_fe.h"
+#include "pg_upgrade.h"
+#include "access/undolog.h"
+
+/*
+ * The relevant parts of UndoLogMetaDataData, in a version-independent format.
+ */
+typedef struct
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogStatus status;
+	UndoLogCategory category;
+	Oid			tablespace;
+} UndoLogInfo;
+
+/*
+ * Read the header of a pg_undo file and extract basic information.  If the
+ * format of the header changes in later versions, this may need to change
+ * depending on "cluster".
+ */
+static void
+read_pg_undo_header(int fd, ClusterInfo *cluster, UndoLogNumber *low_logno,
+					UndoLogNumber *next_logno, UndoLogNumber *num_logs)
+{
+	pg_crc32c crc;
+
+	/* Read the header, much like StartupUndoLogs(). */
+	if (read(fd, low_logno, sizeof(*low_logno)) != sizeof(*low_logno) ||
+		read(fd, next_logno, sizeof(*next_logno)) != sizeof(*next_logno) ||
+		read(fd, num_logs, sizeof(*num_logs)) != sizeof(*num_logs) ||
+		read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("pg_undo file is corrupted or cannot be read\n");
+}
+
+/*
+ * Read a single UndoLogMetaData object.  If the format changes in later
+ * versions, this may need to change to be able to read different structs
+ * depending on "cluster".
+ */
+static void
+read_one_undo_log(int fd, ClusterInfo *cluster, UndoLogInfo *info)
+{
+	UndoLogMetaData meta_data;
+	int		rc;
+
+	rc = read(fd, &meta_data, sizeof(meta_data));
+	if (rc < 0)
+		pg_fatal("could not read undo log meta-data: %m");
+	else if (rc != sizeof(meta_data))
+		pg_fatal("could not read undo log meta-data: expect %zu bytes but read only %d bytes",
+				 sizeof(meta_data), rc);
+
+	info->logno = meta_data.logno;
+	info->category = meta_data.category;
+	info->tablespace = meta_data.tablespace;
+	info->discard = meta_data.discard;
+	info->status = meta_data.status;
+}
+
+static void
+merge_undo_log(UndoLogInfo *logs, UndoLogNumber *num_logs,
+			   const UndoLogInfo *info)
+{
+	UndoLogNumber i;
+
+	/* Do we already have an entry for this logno? */
+	for (i = 0; i < *num_logs; ++i)
+	{
+		if (logs[i].logno == info->logno)
+		{
+			/*
+			 * Take the highest discard offset, so that any pointers that
+			 * originated in either cluster appear to be discarded.
+			 */
+			if (logs[i].discard < info->discard)
+				logs[i].discard = info->discard;
+
+			/*
+			 * Take the highest status so that entirely discarded logs trump
+			 * active logs.
+			 */
+			StaticAssertStmt(UNDO_LOG_STATUS_ACTIVE < UNDO_LOG_STATUS_FULL,
+							 "undo log status out of order");
+			StaticAssertStmt(UNDO_LOG_STATUS_FULL < UNDO_LOG_STATUS_DISCARDED,
+							 "undo log status out of order");
+			if (logs[i].status < info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the most persistent persistence level.  While we could
+			 * just convert them all to permanent and it wouldn't hurt, it's
+			 * probably a better idea to keep about the same number of each
+			 * persistence level, so that a system that has stabilized with
+			 * those numbers will continue to be stable after the upgrade (ie
+			 * not suddenly need to create more undo logs of different
+			 * levels).  The most permanent is the best choice, because TEMP
+			 * undo logs might be rewound in future.
+			 */
+			StaticAssertStmt(UNDO_PERMANENT < UNDO_UNLOGGED,
+							 "undo log persistent out of order");
+			StaticAssertStmt(UNDO_UNLOGGED < UNDO_TEMP,
+							 "undo log persistent out of order");
+			if (logs[i].status > info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the highest tablespace OID.  The choice of 'highest' is
+			 * arbitrary (we don't really expect the new cluster to have more
+			 * than one log), but it seems useful to preserve the distribution
+			 * of tablespaces from the old cluster for stability, as above.
+			 */
+			if (logs[i].tablespace < info->tablespace)
+				logs[i].tablespace = info->tablespace;
+			break;
+		}
+	}
+
+	/* Otherwise create a new entry. */
+	logs[*num_logs++] = *info;
+}
+
+/*
+ * We need to merge the old undo logs and the new undo logs.  We know that
+ * there is no live undo data (see check_for_live_undo_data()), but we need to
+ * make sure that any undo record pointers that exist in the old OR new
+ * cluster appear as discarded.  That is, any log numbers that are entirely
+ * discarded in either cluster appear as entirely discarded, and we retain
+ * the higher of the discard pointers in any log that is active.  This is
+ * mostly a theoretical concern for now, but perhaps a future release will be
+ * able to create higher undo record pointers during initdb than the old
+ * cluster had, so let's use an algorithm that doesn't make any assumptions
+ * about that.
+ */
+void
+merge_undo_logs(void)
+{
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	UndoLogInfo *logs;
+	UndoLogNumber num_logs;
+	UndoLogNumber num_old_logs;
+	UndoLogNumber old_low_logno;
+	UndoLogNumber old_next_logno;
+	UndoLogNumber num_new_logs;
+	UndoLogNumber new_low_logno;
+	UndoLogNumber new_next_logno;
+	UndoLogNumber i;
+	int			old_fd;
+	int			new_fd;
+	pg_crc32c	crc;
+
+	/* If the old cluster has no undo logs, there is nothing to do */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1300)
+		return;
+
+	/*
+	 * Open the pg_undo files corresponding to the old and new redo locations.
+	 * First, we'll reload pg_controldata output, so that we have up-to-date
+	 * redo locations.
+	 */
+	get_control_data(&old_cluster, true);
+	get_control_data(&new_cluster, true);
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 old_cluster.pgdata,
+			 old_cluster.controldata.redo_location);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 new_cluster.pgdata,
+			 new_cluster.controldata.redo_location);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", old_pg_undo_path);
+	new_fd = open(new_pg_undo_path, O_RDWR, 0);
+	if (new_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", new_pg_undo_path);
+
+	/* Read the headers */
+	read_pg_undo_header(old_fd, &old_cluster, &old_low_logno, &old_next_logno, &num_old_logs);
+	read_pg_undo_header(new_fd, &new_cluster, &new_low_logno, &new_next_logno, &num_new_logs);
+
+	/* Allocate workspace that is sure to be enough for the merged set */
+	logs = malloc(sizeof(*logs) * (num_old_logs + num_new_logs));
+	if (logs == NULL)
+	{
+		pg_fatal("out of memory\n");
+		exit(1);
+	}
+	num_logs = 0;
+
+	/*
+	 * Anything below the "low" logno has been entirely discarded, so we'll
+	 * take the higher of the two values.  Likewise, the "next" log number to
+	 * allocate should be the higher of the two.
+	 */
+	new_low_logno = Max(old_low_logno, new_low_logno);
+	new_next_logno = Max(old_next_logno, new_next_logno);
+
+	/* Merge in the old logs */
+	while (num_old_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(old_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_old_logs;
+	}
+
+	/* Merge in the new logs */
+	while (num_new_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(new_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_new_logs;
+	}
+
+	close(old_fd);
+
+	/* Now write out the new file, much like CheckPointUndoLogs() */
+	if (ftruncate(new_fd, 0) < 0)
+		pg_fatal("could not truncate file \"%s\": %m", new_pg_undo_path);
+	if (lseek(new_fd, SEEK_SET, 0) < 0)
+		pg_fatal("could not seek to start of file \"%s\": %m", new_pg_undo_path);
+
+	/* Compute header checksum */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &new_low_logno, sizeof(new_low_logno));
+	COMP_CRC32C(crc, &new_next_logno, sizeof(new_next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the header */
+	if ((write(new_fd, &new_low_logno, sizeof(new_low_logno)) != sizeof(new_low_logno)) ||
+		(write(new_fd, &new_next_logno, sizeof(new_next_logno)) != sizeof(new_next_logno)) ||
+		(write(new_fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(new_fd, &crc, sizeof(crc)) != sizeof(crc)))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	/* Write out the undo logs */
+	INIT_CRC32C(crc);
+	for (i = 0; i < num_logs; ++i)
+	{
+		UndoLogMetaData	meta_data;
+		UndoLogInfo	*info = &logs[i];
+		UndoLogOffset end;
+
+		memset(&meta_data, 0, sizeof(meta_data));
+		meta_data.logno = info->logno;
+
+		/*
+		 * Round the discard offset up so that it points to the first byte in
+		 * a segment, and assign that to all three offsets.  That means there
+		 * is no logical data, and there are no physical files.
+		 */
+		end = ((info->discard + UndoLogSegmentSize - 1) / UndoLogSegmentSize)
+			* UndoLogSegmentSize;
+		meta_data.unlogged.insert = meta_data.discard = meta_data.end = end;
+
+		/*
+		 * We have whatever was the highest status (though it probably
+		 * wouldn't hurt if we set them all to ACTIVE).
+		 */
+		meta_data.status = info->status;
+
+
+		if (write(new_fd, &meta_data, sizeof(meta_data)) != sizeof(meta_data))
+			pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+		COMP_CRC32C(crc, &meta_data, sizeof(meta_data));
+	}
+	FIN_CRC32C(crc);
+
+	if (write(new_fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	close(new_fd);
+	free(logs);
+}
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca4b1..938150dd91 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 3c0db2ccf5..6945e3e950 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
diff --git a/src/include/access/session.h b/src/include/access/session.h
index 8fba568029..da0770e614 100644
--- a/src/include/access/session.h
+++ b/src/include/access/session.h
@@ -17,6 +17,9 @@
 /* Avoid including typcache.h */
 struct SharedRecordTypmodRegistry;
 
+/* Avoid including undolog.h */
+struct UndoLogSlot;
+
 /*
  * A struct encapsulating some elements of a user's session.  For now this
  * manages state that applies to parallel query, but it principle it could
@@ -27,6 +30,10 @@ typedef struct Session
 	dsm_segment *segment;		/* The session-scoped DSM segment. */
 	dsa_area   *area;			/* The session-scoped DSA area. */
 
+	/* State managed by undolog.c. */
+	struct UndoLogSlot *attached_undo_slots[4];		/* UndoLogCategories */
+	bool		need_to_choose_undo_tablespace;
+
 	/* State managed by typcache.c. */
 	struct SharedRecordTypmodRegistry *shared_typmod_registry;
 	dshash_table *shared_record_table;
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
new file mode 100644
index 0000000000..c4a6b29ef0
--- /dev/null
+++ b/src/include/access/undolog.h
@@ -0,0 +1,489 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.h
+ *
+ * PostgreSQL undo log manager.  This module is responsible for lifecycle
+ * management of undo logs and backing files, associating undo logs with
+ * backends, allocating and managing space within undo logs.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_H
+#define UNDOLOG_H
+
+#include "access/transam.h"
+#include "access/xlogreader.h"
+#include "catalog/database_internal.h"
+#include "catalog/pg_class.h"
+#include "common/relpath.h"
+#include "storage/bufpage.h"
+
+#ifndef FRONTEND
+#include "storage/lwlock.h"
+#endif
+
+/* The type used to identify an undo log and position within it. */
+typedef uint64 UndoRecPtr;
+
+/* The type used for undo record lengths. */
+typedef uint16 UndoRecordSize;
+
+/* Undo log statuses. */
+typedef enum
+{
+	UNDO_LOG_STATUS_UNUSED = 0,
+	UNDO_LOG_STATUS_ACTIVE,
+	UNDO_LOG_STATUS_FULL,
+	UNDO_LOG_STATUS_DISCARDED
+} UndoLogStatus;
+
+/*
+ * Undo log categories.  These correspond to the different persistence levels
+ * of relations so that we can discard unlogged and temporary undo data
+ * wholesale in some circumstance.  We also have a separate category for
+ * 'shared' records that are not associated with a single transactions.  Since
+ * they might live longer than the transaction that created them, and since we
+ * prefer to avoid interleaving records that don't belong to the same
+ * transaction, we keep them separate.
+ */
+typedef enum
+{
+	UNDO_PERMANENT = 0,
+	UNDO_UNLOGGED = 1,
+	UNDO_TEMP = 2,
+	UNDO_SHARED = 3
+} UndoLogCategory;
+
+#define UndoLogCategories 4
+
+/*
+ * Convert from relpersistence ('p', 'u', 't') to an UndoLogCategory
+ * enumerator.
+ */
+#define UndoLogCategoryForRelPersistence(rp)						\
+	((rp) == RELPERSISTENCE_PERMANENT ? UNDO_PERMANENT :			\
+	 (rp) == RELPERSISTENCE_UNLOGGED ? UNDO_UNLOGGED : UNDO_TEMP)
+
+/*
+ * Convert from UndoLogCategory to a relpersistence value.  There is no
+ * relpersistence level for UNDO_SHARED, but the only use of this macro is to
+ * pass a value to ReadBufferWithoutRelcache, which cares only about detecting
+ * RELPERSISTENCE_TEMP.  XXX There must be a better way.
+ */
+#define RelPersistenceForUndoLogCategory(up)				\
+	((up) == UNDO_PERMANENT ? RELPERSISTENCE_PERMANENT :	\
+	 (up) == UNDO_UNLOGGED ? RELPERSISTENCE_UNLOGGED :		\
+	 (up) == UNDO_SHARED ? RELPERSISTENCE_PERMANENT :		\
+	 RELPERSISTENCE_TEMP)
+
+/*
+ * Get the appropriate UndoLogCategory value from a Relation.
+ */
+#define UndoLogCategoryForRelation(rel)									\
+	(UndoLogCategoryForRelPersistence((rel)->rd_rel->relpersistence))
+
+/* Type for offsets within undo logs */
+typedef uint64 UndoLogOffset;
+
+/* printf-family format string for UndoRecPtr. */
+#define UndoRecPtrFormat "%016" INT64_MODIFIER "X"
+
+/* printf-family format string for UndoLogOffset. */
+#define UndoLogOffsetFormat UINT64_FORMAT
+
+/* Number of blocks of BLCKSZ in an undo log segment file.  128 = 1MB. */
+#define UNDOSEG_SIZE 128
+
+/* Size of an undo log segment file in bytes. */
+#define UndoLogSegmentSize ((size_t) BLCKSZ * UNDOSEG_SIZE)
+
+/* The width of an undo log number in bits.  24 allows for 16.7m logs. */
+#define UndoLogNumberBits 24
+
+/* The maximum valid undo log number. */
+#define MaxUndoLogNumber ((1 << UndoLogNumberBits) - 1)
+
+/* The width of an undo log offset in bits.  40 allows for 1TB per log.*/
+#define UndoLogOffsetBits (64 - UndoLogNumberBits)
+
+/* Special value for undo record pointer which indicates that it is invalid. */
+#define	InvalidUndoRecPtr	((UndoRecPtr) 0)
+
+/* End-of-list value when building linked lists of undo logs. */
+#define InvalidUndoLogNumber -1
+
+/*
+ * The maximum amount of data that can be stored in an undo log.  Can be set
+ * artificially low to test full log behavior.
+ */
+#define UndoLogMaxSize ((UndoLogOffset) 1 << UndoLogOffsetBits)
+
+/* Type for numbering undo logs. */
+typedef int UndoLogNumber;
+
+/* Extract the undo log number from an UndoRecPtr. */
+#define UndoRecPtrGetLogNo(urp)					\
+	((urp) >> UndoLogOffsetBits)
+
+/* Extract the offset from an UndoRecPtr. */
+#define UndoRecPtrGetOffset(urp)				\
+	((urp) & ((UINT64CONST(1) << UndoLogOffsetBits) - 1))
+
+/* Make an UndoRecPtr from an log number and offset. */
+#define MakeUndoRecPtr(logno, offset)			\
+	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
+
+/* The number of unusable bytes in the header of each block. */
+#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+
+/* The number of usable bytes we can store per block. */
+#define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
+
+/* Length of undo checkpoint filename */
+#define UNDO_CHECKPOINT_FILENAME_LENGTH	16
+
+/*
+ * UndoRecPtrIsValid
+ *		True iff undoRecPtr is valid.
+ */
+#define UndoRecPtrIsValid(undoRecPtr) \
+	((bool) ((UndoRecPtr) (undoRecPtr) != InvalidUndoRecPtr))
+
+/* Extract the relnode for an undo log. */
+#define UndoRecPtrGetRelNode(urp)				\
+	UndoRecPtrGetLogNo(urp)
+
+/* The only valid fork number for undo log buffers. */
+#define UndoLogForkNum MAIN_FORKNUM
+
+/* Compute the block number that holds a given UndoRecPtr. */
+#define UndoRecPtrGetBlockNum(urp)				\
+	(UndoRecPtrGetOffset(urp) / BLCKSZ)
+
+/* Compute the offset of a given UndoRecPtr in the page that holds it. */
+#define UndoRecPtrGetPageOffset(urp)			\
+	(UndoRecPtrGetOffset(urp) % BLCKSZ)
+
+/* Compare two undo checkpoint files to find the oldest file. */
+#define UndoCheckPointFilenamePrecedes(file1, file2)	\
+	(strcmp(file1, file2) < 0)
+
+/* What is the offset of the i'th non-header byte? */
+#define UndoLogOffsetFromUsableByteNo(i)								\
+	(((i) / UndoLogUsableBytesPerPage) * BLCKSZ +						\
+	 UndoLogBlockHeaderSize +											\
+	 ((i) % UndoLogUsableBytesPerPage))
+
+/* How many non-header bytes are there before a given offset? */
+#define UndoLogOffsetToUsableByteNo(offset)				\
+	(((offset) % BLCKSZ - UndoLogBlockHeaderSize) +		\
+	 ((offset) / BLCKSZ) * UndoLogUsableBytesPerPage)
+
+/* Add 'n' usable bytes to offset stepping over headers to find new offset. */
+#define UndoLogOffsetPlusUsableBytes(offset, n)							\
+	UndoLogOffsetFromUsableByteNo(UndoLogOffsetToUsableByteNo(offset) + (n))
+
+/* Populate a RelFileNode from an UndoRecPtr. */
+#define UndoRecPtrAssignRelFileNode(rfn, urp)								 \
+	do																		 \
+	{																		 \
+		(rfn).spcNode = UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp)); \
+		(rfn).dbNode = UndoDbOid;											 \
+		(rfn).relNode = UndoRecPtrGetRelNode(urp);							 \
+	} while (false);
+
+/*
+ * Properties of an undo log that don't have explicit WAL records logging
+ * their changes, to reduce WAL volume.  Instead, they change incrementally
+ * whenever data is inserted as a result of other WAL records.  Since the
+ * values recorded in an online checkpoint may be out of the sync (ie not the
+ * correct values as at the redo LSN), these are backed up in buffer data on
+ * first change after each checkpoint.
+ */
+typedef struct UndoLogUnloggedMetaData
+{
+	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
+	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
+	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
+} UndoLogUnloggedMetaData;
+
+/*
+ * Control metadata for an active undo log.  Lives in shared memory inside an
+ * UndoLogSlot object, but also written to disk during checkpoints.
+ */
+typedef struct UndoLogMetaData
+{
+	/* Members that are not managed by explicit WAL logs. */
+	UndoLogUnloggedMetaData unlogged;
+
+	/* Members that are fixed for the lifetime of the undo log. */
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoLogCategory category;
+
+	/* Members that are changed by explicit WAL records. */
+	UndoLogStatus status;
+	UndoLogOffset end;				/* one past end of highest segment */
+	UndoLogOffset discard;			/* oldest data needed (tail) */
+} UndoLogMetaData;
+
+/*
+ * Context used to hold undo log state across all the undo log insertions
+ * corresponding to a single WAL record.
+ */
+typedef struct UndoLogAllocContext
+{
+	UndoLogCategory category;
+	UndoRecPtr	try_location;
+	XLogReaderState *xlog_record;
+	UndoLogNumber recovery_logno;
+	uint8 		recovery_block_id;
+	bool		new_shared_record_set;
+
+	/*
+	 * The maximum number of undo logs that a single WAL record could insert
+	 * into, modifying its unlogged meta data.  Typically the number is 1, but
+	 * it might touch a couple or more in rare cases where space runs out.
+	 */
+#define MAX_META_DATA_IMAGES 4
+	int			num_meta_data_images;
+	struct
+	{
+		UndoLogNumber logno;
+		UndoLogUnloggedMetaData data;
+	} meta_data_images[MAX_META_DATA_IMAGES];
+} UndoLogAllocContext;
+
+#ifndef FRONTEND
+
+/*
+ * The in-memory control object for an undo log.  We have a fixed-sized array
+ * of these.
+ */
+typedef struct UndoLogSlot
+{
+	/*
+	 * Protected by UndoLogLock and 'mutex'.  Both must be held to steal this
+	 * slot for another undolog.  Either may be held to prevent that from
+	 * happening.
+	 */
+	UndoLogNumber logno;			/* InvalidUndoLogNumber for unused slots */
+
+	/* Protected by UndoLogLock. */
+	UndoLogNumber next_free;		/* link for active unattached undo logs */
+
+	/* Protected by 'mutex'. */
+	LWLock		mutex;
+	UndoLogMetaData meta;			/* current meta-data */
+	pid_t		pid;				/* InvalidPid for unattached */
+
+	/* Protected by 'discard_lock'.  State used by undo workers. */
+	FullTransactionId	wait_fxmin;		/* trigger for processing this log again */
+	UndoRecPtr	oldest_data;
+	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
+} UndoLogSlot;
+
+extern UndoLogSlot *UndoLogGetSlot(UndoLogNumber logno, bool missing_ok);
+extern UndoLogSlot *UndoLogNextSlot(UndoLogSlot *slot);
+extern bool AmAttachedToUndoLogSlot(UndoLogSlot *slot);
+extern UndoRecPtr UndoLogGetOldestRecord(UndoLogNumber logno, bool *full);
+
+/*
+ * Each backend maintains a small hash table mapping undo log numbers to
+ * UndoLogSlot objects in shared memory.
+ *
+ * We also cache the tablespace, category and a recently observed discard
+ * pointer here, since we need fast access to those.  We could also reach them
+ * via slot->meta, but they can't be accessed without locking (since the
+ * UndoLogSlot object might be recycled if the log is entirely discard).
+ * Since tablespace and category are constant for lifetime of the undo log
+ * number, and the discard pointer only travels in one direction, there is no
+ * cache invalidation problem to worry about.
+ */
+typedef struct UndoLogTableEntry
+{
+	UndoLogNumber	number;
+	UndoLogSlot	   *slot;
+	Oid				tablespace;
+	UndoLogCategory category;
+	UndoRecPtr		recent_discard;
+	char			status;			/* used by simplehash */
+} UndoLogTableEntry;
+
+/*
+ * Instantiate fast inline hash table access functions.  We use an identity
+ * hash function for speed, since we already have integers and don't expect
+ * many collisions.
+ */
+#define SH_PREFIX undologtable
+#define SH_ELEMENT_TYPE UndoLogTableEntry
+#define SH_KEY_TYPE UndoLogNumber
+#define SH_KEY number
+#define SH_HASH_KEY(tb, key) (key)
+#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_SCOPE static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+extern PGDLLIMPORT undologtable_hash *undologtable_cache;
+
+/*
+ * Find or create an UndoLogTableGetEntry for this log number.  This is used
+ * only for fast look-ups of tablespace and persistence.
+ */
+static pg_attribute_always_inline UndoLogTableEntry *
+UndoLogGetTableEntry(UndoLogNumber logno)
+{
+	UndoLogTableEntry  *entry;
+
+	/* Fast path. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		return entry;
+
+	/* Slow path: force cache entry to be created. */
+	UndoLogGetSlot(logno, false);
+	entry = undologtable_lookup(undologtable_cache, logno);
+
+	return entry;
+}
+
+/*
+ * Look up the tablespace for an undo log in our cache.
+ */
+static inline Oid
+UndoLogNumberGetTablespace(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->tablespace;
+}
+
+static inline Oid
+UndoRecPtrGetTablespace(UndoRecPtr urp)
+{
+	return UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp));
+}
+
+/*
+ * Look up the category for an undo log in our cache.
+ */
+static inline UndoLogCategory
+UndoLogNumberGetCategory(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->category;
+}
+
+static inline UndoLogCategory
+UndoRecPtrGetCategory(UndoRecPtr urp)
+{
+	return UndoLogNumberGetCategory(UndoRecPtrGetLogNo(urp));
+}
+
+#endif
+
+/* Space management. */
+extern void UndoLogBeginInsert(UndoLogAllocContext *context,
+							   UndoLogCategory category,
+							   XLogReaderState *xlog_record);
+extern void UndoLogRegister(UndoLogAllocContext *context,
+							uint8 block_id,
+							UndoLogNumber logno);
+extern UndoRecPtr UndoLogAllocate(UndoLogAllocContext *context,
+								  uint16 size,
+								  bool *need_xact_header,
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
+extern UndoRecPtr UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+											TransactionId xid,
+											uint16 size,
+											bool *need_xact_header,
+											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp);
+extern void UndoLogAdvance(UndoLogAllocContext *context, size_t size);
+extern void UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size);
+extern bool UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
+extern bool UndoLogRecPtrIsDiscardedSlowPath(UndoRecPtr pointer);
+
+#ifndef FRONTEND
+
+/*
+ * Check if an undo log pointer is discarded.
+ */
+static inline bool
+UndoRecPtrIsDiscarded(UndoRecPtr pointer)
+{
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(pointer);
+	UndoRecPtr		recent_discard;
+
+	/* See if we can answer the question without acquiring any locks. */
+	recent_discard = UndoLogGetTableEntry(logno)->recent_discard;
+	if (likely(recent_discard > pointer))
+		return true;
+
+	/*
+	 * It might be discarded or not, but we'll need to do a bit more work to
+	 * find out.
+	 */
+	return UndoLogRecPtrIsDiscardedSlowPath(pointer);
+}
+
+#endif
+
+/* Initialization interfaces. */
+extern void StartupUndoLogs(XLogRecPtr checkPointRedo);
+extern void UndoLogShmemInit(void);
+extern Size UndoLogShmemSize(void);
+extern void UndoLogInit(void);
+extern void UndoLogDirectory(Oid tablespace, char *path);
+extern void UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace,
+							   char *path);
+extern void ResetUndoLogs(UndoLogCategory category);
+
+/* Interface use by tablespace.c. */
+extern bool DropUndoLogsInTablespace(Oid tablespace);
+
+/* GUC interfaces. */
+extern void assign_undo_tablespaces(const char *newval, void *extra);
+
+/* Checkpointing interfaces. */
+extern void CheckPointUndoLogs(XLogRecPtr checkPointRedo,
+							   XLogRecPtr priorCheckPointRedo);
+
+/* File sync request management. */
+
+
+extern UndoRecPtr UndoLogGetLastXactStartPoint(UndoLogNumber logno);
+extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
+extern void UndoLogSetLSN(XLogRecPtr lsn);
+void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
+/* Redo interface. */
+extern void undolog_redo(XLogReaderState *record);
+/* Discard the undo logs for temp tables */
+extern void TempUndoDiscard(UndoLogNumber);
+
+/* Test-only interfacing. */
+extern void UndoLogDetachFull(void);
+
+#endif
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
new file mode 100644
index 0000000000..aa9003ba4b
--- /dev/null
+++ b/src/include/access/undolog_xlog.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog_xlog.h
+ *	  undo log access XLOG definitions.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_XLOG_H
+#define UNDOLOG_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+
+/* XLOG records */
+#define XLOG_UNDOLOG_CREATE		0x00
+#define XLOG_UNDOLOG_EXTEND		0x10
+#define XLOG_UNDOLOG_DISCARD	0x20
+#define XLOG_UNDOLOG_SWITCH		0x30
+
+/* Create a new undo log. */
+typedef struct xl_undolog_create
+{
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoLogCategory category;
+} xl_undolog_create;
+
+/* Extend an undo log by adding a new segment. */
+typedef struct xl_undolog_extend
+{
+	UndoLogNumber logno;
+	UndoLogOffset end;
+} xl_undolog_extend;
+
+/* Discard space, and possibly destroy or recycle undo log segments. */
+typedef struct xl_undolog_discard
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	TransactionId latestxid;	/* latest xid whose undolog are discarded. */
+	bool		  entirely_discarded;
+} xl_undolog_discard;
+
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
+extern void undolog_desc(StringInfo buf,XLogReaderState *record);
+extern const char *undolog_identify(uint8 info);
+
+#endif
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59904..4db68f1082 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -44,6 +44,20 @@ extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record,
 extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
+extern bool XLogFindBlockId(XLogReaderState *record,
+							RelFileNode rnode,
+							ForkNumber forknum,
+							BlockNumber blockno,
+							uint8 *block_id);
+
+extern XLogRedoAction XLogReadBufferForRedoBlock(XLogReaderState *record,
+												 RelFileNode rnode,
+												 ForkNumber forknum,
+												 BlockNumber blockno,
+												 ReadBufferMode mode,
+												 bool get_cleanup_lock,
+												 Buffer *buf);
+
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
diff --git a/src/include/catalog/database_internal.h b/src/include/catalog/database_internal.h
new file mode 100644
index 0000000000..a2b3a122ef
--- /dev/null
+++ b/src/include/catalog/database_internal.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * database_internal.h
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/database_internal.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DATABASE_INTERNAL_H
+#define DATABASE_INTERNAL_H
+
+/*
+ * We use this header to define the OIDs of pseudo-database OIDs used in
+ * buffer tags to hold system data.
+ */
+#define UndoDbOid 9
+
+#endif							/* DATABASE_INTERNAL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0902dce5f1..de475cb2d4 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10701,4 +10701,11 @@
   proname => 'pg_partition_root', prorettype => 'regclass',
   proargtypes => 'regclass', prosrc => 'pg_partition_root' },
 
+# undo logs
+{ oid => '5032', descr => 'list undo logs',
+  proname => 'pg_stat_get_undo_logs', procost => '1', prorows => '10', proretset => 't',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,text,text,text,text,text,xid,int4,text}', proargmodes => '{o,o,o,o,o,o,o,o,o}',
+  proargnames => '{logno,category,tablespace,discard,insert,end,xid,pid,status}', prosrc => 'pg_stat_get_undo_logs' },
+
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0a3ad3a188..1936c5db6f 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -934,6 +934,13 @@ typedef enum
 	WAIT_EVENT_TWOPHASE_FILE_READ,
 	WAIT_EVENT_TWOPHASE_FILE_SYNC,
 	WAIT_EVENT_TWOPHASE_FILE_WRITE,
+	WAIT_EVENT_UNDO_CHECKPOINT_READ,
+	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
+	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_READ,
+	WAIT_EVENT_UNDO_FILE_WRITE,
+	WAIT_EVENT_UNDO_FILE_FLUSH,
+	WAIT_EVENT_UNDO_FILE_SYNC,
 	WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ,
 	WAIT_EVENT_WAL_BOOTSTRAP_SYNC,
 	WAIT_EVENT_WAL_BOOTSTRAP_WRITE,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 509f4b7ef1..a04190aa92 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -37,8 +37,9 @@ typedef enum BufferAccessStrategyType
 typedef enum
 {
 	RBM_NORMAL,					/* Normal read */
-	RBM_ZERO_AND_LOCK,			/* Don't read from disk, caller will
-								 * initialize. Also locks the page. */
+	RBM_ZERO,					/* Don't read from disk, caller will
+								 * initialize. */
+	RBM_ZERO_AND_LOCK,			/* Like RBM_ZERO, but also locks the page. */
 	RBM_ZERO_AND_CLEANUP_LOCK,	/* Like RBM_ZERO_AND_LOCK, but locks the page
 								 * in "cleanup" mode */
 	RBM_ZERO_ON_ERROR,			/* Read, but return an all-zeros page on error */
@@ -170,7 +171,10 @@ extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BufferAccessStrategy strategy);
 extern Buffer ReadBufferWithoutRelcache(RelFileNode rnode,
 										ForkNumber forkNum, BlockNumber blockNum,
-										ReadBufferMode mode, BufferAccessStrategy strategy);
+										ReadBufferMode mode, BufferAccessStrategy strategy,
+										char relpersistence);
+extern void ForgetBuffer(RelFileNode rnode, ForkNumber forkNum,
+						 BlockNumber blockNum);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -227,6 +231,10 @@ extern void AtProcExit_LocalBuffers(void);
 
 extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation);
 
+/* in localbuf.c */
+extern void ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum,
+							  BlockNumber blockNum);
+
 /* in freelist.c */
 extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
 extern void FreeAccessStrategy(BufferAccessStrategy strategy);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index d2a8c52044..b215201f30 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -143,6 +143,7 @@ extern int	pg_fsync_writethrough(int fd);
 extern int	pg_fdatasync(int fd);
 extern void pg_flush_data(int fd, off_t offset, off_t amount);
 extern void fsync_fname(const char *fname, bool isdir);
+extern int	fsync_fname_ext(const char *fname, bool isdir, bool perm, int elevel);
 extern int	durable_rename(const char *oldfile, const char *newfile, int loglevel);
 extern int	durable_unlink(const char *fname, int loglevel);
 extern int	durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 08e0dc8144..4abb344aec 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -220,7 +220,10 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_SXACT,
-	LWTRANCHE_FIRST_USER_DEFINED
+	LWTRANCHE_UNDOLOG,
+	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_REWIND,
+	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
 /*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index d286c8c7b1..0fd3b7505d 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -70,6 +70,9 @@ typedef struct SMgrRelationData
 	int			md_num_open_segs[MAX_FORKNUM + 1];
 	struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
 
+	/* For use by implementations. */
+	void	   *private_data;
+
 	/* if unowned, list link in list of all unowned SMgrRelations */
 	dlist_node	node;
 } SMgrRelationData;
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index 16428c5f5f..59f1da9178 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -34,7 +34,8 @@ typedef enum SyncRequestType
  */
 typedef enum SyncRequestHandler
 {
-	SYNC_HANDLER_MD = 0			/* md smgr */
+	SYNC_HANDLER_MD = 0,		/* md smgr */
+	SYNC_HANDLER_UNDO = 1		/* undo smgr */
 } SyncRequestHandler;
 
 /*
diff --git a/src/include/storage/undofile.h b/src/include/storage/undofile.h
new file mode 100644
index 0000000000..a5ec30fa7a
--- /dev/null
+++ b/src/include/storage/undofile.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * undofile.h
+ *	  undo storage manager public interface declarations.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/undofile.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOFILE_H
+#define UNDOFILE_H
+
+#include "access/undolog.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "storage/smgr.h"
+#include "storage/sync.h"
+
+extern void undofile_init(void);
+extern void undofile_shutdown(void);
+extern void undofile_open(SMgrRelation reln);
+extern void undofile_close(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_create(SMgrRelation reln, ForkNumber forknum,
+							bool isRedo);
+extern bool undofile_exists(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum,
+							bool isRedo);
+extern void undofile_extend(SMgrRelation reln, ForkNumber forknum,
+		 BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_prefetch(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber blocknum);
+extern void undofile_read(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, char *buffer);
+extern void undofile_write(SMgrRelation reln, ForkNumber forknum,
+		BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+			BlockNumber blocknum, BlockNumber nblocks);
+extern BlockNumber undofile_nblocks(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_truncate(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber nblocks);
+extern void undofile_immedsync(SMgrRelation reln, ForkNumber forknum);
+
+/* Callbacks used by sync.c. */
+extern int undofile_syncfiletag(const FileTag *tag, char *path);
+extern bool undofile_filetagmatches(const FileTag *tag, const FileTag *candidate);
+
+/* Management of checkpointer requests. */
+extern void undofile_request_sync(UndoLogNumber logno, BlockNumber segno,
+								  Oid tablespace);
+extern void undofile_forget_sync(UndoLogNumber logno, BlockNumber segno,
+								 Oid tablespace);
+extern void undofile_forget_sync_tablespace(Oid tablespace);
+extern void undofile_request_sync_dir(Oid tablespace);
+
+#endif							/* UNDOFILE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e709177c37..84639eed22 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -431,6 +431,8 @@ extern void GUC_check_errcode(int sqlerrcode);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
+extern bool check_undo_tablespaces(char **newval, void **extra, GucSource source);
+extern void assign_undo_tablespaces(const char *newval, void *extra);
 
 /* in catalog/namespace.c */
 extern bool check_search_path(char **newval, void **extra, GucSource source);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 210e9cd146..7098461dde 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2010,6 +2010,16 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text));
+pg_stat_undo_logs| SELECT pg_stat_get_undo_logs.logno,
+    pg_stat_get_undo_logs.category,
+    pg_stat_get_undo_logs.tablespace,
+    pg_stat_get_undo_logs.discard,
+    pg_stat_get_undo_logs.insert,
+    pg_stat_get_undo_logs."end",
+    pg_stat_get_undo_logs.xid,
+    pg_stat_get_undo_logs.pid,
+    pg_stat_get_undo_logs.status
+   FROM pg_stat_get_undo_logs() pg_stat_get_undo_logs(logno, category, tablespace, discard, insert, "end", xid, pid, status);
 pg_stat_user_functions| SELECT p.oid AS funcid,
     n.nspname AS schemaname,
     p.proname AS funcname,
-- 
2.16.2.windows.1

0004-Allow-WAL-record-data-on-first-modification-after-a-.patchapplication/octet-stream; name=0004-Allow-WAL-record-data-on-first-modification-after-a-.patchDownload
From 3d3565b64e2c2fd3a47c6fbdfa42d542923d44d4 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Sat, 12 Jan 2019 02:17:02 +1300
Subject: [PATCH 04/13] Allow WAL record data on first modification after a
 checkpoint.

Provide a way to attach data to WAL record conditionally, so that it is
included only if this turns out to the be first modification to a given
block after a checkpoint.

This will be used to record undo log meta-data, to avoid a data
synchronization problem with online checkpoints.

Author: Thomas Munro
Reviewed-by:
Discussion:
---
 src/backend/access/transam/xloginsert.c | 15 +++++++++++++++
 src/include/access/xloginsert.h         |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d468b..001d7cb05b 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -565,6 +565,21 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			needs_data = false;
 		else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
 			needs_data = true;
+		else if ((regbuf->flags & REGBUF_KEEP_DATA_AFTER_CP) != 0)
+		{
+			XLogRecPtr	page_lsn = PageGetLSN(regbuf->page);
+
+			needs_data = (page_lsn <= RedoRecPtr);
+			if (!needs_data)
+			{
+				/*
+				 * XLogInsertRecord() will detect if our view of the latest
+				 * checkpoint's RedoRecPtr is out of date.
+				 */
+				if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)
+					*fpw_lsn = page_lsn;
+			}
+		}
 		else
 			needs_data = !needs_backup;
 
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index df24089ea4..22b388cb33 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -37,6 +37,8 @@
 									 * will be skipped) */
 #define REGBUF_KEEP_DATA	0x10	/* include data even if a full-page image
 									 * is taken */
+#define REGBUF_KEEP_DATA_AFTER_CP 0x20 /* include data on the first
+										* modification after a checkpoint */
 
 /* prototypes for public functions in xloginsert.c: */
 extern void XLogBeginInsert(void);
-- 
2.16.2.windows.1

0005-Add-prefetch-support-for-the-undo-log.patchapplication/octet-stream; name=0005-Add-prefetch-support-for-the-undo-log.patchDownload
From 8e9190898eb0cd12afea72f7ad89c192d5943e60 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/13] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 101 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 87 insertions(+), 35 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c742861dae..d8dc0cc547 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 2b1d60680e..5abf42efae 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -520,14 +520,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -550,42 +593,32 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
+							  BlockNumber blockNum, char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 04d4514e35..3be0f5e922 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -119,7 +119,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1936c5db6f..2fff6734fc 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -937,6 +937,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index a04190aa92..5c0ed58591 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -165,6 +165,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BlockNumber blockNum, ReadBufferMode mode,
-- 
2.16.2.windows.1

0006-Defect-and-enhancement-in-multi-log-support.patchapplication/octet-stream; name=0006-Defect-and-enhancement-in-multi-log-support.patchDownload
From 126e9cc8da96ffa1a0c9842961db78b96d920917 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Fri, 7 Jun 2019 15:03:37 +0530
Subject: [PATCH 06/13] Defect and enhancement in multi-log support

---
 src/backend/access/undo/undolog.c | 18 +++++++++---------
 src/include/access/undolog.h      | 20 ++++++++++----------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index f2e0272ab1..b5502f2417 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -940,11 +940,11 @@ UndoLogAllocateInRecovery(UndoLogAllocContext *context,
 			context->recovery_logno = slot->logno;
 
 			/* Read log switch information from meta and reset it. */
-			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
-			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+			*prevlog_xact_start = slot->meta.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.prevlog_last_urp;
 
-			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
-			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+			slot->meta.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.prevlog_last_urp = InvalidUndoRecPtr;
 
 			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
 		}
@@ -1284,8 +1284,8 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 	Assert(AmAttachedToUndoLogSlot(slot));
 
 	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
-	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
-	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	slot->meta.prevlog_xact_start = prevlog_xact_start;
+	slot->meta.prevlog_last_urp = prevlog_last_urp;
 	LWLockRelease(&slot->mutex);
 
 	/* Wal log the log switch. */
@@ -1293,7 +1293,7 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 		xl_undolog_switch xlrec;
 
 		xlrec.logno = logno;
-		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_xact_start = prevlog_xact_start;
 		xlrec.prevlog_last_urp = prevlog_xact_start;
 
 		XLogBeginInsert();
@@ -2571,8 +2571,8 @@ undolog_xlog_switch(XLogReaderState *record)
 	 * Restore the log switch information in the MyUndoLogState this will be
 	 * reset by following UndoLogAllocateDuringRecovery.
 	 */
-	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
-	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+	slot->meta.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.prevlog_last_urp = xlrec->prevlog_last_urp;
 }
 
 void
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index c4a6b29ef0..1c79c1ff32 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -211,16 +211,6 @@ typedef struct UndoLogUnloggedMetaData
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
-
-	/*
-	 * Below two variable are used during recovery when transaction's undo
-	 * records are split across undo logs.  Replay of switch will restore
-	 * these two undo record pointers which will be reset on next allocation
-	 * during recovery. */
-	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
-									 * in the previous log. */
-	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
-									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -241,6 +231,16 @@ typedef struct UndoLogMetaData
 	UndoLogStatus status;
 	UndoLogOffset end;				/* one past end of highest segment */
 	UndoLogOffset discard;			/* oldest data needed (tail) */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogMetaData;
 
 /*
-- 
2.16.2.windows.1

0007-Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=0007-Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From b4c7d6041f2d7f46814e8bdd3d37e22d5e9c3c05 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 07/13] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro, Vignesh C and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoaccess.c         | 1754 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         | 1051 +++++++++++++++
 src/backend/storage/page/bufpage.c           |   28 +
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  121 ++
 src/include/access/undolog.h                 |   10 +-
 src/include/access/undorecord.h              |  279 ++++
 src/include/storage/bufpage.h                |   40 +
 10 files changed, 3311 insertions(+), 4 deletions(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100755 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c6963cf..049a416f07 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoaccess.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000000..41e2c0ec22
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 0000000000..a8cf46964e
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1754 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the next log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the first and last undo
+ * record of the transaction in the previous log.  The last undo record
+ * location is used find the previous undo record pointer during rollback.
+ * The first undo record location is used to find the previous transaction
+ * header which is required to update the undo apply progress.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoaccess.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/*
+ * Information for compression of the undo records on a page so that we can
+ * avoid duplicating same values across multiple undo records on a page.
+ *
+ * The cid/xid/reloid/rmid information will be added in the undo record header
+ * in the following cases:
+ * a) The first undo record of the transaction.
+ * b) First undo record of the page.
+ * c) All subsequent record for the transaction which is not the first
+ *	  transaction on the page.
+ * Except above cases,  If the rmid/reloid/xid/cid is same in the subsequent
+ * records this information will not be stored in the record, these information
+ * will be retrieved from the first undo record of that page.
+ * If any of the member rmid/reloid/xid/cid has changed, the changed information
+ * will be stored in the undo record and the remaining information will be
+ * retrieved from the first complete undo record of the page
+ */
+UndoCompressionInfo undo_compression_info[UndoLogCategories];
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+											UndoRecPtr urp, RelFileNode rnode,
+											UndoLogCategory category,
+											Buffer *prevbuf);
+static int	UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+									   UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+									  int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+										UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
+							  RelFileNode rnode, BlockNumber blk,
+							  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+								   UndoLogCategory category);
+static bool UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+							  UnpackedUndoRecord *urec, UndoRecPtr urp,
+							  Buffer buffer);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+/*
+ * Compute the size of the partial record on the undo page.
+ *
+ * Compute the complete record size by uur_info and variable field length
+ * stored in the page header and then subtract the offset of the record so that
+ * we can get the exact size of partial record in this page.
+ */
+static inline Size
+UndoPagePartialRecSize(UndoPageHeader phdr)
+{
+	Size		size = UndoRecordHeaderSize(phdr->uur_info);
+
+	/*
+	 * Add length of the variable part and undo length. Now, we know the
+	 * complete length of the undo record.
+	 */
+	size += phdr->tuple_len + phdr->payload_len + sizeof(uint16);
+
+	/*
+	 * Subtract the size which is stored in the previous page to get the
+	 * partial record size stored in this page.
+	 */
+	size -= phdr->record_offset;
+
+	return size;
+}
+
+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress
+ *
+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */
+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	XactUndoRecordInfo *xact_info;
+
+	xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Is there some byte of the urec_next on the current block, if not then
+	 * start from the next block.
+	 */
+	if (remaining_bytes <= offset)
+	{
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+		starting_byte += (offset - remaining_bytes);
+	}
+	else
+		starting_byte += offset;
+
+	/*
+	 * Set the offset in the first block where we need to start writing,
+	 * during the prepare phase so that during update phase we need not to
+	 * compute it again.
+	 */
+	xact_info->offset = starting_byte;
+
+	/* Loop until we have fetched all the buffers in which we need to write. */
+	while (size > 0)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	xact_info->next = InvalidUndoRecPtr;
+	xact_info->progress = 0;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	return (context->nxact_urec_info - 1);
+}
+
+/*
+ * Prepare to update the transaction's next undo pointer.
+ *
+ * Lock necessary buffer for updating the next of the transaction header.  This
+ * function is called for
+ * a. Updating the current transaction's start undo record pointer in previous
+ * transaction's start header.
+ * b. For multi-log transaction update the start undo record pointer of the
+ * current log in the same transaction's start undo record pointer in the
+ * previous log.
+ *
+ * xact_urp - undo record pointer to be updated
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr xact_urp)
+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	slot = UndoLogGetSlot(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoRecPtrIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+		offsetof(UndoRecordTransaction, urec_next);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+
+	/*
+	 * Set the next pointer in xact_urec_info, this will be overwritten in
+	 * actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].next = urecptr;
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&slot->discard_update_lock);
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			i = 0;
+	int			write_bytes;
+	int			write_offset;
+	char	   *sourceptr;
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/* Whether to update the next or undo apply progress. */
+	if (UndoRecPtrIsValid(xact_info->next))
+	{
+		sourceptr = (char *) &xact_info->next;
+		write_bytes = sizeof(xact_info->next);
+	}
+	else
+	{
+		sourceptr = (char *) &xact_info->progress;
+		write_bytes = sizeof(xact_info->progress);
+	}
+
+	/* Where to start writing in the current block. */
+	write_offset = xact_info->offset;
+
+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */
+	while (write_bytes > 0)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+		int			can_write;
+		char	   *writeptr;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/* How may bytes can be written in the current page. */
+		can_write = Min((BLCKSZ - write_offset), write_bytes);
+
+		/*
+		 * If buffer is valid then write it otherwise just skip writing it but
+		 * compute the variable for writing into the next block.
+		 */
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+
+			/* Compute the write pointer. */
+			writeptr = (char *) page + write_offset;
+
+			/* Copy the bytes we can write. */
+			memcpy(writeptr, sourceptr, can_write);
+			MarkBufferDirty(buffer);
+		}
+
+		/* Update bookkeeping information. */
+		write_bytes -= can_write;
+		sourceptr += can_write;
+		write_offset = UndoLogBlockHeaderSize;
+		i++;
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoLogCategory category = context->alloc_context.category;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoLogCategory(category));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * Exclude the common info in undo record flag and also set the compression
+ * info in the context.
+ *
+ * This function will check what information we need to include in the current
+ * undo record based on the undo compression information.  And, it will also
+ * update the compression info if we are writing the first undo record on the
+ * page.
+ */
+static bool
+UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+				  UnpackedUndoRecord *urec, UndoRecPtr urp,
+				  Buffer buffer)
+{
+	bool		record_updated = false;
+	bool		first_complete_undo = false;
+	UndoRecPtr	lasturp = compressioninfo->last_urecptr;
+
+	/*
+	 * If we have valid compression info and the for the same transaction and
+	 * the current undo record is on the same block as the last undo record
+	 * then exclude the common information which are same as first complete
+	 * record on the page.
+	 */
+	if (compressioninfo->valid &&
+		FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+		UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))
+	{
+		urec->uur_info &= ~UREC_INFO_XID;
+
+		/* Don't include rmid if it's same. */
+		if (urec->uur_rmid == compressioninfo->rmid)
+			urec->uur_info &= ~UREC_INFO_RMID;
+
+		/* Don't include reloid if it's same. */
+		if (urec->uur_reloid == compressioninfo->reloid)
+			urec->uur_info &= ~UREC_INFO_RELOID;
+
+		/* Don't include cid if it's same. */
+		if (urec->uur_cid == compressioninfo->cid)
+			urec->uur_info &= ~UREC_INFO_CID;
+
+		record_updated = true;
+	}
+
+	/*
+	 * If the undo record is starting just after the undo page header then
+	 * this is the first complete undo on the page.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) == SizeOfUndoPageHeaderData)
+		first_complete_undo = true;
+	else if (UndoRecPtrIsValid(lasturp))
+	{
+		/*
+		 * If we already have the valid last undo record pointer which
+		 * inserted undo in this log then we can identify whether this is the
+		 * first undo of the page by checking the block number of the previous
+		 * record and the current record.
+		 */
+		if (UndoRecPtrGetBlockNum(urp) != UndoRecPtrGetBlockNum(lasturp))
+			first_complete_undo = true;
+	}
+	else
+	{
+		Page		page = BufferGetPage(buffer);
+		uint16		offset;
+		UndoPageHeader phdr = (UndoPageHeader) page;
+
+		/*
+		 * We need to compute the offset of the first complete record of the
+		 * page and if this undo record starting from that page then this is
+		 * the first complete record on the page.
+		 */
+		offset = SizeOfUndoPageHeaderData + UndoPagePartialRecSize(phdr);
+		if (UndoRecPtrGetPageOffset(urp) == offset)
+			first_complete_undo = true;
+	}
+
+	/*
+	 * If we are writing first undo record for the page the we can set the
+	 * compression so that subsequent records from the same transaction can
+	 * avoid including common information in the undo records.
+	 */
+	if (first_complete_undo)
+	{
+		/* Set this information. */
+		compressioninfo->rmid = urec->uur_rmid;
+		compressioninfo->reloid = urec->uur_reloid;
+		compressioninfo->fxid = urec->uur_fxid;
+		compressioninfo->cid = urec->uur_cid;
+
+		/* Set that we have valid compression info. */
+		compressioninfo->valid = true;
+	}
+
+	return record_updated;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoLogCategory category,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, category, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/*
+	 * Copy the compression global compression info to our context before
+	 * starting prepare because this value might get updated multiple time in
+	 * case of multi-prepare but the global value should be updated only after
+	 * we have successfully inserted the undo record.
+	 */
+	memcpy(&context->undo_compression_info[category],
+		   &undo_compression_info[category], sizeof(UndoCompressionInfo));
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  Oid dbid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId fxid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	bool		logswitched = false;
+	bool		resize = false;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+	UndoCompressionInfo *compression_info =
+	&context->undo_compression_info[context->alloc_context.category];
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	/* Extract the full transaction id from the input undo record. */
+	fxid = urec->uur_fxid;
+	Assert(FullTransactionIdIsValid(fxid));
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  Similarly log switch
+	 * will only be detected after allocation so include the log switch header
+	 * and common information because in case of log switch we need to include
+	 * log switch header and also we need to include common header.  After
+	 * allocation We'll only advance by as many bytes as we turn out to need.
+	 */
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	urec->uur_info |= UREC_INFO_PAGE_COMMON;
+
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.  We don't expect to see temporary or
+		 * unlogged undo data here.
+		 */
+		Assert(context->alloc_context.category != UNDO_TEMP &&
+			   context->alloc_context.category != UNDO_UNLOGGED);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(fxid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.category);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	/*
+	 * If undo log is switched then set the logswitch flag and also reset the
+	 * compression info because we can use same compression info for the new
+	 * undo log.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		logswitched = true;
+		compression_info->valid = false;
+		compression_info->last_urecptr = InvalidUndoRecPtr;
+	}
+
+	/*
+	 * If we need a transaction header then allocate memory for it and
+	 * initialize it.
+	 */
+	if (need_xact_header)
+	{
+		urec->uur_txn = palloc(SizeOfUndoRecordTransaction);
+		urec->uur_txn->urec_dbid = dbid;
+		urec->uur_txn->urec_progress = InvalidBlockNumber;
+		urec->uur_txn->urec_next = InvalidUndoRecPtr;
+	}
+	else
+	{
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		resize = true;
+		urec->uur_txn = NULL;
+	}
+
+	/*
+	 * If undo log got switched then initialize the log switch header
+	 * otherwise reset it in uur_info and recalculate the size.
+	 */
+	if (logswitched)
+	{
+		urec->uur_logswitch = palloc(SizeOfUndoRecordLogSwitch);
+		urec->uur_logswitch->urec_prevurp = prevlogurp;
+		urec->uur_logswitch->urec_prevlogstart = prevlog_xact_start;
+	}
+	else
+	{
+		/* We don't need a log transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_LOGSWITCH;
+		resize = true;
+		urec->uur_logswitch = NULL;
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareUpdateNext(context, urecptr, last_xact_start);
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * Set/overwrite compression info if required and also exclude the common
+	 * fields from the undo record if possible.
+	 */
+	if (UndoSetCommonInfo(compression_info, urec, urecptr,
+						  context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf))
+		resize = true;
+
+	if (resize)
+		size = UndoRecordExpectedSize(urec);
+
+	/*
+	 * If the transaction's undo records are split across the undo logs.  So
+	 * we need to  update our own transaction header in the previous log.
+	 */
+	if (logswitched)
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareUpdateNext(context, urecptr, prevlog_xact_start);
+	}
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	/* Set the current undo pointer in the compression info. */
+	compression_info->last_urecptr = urecptr;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+
+	/*
+	 * We have successfully inserted prepared undo records so overwrite the
+	 * global compression.
+	 */
+	memcpy(&undo_compression_info[context->alloc_context.category],
+		   &context->undo_compression_info[context->alloc_context.category],
+		   sizeof(UndoCompressionInfo));
+
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/*
+	 * Release memory for the transaction header and log switch header if we
+	 * have allocated it in the prepare time.
+	 */
+	for (i = 0; i < context->nprepared_undo; i++)
+	{
+		if (context->prepared_undo[i].urec->uur_txn)
+			pfree(context->prepared_undo[i].urec->uur_txn);
+		if (context->prepared_undo[i].urec->uur_logswitch)
+			pfree(context->prepared_undo[i].urec->uur_logswitch);
+	}
+
+	/* Free memory allocated for the prepare undo and prepared buffers. */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Helper function for UndoGetOneRecord
+ *
+ * If any of  rmid/reloid/xid/cid is not available in the undo record, then
+ * it will get the information from the first complete undo record in the
+ * page.
+ */
+static void
+GetCommonUndoRecInfo(UndoPackContext *ucontext, UndoRecPtr urp,
+					 RelFileNode rnode, UndoLogCategory category, Buffer buffer)
+{
+	/*
+	 * If any of the common header field is not available in the current undo
+	 * record then we must read it from the first complete record of the page.
+	 */
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_PAGE_COMMON) !=
+		UREC_INFO_PAGE_COMMON)
+	{
+		UnpackedUndoRecord first_uur = {0};
+		Page		page = BufferGetPage(buffer);
+		UndoPageHeader undo_phdr = (UndoPageHeader) page;
+		UndoRecPtr	first_urp = InvalidUndoRecPtr;
+		Size		partial_rec_size = SizeOfUndoPageHeaderData;
+		BlockNumber blkno = UndoRecPtrGetBlockNum(urp);
+
+		/*
+		 * If there is a partial record in the page then compute the size of
+		 * it so that we can compute the undo record pointer of the first
+		 * complete undo record of the page.
+		 */
+		if (undo_phdr->record_offset != 0)
+			partial_rec_size += UndoPagePartialRecSize(undo_phdr);
+
+		/*
+		 * Compute the undo record pointer of the first complete record of the
+		 * page.
+		 */
+		first_urp = MakeUndoRecPtr(rnode.relNode,
+								   UndoRecPageOffsetGetRecPtr(partial_rec_size, blkno));
+
+		/* Fetch the first undo record of the page. */
+		UndoGetOneRecord(&first_uur, first_urp, rnode, category, &buffer);
+
+		/*
+		 * Get all missing common header information from the first undo
+		 * records.
+		 */
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) == 0)
+			ucontext->urec_rmid = first_uur.uur_rmid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) == 0)
+			ucontext->urec_reloid = first_uur.uur_reloid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) == 0)
+			ucontext->urec_fxid = first_uur.uur_fxid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) == 0)
+			ucontext->urec_cid = first_uur.uur_cid;
+
+		ucontext->urec_hd.urec_info |= UREC_INFO_PAGE_COMMON;
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoLogCategory category, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoLogCategory(category));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+		}
+
+		/* Acquire shared lock on the buffer before reading undo from it. */
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Get any of the missing fields from the first record of the
+			 * page.
+			 */
+			GetCommonUndoRecInfo(&ucontext, urp, rnode, category, *curbuf);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	/* Unlock the buffer but keep the pin. */
+	LockBuffer(*curbuf, BUFFER_LOCK_UNLOCK);
+
+	return urec;
+}
+
+/*
+ * BeginUndoFetch to fetch undo record.
+ */
+void
+BeginUndoFetch(UndoRecordFetchContext *context)
+{
+	context->buffer = InvalidBuffer;
+	context->urp = InvalidUndoRecPtr;
+}
+
+/*
+ * Fetch the undo record for given undo record pointer.
+ *
+ * This will internally allocate the memory for the unpacked undo record which
+ * intern will hold the pointers to the optional headers and the variable data.
+ * The undo record should be freed by the caller by calling ReleaseUndoRecord.
+ * This function will old the pin on the buffer where we read the previous undo
+ * record so that when this function is called repeatedly with the same context
+ * then it can be benefitted by avoid reading the buffer again if the current
+ * undo record is in the same buffer.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecordFetchContext *context, UndoRecPtr urp)
+{
+	RelFileNode rnode;
+	int			logno;
+	UndoLogSlot *slot;
+	UnpackedUndoRecord *uur = NULL;
+
+	logno = UndoRecPtrGetLogNo(urp);
+	slot = UndoLogGetSlot(logno, true);
+
+	/*
+	 * If slot is NULL that means undo log number is unknown.  Presumably it
+	 * has been entirely discarded.
+	 */
+	if (slot == NULL)
+		return NULL;
+
+	/*
+	 * Prevent UndoDiscardOneLog() from discarding data while we try to read
+	 * it.  Usually we would acquire log->mutex to read log->meta members, but
+	 * in this case we know that discard can't move without also holding
+	 * log->discard_lock.
+	 *
+	 * In Hot Standby mode log->oldest_data is never initialized because it's
+	 * get updated by undo discard worker whereas in HotStandby undo logs are
+	 * getting discarded using discard WAL.  So in HotStandby we can directly
+	 * check whether the undo record pointer is discarded or not.  But, we can
+	 * not do same for normal case because discard worker can concurrently
+	 * discard the undo logs.
+	 *
+	 * XXX We can avoid this check by always initializing log->oldest_data in
+	 * HotStandby mode as well whenever we apply discard WAL.  But, for doing
+	 * that we need to acquire discard lock just for setting this variable?
+	 */
+	if (InHotStandby)
+	{
+		if (UndoRecPtrIsDiscarded(urp))
+			return NULL;
+	}
+	else
+	{
+		LWLockAcquire(&slot->discard_lock, LW_SHARED);
+		if (slot->logno != logno || urp < slot->oldest_data)
+		{
+			/*
+			 * The slot has been recycled because the undo log was entirely
+			 * discarded, or the pointer is before the oldest data.
+			 */
+			LWLockRelease(&slot->discard_lock);
+			return NULL;
+		}
+	}
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory by calling UndoRecordRelease.
+	 */
+	uur = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/*
+	 * Before fetching the next record check whether we have a valid buffer in
+	 * the context.  If so and if we are reading the current record from the
+	 * same then pass that buffer to fetch the undo record, otherwise release
+	 * the buffer.
+	 */
+	if (BufferIsValid(context->buffer) &&
+		(UndoRecPtrGetLogNo(context->urp) != UndoRecPtrGetLogNo(urp) ||
+		 UndoRecPtrGetBlockNum(context->urp) != UndoRecPtrGetBlockNum(urp)))
+	{
+		ReleaseBuffer(context->buffer);
+		context->buffer = InvalidBuffer;
+	}
+
+	/* Fetch the current undo record. */
+	UndoGetOneRecord(uur, urp, rnode, slot->meta.category, &context->buffer);
+
+	/* Release the discard lock after fetching the record. */
+	if (!InHotStandby)
+		LWLockRelease(&slot->discard_lock);
+
+	context->urp = urp;
+
+	return uur;
+}
+
+/*
+ * Finish undo record fetch.
+ */
+void
+FinishUndoFetch(UndoRecordFetchContext *context)
+{
+	if (BufferIsValid(context->buffer))
+		ReleaseBuffer(context->buffer);
+}
+
+/*
+ * Release the memory of the undo record allocated by UndoFetchRecord and
+ * UndoBulkFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	/* Release the memory of payload data if we allocated it. */
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+
+	/* Release memory of tuple data if we allocated it. */
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Release memory of the transaction header if we allocated it. */
+	if (urec->uur_txn)
+		pfree(urec->uur_txn);
+
+	/* Release memory of the logswitch header if we allocated it. */
+	if (urec->uur_logswitch)
+		pfree(urec->uur_logswitch);
+
+	/* Release the memory of the undo record. */
+	pfree(urec);
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  UndoLogCategory category)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(rnode, MAIN_FORKNUM, startblock++,
+									  category == UNDO_TEMP);
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	FullTransactionId fxid = InvalidFullTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.  Also, if we are fetching undo records from more than one
+	 * log, we don't know the boundaries for prefetching.  Hence, we can't use
+	 * prefetching in this case.
+	 */
+	if (!one_page &&
+		(UndoRecPtrGetLogNo(*from_urecptr) == UndoRecPtrGetLogNo(to_urecptr)))
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogSlot *slot;
+		UndoLogCategory category;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			if (BufferIsValid(buffer))
+				ReleaseBuffer(buffer);
+			return NULL;
+		}
+		category = slot->meta.category;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				ReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, category);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoRecPtrIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&slot->discard_lock, LW_SHARED);
+				if (slot->logno != logno || urecptr < slot->oldest_data)
+				{
+					/*
+					 * The undo log slot has been recycled because it was
+					 * entirely discarded, or the data has been discarded
+					 * already.
+					 */
+					LWLockRelease(&slot->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			if (!InHotStandby)
+				LWLockRelease(&slot->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!FullTransactionIdIsValid(fxid))
+				fxid = uur->uur_fxid;
+			else if (!FullTransactionIdEquals(fxid, uur->uur_fxid))
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_prevundo;
+		else if (uur->uur_logswitch)
+			urecptr = uur->uur_logswitch->urec_prevurp;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, category);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		/*
+		 * Including current record, if we have crossed the memory limit or
+		 * undo log got switched then stop processing more records.  Remember
+		 * to set the from_urecptr so that on next call we can resume fetching
+		 * undo records where we left it.
+		 */
+		if (total_size >= undo_apply_size || uur->uur_logswitch)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		ReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoLogCategory category)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoLogCategory(category);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoLogCategory category)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, category);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100755
index 0000000000..a44bfbd374
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,1051 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/bufmask.h"
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+							char **writeptr, char *endptr,
+							int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+						  char **readptr, char *endptr,
+						  int *total_bytes_read, int *partial_read);
+
+ /*
+  * Compute the header size of the undo record.
+  */
+Size
+UndoRecordHeaderSize(uint16 uur_info)
+{
+	Size		size;
+
+	/* Add fixed header size. */
+	size = SizeOfUndoRecordHeader;
+
+	/* Add size of transaction header if it presets. */
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+
+	/* Add size of rmid if it presets. */
+	if ((uur_info & UREC_INFO_RMID) != 0)
+		size += sizeof(RmgrId);
+
+	/* Add size of reloid if it presets. */
+	if ((uur_info & UREC_INFO_RELOID) != 0)
+		size += sizeof(Oid);
+
+	/* Add size of fxid if it presets. */
+	if ((uur_info & UREC_INFO_XID) != 0)
+		size += sizeof(FullTransactionId);
+
+	/* Add size of cid if it presets. */
+	if ((uur_info & UREC_INFO_CID) != 0)
+		size += sizeof(CommandId);
+
+	/* Add size of forknum if it presets. */
+	if ((uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+
+	/* Add size of prevundo if it presets. */
+	if ((uur_info & UREC_INFO_PREVUNDO) != 0)
+		size += sizeof(UndoRecPtr);
+
+	/* Add size of the block header if it presets. */
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+
+	/* Add size of the log switch header if it presets. */
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+
+	/* Add size of the payload header if it presets. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	/* Add undo record length size. */
+	size += sizeof(uint16);
+
+	return size;
+}
+
+/*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint16		uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload *payload = (UndoRecordPayload *) (page_ptr + size);
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);
+
+	/* Copy rmid if present. */
+	if ((uur->uur_info & UREC_INFO_RMID) != 0)
+		ucontext->urec_rmid = uur->uur_rmid;
+
+	/* Copy reloid if present. */
+	if ((uur->uur_info & UREC_INFO_RELOID) != 0)
+		ucontext->urec_reloid = uur->uur_reloid;
+
+	/* Copy fxid if present. */
+	if ((uur->uur_info & UREC_INFO_XID) != 0)
+		ucontext->urec_fxid = uur->uur_fxid;
+
+	/* Copy cid if present. */
+	if ((uur->uur_info & UREC_INFO_CID) != 0)
+		ucontext->urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy prev undo record pointer if it is present. */
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		ucontext->urec_prevundo = uur->uur_prevundo;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		memcpy(&ucontext->urec_logswitch, uur->uur_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+	else
+	{
+		ucontext->urec_payload.urec_payload_len = 0;
+		ucontext->urec_payload.urec_tuple_len = 0;
+	}
+
+	/* Compute undo record expected size and store in the context. */
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_rmid), sizeof(RmgrId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_reloid), sizeof(Oid),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_fxid), sizeof(FullTransactionId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_cid), sizeof(CommandId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_prevundo,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RMID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(RmgrId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(RmgrId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RELOID) != 0)
+			{
+				if (bytes_to_skip < sizeof(Oid))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(Oid);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_XID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(TransactionId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(TransactionId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_CID) != 0)
+			{
+				if (bytes_to_skip < sizeof(CommandId))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(CommandId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_PREVUNDO) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordLogSwitch)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordLogSwitch;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RMID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_rmid,
+								   sizeof(RmgrId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RELOID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_reloid,
+								   sizeof(Oid),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+		case UNDO_PACK_STAGE_XID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fxid,
+								   sizeof(FullTransactionId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+		case UNDO_PACK_STAGE_CID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_cid,
+								   sizeof(CommandId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_prevundo,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_logswitch,
+								   SizeOfUndoRecordLogSwitch,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_txn = palloc(SizeOfUndoRecordTransaction);
+		memcpy(uur->uur_txn, &ucontext->urec_txn, SizeOfUndoRecordTransaction);
+	}
+
+	/*
+	 * Copy the common field.  All of these field must present in the final
+	 * unpacked undo record.
+	 */
+	Assert((uur->uur_info & UREC_INFO_PAGE_COMMON) == UREC_INFO_PAGE_COMMON);
+
+	uur->uur_rmid = ucontext->urec_rmid;
+	uur->uur_reloid = ucontext->urec_reloid;
+	uur->uur_fxid = ucontext->urec_fxid;
+	uur->uur_cid = ucontext->urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy previous undo record pointer if it is present. */
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		uur->uur_prevundo = ucontext->urec_prevundo;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		uur->uur_logswitch = palloc(SizeOfUndoRecordLogSwitch);
+		memcpy(uur->uur_logswitch, &ucontext->urec_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * fields are set.
+ *
+ * Other flags i.e UREC_INFO_TRANSACTION, UREC_INFO_PAGE_COMMON and,
+ * UREC_INFO_LOGSWITCH are directly set by the PrepareUndoInsert function.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	/*
+	 * If fork number is not the main fork then we need to store it in the
+	 * undo record so set the flag.
+	 */
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+
+	/* If prevundo is valid undo record pointer then set the flag. */
+	if (uur->uur_prevundo != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_PREVUNDO;
+
+	/* If the block number is valid then set the flag for the block header. */
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+
+	/*
+	 * Either of the payload or the tuple length is non-zero then we need the
+	 * payload header.
+	 */
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810e37..f5133c78e8 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,34 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint16 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..cc00509699 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 0000000000..f7cfa9f6be
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,121 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * Undo record element.  Used for storing the group of undo record in a array
+ * using UndoBulkFetchRecord.
+ */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element.  For stable qsort. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* Undo record pointer to be updated. */
+	uint32		offset;			/* offset in page where to start updating. */
+	UndoRecPtr	next;			/* first urp of the next transaction which is
+								 * be updated in transaction header */
+	BlockNumber progress;		/* undo apply action progress. */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	UndoCompressionInfo undo_compression_info[UndoLogCategories];	/* Compression info. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+/*
+ * Context for fetching the required undo record.
+ */
+typedef struct UndoRecordFetchContext
+{
+	Buffer		buffer;			/* Previous undo record pinned buffer. */
+	UndoRecPtr	urp;			/* Previous undo record pointer. */
+} UndoRecordFetchContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+								  UndoLogCategory category,
+								  int nprepared,
+								  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+									UnpackedUndoRecord *urec, Oid dbid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void BeginUndoFetch(UndoRecordFetchContext *context);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecordFetchContext *context,
+										   UndoRecPtr urp);
+extern void FinishUndoFetch(UndoRecordFetchContext *context);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+										UndoRecPtr to_urecptr,
+										int undo_apply_size, int *nrecords,
+										bool one_page);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+								   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+								 XLogRecPtr recptr);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+										UndoLogCategory category);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 1c79c1ff32..7ec4cb0ec1 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -139,7 +139,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
@@ -169,6 +169,10 @@ typedef int UndoLogNumber;
 #define UndoRecPtrGetPageOffset(urp)			\
 	(UndoRecPtrGetOffset(urp) % BLCKSZ)
 
+/* Compute the undo record pointer offset given the undo rec page offset and the block number. */
+#define UndoRecPageOffsetGetRecPtr(offset, blkno)             \
+	((blkno * BLCKSZ) + offset)
+
 /* Compare two undo checkpoint files to find the oldest file. */
 #define UndoCheckPointFilenamePrecedes(file1, file2)	\
 	(strcmp(file1, file2) < 0)
@@ -207,7 +211,7 @@ typedef int UndoLogNumber;
  */
 typedef struct UndoLogUnloggedMetaData
 {
-	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset insert;		/* next insertion point (head) */
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
@@ -352,7 +356,7 @@ extern PGDLLIMPORT undologtable_hash *undologtable_cache;
 static pg_attribute_always_inline UndoLogTableEntry *
 UndoLogGetTableEntry(UndoLogNumber logno)
 {
-	UndoLogTableEntry  *entry;
+	UndoLogTableEntry *entry;
 
 	/* Fast path. */
 	entry = undologtable_lookup(undologtable_cache, logno);
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000000..065326705d
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,279 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "access/transam.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+/*
+ * The below common information will be stored in the first undo record of the page.
+ * Every subsequent undo record will not store this information, if required this information
+ * will be retrieved from the first undo record of the page.
+ */
+typedef struct UndoCompressionInfo
+{
+	bool		valid;			/* Undo compression info is valid ? */
+	UndoRecPtr	last_urecptr;	/* last undo rec */
+	FullTransactionId fxid;		/* transaction id */
+	RmgrId		rmid;			/* rmgr ID */
+	Oid			reloid;			/* relation OID */
+	CommandId	cid;			/* command id */
+} UndoCompressionInfo;
+
+/*
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ * If UREC_INFO_RMID is set, rmgr id follows.
+ * if UREC_INFO_RELOID	is set, relation oid follows.
+ * If UREC_INFO_XID	is set, full transaction id follows.
+ * If UREC_INFO_CID	is set, command id follows.
+ * If UREC_INFO_FORK is set, fork number follows.
+ * If UREC_INFO_PREVUNDO is set, previous undo record pointer follows.
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ * If UREC_INFO_LOGSWITCH is set, an UndoRecordLogSwitch structure follows.
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordTransaction appears first.
+ */
+#define UREC_INFO_TRANSACTION				0x001
+#define UREC_INFO_RMID						0x002
+#define UREC_INFO_RELOID					0x004
+#define UREC_INFO_XID						0x008
+#define UREC_INFO_CID						0x010
+#define UREC_INFO_FORK						0x020
+#define UREC_INFO_PREVUNDO					0x040
+#define UREC_INFO_BLOCK						0x080
+#define UREC_INFO_LOGSWITCH					0x100
+#define UREC_INFO_PAYLOAD					0x200
+
+#define UREC_INFO_PAGE_COMMON  (UREC_INFO_RMID | UREC_INFO_RELOID | UREC_INFO_XID | UREC_INFO_CID)
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	uint8		urec_type;		/* record type code */
+	uint16		urec_info;		/* flag bits */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_info) + sizeof(uint16))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * Undo block number where we need to start reading the undo for applying
+	 * the undo action.   InvalidBlockNumber means undo applying hasn't
+	 * started for the transaction and MaxBlockNumber mean undo completely
+	 * applied. And, any other block number means we have applied partial undo
+	 * so next we can start from this block.
+	 */
+	BlockNumber urec_progress;
+	Oid			urec_dbid;		/* database id */
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information of the transaction's undo in the previous log.  If a transaction
+ * is split across the undo logs then this header will be included in the first
+ * undo record of the transaction in next log.
+ */
+typedef struct UndoRecordLogSwitch
+{
+	UndoRecPtr	urec_prevurp;	/* Transaction's last undo record pointer in
+								 * the previous undo log. */
+	UndoRecPtr	urec_prevlogstart;	/* Transaction's first undo record pointer
+									 * in previous undo log. */
+} UndoRecordLogSwitch;
+
+#define SizeOfUndoRecordLogSwitch \
+	(offsetof(UndoRecordLogSwitch, urec_prevlogstart) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_RMID,		/* The next thing to be processed is the rmid
+								 * if present */
+
+	UNDO_PACK_STAGE_RELOID,		/* The next thing to be processed is the
+								 * reloid if present */
+
+	UNDO_PACK_STAGE_XID,		/* The next thing to be processed is the xid
+								 * if present */
+
+	UNDO_PACK_STAGE_CID,		/* The next thing to be processed is the cid
+								 * if present */
+
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_PREVUNDO,	/* The next thing to be processed is the prev
+								 * undo info. */
+
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_LOGSWITCH,	/* The next thing to be processed is the log
+								 * switch details. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+
+	RmgrId		urec_rmid;		/* rmgrid */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	FullTransactionId urec_fxid;	/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecPtr	urec_prevundo;	/* Block prev */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecordLogSwitch urec_logswitch; /* Log switch header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint16		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_prevundo;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	FullTransactionId uur_fxid; /* transaction id */
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+
+	/*
+	 * Below header will be internally set by the undo layer.  Above this all
+	 * information should be set by the caller.
+	 */
+	UndoRecordTransaction *uur_txn; /* Transaction header, included in the
+									 * first record of the transaction in a
+									 * undo log. */
+	UndoRecordLogSwitch *uur_logswitch; /* Log switch header, included in the
+										 * first record of the transaction
+										 * only after undo log is switched
+										 * during a transaction. */
+} UnpackedUndoRecord;
+
+extern Size UndoRecordHeaderSize(uint16 uur_info);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+							UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+								  int bytes_to_skip);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+							 UnpackedUndoRecord *uur);
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+
+#endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 34b68ad0e0..93d108c207 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -215,6 +215,43 @@ typedef PageHeaderData *PageHeader;
  */
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
+/*
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ *
+ * FIXME : for undo page do we need to keep all the information which is
+ * required for the PageHeaderData e.g. pd_lower, pd_upper, pd_special?
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+
+	/*
+	 * Below fields required for computing the offset of the first complete
+	 * record on a undo page, which will be used for the undo record compression
+	 * and undo page consistency checking.
+	 */
+	uint16		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
 /*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
@@ -419,6 +456,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint16 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
-- 
2.16.2.windows.1

0008-undo-page-consistency-checker.patchapplication/octet-stream; name=0008-undo-page-consistency-checker.patchDownload
From 78472d023fa481441dacabc46d689d9dd5c21a00 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 4 Jul 2019 11:52:55 +0530
Subject: [PATCH 08/13] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar with help from Amit Khandekar and Rafia Sabih
---
 src/backend/access/undo/undorecord.c | 110 +++++++++++++++++++++++++++++++++++
 src/include/access/undorecord.h      |   1 +
 2 files changed, 111 insertions(+)

diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index a44bfbd374..5699153acc 100755
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -1049,3 +1049,113 @@ UndoRecordSetInfo(UnpackedUndoRecord *uur)
 	if (uur->uur_payload.len || uur->uur_tuple.len)
 		uur->uur_info |= UREC_INFO_PAYLOAD;
 }
+
+/*
+ * Get the offset of cid information in undo record.
+ */
+static Size
+get_undo_rec_cid_offset(uint16 urec_info)
+{
+	Size		offset_size = SizeOfUndoRecordHeader;
+
+	if ((urec_info & UREC_INFO_TRANSACTION) != 0)
+		offset_size += SizeOfUndoRecordTransaction;
+
+	if ((urec_info & UREC_INFO_RMID) != 0)
+		offset_size += sizeof(RmgrId);
+
+	if ((urec_info & UREC_INFO_RELOID) != 0)
+		offset_size += sizeof(Oid);
+
+	if ((urec_info & UREC_INFO_XID) != 0)
+		offset_size += sizeof(FullTransactionId);
+
+	return offset_size;
+}
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset;
+	UndoPageHeader phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page
+	 * has a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size		partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+			phdr->tuple_len + phdr->payload_len -
+			phdr->record_offset;
+		if ((phdr->uur_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(phdr->uur_info);
+
+			/*
+			 * We just want to mask the cid in the undo record header.  So
+			 * only if the partial record in the current page include the undo
+			 * record header then we need to mask the cid bytes in this page.
+			 * Otherwise, directly jump to the next record.
+			 */
+			if (phdr->record_offset < (cid_offset + sizeof(CommandId)))
+			{
+				char	   *cid_data;
+				Size		mask_size;
+
+				mask_size = Min(cid_offset - phdr->record_offset,
+								sizeof(CommandId));
+
+				cid_data = next_record + cid_offset - phdr->record_offset;
+				memset(&cid_data, MASK_MARKER, mask_size);
+			}
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/* If this undo record has cid present, then mask it */
+		if ((header->urec_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(header->urec_info);
+
+			/*
+			 * If this is not complete record then check whether cid is on
+			 * this page or not.  If not then we are done with this page.
+			 */
+			if ((next_record + cid_offset + sizeof(CommandId)) > page_end)
+			{
+				int			mask_size = page_end - next_record - cid_offset;
+
+				if (mask_size > 0)
+					memset(next_record + cid_offset, MASK_MARKER, mask_size);
+				break;
+			}
+			else
+			{
+				/* Mask cid */
+				memset(next_record + cid_offset, MASK_MARKER, sizeof(CommandId));
+			}
+		}
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 065326705d..6a2c5cc059 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -276,4 +276,5 @@ extern void FinishUnpackUndo(UndoPackContext *ucontext,
 							 UnpackedUndoRecord *uur);
 extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
 
+extern void mask_undo_page(char *pagedata);
 #endif							/* UNDORECORD_H */
-- 
2.16.2.windows.1

0008-undo-page-consistency-checker.patchapplication/octet-stream; name=0008-undo-page-consistency-checker.patchDownload
From 78472d023fa481441dacabc46d689d9dd5c21a00 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 4 Jul 2019 11:52:55 +0530
Subject: [PATCH 08/13] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar with help from Amit Khandekar and Rafia Sabih
---
 src/backend/access/undo/undorecord.c | 110 +++++++++++++++++++++++++++++++++++
 src/include/access/undorecord.h      |   1 +
 2 files changed, 111 insertions(+)

diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index a44bfbd374..5699153acc 100755
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -1049,3 +1049,113 @@ UndoRecordSetInfo(UnpackedUndoRecord *uur)
 	if (uur->uur_payload.len || uur->uur_tuple.len)
 		uur->uur_info |= UREC_INFO_PAYLOAD;
 }
+
+/*
+ * Get the offset of cid information in undo record.
+ */
+static Size
+get_undo_rec_cid_offset(uint16 urec_info)
+{
+	Size		offset_size = SizeOfUndoRecordHeader;
+
+	if ((urec_info & UREC_INFO_TRANSACTION) != 0)
+		offset_size += SizeOfUndoRecordTransaction;
+
+	if ((urec_info & UREC_INFO_RMID) != 0)
+		offset_size += sizeof(RmgrId);
+
+	if ((urec_info & UREC_INFO_RELOID) != 0)
+		offset_size += sizeof(Oid);
+
+	if ((urec_info & UREC_INFO_XID) != 0)
+		offset_size += sizeof(FullTransactionId);
+
+	return offset_size;
+}
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset;
+	UndoPageHeader phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page
+	 * has a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size		partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+			phdr->tuple_len + phdr->payload_len -
+			phdr->record_offset;
+		if ((phdr->uur_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(phdr->uur_info);
+
+			/*
+			 * We just want to mask the cid in the undo record header.  So
+			 * only if the partial record in the current page include the undo
+			 * record header then we need to mask the cid bytes in this page.
+			 * Otherwise, directly jump to the next record.
+			 */
+			if (phdr->record_offset < (cid_offset + sizeof(CommandId)))
+			{
+				char	   *cid_data;
+				Size		mask_size;
+
+				mask_size = Min(cid_offset - phdr->record_offset,
+								sizeof(CommandId));
+
+				cid_data = next_record + cid_offset - phdr->record_offset;
+				memset(&cid_data, MASK_MARKER, mask_size);
+			}
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/* If this undo record has cid present, then mask it */
+		if ((header->urec_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(header->urec_info);
+
+			/*
+			 * If this is not complete record then check whether cid is on
+			 * this page or not.  If not then we are done with this page.
+			 */
+			if ((next_record + cid_offset + sizeof(CommandId)) > page_end)
+			{
+				int			mask_size = page_end - next_record - cid_offset;
+
+				if (mask_size > 0)
+					memset(next_record + cid_offset, MASK_MARKER, mask_size);
+				break;
+			}
+			else
+			{
+				/* Mask cid */
+				memset(next_record + cid_offset, MASK_MARKER, sizeof(CommandId));
+			}
+		}
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 065326705d..6a2c5cc059 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -276,4 +276,5 @@ extern void FinishUnpackUndo(UndoPackContext *ucontext,
 							 UnpackedUndoRecord *uur);
 extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
 
+extern void mask_undo_page(char *pagedata);
 #endif							/* UNDORECORD_H */
-- 
2.16.2.windows.1

0009-Extend-binary-heap-functionality.patchapplication/octet-stream; name=0009-Extend-binary-heap-functionality.patchDownload
From 85a0f0fe2d10dc524d0d52e53969ab02c0694042 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:02:23 +0530
Subject: [PATCH 09/13] Extend binary heap functionality.

Add the routines to allocate binary heap in shared memory and to remove
nth element from binray heap.  This routines will be used by latter commit
to add support for undo workers.

Author: Kuntal Ghosh and Amit Kapila
---
 src/backend/lib/binaryheap.c | 118 +++++++++++++++++++++++++++++++++++++++++++
 src/include/lib/binaryheap.h |  12 ++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967813..5f7454d05f 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -47,6 +48,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 	return heap;
 }
 
+/*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
 /*
  * binaryheap_reset
  *
@@ -211,6 +242,79 @@ binaryheap_replace_first(binaryheap *heap, Datum d)
 		sift_down(heap, 0);
 }
 
+/*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap in O(1).  The caller must ensure that this routine is not used on
+ * an empty heap and is not called with n greater than or equal to the heap
+ * size.
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer to it in
+ * O(1) without preserving the heap property.  This is a convenience routine
+ * to remove elements quickly.  To obtain a valid heap, one must call
+ * binaryheap_build() afterwards.  The caller must ensure that this routine is
+ * not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
 /*
  * Swap the contents of two nodes.
  */
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 21f96b9ca6..ed9e8e8661 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -38,8 +38,11 @@ typedef struct binaryheap
 } binaryheap;
 
 extern binaryheap *binaryheap_allocate(int capacity,
-									   binaryheap_comparator compare,
-									   void *arg);
+					binaryheap_comparator compare,
+					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
+extern Size binaryheap_shmem_size(int capacity);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
-- 
2.16.2.windows.1

0010-Infrastructure-to-register-and-fetch-undo-action-req.patchapplication/octet-stream; name=0010-Infrastructure-to-register-and-fetch-undo-action-req.patchDownload
From 95d10fb308e3ec6ac8a7b4b5e7af78f6825f4dc8 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:10:06 +0530
Subject: [PATCH 10/13] Infrastructure to register and fetch undo action
 requests.

This infrasture provides a way to allow execution of undo actions.  One
might think that we can always execute undo actions on error or explicit
rollabck by user, however there are cases when that is not posssible.
For example, (a) if the system crash while doing operation, then after
startup, we need a way to perform undo actions; (b) If we get error while
performing undo actions.

Apart from this, when there are large rollback requests, then it is quite
inefficient to perform all the undo actions and then return control to
user.

To allow efficient execution of the undo actions, we create three queues
and a hash table for the rollback requests.  A Xid based priority queue
which will allow us to process the requests of older transactions and help
us to move oldesdXidHavingUnappliedUndo (this is a xid-horizon below which
all the transactions are visible) forward.  A size-based queue which will
help us to perform the rollbacks of larger aborts in a timely fashion so
that we don't get stuck while processing them during discard of the logs.
An error queue to hold the requests for transactions that failed to apply
its undo.  The rollback hash table is used to avoid duplicate undo requests
by backends and discard worker.

Amit Kapila and Kuntal Ghosh, design idea by Andres Freund.
---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undoaccess.c          |   50 +-
 src/backend/access/undo/undorequest.c         | 1647 +++++++++++++++++++++++++
 src/backend/storage/lmgr/lwlocknames.txt      |    1 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    8 +
 src/include/access/transam.h                  |    1 +
 src/include/access/undoaccess.h               |    3 +
 src/include/access/undorequest.h              |  231 ++++
 src/include/miscadmin.h                       |    1 +
 src/include/storage/proc.h                    |    2 +
 13 files changed, 1982 insertions(+), 2 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 049a416f07..73275028be 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o
+OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index a8cf46964e..4ffec58c5b 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -46,6 +46,7 @@
 #include "access/undorecord.h"
 #include "access/undoaccess.h"
 #include "access/undolog_xlog.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -754,7 +755,7 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	{
 		urec->uur_txn = palloc(SizeOfUndoRecordTransaction);
 		urec->uur_txn->urec_dbid = dbid;
-		urec->uur_txn->urec_progress = InvalidBlockNumber;
+		urec->uur_txn->urec_progress = XACT_APPLY_PROGRESS_NOT_STARTED;
 		urec->uur_txn->urec_next = InvalidUndoRecPtr;
 	}
 	else
@@ -1752,3 +1753,50 @@ UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 	/* calculate the previous undo record pointer */
 	return MakeUndoRecPtr(logno, offset - prevlen);
 }
+
+/*
+ * Returns the undo record pointer corresponding to first record in the given
+ * block.
+ */
+UndoRecPtr
+UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+							UndoLogCategory category)
+{
+	Buffer buffer;
+	Page page;
+	UndoPageHeader	phdr;
+	RelFileNode		rnode;
+	UndoLogOffset	log_cur_off;
+	Size			partial_rec_size;
+	int				offset_cur_page;
+
+	if (!BlockNumberIsValid(blkno))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid undo block number")));
+
+	UndoRecPtrAssignRelFileNode(rnode, urec_ptr);
+
+	buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, blkno,
+									   RBM_NORMAL, NULL,
+									   RelPersistenceForUndoLogCategory(category));
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader)page;
+
+	/* Calculate the size of the partial record. */
+	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						phdr->tuple_len + phdr->payload_len -
+						phdr->record_offset;
+
+	/* calculate the offset in current log. */
+	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
+	log_cur_off = (blkno * BLCKSZ) + offset_cur_page;
+
+	UnlockReleaseBuffer(buffer);
+
+	/* calculate the undo record pointer based on current offset in log. */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urec_ptr), log_cur_off);
+}
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000000..530887a592
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1647 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To ensure that backend and discard worker don't register the same request
+ * in the hash table, we always register the request with full_xid and the
+ * start pointer for the transaction in the hash table as key.  Backends
+ * always remember the value of start pointer, but discard worker doesn't know
+ * the actual start value in case transaction's undo spans across multiple
+ * logs.  The reason for the same is that discard worker might encounter the
+ * log which has overflowed undo records of the transaction first.  In such
+ * cases, we need to compute the actual start position.  The first record of a
+ * transaction in each undo log contains a reference to the first record of
+ * this transaction in the previous log.  By following the previous log chain
+ * of this transaction, we find the initial location which is used to register
+ * the request.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+#include "storage/proc.h"
+
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+#define UNDO_FAILURE_RETRY_DELAY_MS 10000
+
+int			rollback_overflow_size = 64;
+int			pending_undo_queue_size = 1024;
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr, e_retry_at, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).start_urec_ptr = e_start_urec_ptr, \
+	GetErrorQueueElem(elem).next_retry_at = e_retry_at, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdFollows(xidQueueElem1->full_xid,
+									  xidQueueElem2->full_xid))
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size < sizeQueueElem2->request_size)
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->next_retry_at < errQueueElem2->next_retry_at)
+		return 1;
+	else if (errQueueElem1->next_retry_at > errQueueElem2->next_retry_at)
+		return -1;
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at > errQueueElem2->err_occurred_at)
+		return -1;
+	return 0;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * pending_undo_queue_size) + pending_undo_queue_size +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo *urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+	TimestampTz next_retry;
+
+	/*
+	 * We want to retry this error request after some constant amount of time,
+	 * rather than retrying immediately, otherwise, in some cases (ex. when
+	 * all the pending requests are failed requests) worker will keep retrying
+	 * such errors constantly.
+	 *
+	 * In future, we might want some more sophisticated back-off algorithm
+	 * to delay the execution of such requests.
+	 */
+	next_retry = TimestampTzPlusMilliseconds(now, UNDO_FAILURE_RETRY_DELAY_MS);
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					  urinfo->start_urec_ptr, next_retry, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->next_retry_at = 0;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->next_retry_at = 0;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+			return false;
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+
+		/*
+		 * If it is too early to try the error request again, then check the
+		 * work in some other queue.
+		 */
+		if (GetCurrentTimestamp() < elem->next_retry_at)
+			return false;
+
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * last_log_start_urec_ptr_out - This is an OUT parameter.  If a transaction
+ * writes undo records in multiple undo logs, this is set to the start undo
+ * record pointer of this transaction in the last log.  If the transaction
+ * writes undo records only in single undo log, it is set to start_urec_ptr.
+ * This value is used to update the rollback progress of the transaction in
+ * the last log.  Once, we have start location in last log, the start location
+ * in all the previous logs can be computed.  See execute_undo_actions for
+ * more details.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   UndoRecPtr *last_log_start_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	UndoRecPtr	last_log_start_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoLogCategory category;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+		UndoLogOffset next_insert;
+		UndoRecordFetchContext	context;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				last_log_start_urecptr = urecptr;
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+		category = slot->meta.category;
+
+		next_insert = UndoLogGetNextInsertPtr(slot->logno);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		/* Fetch the undo record. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, urecptr);
+		FinishUndoFetch(&context);
+
+		/*
+		 * If the corresponding undo record got rolled back and discarded as
+		 * well, we return from here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * Case 1: Check whether any undo records have been applied from this
+		 * log.  Else, we've to find the undo location till where the undo
+		 * actions have been applied.
+		 */
+		if (!IsXactApplyProgressNotStarted(uur->uur_txn->urec_progress))
+		{
+			/*
+			 * If all the undo records in this log corresponding to this
+			 * transaction, has been applied, we return from here.
+			 */
+			if (IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+				break;
+
+			/*
+			 * Find the first undo record of uur_progress block number.  We'll
+			 * set end_urec_ptr to this undo record.
+			 */
+			end_urecptr = UndoBlockGetFirstUndoRecord(uur->uur_txn->urec_progress,
+													  urecptr, category);
+
+			/*
+			 * Since rollbacks from this undo log are in-progress, all undo
+			 * records from subsequent undo logs must have been applied.  Hence,
+			 * this is the last log.  So, we set last_log_start_urecptr as the
+			 * start undo record pointer of this transaction from current log.
+			 */
+			last_log_start_urecptr = urecptr;
+			sz += (end_urecptr - urecptr);
+			break;
+		}
+
+		next_urecptr = uur->uur_txn->urec_next;
+
+		/*
+		 * Case 2: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 *
+		 * Even if some new undo got inserted after we have fetched this
+		 * transactions undo record, still the next_insert location will give
+		 * us the right point to compute end_urecptr.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == slot->logno)
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidBuffer, category);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoRecPtrIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 5: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log but before that consider
+		 * this log for request size computation.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+	if (last_log_start_urecptr_out &&
+		(*last_log_start_urecptr_out == InvalidUndoRecPtr))
+		*last_log_start_urecptr_out = last_log_start_urecptr;
+
+	return sz;
+}
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	{
+		if (GetXidQueueSize() >= pending_undo_queue_size ||
+			GetSizeQueueSize() >= pending_undo_queue_size)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < pending_undo_queue_size))
+		{
+			Assert(GetSizeQueueSize() < pending_undo_queue_size);
+
+			/*
+			 * XXX - Here, we should return true once we have background
+			 * worker facility.
+			 */
+			return false;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(pending_undo_queue_size)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId) + sizeof(UndoRecPtr);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 pending_undo_queue_size,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 pending_undo_queue_size,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 pending_undo_queue_size,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Error Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= pending_undo_queue_size)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as UNDO_REQUEST_INQUEUE so that undo
+	 * launcher or other undo worker can process this request.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->status = UNDO_REQUEST_INQUEUE;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr, true);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->status = UNDO_REQUEST_INPROGRESS;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || UndoRequestIsInProgress(rh))
+					continue;
+
+				/*
+				 * The request that is present in any queue must be a valid request
+				 * and its status must be in_queue.
+				 */
+				Assert(UndoRequestIsValid(rh));
+				Assert(UndoRequestIsInQueue(rh));
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->status = UNDO_REQUEST_INPROGRESS;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself if the request is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues.  The two reasons why request can't be pushed are (a) the size of
+ * request is smaller than a threshold and the request is not from discard
+ * worker, (b) the undo request queues are full.
+ *
+ * It is not advisable to apply the undo actions of a very large transaction
+ * in the foreground as that can lead to a delay in retruning the control back
+ * to user after abort.
+ */
+bool
+RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+	UndoRecPtr	last_log_start_urec_ptr = InvalidUndoRecPtr;
+	RollbackHashKey hkey;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * Find the rollback request size and the end_urec_ptr (in case of discard
+	 * worker only).
+	 */
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr,
+										  &last_log_start_urec_ptr, full_xid);
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+										   HASH_ENTER_NULL, &found);
+
+	/*
+	 * It can only fail, if the value of pending_undo_queue_size or
+	 * max_connections guc is reduced after restart of the server.
+	 */
+	if (rh == NULL)
+	{
+		Assert(RollbackHTIsFull());
+
+		ereport(PANIC,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("rollback hash table is full, try running with higher value of pending_undo_queue_size")));
+	}
+
+	/* We shouldn't try to add the same rollback request again. */
+	if (!found)
+	{
+		rh->start_urec_ptr = start_urec_ptr;
+		rh->end_urec_ptr = end_urec_ptr;
+		rh->last_log_start_urec_ptr = last_log_start_urec_ptr;
+		rh->dbid = dbid;
+		rh->full_xid = full_xid;
+
+		/* Increment the pending request counter. */
+		ProcGlobal->xactsHavingPendingUndo++;
+
+		if (can_push)
+		{
+			UndoRequestInfo urinfo;
+
+			ResetUndoRequestInfo(&urinfo);
+
+			urinfo.full_xid = rh->full_xid;
+			urinfo.start_urec_ptr = rh->start_urec_ptr;
+			urinfo.end_urec_ptr = rh->end_urec_ptr;
+			urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+			urinfo.dbid = rh->dbid;
+			urinfo.request_size = req_size;
+
+			InsertRequestIntoUndoQueues(&urinfo);
+
+			/*
+			 * Indicates that the request will be processed by undo
+			 * worker.
+			 */
+			rh->status = UNDO_REQUEST_INQUEUE;
+			pushed = true;
+		}
+		/*
+		 * The request can't be pushed into the undo worker queue.  The
+		 * backends will try executing by itself.
+		 */
+		else
+			rh->status = UNDO_REQUEST_INPROGRESS;
+	}
+	else if (!UndoRequestIsValid(rh) && can_push)
+	{
+		/*
+		 * If we found the request which is still not in queue or not in
+		 * progress then add it to the queue if there is a space in the queue.
+		 */
+		UndoRequestInfo urinfo;
+
+		ResetUndoRequestInfo(&urinfo);
+
+		urinfo.full_xid = rh->full_xid;
+		urinfo.start_urec_ptr = rh->start_urec_ptr;
+		urinfo.end_urec_ptr = rh->end_urec_ptr;
+		urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+		urinfo.dbid = rh->dbid;
+		urinfo.request_size = req_size;
+
+		InsertRequestIntoUndoQueues(&urinfo);
+
+		/* Indicates that the request will be processed by the undo worker */
+		rh->status = UNDO_REQUEST_INQUEUE;
+		pushed = true;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return pushed;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr,
+					  bool lock)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	if (!lock)
+		LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	/* Decrement the pending request counter. */
+	ProcGlobal->xactsHavingPendingUndo--;
+
+	if (!lock)
+		LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Mark the entry status as invalid in the rollback hash table.
+ */
+void
+RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+						   UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	rh->status = UNDO_REQUEST_INVALID;
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Returns the start undo record pointer for the last undo log in which
+ * transaction has spanned.  This will be different from start_urec_ptr only
+ * when the undo for a transaction has spanned across multiple undo logs.
+ */
+UndoRecPtr
+RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+							 UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+	UndoRecPtr	last_log_start_urecptr;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	last_log_start_urecptr = rh->last_log_start_urec_ptr;
+	LWLockRelease(RollbackRequestLock);
+
+	return last_log_start_urecptr;
+}
+
+/*
+ * Returns true, if the rollback hash table is full, false, otherwise.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
+
+/*
+ * Get the smallest of 'xid having pending undo' and 'oldestXmin'.
+ */
+FullTransactionId
+RollbackHTGetOldestFullXid(FullTransactionId oldestXmin)
+{
+	RollbackHashEntry   *rh;
+	FullTransactionId	oldestXid = oldestXmin;
+	HASH_SEQ_STATUS		status;
+
+	/* Fetch the pending undo requests */
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	Assert(hash_get_num_entries(RollbackHT) <= UndoRollbackHashTableSize());
+	hash_seq_init(&status, RollbackHT);
+	while (RollbackHT != NULL &&
+		   (rh = (RollbackHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (!FullTransactionIdIsValid(oldestXid) ||
+			FullTransactionIdPrecedes(rh->full_xid, oldestXid))
+			oldestXid = rh->full_xid;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return oldestXid;
+}
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1cf0b..19e4f1f0c4 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,4 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 498373fd0e..884fa2af52 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	ProcGlobal->xactsHavingPendingUndo = 0;
 }
 
 /*
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 5d9af89036..ede9c510ea 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1085,6 +1085,20 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		CommitTransactionCommand();
 }
 
+/*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
 /*
  * Process any command-line switches and any additional GUC variable
  * settings passed in the startup packet.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 296fb77166..a7d1db5249 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -3040,6 +3041,27 @@ static struct config_int ConfigureNamesInt[] =
 		check_autovacuum_work_mem, NULL, NULL
 	},
 
+	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"pending_undo_queue_size", PGC_POSTMASTER, RESOURCES_MEM,
+			gettext_noop("Sets the size of queue used to register undo requests"),
+			NULL,
+		},
+		&pending_undo_queue_size,
+		1024, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"old_snapshot_threshold", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
 			gettext_noop("Time before a snapshot is too old to read pages changed after the snapshot was taken."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index cfad86c02a..592f6e1b4a 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -604,6 +604,14 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# UNDO options
+#------------------------------------------------------------------------------
+
+#rollback_overflow_size = 64	# default size above which the undo
+					# requests are pushed to undo workers
+#pending_undo_queue_size = 1024	# size of queue used to register undo
+					# requests
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index cc00509699..01f248a41e 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index f7cfa9f6be..24ea97b8d3 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -117,5 +117,8 @@ extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
 								 XLogRecPtr recptr);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 										UndoLogCategory category);
+extern UndoRecPtr UndoBlockGetFirstUndoRecord(BlockNumber blkno,
+											  UndoRecPtr urec_ptr,
+											  UndoLogCategory category);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000000..64d89ba972
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+extern PGDLLIMPORT int	rollback_overflow_size;
+extern PGDLLIMPORT int	pending_undo_queue_size;
+
+/*
+ * Current status of the undo request in the hash table.
+ */
+typedef enum
+{
+	/*
+	 * Request is present in the rollback hash table, but not present in any
+	 * of the queues.  In this state, the undo actions can't be executed.
+	 *
+	 * The request will be marked with this status if a) discard worker finds
+	 * that there is no space in the undo worker queue for inserting the undo
+	 * request, b) there is an error while backend or undo worker is
+	 * executing undo actions and there is no space in the error queue.
+	 *
+	 * Later when the discard worker finds such entry and if there is a
+	 * sufficient space in the undo worker queues, then the request will be
+	 * added to them and the status will be changed to UNDO_REQUEST_INQUEUE.
+	 *
+	 * It is important to keep the request in hash table with this status
+	 * intsead of removing it to compute the value of
+	 * oldestXidHavingUnappliedUndo.  If we don't do that, then the
+	 * corresponding xid won't be considered for computation of
+	 * oldestXidHavingUnappliedUndo.
+	 */
+	UNDO_REQUEST_INVALID,
+
+	/*
+	 * When backend or discard worker push the request to undo worker queue the
+	 * status will be set to this.  Undo workers pulls such requests from the
+	 * queues, change the state as UNDO_REQUEST_INPROGRESS and process the undo
+	 * actions.
+	 */
+	UNDO_REQUEST_INQUEUE,
+
+	/*
+	 * Undo action execution is in progress either by backend or by undo worker.
+	 */
+	UNDO_REQUEST_INPROGRESS
+} UndoRequestStatus;
+
+/*
+ * UndoRequestIsValid
+ *		True iff undo request status is not invalid.
+ */
+#define UndoRequestIsValid(rh) \
+	((bool) ((rh->status) != UNDO_REQUEST_INVALID))
+
+ /*
+  * UndoRequestIsInProgress
+  *		True iff undo request status is in progress.
+  */
+#define UndoRequestIsInProgress(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INPROGRESS))
+
+/*
+ * UndoRequestIsInQueue
+ *		True iff undo request status is in queue.
+ */
+#define UndoRequestIsInQueue(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INQUEUE))
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	UndoRequestStatus	status;	/* current state of the entry. */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	TimestampTz next_retry_at;
+	TimestampTz err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->last_log_start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->last_log_start_urec_ptr = rh->last_log_start_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/*
+ * From an undo log if all the undo actions have been applied for a particular
+ * transaction, we set the uur_progress of the transaction's log in that undo
+ * log as MaxBlockNumber.  If none of the undo actions have yet been applied,
+ * we set it to InvalidBlockNumber.
+ */
+#define XACT_APPLY_PROGRESS_COMPLETED MaxBlockNumber
+#define XACT_APPLY_PROGRESS_NOT_STARTED InvalidBlockNumber
+
+#define IsXactApplyProgressCompleted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_COMPLETED)
+
+#define IsXactApplyProgressNotStarted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_NOT_STARTED)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+			UndoRequestInfo *urinfo, bool *in_other_db);
+
+/* Exposed functions for rollback hash table. */
+extern uint64 FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+							UndoRecPtr *end_urecptr_out,
+							UndoRecPtr *last_log_start_urecptr_out,
+							FullTransactionId full_xid);
+extern bool RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid,
+					UndoRecPtr start_urec_ptr, bool lock);
+extern bool RollbackHTIsFull(void);
+extern int UndoRollbackHashTableSize(void);
+extern void RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+								UndoRecPtr start_urec_ptr);
+extern UndoRecPtr RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+											   UndoRecPtr start_urec_ptr);
+extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 61a24c2e3c..1afc4d3b5d 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -426,6 +426,7 @@ extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 						 Oid useroid, char *out_dbname, bool override_allow_connections);
+extern bool dbid_exists(Oid dboid);
 extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index ac7ee72952..824f6bf232 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Number of aborted transactions with pending undo actions. */
+	int			xactsHavingPendingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
-- 
2.16.2.windows.1

0011-Infrastructure-to-execute-pending-undo-actions.patchapplication/octet-stream; name=0011-Infrastructure-to-execute-pending-undo-actions.patchDownload
From 0a7b5f6cea3f64bc216718c701747e4cff10c333 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:27:58 +0530
Subject: [PATCH 11/13] Infrastructure to execute pending undo actions.

To apply the undo actions, we collect the undo records in bulk and try to
process them together.  We ensure to update the transaction's progress at
regular intervals so that after a crash we can skip already applied undo.

This provides a way for users to register a callback for processing the
undo records based on resource manager.

Dilip Kumar, Amit Kapila, Thomas Munro and Kuntal Ghosh with inputs from
Robert Haas
---
 src/backend/access/rmgrdesc/Makefile         |   3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c |  47 +++
 src/backend/access/transam/rmgr.c            |   5 +-
 src/backend/access/undo/Makefile             |   3 +-
 src/backend/access/undo/undoaccess.c         |  39 +-
 src/backend/access/undo/undoaction.c         | 524 +++++++++++++++++++++++++++
 src/backend/access/undo/undoactionxlog.c     |  60 +++
 src/backend/replication/logical/decode.c     |   1 +
 src/bin/pg_rewind/parsexlog.c                |   2 +-
 src/bin/pg_waldump/rmgrdesc.c                |   3 +-
 src/include/access/rmgr.h                    |   2 +-
 src/include/access/rmgrlist.h                |  52 +--
 src/include/access/undoaccess.h              |   4 +
 src/include/access/undoaction_xlog.h         |  39 ++
 src/include/access/undorequest.h             |   3 -
 src/include/access/xlog_internal.h           |  18 +-
 16 files changed, 767 insertions(+), 38 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/include/access/undoaction_xlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef8a3..640d37f37a 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000000..c396582b59
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b0537405a..c57eca240f 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_status, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 73275028be..68696bc81a 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
+OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
+	   undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 4ffec58c5b..66c3175d03 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -84,8 +84,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 											Buffer *prevbuf);
 static int	UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
 									   UndoRecPtr xact_urp, int size, int offset);
-static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
-									  int idx);
 static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 										UndoRecPtr urecptr, UndoRecPtr xact_urp);
 static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
@@ -284,6 +282,41 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 	LWLockRelease(&slot->discard_update_lock);
 }
 
+/*
+ * Prepare to update the undo apply progress in the transaction header.
+ */
+void
+UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+							   UndoRecPtr xact_urp, BlockNumber progress)
+{
+	int			index = 0;
+	int			offset;
+
+	Assert(UndoRecPtrIsValid(xact_urp));
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetCategory(xact_urp) == UNDO_TEMP)
+		return;
+
+	/* It shouldn't be discarded. */
+	Assert(!UndoRecPtrIsDiscarded(xact_urp));
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_progress);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the undo action progress in xact_urec_info, this will be overwritten
+	 * in actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].progress = progress;
+}
+
 /*
  * Overwrite the first undo record of the previous transaction to update its
  * next pointer.
@@ -292,7 +325,7 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
  * This must be called under the critical section.  This will just overwrite the
  * header of the undo record.
  */
-static void
+void
 UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000000..d130eb8f14
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,524 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to
+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+static void UpdateUndoApplyProgress(UndoRecPtr last_log_start_urec_ptr,
+						  BlockNumber block_num);
+static bool UndoAlreadyApplied(FullTransactionId full_xid,
+						UndoRecPtr to_urecptr);
+static void ApplyUndo(UndoRecInfo *urecinfo, int nrecords);
+static void ProcessAndApplyUndo(FullTransactionId full_xid,
+				UndoRecPtr from_urecptr, UndoRecPtr to_urecptr,
+				UndoRecPtr last_log_start_urec_ptr, bool complete_xact);
+
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else if (luur->uur_block > ruur->uur_block)
+		return 1;
+	else if (luur->uur_offset < ruur->uur_offset)
+		return -1;
+	else if (luur->uur_offset > ruur->uur_offset)
+		return 1;
+	else if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+	{
+		/*
+		 * If records are for the same block and offset, then maintain their
+		 * existing order by comparing their index in the array.
+		 */
+		return -1;
+	}
+	else
+		return 1;
+}
+
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	/*
+	 * We don't need to update the progress for temp tables as they get
+	 * discraded after startup.
+	 */
+	if (category == UNDO_TEMP)
+		return;
+
+	BeginUndoRecordInsert(&context, category, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,
+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecordFetchContext	context;
+
+	/* Fetch the undo record. */
+	BeginUndoFetch(&context);
+	uur = UndoFetchRecord(&context, to_urecptr);
+	FinishUndoFetch(&context);
+
+	/* already processed and discarded */
+	if (uur == NULL)
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		return true;
+	}
+
+	/* already processed */
+	if (IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		UndoRecordRelease(uur);
+		return true;
+	}
+
+	Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+	UndoRecordRelease(uur);
+
+	return false;
+}
+
+/*
+ * ApplyUndo - Invode rmgr specific undo apply functions.
+ *
+ * urecinfo - An array of undo records sorted in the rmgr order.
+ * nrecords - number of records in this array.
+ */
+static void
+ApplyUndo(UndoRecInfo *urecinfo, int nrecords)
+{
+	int			rmgr_start_idx = 0;
+	int			rmgr_nrecords = 0;
+	int			prev_rmid = -1;
+	int			i;
+
+	/* Apply the undo action for each rmgr. */
+	for (i = 0; i < nrecords; i++)
+	{
+		UnpackedUndoRecord *uur = urecinfo[i].uur;
+
+		Assert(uur->uur_rmid >= 0);
+
+		/*
+		 * If this undo is not for the same rmgr then apply all undo
+		 * actions for the previous rmgr.
+		 */
+		if (prev_rmid >= 0 &&
+			prev_rmid != uur->uur_rmid)
+		{
+			Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+			RmgrTable[prev_rmid].rm_undo(rmgr_nrecords,
+										 &urecinfo[rmgr_start_idx]);
+
+			rmgr_start_idx = i;
+			rmgr_nrecords = 0;
+		}
+
+		rmgr_nrecords++;
+		prev_rmid = uur->uur_rmid;
+	}
+
+	/* Apply the last set of the actions. */
+	Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+	RmgrTable[prev_rmid].rm_undo(rmgr_nrecords, &urecinfo[rmgr_start_idx]);
+}
+
+/*
+ * ProcessAndApplyUndo - Fetch undo records and apply actions.
+ *
+ * We always process the undo of the last log when the undo for a transaction
+ * spans across multiple logs.  Then from there onwards the previous undo logs
+ * for the same transaction are processed.
+ *
+ * We also update the undo apply progress in the transaction header so that
+ * after recovery we don't need to process the records that are already
+ * processed.  As we update the progress only after one batch of records,
+ * the crash in-between can cause us to read/apply part of undo records
+ * again but this will never be more than one-batch.  We can further optimize
+ * it by marking the progress in each record, but that has its own downsides
+ * like it will generate more WAL and I/O corresponding to dirty undo buffers.
+ */
+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	do
+	{
+		BlockNumber	progress_block_num = InvalidBlockNumber;
+		int			i;
+		int			nrecords;
+		bool		log_switched = false;
+		bool		rollback_completed = false;
+		bool		update_progress = false;
+		UndoRecPtr	progress_urec_ptr = InvalidUndoRecPtr;
+		UndoRecInfo	*first_urecinfo;
+		UndoRecInfo	*last_urecinfo;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update
+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		first_urecinfo = &urecinfo[0];
+		last_urecinfo = &urecinfo[nrecords - 1];
+		if (last_urecinfo->uur->uur_info & UREC_INFO_LOGSWITCH)
+		{
+			UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;
+
+			/*
+			 * We have crossed the log boundary.  The rest of the undo for
+			 * this transaction is in some other log, the location of which
+			 * can be found from this record.  See commets atop undoaccess.c.
+			 */
+			log_switched = true;
+
+			/*
+			 * We need to save the undo record pointer of the last record from
+			 * previous undo log.  We will use the same as from location in
+			 * next iteration of bulk fetch.
+			 */
+			Assert(UndoRecPtrIsValid(logswitch->urec_prevurp));
+			urec_ptr = logswitch->urec_prevurp;
+
+			/*
+			 * The last fetched undo record corresponds to the first undo
+			 * record of the current log.  Once, the undo actions are performed
+			 * from this log, we've to mark the progress as completed.
+			 */
+			progress_urec_ptr = last_urecinfo->urp;
+
+			/*
+			 * We also need to save the start location of this transaction in
+			 * previous log.  This will be used in the next iteration of bulk
+			 * fetch and updating progress location.
+			 */
+			if (complete_xact)
+			{
+				Assert(UndoRecPtrIsValid(logswitch->urec_prevlogstart));
+				last_log_start_urec_ptr = logswitch->urec_prevlogstart;
+			}
+
+			/* We've to update the progress for the current log as completed. */
+			update_progress = true;
+		}
+		else if (complete_xact)
+		{
+			if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(last_urecinfo->urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(first_urecinfo->urp) > progress_block_num)
+				{
+					update_progress = true;
+					progress_urec_ptr = last_log_start_urec_ptr;
+				}
+			}
+			else
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				rollback_completed = true;
+				update_progress = true;
+				progress_urec_ptr = last_log_start_urec_ptr;
+			}
+		}
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(FullTransactionIdEquals(full_xid, urecinfo[0].uur->uur_fxid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urecinfo, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		/* Call resource manager specific callbacks to apply actions. */
+		ApplyUndo(urecinfo, nrecords);
+
+		/* Set undo action apply progress if required. */
+		if (update_progress)
+		{
+			Assert(UndoRecPtrIsValid(progress_urec_ptr));
+
+			if (log_switched || rollback_completed)
+			{
+				/*
+				 * We have crossed the log boundary or executed all the undo
+				 * actions for the main transaction.  So, mark current log
+				 * header as complete and set the next progress location in
+				 * the previous log.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else
+			{
+				/*
+				 * Update the progress block number.  We increase the block
+				 * number by one since the current block might have some undo
+				 * records that are yet to be applied.  But, all undo records
+				 * from the next block must have been applied.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										progress_block_num + 1);
+			}
+		}
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+	} while (true);
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{
+	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(from_urecptr != InvalidUndoRecPtr);
+	Assert(to_urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * Here we compute the last log start urp which is used for fetching the
+	 * undo records and updating the undo action progress.
+	 *
+	 * For rollbacks of subtransaction, we won't be able to calculate the last
+	 * log start urp since we don't have the start urp of the top xid and hence
+	 * we won't be able to follow the transaction chains to find the last log.
+	 */
+	if (complete_xact)
+	{
+		if (UndoRecPtrGetCategory(to_urecptr) == UNDO_TEMP)
+		{
+			UndoRecPtr end_urec_ptr = from_urecptr;
+
+			/*
+			 * For temporary tables, we don't push the rollback request in the
+			 * rollback hash table so we can't directly get the last log start
+			 * urp from there.  Instead, we need to compute it now.
+			 */
+			(void) FindUndoEndLocationAndSize(to_urecptr, &end_urec_ptr,
+											  &last_log_start_urec_ptr,
+											  full_xid);
+		}
+		else
+		{
+			/*
+			 * It is important here to fetch the latest undo record and validate if
+			 * the actions are already executed.  The reason is that it is possible
+			 * that discard worker or backend might try to execute the rollback
+			 * request which is already executed.  For ex., after discard worker
+			 * fetches the record and found that this transaction need to be
+			 * rolledback, backend might concurrently execute the actions and
+			 * remove the request from rollback hash table.
+			 *
+			 * The other case where this will be required is when the transactions
+			 * records span across multiple logs.  Say, we register the
+			 * transaction from the first log and then we encounter the same
+			 * transaction in the second log where its status is still not marked
+			 * as done.  Now, before we try to register the request for the second
+			 * log, the undo worker came along rolled back the previous request
+			 * and removed its hash entry.  In this case, we will successfully
+			 * register the request from the second log and it should be detected
+			 * here.
+			 */
+			if (UndoAlreadyApplied(full_xid, to_urecptr))
+				return;
+
+			last_log_start_urec_ptr =
+				RollbackHTGetLastLogStartUrp(full_xid, to_urecptr);
+		}
+	}
+
+	ProcessAndApplyUndo(full_xid, from_urecptr, to_urecptr,
+						last_log_start_urec_ptr, complete_xact);
+
+	/*
+	 * Undo actions are applied so delete the hash table entry.
+	 */
+	RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000000..8d4ff7f6b4
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, category, 1, record);
+
+	/* Update the undo apply progress in the transaction header. */
+	UndoRecordPrepareApplyProgress(&context, xlrec->urec_ptr,
+								   xlrec->progress);
+
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index d3a9c4d64c..272edcbcba 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 287af60c4e..b26c45e4a2 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150dd91..976f80e9c3 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56a4c..0a3794a44e 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e950..b424c3ca5c 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -24,27 +24,31 @@
  * Changes to this list possibly need an XLOG_PAGE_MAGIC bump.
  */
 
-/* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+/*
+ * symbol name, textual name, redo, desc, identify, startup, cleanup, mask,
+ * undo, undo_status, undo_desc
+ */
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 24ea97b8d3..7c31332993 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -13,6 +13,7 @@
 #ifndef UNDOACCESS_H
 #define UNDOACCESS_H
 
+#include "access/transam.h"
 #include "access/undolog.h"
 #include "access/undorecord.h"
 #include "access/xlogdefs.h"
@@ -94,6 +95,9 @@ typedef struct UndoRecordFetchContext
 	UndoRecPtr	urp;			/* Previous undo record pointer. */
 } UndoRecordFetchContext;
 
+extern void UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+					UndoRecPtr urecptr, BlockNumber progress);
+extern void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx);
 extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
 								  UndoLogCategory category,
 								  int nprepared,
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000000..b9e65d1f7a
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
index 64d89ba972..82640b88f4 100644
--- a/src/include/access/undorequest.h
+++ b/src/include/access/undorequest.h
@@ -224,8 +224,5 @@ extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin
 /* functions exposed from undoaction.c */
 extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool nopartial);
-extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
-						  int last_idx, Oid reloid, FullTransactionId full_xid,
-						  BlockNumber blkno, bool blk_chain_complete);
 
 #endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b986a38575..35aff69492 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -19,10 +19,14 @@
 #ifndef XLOG_INTERNAL_H
 #define XLOG_INTERNAL_H
 
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undorecord.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -270,6 +274,15 @@ typedef enum
 	RECOVERY_TARGET_ACTION_SHUTDOWN
 }			RecoveryTargetAction;
 
+/*
+ * Return values for undo status callback functions.
+ */
+typedef enum UndoStatus
+{
+	UNDO_STATUS_WAIT_XMIN,		/* wait until the xmin passes an xid */
+	UNDO_STATUS_DISCARD			/* the record set should be discarded */
+} UndoStatus;
+
 /*
  * Method table for resource managers.
  *
@@ -295,9 +308,12 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	void		(*rm_undo) (int nrecords, UndoRecInfo *records);
+	UndoStatus	(*rm_undo_status) (UnpackedUndoRecord *record, TransactionId *xid);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
-- 
2.16.2.windows.1

0012-Allow-foreground-transactions-to-perform-undo-action.patchapplication/octet-stream; name=0012-Allow-foreground-transactions-to-perform-undo-action.patchDownload
From e92123c9135648e3450d1bfbb90d079d59dfe623 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:42:46 +0530
Subject: [PATCH 12/13] Allow foreground transactions to perform undo actions
 on abort.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

Amit Kapila and Dilip Kumar  with inputs from Robert Haas
---
 src/backend/access/transam/twophase.c         |  82 ++++-
 src/backend/access/transam/varsup.c           |  24 ++
 src/backend/access/transam/xact.c             | 490 +++++++++++++++++++++++++-
 src/backend/access/undo/README.UndoProcessing |  39 ++
 src/backend/access/undo/undoaccess.c          |   7 +
 src/backend/access/undo/undoaction.c          |  23 +-
 src/backend/storage/ipc/ipc.c                 |   7 +
 src/backend/tcop/postgres.c                   |   8 +-
 src/backend/utils/error/elog.c                |  23 +-
 src/backend/utils/init/globals.c              |   1 +
 src/backend/utils/resowner/resowner.c         |  11 +-
 src/include/access/transam.h                  |   1 +
 src/include/access/twophase.h                 |   3 +-
 src/include/access/xact.h                     |  11 +
 src/include/miscadmin.h                       |  15 +
 src/include/utils/elog.h                      |   2 +
 16 files changed, 725 insertions(+), 22 deletions(-)
 create mode 100644 src/backend/access/undo/README.UndoProcessing

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 477709bbc2..4d34d11ddb 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -909,7 +910,7 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
 /*
  * Header for a 2PC state file
  */
-#define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
+#define TWOPHASE_MAGIC	0x57F94535	/* format identifier */
 
 typedef struct TwoPhaseFileHeader
 {
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need its start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoLogCategories];
+	UndoRecPtr	end_urec_ptr[UndoLogCategories];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1012,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1044,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1485,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	startUrecPtr[UndoLogCategories];
+	UndoRecPtr	endUrecPtr[UndoLogCategories];
+	bool		uRequestRegistered[UndoLogCategories];
+	uint32		epoch;
+	int			i;
+	FullTransactionId fXid;
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1528,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(startUrecPtr, hdr->start_urec_ptr, sizeof(startUrecPtr));
+	memcpy(endUrecPtr, hdr->end_urec_ptr, sizeof(endUrecPtr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1518,6 +1545,13 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * TransactionIdIsInProgress will stop saying the prepared xact is in
 	 * progress), then run the post-commit or post-abort callbacks. The
 	 * callbacks will release the locks the transaction held.
+	 *
+	 * XXX Note that, unlike non-prepared transactions, we don't skip
+	 * releasing the locks when we have to perform the undo actions.  The
+	 * reason is that here the locks are not directly associated with current
+	 * transaction, rather it has to acquire those locks to apply undo actions.
+	 * So, if we don't release the locks for prepared transaction, the undo
+	 * applying transaction will wait forever.
 	 */
 	if (isCommit)
 		RecordTransactionCommitPrepared(xid,
@@ -1526,10 +1560,36 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 										hdr->ninvalmsgs, invalmsgs,
 										hdr->initfileinval, gid);
 	else
+	{
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id in
+		 * TwoPhaseFileHeader instead.)
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		fXid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Register the rollback request to apply undo actions.  It is
+		 * important to do this before marking it aborted in clog, see
+		 * comments atop CheckAndRegisterUndoRequest for further details.
+		 */
+		for (i = 0; i < UndoLogCategories; i++)
+		{
+			if (endUrecPtr[i] != InvalidUndoRecPtr && i != UNDO_TEMP)
+			{
+				uRequestRegistered[i] = RegisterUndoRequest(endUrecPtr[i],
+															startUrecPtr[i],
+															hdr->database,
+															fXid);
+			}
+		}
+
 		RecordTransactionAbortPrepared(xid,
 									   hdr->nsubxacts, children,
 									   hdr->nabortrels, abortrels,
 									   gid);
+	}
 
 	ProcArrayRemove(proc, latestXid);
 
@@ -1614,6 +1674,24 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	RESUME_INTERRUPTS();
 
+	if (!isCommit)
+	{
+		/*
+		 * We need to perform undo actions while we are still in a transaction.
+		 */
+		if (!ProcessUndoRequestForEachLogCat(fXid, hdr->database,
+											 endUrecPtr, startUrecPtr,
+											 uRequestRegistered, false))
+		{
+			/* Abort the failed transaction. */
+			AbortOutOfAnyTransaction();
+			FlushErrorState();
+
+			/* Restart our transaction. */
+			StartTransactionCommand();
+		}
+	}
+
 	pfree(buf);
 }
 
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec7f3..fd01989302 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -566,3 +566,27 @@ GetNewObjectId(void)
 
 	return result;
 }
+
+/*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d7930c077d..60a9a0a18c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,16 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each log category */
+	UndoRecPtr	startUrecPtr[UndoLogCategories]; /* this is 'to' location */
+	UndoRecPtr	latestUrecPtr[UndoLogCategories]; /* this is 'from'
+												   * location */
+	/*
+	 * whether the undo request is registered to be processed by worker later?
+	 */
+	bool		undoRequestResgistered[UndoLogCategories];
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -339,6 +353,7 @@ static void ShowTransactionState(const char *str);
 static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
+static void CheckAndRegisterUndoRequest(void);
 
 
 /* ----------------------------------------------------------------
@@ -362,9 +377,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +738,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +925,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -1968,6 +1988,14 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2264,6 +2292,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2433,7 +2463,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, s->startUrecPtr, s->latestUrecPtr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2622,7 +2652,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2780,6 +2812,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2879,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2942,18 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ReleaseResourcesAndProcessUndo and for
+			 * subtransaction, we promote the error to fatal in such a
+			 * situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3032,13 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3087,7 +3134,9 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3156,9 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				CheckAndRegisterUndoRequest();
 				AbortSubTransaction();
+				ReleaseResourcesAndProcessUndo();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3175,7 +3226,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!NeedToPerformUndoActions());
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3190,7 +3245,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3258,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!NeedToPerformUndoActions());
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3215,7 +3276,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3226,7 +3289,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_END:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3255,7 +3320,9 @@ AbortCurrentTransaction(void)
 			 * Abort, cleanup, go to idle state.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3266,7 +3333,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_PREPARE:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3277,7 +3346,9 @@ AbortCurrentTransaction(void)
 			 * we get ROLLBACK.
 			 */
 		case TBLOCK_SUBINPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3291,7 +3362,9 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBCOMMIT:
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,6 +3377,19 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ReleaseResourcesAndProcessUndo and for subtransaction, we
+			 * promote the error to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
@@ -3633,6 +3719,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +3913,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4031,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4173,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4157,6 +4251,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4266,6 +4362,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4482,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4473,17 +4573,25 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
+	/* Try to push rollback request to worker if possible. */
+	CheckAndRegisterUndoRequest();
+
 	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ReleaseResourcesAndProcessUndo();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4529,7 +4637,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!NeedToPerformUndoActions());
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4545,7 +4657,27 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_ABORT_PENDING:
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
+				CheckAndRegisterUndoRequest();
 				AbortTransaction();
+				ReleaseResourcesAndProcessUndo();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 *
+				 * AbortTransaction is already done, still need to release
+				 * locks and perform cleanup.
+				 */
+				ResetUndoActionsInfo();
+				ResourceOwnerRelease(s->curTransactionOwner,
+									 RESOURCE_RELEASE_LOCKS,
+									 false,
+									 true);
+				s->state = TRANS_ABORT;
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
 				break;
@@ -4572,7 +4704,9 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBCOMMIT:
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
+				CheckAndRegisterUndoRequest();
 				AbortSubTransaction();
+				ReleaseResourcesAndProcessUndo();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
@@ -4592,6 +4726,24 @@ AbortOutOfAnyTransaction(void)
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
+			case TBLOCK_SUBUNDO:
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 *
+				 * AbortSubTransaction is already done, still need to release
+				 * locks and perform cleanup.
+				 */
+				ResetUndoActionsInfo();
+				ResourceOwnerRelease(s->curTransactionOwner,
+					RESOURCE_RELEASE_LOCKS,
+					false,
+					false);
+				s->state = TRANS_ABORT;
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
 		}
 	} while (s->blockState != TBLOCK_DEFAULT);
 
@@ -4666,6 +4818,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4722,6 +4876,14 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4743,6 +4905,7 @@ static void
 CommitSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int		i;
 
 	ShowTransactionState("CommitSubTransaction");
 
@@ -4765,6 +4928,19 @@ CommitSubTransaction(void)
 	/* Do the actual "commit", such as it is */
 	s->state = TRANS_COMMIT;
 
+	/*
+	 * Propagate the undo pointers from current transaction to parent so that
+	 * if parent transaction get aborted we must not skip performing undo for
+	 * this transaction.
+	 */
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (UndoRecPtrIsValid(s->latestUrecPtr[i]))
+			s->parent->latestUrecPtr[i] = s->latestUrecPtr[i];
+		if (!UndoRecPtrIsValid(s->parent->startUrecPtr[i]))
+			s->parent->startUrecPtr[i] = s->startUrecPtr[i];
+	}
+
 	/* Must CCI to ensure commands of subtransaction are seen as done */
 	CommandCounterIncrement();
 
@@ -4909,7 +5085,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5336,6 +5513,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5533,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5560,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6160,284 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoLogCategory category)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->startUrecPtr[category]))
+		CurrentTransactionState->startUrecPtr[category] = urec_ptr;
+	CurrentTransactionState->latestUrecPtr[category] = urec_ptr;
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+}
+
+/*
+ * NeedToPerformUndoActions - Returns true, if the current transaction needs
+ * to perform undo actions, false otherwise.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing them if required.  We don't release the locks
+ * till we execute undo actions otherwise, there is a risk of deadlock.
+ */
+bool
+NeedToPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (UndoRecPtrIsValid(s->latestUrecPtr[i]))
+			return true;
+	}
+
+	return false;
+}
+
+/*
+ * CheckAndRegisterUndoRequest - Register the request for applying undo
+ *	actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+CheckAndRegisterUndoRequest()
+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	int		i;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  See ReleaseResourcesAndProcessUndo.
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}
+
+	if (!NeedToPerformUndoActions())
+		return;
+	/*
+	 * We can't postpone applying undo actions for subtransactions as the
+	 * modifications made by aborted subtransaction must not be visible even if
+	 * the main transaction commits.  See execute_undo_actions for detailed
+	 * explanation.
+	 */
+	if (IsSubTransaction())
+		return;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (i != UNDO_TEMP && UndoRecPtrIsValid(s->latestUrecPtr[i]))
+		{
+			result = RegisterUndoRequest(s->latestUrecPtr[i],
+										 s->startUrecPtr[i],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undoRequestResgistered[i] = result;
+		}
+	}
+}
+
+/*
+ * ReleaseResourcesAndProcessUndo - Release resources and process undo request.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ReleaseResourcesAndProcessUndo(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  One of the main reasons is that we might be already
+	 * processing undo actions for a (sub)transaction when we reach here
+	 * (for ex. error happens while processing undo actions for a
+	 * subtransaction).
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}
+
+	if (!NeedToPerformUndoActions())
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ReleaseResourcesAndProcessUndo: unexpected state %s",
+			TransStateAsString(s->state));
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	/*
+	 * We ignore the return value here as in either case we need to release
+	 * the resources and allow caller to proceed with further cleanup.
+	 */
+	(void) ProcessUndoRequestForEachLogCat(GetTopFullTransactionId(),
+										   MyDatabaseId,
+										   s->latestUrecPtr, s->startUrecPtr,
+										   s->undoRequestResgistered,
+										   IsSubTransaction());
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	/* Release the locks after processing undo request. */
+	ResourceOwnerRelease(s->curTransactionOwner,
+						 RESOURCE_RELEASE_LOCKS,
+						 false,
+						 !IsSubTransaction());
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
+ * ProcessUndoRequestForEachLogCat - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ *
+ * If we get any error while performing undo actions, we just insert the
+ * request into an error queue for later processing by undo launcher and allow
+ * the main transaction to continue.  In such a case the callers are
+ * responsible to release the resources.
+ */
+bool
+ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+								UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+								bool *undoRequestResgistered, bool isSubTrans)
+{
+	UndoRequestInfo urinfo;
+	int			i;
+	uint32		save_holdoff;
+	bool		success = true;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (end_urec_ptr[i] && !undoRequestResgistered[i])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(fxid,
+									 end_urec_ptr[i],
+									 start_urec_ptr[i],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[i];
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/* We should never reach here when we are in a semi-critical-section. */
+				Assert(SemiCritSectionCount == 0);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000000..e0caf9efeb
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,39 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 66c3175d03..55f51169b3 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -1009,6 +1009,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.category);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
index d130eb8f14..b6169639ce 100644
--- a/src/backend/access/undo/undoaction.c
+++ b/src/backend/access/undo/undoaction.c
@@ -457,11 +457,29 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool complete_xact)
 {
 	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+	UndoLogCategory		logcat = UndoRecPtrGetCategory(to_urecptr);
 
 	/* 'from' and 'to' pointers must be valid. */
 	Assert(from_urecptr != InvalidUndoRecPtr);
 	Assert(to_urecptr != InvalidUndoRecPtr);
 
+	/*
+	 * We need to execute the undo actions in a semi-critical section for
+	 *
+	 * (a) Subtransactions.  We can't proceed without applying
+	 * subtransaction's undo as the modifications made in that case must not
+	 * be visible even if the main transaction commits.  The reason why that
+	 * can happen is because for undo-based AM's we don't need to have a
+	 * separate transaction id for subtransactions and once the main
+	 * transaction commits the tuples modified by subtransactions will become
+	 * visible.
+	 *
+	 * (b) Temp tables.  We don't expect background workers to process undo of
+	 * temporary tables as the same won't be accessible.
+	 */
+	if (!complete_xact || logcat == UNDO_TEMP)
+		START_SEMI_CRIT_SECTION();
+
 	/*
 	 * Here we compute the last log start urp which is used for fetching the
 	 * undo records and updating the undo action progress.
@@ -472,7 +490,7 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 	 */
 	if (complete_xact)
 	{
-		if (UndoRecPtrGetCategory(to_urecptr) == UNDO_TEMP)
+		if (logcat == UNDO_TEMP)
 		{
 			UndoRecPtr end_urec_ptr = from_urecptr;
 
@@ -521,4 +539,7 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 	 * Undo actions are applied so delete the hash table entry.
 	 */
 	RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+
+	if (!complete_xact || logcat == UNDO_TEMP)
+		END_SEMI_CRIT_SECTION();
 }
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 05d02c23f5..22f0bac6f2 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -193,6 +193,13 @@ proc_exit_prepare(int code)
 	/* do our shared memory exits first */
 	shmem_exit(code);
 
+	/*
+	 * We need to clear semi-critical-section after exiting from shmem as we
+	 * can again use it for executing undo actions via
+	 * AbortOutOfAnyTransaction which is called during shmem exit.
+	 */
+	SemiCritSectionCount = 0;
+
 	elog(DEBUG3, "proc_exit(%d): %d callbacks to make",
 		 code, on_proc_exit_index);
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index a6505c7335..68eaacfa87 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -594,7 +594,9 @@ ProcessClientWriteInterrupt(bool blocked)
 			 * Don't mess with whereToSendOutput if ProcessInterrupts wouldn't
 			 * do anything.
 			 */
-			if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+			if (InterruptHoldoffCount == 0 &&
+				CritSectionCount == 0 &&
+				SemiCritSectionCount == 0)
 			{
 				/*
 				 * We don't want to send the client the error message, as a)
@@ -2985,7 +2987,9 @@ void
 ProcessInterrupts(void)
 {
 	/* OK to accept any interrupts now? */
-	if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
+	if (InterruptHoldoffCount != 0 ||
+		CritSectionCount != 0 ||
+		SemiCritSectionCount != 0)
 		return;
 	InterruptPending = false;
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720ef3a..d24828f543 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,16 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. If we are inside a semi-critical section, all errors become FATAL
+		 * errors.  See miscadmin.h.
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				SemiCritSectionCount > 0)
 				elevel = FATAL;
 		}
 
@@ -454,6 +458,7 @@ errfinish(int dummy,...)
 		QueryCancelHoldoffCount = 0;
 
 		CritSectionCount = 0;	/* should be unnecessary, but... */
+		SemiCritSectionCount = 0;
 
 		/*
 		 * Note that we leave CurrentMemoryContext set to ErrorContext. The
@@ -1164,6 +1169,22 @@ internalerrquery(const char *query)
 	return 0;					/* return value does not matter */
 }
 
+/*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
 /*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de256..9faeb84819 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t ConfigReloadPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile uint32 SemiCritSectionCount = 0;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c48ab..7fb832997f 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ReleaseResourcesAndProcessUndo.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (isCommit || !NeedToPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!NeedToPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 01f248a41e..7796f7248c 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -231,6 +231,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern uint32 GetEpochForXid(TransactionId xid);
 
 /*
  * Some frontend programs include this header.  For compilers that emit static
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c96e..497b92f2b8 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,6 +14,7 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undolog.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
@@ -41,7 +42,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726afa0..2ca61ea79d 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/undolog.h"
 #include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
@@ -428,6 +429,16 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
 
+/* functions to allow undo execution */
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+						UndoLogCategory category);
+extern void ResetUndoActionsInfo(void);
+extern bool NeedToPerformUndoActions(void);
+extern void ReleaseResourcesAndProcessUndo(void);
+extern bool ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool *undoRequestResgistered, bool isSubTrans);
+
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
 extern const char *xact_identify(uint8 info);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1afc4d3b5d..5efc7a2a9a 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -74,6 +74,12 @@
  * *critical* code should be marked as a critical section!	Currently, this
  * mechanism is only used for XLOG-related code.
  *
+ * Similar to the "critical section", we have another mechanism known as
+ * "semi critical section".  It generally has similar behaviour as
+ * "critical section" with the difference that it causes any ereport(ERROR) to
+ * become ereport(FATAL).  Currently this is used by undo-machinery, but it
+ * can be used in other places too.
+ *
  *****************************************************************************/
 
 /* in globals.c */
@@ -90,6 +96,7 @@ extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
 extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
+extern PGDLLIMPORT volatile uint32 SemiCritSectionCount;
 
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
@@ -137,6 +144,14 @@ do { \
 	CritSectionCount--; \
 } while(0)
 
+#define START_SEMI_CRIT_SECTION()  (SemiCritSectionCount++)
+
+#define END_SEMI_CRIT_SECTION() \
+do { \
+	Assert(SemiCritSectionCount > 0); \
+	SemiCritSectionCount--; \
+} while(0)
+
 
 /*****************************************************************************
  *	  globals.h --															 *
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8efd26..ed31351bfa 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
-- 
2.16.2.windows.1

0013-Allow-execution-and-discard-of-undo-by-background-wo.patchapplication/octet-stream; name=0013-Allow-execution-and-discard-of-undo-by-background-wo.patchDownload
From 4442753552b998b55e045a46e15071ac2f21b7f7 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 16:03:49 +0530
Subject: [PATCH 13/13] Allow execution and discard of undo by background
 workers.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

We don't allow any transaction older than 2^31 to have pending undo actions.
Also, we have a hard limit on the number of transactions that can have
pending undo which is proportional to pending_undo_queue_size.

Amit Kapila, Dilip Kumar and Kuntal Ghosh with inputs from Andres Freund,
Robert Haas and Thomas Munro.
---
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/varsup.c           |  35 +-
 src/backend/access/transam/xact.c             |   5 +
 src/backend/access/transam/xlog.c             |  29 +
 src/backend/access/undo/Makefile              |   4 +-
 src/backend/access/undo/README.UndoProcessing |  78 +++
 src/backend/access/undo/discardworker.c       | 215 +++++++
 src/backend/access/undo/undoaccess.c          |  58 +-
 src/backend/access/undo/undodiscard.c         | 489 +++++++++++++++
 src/backend/access/undo/undolog.c             |   2 +
 src/backend/access/undo/undorequest.c         | 199 ++++++-
 src/backend/access/undo/undoworker.c          | 828 ++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c              |   5 +
 src/backend/postmaster/bgworker.c             |  11 +
 src/backend/postmaster/pgstat.c               |   9 +
 src/backend/postmaster/postmaster.c           |  11 +
 src/backend/storage/ipc/ipci.c                |   6 +
 src/backend/storage/lmgr/lwlocknames.txt      |   1 +
 src/backend/storage/lmgr/proc.c               |   2 +
 src/backend/utils/misc/guc.c                  |  22 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/access/discardworker.h            |  20 +
 src/include/access/transam.h                  |  10 +
 src/include/access/undodiscard.h              |  26 +
 src/include/access/undolog.h                  |  13 +
 src/include/access/undoworker.h               |  29 +
 src/include/catalog/pg_control.h              |   9 +
 src/include/nodes/primnodes.h                 |   3 +-
 src/include/pgstat.h                          |   5 +-
 src/include/postmaster/postmaster.h           |   1 +
 src/include/storage/lwlock.h                  |   1 +
 src/include/storage/proc.h                    |   4 +
 src/include/storage/procarray.h               |   7 +-
 src/test/regress/expected/sysviews.out        |   3 +-
 34 files changed, 2118 insertions(+), 27 deletions(-)
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3042..4b00d7d83f 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest full xid having unapplied undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUnappliedUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index fd01989302..e74155d162 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -127,14 +127,16 @@ GetNewTransactionId(bool isSubXact)
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
 		{
@@ -147,14 +149,16 @@ GetNewTransactionId(bool isSubXact)
 								oldest_datname,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(WARNING,
 						(errmsg("database with OID %u must be vacuumed within %u transactions",
 								oldest_datoid,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -334,9 +338,23 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
+	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUnapplied Undo because this is the oldest xid
+	 * whose undo is not yet discarded so this is still a valid xid in the
+	 * system.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
 	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
@@ -433,6 +451,9 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 		 * Note: it's also possible that get_database_name fails and returns
 		 * NULL, for example because the database just got dropped.  We'll
 		 * still warn, even though the warning might now be unnecessary.
+		 *
+		 * XXX Can we easily distinguish that the problem is due to unapplied
+		 * undo or some old open transactions?
 		 */
 		if (IsTransactionState())
 			oldest_datname = get_database_name(oldest_datoid);
@@ -445,14 +466,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 							oldest_datname,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 		else
 			ereport(WARNING,
 					(errmsg("database with OID %u must be vacuumed within %u transactions",
 							oldest_datoid,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 	}
 }
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 60a9a0a18c..4e49a2e3b6 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2273,6 +2274,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5dbe485af2..99e4322e9b 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5159,6 +5159,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUnappliedUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6622,6 +6623,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6638,6 +6642,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7326,7 +7334,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8723,6 +8737,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUnappliedUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9635,6 +9653,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9692,12 +9713,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9737,6 +9763,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 68696bc81a..b4e7bab7d4 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
-	   undorequest.o
+OBJS = discardworker.o undoaccess.o undoaction.o undoactionxlog.o undodiscard.o \
+	   undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
index e0caf9efeb..b7817cf712 100644
--- a/src/backend/access/undo/README.UndoProcessing
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -37,3 +37,81 @@ tables are only accessible in the backend that has created them.  We can't
 postpone applying undo actions for subtransactions as the modifications
 made by aborted subtransaction must not be visible even if the main transaction
 commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue will allow us to
+process the requests of older transactions and help us to move
+oldesdXidHavingUnappliedUndo (this is a xid-horizon below which all the
+transactions are visible) forward.  A size-based queue which will help us to
+perform the rollbacks of larger aborts in a timely fashion, so that we don't get
+stuck while processing them during discard of the logs.  An error queue to hold
+the requests for transactions that failed to apply its undo.  The rollback hash
+table is used to avoid duplicate undo requests by backends and discard worker.
+The table must be able to accommodate all active undo requests.  The undo
+requests must appear in both xid and size requests queues or neither.  As of now,
+we process the requests from these queues in a round-robin fashion to give equal
+priority to all three types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.
+
+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+
+In a running system, scanning the rollback hash table will give us the value of
+oldestXidHavingUnappliedUndo, however, after startup, we need to once scan all
+the undo logs and populate the rollback hash table.  After startup, we allow
+connections, but don't allow transactions that want to write undo till the
+rollback hash table is initialized.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+To apply the undo actions, we collect the undo records in bulk and try to
+process them together.  We ensure to update the transaction's progress at
+regular intervals so that after a crash we can skip already applied undo.  The
+undo apply progress is updated in terms of the number of blocks processed.
+Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED indicates that all the
+undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED indicates that no undo action
+has been applied yet and any other value indicates that we have applied undo
+partially and after crash recovery, we need to start processing the undo from
+the same location.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then start reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms (10s as default) after starting.  Also, if there is no
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
+restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000000..6fb9c7070f
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,215 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be processed
+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/undodiscard.h"
+#include "access/discardworker.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* Normal exit from discard worker */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 55f51169b3..5ab9e775db 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -56,6 +56,7 @@
 #include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/proc.h"
 #include "miscadmin.h"
 
 /*
@@ -77,6 +78,13 @@
  */
 UndoCompressionInfo undo_compression_info[UndoLogCategories];
 
+/*
+ * Defines the number of times we try to wait for rollback hash table to get
+ * initialized.  After these many attempts it will return error and the user
+ * can retry the operation.
+ */
+#define ROLLBACK_HT_INIT_WAIT_TRY      60
+
 /* Prototypes for static functions. */
 static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 											UndoRecPtr urp, RelFileNode rnode,
@@ -668,6 +676,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	UndoCompressionInfo *compression_info =
 	&context->undo_compression_info[context->alloc_context.category];
 
+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo
+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}
+
+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+
 	/* Already reached maximum prepared limit. */
 	if (context->nprepared_undo == context->max_prepared_undo)
 		elog(ERROR, "already reached the maximum prepared limit");
@@ -1824,12 +1876,12 @@ UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 	page = BufferGetPage(buffer);
-	phdr = (UndoPageHeader)page;
+	phdr = (UndoPageHeader) page;
 
 	/* Calculate the size of the partial record. */
 	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
-						phdr->tuple_len + phdr->payload_len -
-						phdr->record_offset;
+					   phdr->tuple_len + phdr->payload_len -
+					   phdr->record_offset;
 
 	/* calculate the offset in current log. */
 	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000000..49a4ef2430
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,489 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlog_internal.h"
+#include "access/undolog.h"
+#include "access/undodiscard.h"
+#include "access/undorequest.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/resowner.h"
+
+/*
+ * Discard as many record sets as we can from the undo log occupying a given
+ * slot, considering the given xmin horizon.  If we encounter a record set
+ * that needs to be rolled back, register a rollback request.  Set *hibernate
+ * to false if work was done.
+ */
+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr, next_insert;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	UnpackedUndoRecord	*uur = NULL;
+	bool	need_discard = false;
+	FullTransactionId	undofxid = InvalidFullTransactionId;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		TransactionId wait_xid = InvalidTransactionId;
+		bool pending_abort = false;
+		bool request_rollback = false;
+		UndoStatus status;
+		UndoRecordFetchContext	context;
+
+		next_insert = UndoLogGetNextInsertPtr(logno);
+
+		/* There must be some undo data for a transaction. */
+		Assert(next_insert != undo_recptr);
+
+		/* Fetch the undo record for the given undo_recptr. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, undo_recptr);
+		FinishUndoFetch(&context);
+
+		if (uur != NULL)
+		{
+			if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)
+			{
+				/*
+				 * For the "shared" category, we only discard when the
+				 * rm_undo_status callback tells us we can.
+				 */
+				status = RmgrTable[uur->uur_rmid].rm_undo_status(uur, &wait_xid);
+
+				Assert((status == UNDO_STATUS_WAIT_XMIN &&
+						TransactionIdIsValid(wait_xid)) ^
+						(status == UNDO_STATUS_DISCARD &&
+						!TransactionIdIsValid(wait_xid)));
+			}
+			else
+			{
+				TransactionId xid = XidFromFullTransactionId(uur->uur_fxid);
+
+				/*
+				 * Otherwise we use the CLOG and xmin to decide whether to
+				 * wait, discard or roll back.
+				 *
+				 * XXX: We've added the transaction-in-progress check to
+				 * avoid xids of in-progress autovacuum as those are not
+				 * computed for oldestxmin calculation.  See
+				 * DiscardWorkerMain.
+				 */
+				if (TransactionIdDidCommit(xid))
+				{
+					/*
+					 * If this record set's xid isn't before the xmin
+					 * horizon, we'll have to wait before we can discard
+					 * it.
+					 */
+					if (TransactionIdFollowsOrEquals(xid, xmin))
+						wait_xid = xid;
+
+				}
+				else if (!TransactionIdIsInProgress(xid))
+				{
+					/*
+					 * If it hasn't been applied already, then we'll ask
+					 * for it to be applied now.  Otherwise it'll be
+					 * discarded.
+					 */
+					if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+						request_rollback = true;
+				}
+				else
+				{
+					/*
+					 * It's either in progress or isn't yet before the
+					 * xmin horizon, so we'll have to wait.
+					 */
+					wait_xid = XidFromFullTransactionId(uur->uur_fxid);
+				}
+			}
+
+			/*
+			 * Add the aborted transaction to the rollback request queues.
+			 *
+			 * We can ignore the abort for transactions whose corresponding
+			 * database doesn't exist.
+			 */
+			if (request_rollback && dbid_exists(uur->uur_txn->urec_dbid))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr,
+										   undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+
+				pending_abort = true;
+			}
+
+			next_urecptr = uur->uur_txn->urec_next;
+			undofxid = uur->uur_fxid;
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) we need to wait for a transaction first. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if (TransactionIdIsValid(wait_xid) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If we don't need to wait for this transaction and this is not
+			 * an aborted transaction, then we can discard it as well.
+			 */
+			if (!TransactionIdIsValid(wait_xid) && !pending_abort)
+			{
+				/*
+				 * It is safe to use next_insert as the location till which we
+				 * want to discard in this case.  If something new has been
+				 * added after we have fetched this transaction's record, it
+				 * won't be considered in this pass of discard.
+				 */
+				undo_recptr = next_insert;
+				latest_discardxid = XidFromFullTransactionId(undofxid);
+				need_discard = true;
+
+				/* We don't have anything more to discard. */
+				undofxid = InvalidFullTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/* Update the slot information for the next pass of discard. */
+			slot->wait_fxmin = undofxid;
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = XidFromFullTransactionId(undofxid);
+
+		Assert(uur == NULL);
+
+		/* If we reach here, this means there is something to discard. */
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+}
+
+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass
+ * over undo logs is complete, new undo can is allowed to be written in the
+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before start allowing any new transaction to write the
+ * undo we need to make sure that we know exact number of pending requests.
+ */
+void
+UndoLogProcess()
+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();
+
+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* We do not execute shared (non-transactional) undo records. */
+		if (slot->meta.category == UNDO_SHARED)
+			continue;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			TransactionId xid;
+			UndoRecordFetchContext	context;
+
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;
+
+			/* Fetch the undo record for the given undo_recptr. */
+			BeginUndoFetch(&context);
+			uur = UndoFetchRecord(&context, undo_recptr);
+			FinishUndoFetch(&context);
+
+			Assert(uur != NULL);
+
+			xid = XidFromFullTransactionId(uur->uur_fxid);
+
+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(xid) &&
+				!TransactionIdIsInProgress(xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are
+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) ==
+				UndoRecPtrGetLogNo(uur->uur_txn->urec_next))
+				undo_recptr = uur->uur_txn->urec_next;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+
+	CommitTransactionCommand();
+
+	/* Allow the transactions to start writting undo. */
+	ProcGlobal->rollbackHTInitialized = true;
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		/*
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.category == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin then try
+		 * to discard the undo log.
+		 */
+		if (!FullTransactionIdIsValid(slot->wait_fxmin) ||
+			FullTransactionIdPrecedes(slot->wait_fxmin, oldestXidHavingUndo))
+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+	}
+
+	/* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+	oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+
+	/*
+	 * Update the oldestFullXidHavingUnappliedUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.category == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index b5502f2417..09d9b3050a 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -145,6 +145,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 		}
 	}
 	else
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
index 530887a592..02609e64b9 100644
--- a/src/backend/access/undo/undorequest.c
+++ b/src/backend/access/undo/undorequest.c
@@ -54,10 +54,12 @@
 #include "postgres.h"
 #include "miscadmin.h"
 
+#include "access/discardworker.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_database.h"
@@ -927,6 +929,138 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 	return sz;
 }
 
+/*
+ * Fetch the start urec pointer for the transaction and the undo request size.
+ *
+ * start_urecptr_inout - This is an INOUT parameter.  If a transaction has
+ * overflowed to multiple undo logs, the caller can set start_urecptr_inout
+ * to a location of any of the undo logs where the transaction has written its
+ * first record for that particular log.  Given that, this function calculates
+ * the undo location where the transaction has inserted its first undo record.
+ * If a transaction hasn't overflowed to multiple undo logs, the value of this
+ * parameter remains unchanged.
+ *
+ * The first record of a transaction in each undo log contains a reference to
+ * the first record of this transaction in the previous log.  It finds the
+ * initial location by moving backward in the undo chain of this transaction
+ * across undo logs.  While doing the same, it also calculates the undo size
+ * between the input and output start undo record pointer value.
+ */
+static uint64
+FindUndoStartLocationAndSize(UndoRecPtr *start_urecptr_inout,
+							 FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+
+	Assert(start_urecptr_inout);
+
+	urecptr = *start_urecptr_inout;
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * A backend always set the start undo record pointer to the first undo
+	 * record inserted by this transaction.  Hence, we don't have to proceed
+	 * further.
+	 */
+	if (!IsDiscardProcess())
+		return sz;
+
+	/*
+	 * Since the discard worker processes the undo logs sequentially, it's
+	 * possible that start undo record pointer doesn't refer to the actual
+	 * start of the transaction.  Instead, it may refer to the start location
+	 * of the transaction in any of the subsequent logs.  In that case, we've
+	 * to find the actual start location of the transaction by going backwards
+	 * in the chain.
+	 */
+	while (true)
+	{
+		UndoLogOffset next_insert;
+		UndoRecordFetchContext	context;
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		/* Fetch the undo record. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, urecptr);
+		FinishUndoFetch(&context);
+
+		/*
+		 * Since the rollback isn't completed for this transaction, this undo
+		 * record can't be discarded.
+		 */
+		Assert (uur != NULL);
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * If this is the first undo record of this transaction, return from
+		 * here.
+		 */
+		if ((uur->uur_info & UREC_INFO_LOGSWITCH) == 0)
+		{
+			UndoRecordRelease(uur);
+			break;
+		}
+
+		/*
+		 * This is a start of a overflowed transaction header, so it must have
+		 * a valid pointer to previous log's start transaction header.
+		 */
+		Assert(UndoRecPtrIsValid(uur->uur_logswitch->urec_prevlogstart));
+
+
+		/*
+		 * Find the previous log from which the transaction is overflowed
+		 * to current log.
+		 */
+		urecptr = uur->uur_logswitch->urec_prevlogstart;
+		slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		/*
+		 * When a transaction overflows to a new undo log, it's guaranteed
+		 * that this transaction will be the last transaction in the previous
+		 * log and we mark that log as full so that no other transaction can
+		 * write in that log further.  Check UndoLogAllocate for details.
+		 *
+		 * So, to find the undo size in the previous log, we've to find the
+		 * next insert location of the previous log and subtract current
+		 * transaction's start location in the previous log from it.
+		 */
+		next_insert = UndoLogGetNextInsertPtr(slot->logno);
+		Assert(UndoRecPtrIsValid(next_insert));
+
+		sz += (next_insert - urecptr);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	*start_urecptr_inout = urecptr;
+
+	return sz;
+}
+
 /*
  * Returns true, if we can push the rollback request to undo wrokers, false,
  * otherwise.
@@ -943,9 +1077,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 
 	/*
 	 * We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
 	 */
-	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
 	{
 		if (GetXidQueueSize() >= pending_undo_queue_size ||
 			GetSizeQueueSize() >= pending_undo_queue_size)
@@ -971,12 +1120,7 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 		if ((GetXidQueueSize() < pending_undo_queue_size))
 		{
 			Assert(GetSizeQueueSize() < pending_undo_queue_size);
-
-			/*
-			 * XXX - Here, we should return true once we have background
-			 * worker facility.
-			 */
-			return false;
+			return true;
 		}
 	}
 
@@ -1416,6 +1560,12 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	Assert(UndoRecPtrIsValid(start_urec_ptr));
 	Assert(dbid != InvalidOid);
 
+	/*
+	 * The discard worker can only send the start undo record pointer of a
+	 * transaction.  It doesn't set the end_urec_ptr.
+	 */
+	Assert(IsDiscardProcess() || UndoRecPtrIsValid(end_urec_ptr));
+
 	/*
 	 * Find the rollback request size and the end_urec_ptr (in case of discard
 	 * worker only).
@@ -1431,6 +1581,14 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	if (!UndoRecPtrIsValid(end_urec_ptr))
 		return false;
 
+	/*
+	 * For registering a rollback request, we always store the full transaction
+	 * ID and the first undo record pointer inserted by this transaction.  This
+	 * ensures that backends and discard worker don't register the same request
+	 * twice.
+	 */
+	req_size += FindUndoStartLocationAndSize(&start_urec_ptr, full_xid);
+
 	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
 
 	/*
@@ -1446,7 +1604,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 										   HASH_ENTER_NULL, &found);
 
 	/*
-	 * It can only fail, if the value of pending_undo_queue_size or
+	 * Except the first pass over the undo logs by discard worker, the hash
+	 * table can never be full.
+	 */
+	Assert(!ProcGlobal->rollbackHTInitialized || (rh != NULL));
+
+	/*
+	 * It can only fail, if  the value of pending_undo_queue_size or
 	 * max_connections guc is reduced after restart of the server.
 	 */
 	if (rh == NULL)
@@ -1494,10 +1658,16 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 		}
 		/*
 		 * The request can't be pushed into the undo worker queue.  The
-		 * backends will try executing by itself.
+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with
+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
 		 */
-		else
+		else if (!IsDiscardProcess())
 			rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
 	}
 	else if (!UndoRequestIsValid(rh) && can_push)
 	{
@@ -1525,6 +1695,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 
 	LWLockRelease(RollbackRequestLock);
 
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
 	return pushed;
 }
 
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000000..4f6927b4ac
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,828 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then start reading from one of the queue the requests for
+ * that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+
+#include "libpq/pqsignal.h"
+
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+
+#include "tcop/tcopprot.h"
+
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 4;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.  This has to be more than UNDO_FAILURE_RETRY_DELAY_MS,
+ * otherwise, worker can exit before retrying the failed requests.
+ */
+#define UNDO_WORKER_LINGER_MS 20000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t got_SIGTERM = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker *worker);
+static void UndoWorkerIsLingering(bool sleep);
+static void UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo);
+static void UndoworkerSigtermHandler(SIGNAL_ARGS);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+UndoworkerSigtermHandler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty, cannot attach",
+						slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is already used by "
+						"another worker, cannot attach", slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible otherwise return false.
+ */
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			i;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock so that
+	 * we have consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			slot = i;
+			break;
+		}
+	}
+
+	/* We must not try to start a worker if there are no available workers. */
+	Assert(worker != NULL);
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo.dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo.undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		return false;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return true;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker * worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.  If we fail to add the request in
+		 * an error queue, then mark the entry status invalid.  This request
+		 * will be later added back to the queue by the discard worker.
+		 */
+		if (!InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTMarkEntryInvalid(urinfo->full_xid,
+									   urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of LogicalRepWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			IsUndoWorkerAvailable())
+			UndoWorkerLaunch(urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+
+	/* Normal exit from undo launcher main */
+	ereport(LOG,
+			(errmsg("undo launcher shutting down")));
+	proc_exit(0);
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the wroker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetSlotInfo(worker_slot, &urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker start
+	 * looking for a work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	while (!got_SIGTERM)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		CHECK_FOR_INTERRUPTS();
+
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, requester can
+			 * wake us up.
+			 */
+			UndoWorkerIsLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerIsLingering(false);
+
+			/* emergency bailout if postmaster has died */
+			if (rc & WL_POSTMASTER_DEATH)
+				proc_exit(1);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	/* Normal exit from undo worker main */
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0c0ddd58c5..e774c55694 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14547,6 +14548,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index b66b517aca..b3db6fbf9d 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d8dc0cc547..4c906f6efa 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3339804be9..6521efa09e 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher = true;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c324925c..56b5d087b0 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 19e4f1f0c4..1f650561cf 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -51,3 +51,4 @@ LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
 RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 884fa2af52..61406cff8f 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -297,7 +297,9 @@ InitProcGlobal(void)
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
 
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo, 0);
 	ProcGlobal->xactsHavingPendingUndo = 0;
+	ProcGlobal->rollbackHTInitialized = false;
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a7d1db5249..8b4aa0cdab 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1955,6 +1956,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		 },
+		 &enable_undo_launcher,
+		 true,
+		 NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3030,6 +3042,16 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"max_undo_workers", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
+			gettext_noop("Maximum number of undo worker processes."),
+			NULL,
+		},
+		&max_undo_workers,
+		4, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 592f6e1b4a..e86507c089 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -612,6 +612,7 @@
 					# requests are pushed to undo workers
 #pending_undo_queue_size = 1024	# size of queue used to register undo
 					# requests
+#max_undo_workers = 4	# maximum undo workers
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000000..5b065bf11b
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7796f7248c..e68e74342a 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -73,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000000..282afc04e1
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+extern void UndoLogProcess(void);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 7ec4cb0ec1..b19b931416 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -279,6 +279,19 @@ typedef struct UndoLogAllocContext
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogSlot
 {
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000000..1bdc31fac8
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+extern int max_undo_workers;
+
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e91a..babcc6b1dc 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,15 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest full transaction id which is having unapplied undo.  We include
+	 * this value in the checkpoint record so that whenever server re-starts
+	 * we can use this to initialize the server-wide value for same variable.
+	 * Any Xid prior to this should be all-visible, so if this is not set,
+	 * then the scans might try to fetch undo which can suck the performance.
+	 */
+	FullTransactionId		oldestFullXidHavingUnappliedUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 860a84de7c..cca575bb3b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2fff6734fc..f9dc0bb979 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -785,7 +785,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b692d8be11..b9af96deee 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,7 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344aec..b220a051cc 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -223,6 +223,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
 	LWTRANCHE_REWIND,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 824f6bf232..bcee3e1b04 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,8 +272,12 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUnappliedUndo;
 	/* Number of aborted transactions with pending undo actions. */
 	int			xactsHavingPendingUndo;
+	/* Whether the rollback hash table is initialized after the startup? */
+	bool		rollbackHTInitialized;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672096..1d12994d7d 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb905..6034c5e41a 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
2.16.2.windows.1

#194Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#150)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 10:00 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 22, 2019 at 3:51 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Please find my review comments for
0013-Allow-foreground-transactions-to-perform-undo-action

+ /* initialize undo record locations for the transaction */
+ for (i = 0; i < UndoLogCategories; i++)
+ {
+ s->start_urec_ptr[i] = InvalidUndoRecPtr;
+ s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+ s->undo_req_pushed[i] = false;
+ }

Can't we just memset this memory?

Yeah, that sounds better, so changed.

+ * We can't postpone applying undo actions for subtransactions as the
+ * modifications made by aborted subtransaction must not be visible even if
+ * the main transaction commits.
+ */
+ if (IsSubTransaction())
+ return;

I am not completely sure but is it possible that the outer function
CommitTransactionCommand/AbortCurrentTransaction can avoid
calling this function in the switch case based on the current state,
so that under subtransaction this will never be called?

I have already explained as a separate response to this email why I
don't think this is a very good idea.

+ /*
+ * Prepare required undo request info so that it can be used in
+ * exception.
+ */
+ ResetUndoRequestInfo(&urinfo);
+ urinfo.dbid = dbid;
+ urinfo.full_xid = fxid;
+ urinfo.start_urec_ptr = start_urec_ptr[per_level];
+

I see that we are preparing urinfo before execute_undo_actions so that
in case of an error in CATCH we can use that to
insert into the queue, but can we just initialize urinfo right there
before inserting into the queue, we have all the information
Am I missing something?

IIRC, the only idea was that we can use the same variable
(urinfo.full_xid) in execute_undo_actions call and in the catch block,
but I think your suggestion sounds better as we can avoid declaring
urinfo as volatile in that case.

+
+ /*
+ * We need the locations of the start and end undo record pointers when
+ * rollbacks are to be performed for prepared transactions using undo-based
+ * relations.  We need to store this information in the file as the user
+ * might rollback the prepared transaction after recovery and for that we
+ * need it's start and end undo locations.
+ */
+ UndoRecPtr start_urec_ptr[UndoLogCategories];
+ UndoRecPtr end_urec_ptr[UndoLogCategories];

it's -> its

..

We must have some comments to explain how performUndoActions is used,
where it's set. If it's explained somewhere else then we can
give reference to that code.

+ for (i = 0; i < UndoLogCategories; i++)
+ {
+ if (s->latest_urec_ptr[i])
+ {
+ s->performUndoActions = true;
+ break;
+ }
+ }

I think we should chek UndoRecPtrIsValid(s->latest_urec_ptr[i])

Changed as per suggestion.

+ PG_TRY();
+ {
+ /*
+ * Prepare required undo request info so that it can be used in
+ * exception.
+ */
+ ResetUndoRequestInfo(&urinfo);
+ urinfo.dbid = dbid;
+ urinfo.full_xid = fxid;
+ urinfo.start_urec_ptr = start_urec_ptr[per_level];
+
+ /* for subtransactions, we do partial rollback. */
+ execute_undo_actions(urinfo.full_xid,
+ end_urec_ptr[per_level],
+ start_urec_ptr[per_level],
+ !isSubTrans);
+ }
+ PG_CATCH();

Wouldn't it be good to explain in comments that we are not rethrowing
the error in PG_CATCH but because we don't want the main
transaction to get an error if there is an error while applying to
undo action for the main transaction and we will abort the transaction
in the caller of this function?

I have added a comment atop of the function containing this code.

+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.

I think we need to give detail reasoning why subtransaction changes
will be visible if we don't apply it's undo and the main
the transaction commits by mentioning that we don't use separate
transaction id for the subtransaction and that will make all the
changes of the transaction id visible when it commits.

I have added a detailed explanation in execute_undo_actions() and
given a reference of same here.

The changes are present in the patch series just posted by me [1]/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com.

[1]: /messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#195Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#192)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 31, 2019 at 10:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 30, 2019 at 5:26 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Hi Amit

I've been testing some undo worker workloads (more on that soon),

One small point, there is one small bug in the error queues which is
that the element pushed into error queue doesn't have an updated value
of to_urec_ptr which is important to construct the hash key. This
will lead to undolauncher/worker think that the action for the same is
already processed and it removes the same from the hash table. I have
a fix for the same which I will share in next version of the patch
(which I am going to share in the next day or two).

but
here's a small thing: I managed to reach an LWLock self-deadlock in
the undo worker launcher:

I could see the problem, will fix in next version.

Fixed both of these problems in the patch just posted by me [1]/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com.

[1]: /messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#196Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#143)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 3:51 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 22, 2019 at 2:21 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

I have reviewed 0012-Infrastructure-to-execute-pending-undo-actions,
Please find my comment so far.

..

4.
+void
+undoaction_redo(XLogReaderState *record)
+{
+ uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+ switch (info)
+ {
+ case XLOG_UNDO_APPLY_PROGRESS:
+ undo_xlog_apply_progress(record);
+ break;

For HotStandby it doesn't make sense to apply this wal as this
progress is only required when we try to apply the undo action after
restart
but in HotStandby we never apply undo actions.

Hmm, I think it is required. Think what if Hotstandby is later
promoted to master and a large part of undo is already applied? In
such a case, we can skip the already applied undo.

6.
+ if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+ slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+ Assert(slot != NULL);
We are passing missing_ok as false in UndoLogGetSlot.  But, not sure
why we are expecting that undo lot can not be dropped.  In multi-log
transaction it's possible
that the tablespace in which next undolog is there is already dropped?

If the transaction spans multiple logs, then both the logs should be
in the same tablespace. So, how is it possible to drop the tablespace
when part of undo is still pending? AFAICS, the code in
choose_undo_tablespace() doesn't seem to allow switching tablespace
for the same transaction, but I agree if someone used a different
algorithm, then it might be possible.

I think the important question is whether we should allow the same
transactions undo to span across tablespaces? If so, then what you are
telling makes sense and we should handle that, if not, then I think we
are fine here. One might argue that there should be some more strong
checks to ensure that the same transaction will always get the undo
logs from the same tablespace, but I think that is a different thing
then what you are raising here.

Thomas, others, do you have any opinion on this matter?

In FindUndoEndLocationAndSize, there is a check if the next log is
discarded (Case 4: If the transaction is overflowed to ...), won't
this case (considering it is possible) get detected by that check?

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#197Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Amit Kapila (#193)
Re: POC: Cleaning up orphaned files using undo logs

I had a look at the UNDO patches at
/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com,
and at the patch to use the UNDO logs to clean up orphaned files, from
undo-2019-05-10.tgz earlier in this thread. Are these the latest ones to
review?

Thanks Thomas and Amit and others for working on this! Orphaned relfiles
has been an ugly wart forever. It's a small thing, but really nice to
fix that finally. This has been a long thread, and I haven't read it
all, so please forgive me if I repeat stuff that's already been discussed.

There are similar issues in CREATE/DROP DATABASE code. If you crash in
the middle of CREATE DATABASE, you can be left with orphaned files in
the data directory, or if you crash in the middle of DROP DATABASE, the
data might be gone already but the pg_database entry is still there. We
should plug those holes too.

There's a lot of stuff in the patches that are not relevant for cleaning
up orphaned files. I know this cleaning up orphaned files work is mainly
a vehicle to get the UNDO log committed, so that's expected. If we only
cared about orphaned files, I'm sure the patches wouldn't spend so much
effort on concurrency, for example. Nevertheless, I think we should
leave out some stuff that's clearly unused, for now. For example, a
bunch of fields in the record format: uur_block, uur_offset, uur_tuple.
You can add them later, as part of the patches that actually need them,
but for now they just make the patch larger to review.

Some more thoughts on the record format:

I feel that the level of abstraction is not quite right. There are a
bunch of fields, like uur_block, uur_offset, uur_tuple, that are
probably useful for some UNDO resource managers (zheap I presume), but
seem kind of arbitrary. How is uur_tuple different from uur_payload?
Should they be named more generically as uur_payload1 and uur_payload2?
And why two, why not three or four different payloads? In the WAL record
format, there's a concept of "block id", which allows you to store N
number of different payloads in the record, I think that would be a
better approach. Or only have one payload, and let the resource manager
code divide it as it sees fit.

Many of the fields support a primitive type of compression, where a
field can be omitted if it has the same value as on the first record on
an UNDO page. That's handy. But again I don't like the fact that the
fields have been hard-coded into the UNDO record format. I can see e.g.
the relation oid to be useful for many AMs. But not all. And other AMs
might well want to store and deduplicate other things, aside from the
fields that are in the patch now. I'd like to move most of the fields to
AM specific code, and somehow generalize the compression. One approach
would be to let the AM store an arbitrary struct, and run it through a
general-purpose compression algorithm, using the UNDO page's first
record as the "dictionary". Or make the UNDO page's first record
available in whole to the AM specific code, and let the AM do the
deduplication. For cleaning up orphaned files, though, we don't really
care about any of that, so I'd recommend just ripping it out for now.
Compression/deduplication can be added later as a separate patch.

The orphaned-file cleanup patch doesn't actually use the uur_reloid
field. It stores the RelFileNode instead, in the paylod. I think that's
further evidence that the hard-coded fields in the record format are not
quite right.

I don't like the way UndoFetchRecord returns a palloc'd
UnpackedUndoRecord. I would prefer something similar to the xlogreader
API, where a new call to UndoFetchRecord invalidates the previous
result. On efficiency grounds, to avoid the palloc, but also to be
consistent with xlogreader.

In the UNDO page header, there are a bunch of fields like
pd_lower/pd_upper/pd_special that are copied from the "standard" page
header, that are unused. There's a FIXME comment about that too. Let's
remove them, there's no need for UNDO pages to look like standard
relation pages. The LSN needs to be at the beginning, to work with the
buffer manager, but that's the only requirement.

Could we leave out the UNDO and discard worker processes for now?
Execute all UNDO actions immediately at rollback, and after crash
recovery. That would be fine for cleaning up orphaned files, and it
would cut down the size of the patch to review.

Can this race condition happen: Transaction A creates a table and an
UNDO record to remember it. The transaction is rolled back, and the file
is removed. Another transaction, B, creates a different table, and
chooses the same relfilenode. It loads the table with data, and commits.
Then the system crashes. After crash recovery, the UNDO record for the
first transaction is applied, and it removes the file that belongs to
the second table, created by transaction B.

- Heikki

#198Amit Kapila
amit.kapila16@gmail.com
In reply to: Heikki Linnakangas (#197)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, Aug 4, 2019 at 2:46 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I had a look at the UNDO patches at
/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com,
and at the patch to use the UNDO logs to clean up orphaned files, from
undo-2019-05-10.tgz earlier in this thread. Are these the latest ones to
review?

Yes, I am not sure of cleanup orphan file patch (Thomas can confirm
the same), but others are latest.

Thanks Thomas and Amit and others for working on this! Orphaned relfiles
has been an ugly wart forever. It's a small thing, but really nice to
fix that finally. This has been a long thread, and I haven't read it
all, so please forgive me if I repeat stuff that's already been discussed.

There are similar issues in CREATE/DROP DATABASE code. If you crash in
the middle of CREATE DATABASE, you can be left with orphaned files in
the data directory, or if you crash in the middle of DROP DATABASE, the
data might be gone already but the pg_database entry is still there. We
should plug those holes too.

+1. Interesting.

There's a lot of stuff in the patches that are not relevant for cleaning
up orphaned files. I know this cleaning up orphaned files work is mainly
a vehicle to get the UNDO log committed, so that's expected. If we only
cared about orphaned files, I'm sure the patches wouldn't spend so much
effort on concurrency, for example. Nevertheless, I think we should
leave out some stuff that's clearly unused, for now. For example, a
bunch of fields in the record format: uur_block, uur_offset, uur_tuple.
You can add them later, as part of the patches that actually need them,
but for now they just make the patch larger to review.

Some more thoughts on the record format:

I feel that the level of abstraction is not quite right. There are a
bunch of fields, like uur_block, uur_offset, uur_tuple, that are
probably useful for some UNDO resource managers (zheap I presume), but
seem kind of arbitrary. How is uur_tuple different from uur_payload?

The uur_tuple field can only store tuple whereas uur_payload can have
miscellaneous information. For ex. in zheap, we store transaction
information like CID, CTID, some information related to TPD, etc. in
the payload. Basically, I think eventually payload will have some
bitmap to indicate what all is stored in it. OTOH, I agree that if we
want we can store tuple as well in the payload.

Should they be named more generically as uur_payload1 and uur_payload2?
And why two, why not three or four different payloads? In the WAL record
format, there's a concept of "block id", which allows you to store N
number of different payloads in the record, I think that would be a
better approach. Or only have one payload, and let the resource manager
code divide it as it sees fit.

For payload, something like what you describe here sounds like a good
idea, but I feel we can have tuple as a separate field. It will help
in accessing tuple quickly and easily during visibility or rollbacks
for some AM's like zheap.

Many of the fields support a primitive type of compression, where a
field can be omitted if it has the same value as on the first record on
an UNDO page. That's handy. But again I don't like the fact that the
fields have been hard-coded into the UNDO record format. I can see e.g.
the relation oid to be useful for many AMs. But not all. And other AMs
might well want to store and deduplicate other things, aside from the
fields that are in the patch now. I'd like to move most of the fields to
AM specific code, and somehow generalize the compression. One approach
would be to let the AM store an arbitrary struct, and run it through a
general-purpose compression algorithm, using the UNDO page's first
record as the "dictionary". Or make the UNDO page's first record
available in whole to the AM specific code, and let the AM do the
deduplication. For cleaning up orphaned files, though, we don't really
care about any of that, so I'd recommend just ripping it out for now.
Compression/deduplication can be added later as a separate patch.

I think this will make the undorecord-interface patch a bit simpler as well.

The orphaned-file cleanup patch doesn't actually use the uur_reloid
field. It stores the RelFileNode instead, in the paylod. I think that's
further evidence that the hard-coded fields in the record format are not
quite right.

I don't like the way UndoFetchRecord returns a palloc'd
UnpackedUndoRecord. I would prefer something similar to the xlogreader
API, where a new call to UndoFetchRecord invalidates the previous
result. On efficiency grounds, to avoid the palloc, but also to be
consistent with xlogreader.

In the UNDO page header, there are a bunch of fields like
pd_lower/pd_upper/pd_special that are copied from the "standard" page
header, that are unused. There's a FIXME comment about that too. Let's
remove them, there's no need for UNDO pages to look like standard
relation pages. The LSN needs to be at the beginning, to work with the
buffer manager, but that's the only requirement.

Could we leave out the UNDO and discard worker processes for now?
Execute all UNDO actions immediately at rollback, and after crash
recovery. That would be fine for cleaning up orphaned files,

Even if we execute all the undo actions on rollback, we need discard
worker to discard undo at regular intervals. Also, what if we get an
error while applying undo actions during rollback? Right now, we have
a mechanism to push such a request to background worker and allow the
session to continue. Instead, we might want to Panic in such cases if
we don't want to have background undo workers.

and it
would cut down the size of the patch to review.

If we can find some way to handle all cases and everyone agrees to it,
that would be good. In fact, we can try to get the basic stuff
committed first and then try to get the rest (undo-worker machinery)
done.

Can this race condition happen: Transaction A creates a table and an
UNDO record to remember it. The transaction is rolled back, and the file
is removed. Another transaction, B, creates a different table, and
chooses the same relfilenode. It loads the table with data, and commits.
Then the system crashes. After crash recovery, the UNDO record for the
first transaction is applied, and it removes the file that belongs to
the second table, created by transaction B.

I don't think such a race exists, but we should verify it once.
Basically, once the rollback is complete, we mark the transaction
rollback as complete in the transaction header in undo and write a WAL
for it. After crash-recovery, we will skip such a transaction. Isn't
that sufficient to prevent such a race condition?

Thank you for looking into this work.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#199Thomas Munro
thomas.munro@gmail.com
In reply to: Amit Kapila (#198)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 3:54 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Sun, Aug 4, 2019 at 2:46 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I had a look at the UNDO patches at
/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com,
and at the patch to use the UNDO logs to clean up orphaned files, from
undo-2019-05-10.tgz earlier in this thread. Are these the latest ones to
review?

Yes, I am not sure of cleanup orphan file patch (Thomas can confirm
the same), but others are latest.

I have a new patch set to post soon, handling all the feedback that
arrived in the past couple of weeks from 5 different reviewers (thanks
all!).

There are similar issues in CREATE/DROP DATABASE code. If you crash in
the middle of CREATE DATABASE, you can be left with orphaned files in
the data directory, or if you crash in the middle of DROP DATABASE, the
data might be gone already but the pg_database entry is still there. We
should plug those holes too.

+1. Interesting.

Huh. Right.

Could we leave out the UNDO and discard worker processes for now?
Execute all UNDO actions immediately at rollback, and after crash
recovery. That would be fine for cleaning up orphaned files,

Even if we execute all the undo actions on rollback, we need discard
worker to discard undo at regular intervals. Also, what if we get an
error while applying undo actions during rollback? Right now, we have
a mechanism to push such a request to background worker and allow the
session to continue. Instead, we might want to Panic in such cases if
we don't want to have background undo workers.

and it
would cut down the size of the patch to review.

If we can find some way to handle all cases and everyone agrees to it,
that would be good. In fact, we can try to get the basic stuff
committed first and then try to get the rest (undo-worker machinery)
done.

I think it's definitely worth exploring.

Can this race condition happen: Transaction A creates a table and an
UNDO record to remember it. The transaction is rolled back, and the file
is removed. Another transaction, B, creates a different table, and
chooses the same relfilenode. It loads the table with data, and commits.
Then the system crashes. After crash recovery, the UNDO record for the
first transaction is applied, and it removes the file that belongs to
the second table, created by transaction B.

I don't think such a race exists, but we should verify it once.
Basically, once the rollback is complete, we mark the transaction
rollback as complete in the transaction header in undo and write a WAL
for it. After crash-recovery, we will skip such a transaction. Isn't
that sufficient to prevent such a race condition?

The usual protection against relfilenode recycling applies: we don't
actually remove the files on disk until after the next checkpoint,
following the successful rollback. That is, the executing the
rollback doesn't actually remove any files immediately, so you can't
reuse the OID yet.

There might be some problems like that if we tried to handle the
CREATE DATABASE orphans you mentioned too naively though. Not sure.

Thank you for looking into this work.

+1

--
Thomas Munro
https://enterprisedb.com

#200Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Thomas Munro (#199)
Re: POC: Cleaning up orphaned files using undo logs

On 05/08/2019 07:23, Thomas Munro wrote:

On Mon, Aug 5, 2019 at 3:54 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Sun, Aug 4, 2019 at 2:46 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Could we leave out the UNDO and discard worker processes for now?
Execute all UNDO actions immediately at rollback, and after crash
recovery. That would be fine for cleaning up orphaned files,

Even if we execute all the undo actions on rollback, we need discard
worker to discard undo at regular intervals. Also, what if we get an
error while applying undo actions during rollback? Right now, we have
a mechanism to push such a request to background worker and allow the
session to continue. Instead, we might want to Panic in such cases if
we don't want to have background undo workers.

and it
would cut down the size of the patch to review.

If we can find some way to handle all cases and everyone agrees to it,
that would be good. In fact, we can try to get the basic stuff
committed first and then try to get the rest (undo-worker machinery)
done.

I think it's definitely worth exploring.

Yeah. For cleaning up orphaned files, if unlink() fails, we can just log
the error and move on. That's what we do in the main codepath, too. For
any other error, PANIC seems ok. We're not expecting any errors during
undo processing, so it doesn't seems safe to continue running.

Hmm. Since applying the undo record is WAL-logged, you could run out of
disk space while creating the WAL record. That seems unpleasant.

Can this race condition happen: Transaction A creates a table and an
UNDO record to remember it. The transaction is rolled back, and the file
is removed. Another transaction, B, creates a different table, and
chooses the same relfilenode. It loads the table with data, and commits.
Then the system crashes. After crash recovery, the UNDO record for the
first transaction is applied, and it removes the file that belongs to
the second table, created by transaction B.

I don't think such a race exists, but we should verify it once.
Basically, once the rollback is complete, we mark the transaction
rollback as complete in the transaction header in undo and write a WAL
for it. After crash-recovery, we will skip such a transaction. Isn't
that sufficient to prevent such a race condition?

Ok, I didn't realize there's a flag in the undo record to mark it as
applied. Yeah, that fixes it. Seems a bit heavy-weight, but I guess it's
fine. Do you do something different in zheap? I presume writing a WAL
record for every applied undo record would be too heavy there.

This needs some performance testing. We're creating one extra WAL record
and one UNDO record for every file creation, and another WAL record on
abort. It's probably cheap compared to all the other work done during
table creation, but we should still get some numbers on it.

Some regression tests would be nice too.

- Heikki

#201Amit Kapila
amit.kapila16@gmail.com
In reply to: Heikki Linnakangas (#200)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 12:09 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 05/08/2019 07:23, Thomas Munro wrote:

On Mon, Aug 5, 2019 at 3:54 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Sun, Aug 4, 2019 at 2:46 PM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Could we leave out the UNDO and discard worker processes for now?
Execute all UNDO actions immediately at rollback, and after crash
recovery. That would be fine for cleaning up orphaned files,

Even if we execute all the undo actions on rollback, we need discard
worker to discard undo at regular intervals. Also, what if we get an
error while applying undo actions during rollback? Right now, we have
a mechanism to push such a request to background worker and allow the
session to continue. Instead, we might want to Panic in such cases if
we don't want to have background undo workers.

and it
would cut down the size of the patch to review.

If we can find some way to handle all cases and everyone agrees to it,
that would be good. In fact, we can try to get the basic stuff
committed first and then try to get the rest (undo-worker machinery)
done.

I think it's definitely worth exploring.

Yeah. For cleaning up orphaned files, if unlink() fails, we can just log
the error and move on. That's what we do in the main codepath, too. For
any other error, PANIC seems ok. We're not expecting any errors during
undo processing, so it doesn't seems safe to continue running.

Hmm. Since applying the undo record is WAL-logged, you could run out of
disk space while creating the WAL record. That seems unpleasant.

We might get away by doing some minimum error handling for orphan file
cleanup patch, but this facility was supposed to be a generic
facility. Assuming, all of us agree on error handling stuff, still, I
think we might not be able to get away with the requirement for
discard worker to discard the logs.

Can this race condition happen: Transaction A creates a table and an
UNDO record to remember it. The transaction is rolled back, and the file
is removed. Another transaction, B, creates a different table, and
chooses the same relfilenode. It loads the table with data, and commits.
Then the system crashes. After crash recovery, the UNDO record for the
first transaction is applied, and it removes the file that belongs to
the second table, created by transaction B.

I don't think such a race exists, but we should verify it once.
Basically, once the rollback is complete, we mark the transaction
rollback as complete in the transaction header in undo and write a WAL
for it. After crash-recovery, we will skip such a transaction. Isn't
that sufficient to prevent such a race condition?

Ok, I didn't realize there's a flag in the undo record to mark it as
applied. Yeah, that fixes it. Seems a bit heavy-weight, but I guess it's
fine. Do you do something different in zheap? I presume writing a WAL
record for every applied undo record would be too heavy there.

For zheap, we collect all the records of a page, apply them together
and then write the entire page in WAL. The progress of transaction is
updated at either transaction end (rollback complete) or after
processing some threshold of undo records. So, generally, the WAL
won't be for each undo record apply.

This needs some performance testing. We're creating one extra WAL record
and one UNDO record for every file creation, and another WAL record on
abort. It's probably cheap compared to all the other work done during
table creation, but we should still get some numbers on it.

makes sense.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#202Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#201)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 6:16 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

For zheap, we collect all the records of a page, apply them together
and then write the entire page in WAL. The progress of transaction is
updated at either transaction end (rollback complete) or after
processing some threshold of undo records. So, generally, the WAL
won't be for each undo record apply.

This explanation omits a crucial piece of the mechanism, because
Heikki is asking what keeps the undo from being applied multiple
times. When we apply the undo records to a page, we also adjust the
undo pointers in the page. Since we have an undo pointer per
transaction slot, and each transaction has its own slot, if we apply
all the undo for a transaction to a page, we can just clear the slot;
if we somehow end up back at the same point later, we'll know not to
apply the undo a second time because we'll see that there's no
transaction slot pointing to the undo we were thinking of applying. If
we roll back to a savepoint, or for some other reason choose to apply
only some of the undo to a page, we can set the undo record pointer
for the transaction back to the value it had before we generated any
newer undo. Then, we'll know that the newer undo doesn't need to be
applied but the older undo can be applied.

At least, I think that's how it's supposed to work. If you just
update the progress field, it doesn't guarantee anything, because in
the event of a crash, we could end up keeping the page changes but
losing the update to the progress, as they are part of separate undo
records.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#203Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#197)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, Aug 4, 2019 at 5:16 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I feel that the level of abstraction is not quite right. There are a
bunch of fields, like uur_block, uur_offset, uur_tuple, that are
probably useful for some UNDO resource managers (zheap I presume), but
seem kind of arbitrary. How is uur_tuple different from uur_payload?
Should they be named more generically as uur_payload1 and uur_payload2?
And why two, why not three or four different payloads? In the WAL record
format, there's a concept of "block id", which allows you to store N
number of different payloads in the record, I think that would be a
better approach. Or only have one payload, and let the resource manager
code divide it as it sees fit.

Many of the fields support a primitive type of compression, where a
field can be omitted if it has the same value as on the first record on
an UNDO page. That's handy. But again I don't like the fact that the
fields have been hard-coded into the UNDO record format. I can see e.g.
the relation oid to be useful for many AMs. But not all. And other AMs
might well want to store and deduplicate other things, aside from the
fields that are in the patch now. I'd like to move most of the fields to
AM specific code, and somehow generalize the compression. One approach
would be to let the AM store an arbitrary struct, and run it through a
general-purpose compression algorithm, using the UNDO page's first
record as the "dictionary".

I thought about this, too. I agree that there's something a little
unsatisfying about the current structure, but I haven't been able to
come up with something that seems definitively better. I think
something along the lines of what you are describing here might work
well, but I am VERY doubtful about the idea of a fixed-size struct. I
think AMs are going to want to store variable-length data: especially
tuples, but maybe also other stuff. For instance, imagine some AM that
wants to implement locking that's more fine-grained that the four
levels of tuple locks we have today: instead of just having key locks
and all-columns locks, you could want to store the exact columns to be
locked. Or maybe your TIDs are variable-width.

And the problem is that as soon as you move to something where you
pack in a bunch of variable-sized fields, you lose the ability to
refer to thinks using reasonable names. That's where I came up with
the idea of an UnpackedUndoRecord: give the common fields that
"everyone's going to need" human-readable names, and jam only the
strange, AM-specific stuff into the payload. But if those needs are
not actually universal but very much AM-specific, then I'm afraid
we're going to end up with deeply inscrutable code for packing and
unpacking records. I imagine it's possible to come up with a good
structure for that, but I don't think we have one today.

I don't like the way UndoFetchRecord returns a palloc'd
UnpackedUndoRecord. I would prefer something similar to the xlogreader
API, where a new call to UndoFetchRecord invalidates the previous
result. On efficiency grounds, to avoid the palloc, but also to be
consistent with xlogreader.

I don't think that's going to work very well, because we often need to
deal with multiple records at a time. There is (or was) a bulk-fetch
interface, but I've also found while experimenting with this code that
it can be useful to do things like:

current = undo_fetch(starting_record);
loop:
next = undo_fetch(current->next_record_ptr);
if some_test(next):
break;
undo_free(current);
current = next;

I think we shouldn't view such cases as exceptions to the general
paradigm of looking at undo records one at a time, but instead as the
normal case for which everything is optimized. Cases like orphaned
file cleanup where the number of undo records is probably small and
they're all independent of each other will, I think, turn out to be
the exception rather than the rule.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#204Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#188)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 4:02 AM Andres Freund <andres@anarazel.de> wrote:

I'm a bit worried about expanding the use of
ReadBufferWithoutRelcache(). Not so much because of the relcache itself,
but because it requires doing separate smgropen() calls. While not
crazily expensive, it's also not free. Especially combined with closing
all such relations at transaction end (c.f. AtEOXact_SMgr).

I'm somewhat inclined to think that this requires a slightly bigger
refactoring than done in this patch. Imo at the very least the smgr
entries ought not to be unowned. But working towards not haven to
re-open the smgr entry for every single trival request ought to be part
of this too.

I spent some time trying to analyze this today and I agree with you
that there seems to be room for improvement here. When I first looked
at your comments, I wasn't too convinced, because access patterns that
skip around between undo logs seem like they may be fairly common.
Admittedly, there are cases where we want to read from just one undo
log over and over again, and it would be good to optimize those, but I
was initially a bit unconvinced that that there was a problem here
worth being concerned about. Then I realized that you would also
repeat the smgropen() if you read a single record that happens to be
split across two pages, which seems a little silly.

But then I realized that we're being a little silly even in the case
where we're reading a single undo record that is stored entirely on a
single page. We are certainly going to need to look up the undo log,
but as things stand, we'll basically do it twice. For example, in the
write path, we'll call UndoLogAllocate() and it will look up an
UndoLogControl object for the undo log of interest, and then we'll
call ReadBufferWithoutRelcache() which will call smgropen() which will
do a hash table lookup to find the SMgrRelation associated with that
undo log. That's not a large cost, as you say, but it does seem like
it might be better to avoid having two different lookups in the same
commonly-used code path, each of which peeks into a different
backend-private data structure for information about the very same
undo log.

The obvious thing to do seems to be to have UndoLogControl objects own
SmgrRelations. That would be something of a novelty, since it looks
like currently only a Relation ever owns an SMgrRelation, but the smgr
infrastructure seems to have been set up in a generic way so as to
permit that sort of thing, so it seems like it should be workable.
Perhaps UndoLogAllocate() function could return a pointer to the
UndoLogControl object as well as UndoRecPtr. Then, there could be a
function UndoLogWrite(UndoLogControl *, UndoRecPtr, char *, Size). On
the read side, instead of calling UndoRecPtrAssignRelFileNode, maybe
the undo log storage layer should provide a function that again
returns an UndoLogControl, and then we could have a matching function
UndoLogRead(UndoLogControl *, UndoRecPtr, char *, Size).

I think this kind of design would address your concerns about using
the unowned list, too, since the UndoLogControl objects would be
owning the SMgrRelations. It took me a while to understand why you
were concerned about using the unowned list, so I'm going to repeat it
in my own words to make sure I've got it right, and also to possibly
help out anyone else who may also have had difficulty grokking your
concern. If we have a bunch of short transactions each of which
accesses the same relation, the relcache entry will remain open and
the file won't get closed in between, but if we have a bunch of short
transactions each of which accesses the same undo log, the undo log
will be closed and reopened at the operating system level for each
individual transaction. That happens because when an SMgrRelation is
"owned," the owner takes care of closing it, and so can keep it open
across transactions, but when it's "unowned," it's automatically
closed during transaction cleanup. And we should fix it, because
closing and reopening the same file for every transaction
unnecessarily might be expensive enough to matter, at least a little
bit.

How does all that sound?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#205Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#204)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-05 11:25:10 -0400, Robert Haas wrote:

The obvious thing to do seems to be to have UndoLogControl objects own
SmgrRelations. That would be something of a novelty, since it looks
like currently only a Relation ever owns an SMgrRelation, but the smgr
infrastructure seems to have been set up in a generic way so as to
permit that sort of thing, so it seems like it should be workable.

Yea, I think that'd be a good step.

I'm not 100% convinced it's quite enough, due to the way the undo smgr
only ever has a single file descriptor open, and that undo log segments
are fairly small, and that there'll often be multiple persistence levels
active at the same time. But the undo fd handling is probably a separate
concern than from who owns the smgr relations.

I think this kind of design would address your concerns about using
the unowned list, too, since the UndoLogControl objects would be
owning the SMgrRelations.

Yup.

How does all that sound?

A good move in the right direction, imo.

Greetings,

Andres Freund

#206Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#188)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

(as I was out of context due to dealing with bugs, I've switched to
lookign at the current zheap/undoprocessing branch.

On 2019-07-30 01:02:20 -0700, Andres Freund wrote:

+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

As far as I can tell there's simply no deadlock avoidance scheme in use
here *at all*? I must be missing something.

+		/* Main loop for writing the undo record. */
+		do
+		{

I'd prefer this to not be a do{} while(true) loop - as written I need to
read to the end to see what the condition is. I don't think we have any
loops like that in the code.

+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}

How exactly can this happen?

+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;

At this point we're five indentation levels deep. I'd extract at least
either the the per prepared undo code or the code performing the writing
across block boundaries into a separate function. Perhaps both.

+/*
+ * Helper function for UndoGetOneRecord
+ *
+ * If any of  rmid/reloid/xid/cid is not available in the undo record, then
+ * it will get the information from the first complete undo record in the
+ * page.
+ */
+static void
+GetCommonUndoRecInfo(UndoPackContext *ucontext, UndoRecPtr urp,
+					 RelFileNode rnode, UndoLogCategory category, Buffer buffer)
+{
+	/*
+	 * If any of the common header field is not available in the current undo
+	 * record then we must read it from the first complete record of the page.
+	 */

How is it guaranteed that the first record on the page is actually from
the current transaction? Can't there be a situation where that's from
another transaction?

+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */

Wait, so at exit *curbuf is pinned but not locked, if passed in, but is
pinned *and* locked when not? That'd not be a sane API. I don't think
the code works like that atm though.

+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoLogCategory category, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoLogCategory(category));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+		}
+
+		/* Acquire shared lock on the buffer before reading undo from it. */
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Get any of the missing fields from the first record of the
+			 * page.
+			 */
+			GetCommonUndoRecInfo(&ucontext, urp, rnode, category, *curbuf);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */

Where would it have been copied? Presumably in UnpackUndoData()? Imo the
comment should say so.

I'm a bit confused by the use of "would" in that comment. Either we
have, or not?

+ if (buffer != *curbuf)
+ UnlockReleaseBuffer(buffer);

Wait, so we *keep* the buffer locked if it the same as *curbuf? That
can't be right.

+ * Fetch the undo record for given undo record pointer.
+ *
+ * This will internally allocate the memory for the unpacked undo record which
+ * intern will

"intern" should probably be internally? But I'm not sure what the two
"internally"s really add here.

+/*
+ * Release the memory of the undo record allocated by UndoFetchRecord and
+ * UndoBulkFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	/* Release the memory of payload data if we allocated it. */
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+
+	/* Release memory of tuple data if we allocated it. */
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Release memory of the transaction header if we allocated it. */
+	if (urec->uur_txn)
+		pfree(urec->uur_txn);
+
+	/* Release memory of the logswitch header if we allocated it. */
+	if (urec->uur_logswitch)
+		pfree(urec->uur_logswitch);
+
+	/* Release the memory of the undo record. */
+	pfree(urec);
+}

Those comments before each pfree are not useful.

Also, isn't this both fairly slow and fairly failure prone? The next
record is going to need all that memory again, no? It seems to me that
there should be one record that's allocated once, and then reused over
multiple fetches, increasing the size if necesssary.

I'm very doubtful that all this freeing of individual allocations in the
undo code makes sense. Shouldn't this just be done in short lived memory
contexts, that then get reset as a whole? That's both far less failure
prone, and faster.

+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)

There's no caller for one_page mode in the series - I assume that's for
later, during page-wise undo? It seems to behave in quite noticably
different ways, is that really OK? Makes the code quite hard to
understand.

Also, it seems quite poorly named to me. It sounds like it's about
fetching a single undo page (which makes no sense, obviously). But what
it does is to switch to an entirely different way of traversing the undo
chains.

+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.

What does "interleaved" mean here? I assume that there will often be
other UNDO records interspersed? But that's not guaranteed at all,
right? In fact, for a lot of workloads it seems likely that there will
be many consecutive undo records for a single page? In fact, won't that
be the majority of cases?

Thus it's not obvious to me that there's not often going to be
consecutive pages for this case too. I'd even say that minimizing IO
delay is *MORE* important during page-wise undo, as that happens in the
context of client accesses, and it's not incurring cost on the party
that performed DML, but on some random third party.

I'm doubtful this is a sane interface. There's a lot of duplication
between one_page and not one_page. It presupposes specific ways of
constructing chains that are likely to depend on the AM. to_urecptr is
only used in certain situations. E.g. I strongly suspect that for
zheap's visibility determinations we'd want to concurrently follow all
the necessary chains to determine visibility for all all tuples on the
page, far enough to find the visible tuple - for seqscan's / bitmap heap
scans / everything using page mode scans, that'll be way more efficient
than doing this one-by-one and possibly even repeatedly. But what is
exactly the right thing to do is going to be highly AM specific.

I vaguely suspect what you'd want is an interface where the "bulk fetch"
context basically has a FIFO queue of undo records to fetch, and a
function to actually perform fetching. Whenever a record has been
retrieved, a callback determines whether additional records are needed.
In the case of fetching all the undo for a transaction, you'd just queue
- probably in a more efficient representation - all the necessary
undo. In case of page-wise undo, you'd queue the first record of the
chain you'd want to undo, with a callback for queuing the next
record. For visibility determinations in zheap, you'd queue all the
different necessary chains, with a callback that queues the next
necessary record if still needed for visibility determination.

And then I suspect you'd have a separate callback whenever records have
been fetched, with all the 'unconsumed' records. That then can,
e.g. based on memory consumption, decide to process them or not. For
visibility information you'd probably just want to condense the records
to the minimum necessary (i.e. visibility information for the relevant
tuples, and the visibile tuple when encountered) as soon as available.

Obviously that's pretty handwavy.

Also, if we are fetching undo records from more than one
+	 * log, we don't know the boundaries for prefetching.  Hence, we can't use
+	 * prefetching in this case.
+	 */

Hm. Why don't we know the boundaries (or cheaply infer them)?

+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, category);

Hm. Why aren't we prefetching again as soon as possible? Given the
current code there's not really benefit in fetching many adjacent pages
at once. And this way it seems we're somewhat likely to cause fairly
bursty IO?

+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */

I don't really see a comment explaining this in UndoFetchRecord. Are
you referring to InHotStandby? Because there's no comment about one_page
mode as far as I can tell? The comment is clearly referring to that,
rather than InHotStandby?

+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */

Missing "to".

+			if (InHotStandby)
+			{
+				if (UndoRecPtrIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&slot->discard_lock, LW_SHARED);
+				if (slot->logno != logno || urecptr < slot->oldest_data)
+				{
+					/*
+					 * The undo log slot has been recycled because it was
+					 * entirely discarded, or the data has been discarded
+					 * already.
+					 */
+					LWLockRelease(&slot->discard_lock);
+					break;
+				}
+			}

I find this deeply unsatisfying. It's repeated in a bunch of
places. There's completely different behaviour between the hot-standby
and !hot-standby case. There's UndoRecPtrIsDiscarded for the HS case,
but we do a different test for !HS. There's no explanation as to why
this is even reachable.

+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			if (!InHotStandby)
+				LWLockRelease(&slot->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);

And then we do none of this in !one_page mode.

+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!FullTransactionIdIsValid(fxid))
+				fxid = uur->uur_fxid;
+			else if (!FullTransactionIdEquals(fxid, uur->uur_fxid))
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_prevundo;
+		else if (uur->uur_logswitch)
+			urecptr = uur->uur_logswitch->urec_prevurp;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, category);
+

FWIW, this is one of those concerns I was referring to above. What
exactly needs to happen seems highly AM specific.

+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */

This should add some note as to when it's expected to be necessary. I
was kind of concerned that this can be necessary, but it's only needed
during log switches, which disarms that concern.

+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoLogCategory category)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);

Shouldn't it be byte_to_read? And the sizeof a type that's tied with the
actual undo format? Imagine we'd ever want to change the length format
for undo records - this would be hard to find.

+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoLogCategory(category);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{

Why does this need a loop around the number of bytes? Can there ever be
a case where this is split across a record? If so, isn't that a bad idea
anyway?

+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}

I can't help but think that this shouldn't be yet another copy of logic
for how to read undo pages.

Need to do something else for a bit. More later.

Greetings,

Andres Freund

#207Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#205)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 12:42 PM Andres Freund <andres@anarazel.de> wrote:

A good move in the right direction, imo.

I spent some more time thinking about this and talking to Thomas about
it and I'd like to propose a somewhat more aggressive restructuring
proposal, with the aim of getting a cleaner separation between layers
of this patch set.

Right now, the undo log storage stuff knows nothing about the contents
of an undo log, whereas the undo interface storage knows everything
about the contents of an undo log. In particular, it knows that it's a
series of records, and those records are grouped into transactions,
and it knows both the format of the individual records and also the
details of how transaction headers work. Nothing can use the undo log
storage system except for the undo interface layer, because the undo
interface layer assumes that all the data in the undo storage system
conforms to the record/recordset format which it defines. However,
there are a few warts: while the undo log storage patch doesn't know
anything about the contents of undo logs, it does know that that
transaction boundaries matter, and it signals to the undo interface
layer whether a transaction header should be inserted for a new
record. That's a strange thing for the storage layer to be doing.
Also, in addition to three persistence levels, it knows about a fourth
undo log category for "special" data for multixact or TPD-like things.
That's another wart.

Suppose that we instead invent a new layer which sits on top of the
undo log storage layer. This layer manages what I'm going to call
GHOBs, growable hunks of bytes. (This is probably not the best name,
but I thought of it in 5 seconds during a complex technical
conversation, so bear with me.) The GHOB layer supports
open/close/grow/write/overwrite operations. Conceptually, you open a
GHOB with an initial size and a persistence level, and then you can
subsequently grow it unless you fill up the undo log in which case you
can't grow it any more; when you're done, you close it. Opening and
closing a GHOB are operations that only make in-memory state changes.
Opening a GHOB finds a place where you could write the initial amount
of data you specify, but it doesn't actually write any data or change
any persistent state yet, except for making sure that nobody else can
grab that space as long as you have the GHOB open. Closing a GHOB
tells the system that you're not going to grow the object any more,
which means some other GHOB can be placed immediately after the last
data you wrote. Growing a GHOB doesn't do anything persistent either;
it just tests whether there would be room to write those bytes. So,
the only operations that make actual persistent changes are write and
overwrite. These operations just copy data into shared buffers and
mark them dirty, but they are set up so that you can integrate this
with whatever WAL-logging your doing for those operations, so that you
can make the same writes happen at redo time.

Then, on top of the GHOB layer, you have separate submodules for
different kinds of GHOBs. Most importantly, you have a
transaction-GHOB manager, which opens a GHOB per persistence level the
first time somebody wants to write to it and closes those GHOBs at
end-of-xact. AMs push records into the transaction-GHOB manager, and
it pushes them into GHOBs on the other side. Then you can also have a
multi-GHOB manager, which would replace what Thomas now has as a
separate undo log category. The undo-log-storage layer wouldn't have
any fixed limit on the number of GHOBs that could be open at the same
time; it would just be the sum of whatever the individual GHOB type
managers can open. It would be important to keep that number fairly
small since there's not an unlimited supply of undo logs, but that
doesn't seem like a problem for any of the uses we currently have in
mind. Each GHOB would begin with a magic number identifying the GHOB
type, and would have callbacks for everything else, like "how big is
this GHOB?" and "is it discardable?".

I'm not totally sure I've thought through all of the problems here,
but it seems like this might help us fix some of the aforementioned
layering inversions. The undo log storage system only knows about
storage: it doesn't have to help with things like transaction
boundaries any more, and it continues to be indifferent to the actual
contents of the storage. At the GHOB layer, we know that we've got
chunks of storage which are the unit of undo discard, and we know that
they start with a magic number that identifies the type, but it
doesn't know whether they are internally broken into records or, if
so, how those records are organized. The individual GHOB managers do
know that stuff; for example, the transaction-GHOB manager would know
that AMs insert undo records and how those records are compressed and
so forth. One thing that feels good about this system is that you
could actually write something like the test_undo module that Thomas
had in an older patch set. He threw it away because it doesn't play
nice with the way the undorecord/undoaccess stuff works: that stuff
thinks that all undo records have to be in the format that it knows
about, and if they're not, it will barf. With this, test_undo could
define its own kind of GHOB that keeps stuff until it's explicitly
told to throw it away, and that'd be fine for 'make check' (but not
'make installcheck', probably).

Thoughts?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#208Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#202)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 6:29 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Aug 5, 2019 at 6:16 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

For zheap, we collect all the records of a page, apply them together
and then write the entire page in WAL. The progress of transaction is
updated at either transaction end (rollback complete) or after
processing some threshold of undo records. So, generally, the WAL
won't be for each undo record apply.

This explanation omits a crucial piece of the mechanism, because
Heikki is asking what keeps the undo from being applied multiple
times.

Okay, I didn't realize that.

When we apply the undo records to a page, we also adjust the
undo pointers in the page. Since we have an undo pointer per
transaction slot, and each transaction has its own slot, if we apply
all the undo for a transaction to a page, we can just clear the slot;
if we somehow end up back at the same point later, we'll know not to
apply the undo a second time because we'll see that there's no
transaction slot pointing to the undo we were thinking of applying. If
we roll back to a savepoint, or for some other reason choose to apply
only some of the undo to a page, we can set the undo record pointer
for the transaction back to the value it had before we generated any
newer undo. Then, we'll know that the newer undo doesn't need to be
applied but the older undo can be applied.

At least, I think that's how it's supposed to work.

Right, this is how it works.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#209Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#206)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

Need to do something else for a bit. More later.

Here we go.

+ /*
+  * Compute the header size of the undo record.
+  */
+Size
+UndoRecordHeaderSize(uint16 uur_info)
+{
+	Size		size;
+
+	/* Add fixed header size. */
+	size = SizeOfUndoRecordHeader;
+
+	/* Add size of transaction header if it presets. */
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+
+	/* Add size of rmid if it presets. */
+	if ((uur_info & UREC_INFO_RMID) != 0)
+		size += sizeof(RmgrId);
+
+	/* Add size of reloid if it presets. */
+	if ((uur_info & UREC_INFO_RELOID) != 0)
+		size += sizeof(Oid);
+
+	/* Add size of fxid if it presets. */
+	if ((uur_info & UREC_INFO_XID) != 0)
+		size += sizeof(FullTransactionId);
+
+	/* Add size of cid if it presets. */
+	if ((uur_info & UREC_INFO_CID) != 0)
+		size += sizeof(CommandId);
+
+	/* Add size of forknum if it presets. */
+	if ((uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+
+	/* Add size of prevundo if it presets. */
+	if ((uur_info & UREC_INFO_PREVUNDO) != 0)
+		size += sizeof(UndoRecPtr);
+
+	/* Add size of the block header if it presets. */
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+
+	/* Add size of the log switch header if it presets. */
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+
+	/* Add size of the payload header if it presets. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;

There's numerous blocks with one if for each type, and the body copied
basically the same for each alternative. That doesn't seem like a
reasonable approach to me. Means that many places need to be adjusted
when we invariably add another type, and seems likely to lead to bugs
over time.

+ /* Add size of the payload header if it presets. */

FWIW, repeating the same comment, with or without minor differences, 10
times is a bad idea. Especially when the comment doesn't add *any* sort
of information.

Also, "if it presets" presumably is a typo?

+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	/* Add undo record length size. */
+	size += sizeof(uint16);
+
+	return size;
+}
+
+/*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint16		uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload *payload = (UndoRecordPayload *) (page_ptr + size);
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}

These functions are all basically the same. We shouldn't copy code over
and over like this.

+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);
+
+	/* Copy rmid if present. */
+	if ((uur->uur_info & UREC_INFO_RMID) != 0)
+		ucontext->urec_rmid = uur->uur_rmid;
+
+	/* Copy reloid if present. */
+	if ((uur->uur_info & UREC_INFO_RELOID) != 0)
+		ucontext->urec_reloid = uur->uur_reloid;
+
+	/* Copy fxid if present. */
+	if ((uur->uur_info & UREC_INFO_XID) != 0)
+		ucontext->urec_fxid = uur->uur_fxid;
+
+	/* Copy cid if present. */
+	if ((uur->uur_info & UREC_INFO_CID) != 0)
+		ucontext->urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy prev undo record pointer if it is present. */
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		ucontext->urec_prevundo = uur->uur_prevundo;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		memcpy(&ucontext->urec_logswitch, uur->uur_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+	else
+	{
+		ucontext->urec_payload.urec_payload_len = 0;
+		ucontext->urec_payload.urec_tuple_len = 0;
+	}
+
+	/* Compute undo record expected size and store in the context. */
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}

It really can't be right to have all these fields basically twice, in
UnackedUndoRecord, and UndoPackContext. And then copy them one-by-one.
I mean there's really just some random differences (ordering, some field
names) between the structures, but otherwise they're the same?

What on earth do we gain by this? This entire intermediate stage makes
no sense at all to me. We copy data into an UndoRecord, then we copy
into an UndoRecordContext, with essentially a field-by-field copy
logic. Then we have another field-by-field logic that copies the data
into the page.

+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_rmid), sizeof(RmgrId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_reloid), sizeof(Oid),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_fxid), sizeof(FullTransactionId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_cid), sizeof(CommandId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_prevundo,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}

I don't understand. The only purpose of this is that we can partially
write a packed-but-not-actually-packed record onto a bunch of pages? And
for that we have an endless chain of copy and pasted code calling
InsertUndoBytes()? Copying data into shared buffers in tiny increments?

If we need to this, what is the whole packed record format good for?
Except for adding a bunch of functions with 10++ ifs and nearly
identical code?

Copying data is expensive. Copying data in tiny increments is more
expensive. Copying data in tiny increments, with a bunch of branches, is
even more expensive. Copying data in tiny increments, with a bunch of
branches, is even more expensive, especially when it's shared
memory. Copying data in tiny increments, with a bunch of branches, is
even more expensive, especially when it's shared memory, especially when
all that shared meory is locked at once.

+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:

You know roughly what I'm thinking.

commit 95d10fb308e3ec6ac8a7b4b5e7af78f6825f4dc8
Author: Amit Kapila <amit.kapila@enterprisedb.com>
AuthorDate: 2019-06-13 15:10:06 +0530
Commit: Amit Kapila <amit.kapila@enterprisedb.com>
CommitDate: 2019-07-31 16:36:52 +0530

Infrastructure to register and fetch undo action requests.

I'm pretty sure I suggested that before, but this seems the wrong
order. We should have very basic undo functionality in place, even if it
can't actually guarantee that undo gets processed, before this. The
design of this piece depends on understanding the later parts too much.

This infrasture provides a way to allow execution of undo actions. One
might think that we can always execute undo actions on error or explicit
rollabck by user, however there are cases when that is not posssible.

s/rollabck by user/rollback by a user/

For example, (a) if the system crash while doing operation, then after
startup, we need a way to perform undo actions; (b) If we get error while
performing undo actions.

"doing operation" doesn't sound right. Maybe "performing an operation"?

Apart from this, when there are large rollback requests, then it is quite
inefficient to perform all the undo actions and then return control to
user.

I don't think efficiency is the right word to describe that. I'd argue
that it's probably often at least as efficient to let that rollback be
processed in that context (higher cache locality, preventing that
backend from creating further undo). It's just that doing so has a bad
effect on latency.

To allow efficient execution of the undo actions, we create three queues
and a hash table for the rollback requests.

Again I don't think efficient is the right descriptor. My understanding
of the goals of having multiple queues is that it helps to achieve
forward progress among separate goals, without loosing too much
efficiency.

A Xid based priority queue
which will allow us to process the requests of older transactions and help
us to move oldesdXidHavingUnappliedUndo (this is a xid-horizon below which
all the transactions are visible) forward.

"This is an important concern, because ..."

+/*
+ * Returns the undo record pointer corresponding to first record in the given
+ * block.
+ */
+UndoRecPtr
+UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+							UndoLogCategory category)
+{
+	Buffer buffer;
+	Page page;
+	UndoPageHeader	phdr;
+	RelFileNode		rnode;
+	UndoLogOffset	log_cur_off;
+	Size			partial_rec_size;
+	int				offset_cur_page;
+
+	if (!BlockNumberIsValid(blkno))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid undo block number")));
+
+	UndoRecPtrAssignRelFileNode(rnode, urec_ptr);
+
+	buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, blkno,
+									   RBM_NORMAL, NULL,
+									   RelPersistenceForUndoLogCategory(category));
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader)page;
+
+	/* Calculate the size of the partial record. */
+	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						phdr->tuple_len + phdr->payload_len -
+						phdr->record_offset;
+
+	/* calculate the offset in current log. */
+	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
+	log_cur_off = (blkno * BLCKSZ) + offset_cur_page;
+
+	UnlockReleaseBuffer(buffer);
+
+	/* calculate the undo record pointer based on current offset in log. */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urec_ptr), log_cur_off);
+}

Yet another function reading undo blocks. No.

The undo requests must appear in both xid and size
+ * requests queues or neither.

Why?

As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.

*types

+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.

Why aren't rollbacks below the threshold in the hashtable?

+ * To ensure that backend and discard worker don't register the same request
+ * in the hash table, we always register the request with full_xid and the
+ * start pointer for the transaction in the hash table as key.  Backends
+ * always remember the value of start pointer, but discard worker doesn't know

*the discard worker

There's no explanation as to why we need more than the full_xid
(presumably persistency levels). Nor why you chose not to include those.

+ * the actual start value in case transaction's undo spans across multiple
+ * logs.  The reason for the same is that discard worker might encounter the
+ * log which has overflowed undo records of the transaction first.

"the log which has overflowed undo records of the transaction first" is
confusing. Perhaps "the undo log into which the logically earlier undo
overflowed before encountering the logically earlier undo"?

In such
+ * cases, we need to compute the actual start position.  The first record of a
+ * transaction in each undo log contains a reference to the first record of
+ * this transaction in the previous log.  By following the previous log chain
+ * of this transaction, we find the initial location which is used to register
+ * the request.

It seem wrong that the undo request layer needs to care about any of
this.

+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;

As we IIRC have decided to change this into a rbtree, I'll ignore
related parts of the current code. What is the status of that work?
I've checked the git trees, without seeing anything? Your last mail with
patches
/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com
doesn't seem to contain that either?

+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)

-ETOOMANYMACROS

I think nearly all of these shouldn't exist. See further below.

+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr, e_retry_at, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).start_urec_ptr = e_start_urec_ptr, \
+	GetErrorQueueElem(elem).next_retry_at = e_retry_at, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)

It's very very rarely a good idea to have macros that evaluate their
arguments multiple times. It'll also never be a good idea to get the
same element multiple times from a queue. If needed - I'm very doubtful
of that, given that there's a single caller - it should be a static
inline function that gets the element once, stores it in a local
variable, and then updates all the fields.

+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);

It's very odd that elements are named 'Queue' rather than a queue element.

+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */

Why is it useful to have error_occurred_at be part of the comparison at
all? If we need a tiebraker, err_occurred_at isn't that (if we can get
conflicts for next_retry_at, then we can also get conflicts in
err_occurred_at). Seems better to use something actually guaranteed to
be unique for a tiebreaker.

+int
+UndoRollbackHashTableSize()
+{

missing void, at least compared to our common style.

+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max

"the same"? I assume this intended to mean the same size?

+	 * backends. This will ensure that it won't get filled.
+	 */

How does this ensure anything?

+static int
+RemoveOldElemsFromXidQueue()

void.

+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()

void.

We shouldn't need this in this form anymore after the rbtree conversion
- but because it again highlights on of my main complaints of all this
work: Don't have multiple copies of essentially equivalent non-trivial
functions. Especially not in the same file. This is a near verbatim
copy of RemoveOldElemsFromXidQueue. Without any explanations why it's
needed.

Even if you intended it only as a short-term workaround (e.g. for the
queues not sharing enough of a common base-layout to be able to share
one cleanup routine), at the very least you need to add a FIXME or such
explaining that this needs to be fixed.

+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{

Another copy.

+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}

This is a slightly different form of copying code repeatedly. Instead of
passing in the queue type, this should get a pointer to the queue passed
in. Functions like Get*QueueSize(), GetErrorQueueNthElem() shouldn't
exist once for each queue type, they should be agnostic as to what the
queue type is, and accept a queue as the parameter.

Yes, there'd still be one additional queue type specific check, for the
time. But that's still a lot less copied code.

I also don't think it's a good idea to use RollbackHashKey as the
parameter/function name here. This function doesn't need to know that
it's for a hash table lookup.

+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * last_log_start_urec_ptr_out - This is an OUT parameter.  If a transaction
+ * writes undo records in multiple undo logs, this is set to the start undo
+ * record pointer of this transaction in the last log.  If the transaction
+ * writes undo records only in single undo log, it is set to start_urec_ptr.
+ * This value is used to update the rollback progress of the transaction in
+ * the last log.  Once, we have start location in last log, the start location
+ * in all the previous logs can be computed.  See execute_undo_actions for
+ * more details.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   UndoRecPtr *last_log_start_urecptr_out,
+						   FullTransactionId full_xid)
+{

This really can't be the right place for this function.

+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,

*workers

Also, it's not really queued to workers. Something like "can queue the
rollback request to be executed in the background" would be more
accurate afaict.

+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check

*acquiring

+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	{

Why is this being checked with the lock held? Seems like this should be
handled in a pre-check?

+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId) + sizeof(UndoRecPtr);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 pending_undo_queue_size,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 pending_undo_queue_size,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 pending_undo_queue_size,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Error Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);

Hm. Aren't you overwriting previously initialized data here with memset
and Init*Queue, when using an EXEC_BACKEND build (e.g windows)?

I think all the initialization should only be done once, e.g. if
ShmemInitStruct() sets the *found to true. And then the other elements
should be asserted to also exist/not exist.

Also, what is the memset() here supposed to be doing? Aren't you just
memsetting() the first element in the queue? Since the queue is
dynamically sized, a static length (sizeof(UndoSizeQueue)) memset()
obviously cannot cannot initialize the members.

Also, this again is repeating code unnecessarily.

+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= pending_undo_queue_size)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();

If we kept this, I'd rename these as Prune* and reword the comments to
match. This makes the code look like we're actually removing valid
entries.

+/*
+ * Get the next set of pending rollback request for undo worker.

"set"? We only remove one, no?

+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}

When is it possible to hit the in-progress case?

+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr, true);
+				cur_undo_queue++;
+				continue;
+			}

I still think there never should be a case in which this is
possible. Dropping a database ought to remove all the associated undo.

+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->status = UNDO_REQUEST_INPROGRESS;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;

Copy of code from above.

+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself if the request is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues.  The two reasons why request can't be pushed are (a) the size of
+ * request is smaller than a threshold and the request is not from discard
+ * worker, (b) the undo request queues are full.
+ *
+ * It is not advisable to apply the undo actions of a very large transaction
+ * in the foreground as that can lead to a delay in retruning the control back

*returning

+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;

IMO odd to explictly number two elements of an enum, but not the third.

+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;

As I said before, this isn't a queue, it's a queue entry.

+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->last_log_start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->last_log_start_urec_ptr = rh->last_log_start_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)

See my other complaint about such macros. Multiple evaluation hazard
etc. Also, the different formatting in two consecutively defined macros
is odd.

+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to

s/the//g

+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	/*
+	 * We don't need to update the progress for temp tables as they get
+	 * discraded after startup.
+	 */
+	if (category == UNDO_TEMP)
+		return;
+
+	BeginUndoRecordInsert(&context, category, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}

This whole prepare/execute split for updating apply pregress, and next
undo pointers makes no sense to me.

+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,

*returns

+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecordFetchContext	context;
+
+	/* Fetch the undo record. */
+	BeginUndoFetch(&context);
+	uur = UndoFetchRecord(&context, to_urecptr);
+	FinishUndoFetch(&context);

Literally all the places that fetch a record, fetch them with exactly
this combination of calls. If that's the pattern, what do we gain by
this split? Note that UndoBulkFetchRecord does *NOT* use an
UndoRecordFetchContext, for reasons that are beyond me.

+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	do
+	{

use for(;;) or while (true).

+		BlockNumber	progress_block_num = InvalidBlockNumber;
+		int			i;
+		int			nrecords;
+		bool		log_switched = false;
+		bool		rollback_completed = false;
+		bool		update_progress = false;
+		UndoRecPtr	progress_urec_ptr = InvalidUndoRecPtr;
+		UndoRecInfo	*first_urecinfo;
+		UndoRecInfo	*last_urecinfo;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update

s/Once, we process/Once we have processed/

+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		first_urecinfo = &urecinfo[0];
+		last_urecinfo = &urecinfo[nrecords - 1];
+		if (last_urecinfo->uur->uur_info & UREC_INFO_LOGSWITCH)
+		{
+			UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;
+
+			/*
+			 * We have crossed the log boundary.  The rest of the undo for
+			 * this transaction is in some other log, the location of which
+			 * can be found from this record.  See commets atop undoaccess.c.

*comments

+			/*
+			 * We need to save the undo record pointer of the last record from
+			 * previous undo log.  We will use the same as from location in
+			 * next iteration of bulk fetch.
+			 */
+			Assert(UndoRecPtrIsValid(logswitch->urec_prevurp));
+			urec_ptr = logswitch->urec_prevurp;
+
+			/*
+			 * The last fetched undo record corresponds to the first undo
+			 * record of the current log.  Once, the undo actions are performed
+			 * from this log, we've to mark the progress as completed.
+			 */
+			progress_urec_ptr = last_urecinfo->urp;
+
+			/*
+			 * We also need to save the start location of this transaction in
+			 * previous log.  This will be used in the next iteration of bulk
+			 * fetch and updating progress location.
+			 */
+			if (complete_xact)
+			{
+				Assert(UndoRecPtrIsValid(logswitch->urec_prevlogstart));
+				last_log_start_urec_ptr = logswitch->urec_prevlogstart;
+			}
+
+			/* We've to update the progress for the current log as completed. */
+			update_progress = true;
+		}
+		else if (complete_xact)
+		{
+			if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(last_urecinfo->urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(first_urecinfo->urp) > progress_block_num)
+				{
+					update_progress = true;
+					progress_urec_ptr = last_log_start_urec_ptr;
+				}
+			}
+			else
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				rollback_completed = true;
+				update_progress = true;
+				progress_urec_ptr = last_log_start_urec_ptr;
+			}
+		}

This should be in a separate function.

+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);

As noted elsewhere, I think that's the wrong memory management
strategy. We should be using a memory context for undo processing, and
then just reset it as a whole. For one, freeing granularly is
inefficient. But more than that, it also means there's nothing to
prevent memory leaks here.

+/*
+ * execute_undo_actions - Execute the undo actions

That's juts a restatement of the function name.

+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{

Why is this lower case, but ApplyUndo() camel case? How is a reader
supposed to know which one uses for what?

typedef struct TwoPhaseFileHeader
{
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
uint16		gidlen;			/* length of the GID - GID follows the header */
XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need its start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoLogCategories];
+	UndoRecPtr	end_urec_ptr[UndoLogCategories];
} TwoPhaseFileHeader;

Why do we not need that knowledge for undo processing of a non-prepared
transaction?

@@ -191,6 +195,16 @@ typedef struct TransactionStateData
bool		didLogXid;		/* has xid been included in WAL record? */
int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each log category */
+	UndoRecPtr	startUrecPtr[UndoLogCategories]; /* this is 'to' location */
+	UndoRecPtr	latestUrecPtr[UndoLogCategories]; /* this is 'from'
+												   * location */
+	/*
+	 * whether the undo request is registered to be processed by worker later?
+	 */
+	bool		undoRequestResgistered[UndoLogCategories];
+

s/Resgistered/Registered/

@@ -2906,9 +2942,18 @@ CommitTransactionCommand(void)
* StartTransactionCommand didn't set the STARTED state
* appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
* by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while

s/We can't reach here as while/This can't be reached while/

+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ReleaseResourcesAndProcessUndo

Where and how does it handle that? Maybe I misunderstand what you mean?

+			case TBLOCK_UNDO:
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 *
+				 * AbortTransaction is already done, still need to release
+				 * locks and perform cleanup.
+				 */
+				ResetUndoActionsInfo();
+				ResourceOwnerRelease(s->curTransactionOwner,
+									 RESOURCE_RELEASE_LOCKS,
+									 false,
+									 true);
+				s->state = TRANS_ABORT;
CleanupTransaction();

Hm. Why is it ok that we only perform that cleanup action? Either the
rest of potentially held resources will get cleaned up somehow as well,
in which case this ResourceOwnerRelease() ought to be redundant, or
we're potentially leaking important resources like buffer pins, relcache
references and whatnot here?

+/*
+ * CheckAndRegisterUndoRequest - Register the request for applying undo
+ *	actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+CheckAndRegisterUndoRequest()

(void)

+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	int		i;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  See ReleaseResourcesAndProcessUndo.
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}

Wait what? Semi critical sections?

+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (i != UNDO_TEMP && UndoRecPtrIsValid(s->latestUrecPtr[i]))
+		{
+			result = RegisterUndoRequest(s->latestUrecPtr[i],
+										 s->startUrecPtr[i],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undoRequestResgistered[i] = result;
+		}
+	}

Give code like this I have a hard time seing what the point of having
separate queue entries for the different persistency levels is.

+void
+ReleaseResourcesAndProcessUndo(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  One of the main reasons is that we might be already
+	 * processing undo actions for a (sub)transaction when we reach here
+	 * (for ex. error happens while processing undo actions for a
+	 * subtransaction).
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}
+
+	if (!NeedToPerformUndoActions())
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ReleaseResourcesAndProcessUndo: unexpected state %s",
+			TransStateAsString(s->state));
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */

Why do precisely these actions need to be performed here?

+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;

This seems guaranteed to constantly be out of date with other
modifications of the commit/abort sequence.

+bool
+ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+								UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+								bool *undoRequestResgistered, bool isSubTrans)
+{
+	UndoRequestInfo urinfo;
+	int			i;
+	uint32		save_holdoff;
+	bool		success = true;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (end_urec_ptr[i] && !undoRequestResgistered[i])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(fxid,
+									 end_urec_ptr[i],
+									 start_urec_ptr[i],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[i];
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/* We should never reach here when we are in a semi-critical-section. */
+				Assert(SemiCritSectionCount == 0);

This seems entirely and completely broken. You can't just catch an
exception and continue. What if somebody held an lwlock when the error
was thrown? A buffer pin? As far as I can tell the semi crit section
stuff doesn't protect you against anything here, because it's not used
exclusively.

+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.

You say it's an exception, but you do not explain why that exception is
there.

Nor why that's not a problem for:

+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It

*rolled back

+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and

*transactions

+ * all-visible transactions are discarded seprately for each log. This is

*separately

+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions

*tranaction headers

+ * of entire transaction at one-shot as we need to perform the actions starting

*an entire transaction in one shot

+ * from end location to start location. However, it is possbile that the later

*possible

+ * portion of transaction that is overflowed into a separate log can be processed

*a transaction

+ * separately if we encounter the corresponding log first.  If we want we can
+ * combine the log for processing in that case as well, but there is no clear
+ * advantage of the same.

*of doing so

+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;

Why is a database needed?

+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();

Too generic name.

@@ -668,6 +676,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
UndoCompressionInfo *compression_info =
&context->undo_compression_info[context->alloc_context.category];

+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo

s/in a single-user-mode/in single-user-mode/ (although I'd also remove
the dashes)

+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}

I think it's wrong to do this here. We shouldn't open the database for
writes before having performed sufficient initialization. If done like
that, we shouldn't ever get here. Without such sequencing it's actually
not possible to bring up a standby and allow writes in a normal way -
the first few transactions will just fail. That's not ok.

Nor are new retry loops with sleeps ok IMO.

+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+

Why do we need to this work every time we're inserting undo? shouldn't
that just happen once, when first accessing an undo log in a
transaction?

+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();

Why do we need this? I assume it's so we can have a resource owner?

Out of energy.

Greetings,

Andres Freund

#210Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-06 00:56:26 -0700, Andres Freund wrote:

Out of energy.

Here's the last section of my low-leve review. Plan to write a higher
level summary afterwards, now that I have a better picture of the code.

+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)

I think the naming here is pretty confusing. We have UndoDiscard(),
UndoDiscardOneLog(), UndoLogDiscard(). I don't think anybody really can
be expected to understand what is supposed to be what from these names.

+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{

for(;;) or while (true)

+		TransactionId wait_xid = InvalidTransactionId;
+		bool pending_abort = false;
+		bool request_rollback = false;
+		UndoStatus status;
+		UndoRecordFetchContext	context;
+
+		next_insert = UndoLogGetNextInsertPtr(logno);
+
+		/* There must be some undo data for a transaction. */
+		Assert(next_insert != undo_recptr);
+
+		/* Fetch the undo record for the given undo_recptr. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, undo_recptr);
+		FinishUndoFetch(&context);
+
+		if (uur != NULL)
+		{
+			if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)

FWIW, this is precisely my problem with exposing such small
informational functions, which actually have to perform some work. As is
there's several places looking up the underlying undo slot, within just
these lines of code.

We do it once in UndoLogGetNextInsertPtr(). Then again in
UndoFetchRecord(). And then again in UndoRecPtrGetCategory(). And then
later again multiple times when actually discarding. That perhaps
doesn't matter from a performance POV, but for me that indicates that
the APIs aren't quite right.

+			{
+				/*
+				 * For the "shared" category, we only discard when the
+				 * rm_undo_status callback tells us we can.
+				 */

Is there a description as to what the rm_status callback is intended to
do? It currently is mandatory, is that intended? Why does this only
apply to shared records? And why just for SHARED, not for any of the others?

+			else
+			{
+				TransactionId xid = XidFromFullTransactionId(uur->uur_fxid);
+
+				/*
+				 * Otherwise we use the CLOG and xmin to decide whether to
+				 * wait, discard or roll back.
+				 *
+				 * XXX: We've added the transaction-in-progress check to
+				 * avoid xids of in-progress autovacuum as those are not
+				 * computed for oldestxmin calculation.

Hm. xids of autovacuum? The concern here is the xid that autovacuum
might acquire when locking a relation for truncating a table at the end,
with wal_level=replica? Because otherwise it shouldn't have any xids?

See
+ * DiscardWorkerMain.

Hm. This actually reminds me of a complaint I have about this. ISTM that
the logic for discarding itself should be separate from the discard
worker. I'd just add that, and a UDF to invoke it, in a separate commit.

+			/*
+			 * Add the aborted transaction to the rollback request queues.
+			 *
+			 * We can ignore the abort for transactions whose corresponding
+			 * database doesn't exist.
+			 */
+			if (request_rollback && dbid_exists(uur->uur_txn->urec_dbid))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr,
+										   undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+
+				pending_abort = true;
+			}

As I, I think, said before: This imo should not be necessary.

+
+		/*
+		 * We can discard upto this point when one of following conditions is

*up to

+		 * met: (a) we need to wait for a transaction first. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */

This comment is hard to understand. Perhaps you're missing some words?
Because it's e.g. not clear what it means that "we can discard up to
this point", when we "need to wait for a transaction firts". Those seem
strictly contradictory. I assume what this is trying to say is that we
now have reached the end of the range of undo that can be discarded, so
we should do so now? But it's really quite muddled, because we don't
actually necessarily discard here, because we might have a wait_xid, for
example?

+		if (TransactionIdIsValid(wait_xid) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)

Hm. Is it guaranteed that wait_xid isn't actually old enough that we
could discard further? I haven't figured out what precisely the purpose
of rm_undo_status is, so I'm not sure. But the alternative seems to be
that the callback would need to perform its own GetOldestXmin()
computations etc, which seems to make no sense?

It seems to me that the whole DidCommit/!InProgress/ block should not be
part of the if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED) else
if block, but follow it? I.e. the only thing inside the else should be
XidFromFullTransactionId(uur->uur_fxid), and then we check afterwards
whether it, or rm_undo_status()'s return value requires waiting?

+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;

I don't understand why this block sets *hibernate to false. I mean
need_discard is not guranteed to be true at this point, no?

+			/*
+			 * If we don't need to wait for this transaction and this is not
+			 * an aborted transaction, then we can discard it as well.
+			 */
+			if (!TransactionIdIsValid(wait_xid) && !pending_abort)
+			{
+				/*
+				 * It is safe to use next_insert as the location till which we
+				 * want to discard in this case.  If something new has been
+				 * added after we have fetched this transaction's record, it
+				 * won't be considered in this pass of discard.
+				 */
+				undo_recptr = next_insert;
+				latest_discardxid = XidFromFullTransactionId(undofxid);
+				need_discard = true;
+
+				/* We don't have anything more to discard. */
+				undofxid = InvalidFullTransactionId;
+			}
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,

*recycled

+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/* Update the slot information for the next pass of discard. */
+			slot->wait_fxmin = undofxid;
+			slot->oldest_data = undo_recptr;

Perhaps 'next pass of UndoDiscard()' instead? I found it confusing that
UndoDiscardLog() is a loop, meaning that the "next pass" could perhaps
reference the next pass through UndoDiscardLog()'s loop. But it's for
UndoDiscard().

+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}

It seems to me that the entire block above just shouldn't be inside the
loop. As far as I can tell the point of the loop is to figure out up to
where we can discard. Putting the actually discarding inside that loop
is just confusing (and requires deeper indentation than necessary).

+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass

"a first function"? There can only be one first function, no? Also, what
does "first function" really mean?

As I write earlier, I think this function name is too generic, it
doesn't explain anything. And I think it's not OK for it to be called
(the bgworker is started with BgWorkerStart_RecoveryFinished) after the
system is supposed to be ready (i.e. StartupXlog() has finished, we
signal that we're up to pg_ctl etc, and allow writing transaction), but
necessary for allowing writes to be allowed (we throw errors in
PrepareUndoInsert()).

+ * over undo logs is complete, new undo can is allowed to be written in the

"undo can"?

+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before start allowing any new transaction to write the
+ * undo we need to make sure that we know exact number of pending requests.
+ */
+void
+UndoLogProcess()

(void)

+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();

The need for resowners does not imply needing transactions. I think
nearly all aux processes, for example, don't use transactions, but do
have a resowner.

+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* We do not execute shared (non-transactional) undo records. */
+		if (slot->meta.category == UNDO_SHARED)
+			continue;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			TransactionId xid;
+			UndoRecordFetchContext	context;
+
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;

Why isn't this loop while(UndoRecPtrIsValid(undo_recptr))?

+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(xid) &&
+				!TransactionIdIsInProgress(xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are

"is point"

+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) ==
+				UndoRecPtrGetLogNo(uur->uur_txn->urec_next))
+				undo_recptr = uur->uur_txn->urec_next;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		/*
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);

I'm fairly sure that pgindent will add some newlines here... It's a good
practice to re-pgindent patches.

+		/* We can't process temporary undo logs. */
+		if (slot->meta.category == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin then try
+		 * to discard the undo log.
+		 */
+		if (!FullTransactionIdIsValid(slot->wait_fxmin) ||
+			FullTransactionIdPrecedes(slot->wait_fxmin, oldestXidHavingUndo))

So the comment describes something different than what's happening,
while otherwise not adding much over the code... That's imo confusing.

+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);

That comment seems unhelpful.

+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	if (FullTransactionIdIsValid(oldestXidHavingUndo))
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+							U64FromFullTransactionId(oldestXidHavingUndo));

Seems like a lock would be more appropriate if we ever needed that -
only other discard workers would need it, so ...

+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}

Uh. So. What happens if we start up in single user mode while
transactions that haven't been rolled back yet exist? Which seems like a
pretty typical situation for single user mode, because usually something
has gone wrong before, which means it's quite likely that there are
transactions that effectively aborted and haven't processed undo? How
is this not entirely broken?

+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{

The only callsite for this is:

+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;

Which looks mightily odd, given that relid doesn't really sound like an
undo log number. There's also no code actually registering an
ONCOMMIT_TEMP_DISCARD callback.

Nor is it clear to me why it, in general, would be correct to drop undo
pre-commit, even for temp relations. It's fine for ON COMMIT DROP
relations, but what about temporary relations that are longer lived than
that? As the transaction can still fail at this stage - e.g. due to
serialization failures - we'd just throw undo away that we'll need later?

@@ -943,9 +1077,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,

/*
* We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed

*the discard worker

* The request can't be pushed into the undo worker queue. The

I don't think 'undo worker queue' is really correct. It's not one
worker, and it's not one queue. And we're not queueing for a specific
worker.

- * backends will try executing by itself.

"Executing by itself" doesn't sound right. Execute the undo itself?

+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with

"will keep the entry into" doesn't sound right. Insert?

+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
*/
-		else
+		else if (!IsDiscardProcess())
rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
}

I don't understand what the point of this is. We add an entry into the
hashtable, but mark it as invalid? How does this not allow to run out of
memory?

+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.

I thought we had agreed that workers pick databases after they're
started? There seems to be plenty code in here that does not implement
that.

+/* SIGTERM: set flag to exit at next convenient time */
+static void
+UndoworkerSigtermHandler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}

So one handler saves errno, the other doesn't...

+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)

Once we have undo workers pick their db, this should not be needed
anymore. The launcher shouldn't even prepare anything in shared memory
for it.

+/*
+ * Returns whether an undo worker is available.
+ */
+static int
+IsUndoWorkerAvailable(void)
+{
+	int			i;
+	int			alive_workers = 0;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use)
+			alive_workers++;
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return (alive_workers < max_undo_workers);
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("undo worker slot %d is empty",
+						slot)));
+	}
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}

Why do all these need an exclusive lock?

+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
+ * performing undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo * urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);

The comment above says "We need to connect to the database", yet we
assert here that we "must be connected to the database".

+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+		return;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);

Why do we need to be connected in the launcher? I assume that's because
we still do checks on the database?

Greetings,

Andres Freund

#211Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#210)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

I'll be responding to a bunch of long review emails in this thread
point by point separately, but just picking out a couple of points
here that jumped out at me:

On Wed, Aug 7, 2019 at 9:18 AM Andres Freund <andres@anarazel.de> wrote:

+                     {
+                             /*
+                              * For the "shared" category, we only discard when the
+                              * rm_undo_status callback tells us we can.
+                              */

Is there a description as to what the rm_status callback is intended to
do? It currently is mandatory, is that intended? Why does this only
apply to shared records? And why just for SHARED, not for any of the others?

Yeah, I will respond to this. After recent discussions with Robert
the whole UNDO_SHARED concept looks a bit shaky, and there's a better
way trying to get out -- more on that soon.

See
+ * DiscardWorkerMain.

Hm. This actually reminds me of a complaint I have about this. ISTM that
the logic for discarding itself should be separate from the discard
worker. I'd just add that, and a UDF to invoke it, in a separate commit.

That's not a bad idea -- I have a 'pg_force_discard()' SP which I'll
include in my next patchset, which is a bit raw, which I'm planning to
make a bit smarter -- it might make sense to use the same code path
for that.

--
Thomas Munro
https://enterprisedb.com

#212Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

Hello Andres,

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

+/* Each worker queue is a binary heap. */
+typedef struct
+{
+     binaryheap *bh;
+     union
+     {
+             UndoXidQueue *xid_elems;
+             UndoSizeQueue *size_elems;
+             UndoErrorQueue *error_elems;
+     }                       q_choice;
+} UndoWorkerQueue;

As we IIRC have decided to change this into a rbtree, I'll ignore
related parts of the current code. What is the status of that work?
I've checked the git trees, without seeing anything? Your last mail with
patches
/messages/by-id/CAA4eK1KKAFBCJuPnFtgdc89djv4xO=ZkAdXvKQinqN4hWiRbvA@mail.gmail.com
doesn't seem to contain that either?

Yeah, we're changing this into a rbtree. This is still work-in-progress.

.........
+#define GetErrorQueueNthElem(n) \
+( \
+     AssertMacro(!ErrorQueueIsEmpty()), \
+     DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)

-ETOOMANYMACROS

I think nearly all of these shouldn't exist. See further below.

+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr, e_retry_at, e_occurred_at) \
+( \
+     GetErrorQueueElem(elem).dbid = e_dbid, \
+     GetErrorQueueElem(elem).full_xid = e_full_xid, \
+     GetErrorQueueElem(elem).start_urec_ptr = e_start_urec_ptr, \
+     GetErrorQueueElem(elem).next_retry_at = e_retry_at, \
+     GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)

It's very very rarely a good idea to have macros that evaluate their
arguments multiple times. It'll also never be a good idea to get the
same element multiple times from a queue. If needed - I'm very doubtful
of that, given that there's a single caller - it should be a static
inline function that gets the element once, stores it in a local
variable, and then updates all the fields.

Noted. Earlier, Robert also raised the point of using so many macros.
He also suggested to use a single type of object that stores all the
information we need. It'll make things simpler and easier to
understand. In the upcoming patch set, we're removing all these
changes.

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

#213Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

Need to do something else for a bit. More later.

Here we go.

Thanks for the review. I will work on them. Currently, I need
suggestions on some of the review comments.

+ /*
+  * Compute the header size of the undo record.
+  */
+Size
+UndoRecordHeaderSize(uint16 uur_info)
+{
+     Size            size;
+
+     /* Add fixed header size. */
+     size = SizeOfUndoRecordHeader;
+
+     /* Add size of transaction header if it presets. */
+     if ((uur_info & UREC_INFO_TRANSACTION) != 0)
+             size += SizeOfUndoRecordTransaction;
+
+     /* Add size of rmid if it presets. */
+     if ((uur_info & UREC_INFO_RMID) != 0)
+             size += sizeof(RmgrId);
+
+     /* Add size of reloid if it presets. */
+     if ((uur_info & UREC_INFO_RELOID) != 0)
+             size += sizeof(Oid);
+

There's numerous blocks with one if for each type, and the body copied
basically the same for each alternative. That doesn't seem like a
reasonable approach to me. Means that many places need to be adjusted
when we invariably add another type, and seems likely to lead to bugs
over time.

I agree with the point that we are repeating this in a couple of
function and doing different actions e.g. In this function we are
computing the size and in some other function we are copying the
field. I am not sure what would be the best way to handle it? One
approach could just write one function which handles all these cases
but the caller will suggest what action to take. Basically, it will
look like this.

Function (uur_info, action)
{
if ((uur_info & UREC_INFO_TRANSACTION) != 0)
{
// if action is compute header size
size += SizeOfUndoRecordTransaction;
//else if action is copy to dest
dest = src
...
}
Repeat for other types
}

But, IMHO that function will look confusing to anyone that what
exactly it's trying to achieve. If anyone has a better idea please
suggest.

+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+     char       *writeptr = (char *) page + starting_byte;
+     char       *endptr = (char *) page + BLCKSZ;
+
+     switch (ucontext->stage)
+     {
+             case UNDO_PACK_STAGE_HEADER:
+                     /* Insert undo record header. */
+                     if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+                                                              SizeOfUndoRecordHeader, &writeptr, endptr,
+                                                              &ucontext->already_processed,
+                                                              &ucontext->partial_bytes))
+                             return;
+                     ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+                     /* fall through */
+

I don't understand. The only purpose of this is that we can partially
write a packed-but-not-actually-packed record onto a bunch of pages? And
for that we have an endless chain of copy and pasted code calling
InsertUndoBytes()? Copying data into shared buffers in tiny increments?

If we need to this, what is the whole packed record format good for?
Except for adding a bunch of functions with 10++ ifs and nearly
identical code?

Copying data is expensive. Copying data in tiny increments is more
expensive. Copying data in tiny increments, with a bunch of branches, is
even more expensive. Copying data in tiny increments, with a bunch of
branches, is even more expensive, especially when it's shared
memory. Copying data in tiny increments, with a bunch of branches, is
even more expensive, especially when it's shared memory, especially when
all that shared meory is locked at once.

My idea is, indeed of keeping all these fields duplicated in the
context, just allocate a single memory segment equal to the expected
record size (maybe the payload data can keep separate). Now, based on
uur_info pack all the field of UnpackedUndoRecord in that memory
segment. After that In InsertUndoData, we just need one call to
InsertUndoBytes for copying complete header in one shot and another
call for copying payload data. Does this sound reasonable to you?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#214Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

Need to do something else for a bit. More later.

+ *   false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+     UnpackedUndoRecord *uur = NULL;
+     UndoRecordFetchContext  context;
+
+     /* Fetch the undo record. */
+     BeginUndoFetch(&context);
+     uur = UndoFetchRecord(&context, to_urecptr);
+     FinishUndoFetch(&context);

Literally all the places that fetch a record, fetch them with exactly
this combination of calls. If that's the pattern, what do we gain by
this split? Note that UndoBulkFetchRecord does *NOT* use an
UndoRecordFetchContext, for reasons that are beyond me.

Actually, for the zheap or any other AM, where it needs to traverse
the transactions undo the chain. For example, in zheap we will get the
latest undo record pointer from the slot but we need to traverse the
undo record chain backward using the prevundo pointer store in the
undo record and find the undo record for a particular tuple. Earlier,
there was a loop in UndoFetchRecord which were traversing the chain of
the undo until it finds the matching record and record was matched
using a callback. There was also an optimization that if the current
record doesn't satisfy the callback then we keep the pin hold on the
buffer and go to the previous record in the chain. Later based on the
review comments by Robert we have decided that finding the matching
undo record should be caller's responsibility so we have moved the
loop out of the UndoFetchRecord and kept it in the zheap code. The
reason for keeping the context is that we can keep the buffer pin held
and remember that buffer in the context so that the caller can call
UndoFetchRecord in a loop and the pin will be held on the buffer from
which we have read the last undo record.

I agree that in undoprocessing patch set we always need to fetch one
record so instead of repeating this pattern everywhere we can write
one function and move this sequence of calls in that function.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#215Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

I am responding to some of the points where I need some more inputs or
some discussion is required. Some of the things need more thoughts
which I will respond later and some are quite straight forward and
doesn't need much discussion.

+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */

Why is it useful to have error_occurred_at be part of the comparison at
all? If we need a tiebraker, err_occurred_at isn't that (if we can get
conflicts for next_retry_at, then we can also get conflicts in
err_occurred_at). Seems better to use something actually guaranteed to
be unique for a tiebreaker.

This was to distinguish the cases where the request is failing
multiple times with the request failed the first time. I agree that
we need a better unique identifier like FullTransactionid though. Do
let me know if you have any other suggestion?

+     /*
+      * The rollback hash table is used to avoid duplicate undo requests by
+      * backends and discard worker.  The table must be able to accomodate all
+      * active undo requests.  The undo requests must appear in both xid and
+      * size requests queues or neither.  In same transaction, there can be two
+      * requests one for logged relations and another for unlogged relations.
+      * So, the rollback hash table size should be equal to two request queues,
+      * an error queue (currently this is same as request queue) and max

"the same"? I assume this intended to mean the same size?

Yes. I will add the word size to be more clear.

+      * backends. This will ensure that it won't get filled.
+      */

How does this ensure anything?

Because based on this we will have a hard limit on the number of undo
requests after which we won't allow more requests. See some more
detailed explanation for the same later in this email. I think the
comment needs to be updated.

+      * the binary heaps which can change.
+      */
+     Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+     /*
+      * We normally push the rollback request to undo workers if the size of
+      * same is above a certain threshold.
+      */
+     if (req_size >= rollback_overflow_size * 1024 * 1024)
+     {

Why is this being checked with the lock held? Seems like this should be
handled in a pre-check?

Yeah, it can be a pre-check, but I thought it is better to encapsulate
everything in the function as this is not an expensive check. I think
we can move it to outside lock to avoid any such confusion.

+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+                     bool *in_other_db_out)
+{
+             /*
+              * If some undo worker is already processing the rollback request or
+              * it is already processed, then we drop that request from the queue
+              * and fetch the next entry from the queue.
+              */
+             if (!rh || UndoRequestIsInProgress(rh))
+             {
+                     RemoveRequestFromQueue(cur_queue, 0);
+                     cur_undo_queue++;
+                     continue;
+             }

When is it possible to hit the in-progress case?

The same request is in two queues. It is possible that when the
request is being processed from xid queue by one of the workers, the
request from another queue is picked by another worker. I think this
case won't exist after making rbtree based queues.

+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+                                             BlockNumber block_num)
+{
+     UndoLogCategory category;
+     UndoRecordInsertContext context = {{0}};
+
+     category =
+             UndoLogNumberGetCategory(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+     /*
+      * We don't need to update the progress for temp tables as they get
+      * discraded after startup.
+      */
+     if (category == UNDO_TEMP)
+             return;
+
+     BeginUndoRecordInsert(&context, category, 1, NULL);
+
+     /*
+      * Prepare and update the undo apply progress in the transaction header.
+      */
+     UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+     START_CRIT_SECTION();
+
+     /* Update the progress in the transaction header. */
+     UndoRecordUpdateTransInfo(&context, 0);
+
+     /* WAL log the undo apply progress. */
+     {
+             XLogRecPtr      lsn;
+             xl_undoapply_progress xlrec;
+
+             xlrec.urec_ptr = progress_urec_ptr;
+             xlrec.progress = block_num;
+
+             XLogBeginInsert();
+             XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+             RegisterUndoLogBuffers(&context, 1);
+             lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+             UndoLogBuffersSetLSN(&context, lsn);
+     }
+
+     END_CRIT_SECTION();
+
+     /* Release undo buffers. */
+     FinishUndoRecordInsert(&context);
+}

This whole prepare/execute split for updating apply pregress, and next
undo pointers makes no sense to me.

Can you explain what is your concern here? Basically, in the prepare
phase, we do read and lock the buffer and in the actual update phase
(which is under critical section), we update the contents in the
shared buffer. This is the same idea as we use in many places in
code.

typedef struct TwoPhaseFileHeader
{
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
uint16          gidlen;                 /* length of the GID - GID follows the header */
XLogRecPtr      origin_lsn;             /* lsn of this record at origin node */
TimestampTz origin_timestamp;   /* time of prepare at origin node */
+
+     /*
+      * We need the locations of the start and end undo record pointers when
+      * rollbacks are to be performed for prepared transactions using undo-based
+      * relations.  We need to store this information in the file as the user
+      * might rollback the prepared transaction after recovery and for that we
+      * need its start and end undo locations.
+      */
+     UndoRecPtr      start_urec_ptr[UndoLogCategories];
+     UndoRecPtr      end_urec_ptr[UndoLogCategories];
} TwoPhaseFileHeader;

Why do we not need that knowledge for undo processing of a non-prepared
transaction?

The non-prepared transaction also needs to be aware of that. It is
stored in TransactionStateData. I am not sure if I understand your
question here.

+                      * applying undo via top-level transaction, if we get an error,
+                      * then it is handled by ReleaseResourcesAndProcessUndo

Where and how does it handle that? Maybe I misunderstand what you mean?

It is handled in ProcessUndoRequestForEachLogCat which is called from
ReleaseResourcesAndProcessUndo. Basically, the error is handled in
catch and we insert the request in error queue. The function name
should be changed in comments.

+                     case TBLOCK_UNDO:
+                             /*
+                              * We reach here when we got error while applying undo
+                              * actions, so we don't want to again start applying it. Undo
+                              * workers can take care of it.
+                              *
+                              * AbortTransaction is already done, still need to release
+                              * locks and perform cleanup.
+                              */
+                             ResetUndoActionsInfo();
+                             ResourceOwnerRelease(s->curTransactionOwner,
+                                                                      RESOURCE_RELEASE_LOCKS,
+                                                                      false,
+                                                                      true);
+                             s->state = TRANS_ABORT;
CleanupTransaction();

Hm. Why is it ok that we only perform that cleanup action? Either the
rest of potentially held resources will get cleaned up somehow as well,
in which case this ResourceOwnerRelease() ought to be redundant, or
we're potentially leaking important resources like buffer pins, relcache
references and whatnot here?

I had initially used AbortTransaction() here for such things, but I
was not sure whether that is the right thing when we reach here in
this state. Because AbortTransaction is already done once we reach
here. The similar thing happens for the TBLOCK_SUBUNDO state few
lines below where I had used AbortSubTransaction. Now, one problem I
faced when AbortSubTransaction got invoked in this code path was it
internally invokes RecordTransactionAbort->XidCacheRemoveRunningXids
which result in the error "did not find subXID %u in MyProc". The
reason is obvious which is that we had already removed it when
AbortSubTransaction was invoked before applying undo actions. The
releasing of locks was the thing which we have delayed to allow undo
actions to be applied which is done here. The other idea here I had
was to call AbortTransaction/AbortSubTransaction but somehow avoid
calling RecordTransactionAbort when in this state. Do you have any
suggestion to deal with this?

+{
+     TransactionState s = CurrentTransactionState;
+     bool    result;
+     int             i;
+
+     /*
+      * We don't want to apply the undo actions when we are already cleaning up
+      * for FATAL error.  See ReleaseResourcesAndProcessUndo.
+      */
+     if (SemiCritSectionCount > 0)
+     {
+             ResetUndoActionsInfo();
+             return;
+     }

Wait what? Semi critical sections?

Robert up thread suggested this idea [1] (See paragraph starting with
"I am not a fan of applying_subxact_undo....") to deal with cases
where we get an error while applying undo actions and we need to
promote the error to FATAL. We have two such cases as of now in this
patch, one is when we process temp log category log and other is when
we are rolling back sub-transactions. The detailed reasons are
mentioned in function execute_undo_actions. I think this can be used
for other things as well in the future.

+     for (i = 0; i < UndoLogCategories; i++)
+     {
+             /*
+              * We can't push the undo actions for temp table to background
+              * workers as the the temp tables are only accessible in the
+              * backend that has created them.
+              */
+             if (i != UNDO_TEMP && UndoRecPtrIsValid(s->latestUrecPtr[i]))
+             {
+                     result = RegisterUndoRequest(s->latestUrecPtr[i],
+                                                                              s->startUrecPtr[i],
+                                                                              MyDatabaseId,
+                                                                              GetTopFullTransactionId());
+                     s->undoRequestResgistered[i] = result;
+             }
+     }

Give code like this I have a hard time seing what the point of having
separate queue entries for the different persistency levels is.

It is not for this case, rather, it is for the case of discard worker
(background worker) where we process the transactions at log level.
The permanent and unlogged transactions will be in a separate log and
can be encountered at different times, so this leads to having
separate entries for them. I am planning to give a try to unify them
based on some of the discussions in this email chain.

+void
+ReleaseResourcesAndProcessUndo(void)
+{
+     TransactionState s = CurrentTransactionState;
+
+     /*
+      * We don't want to apply the undo actions when we are already cleaning up
+      * for FATAL error.  One of the main reasons is that we might be already
+      * processing undo actions for a (sub)transaction when we reach here
+      * (for ex. error happens while processing undo actions for a
+      * subtransaction).
+      */
+     if (SemiCritSectionCount > 0)
+     {
+             ResetUndoActionsInfo();
+             return;
+     }
+
+     if (!NeedToPerformUndoActions())
+             return;
+
+     /*
+      * State should still be TRANS_ABORT from AbortTransaction().
+      */
+     if (s->state != TRANS_ABORT)
+             elog(FATAL, "ReleaseResourcesAndProcessUndo: unexpected state %s",
+                     TransStateAsString(s->state));
+
+     /*
+      * Do abort cleanup processing before applying the undo actions.  We must
+      * do this before applying the undo actions to remove the effects of
+      * failed transaction.
+      */
+     if (IsSubTransaction())
+     {
+             AtSubCleanup_Portals(s->subTransactionId);
+             s->blockState = TBLOCK_SUBUNDO;
+     }
+     else
+     {
+             AtCleanup_Portals();    /* now safe to release portal memory */
+             AtEOXact_Snapshot(false, true); /* and release the transaction's
+                                                                              * snapshots */

Why do precisely these actions need to be performed here?

This is to get a transaction into a clean state. Before calling this
function AbortTransaction has been performed and there were few more
things we need to do for cleanup.

+             s->fullTransactionId = InvalidFullTransactionId;
+             s->subTransactionId = TopSubTransactionId;
+             s->blockState = TBLOCK_UNDO;
+     }
+
+     s->state = TRANS_UNDO;

This seems guaranteed to constantly be out of date with other
modifications of the commit/abort sequence.

It is similar to how we change state in Abort(Sub)Transaction and we
change the state back to TRANS_ABORT after applying undo in this
function. So not sure, how it can be out-of-date. Do you have any
better suggestion here?

+bool
+ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+                                                             UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+                                                             bool *undoRequestResgistered, bool isSubTrans)
+{
+     UndoRequestInfo urinfo;
+     int                     i;
+     uint32          save_holdoff;
+     bool            success = true;
+
+     for (i = 0; i < UndoLogCategories; i++)
+     {
+             if (end_urec_ptr[i] && !undoRequestResgistered[i])
+             {
+                     save_holdoff = InterruptHoldoffCount;
+
+                     PG_TRY();
+                     {
+                             /* for subtransactions, we do partial rollback. */
+                             execute_undo_actions(fxid,
+                                                                      end_urec_ptr[i],
+                                                                      start_urec_ptr[i],
+                                                                      !isSubTrans);
+                     }
+                     PG_CATCH();
+                     {
+                             /*
+                              * Add the request into an error queue so that it can be
+                              * processed in a timely fashion.
+                              *
+                              * If we fail to add the request in an error queue, then mark
+                              * the entry status as invalid and continue to process the
+                              * remaining undo requests if any.  This request will be later
+                              * added back to the queue by discard worker.
+                              */
+                             ResetUndoRequestInfo(&urinfo);
+                             urinfo.dbid = dbid;
+                             urinfo.full_xid = fxid;
+                             urinfo.start_urec_ptr = start_urec_ptr[i];
+                             if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+                                     RollbackHTMarkEntryInvalid(urinfo.full_xid,
+                                                                                        urinfo.start_urec_ptr);
+                             /*
+                              * Errors can reset holdoff count, so restore back.  This is
+                              * required because this function can be called after holding
+                              * interrupts.
+                              */
+                             InterruptHoldoffCount = save_holdoff;
+
+                             /* Send the error only to server log. */
+                             err_out_to_client(false);
+                             EmitErrorReport();
+
+                             success = false;
+
+                             /* We should never reach here when we are in a semi-critical-section. */
+                             Assert(SemiCritSectionCount == 0);

This seems entirely and completely broken. You can't just catch an
exception and continue. What if somebody held an lwlock when the error
was thrown? A buffer pin?

The caller deals with that. For example, when this is called from
FinishPreparedTransaction, we do AbortOutOfAnyTransaction and when
called from ReleaseResourcesAndProcessUndo, we just release locks. I
think we might need to do something additional for
ReleaseResourcesAndProcessUndo. Earlier here also, I had
AbortTransaction but was not sure whether that is the right thing to
do especially because it will lead to RecordTransactionAbort called
twice, once when we do AbortTransaction before applying undo actions
and once when we do it after catching the exception. Like as I said
earlier maybe the right way is to just avoid calling
RecordTransactionAbort again.

+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.

You say it's an exception, but you do not explain why that exception is
there.

The exception is when the error queue becomes full. The idea is that
individual queues can be full but not the hash table.

Nor why that's not a problem for:

+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.

The reason why it is not a problem is that we don't remove the entry
from the hash table rather just mark it such that later discard worker
can add it to the queues. I am not sure if I understood your question
completely, but let me try to explain this idea in a bit more detail.

The basic idea is that Rollback Hash Table has space equivalent to all
the three queues plus (2 * MaxBackends). Now, we will stop allowing
the new transactions that want to write undo once the hash table has
entries equivalent to all three queues and we have 2 * Max_Backends
already attached to undo logs that are not committed. Assume we have
each queue size as 5 and Max_Backends =10, then ideally we can 35
entries (3 * 5 + 2 * 10) in the hash table. The way all this is
related to the error queue being full is like this:

Say, we have a number of hash table entries equal to 15 which
indicates all queues are full and now 10 backends connected to two
different logs (permanent and unlogged). Next one of the transaction
errors out and try to rollback, at this stage, it will add an entry in
the hash table and try to execute the actions. While executing
actions, it got an error and couldn't add to error queue because it
was full, so at this stage, it just marks the hash table entry as
invalid and proceeds (consider this happens for both logged and
unlogged categories). So, at this stage, we will have 17 entries in
the hash table and the other 9 backends attached to 18 logs which
makes space for 35 xacts if the system crashes at this stage. The
backend which errored out again tries to perform an operation for
which it needs to perform undo. Now, we won't allow this backend to
perform that action because if it crashed after performing the
operation and before committing, the hash table will overflow.

Currently, there are some problems with the hash table overflow checks
in the code that needs to be fixed.

+     /* There might not be any undo log and hibernation might be needed. */
+     *hibernate = true;
+
+     StartTransactionCommand();

Why do we need this? I assume it's so we can have a resource owner?

Yes, and another reason is we are using dbid_exists in this function.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#216Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#189)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 1:54 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Tue, Jul 30, 2019 at 12:21 PM Thomas Munro <thomas.munro@gmail.com> wrote:

One data structure that could perhaps hold this would be
UndoLogTableEntry (the per-backend cache, indexed by undo log number,
with pretty fast lookups; used for things like
UndoLogNumberGetCategory()). As long as you never want to have
inter-transaction compression, that should have the right scope to
give recovery per-undo log tracking. If you ever wanted to do
compression between transactions too, maybe UndoLogSlot could work,
but that'd have more complications.

I think this could be a good idea. I had thought of keeping in the
slot as my 3rd option but later I removed it thinking that we need to
expose the compression field to the undo log layer. I think keeping
in the UndoLogTableEntry is a better idea than keeping in the slot.
But, I still have the same problem that we need to expose undo
record-level fields to undo log layer to compute the cache entry size.
OTOH, If we decide to get from the first record of the page (as I
mentioned up thread) then I don't think there is any performance issue
because we are inserting on the same page. But, for doing that we
need to unpack the complete undo record (guaranteed to be on one
page). And, UnpackUndoData will internally unpack the payload data
as well which is not required in our case unless we change
UnpackUndoData such that it unpacks only what the caller wants (one
input parameter will do).

I am not sure out of these two which idea is better?

I have one more problem related to compression of the command id
field. Basically, the problem is that we don't set the command id in
the WAL and we will always store FirstCommandId in the undo[1]/messages/by-id/CAFiTN-u2Ny2E-NgT8nmE65awJ7keOzePODZTEg98ceF+sNhRtw@mail.gmail.com. So
suppose there were 2 operations under a different CID then during DO
time both the undo record will store the CID field in their respective
undo records but during REDO time, all the commands will store the
same CID(FirstCommandId) so as per the compression logic the
subsequent record for the same transaction will not store the CID
field. I am not sure what is the best way to handle this but I have
few ideas.

1) Don't compress the CID field ever.
2) Write CID in WAL, but just for compressing the CID field in undo
(which may not necessarily go to disk) we don't want to add extra 4
bytes in the WAL.

Any better idea to handle this?

[1]: /messages/by-id/CAFiTN-u2Ny2E-NgT8nmE65awJ7keOzePODZTEg98ceF+sNhRtw@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#217Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Robert Haas (#203)
Re: POC: Cleaning up orphaned files using undo logs

On 05/08/2019 16:24, Robert Haas wrote:

On Sun, Aug 4, 2019 at 5:16 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I feel that the level of abstraction is not quite right. There are a
bunch of fields, like uur_block, uur_offset, uur_tuple, that are
probably useful for some UNDO resource managers (zheap I presume), but
seem kind of arbitrary. How is uur_tuple different from uur_payload?
Should they be named more generically as uur_payload1 and uur_payload2?
And why two, why not three or four different payloads? In the WAL record
format, there's a concept of "block id", which allows you to store N
number of different payloads in the record, I think that would be a
better approach. Or only have one payload, and let the resource manager
code divide it as it sees fit.

Many of the fields support a primitive type of compression, where a
field can be omitted if it has the same value as on the first record on
an UNDO page. That's handy. But again I don't like the fact that the
fields have been hard-coded into the UNDO record format. I can see e.g.
the relation oid to be useful for many AMs. But not all. And other AMs
might well want to store and deduplicate other things, aside from the
fields that are in the patch now. I'd like to move most of the fields to
AM specific code, and somehow generalize the compression. One approach
would be to let the AM store an arbitrary struct, and run it through a
general-purpose compression algorithm, using the UNDO page's first
record as the "dictionary".

I thought about this, too. I agree that there's something a little
unsatisfying about the current structure, but I haven't been able to
come up with something that seems definitively better. I think
something along the lines of what you are describing here might work
well, but I am VERY doubtful about the idea of a fixed-size struct. I
think AMs are going to want to store variable-length data: especially
tuples, but maybe also other stuff. For instance, imagine some AM that
wants to implement locking that's more fine-grained that the four
levels of tuple locks we have today: instead of just having key locks
and all-columns locks, you could want to store the exact columns to be
locked. Or maybe your TIDs are variable-width.

Sure, a fixed-size struct is quite limiting. My point is that all that
packing of data into UNDO records should be AM-specific. Maybe it would
be handy to have a a few common fields in the undo record header itself,
but most data should be in the AM-specific payload, because it varies
across AMs.

And the problem is that as soon as you move to something where you
pack in a bunch of variable-sized fields, you lose the ability to
refer to thinks using reasonable names. That's where I came up with
the idea of an UnpackedUndoRecord: give the common fields that
"everyone's going to need" human-readable names, and jam only the
strange, AM-specific stuff into the payload. But if those needs are
not actually universal but very much AM-specific, then I'm afraid
we're going to end up with deeply inscrutable code for packing and
unpacking records. I imagine it's possible to come up with a good
structure for that, but I don't think we have one today.

Yeah, that's also a problem with complicated WAL record types. Hopefully
the complex cases are an exception, not the norm. A complex case is
unlikely to fit any pre-defined set of fields anyway. (We could look at
how e.g. protobuf works, if this is really a big problem. I'm not
suggesting that we add a dependency just for this, but there might be
some patterns or interfaces that we could mimic.)

If you remember, we did a big WAL format refactoring in 9.5, which moved
some information from AM-specific structs to the common headers. Namely,
the information on the relation blocks that the WAL record applies to.
That was a very handy refactoring, and allowed tools like pg_waldump to
print more detailed information about all WAL record types. For WAL
records, moving the block information was natural, because there was
special handling for full-page images anyway. However, I don't think we
have enough experience with UNDO log yet, to know which fields would be
best to include in the common undo header, and which to leave as
AM-specific payload. I think we should keep the common header slim, and
delegate to the AM routines.

For UNDO records, having an XID on every record probably makes sense;
all the use cases for UNDO log we've discussed are transactional. The
rules on which UNDO records to apply and what/when to discard, depend on
whether a transaction committed or aborted and when, so you need the XID
for that. Although, the rule also depends on the AM; for cleaning up
orphaned files, an UNDO record for a committed transaction can be
discarded immediately, while zheap and zedstore records need to be kept
around longer. So the exact rules for that will need to be AM-specific,
too. Or maybe there are only a few different cases and we can enumerate
them, so that an AM can just set a flag on the UNDO record to indicate
when it can be discarded, instead of having a callback or some other
totally generic approach.

In short, I think we should keep the common code that deals with UNDO
records more dumb, and delegate to the AMs more. That's enough for
cleaning up orphaned files, we don't really need the more complicated
stuff for that. We probably need more smarts for zheap/zedstore, but we
don't quite know what it should look like yet. Let's keep it simple for
now, so that we can get something we can review and commit sooner, and
we can build on top of that later.

I don't like the way UndoFetchRecord returns a palloc'd
UnpackedUndoRecord. I would prefer something similar to the xlogreader
API, where a new call to UndoFetchRecord invalidates the previous
result. On efficiency grounds, to avoid the palloc, but also to be
consistent with xlogreader.

I don't think that's going to work very well, because we often need to
deal with multiple records at a time. There is (or was) a bulk-fetch
interface, but I've also found while experimenting with this code that
it can be useful to do things like:

current = undo_fetch(starting_record);
loop:
next = undo_fetch(current->next_record_ptr);
if some_test(next):
break;
undo_free(current);
current = next;

I think we shouldn't view such cases as exceptions to the general
paradigm of looking at undo records one at a time, but instead as the
normal case for which everything is optimized. Cases like orphaned
file cleanup where the number of undo records is probably small and
they're all independent of each other will, I think, turn out to be
the exception rather than the rule.

Hmm. If you're following an UNDO chain, from newest to oldest, I would
assume that the newer record has enough information to decide whether
you need to look at the previous record. If the previous record is no
longer interesting, it might already be discarded away, after all.

I tried to browse through the zheap code but couldn't see that pattern.
I'm not too familiar with the code, so I might've looked in the wrong
place, though.

- Heikki

#218Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Dilip Kumar (#216)
Re: POC: Cleaning up orphaned files using undo logs

On 07/08/2019 13:52, Dilip Kumar wrote:

I have one more problem related to compression of the command id
field. Basically, the problem is that we don't set the command id in
the WAL and we will always store FirstCommandId in the undo[1]. So
suppose there were 2 operations under a different CID then during DO
time both the undo record will store the CID field in their respective
undo records but during REDO time, all the commands will store the
same CID(FirstCommandId) so as per the compression logic the
subsequent record for the same transaction will not store the CID
field. I am not sure what is the best way to handle this but I have
few ideas.

1) Don't compress the CID field ever.
2) Write CID in WAL, but just for compressing the CID field in undo
(which may not necessarily go to disk) we don't want to add extra 4
bytes in the WAL.

Most transactions have only a few commands, so you could optimize for
that. If you use some kind of a variable-byte encoding for it, it could
be a single byte or even just a few bits, for the common cases.

For the first version, I'd suggest keeping it simple, though, and
optimize later.

- Heikki

#219Thomas Munro
thomas.munro@gmail.com
In reply to: Amit Kapila (#195)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 1, 2019 at 1:22 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 31, 2019 at 10:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 30, 2019 at 5:26 PM Thomas Munro <thomas.munro@gmail.com> wrote:

but
here's a small thing: I managed to reach an LWLock self-deadlock in
the undo worker launcher:

I could see the problem, will fix in next version.

Fixed both of these problems in the patch just posted by me [1].

I reran the script that found that problem, so I could play with the
linger logic. It creates N databases, and then it creates tables in
random databases (because I'm testing with the orphaned table cleanup
patch) and commits or rolls back at (say) 100 tx/sec. While it's
doing that, you can look at the pg_stat_undo_logs view to see the
discard and insert pointers whizzing along nicely, but if you look at
the process table with htop or similar you can see that it's forking
undo apply workers at 100/sec (the pid keeps changing), whenever there
is more than one database involved. With a single database it lingers
as I was expecting (and then creates problems when you want to drop
the database). What I was expecting to see is that if you configure
the test to generate undo work in 2, 3 or 4 dbs, and you have
max_undo_workers set to 4, then you should finish up with 4 undo apply
workers hanging around to service the work calmly without any new
forking happening. If you generate undo work in more than 4
databases, I was expecting to see the undo workers exiting and being
forked so that a total of 4 workers (at any time) can work their way
around the more-than-4 databases, but not switching as fast as they
can, so that we don't waste all our energy on forking and setup (how
fast exactly they should switch, I don't know, that's what I wanted to
see). A more advanced thing to worry about, not yet tested, is how
well they'll handle asymmetrical work distributions (not enough
workers, but some databases producing a lot and some a little undo
work). Script attached.

--
Thomas Munro
https://enterprisedb.com

Attachments:

test_undo_worker_load_balancing.pytext/x-python-script; charset=US-ASCII; name=test_undo_worker_load_balancing.pyDownload
#220Amit Kapila
amit.kapila16@gmail.com
In reply to: Thomas Munro (#219)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 7, 2019 at 5:06 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Thu, Aug 1, 2019 at 1:22 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 31, 2019 at 10:13 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 30, 2019 at 5:26 PM Thomas Munro <thomas.munro@gmail.com> wrote:

but
here's a small thing: I managed to reach an LWLock self-deadlock in
the undo worker launcher:

I could see the problem, will fix in next version.

Fixed both of these problems in the patch just posted by me [1].

I reran the script that found that problem, so I could play with the
linger logic.

Thanks for the test. I will look into it and get back to you.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#221Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#215)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-07 14:50:17 +0530, Amit Kapila wrote:

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */

Why is it useful to have error_occurred_at be part of the comparison at
all? If we need a tiebraker, err_occurred_at isn't that (if we can get
conflicts for next_retry_at, then we can also get conflicts in
err_occurred_at). Seems better to use something actually guaranteed to
be unique for a tiebreaker.

This was to distinguish the cases where the request is failing
multiple times with the request failed the first time. I agree that
we need a better unique identifier like FullTransactionid though. Do
let me know if you have any other suggestion?

Sure, I get why you have the field. Even if it were just for debugging
or such. Was just commenting upon it being used as part of the
comparison. I'd just go for (next_retry_at, fxid).

+      * backends. This will ensure that it won't get filled.
+      */

How does this ensure anything?

Because based on this we will have a hard limit on the number of undo
requests after which we won't allow more requests. See some more
detailed explanation for the same later in this email. I think the
comment needs to be updated.

Well, as your code stands, I don't think there is an actual hard limit
on the number of transactions needing to be undone due to the way errors
are handled. There's no consideration of prepared transactions.

+     START_CRIT_SECTION();
+
+     /* Update the progress in the transaction header. */
+     UndoRecordUpdateTransInfo(&context, 0);
+
+     /* WAL log the undo apply progress. */
+     {
+             XLogRecPtr      lsn;
+             xl_undoapply_progress xlrec;
+
+             xlrec.urec_ptr = progress_urec_ptr;
+             xlrec.progress = block_num;
+
+             XLogBeginInsert();
+             XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+             RegisterUndoLogBuffers(&context, 1);
+             lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+             UndoLogBuffersSetLSN(&context, lsn);
+     }
+
+     END_CRIT_SECTION();
+
+     /* Release undo buffers. */
+     FinishUndoRecordInsert(&context);
+}

This whole prepare/execute split for updating apply pregress, and next
undo pointers makes no sense to me.

Can you explain what is your concern here? Basically, in the prepare
phase, we do read and lock the buffer and in the actual update phase
(which is under critical section), we update the contents in the
shared buffer. This is the same idea as we use in many places in
code.

I'll comment on the concerns with the whole API separately.

typedef struct TwoPhaseFileHeader
{
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
uint16          gidlen;                 /* length of the GID - GID follows the header */
XLogRecPtr      origin_lsn;             /* lsn of this record at origin node */
TimestampTz origin_timestamp;   /* time of prepare at origin node */
+
+     /*
+      * We need the locations of the start and end undo record pointers when
+      * rollbacks are to be performed for prepared transactions using undo-based
+      * relations.  We need to store this information in the file as the user
+      * might rollback the prepared transaction after recovery and for that we
+      * need its start and end undo locations.
+      */
+     UndoRecPtr      start_urec_ptr[UndoLogCategories];
+     UndoRecPtr      end_urec_ptr[UndoLogCategories];
} TwoPhaseFileHeader;

Why do we not need that knowledge for undo processing of a non-prepared
transaction?

The non-prepared transaction also needs to be aware of that. It is
stored in TransactionStateData. I am not sure if I understand your
question here.

My concern is that I think it's fairly ugly to store data like this in
the 2pc state file. And it's not an insubstantial amount of additional
data either, compared to the current size, even when no undo is in
use. There's a difference between an unused feature increasing backend
local memory and increasing the size of WAL logged data. Obviously it's
not by a huge amount, but still. It also just feels wrong to me.

We don't need the UndoRecPtr's when recovering from a crash/restart to
process undo. Now we obviously don't want to unnecessarily search for
data that is expensive to gather, which is a good reason for keeping
track of this data. But I do wonder if this is the right approach.

I know that Robert is working on a patch that revises the undo request
layer somewhat, it's possible that this is best discussed afterwards.

+                     case TBLOCK_UNDO:
+                             /*
+                              * We reach here when we got error while applying undo
+                              * actions, so we don't want to again start applying it. Undo
+                              * workers can take care of it.
+                              *
+                              * AbortTransaction is already done, still need to release
+                              * locks and perform cleanup.
+                              */
+                             ResetUndoActionsInfo();
+                             ResourceOwnerRelease(s->curTransactionOwner,
+                                                                      RESOURCE_RELEASE_LOCKS,
+                                                                      false,
+                                                                      true);
+                             s->state = TRANS_ABORT;
CleanupTransaction();

Hm. Why is it ok that we only perform that cleanup action? Either the
rest of potentially held resources will get cleaned up somehow as well,
in which case this ResourceOwnerRelease() ought to be redundant, or
we're potentially leaking important resources like buffer pins, relcache
references and whatnot here?

I had initially used AbortTransaction() here for such things, but I
was not sure whether that is the right thing when we reach here in
this state. Because AbortTransaction is already done once we reach
here. The similar thing happens for the TBLOCK_SUBUNDO state few
lines below where I had used AbortSubTransaction. Now, one problem I
faced when AbortSubTransaction got invoked in this code path was it
internally invokes RecordTransactionAbort->XidCacheRemoveRunningXids
which result in the error "did not find subXID %u in MyProc". The
reason is obvious which is that we had already removed it when
AbortSubTransaction was invoked before applying undo actions. The
releasing of locks was the thing which we have delayed to allow undo
actions to be applied which is done here. The other idea here I had
was to call AbortTransaction/AbortSubTransaction but somehow avoid
calling RecordTransactionAbort when in this state. Do you have any
suggestion to deal with this?

Well, what I'm asking is how this possibly could be correct. Perhaps I'm
just missing something, in which case I don't yet want to make
suggestions for how this should look.

My concern is that you seem to have added a state where process quite a
lot of code - the undo actions, which use buffer pins, lwlocks,
sometimes heavyweight locks, potentially even relache, much more - but
we don't actually clean up any of those in case of error, *except* for
*some* resowner managed things. I just don't understand how that could
possibly be correct.

I'm also fairly certain that we had discussed that we can't actually
execute undo outside of a somewhat valid transaction environment - and
as far as I can tell, there's nothing of that here.

Even in the path without an error during UNDO, I see code like:
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}

without any comments why exactly these two cleanup callbacks need to be
called, and no others. See also below.

And then when UNDO errors out, I see:

+	for (i = 0; i < UndoLogCategories; i++)
+	{
+			PG_CATCH();
+			{
...
+				/* We should never reach here when we are in a semi-critical-section. */
+				Assert(SemiCritSectionCount == 0);
+			}
+			PG_END_TRY();

meaning that we'll just move on to undo the next persistency category
after an error. But there's absolutely no resource cleanup here. Which,
to me, means we'll very easily self-deadlock and things like
that. Consider an error thrown during undo, while holding an lwlock. If
the next persistence category acquires that lock again, we'll
self-deadlock. There's a lot of other similar issues.

So I just don't understand the current model of the xact.c
integration. That might be because I just don't understand the current
design, or because the current design is pretty broken.

+{
+     TransactionState s = CurrentTransactionState;
+     bool    result;
+     int             i;
+
+     /*
+      * We don't want to apply the undo actions when we are already cleaning up
+      * for FATAL error.  See ReleaseResourcesAndProcessUndo.
+      */
+     if (SemiCritSectionCount > 0)
+     {
+             ResetUndoActionsInfo();
+             return;
+     }

Wait what? Semi critical sections?

Robert up thread suggested this idea [1] (See paragraph starting with
"I am not a fan of applying_subxact_undo....") to deal with cases
where we get an error while applying undo actions and we need to
promote the error to FATAL.

Well, my problem with this starts with the fact that I don't know see a
reason why we would want to promote subtransaction failures to FATAL. Or
why that would be OK - loosing reliability when using savepoints seems
pretty dubious to me. And sometimes we're can expect to get errors when
savepoints are in use, e.g. out-of-memory errors. And they're often
going to happen again during undo processing. So this isn't a "oh, it
never realistically happens" scenario imo.

There's two comments about this:

+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.
+	 * (a) Subtransactions.  We can't proceed without applying
+	 * subtransaction's undo as the modifications made in that case must not
+	 * be visible even if the main transaction commits.  The reason why that
+	 * can happen is because for undo-based AM's we don't need to have a
+	 * separate transaction id for subtransactions and once the main
+	 * transaction commits the tuples modified by subtransactions will become
+	 * visible.

But that only means we can't allow such errors to be caught - there
should be a lot less harsh ways to achieve that than throwing a FATAL
error. We could e.g. just walk up the transaction stack and mark the
transaction levels as failed or something. So if somebody catches the
error, any database access done will just cause a failure again.

There's also:

+	 * (b) Temp tables.  We don't expect background workers to process undo of
+	 * temporary tables as the same won't be accessible.

But I fail to see why that requires FATALing either. Isn't the worst
outcome here that we'll have some unnecessary undo around?

Give code like this I have a hard time seing what the point of having
separate queue entries for the different persistency levels is.

It is not for this case, rather, it is for the case of discard worker
(background worker) where we process the transactions at log level.
The permanent and unlogged transactions will be in a separate log and
can be encountered at different times, so this leads to having
separate entries for them.

Given a hashtable over fxid, that doesn't seems like a
counter-argument. We can just do an fxid lookup, and if there's already
an entry, update it to reference the additional persistence level.

One question of understanding: Why do we ever want to register undo
requests for transactions that did not start in the log the discard
worker is currently looking at? It seems to me that there's some
complexity involved due to wanting to do that? We might have already
processed the portion of the transaction in the later log, but I don't
see why that'd be a problem?

+void
+ReleaseResourcesAndProcessUndo(void)
+{
+     TransactionState s = CurrentTransactionState;
+
+     /*
+      * We don't want to apply the undo actions when we are already cleaning up
+      * for FATAL error.  One of the main reasons is that we might be already
+      * processing undo actions for a (sub)transaction when we reach here
+      * (for ex. error happens while processing undo actions for a
+      * subtransaction).
+      */
+     if (SemiCritSectionCount > 0)
+     {
+             ResetUndoActionsInfo();
+             return;
+     }
+
+     if (!NeedToPerformUndoActions())
+             return;
+
+     /*
+      * State should still be TRANS_ABORT from AbortTransaction().
+      */
+     if (s->state != TRANS_ABORT)
+             elog(FATAL, "ReleaseResourcesAndProcessUndo: unexpected state %s",
+                     TransStateAsString(s->state));
+
+     /*
+      * Do abort cleanup processing before applying the undo actions.  We must
+      * do this before applying the undo actions to remove the effects of
+      * failed transaction.
+      */
+     if (IsSubTransaction())
+     {
+             AtSubCleanup_Portals(s->subTransactionId);
+             s->blockState = TBLOCK_SUBUNDO;
+     }
+     else
+     {
+             AtCleanup_Portals();    /* now safe to release portal memory */
+             AtEOXact_Snapshot(false, true); /* and release the transaction's
+                                                                              * snapshots */

Why do precisely these actions need to be performed here?

This is to get a transaction into a clean state. Before calling this
function AbortTransaction has been performed and there were few more
things we need to do for cleanup.

That doesn't answer my question. Why is it specifically these ones that
need to be called "manually"? Why no others? Where is that explained? I
assume you just copied them from CleanupTransaction() - but there's no
reference to that fact on either side, which means nobody would know to
keep them in sync.

I'll also note that the way it's currently set up, we don't delete the
transaction context before processing undo, at least as far as I can
see. Which seems that some OOM cases won't be able to roll back, even if
there'd be plenty memory except for the memory used by the
transaction. The portal cleanup will allow for some, but not all of
that, I think.

+bool
+ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+                                                             UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+                                                             bool *undoRequestResgistered, bool isSubTrans)
+{
+     UndoRequestInfo urinfo;
+     int                     i;
+     uint32          save_holdoff;
+     bool            success = true;
+
+     for (i = 0; i < UndoLogCategories; i++)
+     {
+             if (end_urec_ptr[i] && !undoRequestResgistered[i])
+             {
+                     save_holdoff = InterruptHoldoffCount;
+
+                     PG_TRY();
+                     {
+                             /* for subtransactions, we do partial rollback. */
+                             execute_undo_actions(fxid,
+                                                                      end_urec_ptr[i],
+                                                                      start_urec_ptr[i],
+                                                                      !isSubTrans);
+                     }
+                     PG_CATCH();
+                     {
+                             /*
+                              * Add the request into an error queue so that it can be
+                              * processed in a timely fashion.
+                              *
+                              * If we fail to add the request in an error queue, then mark
+                              * the entry status as invalid and continue to process the
+                              * remaining undo requests if any.  This request will be later
+                              * added back to the queue by discard worker.
+                              */
+                             ResetUndoRequestInfo(&urinfo);
+                             urinfo.dbid = dbid;
+                             urinfo.full_xid = fxid;
+                             urinfo.start_urec_ptr = start_urec_ptr[i];
+                             if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+                                     RollbackHTMarkEntryInvalid(urinfo.full_xid,
+                                                                                        urinfo.start_urec_ptr);
+                             /*
+                              * Errors can reset holdoff count, so restore back.  This is
+                              * required because this function can be called after holding
+                              * interrupts.
+                              */
+                             InterruptHoldoffCount = save_holdoff;
+
+                             /* Send the error only to server log. */
+                             err_out_to_client(false);
+                             EmitErrorReport();
+
+                             success = false;
+
+                             /* We should never reach here when we are in a semi-critical-section. */
+                             Assert(SemiCritSectionCount == 0);

This seems entirely and completely broken. You can't just catch an
exception and continue. What if somebody held an lwlock when the error
was thrown? A buffer pin?

The caller deals with that. For example, when this is called from
FinishPreparedTransaction, we do AbortOutOfAnyTransaction and when
called from ReleaseResourcesAndProcessUndo, we just release locks.

I don't see the caller being able to do anything here - the danger is
that a previous category of undo processing might have acquired
resources, and they're not cleaned up on failure, as you've set things up.

Earlier here also, I had AbortTransaction but was not sure whether
that is the right thing to do especially because it will lead to
RecordTransactionAbort called twice, once when we do AbortTransaction
before applying undo actions and once when we do it after catching the
exception. Like as I said earlier maybe the right way is to just
avoid calling RecordTransactionAbort again.

I think that "just" means that you've not divorced the state in which
undo processing is happening well enough from the "original"
transaction. I stand by my suggestion that what needs to happen is
roughly
1) re-assign locks from failed (sub-)transaction to a special "undo"
resource owner
2) completely abort (sub-)transaction
3) start a new (sub-)transaction
4) process undo
5) commit/abort that (sub-)transaction
6) release locks from "undo" resource owner

Nor why that's not a problem for:

+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.

The reason why it is not a problem is that we don't remove the entry
from the hash table rather just mark it such that later discard worker
can add it to the queues. I am not sure if I understood your question
completely, but let me try to explain this idea in a bit more detail.

The basic idea is that Rollback Hash Table has space equivalent to all
the three queues plus (2 * MaxBackends). Now, we will stop allowing
the new transactions that want to write undo once the hash table has
entries equivalent to all three queues and we have 2 * Max_Backends
already attached to undo logs that are not committed. Assume we have
each queue size as 5 and Max_Backends =10, then ideally we can 35
entries (3 * 5 + 2 * 10) in the hash table. The way all this is
related to the error queue being full is like this:

Say, we have a number of hash table entries equal to 15 which
indicates all queues are full and now 10 backends connected to two
different logs (permanent and unlogged). Next one of the transaction
errors out and try to rollback, at this stage, it will add an entry in
the hash table and try to execute the actions. While executing
actions, it got an error and couldn't add to error queue because it
was full, so at this stage, it just marks the hash table entry as
invalid and proceeds (consider this happens for both logged and
unlogged categories). So, at this stage, we will have 17 entries in
the hash table and the other 9 backends attached to 18 logs which
makes space for 35 xacts if the system crashes at this stage. The
backend which errored out again tries to perform an operation for
which it needs to perform undo. Now, we won't allow this backend to
perform that action because if it crashed after performing the
operation and before committing, the hash table will overflow.

What I don't understand is why there's any need for these "in hash
table, but not in any queue, and not being processed" type entries. All
that avoiding that seems to need is to make the error queue a bit
bigger?

+     /* There might not be any undo log and hibernation might be needed. */
+     *hibernate = true;
+
+     StartTransactionCommand();

Why do we need this? I assume it's so we can have a resource owner?

Yes, and another reason is we are using dbid_exists in this function.

I think it'd be good to avoid needing any database access in both
discard worker, and undo launcher. They really shouldn't need catalog
access architecturally, and in the case of the discard worker we'd add
another process that'd potentially hold the xmin horizon down for a
while in some situations. We could of course add exceptions like we have
for vacuum, but I think we really shouldn't need that.

Greetings,

Andres Freund

#222Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#221)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 8, 2019 at 9:31 AM Andres Freund <andres@anarazel.de> wrote:

I know that Robert is working on a patch that revises the undo request
layer somewhat, it's possible that this is best discussed afterwards.

Here's what I have at the moment. This is not by any means a complete
replacement for Amit's undo worker machinery, but it is a significant
redesign (and I believe a significant improvement to) the queue
management stuff from Amit's patch. I wrote this pretty quickly, so
while it passes simple testing, it probably has a number of bugs, and
to actually use it, it would need to be integrated with xact.c; right
now it's just a standalone module that doesn't do anything except let
itself be tested.

Some of the ways it is different from Amit's patches include:

* It uses RBTree rather than binaryheap, so when we look ahead, we
look ahead in the right order.

* There's no limit to the lookahead distance; when looking ahead, it
will search the entirety of all 3 RBTrees for an entry from the right
database.

* It doesn't have a separate hash table keyed by XID. I didn't find
that necessary.

* It's better-isolated, as you can see from the fact that I've
included a test module that tests this code without actually ever
putting an UndoRequestManager in shared memory. I would've liked to
expand this test module, but I don't have time to do that today and
felt it better to get this much sent out.

* It has a lot of comments explaining the design and how it's intended
to integrate with the rest of the system.

Broadly, my vision for how this would get used is:

- Create an UndoRecordManager in shared memory.
- Before a transaction first attaches to a permanent or unlogged undo
log, xact.c would call RegisterUndoRequest(); thereafter, xact.c would
store a pointer to the UndoRecord for the lifetime of the toplevel
transaction.
- Immediately after attaching to a permanent or unlogged undo log,
xact.c would call UndoRequestSetLocation.
- xact.c would track the number of bytes of permanent and unlogged
undo records the transaction generates. If the transaction goes onto
abort, it reports these by calling FinalizeUndoRequest.
- If the transaction commits, it doesn't need that information, but
does need to call UnregisterUndoRequest() as a post-commit step in
CommitTransaction().
- In the case of an abort, after calling FinalizeUndoRequest, xact.c
would call PerformUndoInBackground() to find out whether to do undo in
the background or the foreground. If undo is to be done in the
foreground, the backend must go on to call UnregisterUndoRequest() if
undo succeeds, and RescheduleUndoRequest() if it fails.

- In the case of a prepared transaction, a pointer to the UndoRequest
would get stored in the GlobalTransaction (but nothing extra would get
stored in the twophase state file).
- COMMIT PREPARED calls UnregisterUndoRequest().
- ROLLBACK PREPARED calls PerformUndoInBackground; if told to do undo
in the foreground, it must go on to call either
UnregisterUndoRequest() on success or RescheduleUndoRequest() on
failure, just like in the regular abort case.

- After a crash, once recovery is complete but before we open for
connections, or at least before we allow any new undo activity, the
discard worker scans all the logs and makes a bunch of calls to
RecreateUndoRequest(). Then, for each prepared transaction that still
exists, it calls SuspendPreparedUndoRequest() and use the return value
to reset the UndoRequest pointer in the GlobalTransaction. Only once
both of those steps are completed can undo workers be safely started.
- Undo workers call GetNextUndoRequest() to get the next task that
they should perform, and once they do, they "own" the undo request.
When undo succeeds or fails, they must call either
UnregisterUndoRequest() or RescheduleUndoRequest(), as appropriate,
just like for foreground undo. Making sure this is water-tight will
probably require some well-done integration with xact.c, so that an
undo request that we "own" because we got it in a background undo
apply process looks exactly the same as one we "own" because it's our
transaction originally.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

0001-Draft-of-new-undo-request-manager.patchapplication/octet-stream; name=0001-Draft-of-new-undo-request-manager.patchDownload
From 3bab0e97a0328a6ab764e817f532b21e31f5fd60 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 8 Aug 2019 16:05:53 -0400
Subject: [PATCH] Draft of new undo request manager.

---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undorequest.c         | 1030 +++++++++++++++++
 src/backend/lib/rbtree.c                      |   46 +-
 src/include/access/undorequest.h              |   77 ++
 src/include/lib/rbtree.h                      |   42 +-
 src/test/modules/Makefile                     |    1 +
 .../test_undo_request_manager/Makefile        |   21 +
 .../expected/test_undo_request_manager.out    |   28 +
 .../sql/test_undo_request_manager.sql         |   16 +
 .../test_undo_request_manager--1.0.sql        |    9 +
 .../test_undo_request_manager.c               |  139 +++
 .../test_undo_request_manager.control         |    4 +
 12 files changed, 1387 insertions(+), 28 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h
 create mode 100644 src/test/modules/test_undo_request_manager/Makefile
 create mode 100644 src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
 create mode 100644 src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.c
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.control

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c6963cf..d036717671 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undolog.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000000..cc6bb5decc
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1030 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *		Undo request manager.
+ *
+ * From the moment a transaction begins until the moment that it commits,
+ * there is a possibility that it might abort, either due to an exception
+ * or because the entire system is restarted (e.g. because of a power
+ * cut). If this happens, all undo generated by that transaction prior
+ * to the abort must be applied. It's possible that many transactions
+ * might abort all around the same time; if so, it may be necessary to
+ * choose which transactions to apply first. This code is in charge of
+ * deciding which transactions should be undone first.
+ *
+ * We ensure that every transaction which aborts gets undone by
+ * registering an "undo request" for it.  The undo request should be
+ * registered before the transaction writes any undo records (except for
+ * temporary undo records, which the creating backend will need to
+ * process locally). If the transaction goes on to commit, the undo
+ * request can be deleted; if it goes on to abort, it needs to be updated
+ * with the final size of the undo generated by that transaction so that
+ * we can prioritize it appropriately. After a system restart, undo
+ * requests for any transactions that were pending undo prior to the
+ * restart need to be reregistered, and any transactions aborted by the
+ * crash need to be registered as well, but this code is not responsible
+ * for those tasks; it just manages the requests that are handed to it.
+ *
+ * We have only a fixed amount of shared memory to store undo requests;
+ * because an undo request has to be created before any undo that might
+ * need to be processed is written, we should never end up in a situation
+ * where there are more existing undo requests that can fit. In extreme
+ * cases, this might cause us to have to refuse to create new requests,
+ * but that should very rare.  If we're starting to run low on space,
+ * FinalizeUndoRequest() will signal callers that undo should be
+ * performed in the foreground; actually hitting the hard limit requires
+ * foreground undo to be interrupted by a crash.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "lib/rbtree.h"
+#include "storage/shmem.h"
+#include "utils/timestamp.h"
+
+/*
+ * An UndoRequest represents the possible need to perform undo actions for
+ * a transaction if it aborts; thus, it should be allocated before writing
+ * undo that might require the system to perform cleanup actions (except
+ * temporary undo, for which the backend is always responsible) and
+ * deallocated when it is clear that no such actions will need to be
+ * performed or when they have all been performed successfully.
+ */
+struct UndoRequest
+{
+	FullTransactionId	fxid;
+	Oid			dbid;
+	Size		size;
+	UndoRecPtr	start_location_logged;
+	UndoRecPtr	end_location_logged;
+	UndoRecPtr	start_location_unlogged;
+	UndoRecPtr	end_location_unlogged;
+	TimestampTz	retry_time;
+};
+
+/*
+ * An UndoRequestNode just points to an UndoRequest. We use it so that the
+ * same UndoRequest can be placed into more than one RBTree at the same
+ * time.
+ */
+typedef struct UndoRequestNode
+{
+	RBTNode		rbtnode;
+	UndoRequest *req;
+} UndoRequestNode;
+
+/*
+ * Possible sources of UndoRequest objects in need of processing.
+ */
+typedef enum UndoRequestSource
+{
+	UNDO_SOURCE_FXID,
+	UNDO_SOURCE_SIZE,
+	UNDO_SOURCE_RETRY_TIME
+} UndoRequestSource;
+
+/*
+ * An UndoRequestManager manages a collection of UndoRequest and
+ * UndoRequestNode objects. Typically, there would only be one such object
+ * for the whole system, but it's possible to create others for testing
+ * purposes.
+ */
+struct UndoRequestManager
+{
+	LWLock	   *lock;
+	Size		capacity;
+	Size		utilization;
+	Size		soft_size_limit;
+	UndoRequestSource	source;
+	RBTree		requests_by_fxid;
+	RBTree		requests_by_size;
+	RBTree		requests_by_retry_time;
+	UndoRequest *first_free_request;
+	UndoRequestNode *first_free_request_node;
+};
+
+/* Static functions. */
+static UndoRequest *FindUndoRequestForDatabase(UndoRequestManager *urm,
+											   Oid dbid);
+static bool BackgroundUndoOK(UndoRequestManager *urm,
+							 UndoRequest *req);
+static RBTNode *UndoRequestNodeAllocate(void *arg);
+static void UndoRequestNodeFree(RBTNode *x, void *arg);
+static void UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata,
+								   void *arg);
+static int UndoRequestNodeCompareRetryTime(const RBTNode *a,
+										   const RBTNode *b,
+										   void *arg);
+static int UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b,
+									  void *arg);
+static int UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b,
+									  void *arg);
+static void InsertUndoRequest(RBTree *rbt, UndoRequest *req);
+static void RemoveUndoRequest(RBTree *rbt, UndoRequest *req);
+static UndoRequest *FindUndoRequest(UndoRequestManager *urm,
+									FullTransactionId fxid);
+
+/*
+ * Compute the amount of space that will be needed by an undo request manager.
+ *
+ * We need space for the UndoRequestManager itself, for the UndoRequests, and
+ * for the UndoRequestNode objects.  We need twice as many UndoRequestNode
+ * objects as we do UndoRequest objects, because unfailed requests are stored
+ * in both requests_by_fxid and requests_by_size; failed requests are stored
+ * only in requests_by_retry_time.
+ */
+Size
+EstimateUndoRequestManagerSize(Size capacity)
+{
+	Size	s = MAXALIGN(sizeof(UndoRequestManager));
+
+	s = add_size(s, MAXALIGN(mul_size(capacity, sizeof(UndoRequest))));
+	s = add_size(s, MAXALIGN(mul_size(capacity,
+									  mul_size(2, sizeof(UndoRequestNode)))));
+
+	return s;
+}
+
+/*
+ * Initialize an undo request manager.
+ *
+ * The caller is responsible for providing an appropriately-sized chunk of
+ * memory; use EstimateUndoRequestManagerSize to find out how much space will
+ * be needed. This means that this infrastructure can potentially be used in
+ * either shared memory or, if desired, in backend-private memory. It will not
+ * work in DSM, though, because it uses pointers.
+ *
+ * The caller must also provide a lock that will be used to protect access
+ * to the data managed by this undo request manager.  This cannot be NULL,
+ * even if the memory is private.
+ */
+void
+InitializeUndoRequestManager(UndoRequestManager *urm, LWLock *lock,
+							 Size capacity, Size soft_limit)
+{
+	UndoRequest *reqs;
+	UndoRequestNode *nodes;
+	int		i;
+
+	/* Basic initialization. */
+	urm->lock = lock;
+	urm->capacity = capacity;
+	urm->utilization = 0;
+	urm->soft_size_limit = soft_limit;
+	urm->source = UNDO_SOURCE_FXID;
+	rbt_initialize(&urm->requests_by_fxid, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareFXID, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_size, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareSize, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_retry_time, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareRetryTime, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+
+	/* Find memory for UndoRequest and UndoRequestNode arenas. */
+	reqs = (UndoRequest *)
+		(((char *) urm) + MAXALIGN(sizeof(UndoRequestManager)));
+	nodes = (UndoRequestNode *)
+		(((char *) reqs) + MAXALIGN(capacity * sizeof(UndoRequest)));
+
+	/*
+	 * Build a free list of UndoRequest objects. We use the first few bytes
+	 * of the free object to store a pointer to the next free object.
+	 */
+	StaticAssertStmt(sizeof(UndoRequest) >= sizeof(UndoRequest *),
+					 "UndoRequest is too small");
+	urm->first_free_request = reqs;
+	for (i = 0; i < capacity - 1; ++i)
+	{
+		UndoRequest *current = &reqs[i];
+		UndoRequest *next = &reqs[i + 1];
+
+		*(UndoRequest **) current = next;
+	}
+	*(UndoRequest **) &reqs[capacity - 1] = NULL;
+
+	/* Similarly, build a free list of UndoRequestNode objects. */
+	StaticAssertStmt(sizeof(UndoRequestNode) >= sizeof(UndoRequestNode *),
+					 "UndoRequestNode is too small");
+	urm->first_free_request_node = nodes;
+	for (i = 0; i < 2 * capacity - 1; ++i)
+	{
+		UndoRequestNode *current = &nodes[i];
+		UndoRequestNode *next = &nodes[i + 1];
+
+		*(UndoRequestNode **) current = next;
+	}
+	*(UndoRequestNode **) &nodes[2 * capacity - 1] = NULL;
+}
+
+/*
+ * Register a new undo request. If unable, returns NULL.
+ *
+ * Note that we don't add the undo request to any RBTree at this point
+ * and that there is no mechanism for garbage collecting an entry that is
+ * leaked! The caller must be sure to call either UnregisterUndoRequest
+ * (if the transaction commits) or FinalizeUndoRequest (if it aborts).
+ */
+UndoRequest *
+RegisterUndoRequest(UndoRequestManager *urm, FullTransactionId fxid, Oid dbid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	req = urm->first_free_request;
+	if (req != NULL)
+	{
+		/* Pop free list. */
+		urm->first_free_request = *(UndoRequest **) req;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = 0;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->end_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->end_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+	}
+
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Set the start location for either logged or unlogged undo.
+ *
+ * We don't need a lock here, because it's only legal to call this after
+ * calling RegisterUndoRequest and before calling UnregisterUndoRequest
+ * or FinalizeUndoRequest. During that window, no other process can be
+ * accessing the UndoRequest we allocated.
+ */
+void
+UndoRequestSetStartLocation(UndoRequestManager *urm, UndoRequest *req,
+							bool is_logged, UndoRecPtr start_location)
+{
+	Assert(UndoRecPtrIsValid(start_location));
+	if (is_logged)
+		req->start_location_logged = start_location;
+	else
+		req->start_location_unlogged = start_location;
+}
+
+/*
+ * Finalize details for an undo request.
+ *
+ * We don't need a lock here for the same reasons as in UndoRequestSetLocation.
+ *
+ * Since an UndoRequest should be registered before beginning to write undo,
+ * the undo size won't be known at that point; this function should be getting
+ * called at prepare time for a prepared transaction, or at abort time
+ * otherwise, by which point the size should be known.
+ *
+ * Caller should report the total size of generated undo in bytes, counting
+ * only logged and unlogged undo that will be processed by background workers.
+ * Any undo bytes that aren't part of the logged or unlogged undo records
+ * that may need cleanup actions performed should not be included in size;
+ * for example, temporary undo doesn't count, as the caller must deal with
+ * that outside of this mechanism.
+ *
+ * Caller must also pass the end location for logged and unlogged undo;
+ * each should be if InvalidUndoRecPtr if and only if the corresponding
+ * start location was never set.
+ */
+void
+FinalizeUndoRequest(UndoRequestManager *urm, UndoRequest *req, Size size,
+					UndoRecPtr end_location_logged,
+					UndoRecPtr end_location_unlogged)
+{
+	Assert(size != 0);
+	Assert(UndoRecPtrIsValid(end_location_logged) ||
+		   UndoRecPtrIsValid(end_location_unlogged));
+	Assert(UndoRecPtrIsValid(end_location_logged) ==
+		   UndoRecPtrIsValid(req->start_location_logged));
+	Assert(UndoRecPtrIsValid(end_location_unlogged) ==
+		   UndoRecPtrIsValid(req->start_location_unlogged));
+	req->size = size;
+	req->end_location_logged = end_location_logged;
+	req->end_location_unlogged = end_location_unlogged;
+}
+
+/*
+ * Release a previously-allocated undo request.
+ *
+ * This can be used either on an UndoRequest returned by RegisterUndoRequest
+ * (in the case where the transaction commits, or where it aborts without
+ * actually generating any undo) or on one returned by GetNextUndoRequest
+ * (after any undo actions have been succesfully executed).
+ *
+ * Because this function may be called as a post-commit step, it must never
+ * throw an ERROR.
+ */
+void
+UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * Remove the UndoRequest from any RBTree that contains it.  If the
+	 * retry time is not DT_NOBEGIN, then the request has been finalized
+	 * and undo has subsequently failed.  If the size is 0, the request has
+	 * not been finalized yet, so it's not in any RBTree.
+	 */
+	if (req->retry_time != DT_NOBEGIN)
+		RemoveUndoRequest(&urm->requests_by_retry_time, req);
+	else if (req->size != 0)
+	{
+		RemoveUndoRequest(&urm->requests_by_fxid, req);
+		RemoveUndoRequest(&urm->requests_by_size, req);
+	}
+
+	/* Push onto freelist. */
+	*(UndoRequest **) req = urm->first_free_request;
+	urm->first_free_request = req;
+
+	/* Decrease utilization. */
+	--urm->utilization;
+
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Try to hand an undo request off for background processing.
+ *
+ * If this function returns true, the UndoRequest can be left for background
+ * processing; the caller need not do anything more. If this function returns
+ * false, the caller should try to process it in the foreground, and must
+ * call either UnregisterUndoRequest on success or RescheduleUndoRequest
+ * on failure.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ */
+bool
+PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req)
+{
+	bool	background;
+
+	/*
+	 * If we failed after allocating an UndoRequest but before setting any
+	 * start locations, there's no work to be done.  In that case, we can
+	 * just unregister the request. Note that no concurrent access to our
+	 * UndoRequest is possible at this stage, since it is not on the freelist
+	 * or accessible via any RBTree.
+	 */
+	if (!UndoRecPtrIsValid(req->start_location_logged) &&
+		!UndoRecPtrIsValid(req->start_location_unlogged))
+	{
+		UnregisterUndoRequest(urm, req);
+		return true;
+	}
+
+	/*
+	 * We need to check shared state in order to determine whether or not
+	 * to perform this undo in the background, and if we are going to perform
+	 * it in the background, also to add it to requests_by_fxid and
+	 * requests_by_size.
+	 */
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	background = BackgroundUndoOK(urm, req);
+	if (background)
+	{
+		/*
+		 * We're going to handle this in the background, so add it to
+		 * requests_by_fxid and requests_by_size, so that GetNextUndoRequest
+		 * can find it.
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	LWLockRelease(urm->lock);
+
+	return background;
+}
+
+/*
+ * Get an undo request that needs background processing.
+ *
+ * Unless dbid is InvalidOid, any request returned must be from the indicated
+ * database.  If minimum_runtime_reached is true, the caller only wants to
+ * process another request if the next request happens to be from the correct
+ * database. If it's false, the caller wants to avoiding exiting too quickly,
+ * and would like to process a request from the database if there's one
+ * available.
+ *
+ * If no suitable request is found, *fxid gets InvalidFullTransactionId;
+ * otherwise, *fxid gets the FullTransactionId of the transaction and
+ * the parameters which follow get the start and end locations of logged
+ * and unlogged undo for that transaction.  It's possible that the transaction
+ * wrote only logged undo or only unlogged undo, in which case the other
+ * pair fields will have a value of InvalidUndoRecPtr, but it should never
+ * happen that all of the fields get InvalidUndoRecPtr, because that would
+ * mean we queued up an UndoRequest to do nothing.
+ *
+ * This function, as a side effect, removes the returned UndoRequest from
+ * any RBTree that contains it, so that no other backend will attempt to
+ * process it simultaneously. The caller must be certain to call either
+ * UnregisterUndoRequest (if successful) or RescheduleUndoRequest (on
+ * failure) to avoid leaking the UndoRequest.
+ */
+UndoRequest *
+GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+				   bool minimum_runtime_reached,
+				   FullTransactionId *fxid,
+				   UndoRecPtr *start_location_logged,
+				   UndoRecPtr *end_location_logged,
+				   UndoRecPtr *start_location_unlogged,
+				   UndoRecPtr *end_location_unlogged)
+{
+	UndoRequest *req = NULL;
+	int 	nloops;
+	bool	saw_db_mismatch = false;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/* Some might have no work, so loop until all are checked. */
+	for (nloops = 0; nloops < 3; ++nloops)
+	{
+		RBTree *rbt;
+		UndoRequestSource source = urm->source;
+		UndoRequestNode *node;
+
+		/*
+		 * We rotate between the three possible sources of UndoRequest objects.
+		 *
+		 * The idea here is that processing the requests with the oldest
+		 * transaction IDs is important because it helps us discard undo log
+		 * data sooner and because it allows XID horizons to advance. On
+		 * the other hand, handling transactions that generated a very large
+		 * amount of undo is also a priority, because undo will probably
+		 * take a long to finish and thus should be started as early as
+		 * possible and also because it likely touched a large number of
+		 * pages which will be slow to access until the undo is processed.
+		 *
+		 * However, we also need to make sure to periodically retry undo for
+		 * transactions that previously failed. We hope that this will be very
+		 * rare, but if it does happen we can neither affort to retry those
+		 * transactions over and over in preference to all others, nor on the
+		 * other hand to just ignore them forever.
+		 *
+		 * We could try to come up with some scoring system that assigns
+		 * relative levels of importance to FullTransactionId age, undo size,
+		 * and retry time, but it seems difficult to come up with a weighting
+		 * system that can ensure that nothing gets starved. By rotating among
+		 * the sources evenly, we know that as long as we continue to process
+		 * undo requests on some sort of regular basis, each source will get
+		 * some amount of attention.
+		 */
+		switch (source)
+		{
+			case UNDO_SOURCE_FXID:
+				rbt = &urm->requests_by_fxid;
+				urm->source = UNDO_SOURCE_SIZE;
+				break;
+			case UNDO_SOURCE_SIZE:
+				rbt = &urm->requests_by_size;
+				urm->source = UNDO_SOURCE_RETRY_TIME;
+				break;
+			case UNDO_SOURCE_RETRY_TIME:
+				rbt = &urm->requests_by_retry_time;
+				urm->source = UNDO_SOURCE_FXID;
+				break;
+		}
+
+		/* Get highest-priority item. */
+		node = (UndoRequestNode *) rbt_leftmost(rbt);
+		if (node == NULL)
+			continue;
+
+		/*
+		 * We can only take an item from the retry time RBTree if the retry
+		 * time is in the past.
+		 */
+		if (source == UNDO_SOURCE_RETRY_TIME &&
+			node->req->retry_time > GetCurrentTimestamp())
+			continue;
+
+		/*
+		 * If a database OID was specified, it must match. If it does not,
+		 * we go ahead and try any remaining RBTree.  Note that this needs to
+		 * be after the other tests so that we get the right value for the
+		 * saw_db_mismatch flag.
+		 */
+		if (OidIsValid(dbid) && node->req->dbid != dbid)
+		{
+			saw_db_mismatch = true;
+			continue;
+		}
+
+		/* Looks like we have a winner. */
+		req = node->req;
+		break;
+	}
+
+	/*
+	 * Determine whether we should do a more exhaustive search.
+	 *
+	 * If we found a node, we don't need look any harder.  If we didn't
+	 * see a database mismatch, then looking harder can't help: there's nothing
+	 * to do at all, never mind for which database.  If the caller set
+	 * minimum_runtime_reached, then they don't want us to look harder.
+	 */
+	if (req == NULL && saw_db_mismatch && !minimum_runtime_reached)
+		req = FindUndoRequestForDatabase(urm, dbid);
+
+	/*
+	 * If we found a suitable request, remove it from any RBTree that
+	 * contains it.
+	 */
+	if (req != NULL)
+	{
+		if (req->retry_time != DT_NOBEGIN)
+			RemoveUndoRequest(&urm->requests_by_retry_time, req);
+		else
+		{
+			RemoveUndoRequest(&urm->requests_by_fxid, req);
+			RemoveUndoRequest(&urm->requests_by_size, req);
+		}
+	}
+
+	LWLockRelease(urm->lock);
+
+	/*
+	 * Set output parameters.  We've already removed the UndoRequest from any
+	 * RBTree that contained it, so it's safe to do this without the lock.
+	 */
+	if (req == NULL)
+		*fxid = InvalidFullTransactionId;
+	else
+	{
+		*fxid = req->fxid;
+		*start_location_logged = req->start_location_logged;
+		*end_location_logged = req->end_location_logged;
+		*start_location_unlogged = req->start_location_unlogged;
+		*end_location_unlogged = req->end_location_unlogged;
+	}
+
+	/* All done. */
+	return req;
+}
+
+/*
+ * Reschedule an undo request after undo failure.
+ *
+ * This function should be called when undo processing fails, either in the
+ * foreground or in the background.  The foreground case occurs when
+ * FinalizeUndoRequest returns false and undo then also fails; the background
+ * case occurs when GetNextUndoRequest returns an UndoRequest and undo then
+ * fails. Note that this function isn't used after a shutdown or crash: see
+ * comments in RecreateUndoRequest for how we handle that case.
+ *
+ * In either of the cases where this function is reached, the UndoRequest
+ * should not be in any RBTree. If it's a foreground undo failure, it's never
+ * been added; if it's a background undo failure, it was removed by
+ * GetNextUndoRequest. So, we don't have to remove the request from anywhere,
+ * not even conditionally; we just need to add it to the set of failed
+ * requests.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ */
+void
+RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * This algorithm for determining the next retry time is fairly
+	 * unsophisticated: the first retry happens after 10 seconds, and each
+	 * subsequent retry after 30 seconds. We could do something more
+	 * complicated here, but we'd need to do more bookkeeping and it's
+	 * unclear what we'd gain.
+	 */
+	if (req->retry_time == DT_NOBEGIN)
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 10 * 1000);
+	else
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 30 * 1000);
+
+	InsertUndoRequest(&urm->requests_by_retry_time, req);
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Recreate UndoRequest state after a shutdown.
+ *
+ * This function is expected to be called after a shutdown, whether a clean
+ * shutdown or a crash, both for aborted transactions with unprocessed undo
+ * and also for prepared transactions.  All calls to this function must be
+ * completed, and SuspendPreparedUndoRequest must be called for every prepared
+ * transaction, before the first call to GetNextUndoRequest occurs.
+ *
+ * This function be called up two twice per FullTransactionId, once with
+ * is_logged true and once with is_logged false, because the transaction may
+ * have both logged and unlogged undo in different places. start_location is
+ * the beginning of the type of undo indicated by the is_logged parameter, and
+ * size is the amount of such undo in bytes.  If this function is called twice,
+ * the result will be a single UndoRequest containing both start locations and
+ * a size which is the sum of the two sizes passed to the separate calls.
+ *
+ * If this function is unable to allocate a new UndoRequest when required,
+ * it will return false.  If that happens, it's not safe to continue using
+ * this UndoRequestManager and a system-wide shutdown to raise the limit on
+ * the number of outstanding requests is indicated.
+ */
+bool
+RecreateUndoRequest(UndoRequestManager *urm, FullTransactionId fxid,
+					Oid dbid, bool is_logged, UndoRecPtr start_location,
+					UndoRecPtr end_location, Size size)
+{
+	UndoRequest *req;
+
+	Assert(UndoRecPtrIsValid(start_location));
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	if (req)
+	{
+		/* Already called for opposite value of is_logged. */
+		if (is_logged)
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_logged));
+			Assert(!UndoRecPtrIsValid(req->end_location_logged));
+			req->start_location_logged = start_location;
+			req->end_location_logged = end_location;
+		}
+		else
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_unlogged));
+			Assert(!UndoRecPtrIsValid(req->end_location_unlogged));
+			req->start_location_unlogged = start_location;
+			req->end_location_unlogged = end_location;
+		}
+		Assert(req->dbid == dbid);
+
+		/* Adjusting size may change position in RBTree. */
+		RemoveUndoRequest(&urm->requests_by_size, req);
+		req->size += size;
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	else
+	{
+		/* First call for this FullTransactionId. */
+		req = urm->first_free_request;
+		if (req == NULL)
+		{
+			LWLockRelease(urm->lock);
+			return false;
+		}
+
+		/* We got an item; pop it from the free list. */
+		urm->first_free_request = *(UndoRequest **) req;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = size;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+		if (is_logged)
+			req->start_location_logged = start_location;
+		else
+			req->start_location_unlogged = start_location;
+
+		/*
+		 * Put it where undo workers will see it.  Note that we assume that
+		 * these are new aborts, but it's possible that there are actually
+		 * a whole series of previous undo failures before the shutdown or
+		 * crash. If we had the information about whether this request had
+		 * failed previously, we could set req->retry_time and insert it into
+		 * requests_by_retry_time rather than requests_by_fxid and
+		 * requests_by_size, but it doesn't seem important to retain
+		 * information about undo failure across crashes or shutdowns, because
+		 * we're just trying to guarantee that we don't busy-loop or starve
+		 * other requests. (FindUndoRequest would get confused, too.)
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+
+	LWLockRelease(urm->lock);
+	return true;
+}
+
+/*
+ * Adjust UndoRequestManager state for prepared transactions.
+ *
+ * After a restart, once all calls to RecreateUndoRequest have been completed
+ * and before the first call to GetNextUndoRequest, this function should
+ * be called for each prepared transaction. That's necessary to avoid
+ * prematurely executed undo actions for transactions that haven't aborted
+ * yet and might go on to commit. It removes the entries for the transaction
+ * from each RBTree so that GetNextUndoRequest does not find them.
+ *
+ * The caller should retain a pointer to the returned UndoRequest and, when
+ * the prepared transaction is eventually committed or rolled back, should
+ * invoke UnregisterUndoRequest on commit or FinalizeUndoRequest on abort.
+ */
+UndoRequest *
+SuspendPreparedUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	Assert(req != NULL);
+	Assert(req->size != 0);
+	RemoveUndoRequest(&urm->requests_by_fxid, req);
+	RemoveUndoRequest(&urm->requests_by_size, req);
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Perform a left-to-right search of all three RBTrees, looking for a request
+ * for a given database. The searches are interleaved so that we latch
+ * onto the highest-priority request in any RBTree.
+ *
+ * It's possible that we should have some kind of limit on this search, so
+ * that it doesn't do an exhaustive search of every RBTree. However, it's not
+ * exactly clear how that would affect the behavior, or how to pick a
+ * reasonable limit.
+ */
+static UndoRequest *
+FindUndoRequestForDatabase(UndoRequestManager *urm, Oid dbid)
+{
+	RBTreeIterator	iter[3];
+	int		doneflags = 0;
+	int		i = 0;
+
+	rbt_begin_iterate(&urm->requests_by_fxid, LeftRightWalk, &iter[0]);
+	rbt_begin_iterate(&urm->requests_by_size, LeftRightWalk, &iter[1]);
+	rbt_begin_iterate(&urm->requests_by_retry_time, LeftRightWalk, &iter[2]);
+
+	while (1)
+	{
+		UndoRequestNode *node;
+
+		if ((doneflags & (1 << i)) == 0)
+		{
+			node = (UndoRequestNode *) rbt_iterate(&iter[i]);
+			if (node == NULL)
+			{
+				doneflags |= 1 << i;
+				if (doneflags == 7)		/* all bits set */
+					break;
+			}
+			else if (node->req->dbid == dbid)
+				return node->req;
+		}
+		i = (i + 1) % 3;
+	}
+
+	return NULL;
+}
+
+/*
+ * Is it OK to handle this UndoRequest in the background?
+ */
+static bool
+BackgroundUndoOK(UndoRequestManager *urm, UndoRequest *req)
+{
+	/*
+	 * If we've passed the soft size limit, it's not OK to background it.
+	 */
+	if (urm->utilization > urm->soft_size_limit)
+		return false;
+
+	/*
+	 * Otherwise, allow it.
+	 *
+	 * TODO: We probably want to introduce some additional rules here based
+	 * on the size of the request.
+	 */
+	return true;
+}
+
+/*
+ * RBTree callback to allocate an UndoRequestNode.
+ *
+ * Everything is preallocated, so we're just popping the freelist.
+ */
+static RBTNode *
+UndoRequestNodeAllocate(void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = urm->first_free_request_node;
+
+	/*
+	 * Any given UndoRequest should either be in both requests_by_fxid
+	 * and requests_by_size, or it should be in requests_by_retry_time,
+	 * or it should be in neither RBTree; consequently, it should be impossible
+	 * to use more than 2 UndoRequestNode objects per UndoRequest. Since we
+	 * preallocate that number, we should never run out. In case there's a
+	 * bug in the logic, let's insert a runtime check here even when Asserts
+	 * are disabled.
+	 */
+	if (node == NULL)
+		elog(ERROR, "no free UndoRequestNode");
+
+	/* Pop freelist. */
+	urm->first_free_request_node = *(UndoRequestNode **) node;
+
+	return &node->rbtnode;
+}
+
+/*
+ * RBTree callback to free an UndoRequestNode.
+ *
+ * Just put it back on the freelist.
+ */
+static void
+UndoRequestNodeFree(RBTNode *x, void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = (UndoRequestNode *) x;
+
+	*(UndoRequestNode **) node = urm->first_free_request_node;
+	urm->first_free_request_node = node;
+}
+
+/*
+ * RBTree callback to combine an UndoRequestNode with another one.
+ *
+ * The key for every RBTree includes the FXID, which is unique, so it should
+ * never happen that we need to merge requests.
+ */
+static void
+UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata, void *arg)
+{
+	elog(ERROR, "undo requests should never need to be combined");
+}
+
+/*
+ * RBTree comparator for requests_by_retry_time. Older retry
+ * times first; in the case of a tie, smaller FXIDs first.  This avoids ties,
+ * which is important since we don't want to merge requests, and also favors
+ * retiring older transactions first, which is generally desirable.
+ */
+static int
+UndoRequestNodeCompareRetryTime(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	TimestampTz retry_time_a = aa->req->retry_time;
+	TimestampTz retry_time_b = bb->req->retry_time;
+
+	if (retry_time_a != retry_time_b)
+		return retry_time_a < retry_time_b ? -1 : 1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. Lower FXIDs first. No tiebreak,
+ * because FXIDs should be unique.
+ */
+static int
+UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. As in we do for the retry
+ * time RBTree, break ties in favor of lower FXIDs.
+ */
+static int
+UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	Size size_a = aa->req->size;
+	Size size_b = bb->req->size;
+
+	if (size_a != size_b)
+		return size_a < size_b ? 1 : -1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Insert an UndoRequest into one RBTree.
+ *
+ * The actual RBTree element is an UndoRequestNode, which just points to
+ * the actual UndoRequest.
+ */
+static void
+InsertUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode	dummy;
+	bool isNew;
+
+	/*
+	 * The rbt_insert interface is a bit strange: we have to pass something
+	 * that looks like an RBTNode, but the RBTNode itself doesn't need to
+	 * be initialized - only the "extra" data that follows the end of the
+	 * structure needs to be correct.
+	 */
+	dummy.req = req;
+	rbt_insert(rbt, &dummy.rbtnode, &isNew);
+	Assert(isNew);
+}
+
+/*
+ * Remove an UndoRequest from one RBTree.
+ *
+ * This is just the reverse of InsertUndoRequest, with the same interface
+ * quirk.
+ */
+static void
+RemoveUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode	dummy;
+	RBTNode *node;
+
+	dummy.req = req;
+	node = rbt_find(rbt, &dummy.rbtnode);
+	rbt_delete(rbt, node);
+}
+
+/*
+ * Find an UndoRequest by FXID.
+ *
+ * If we needed to do this frequently, it might be worth maintaining a hash
+ * table mapping FXID -> UndoRequest, but since we only need it after a system
+ * restart, RBTree's O(lg n) performance seems good enough.
+ *
+ * Note that this can only find an UndoRequest that has not failed and is not
+ * yet being processed, because a failed UndoRequest would be in
+ * requests_by_retry_time, not requests_by_fxid, and an in-progress
+ * UndoRequest wouldn't be in either data structure. That restriction, too,
+ * is OK for current uses.
+ */
+static UndoRequest *
+FindUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest	dummy_request;
+	UndoRequestNode	dummy_node;
+	RBTNode *node;
+
+	/*
+	 * Here we need both a dummy UndoRequest and a dummy UndoRequestNode; only
+	 * the comparator will look at the dummy UndoRequestNode, and it will only
+	 * look at UndoRequest, and specifically its FXID.
+	 */
+	dummy_request.fxid = fxid;
+	dummy_node.req = &dummy_request;
+	node = rbt_find(&urm->requests_by_fxid, &dummy_node.rbtnode);
+	if (node == NULL)
+		return NULL;
+	return ((UndoRequestNode *) node)->req;
+}
diff --git a/src/backend/lib/rbtree.c b/src/backend/lib/rbtree.c
index 33181e9211..bda870eab7 100644
--- a/src/backend/lib/rbtree.c
+++ b/src/backend/lib/rbtree.c
@@ -35,25 +35,6 @@
 #define RBTBLACK	(0)
 #define RBTRED		(1)
 
-/*
- * RBTree control structure
- */
-struct RBTree
-{
-	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
-
-	/* Remaining fields are constant after rbt_create */
-
-	Size		node_size;		/* actual size of tree nodes */
-	/* The caller-supplied manipulation functions */
-	rbt_comparator comparator;
-	rbt_combiner combiner;
-	rbt_allocfunc allocfunc;
-	rbt_freefunc freefunc;
-	/* Passthrough arg passed to all manipulation functions */
-	void	   *arg;
-};
-
 /*
  * all leafs are sentinels, use customized NIL name to prevent
  * collision with system-wide constant NIL which is actually NULL
@@ -122,6 +103,33 @@ rbt_create(Size node_size,
 	return tree;
 }
 
+/*
+ * rbt_initialize: initalize an empty RBTree
+ *
+ * This is just like rbt_create, except that the caller is responsible for
+ * allocating the memory.
+ */
+void
+rbt_initialize(RBTree *rbt,
+			   Size node_size,
+			   rbt_comparator comparator,
+			   rbt_combiner combiner,
+			   rbt_allocfunc allocfunc,
+			   rbt_freefunc freefunc,
+			   void *arg)
+{
+	Assert(node_size > sizeof(RBTNode));
+
+	rbt->root = RBTNIL;
+	rbt->node_size = node_size;
+	rbt->comparator = comparator;
+	rbt->combiner = combiner;
+	rbt->allocfunc = allocfunc;
+	rbt->freefunc = freefunc;
+
+	rbt->arg = arg;
+}
+
 /* Copy the additional data fields from one RBTNode to another */
 static inline void
 rbt_copy_data(RBTree *rbt, RBTNode *dest, const RBTNode *src)
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000000..11324b4888
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,77 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *		Undo request manager.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOREQUEST_H
+#define UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "datatype/timestamp.h"
+
+struct UndoRequest;
+struct UndoRequestManager;
+typedef struct UndoRequest UndoRequest;
+typedef struct UndoRequestManager UndoRequestManager;
+
+/* Initialization functions. */
+extern Size EstimateUndoRequestManagerSize(Size capacity);
+extern void InitializeUndoRequestManager(UndoRequestManager *urm,
+										 LWLock *lock, Size capacity,
+										 Size soft_limit);
+
+/* Call this before inserting undo records. */
+extern UndoRequest *RegisterUndoRequest(UndoRequestManager *urm,
+										FullTransactionId fxid,
+										Oid dbid);
+
+/* Remember where our undo starts and ends. */
+extern void UndoRequestSetStartLocation(UndoRequestManager *urm,
+										UndoRequest *req,
+										bool is_logged,
+										UndoRecPtr start_location);
+
+/* Remember undo size and end locations. */
+extern void FinalizeUndoRequest(UndoRequestManager *urm,
+								UndoRequest *req,
+								Size size,
+								UndoRecPtr end_location_logged,
+								UndoRecPtr end_location_unlogged);
+
+/* Forget about an UndoRequest we don't need any more. */
+extern void UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Attempt to dispatch UndoRequest for background processing. */
+extern bool PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req);
+
+/* Get work for background undo process. */
+extern UndoRequest *GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+									   bool minimum_runtime_reached,
+									   FullTransactionId *fxid,
+									   UndoRecPtr *start_location_logged,
+									   UndoRecPtr *end_location_logged,
+									   UndoRecPtr *start_location_unlogged,
+									   UndoRecPtr *end_location_unlogged);
+
+/* Reschedule failed undo attempt. */
+extern void RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Restore state after crash. */
+extern bool RecreateUndoRequest(UndoRequestManager *urm,
+								FullTransactionId fxid, Oid dbid,
+								bool is_logged,
+								UndoRecPtr start_location,
+								UndoRecPtr end_location,
+								Size size);
+extern UndoRequest *SuspendPreparedUndoRequest(UndoRequestManager *urm,
+											   FullTransactionId fxid);
+
+#endif
diff --git a/src/include/lib/rbtree.h b/src/include/lib/rbtree.h
index 6d79a24015..ff6f99a932 100644
--- a/src/include/lib/rbtree.h
+++ b/src/include/lib/rbtree.h
@@ -28,8 +28,33 @@ typedef struct RBTNode
 	struct RBTNode *parent;		/* parent, or NULL (not RBTNIL!) if none */
 } RBTNode;
 
-/* Opaque struct representing a whole tree */
-typedef struct RBTree RBTree;
+/* Support functions to be provided by caller */
+typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
+typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
+typedef RBTNode *(*rbt_allocfunc) (void *arg);
+typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
+
+/*
+ * RBTree control structure
+ *
+ * This is declared here to make it possible to preallocate an object of
+ * the correct size, but callers should not access the members diretly.
+ */
+typedef struct RBTree
+{
+	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
+
+	/* Remaining fields are constant after rbt_create */
+
+	Size		node_size;		/* actual size of tree nodes */
+	/* The caller-supplied manipulation functions */
+	rbt_comparator comparator;
+	rbt_combiner combiner;
+	rbt_allocfunc allocfunc;
+	rbt_freefunc freefunc;
+	/* Passthrough arg passed to all manipulation functions */
+	void	   *arg;
+} RBTree;
 
 /* Available tree iteration orderings */
 typedef enum RBTOrderControl
@@ -53,18 +78,19 @@ struct RBTreeIterator
 	bool		is_over;
 };
 
-/* Support functions to be provided by caller */
-typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
-typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
-typedef RBTNode *(*rbt_allocfunc) (void *arg);
-typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
-
 extern RBTree *rbt_create(Size node_size,
 						  rbt_comparator comparator,
 						  rbt_combiner combiner,
 						  rbt_allocfunc allocfunc,
 						  rbt_freefunc freefunc,
 						  void *arg);
+extern void rbt_initialize(RBTree *rbt,
+						   Size node_size,
+						   rbt_comparator comparator,
+						   rbt_combiner combiner,
+						   rbt_allocfunc allocfunc,
+						   rbt_freefunc freefunc,
+						   void *arg);
 
 extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
 extern RBTNode *rbt_leftmost(RBTree *rbt);
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 60d6d7be1b..f32afffab1 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		  test_rbtree \
 		  test_rls_hooks \
 		  test_shm_mq \
+		  test_undo_request_manager \
 		  unsafe_tests \
 		  worker_spi
 
diff --git a/src/test/modules/test_undo_request_manager/Makefile b/src/test/modules/test_undo_request_manager/Makefile
new file mode 100644
index 0000000000..5bc4695004
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo_request_manager/Makefile
+
+MODULE_big = test_undo_request_manager
+OBJS = test_undo_request_manager.o $(WIN32RES)
+PGFILEDESC = "test_undo_request_manager - test undo request manager code"
+
+EXTENSION = test_undo_request_manager
+DATA = test_undo_request_manager--1.0.sql
+
+REGRESS = test_undo_request_manager
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_request_manager
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
new file mode 100644
index 0000000000..c79611b3b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
@@ -0,0 +1,28 @@
+CREATE EXTENSION test_undo_request_manager;
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+ERROR:  unable to register undo request #2
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+ urm_simple_test 
+-----------------
+ {1001,1002}
+(1 row)
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+                urm_simple_test                 
+------------------------------------------------
+ {1001,1006,1002,1007,1003,1008,1004,1009,1005}
+(1 row)
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
+                   urm_simple_test                   
+-----------------------------------------------------
+ {1001,1010,1002,1009,1003,1008,1004,1007,1005,1006}
+(1 row)
+
diff --git a/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
new file mode 100644
index 0000000000..6611e040b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
@@ -0,0 +1,16 @@
+CREATE EXTENSION test_undo_request_manager;
+
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
new file mode 100644
index 0000000000..30ff471c23
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
@@ -0,0 +1,9 @@
+/* src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_undo_request_manager" to load this file. \quit
+
+CREATE FUNCTION urm_simple_test(capacity pg_catalog.int4,
+								requests pg_catalog.int8[])
+    RETURNS pg_catalog.int8[] STRICT
+	AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.c b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
new file mode 100644
index 0000000000..8b994283c8
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
@@ -0,0 +1,139 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_undo_request_manager.c
+ *		Test undo request manager.
+ *
+ * Copyright (c) 2013-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/test_undo_request_manager/undo_request_manager.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "catalog/pg_type_d.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "utils/array.h"
+
+PG_MODULE_MAGIC;
+PG_FUNCTION_INFO_V1(urm_simple_test);
+
+/*
+ * SQL-callable test function.  We create an UndoRequestManager in
+ * backend-private memory here and exercise it a bit to see if it breaks.
+ *
+ * The first argument is the capacity of the UndoRequestManager as an integer.
+ *
+ * The second argument is 1-dimensional bigint array, where each subarray
+ * contains a hypothetical undo size.
+ *
+ * This function registers and inserts all the requests (failing if space is
+ * exhausted) with fake, sequentially assigned transaction IDs, and then
+ * fetches them back one by one. The return value is an array of fake
+ * transaction IDs in the order they were returned.
+ *
+ * This test doesn't simulate undo failure, multi-database operation, or
+ * prepared transactions.
+ */
+Datum
+urm_simple_test(PG_FUNCTION_ARGS)
+{
+	int64	capacity = PG_GETARG_INT32(0);
+	ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	  *darray;
+	int			nentries;
+	Datum	  *dresult;
+	ArrayType *result;
+	UndoRequestManager *urm;
+	const UndoRecPtr SomeValidUndoRecPtr = InvalidUndoRecPtr + 1;
+	int			i;
+	FullTransactionId fake_fxid = FullTransactionIdFromEpochAndXid(0, 1000);
+
+	/* Require positive capacity. */
+	if (capacity <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("undo request manager capacity must be a positive integer")));
+
+	/* Sanity-check and deconstruct array. */
+	if (ARR_NDIM(array) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("array must have exactly 1 dimension")));
+	if (array_contains_nulls(array))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("cannot work with arrays containing NULLs")));
+	deconstruct_array(array, INT8OID, 8, FLOAT8PASSBYVAL, 'd',
+					  &darray, NULL, &nentries);
+
+	/*
+	 * Initialize UndoRequestManager. We have to supply an LWLock; rather than
+	 * creating a new one somewhere, just use our own backendLock. These locks
+	 * aren't that heavily trafficked and we won't have any reason to take it
+	 * for any other purpose while the UndoRequstManager holds it, so this
+	 * should be safe enough.
+	 *
+	 * We make the soft limit equal to the full capacity here for testing
+	 * purposes, which means that we should always succeed in dispatching to
+	 * the background.
+	 */
+	urm = palloc(EstimateUndoRequestManagerSize(capacity));
+	InitializeUndoRequestManager(urm, &MyProc->backendLock,
+								 capacity, capacity);
+
+	/* Insert entries as provided by caller. */
+	for (i = 0; i < nentries; ++i)
+	{
+		int64	size = DatumGetInt64(darray[i]);
+		UndoRequest *req;
+
+		FullTransactionIdAdvance(&fake_fxid);
+
+		req = RegisterUndoRequest(urm, fake_fxid, MyDatabaseId);
+		if (req == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to register undo request #%d", i + 1)));
+		UndoRequestSetStartLocation(urm, req, true, SomeValidUndoRecPtr);
+		FinalizeUndoRequest(urm, req, size,
+							SomeValidUndoRecPtr,
+							InvalidUndoRecPtr);
+		if (!PerformUndoInBackground(urm, req))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to background undo request #%d", i + 1)));
+	}
+
+	/* Now get the entries back. */
+	dresult = palloc(nentries * sizeof(Datum));
+	for (i = 0; true; ++i)
+	{
+		UndoRequest *req;
+		UndoRecPtr	p[4];
+
+		/* Get some work. */
+		req = GetNextUndoRequest(urm, MyDatabaseId, true,
+								 &fake_fxid, &p[0], &p[1], &p[2], &p[3]);
+		if (req == NULL)
+			break;
+		if (i >= nentries)
+			elog(ERROR, "found more undo requests than were inserted");
+
+		/* Save the fake FXID. */
+		dresult[i] =
+			Int64GetDatum((int64) U64FromFullTransactionId(fake_fxid));
+
+		/* Report that we successfully processed the imaginary undo. */
+		UnregisterUndoRequest(urm, req);
+	}
+
+	/* Put result into array form. */
+	result = construct_array(dresult, i, INT8OID, 8, FLOAT8PASSBYVAL, 'd');
+	PG_RETURN_ARRAYTYPE_P(result);
+}
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.control b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
new file mode 100644
index 0000000000..0a340e9843
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
@@ -0,0 +1,4 @@
+comment = 'Test code for undo request manager'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_request_manager'
+relocatable = true
-- 
2.17.2 (Apple Git-113)

#223Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#222)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Aug 9, 2019 at 1:57 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Aug 8, 2019 at 9:31 AM Andres Freund <andres@anarazel.de> wrote:

I know that Robert is working on a patch that revises the undo request
layer somewhat, it's possible that this is best discussed afterwards.

Here's what I have at the moment. This is not by any means a complete
replacement for Amit's undo worker machinery, but it is a significant
redesign (and I believe a significant improvement to) the queue
management stuff from Amit's patch.

Thanks for working on this. Neither Kuntal nor I have got time to
look into this part in detail.

I wrote this pretty quickly, so
while it passes simple testing, it probably has a number of bugs, and
to actually use it, it would need to be integrated with xact.c;

I can look into this and integrate with other parts of the patch next
week unless you are planning to do. Right now, I am working on fixing
up some other comments raised on the patches which I will share today
or early next week after which I can start looking into this. I hope
that is fine with you.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#224Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#143)
13 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 3:51 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Mon, Jul 22, 2019 at 2:21 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

I have reviewed 0012-Infrastructure-to-execute-pending-undo-actions,
Please find my comment so far.

1.
+ /* It shouldn't be discarded. */
+ Assert(!UndoRecPtrIsDiscarded(xact_urp));

I think comments can be added to explain why it shouldn't be discarded.

2.
+ /* Compute the offset of the uur_next in the undo record. */
+ offset = SizeOfUndoRecordHeader +
+ offsetof(UndoRecordTransaction, urec_progress);
+
in comment /uur_next/uur_progress
3.
+/*
+ * undo_record_comparator
+ *
+ * qsort comparator to handle undo record for applying undo actions of the
+ * transaction.
+ */
Function header formating is not in sync with other functions.

Fixed all the above comments in the attached patch.

4.
+void
+undoaction_redo(XLogReaderState *record)
+{
+ uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+ switch (info)
+ {
+ case XLOG_UNDO_APPLY_PROGRESS:
+ undo_xlog_apply_progress(record);
+ break;

For HotStandby it doesn't make sense to apply this wal as this
progress is only required when we try to apply the undo action after
restart
but in HotStandby we never apply undo actions.

I have already responded in my earlier email on why this is required [1]/messages/by-id/CAA4eK1KoA0L=PNBc_uu2v8H0=LA_Cm=o9GyFm6i6DSD6mUMppg@mail.gmail.com.

5.
+ Assert(from_urecptr != InvalidUndoRecPtr);
+ Assert(to_urecptr != InvalidUndoRecPtr);

we can use macros UndoRecPtrIsValid instead of checking like this.

Fixed.

6.
+ if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+ slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+ Assert(slot != NULL);
We are passing missing_ok as false in UndoLogGetSlot.  But, not sure
why we are expecting that undo lot can not be dropped.  In multi-log
transaction it's possible
that the tablespace in which next undolog is there is already dropped?

Already responded on this in my earlier reply [1]/messages/by-id/CAA4eK1KoA0L=PNBc_uu2v8H0=LA_Cm=o9GyFm6i6DSD6mUMppg@mail.gmail.com.

7.
+ */
+ do
+ {
+ BlockNumber progress_block_num = InvalidBlockNumber;
+ int i;
+ int nrecords;
.....
+ */
+ if (!UndoRecPtrIsValid(urec_ptr))
+ break;
+ } while (true);

I think we can convert above loop to while(true) instead of do..while,
because there is no need for do while loop.

8.
+ if (last_urecinfo->uur->uur_info & UREC_INFO_LOGSWITCH)
+ {
+ UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;

IMHO, the caller of UndoFetchRecord should directly check
uur->uur_logswitch instead of uur_info & UREC_INFO_LOGSWITCH.
Actually, uur_info is internally set
for inserting the tuple and check there to know what to insert and
fetch but I think caller of UndoFetchRecord should directly rely on
the field because ideally all
the fields in UnpackUndoRecord must be set and uur_txt or
uur_logswitch will be allocated when those headers present. I think
this needs to be improved in undo interface patch
as well (in UndoBulkFetchRecord).

Okay, fixed both of the above. I have exposed a new macro
IsUndoLogSwitched from undorecord.h which you might also want to use
in your patch.

Apart from this, in the attached patches, I have fixed various
comments raised in this thread from Amit Khandekar. I'll respond to
them separately. I have yet to address various comments raised by
Andres and Robert which also includes integration with the latest
patch on queues posted by Robert.

Note - The patches for undo-log and undo-interface has not been
rebased as others are working actively on their branches. The branch
where this code resides can be accessed at
https://github.com/EnterpriseDB/zheap/tree/undoprocessing

[1]: /messages/by-id/CAA4eK1KoA0L=PNBc_uu2v8H0=LA_Cm=o9GyFm6i6DSD6mUMppg@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

Attachments:

0003-Add-undo-log-manager.patchapplication/octet-stream; name=0003-Add-undo-log-manager.patchDownload
From 3da534dc6c28b2b3127d7400ab524f12b9faa0ae Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 6 Mar 2019 16:46:04 +1300
Subject: [PATCH 03/13] Add undo log manager.

Add a new subsystem to manage undo logs.  Undo logs allow data to be appended
efficiently, like logs.  They also allow data to be discarded efficiently from
the other end, like a queue.  Thirdly, they allow efficient buffered random
access, like a relation.

Undo logs physically consist of a set of 1MB segment files under
$PGDATA/base/undo (or per-tablespace equivalent) that are created, deleted or
renamed as required, similarly to the way that WAL segments are managed.
Meta-data about the set of undo logs is stored in shared memory, and written
to per-checkpoint files under $PGDATA/pg_undo.

Provide access to the undo files managed by undolog.c through bufmgr.c.
A new SMGR implementation allows bufmgr.c to access files created by
undolog.c.

Author: Thomas Munro, with contributions from Dilip Kumar, Rafia Sabih,
        Robert Haas and Amit Kapila
Reviewed-by:
Discussion: https://postgr.es/m/CAEepm%3D2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ%40mail.gmail.com
---
 src/backend/access/Makefile               |    2 +-
 src/backend/access/rmgrdesc/Makefile      |    2 +-
 src/backend/access/rmgrdesc/undologdesc.c |   81 +
 src/backend/access/transam/rmgr.c         |    1 +
 src/backend/access/transam/xlog.c         |    5 +
 src/backend/access/transam/xlogutils.c    |   70 +-
 src/backend/access/undo/Makefile          |   17 +
 src/backend/access/undo/undolog.c         | 2627 +++++++++++++++++++++++++++++
 src/backend/bootstrap/bootstrap.c         |    3 +
 src/backend/catalog/system_views.sql      |    4 +
 src/backend/commands/tablespace.c         |   23 +
 src/backend/postmaster/pgstat.c           |   21 +
 src/backend/replication/basebackup.c      |   18 +-
 src/backend/replication/logical/decode.c  |    1 +
 src/backend/storage/buffer/bufmgr.c       |   80 +-
 src/backend/storage/buffer/localbuf.c     |   43 +
 src/backend/storage/file/fd.c             |    3 +-
 src/backend/storage/ipc/ipci.c            |    3 +
 src/backend/storage/lmgr/lwlock.c         |    2 +
 src/backend/storage/lmgr/lwlocknames.txt  |    1 +
 src/backend/storage/smgr/Makefile         |    2 +-
 src/backend/storage/smgr/smgr.c           |   23 +
 src/backend/storage/smgr/undofile.c       |  422 +++++
 src/backend/storage/sync/sync.c           |    6 +
 src/backend/utils/init/postinit.c         |    2 +
 src/backend/utils/misc/guc.c              |   12 +
 src/bin/initdb/initdb.c                   |    2 +
 src/bin/pg_checksums/pg_checksums.c       |   23 +-
 src/bin/pg_resetwal/pg_resetwal.c         |   76 +
 src/bin/pg_upgrade/Makefile               |    2 +-
 src/bin/pg_upgrade/check.c                |   43 +
 src/bin/pg_upgrade/controldata.c          |   25 +
 src/bin/pg_upgrade/exec.c                 |    4 +
 src/bin/pg_upgrade/pg_upgrade.c           |    2 +
 src/bin/pg_upgrade/pg_upgrade.h           |    5 +
 src/bin/pg_upgrade/undo.c                 |  292 ++++
 src/bin/pg_waldump/rmgrdesc.c             |    1 +
 src/include/access/rmgrlist.h             |    1 +
 src/include/access/session.h              |    7 +
 src/include/access/undolog.h              |  489 ++++++
 src/include/access/undolog_xlog.h         |   62 +
 src/include/access/xlogutils.h            |   14 +
 src/include/catalog/database_internal.h   |   21 +
 src/include/catalog/pg_proc.dat           |    7 +
 src/include/pgstat.h                      |    7 +
 src/include/storage/bufmgr.h              |   14 +-
 src/include/storage/fd.h                  |    1 +
 src/include/storage/lwlock.h              |    5 +-
 src/include/storage/smgr.h                |    3 +
 src/include/storage/sync.h                |    3 +-
 src/include/storage/undofile.h            |   59 +
 src/include/utils/guc.h                   |    2 +
 src/test/regress/expected/rules.out       |   10 +
 53 files changed, 4613 insertions(+), 41 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undologdesc.c
 create mode 100644 src/backend/access/undo/Makefile
 create mode 100644 src/backend/access/undo/undolog.c
 create mode 100644 src/backend/storage/smgr/undofile.c
 create mode 100644 src/bin/pg_upgrade/undo.c
 create mode 100644 src/include/access/undolog.h
 create mode 100644 src/include/access/undolog_xlog.h
 create mode 100644 src/include/catalog/database_internal.h
 create mode 100644 src/include/storage/undofile.h

diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0880e0a..bf6d3fa 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -9,6 +9,6 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
-			  table tablesample transam
+			  table tablesample transam undo
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 5514db1..91ad1ef 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,6 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undologdesc.c b/src/backend/access/rmgrdesc/undologdesc.c
new file mode 100644
index 0000000..f89fcb3
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undologdesc.c
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * undologdesc.c
+ *	  rmgr descriptor routines for access/undo/undolog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undologdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+
+void
+undolog_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDOLOG_CREATE)
+	{
+		xl_undolog_create *xlrec = (xl_undolog_create *) rec;
+
+		appendStringInfo(buf, "logno %u", xlrec->logno);
+	}
+	else if (info == XLOG_UNDOLOG_EXTEND)
+	{
+		xl_undolog_extend *xlrec = (xl_undolog_extend *) rec;
+
+		appendStringInfo(buf, "logno %u end " UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_DISCARD)
+	{
+		xl_undolog_discard *xlrec = (xl_undolog_discard *) rec;
+
+		appendStringInfo(buf, "logno %u discard " UndoLogOffsetFormat " end "
+						 UndoLogOffsetFormat,
+						 xlrec->logno, xlrec->discard, xlrec->end);
+	}
+	else if (info == XLOG_UNDOLOG_SWITCH)
+	{
+		xl_undolog_switch *xlrec = (xl_undolog_switch *) rec;
+
+		appendStringInfo(buf, "logno %u start " UndoLogOffsetFormat " last " UndoLogOffsetFormat,
+						 xlrec->logno,
+						 xlrec->prevlog_xact_start,
+						 xlrec->prevlog_last_urp);
+	}
+
+}
+
+const char *
+undolog_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			id = "CREATE";
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			id = "EXTEND";
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			id = "DISCARD";
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			id = "SWITCH";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 9368b56..8b05374 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b6c9353..5dbe485 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -31,6 +31,7 @@
 #include "access/transam.h"
 #include "access/tuptoaster.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "access/xloginsert.h"
@@ -6710,6 +6711,9 @@ StartupXLOG(void)
 	 */
 	restoreTwoPhaseData();
 
+	/* Recover undo log meta data corresponding to this checkpoint. */
+	StartupUndoLogs(ControlFile->checkPointCopy.redo);
+
 	lastFullPageWrites = checkPoint.fullPageWrites;
 
 	RedoRecPtr = XLogCtl->RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
@@ -8977,6 +8981,7 @@ static void
 CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 {
 	CheckPointCLOG();
+	CheckPointUndoLogs(checkPointRedo, ControlFile->checkPointCopy.redo);
 	CheckPointCommitTs();
 	CheckPointSUBTRANS();
 	CheckPointMultiXact();
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663b..c227c03 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -294,6 +294,65 @@ XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
 }
 
 /*
+ * Find the block ID of the first block that matches the given rnode forknum
+ * and blockno.  If blockno is InvalidBlockNumber, then match any block
+ * number.  Return true if found.
+ */
+bool
+XLogFindBlockId(XLogReaderState *record,
+				RelFileNode rnode,
+				ForkNumber forknum,
+				BlockNumber blockno,
+				uint8 *block_id)
+{
+	uint8	i;
+
+	for (i = 0; i <= record->max_block_id; ++i)
+	{
+		DecodedBkpBlock *block = &record->blocks[i];
+
+		if (block->in_use &&
+			RelFileNodeEquals(block->rnode, rnode) &&
+			block->forknum == forknum &&
+			(block->blkno == blockno || blockno == InvalidBlockNumber))
+		{
+			*block_id = i;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * If the caller doesn't know the the block_id, but does know the RelFileNode,
+ * forknum and block number, then we try to find it.
+ */
+XLogRedoAction
+XLogReadBufferForRedoBlock(XLogReaderState *record,
+						   RelFileNode rnode,
+						   ForkNumber forknum,
+						   BlockNumber blockno,
+						   ReadBufferMode mode,
+						   bool get_cleanup_lock,
+						   Buffer *buf)
+{
+	uint8  	block_id;
+
+	if (XLogFindBlockId(record, rnode, forknum, blockno, &block_id))
+		return XLogReadBufferForRedoExtended(record,
+											 block_id,
+											 mode,
+											 get_cleanup_lock,
+											 buf);
+
+	elog(ERROR, "failed to find block reference rel %u/%u/%u, forknum = %u, block = %u",
+		 rnode.spcNode, rnode.dbNode, rnode.relNode, forknum, blockno);
+
+	return BLK_NOTFOUND;	/* not reached */
+}
+
+/*
  * Pin and lock a buffer referenced by a WAL record, for the purpose of
  * re-initializing it.
  */
@@ -346,7 +405,8 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
 	 * Make sure that if the block is marked with WILL_INIT, the caller is
 	 * going to initialize it. And vice versa.
 	 */
-	zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
+	zeromode = (mode == RBM_ZERO || mode == RBM_ZERO_AND_LOCK ||
+				mode == RBM_ZERO_AND_CLEANUP_LOCK);
 	willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
 	if (willinit && !zeromode)
 		elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
@@ -462,7 +522,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 	{
 		/* page exists in file */
 		buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
-										   mode, NULL);
+										   mode, NULL, RELPERSISTENCE_PERMANENT);
 	}
 	else
 	{
@@ -487,7 +547,8 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 				ReleaseBuffer(buffer);
 			}
 			buffer = ReadBufferWithoutRelcache(rnode, forknum,
-											   P_NEW, mode, NULL);
+											   P_NEW, mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 		while (BufferGetBlockNumber(buffer) < blkno);
 		/* Handle the corner case that P_NEW returns non-consecutive pages */
@@ -497,7 +558,8 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
 			buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
-											   mode, NULL);
+											   mode, NULL,
+											   RELPERSISTENCE_PERMANENT);
 		}
 	}
 
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
new file mode 100644
index 0000000..219c696
--- /dev/null
+++ b/src/backend/access/undo/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/undo
+#
+# IDENTIFICATION
+#    src/backend/access/undo/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/undo
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = undolog.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
new file mode 100644
index 0000000..f2e0272
--- /dev/null
+++ b/src/backend/access/undo/undolog.c
@@ -0,0 +1,2627 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.c
+ *	  management of undo logs
+ *
+ * PostgreSQL undo log manager.  This module is responsible for managing the
+ * lifecycle of undo logs and their segment files, associating undo logs with
+ * backends, and allocating space within undo logs.
+ *
+ * For the code that reads and writes blocks of data, see undofile.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undolog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/session.h"
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogreader.h"
+#include "access/xlogutils.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablespace.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "pgstat.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "storage/standby.h"
+#include "storage/sync.h"
+#include "storage/undofile.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/varlena.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+/*
+ * Main control structure for undo log management in shared memory.
+ * UndoLogSlot objects are arranged in a fixed-size array, with no particular
+ * ordering.
+ */
+typedef struct UndoLogSharedData
+{
+	UndoLogNumber	free_lists[UndoLogCategories];
+	UndoLogNumber	low_logno;
+	UndoLogNumber	next_logno;
+	UndoLogNumber	nslots;
+	UndoLogSlot		slots[FLEXIBLE_ARRAY_MEMBER];
+} UndoLogSharedData;
+
+/* The shared memory region that all backends are attach to. */
+UndoLogSharedData *UndoLogShared;
+
+undologtable_hash *undologtable_cache;
+
+/* GUC variables */
+char	   *undo_tablespaces = NULL;
+
+static UndoLogSlot *find_undo_log_slot(UndoLogNumber logno, bool locked);
+static UndoLogSlot *allocate_undo_log_slot(void);
+static void free_undo_log_slot(UndoLogSlot *log);
+static void attach_undo_log(UndoLogCategory category, Oid tablespace);
+static void detach_current_undo_log(UndoLogCategory category, bool full);
+static void extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end);
+static void undo_log_before_exit(int code, Datum value);
+static void forget_undo_buffers(int logno, UndoLogOffset old_discard,
+								UndoLogOffset new_discard,
+								bool drop_tail);
+static bool choose_undo_tablespace(bool force_detach, Oid *oid);
+
+PG_FUNCTION_INFO_V1(pg_stat_get_undo_logs);
+
+/*
+ * How many undo logs can be active at a time?  This creates a theoretical
+ * maximum amount of undo data that can exist, but if we set it to a multiple
+ * of the maximum number of backends it will be a very high limit.
+ * Alternative designs involving demand paging or dynamic shared memory could
+ * remove this limit but would be complicated.
+ */
+static inline size_t
+UndoLogNumSlots(void)
+{
+	return MaxBackends * 4;
+}
+
+/*
+ * Return the amount of traditional shmem required for undo log management.
+ */
+Size
+UndoLogShmemSize(void)
+{
+	return sizeof(UndoLogSharedData) +
+		UndoLogNumSlots() * sizeof(UndoLogSlot);
+}
+
+/*
+ * Initialize the undo log subsystem.  Called in each backend.
+ */
+void
+UndoLogShmemInit(void)
+{
+	bool found;
+
+	UndoLogShared = (UndoLogSharedData *)
+		ShmemInitStruct("UndoLogShared", UndoLogShmemSize(), &found);
+
+	/* The postmaster initialized the shared memory state. */
+	if (!IsUnderPostmaster)
+	{
+		int		i;
+
+		Assert(!found);
+
+		/*
+		 * We start with no active undo logs.  StartUpUndoLogs() will recreate
+		 * the undo logs that were known at the last checkpoint.
+		 */
+		memset(UndoLogShared, 0, sizeof(*UndoLogShared));
+		UndoLogShared->nslots = UndoLogNumSlots();
+		for (i = 0; i < UndoLogCategories; ++i)
+			UndoLogShared->free_lists[i] = InvalidUndoLogNumber;
+		for (i = 0; i < UndoLogShared->nslots; ++i)
+		{
+			memset(&UndoLogShared->slots[i], 0, sizeof(UndoLogShared->slots[i]));
+			UndoLogShared->slots[i].logno = InvalidUndoLogNumber;
+			LWLockInitialize(&UndoLogShared->slots[i].mutex,
+							 LWTRANCHE_UNDOLOG);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
+							 LWTRANCHE_UNDODISCARD);
+		}
+	}
+	else
+		Assert(found);
+
+	/* All backends prepare their per-backend lookup table. */
+	undologtable_cache = undologtable_create(TopMemoryContext,
+											 UndoLogNumSlots(),
+											 NULL);
+}
+
+void
+UndoLogInit(void)
+{
+	before_shmem_exit(undo_log_before_exit, 0);
+}
+
+/*
+ * Figure out which directory holds an undo log based on tablespace.
+ */
+void
+UndoLogDirectory(Oid tablespace, char *dir)
+{
+	if (tablespace == DEFAULTTABLESPACE_OID ||
+		tablespace == InvalidOid)
+		snprintf(dir, MAXPGPATH, "base/undo");
+	else
+		snprintf(dir, MAXPGPATH, "pg_tblspc/%u/%s/undo",
+				 tablespace, TABLESPACE_VERSION_DIRECTORY);
+}
+
+/*
+ * Compute the pathname to use for an undo log segment file.
+ */
+void
+UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace, char *path)
+{
+	char		dir[MAXPGPATH];
+
+	/* Figure out which directory holds the segment, based on tablespace. */
+	UndoLogDirectory(tablespace, dir);
+
+	/*
+	 * Build the path from log number and offset.  The pathname is the
+	 * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+	 * period inserted between the components.
+	 */
+	snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+			 segno * UndoLogSegmentSize);
+}
+
+/*
+ * Iterate through the set of currently active logs.  Pass in NULL to get the
+ * first undo log.  NULL indicates the end of the set of logs.  The caller
+ * must lock the returned log before accessing its members, and must skip if
+ * logno is not valid.
+ */
+UndoLogSlot *
+UndoLogNextSlot(UndoLogSlot *slot)
+{
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+	for (;;)
+	{
+		/* Advance to the next log. */
+		if (slot == NULL)
+		{
+			/* Start at the beginning. */
+			slot = &UndoLogShared->slots[0];
+		}
+		else if (++slot == &UndoLogShared->slots[UndoLogShared->nslots])
+		{
+			/* Past the end. */
+			slot = NULL;
+			break;
+		}
+		/* Have we found a slot with a valid log? */
+		if (slot->logno != InvalidUndoLogNumber)
+			break;
+	}
+	LWLockRelease(UndoLogLock);
+
+	/* XXX: erm, which lock should the caller hold!? */
+	return slot;
+}
+
+/*
+ * Check if an undo log position has been discarded.  'pointer' must be an
+ * undo log pointer that was allocated at some point in the past, otherwise
+ * the result is undefined.
+ */
+bool
+UndoLogRecPtrIsDiscardedSlowPath(UndoRecPtr pointer)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(pointer);
+	UndoLogSlot *slot;
+	UndoRecPtr discard;
+
+	slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL)
+	{
+		/*
+		 * If we couldn't find the undo log number, then it must be entirely
+		 * discarded.  Set this backend's recent_discard value to the highest
+		 * possible value, so that all records appear to be discarded to the
+		 * fast-path code.  Technically this value is too low by 1, but
+		 * assuming only pointers to records are tested, and no record can
+		 * have size 1, this value suffices.
+		 */
+		discard = MakeUndoRecPtr(logno, UndoLogMaxSize - 1);
+	}
+	else
+	{
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if (unlikely(logno != slot->logno))
+		{
+			/*
+			 * The undo log has been entirely discarded since we looked it up
+			 * above, and the UndoLogSlot is now unused or being used for some
+			 * other undo log.  This is the same as not finding it.
+			 */
+			discard = MakeUndoRecPtr(logno, UndoLogMaxSize - 1);
+		}
+		else
+			discard = MakeUndoRecPtr(logno, slot->meta.discard);
+		LWLockRelease(&slot->mutex);
+	}
+
+	/*
+	 * Remember this discard pointer in this backend so that future lookups
+	 * via UndoLogRecPtrIsDiscarded() have a chance of avoiding the slow path.
+	 */
+	UndoLogGetTableEntry(logno)->recent_discard = discard;
+
+	return pointer < discard;
+}
+
+/*
+ * Fetch the previous transaction's start undo record point.
+ */
+UndoRecPtr
+UndoLogGetLastXactStartPoint(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	uint64 last_xact_start = 0;
+
+	if (unlikely(slot == NULL))
+		return InvalidUndoRecPtr;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: review */
+	last_xact_start = slot->meta.unlogged.last_xact_start;
+	LWLockRelease(&slot->mutex);
+
+	if (last_xact_start == 0)
+		return InvalidUndoRecPtr;
+
+	return MakeUndoRecPtr(logno, last_xact_start);
+}
+
+/*
+ * Detach from the undo log we are currently attached to, returning it to the
+ * appropriate free list if it still has space.
+ */
+static void
+detach_current_undo_log(UndoLogCategory category, bool full)
+{
+	UndoLogSlot *slot;
+
+	slot = CurrentSession->attached_undo_slots[category];
+
+	Assert(slot != NULL);
+
+	CurrentSession->attached_undo_slots[category] = NULL;
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = InvalidPid;
+	slot->meta.unlogged.xid = InvalidTransactionId;
+	if (full)
+		slot->meta.status = UNDO_LOG_STATUS_FULL;
+	LWLockRelease(&slot->mutex);
+
+	/* Push back onto the appropriate free list, unless it's full. */
+	if (!full)
+	{
+		LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+		slot->next_free = UndoLogShared->free_lists[category];
+		UndoLogShared->free_lists[category] = slot->logno;
+		LWLockRelease(UndoLogLock);
+	}
+}
+
+/*
+ * Exit handler, detaching from all undo logs.
+ */
+static void
+undo_log_before_exit(int code, Datum arg)
+{
+	int		i;
+
+	if (!CurrentSession)
+		return;
+
+	for (i = 0; i < UndoLogCategories; ++i)
+	{
+		if (CurrentSession->attached_undo_slots[i] != NULL)
+			detach_current_undo_log(i, false);
+	}
+}
+
+/*
+ * Create a new empty segment file on disk for the byte starting at 'end'.
+ */
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+							UndoLogOffset end)
+{
+	struct stat	stat_buffer;
+	off_t	size;
+	char	path[MAXPGPATH];
+	void   *zeroes;
+	size_t	nzeroes = 8192;
+	int		fd;
+
+	UndoLogSegmentPath(logno, end / UndoLogSegmentSize, tablespace, path);
+
+	/*
+	 * Create and fully allocate a new file.  If we crashed and recovered
+	 * then the file might already exist, so use flags that tolerate that.
+	 * It's also possible that it exists but is too short, in which case
+	 * we'll write the rest.  We don't really care what's in the file, we
+	 * just want to make sure that the filesystem has allocated physical
+	 * blocks for it, so that non-COW filesystems will report ENOSPC now
+	 * rather than later when the space is needed and we'll avoid creating
+	 * files with holes.
+	 */
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0 && tablespace != 0)
+	{
+		char undo_path[MAXPGPATH];
+
+		/* Try creating the undo directory for this tablespace. */
+		UndoLogDirectory(tablespace, undo_path);
+		if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+		{
+			char	   *parentdir;
+
+			if (errno != ENOENT || !InRecovery)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+
+			/*
+			 * In recovery, it's possible that the tablespace directory
+			 * doesn't exist because a later WAL record removed the whole
+			 * tablespace.  In that case we create a regular directory to
+			 * stand in for it.  This is similar to the logic in
+			 * TablespaceCreateDbspace().
+			 */
+
+			/* create two parents up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			/* create one parent up if not exist */
+			parentdir = pstrdup(undo_path);
+			get_parent_directory(parentdir);
+			/* Can't create parent and it doesn't already exist? */
+			if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								parentdir)));
+			pfree(parentdir);
+
+			if (mkdir(undo_path, S_IRWXU) != 0 && errno != EEXIST)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not create directory \"%s\": %m",
+								undo_path)));
+		}
+
+		fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	}
+	if (fd < 0)
+		elog(ERROR, "could not create new file \"%s\": %m", path);
+	if (fstat(fd, &stat_buffer) < 0)
+		elog(ERROR, "could not stat \"%s\": %m", path);
+	size = stat_buffer.st_size;
+
+	/* A buffer full of zeroes we'll use to fill up new segment files. */
+	zeroes = palloc0(nzeroes);
+
+	while (size < UndoLogSegmentSize)
+	{
+		ssize_t written;
+
+		written = write(fd, zeroes, Min(nzeroes, UndoLogSegmentSize - size));
+		if (written < 0)
+			elog(ERROR, "cannot initialize undo log segment file \"%s\": %m",
+				 path);
+		size += written;
+	}
+
+	/* Flush the contents of the file to disk before the next checkpoint. */
+	undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
+
+	CloseTransientFile(fd);
+
+	pfree(zeroes);
+
+	elog(DEBUG1, "created undo segment \"%s\"", path);
+}
+
+/*
+ * Create a new undo segment, when it is unexpectedly not present.
+ */
+void
+UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno)
+{
+	Assert(InRecovery);
+	allocate_empty_undo_segment(logno, tablespace, segno * UndoLogSegmentSize);
+}
+
+/*
+ * Create and zero-fill a new segment for a given undo log number.
+ */
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+	UndoLogSlot *slot;
+	size_t		end;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/* TODO review interlocking */
+
+	Assert(slot != NULL);
+	Assert(slot->meta.end % UndoLogSegmentSize == 0);
+	Assert(new_end % UndoLogSegmentSize == 0);
+	Assert(InRecovery ||
+		   CurrentSession->attached_undo_slots[slot->meta.category] == slot);
+
+	/*
+	 * Create all the segments needed to increase 'end' to the requested
+	 * size.  This is quite expensive, so we will try to avoid it completely
+	 * by renaming files into place in UndoLogDiscard() instead.
+	 */
+	end = slot->meta.end;
+	while (end < new_end)
+	{
+		allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/*
+	 * If we're not in recovery, we need to WAL-log the creation of the new
+	 * file(s).  We do that after the above filesystem modifications, in
+	 * violation of the data-before-WAL rule as exempted by
+	 * src/backend/access/transam/README.  This means that it's possible for
+	 * us to crash having made some or all of the filesystem changes but
+	 * before WAL logging, but in that case we'll eventually try to create the
+	 * same segment(s) again, which is tolerated.
+	 */
+	if (!InRecovery)
+	{
+		xl_undolog_extend xlrec;
+		XLogRecPtr	ptr;
+
+		xlrec.logno = logno;
+		xlrec.end = end;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+		XLogFlush(ptr);
+	}
+
+	/*
+	 * We didn't need to acquire the mutex to read 'end' above because only
+	 * we write to it.  But we need the mutex to update it, because the
+	 * checkpointer might read it concurrently.
+	 *
+	 * XXX It's possible for meta.end to be higher already during
+	 * recovery, because of the timing of a checkpoint; in that case we did
+	 * nothing above and we shouldn't update shmem here.  That interaction
+	 * needs more analysis.
+	 */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (slot->meta.end < end)
+		slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * This function must be called before all of the undo log activity that will
+ * be covered by a single WAL record.
+ */
+void
+UndoLogBeginInsert(UndoLogAllocContext *context,
+				   UndoLogCategory category,
+				   XLogReaderState *xlog_record)
+{
+	context->try_location = InvalidUndoRecPtr;
+	context->category = category;
+
+	/*
+	 * Tell UndoLogAllocate() to capture undo log meta-data before-change
+	 * images, so that UndoLogRegister() can find them and they can be written
+	 * to the WAL once per checkpoint.
+	 */
+	context->num_meta_data_images = 0;
+
+	/*
+	 * Tell UndoLogAllocateInRecovery() that we don't know which undo log to
+	 * allocate in yet, and to start its search for registered blocks at
+	 * the lowest-numbered block_id.
+	 */
+	context->xlog_record = xlog_record;
+	context->recovery_logno = InvalidUndoLogNumber;
+	context->recovery_block_id = 0;
+
+	/*
+	 * For UNDO_SHARED, this always denotes the beginning of a new record set.
+	 * For other categories, the boundaries are detected by transaction ID
+	 * changes.
+	 */
+	context->new_shared_record_set = category == UNDO_SHARED;
+}
+
+/*
+ * Get an insertion point that is guaranteed to be backed by enough space to
+ * hold 'size' bytes of data.  To actually write into the undo log, client
+ * code should call this first and then use bufmgr routines to access buffers
+ * and provide WAL logs and redo handlers.  In other words, while this module
+ * looks after making sure the undo log has sufficient space and the undo meta
+ * data is crash safe, the *contents* of the undo log and (indirectly) the
+ * insertion point are the responsibility of client code.
+ *
+ * A suggested insertion point can optionally be passed in as 'try_location',
+ * and will be returned if possible.  If not InvalidUndoRecPtr, it must fall
+ * with, or exactly one byte after, the most recent allocation for the same
+ * persistence level.  This interface allows for a series of allocation to be
+ * made without committing to using the space yet; call UndoLogAdvance() to
+ * actually advance the insert pointer.
+ *
+ * Return an undo log insertion point that can be converted to a buffer tag
+ * and an insertion point within a buffer page.
+ */
+UndoRecPtr
+UndoLogAllocate(UndoLogAllocContext *context,
+				uint16 size,
+				bool *need_xact_header,
+				UndoRecPtr *last_xact_start,
+				UndoRecPtr *prevlog_xact_start,
+				UndoRecPtr *prevlog_insert_urp)
+{
+	Session *session = CurrentSession;
+	UndoLogSlot *slot;
+	UndoLogOffset new_insert;
+	TransactionId logxid;
+
+	slot = CurrentSession->attached_undo_slots[context->category];
+
+	/*
+	 * We may need to attach to an undo log, either because this is the first
+	 * time this backend as needed to write to an undo log at all or because
+	 * the undo_tablespaces GUC was changed.  When doing that, we'll need
+	 * interlocking against tablespaces being concurrently dropped.
+	 */
+
+ retry:
+	/* See if we need to check the undo_tablespaces GUC. */
+	if (unlikely(session->need_to_choose_undo_tablespace || slot == NULL))
+	{
+		Oid		tablespace;
+		bool	need_to_unlock;
+
+		need_to_unlock =
+			choose_undo_tablespace(session->need_to_choose_undo_tablespace,
+								   &tablespace);
+		attach_undo_log(context->category, tablespace);
+		if (need_to_unlock)
+			LWLockRelease(TablespaceCreateLock);
+		slot = CurrentSession->attached_undo_slots[context->category];
+		session->need_to_choose_undo_tablespace = false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	logxid = slot->meta.unlogged.xid;
+
+	if (logxid != GetTopTransactionId())
+	{
+		/*
+		 * While we have the lock, check if we have been forcibly detached by
+		 * DROP TABLESPACE.  That can only happen between transactions (see
+		 * DropUndoLogsInsTablespace()).
+		 */
+		if (slot->pid == InvalidPid)
+		{
+			LWLockRelease(&slot->mutex);
+			slot = NULL;
+			goto retry;
+		}
+		/* Record that we are attached to this log. */
+		slot->meta.unlogged.xid = GetTopTransactionId();
+		/*
+		 * Maintain our tracking of the and the previous transaction start
+		 * locations.
+		 */
+		if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+		{
+			slot->meta.unlogged.last_xact_start =
+				slot->meta.unlogged.this_xact_start;
+			slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+		}
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * 'size' is expressed in usable non-header bytes.  Figure out how far we
+	 * have to move insert to create space for 'size' usable bytes, stepping
+	 * over any intervening headers.
+	 */
+	Assert(slot->meta.unlogged.insert % BLCKSZ >= UndoLogBlockHeaderSize);
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+
+		Assert(UndoRecPtrGetLogNo(context->try_location) == slot->logno);
+		Assert(try_offset <= slot->meta.end);
+		new_insert = UndoLogOffsetPlusUsableBytes(try_offset, size);
+	}
+	else
+	{
+		new_insert = UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert,
+												  size);
+	}
+	Assert(new_insert % BLCKSZ >= UndoLogBlockHeaderSize);
+
+	/*
+	 * We don't need to acquire log->mutex to read log->meta.insert and
+	 * log->meta.end, because this backend is the only one that can
+	 * modify them.
+	 */
+	if (unlikely(new_insert > slot->meta.end))
+	{
+		if (new_insert > UndoLogMaxSize)
+		{
+			/* This undo log is entirely full.  Get a new one. */
+			if (logxid == GetTopTransactionId())
+			{
+				/*
+				 * If the same transaction is split over two undo logs then
+				 * store the previous log number in new log.  See detailed
+				 * comments in undorecord.c file header.
+				 */
+				*prevlog_xact_start =
+					MakeUndoRecPtr(slot->logno,
+								   slot->meta.unlogged.this_xact_start);
+				*prevlog_insert_urp =
+					MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+			}
+			elog(DEBUG1, "undo log %u is full, switching to a new one", slot->logno);
+			slot = NULL;
+			detach_current_undo_log(context->category, true);
+			context->try_location = InvalidUndoRecPtr;
+			goto retry;
+		}
+		/*
+		 * Extend the end of this undo log to cover new_insert (in other words
+		 * round up to the segment size).
+		 */
+		extend_undo_log(slot->logno,
+						new_insert + UndoLogSegmentSize -
+						new_insert % UndoLogSegmentSize);
+		Assert(new_insert <= slot->meta.end);
+	}
+
+	/*
+	 * Create a back-up image of the unlogged part of the undo log's
+	 * meta-data, if we haven't already done so since UndoLogBeginInsert() (ie
+	 * for the WAL record that this undo allocation will be replayed by).
+	 */
+	if (context->num_meta_data_images == 0 ||
+		context->meta_data_images[context->num_meta_data_images - 1].logno != slot->logno)
+	{
+		if (context->num_meta_data_images >= MAX_META_DATA_IMAGES)
+			elog(ERROR, "too many undo log meta data images");
+		context->meta_data_images[context->num_meta_data_images].logno = slot->logno;
+		context->meta_data_images[context->num_meta_data_images++].data = slot->meta.unlogged;
+	}
+
+	/*
+	 * If no try_location was passed in, or if we switched logs, then we'll
+	 * return the current insertion point.
+	 */
+	if (context->try_location == InvalidUndoRecPtr)
+		context->try_location = MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+
+	/*
+	 * Is this location the first in this undo log for a transaction or a
+	 * shared record set?
+	 */
+	if (context->new_shared_record_set)
+	{
+		context->new_shared_record_set = false;
+		*need_xact_header = true;
+	}
+	else
+	{
+		*need_xact_header =
+			UndoRecPtrGetOffset(context->try_location) ==
+			slot->meta.unlogged.this_xact_start;
+	}
+	*last_xact_start =
+		MakeUndoRecPtr(slot->logno, slot->meta.unlogged.last_xact_start);
+
+	return context->try_location;
+}
+
+void
+UndoLogRegister(UndoLogAllocContext *context, uint8 block_id, UndoLogNumber logno)
+{
+	int		i;
+
+	for (i = 0; i < context->num_meta_data_images; ++i)
+	{
+		if (context->meta_data_images[i].logno == logno)
+		{
+			XLogRegisterBufData(block_id,
+								(char *) &context->meta_data_images[i].data,
+								sizeof(context->meta_data_images[i].data));
+			return;
+		}
+	}
+}
+
+/*
+ * In recovery, we expect exactly the same sequence of allocation sizes, but
+ * we also need the WAL record that is being replayed so we can figure out
+ * where the undo space was allocated.
+ */
+UndoRecPtr
+UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+						  TransactionId xid,
+						  uint16 size,
+						  bool *need_xact_header,
+						  UndoRecPtr *last_xact_start,
+						  UndoRecPtr *prevlog_xact_start,
+						  UndoRecPtr *prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	Assert(InRecovery);
+
+	/*
+	 * Just as in UndoLogAllocate(), the caller may be extending an existing
+	 * allocation before committing with UndoLogAdvance().
+	 */
+	if (context->try_location != InvalidUndoRecPtr)
+	{
+		/*
+		 * The try location must be in the log we're attached to, at most one
+		 * byte past the end of space backed by files.
+		 */
+		UndoLogOffset try_offset = UndoRecPtrGetOffset(context->try_location);
+		UndoLogNumber logno = UndoRecPtrGetLogNo(context->try_location);
+
+		/*
+		 * You can only have a try_location on your second or later allocation
+		 * for a given WAL record.  It had better be in the same log as the
+		 * previous allocation for this WAL record (though it may not turn out
+		 * to have enough space, below).
+		 */
+		Assert(logno == context->recovery_logno);
+
+		/*
+		 * Any log extension triggered by UndoLogAllocate() must have been
+		 * replayed by now, so we can just check if this log has enough space,
+		 * and if so, return.
+		 */
+		slot = find_undo_log_slot(logno, false);
+		if (UndoLogOffsetPlusUsableBytes(try_offset, size) <= slot->meta.end)
+		{
+			*need_xact_header = false;
+			return try_offset;
+		}
+
+		/* Full.  Ignore try_location and find the next log that was used. */
+		Assert(slot->meta.status == UNDO_LOG_STATUS_FULL);
+	}
+	else
+	{
+		/*
+		 * For now we only support one allocation per WAL record that doesn't
+		 * have a try_location (ie the first one).  We'll have to find out
+		 * which log was used first.
+		 */
+		Assert(context->recovery_logno == InvalidUndoLogNumber);
+	}
+
+	/*
+	 * In order to find the undo log that was used by UndoLogAllocate(), we
+	 * consult the list of registered blocks to figure out which undo logs
+	 * should be written to by this WAL record.
+	 */
+	while (context->recovery_block_id <= context->xlog_record->max_block_id)
+	{
+		DecodedBkpBlock *block;
+
+		/* We're looking for the first block referencing a new undo log. */
+		block = &context->xlog_record->blocks[context->recovery_block_id];
+		if (block->rnode.dbNode == UndoDbOid &&
+			block->rnode.relNode != context->recovery_logno)
+		{
+			UndoLogNumber logno = block->rnode.relNode;
+			const void *backup;
+			size_t backup_size;
+
+			/* We found a reference to a different (or first) undo log. */
+			slot = find_undo_log_slot(logno, false);
+
+			/*
+			 * Since on-line checkpoints capture an inconsistent snapshot of
+			 * undo log meta-data, we'll restore the unlogged part of the
+			 * meta-data image if one was attached to the WAL record (that is,
+			 * the members that don't have WAL records for every change
+			 * already).
+			 */
+			backup =
+				XLogRecGetBlockData(context->xlog_record,
+									context->recovery_block_id,
+									&backup_size);
+			if (unlikely(backup))
+			{
+				Assert(backup_size == sizeof(UndoLogUnloggedMetaData));
+
+				/* Restore the unlogged members from the backup-imaged. */
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				memcpy(&slot->meta.unlogged, backup, sizeof(UndoLogUnloggedMetaData));
+				LWLockRelease(&slot->mutex);
+			}
+			else
+			{
+				/*
+				 * Otherwise we need to do our own transaction tracking
+				 * whenever we see a new xid, to match the logic in
+				 * UndoLogAllocate().
+				 */
+				if (xid != slot->meta.unlogged.xid)
+				{
+					slot->meta.unlogged.xid = xid;
+					if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+						slot->meta.unlogged.last_xact_start =
+							slot->meta.unlogged.this_xact_start;
+					slot->meta.unlogged.this_xact_start =
+						slot->meta.unlogged.insert;
+				}
+			}
+
+			/* TODO: check locking against undo log slot recycling? */
+
+			/*
+			 * At this stage we should have an undo log that can handle this
+			 * allocation.  If we don't, something is screwed up.
+			 */
+			if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) > slot->meta.end)
+				elog(ERROR,
+					 "cannot allocate %d bytes in undo log %d",
+					 (int) size, slot->logno);
+
+			*need_xact_header =
+				context->try_location == InvalidUndoRecPtr &&
+				slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+			*last_xact_start = slot->meta.unlogged.last_xact_start;
+			context->recovery_logno = slot->logno;
+
+			/* Read log switch information from meta and reset it. */
+			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+
+			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+
+			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+		}
+		++context->recovery_block_id;
+	}
+
+	/*
+	 * If we've run out of blocks to inspect, then we must have replayed a
+	 * different sequence of allocation sizes, or screwed up the
+	 * XLOG_UNDOLOG_EXTEND records, indicating a bug somewhere.
+	 */
+	elog(ERROR, "cannot determine undo log to allocate from");
+
+	return 0;		/* not reached */
+}
+
+/*
+ * Advance the insertion pointer in this context by 'size' usable (non-header)
+ * bytes.  This is the next place we'll try to allocate a record, if it fits.
+ * This is not committed to shared memory until after we've WAL-logged the
+ * record and UndoLogAdvanceFinal() is called.
+ */
+void
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+	context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+														 size);
+}
+
+/*
+ * Advance the insertion pointer to 'size' usable (non-header) bytes past
+ * insertion_point.
+ */
+void
+UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(insertion_point) ;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(InRecovery ||
+		   AmAttachedToUndoLogSlot(slot) ||
+		   slot->meta.status == UNDO_LOG_STATUS_FULL);
+
+	/*
+	 * The caller has the current insertion point, as returned by
+	 * UndoLogAllocate[InRecovery]().
+	 */
+	Assert(UndoRecPtrGetOffset(insertion_point) == slot->meta.unlogged.insert);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.insert =
+		UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size);
+	LWLockRelease(&slot->mutex);
+}
+
+/*
+ * Advance the discard pointer in one undo log, discarding all undo data
+ * relating to one or more whole transactions.  The passed in undo pointer is
+ * the address of the oldest data that the called would like to keep, and the
+ * affected undo log is implied by this pointer, ie
+ * UndoRecPtrGetLogNo(discard_pointer).
+ *
+ * The caller asserts that there will be no attempts to access the undo log
+ * region being discarded after this moment.  This operation will cause the
+ * relevant buffers to be dropped immediately, without writing any data out to
+ * disk.  Any attempt to read the buffers (except a partial buffer at the end
+ * of this range which will remain) may result in IO errors, because the
+ * underlying segment file may have been physically removed.
+ *
+ * Return true if the discard point was updated, and false if nothing was done
+ * because the log precending the given point was already discarded.
+ *
+ * TODO: The return value is not yet reliable and the code still doesn't work
+ * correctly if called for the same undo log in two backends; more
+ * interlocking work required here.
+ */
+bool
+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(discard_point);
+	UndoLogOffset discard = UndoRecPtrGetOffset(discard_point);
+	UndoLogOffset old_discard;
+	UndoLogOffset end;
+	UndoLogSlot *slot;
+	int			segno;
+	int			new_segno;
+	bool		need_to_flush_wal = false;
+	bool		entirely_discarded = false;
+
+	slot = find_undo_log_slot(logno, false);
+	if (unlikely(slot == NULL))
+	{
+		/* Already discarded (entirely). */
+		return false;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	if (unlikely(slot->logno != logno || discard <= slot->meta.discard))
+	{
+		/*
+		 * Already discarded entirely and the slot has been recycled, or up
+		 * to this point).
+		 */
+		LWLockRelease(&slot->mutex);
+		return false;
+	}
+	if (discard > slot->meta.unlogged.insert)
+		elog(ERROR, "cannot move discard point past insert point");
+	old_discard = slot->meta.discard;
+	end = slot->meta.end;
+	/* Are we discarding the last remaining data in a log marked as full? */
+	if (slot->meta.status == UNDO_LOG_STATUS_FULL &&
+		discard == slot->meta.unlogged.insert)
+	{
+		/*
+		 * Adjust the discard and insert pointers so that the final segment is
+		 * deleted from disk, and remember not to recycle it.
+		 */
+		entirely_discarded = true;
+		/* TODO: Check if the following line is replayed correctly */
+		slot->meta.unlogged.insert = slot->meta.end;
+		discard = slot->meta.end;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/*
+	 * TODO: I think we need a new lock just for this phase, so that buffer
+	 * dropping and IO are done by only one backend if a superuser command and
+	 * a discard worker both run this!
+	 */
+
+	/*
+	 * Drop all buffers holding this undo data out of the buffer pool (except
+	 * the last one, if the new location is in the middle of it somewhere), so
+	 * that the contained data doesn't ever touch the disk.  The caller
+	 * promises that this data will not be needed again.  We have to drop the
+	 * buffers from the buffer pool before removing files, otherwise a
+	 * concurrent session might try to write the block to evict the buffer.
+	 */
+	forget_undo_buffers(logno, old_discard, discard, entirely_discarded);
+
+	/*
+	 * Check if we crossed a segment boundary and need to do some synchronous
+	 * filesystem operations.
+	 */
+	segno = old_discard / UndoLogSegmentSize;
+	new_segno = discard / UndoLogSegmentSize;
+	if (segno < new_segno)
+	{
+		int		recycle;
+		UndoLogOffset pointer;
+
+		/*
+		 * We always WAL-log discards, but we only need to flush the WAL if we
+		 * have performed a filesystem operation.
+		 */
+		need_to_flush_wal = true;
+
+		/*
+		 * XXX When we rename or unlink a file, it's possible that some
+		 * backend still has it open because it has recently read a page from
+		 * it.  smgr/undofile.c in any such backend will eventually close it,
+		 * because it considers that fd to belong to the file with the name
+		 * that we're unlinking or renaming and it doesn't like to keep more
+		 * than one open at a time.  No backend should ever try to read from
+		 * such a file descriptor; that is what it means when we say that the
+		 * caller of UndoLogDiscard() asserts that there will be no attempts
+		 * to access the discarded range of undo log.  In the case of a
+		 * rename, if a backend were to attempt to read undo data in the range
+		 * being discarded, it would read entirely the wrong data.
+		 */
+
+		/*
+		 * How many segments should we recycle (= rename from tail position to
+		 * head position)?  For now it's always 1 unless there is already a
+		 * spare one, but we could have an adaptive algorithm that recycles
+		 * multiple segments at a time and pays just one fsync().
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if ((slot->meta.end - slot->meta.unlogged.insert) < UndoLogSegmentSize &&
+			slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+			recycle = 1;
+		else
+			recycle = 0;
+		LWLockRelease(&slot->mutex);
+
+		/* Rewind to the start of the segment. */
+		pointer = segno * UndoLogSegmentSize;
+
+		while (pointer < new_segno * UndoLogSegmentSize)
+		{
+			char	discard_path[MAXPGPATH];
+
+			/* Tell the checkpointer that the file is going away. */
+			undofile_forget_sync(logno, pointer / UndoLogSegmentSize,
+								 slot->meta.tablespace);
+
+			UndoLogSegmentPath(logno, pointer / UndoLogSegmentSize,
+							   slot->meta.tablespace, discard_path);
+
+			/* Can we recycle the oldest segment? */
+			if (recycle > 0)
+			{
+				char	recycle_path[MAXPGPATH];
+
+				/*
+				 * End points one byte past the end of the current undo space,
+				 * ie to the first byte of the segment file we want to create.
+				 */
+				UndoLogSegmentPath(logno, end / UndoLogSegmentSize,
+								   slot->meta.tablespace, recycle_path);
+				if (rename(discard_path, recycle_path) == 0)
+				{
+					elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+						 discard_path, recycle_path);
+					end += UndoLogSegmentSize;
+					--recycle;
+				}
+				else
+				{
+					elog(ERROR, "could not rename \"%s\" to \"%s\": %m",
+						 discard_path, recycle_path);
+				}
+			}
+			else
+			{
+				if (unlink(discard_path) == 0)
+					elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+				else
+					elog(ERROR, "could not unlink \"%s\": %m", discard_path);
+			}
+			pointer += UndoLogSegmentSize;
+		}
+	}
+
+	/* WAL log the discard. */
+	{
+		xl_undolog_discard xlrec;
+		XLogRecPtr ptr;
+
+		xlrec.logno = logno;
+		xlrec.discard = discard;
+		xlrec.end = end;
+		xlrec.latestxid = xid;
+		xlrec.entirely_discarded = entirely_discarded;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_DISCARD);
+
+		if (need_to_flush_wal)
+			XLogFlush(ptr);
+	}
+
+	/* Update shmem to show the new discard and end pointers. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (entirely_discarded)
+		free_undo_log_slot(slot);
+
+	return true;
+}
+
+/*
+ * Return an UndoRecPtr to the oldest valid data in an undo log, or
+ * InvalidUndoRecPtr if it is empty.
+ */
+UndoRecPtr
+UndoLogGetOldestRecord(UndoLogNumber logno, bool *full)
+{
+	UndoLogSlot *slot;
+	UndoRecPtr	result;
+
+	/* Try to find the slot for this undo log number. */
+	slot = find_undo_log_slot(logno, false);
+	if (slot == NULL)
+	{
+		/* It's unknown to us, so we assume it's been entirely discarded. */
+		if (full)
+			*full = true;
+		return InvalidUndoRecPtr;
+	}
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->logno != logno)
+	{
+		/* It's been recycled.  SO it must have been entirely discarded. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = true;
+	}
+	else if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		/* It's empty, so there is no oldest record pointer to return. */
+		result = InvalidUndoRecPtr;
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	else
+	{
+		/* There is a record here! */
+		result = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (full)
+			*full = slot->meta.status == UNDO_LOG_STATUS_FULL;
+	}
+	LWLockRelease(&slot->mutex);
+
+	return result;
+}
+
+/*
+ * UndoLogSwitchSetPrevLogInfo - Store previous log info on the log switch and
+ * wal log the same.
+ */
+void
+UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
+							UndoRecPtr prevlog_last_urp)
+{
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(logno, false);
+
+	/*
+	 * Either we're in recovery, or is a log we are currently attached to, or
+	 * recently detached from because it was full.
+	 */
+	Assert(AmAttachedToUndoLogSlot(slot));
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
+	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	LWLockRelease(&slot->mutex);
+
+	/* Wal log the log switch. */
+	{
+		xl_undolog_switch xlrec;
+
+		xlrec.logno = logno;
+		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_last_urp = prevlog_xact_start;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+		XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_SWITCH);
+	}
+}
+
+/*
+ * Return the next insert location.
+ */
+UndoRecPtr
+UndoLogGetNextInsertPtr(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+	UndoRecPtr	insert;
+
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	/* TODO: what if the slot has been recycled? */
+	insert = slot->meta.unlogged.insert;
+	LWLockRelease(&slot->mutex);
+
+	return MakeUndoRecPtr(logno, insert);
+}
+
+/*
+ * Delete unreachable files under pg_undo.  Any files corresponding to LSN
+ * positions before the previous checkpoint are no longer needed.
+ */
+static void
+CleanUpUndoCheckPointFiles(XLogRecPtr checkPointRedo)
+{
+	DIR	   *dir;
+	struct dirent *de;
+	char	path[MAXPGPATH];
+	char	oldest_path[MAXPGPATH];
+
+	/*
+	 * If a base backup is in progress, we can't delete any checkpoint
+	 * snapshot files because one of them corresponds to the backup label but
+	 * there could be any number of checkpoints during the backup.
+	 */
+	if (BackupInProgress())
+		return;
+
+	/* Otherwise keep only those >= the previous checkpoint's redo point. */
+	snprintf(oldest_path, MAXPGPATH, "%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	dir = AllocateDir("pg_undo");
+	while ((de = ReadDir(dir, "pg_undo")) != NULL)
+	{
+		/*
+		 * Assume that fixed width uppercase hex strings sort the same way as
+		 * the values they represent, so we can use strcmp to identify undo
+		 * log snapshot files corresponding to checkpoints that we don't need
+		 * anymore.  This assumption holds for ASCII.
+		 */
+		if (!(strlen(de->d_name) == UNDO_CHECKPOINT_FILENAME_LENGTH))
+			continue;
+
+		if (UndoCheckPointFilenamePrecedes(de->d_name, oldest_path))
+		{
+			snprintf(path, MAXPGPATH, "pg_undo/%s", de->d_name);
+			if (unlink(path) != 0)
+				elog(ERROR, "could not unlink file \"%s\": %m", path);
+		}
+	}
+	FreeDir(dir);
+}
+
+/*
+ * Write out the undo log meta data to the pg_undo directory.  The actual
+ * contents of undo logs is in shared buffers and therefore handled by
+ * CheckPointBuffers(), but here we record the table of undo logs and their
+ * properties.
+ */
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
+{
+	UndoLogMetaData *serialized = NULL;
+	size_t	serialized_size = 0;
+	char   *data;
+	char	path[MAXPGPATH];
+	UndoLogNumber num_logs;
+	int		fd;
+	int		i;
+	pg_crc32c crc;
+
+	/*
+	 * We acquire UndoLogLock to prevent any undo logs from being created or
+	 * discarded while we build a snapshot of them.  This isn't expected to
+	 * take long on a healthy system because the number of active logs should
+	 * be around the number of backends.  Holding this lock won't prevent
+	 * concurrent access to the undo log, except when segments need to be
+	 * added or removed.
+	 */
+	LWLockAcquire(UndoLogLock, LW_SHARED);
+
+	/*
+	 * Rather than doing the file IO while we hold locks, we'll copy the
+	 * meta-data into a palloc'd buffer.
+	 */
+	serialized_size = sizeof(UndoLogMetaData) * UndoLogNumSlots();
+	serialized = (UndoLogMetaData *) palloc0(serialized_size);
+
+	/* Scan through all slots looking for non-empty ones. */
+	num_logs = 0;
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+
+		/* Skip empty slots. */
+		if (slot->logno == InvalidUndoLogNumber)
+			continue;
+
+		/* Capture snapshot while holding each mutex. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		serialized[num_logs++] = slot->meta;
+		LWLockRelease(&slot->mutex);
+	}
+
+	LWLockRelease(UndoLogLock);
+
+	/* Dump into a file under pg_undo. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE);
+	fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+	if (fd < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not create file \"%s\": %m", path)));
+
+	/* Compute header checksum. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the number of active logs + crc. */
+	if ((write(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno)) ||
+		(write(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno)) != sizeof(UndoLogShared->next_logno)) ||
+		(write(fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+	/* Write out the meta data for all active undo logs. */
+	data = (char *) serialized;
+	INIT_CRC32C(crc);
+	serialized_size = num_logs * sizeof(UndoLogMetaData);
+	while (serialized_size > 0)
+	{
+		ssize_t written;
+
+		written = write(fd, data, serialized_size);
+		if (written < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write to file \"%s\": %m", path)));
+		COMP_CRC32C(crc, data, written);
+		serialized_size -= written;
+		data += written;
+	}
+	FIN_CRC32C(crc);
+
+	if (write(fd, &crc, sizeof(crc)) != sizeof(crc))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", path)));
+
+
+	/* Flush file and directory entry. */
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC);
+	pg_fsync(fd);
+	if (CloseTransientFile(fd) < 0)
+		ereport(data_sync_elevel(ERROR),
+				(errcode_for_file_access(),
+				 errmsg("could not close file \"%s\": %m", path)));
+	fsync_fname("pg_undo", true);
+	pgstat_report_wait_end();
+
+	if (serialized)
+		pfree(serialized);
+
+	CleanUpUndoCheckPointFiles(priorCheckPointRedo);
+}
+
+void
+StartupUndoLogs(XLogRecPtr checkPointRedo)
+{
+	char	path[MAXPGPATH];
+	int		i;
+	int		fd;
+	int		nlogs;
+	pg_crc32c crc;
+	pg_crc32c new_crc;
+
+	/* If initdb is calling, there is no file to read yet. */
+	if (IsBootstrapProcessingMode())
+		return;
+
+	/* Open the pg_undo file corresponding to the given checkpoint. */
+	snprintf(path, MAXPGPATH, "pg_undo/%016" INT64_MODIFIER "X",
+			 checkPointRedo);
+	pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_READ);
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
+
+	/* Read the active log number range. */
+	if ((read(fd, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno))
+		 != sizeof(UndoLogShared->low_logno)) ||
+		(read(fd, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno))
+		 != sizeof(UndoLogShared->next_logno)) ||
+		(read(fd, &nlogs, sizeof(nlogs)) != sizeof(nlogs)) ||
+		(read(fd, &crc, sizeof(crc)) != sizeof(crc)))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+
+	/* Verify the header checksum. */
+	INIT_CRC32C(new_crc);
+	COMP_CRC32C(new_crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+	COMP_CRC32C(new_crc, &UndoLogShared->next_logno, sizeof(UndoLogShared->next_logno));
+	COMP_CRC32C(new_crc, &nlogs, sizeof(UndoLogShared->next_logno));
+	FIN_CRC32C(new_crc);
+
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	/*
+	 * We'll acquire UndoLogLock just because allocate_undo_log() asserts we
+	 * hold it (we don't actually expect concurrent access yet).
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* Initialize all the logs and set up the freelist. */
+	INIT_CRC32C(new_crc);
+	for (i = 0; i < nlogs; ++i)
+	{
+		ssize_t size;
+		UndoLogSlot *slot;
+
+		/*
+		 * Get a new UndoLogSlot.  If this checkpoint was created on a system
+		 * with a higher max_connections setting, it's theoretically possible
+		 * that we don't have enough space and cannot start up.
+		 */
+		slot = allocate_undo_log_slot();
+		if (!slot)
+			ereport(ERROR,
+					(errmsg("not enough undo log slots to recover from checkpoint: need at least %d, have %zu",
+							nlogs, UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections")));
+
+		/* Read in the meta data for this undo log. */
+		if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
+			elog(ERROR, "short read of pg_undo meta data in file \"%s\": %m (got %zu, wanted %zu)",
+				 path, size, sizeof(slot->meta));
+		COMP_CRC32C(new_crc, &slot->meta, sizeof(slot->meta));
+
+		/*
+		 * At normal start-up, or during recovery, all active undo logs start
+		 * out on the appropriate free list.
+		 */
+		slot->logno = slot->meta.logno;
+		slot->pid = InvalidPid;
+		slot->oldest_data = MakeUndoRecPtr(slot->logno, slot->meta.discard);
+		if (slot->meta.status == UNDO_LOG_STATUS_ACTIVE)
+		{
+			slot->next_free = UndoLogShared->free_lists[slot->meta.category];
+			UndoLogShared->free_lists[slot->meta.category] = slot->logno;
+		}
+	}
+	FIN_CRC32C(new_crc);
+
+	LWLockRelease(UndoLogLock);
+
+	/* Verify body checksum. */
+	if (read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		elog(ERROR, "pg_undo file \"%s\" is corrupted", path);
+	if (crc != new_crc)
+		elog(ERROR,
+			 "pg_undo file \"%s\" has incorrect checksum", path);
+
+	CloseTransientFile(fd);
+	pgstat_report_wait_end();
+}
+
+/*
+ * Allocate a new UndoLogSlot object.
+ */
+static UndoLogSlot *
+allocate_undo_log_slot(void)
+{
+	UndoLogSlot *slot;
+	UndoLogNumber i;
+
+	Assert(LWLockHeldByMeInMode(UndoLogLock, LW_EXCLUSIVE));
+
+	for (i = 0; i < UndoLogNumSlots(); ++i)
+	{
+		slot = &UndoLogShared->slots[i];
+		if (slot->logno == InvalidUndoLogNumber)
+		{
+			memset(&slot->meta, 0, sizeof(slot->meta));
+			slot->pid = 0;
+			slot->wait_fxmin = InvalidFullTransactionId;
+			slot->oldest_data =0;
+			slot->next_free = -1;
+			slot->logno = -1;
+			return slot;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Free an UndoLogSlot object in shared memory, so that it can be reused.
+ * This is a rare event, and has complications for all code paths that access
+ * slots.  Unless the current session is attached to the slot, it must be
+ * prepared for it to be freed and then potentially recycled for use by
+ * another log.  See UndoLogGetSlot().
+ */
+static void
+free_undo_log_slot(UndoLogSlot *slot)
+{
+	/*
+	 * When removing an undo log from a slot in shared memory, we acquire
+	 * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+	 * hold any one of those locks to prevent the slot from being recycled.
+	 */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno != InvalidUndoLogNumber);
+	slot->logno = InvalidUndoLogNumber;
+	memset(&slot->meta, 0, sizeof(slot->meta));
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * Find the UndoLogSlot object for a given log number.
+ *
+ * The caller may or may not already hold UndoLogLock, and should indicate
+ * this by passing 'locked'.  We'll acquire it in the slow path if necessary.
+ * If it is not held by the caller, the caller must deal with the possibility
+ * that the returned UndoLogSlot no longer contains the requested logno by the
+ * time it is accessed.
+ *
+ * To do that, one of the following approaches must be taken by the calling
+ * code:
+ *
+ * 1.  If the calling code knows that it is attached to this lock or is the
+ * recovery process, then there is no way for the slot to be recycled, so it's
+ * not necessary to check that the log number hasn't changed.  The slot cannot
+ * be recycled while a backend is attached.  It should probably assert that it
+ * is attached, however.
+ *
+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
+ *
+ * Return NULL if the undo log has been entirely discarded.  It is an error to
+ * ask for undo logs that have never been created.
+ */
+static UndoLogSlot *
+find_undo_log_slot(UndoLogNumber logno, bool locked)
+{
+	UndoLogSlot *result = NULL;
+	UndoLogTableEntry *entry;
+	bool	   found;
+
+	Assert(locked == LWLockHeldByMe(UndoLogLock));
+
+	/* First see if we already have it in our cache. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		result = entry->slot;
+	else
+	{
+		UndoLogNumber i;
+
+		/* Nope.  Linear search for the slot in shared memory. */
+		if (!locked)
+			LWLockAcquire(UndoLogLock, LW_SHARED);
+		for (i = 0; i < UndoLogNumSlots(); ++i)
+		{
+			if (UndoLogShared->slots[i].logno == logno)
+			{
+				/* Found it. */
+
+				/*
+				 * TODO: Should this function be usable in a critical section?
+				 * Would it make sense to detect that we are in a critical
+				 * section and just return the pointer to the log without
+				 * updating the cache, to avoid any chance of allocating
+				 * memory?
+				 */
+
+				entry = undologtable_insert(undologtable_cache, logno, &found);
+				entry->number = logno;
+				entry->slot = &UndoLogShared->slots[i];
+				entry->tablespace = entry->slot->meta.tablespace;
+				entry->category = entry->slot->meta.category;
+				entry->recent_discard =
+					MakeUndoRecPtr(logno, entry->slot->meta.discard);
+				result = entry->slot;
+				break;
+			}
+		}
+
+		/*
+		 * If we didn't find it, then it must already have been entirely
+		 * discarded.  We create a negative cache entry so that we can answer
+		 * this question quickly next time.
+		 *
+		 * TODO: We could track the lowest known undo log number, to reduce
+		 * the negative cache entry bloat.
+		 */
+		if (result == NULL)
+		{
+			/*
+			 * Sanity check: the caller should not be asking about undo logs
+			 * that have never existed.
+			 */
+			if (logno >= UndoLogShared->next_logno)
+				elog(ERROR, "undo log %u hasn't been created yet", logno);
+			entry = undologtable_insert(undologtable_cache, logno, &found);
+			entry->number = logno;
+			entry->slot = NULL;
+			entry->tablespace = 0;
+		}
+		if (!locked)
+			LWLockRelease(UndoLogLock);
+	}
+
+	return result;
+}
+
+/*
+ * Get a pointer to an UndoLogSlot object corresponding to a given logno.
+ *
+ * In general, the caller must acquire the UndoLogSlot's mutex to access
+ * the contents, and at that time must consider that the logno might have
+ * changed because the undo log it contained has been entirely discarded.
+ *
+ * If the calling backend is currently attached to the undo log, that is not
+ * possible, because logs can only reach UNDO_LOG_STATUS_DISCARDED after first
+ * reaching UNDO_LOG_STATUS_FULL, and that only happens while detaching.
+ */
+UndoLogSlot *
+UndoLogGetSlot(UndoLogNumber logno, bool missing_ok)
+{
+	UndoLogSlot *slot = find_undo_log_slot(logno, false);
+
+	if (slot == NULL && !missing_ok)
+		elog(ERROR, "unknown undo log number %d", logno);
+
+	return slot;
+}
+
+/*
+ * Attach to a free undo log, creating a new one if required.
+ */
+static void
+attach_undo_log(UndoLogCategory category, Oid tablespace)
+{
+	UndoLogSlot *slot = NULL;
+	UndoLogNumber logno;
+	UndoLogNumber *place;
+
+	Assert(!InRecovery);
+	Assert(CurrentSession->attached_undo_slots[category] == NULL);
+
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/*
+	 * For now we have a simple linked list of unattached undo logs for each
+	 * persistence level.  We'll grovel though it to find something for the
+	 * tablespace you asked for.  If you're not using multiple tablespaces
+	 * it'll be able to pop one off the front.  We might need a hash table
+	 * keyed by tablespace if this simple scheme turns out to be too slow when
+	 * using many tablespaces and many undo logs, but that seems like an
+	 * unusual use case not worth optimizing for.
+	 */
+	place = &UndoLogShared->free_lists[category];
+	while (*place != InvalidUndoLogNumber)
+	{
+		UndoLogSlot *candidate = find_undo_log_slot(*place, true);
+
+		/*
+		 * There should never be an undo log on the freelist that has been
+		 * entirely discarded, or hasn't been created yet.  The persistence
+		 * level should match the freelist.
+		 */
+		if (unlikely(candidate == NULL))
+			elog(ERROR,
+				 "corrupted undo log freelist, no such undo log %u", *place);
+		if (unlikely(candidate->meta.category != category))
+			elog(ERROR,
+				 "corrupted undo log freelist, undo log %u with persistence %d found on freelist %d",
+				 *place, candidate->meta.category, category);
+
+		if (candidate->meta.tablespace == tablespace)
+		{
+			logno = *place;
+			slot = candidate;
+			*place = candidate->next_free;
+			break;
+		}
+		place = &candidate->next_free;
+	}
+
+	/*
+	 * If all existing undo logs for this tablespace and persistence level are
+	 * busy, we'll have to create a new one.
+	 */
+	if (slot == NULL)
+	{
+		if (UndoLogShared->next_logno > MaxUndoLogNumber)
+		{
+			/*
+			 * You've used up all 16 exabytes of undo log addressing space.
+			 * This is a difficult state to reach using only 16 exabytes of
+			 * WAL.
+			 */
+			elog(ERROR, "undo log address space exhausted");
+		}
+
+		/* Allocate a slot from the UndoLogSlot pool. */
+		slot = allocate_undo_log_slot();
+		if (unlikely(!slot))
+			ereport(ERROR,
+					(errmsg("could not create new undo log"),
+					 errdetail("The maximum number of active undo logs is %zu.",
+							   UndoLogNumSlots()),
+					 errhint("Consider increasing max_connections.")));
+		slot->logno = logno = UndoLogShared->next_logno;
+
+		/*
+		 * The insert and discard pointers start after the first block's
+		 * header.  XXX That means that insert is > end for a short time in a
+		 * newly created undo log.  Is there any problem with that?
+		 */
+		slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+		slot->meta.discard = UndoLogBlockHeaderSize;
+
+		slot->meta.logno = logno;
+		slot->meta.tablespace = tablespace;
+		slot->meta.category = category;
+		slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+
+		/* Move the high log number pointer past this one. */
+		++UndoLogShared->next_logno;
+
+		/* WAL-log the creation of this new undo log. */
+		{
+			xl_undolog_create xlrec;
+
+			xlrec.logno = logno;
+			xlrec.tablespace = slot->meta.tablespace;
+			xlrec.category = slot->meta.category;
+
+			XLogBeginInsert();
+			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+			XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_CREATE);
+		}
+
+		/*
+		 * This undo log has no segments.  UndoLogAllocate will create the
+		 * first one on demand.
+		 */
+	}
+	LWLockRelease(UndoLogLock);
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->pid = MyProcPid;
+	LWLockRelease(&slot->mutex);
+
+	CurrentSession->attached_undo_slots[category] = slot;
+}
+
+/* check_hook: validate new undo_tablespaces */
+bool
+check_undo_tablespaces(char **newval, void **extra, GucSource source)
+{
+	char	   *rawname;
+	List	   *namelist;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(*newval);
+
+	/*
+	 * Parse string into list of identifiers, just to check for
+	 * well-formedness (unfortunateley we can't validate the names in the
+	 * catalog yet).
+	 */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawname);
+		list_free(namelist);
+		return false;
+	}
+
+	/*
+	 * Make sure we aren't already in a transaction that has been assigned an
+	 * XID.  This ensures we don't detach from an undo log that we might have
+	 * started writing undo data into for this transaction.
+	 */
+	if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 (errmsg("undo_tablespaces cannot be changed while a transaction is in progress"))));
+	list_free(namelist);
+
+	return true;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_undo_tablespaces(const char *newval, void *extra)
+{
+	/*
+	 * This is normally called only when GetTopTransactionIdIfAny() ==
+	 * InvalidTransactionId (because you can't change undo_tablespaces in the
+	 * middle of a transaction that's been asigned an xid), but we can't
+	 * assert that because it's also called at the end of a transaction that's
+	 * rolling back, to reset the GUC if it was set inside the transaction.
+	 */
+
+	/* Tell UndoLogAllocate() to reexamine undo_tablespaces. */
+	if (CurrentSession)
+		CurrentSession->need_to_choose_undo_tablespace = true;
+}
+
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+	char   *rawname;
+	List   *namelist;
+	bool	need_to_unlock;
+	int		length;
+	int		i;
+
+	/* We need a modifiable copy of string. */
+	rawname = pstrdup(undo_tablespaces);
+
+	/* Break string into list of identifiers. */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+		elog(ERROR, "undo_tablespaces is unexpectedly malformed");
+
+	length = list_length(namelist);
+	if (length == 0 ||
+		(length == 1 && ((char *) linitial(namelist))[0] == '\0'))
+	{
+		/*
+		 * If it's an empty string, then we'll use the default tablespace.  No
+		 * locking is required because it can't be dropped.
+		 */
+		*tablespace = DEFAULTTABLESPACE_OID;
+		need_to_unlock = false;
+	}
+	else
+	{
+		/*
+		 * Choose an OID using our pid, so that if several backends have the
+		 * same multi-tablespace setting they'll spread out.  We could easily
+		 * do better than this if more serious load balancing is judged
+		 * useful.
+		 */
+		int		index = MyProcPid % length;
+		int		first_index = index;
+		Oid		oid = InvalidOid;
+
+		/*
+		 * Take the tablespace create/drop lock while we look the name up.
+		 * This prevents the tablespace from being dropped while we're trying
+		 * to resolve the name, or while the called is trying to create an
+		 * undo log in it.  The caller will have to release this lock.
+		 */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		for (;;)
+		{
+			const char *name = list_nth(namelist, index);
+
+			oid = get_tablespace_oid(name, true);
+			if (oid == InvalidOid)
+			{
+				/* Unknown tablespace, try the next one. */
+				index = (index + 1) % length;
+				/*
+				 * But if we've tried them all, it's time to complain.  We'll
+				 * arbitrarily complain about the last one we tried in the
+				 * error message.
+				 */
+				if (index == first_index)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist", name),
+							 errhint("Create the tablespace or set undo_tablespaces to a valid or empty list.")));
+				continue;
+			}
+			if (oid == GLOBALTABLESPACE_OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("undo logs cannot be placed in pg_global tablespace")));
+			/* If we got here we succeeded in finding one. */
+			break;
+		}
+
+		Assert(oid != InvalidOid);
+		*tablespace = oid;
+		need_to_unlock = true;
+	}
+
+	/*
+	 * If we came here because the user changed undo_tablesaces, then detach
+	 * from any undo logs we happen to be attached to.
+	 */
+	if (force_detach)
+	{
+		for (i = 0; i < UndoLogCategories; ++i)
+		{
+			UndoLogSlot *slot = CurrentSession->attached_undo_slots[i];
+
+			if (slot != NULL)
+			{
+				LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+				slot->pid = InvalidPid;
+				slot->meta.unlogged.xid = InvalidTransactionId;
+				LWLockRelease(&slot->mutex);
+
+				LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+				slot->next_free = UndoLogShared->free_lists[i];
+				UndoLogShared->free_lists[i] = slot->logno;
+				LWLockRelease(UndoLogLock);
+
+				CurrentSession->attached_undo_slots[i] = NULL;
+			}
+		}
+	}
+
+	return need_to_unlock;
+}
+
+bool
+DropUndoLogsInTablespace(Oid tablespace)
+{
+	DIR *dir;
+	char undo_path[MAXPGPATH];
+	UndoLogSlot *slot = NULL;
+	int		i;
+
+	Assert(LWLockHeldByMe(TablespaceCreateLock));
+	Assert(tablespace != DEFAULTTABLESPACE_OID);
+
+	/* First, try to kick everyone off any undo logs in this tablespace. */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		bool ok;
+		bool return_to_freelist = false;
+
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/* Check if this undo log can be forcibly detached. */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		if (slot->meta.discard == slot->meta.unlogged.insert &&
+			(slot->meta.unlogged.xid == InvalidTransactionId ||
+			 !TransactionIdIsInProgress(slot->meta.unlogged.xid)))
+		{
+			slot->meta.unlogged.xid = InvalidTransactionId;
+			if (slot->pid != InvalidPid)
+			{
+				slot->pid = InvalidPid;
+				return_to_freelist = true;
+			}
+			ok = true;
+		}
+		else
+		{
+			/*
+			 * There is data we need in this undo log.  We can't force it to
+			 * be detached.
+			 */
+			ok = false;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* If we failed, then give up now and report failure. */
+		if (!ok)
+			return false;
+
+		/*
+		 * Put this undo log back on the appropriate free-list.  No one can
+		 * attach to it while we hold TablespaceCreateLock, but if we return
+		 * earlier in a future go around this loop, we need the undo log to
+		 * remain usable.  We'll remove all appropriate logs from the
+		 * free-lists in a separate step below.
+		 */
+		if (return_to_freelist)
+		{
+			LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+			slot->next_free = UndoLogShared->free_lists[slot->meta.category];
+			UndoLogShared->free_lists[slot->meta.category] = slot->logno;
+			LWLockRelease(UndoLogLock);
+		}
+	}
+
+	/*
+	 * We detached all backends from undo logs in this tablespace, and no one
+	 * can attach to any non-default-tablespace undo logs while we hold
+	 * TablespaceCreateLock.  We can now drop the undo logs.
+	 */
+	slot = NULL;
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* Skip undo logs in other tablespaces. */
+		if (slot->meta.tablespace != tablespace)
+			continue;
+
+		/*
+		 * Make sure no buffers remain.  When that is done by
+		 * UndoLogDiscard(), the final page is left in shared_buffers because
+		 * it may contain data, or at least be needed again very soon.  Here
+		 * we need to drop even that page from the buffer pool.
+		 */
+		forget_undo_buffers(slot->logno, slot->meta.discard, slot->meta.discard, true);
+
+		/*
+		 * TODO: For now we drop the undo log, meaning that it will never be
+		 * used again.  That wastes the rest of its address space.  Instead,
+		 * we should put it onto a special list of 'offline' undo logs, ready
+		 * to be reactivated in some other tablespace.  Then we can keep the
+		 * unused portion of its address space.
+		 */
+		LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+		slot->meta.status = UNDO_LOG_STATUS_DISCARDED;
+		LWLockRelease(&slot->mutex);
+	}
+
+	/* Forget about all sync requests relating to this tablespace. */
+	undofile_forget_sync_tablespace(tablespace);
+
+	/* Unlink all undo segment files in this tablespace. */
+	UndoLogDirectory(tablespace, undo_path);
+
+	dir = AllocateDir(undo_path);
+	if (dir != NULL)
+	{
+		struct dirent *de;
+
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strcmp(de->d_name, ".") == 0 ||
+				strcmp(de->d_name, "..") == 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+	}
+
+	/* Remove all dropped undo logs from the free-lists. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+	for (i = 0; i < UndoLogCategories; ++i)
+	{
+		UndoLogSlot *slot;
+		UndoLogNumber *place;
+
+		place = &UndoLogShared->free_lists[i];
+		while (*place != InvalidUndoLogNumber)
+		{
+			slot = find_undo_log_slot(*place, true);
+			if (!slot)
+				elog(ERROR,
+					 "corrupted undo log freelist, unknown log %u", *place);
+			if (slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+				*place = slot->next_free;
+			else
+				place = &slot->next_free;
+		}
+	}
+	LWLockRelease(UndoLogLock);
+
+	return true;
+}
+
+void
+ResetUndoLogs(UndoLogCategory category)
+{
+	UndoLogSlot *slot = NULL;
+
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		DIR	   *dir;
+		struct dirent *de;
+		char	undo_path[MAXPGPATH];
+		char	segment_prefix[MAXPGPATH];
+		size_t	segment_prefix_size;
+
+		if (slot->meta.category != category)
+			continue;
+
+		/* Scan the directory for files belonging to this undo log. */
+		snprintf(segment_prefix, sizeof(segment_prefix), "%06X.", slot->logno);
+		segment_prefix_size = strlen(segment_prefix);
+		UndoLogDirectory(slot->meta.tablespace, undo_path);
+		dir = AllocateDir(undo_path);
+		if (dir == NULL)
+			continue;
+		while ((de = ReadDirExtended(dir, undo_path, LOG)) != NULL)
+		{
+			char segment_path[MAXPGPATH];
+
+			if (strncmp(de->d_name, segment_prefix, segment_prefix_size) != 0)
+				continue;
+			snprintf(segment_path, sizeof(segment_path), "%s/%s",
+					 undo_path, de->d_name);
+			elog(DEBUG1, "unlinked undo segment \"%s\"", segment_path);
+			if (unlink(segment_path) < 0)
+				elog(LOG, "couldn't unlink file \"%s\": %m", segment_path);
+		}
+		FreeDir(dir);
+
+		/*
+		 * We have no segment files.  Set the pointers to indicate that there
+		 * is no data.  The discard and insert pointers point to the first
+		 * usable byte in the segment we will create when we next try to
+		 * allocate.  This is a bit strange, because it means that they are
+		 * past the end pointer.  That's the same as when new undo logs are
+		 * created.
+		 *
+		 * TODO: Should we rewind to zero instead, so we can reuse that (now)
+		 * unreferenced address space?
+		 */
+		slot->meta.unlogged.insert = slot->meta.discard = slot->meta.end +
+			UndoLogBlockHeaderSize;
+	}
+}
+
+Datum
+pg_stat_get_undo_logs(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_UNDO_LOGS_COLS 9
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	char *tablespace_name = NULL;
+	Oid last_tablespace = InvalidOid;
+	int			i;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Scan all undo logs to build the results. */
+	for (i = 0; i < UndoLogShared->nslots; ++i)
+	{
+		UndoLogSlot *slot = &UndoLogShared->slots[i];
+		char buffer[17];
+		Datum values[PG_STAT_GET_UNDO_LOGS_COLS];
+		bool nulls[PG_STAT_GET_UNDO_LOGS_COLS] = { false };
+		Oid tablespace;
+
+		/*
+		 * This won't be a consistent result overall, but the values for each
+		 * log will be consistent because we'll take the per-log lock while
+		 * copying them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+
+		/* Skip unused slots and entirely discarded undo logs. */
+		if (slot->logno == InvalidUndoLogNumber ||
+			slot->meta.status == UNDO_LOG_STATUS_DISCARDED)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+
+		values[0] = ObjectIdGetDatum((Oid) slot->logno);
+		values[1] = CStringGetTextDatum(
+			slot->meta.category == UNDO_PERMANENT ? "permanent" :
+			slot->meta.category == UNDO_UNLOGGED ? "unlogged" :
+			slot->meta.category == UNDO_TEMP ? "temporary" :
+			slot->meta.category == UNDO_SHARED ? "shared" : "<unknown>");
+		tablespace = slot->meta.tablespace;
+
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.discard));
+		values[3] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert));
+		values[4] = CStringGetTextDatum(buffer);
+		snprintf(buffer, sizeof(buffer), UndoRecPtrFormat,
+				 MakeUndoRecPtr(slot->logno, slot->meta.end));
+		values[5] = CStringGetTextDatum(buffer);
+		if (slot->meta.unlogged.xid == InvalidTransactionId)
+			nulls[6] = true;
+		else
+			values[6] = TransactionIdGetDatum(slot->meta.unlogged.xid);
+		if (slot->pid == InvalidPid)
+			nulls[7] = true;
+		else
+			values[7] = Int32GetDatum((int32) slot->pid);
+		switch (slot->meta.status)
+		{
+		case UNDO_LOG_STATUS_ACTIVE:
+			values[8] = CStringGetTextDatum("ACTIVE"); break;
+		case UNDO_LOG_STATUS_FULL:
+			values[8] = CStringGetTextDatum("FULL"); break;
+		default:
+			nulls[8] = true;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/*
+		 * Deal with potentially slow tablespace name lookup without the lock.
+		 * Avoid making multiple calls to that expensive function for the
+		 * common case of repeating tablespace.
+		 */
+		if (tablespace != last_tablespace)
+		{
+			if (tablespace_name)
+				pfree(tablespace_name);
+			tablespace_name = get_tablespace_name(tablespace);
+			last_tablespace = tablespace;
+		}
+		if (tablespace_name)
+		{
+			values[2] = CStringGetTextDatum(tablespace_name);
+			nulls[2] = false;
+		}
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	if (tablespace_name)
+		pfree(tablespace_name);
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * replay the creation of a new undo log
+ */
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+	xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	/* Create meta-data space in shared memory. */
+	LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+	/* TODO: assert that it doesn't exist already? */
+
+	slot = allocate_undo_log_slot();
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->logno = xlrec->logno;
+	slot->meta.logno = xlrec->logno;
+	slot->meta.status = UNDO_LOG_STATUS_ACTIVE;
+	slot->meta.category = xlrec->category;
+	slot->meta.tablespace = xlrec->tablespace;
+	slot->meta.unlogged.insert = UndoLogBlockHeaderSize;
+	slot->meta.discard = UndoLogBlockHeaderSize;
+	UndoLogShared->next_logno = Max(xlrec->logno + 1, UndoLogShared->next_logno);
+	LWLockRelease(&slot->mutex);
+	LWLockRelease(UndoLogLock);
+}
+
+/*
+ * replay the addition of a new segment to an undo log
+ */
+static void
+undolog_xlog_extend(XLogReaderState *record)
+{
+	xl_undolog_extend *xlrec = (xl_undolog_extend *) XLogRecGetData(record);
+
+	/* Extend exactly as we would during DO phase. */
+	extend_undo_log(xlrec->logno, xlrec->end);
+}
+
+/*
+ * Drop all buffers for the given undo log, from the old_discard to up
+ * new_discard.  If drop_tail is true, also drop the buffer that holds
+ * new_discard; this is used when discarding undo logs completely, for example
+ * via DROP TABLESPACE.  If it is false, then the final buffer is not dropped
+ * because it may contain data.
+ *
+ */
+static void
+forget_undo_buffers(int logno, UndoLogOffset old_discard,
+					UndoLogOffset new_discard, bool drop_tail)
+{
+	BlockNumber old_blockno;
+	BlockNumber new_blockno;
+	RelFileNode	rnode;
+
+	UndoRecPtrAssignRelFileNode(rnode, MakeUndoRecPtr(logno, old_discard));
+	old_blockno = old_discard / BLCKSZ;
+	new_blockno = new_discard / BLCKSZ;
+	if (drop_tail)
+		++new_blockno;
+	while (old_blockno < new_blockno)
+	{
+		ForgetBuffer(rnode, UndoLogForkNum, old_blockno);
+		ForgetLocalBuffer(rnode, UndoLogForkNum, old_blockno++);
+	}
+}
+/*
+ * replay an undo segment discard record
+ */
+static void
+undolog_xlog_discard(XLogReaderState *record)
+{
+	xl_undolog_discard *xlrec = (xl_undolog_discard *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	UndoLogOffset old_segment_begin;
+	UndoLogOffset new_segment_begin;
+	RelFileNode rnode = {0};
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+	if (slot == NULL)
+		elog(ERROR, "unknown undo log %d", xlrec->logno);
+
+	/*
+	 * We're about to discard undologs. In Hot Standby mode, ensure that
+	 * there's no queries running which need to get tuple from discarded undo.
+	 *
+	 * XXX we are passing empty rnode to the conflict function so that it can
+	 * check conflict in all the backend regardless of which database the
+	 * backend is connected.
+	 */
+	if (InHotStandby && TransactionIdIsValid(xlrec->latestxid))
+		ResolveRecoveryConflictWithSnapshot(xlrec->latestxid, rnode);
+
+	/*
+	 * See if we need to unlink or rename any files, but don't consider it an
+	 * error if we find that files are missing.  Since UndoLogDiscard()
+	 * performs filesystem operations before WAL logging or updating shmem
+	 * which could be checkpointed, a crash could have left files already
+	 * deleted, but we could replay WAL that expects the files to be there.
+	 */
+
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	Assert(slot->logno == xlrec->logno);
+	discard = slot->meta.discard;
+	end = slot->meta.end;
+	LWLockRelease(&slot->mutex);
+
+	/* Drop buffers before we remove/recycle any files. */
+	forget_undo_buffers(xlrec->logno, discard, xlrec->discard,
+						xlrec->entirely_discarded);
+
+	/* Rewind to the start of the segment. */
+	old_segment_begin = discard - discard % UndoLogSegmentSize;
+	new_segment_begin = xlrec->discard - xlrec->discard % UndoLogSegmentSize;
+
+	/* Unlink or rename segments that are no longer in range. */
+	while (old_segment_begin < new_segment_begin)
+	{
+		char	discard_path[MAXPGPATH];
+
+		/* Tell the checkpointer that the file is going away. */
+		undofile_forget_sync(slot->logno,
+							 old_segment_begin / UndoLogSegmentSize,
+							 slot->meta.tablespace);
+
+		UndoLogSegmentPath(xlrec->logno, old_segment_begin / UndoLogSegmentSize,
+						   slot->meta.tablespace, discard_path);
+
+		/* Can we recycle the oldest segment? */
+		if (end < xlrec->end)
+		{
+			char	recycle_path[MAXPGPATH];
+
+			UndoLogSegmentPath(xlrec->logno, end / UndoLogSegmentSize,
+							   slot->meta.tablespace, recycle_path);
+			if (rename(discard_path, recycle_path) == 0)
+			{
+				elog(DEBUG1, "recycled undo segment \"%s\" -> \"%s\"",
+					 discard_path, recycle_path);
+				end += UndoLogSegmentSize;
+			}
+			else
+			{
+				elog(LOG, "could not rename \"%s\" to \"%s\": %m",
+					 discard_path, recycle_path);
+			}
+		}
+		else
+		{
+			if (unlink(discard_path) == 0)
+				elog(DEBUG1, "unlinked undo segment \"%s\"", discard_path);
+			else
+				elog(LOG, "could not unlink \"%s\": %m", discard_path);
+		}
+		old_segment_begin += UndoLogSegmentSize;
+	}
+
+	/* Create any further new segments that are needed the slow way. */
+	while (end < xlrec->end)
+	{
+		allocate_empty_undo_segment(xlrec->logno, slot->meta.tablespace, end);
+		end += UndoLogSegmentSize;
+	}
+
+	/* Flush the directory entries before next checkpoint. */
+	undofile_request_sync_dir(slot->meta.tablespace);
+
+	/* Update shmem. */
+	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+	slot->meta.discard = xlrec->discard;
+	slot->meta.end = end;
+	LWLockRelease(&slot->mutex);
+
+	/* If we discarded everything, the slot can be given up. */
+	if (xlrec->entirely_discarded)
+		free_undo_log_slot(slot);
+}
+
+/*
+ * replay the switch of a undo log
+ */
+static void
+undolog_xlog_switch(XLogReaderState *record)
+{
+	xl_undolog_switch *xlrec = (xl_undolog_switch *) XLogRecGetData(record);
+	UndoLogSlot *slot;
+
+	slot = find_undo_log_slot(xlrec->logno, false);
+
+	/*
+	 * Restore the log switch information in the MyUndoLogState this will be
+	 * reset by following UndoLogAllocateDuringRecovery.
+	 */
+	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+}
+
+void
+undolog_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDOLOG_CREATE:
+			undolog_xlog_create(record);
+			break;
+		case XLOG_UNDOLOG_EXTEND:
+			undolog_xlog_extend(record);
+			break;
+		case XLOG_UNDOLOG_DISCARD:
+			undolog_xlog_discard(record);
+			break;
+		case XLOG_UNDOLOG_SWITCH:
+			undolog_xlog_switch(record);
+		default:
+			elog(PANIC, "undo_redo: unknown op code %u", info);
+	}
+}
+
+/*
+ * For assertions only.
+ */
+bool
+AmAttachedToUndoLogSlot(UndoLogSlot *slot)
+{
+	/*
+	 * In general, we can't access log's members without locking.  But this
+	 * function is intended only for asserting that you are attached, and
+	 * while you're attached the slot can't be recycled, so don't bother
+	 * locking.
+	 */
+	return CurrentSession->attached_undo_slots[slot->meta.category] == slot;
+}
+
+/*
+ * For testing use only.  This function is only used by the test_undo module.
+ */
+void
+UndoLogDetachFull(void)
+{
+	int		i;
+
+	for (i = 0; i < UndoLogCategories; ++i)
+		if (CurrentSession->attached_undo_slots[i])
+			detach_current_undo_log(i, true);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9238fbe..4671838 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -20,6 +20,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/session.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -519,6 +520,8 @@ BootstrapModeMain(void)
 
 	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
 
+	InitializeSession();
+
 	/* Initialize stuff for bootstrap-file processing */
 	for (i = 0; i < MAXATTR; i++)
 	{
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ea4c85e..cbe04e4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1062,6 +1062,10 @@ GRANT SELECT (subdbid, subname, subowner, subenabled, subslotname, subpublicatio
     ON pg_subscription TO public;
 
 
+CREATE VIEW pg_stat_undo_logs AS
+    SELECT *
+    FROM pg_stat_get_undo_logs();
+
 --
 -- We have a few function definitions in here, too.
 -- At some point there might be enough to justify breaking them out into
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 84efb41..17c28b2 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -55,6 +55,7 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -497,6 +498,20 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 	LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
 
 	/*
+	 * Drop the undo logs in this tablespace.  This will fail (without
+	 * dropping anything) if there are undo logs that we can't afford to drop
+	 * because they contain non-discarded data or a transaction is in
+	 * progress.  Since we hold TablespaceCreateLock, no other session will be
+	 * able to attach to an undo log in this tablespace (or any tablespace
+	 * except default) concurrently.
+	 */
+	if (!DropUndoLogsInTablespace(tablespaceoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("tablespace \"%s\" cannot be dropped because it contains non-empty undo logs",
+						tablespacename)));
+
+	/*
 	 * Try to remove the physical infrastructure.
 	 */
 	if (!destroy_tablespace_directories(tablespaceoid, false))
@@ -1517,6 +1532,14 @@ tblspc_redo(XLogReaderState *record)
 	{
 		xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
 
+		/* This shouldn't be able to fail in recovery. */
+		LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
+		if (!DropUndoLogsInTablespace(xlrec->ts_id))
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("tablespace cannot be dropped because it contains non-empty undo logs")));
+		LWLockRelease(TablespaceCreateLock);
+
 		/*
 		 * If we issued a WAL record for a drop tablespace it implies that
 		 * there were no files in it at all when the DROP was done. That means
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index b4f2b28..c742861 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4070,6 +4070,27 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_TWOPHASE_FILE_WRITE:
 			event_name = "TwophaseFileWrite";
 			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_READ:
+			event_name = "UndoCheckpointRead";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_WRITE:
+			event_name = "UndoCheckpointWrite";
+			break;
+		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
+			event_name = "UndoCheckpointSync";
+			break;
+		case WAIT_EVENT_UNDO_FILE_READ:
+			event_name = "UndoFileRead";
+			break;
+		case WAIT_EVENT_UNDO_FILE_WRITE:
+			event_name = "UndoFileWrite";
+			break;
+		case WAIT_EVENT_UNDO_FILE_FLUSH:
+			event_name = "UndoFileFlush";
+			break;
+		case WAIT_EVENT_UNDO_FILE_SYNC:
+			event_name = "UndoFileSync";
+			break;
 		case WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ:
 			event_name = "WALSenderTimelineHistoryRead";
 			break;
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index d5f9b61..abcb5e5 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1368,7 +1368,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	char	   *page;
 	size_t		pad;
 	PageHeader	phdr;
-	int			segmentno = 0;
+	BlockNumber	first_blkno = 0;
 	char	   *segmentpath;
 	bool		verify_checksum = false;
 
@@ -1406,12 +1406,18 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 			segmentpath = strstr(filename, ".");
 			if (segmentpath != NULL)
 			{
-				segmentno = atoi(segmentpath + 1);
-				if (segmentno == 0)
+				char	   *end;
+				if (strstr(readfilename, "undo"))
+					first_blkno = strtol(segmentpath + 1, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath + 1, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 					ereport(ERROR,
-							(errmsg("invalid segment number %d in file \"%s\"",
-									segmentno, filename)));
+							(errmsg("invalid segment number in file \"%s\"",
+									filename)));
 			}
+			else
+				first_blkno = 0;
 		}
 	}
 
@@ -1451,7 +1457,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + first_blkno);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 151c3ef..d3a9c4d 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -154,6 +154,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_COMMIT_TS_ID:
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
+		case RM_UNDOLOG_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 6f3a402..2b1d606 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -177,6 +177,7 @@ static PrivateRefCountEntry *NewPrivateRefCountEntry(Buffer buffer);
 static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move);
 static inline int32 GetPrivateRefCount(Buffer buffer);
 static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref);
+static void InvalidateBuffer(BufferDesc *buf);
 
 /*
  * Ensure that the PrivateRefCountArray has sufficient space to store one more
@@ -620,10 +621,12 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
  * valid, the page is zeroed instead of throwing an error. This is intended
  * for non-critical data, where the caller is prepared to repair errors.
  *
- * In RBM_ZERO_AND_LOCK mode, if the page isn't in buffer cache already, it's
+ * In RBM_ZERO mode, if the page isn't in buffer cache already, it's
  * filled with zeros instead of reading it from disk.  Useful when the caller
  * is going to fill the page from scratch, since this saves I/O and avoids
  * unnecessary failure if the page-on-disk has corrupt page headers.
+ *
+ * In RBM_ZERO_AND_LOCK mode, the page is zeroed and also locked.
  * The page is returned locked to ensure that the caller has a chance to
  * initialize the page before it's made visible to others.
  * Caution: do not use this mode to read a page that is beyond the relation's
@@ -674,24 +677,20 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 /*
  * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
  *		a relcache entry for the relation.
- *
- * NB: At present, this function may only be used on permanent relations, which
- * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary or unlogged relations, we could pass additional
- * parameters.
  */
 Buffer
 ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
 						  BlockNumber blockNum, ReadBufferMode mode,
-						  BufferAccessStrategy strategy)
+						  BufferAccessStrategy strategy,
+						  char relpersistence)
 {
 	bool		hit;
 
-	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
-
-	Assert(InRecovery);
+	SMgrRelation smgr = smgropen(rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+	return ReadBuffer_common(smgr, relpersistence, forkNum, blockNum,
 							 mode, strategy, &hit);
 }
 
@@ -885,7 +884,9 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * Read in the page, unless the caller intends to overwrite it and
 		 * just wants us to allocate a buffer.
 		 */
-		if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
+		if (mode == RBM_ZERO ||
+			mode == RBM_ZERO_AND_LOCK ||
+			mode == RBM_ZERO_AND_CLEANUP_LOCK)
 			MemSet((char *) bufBlock, 0, BLCKSZ);
 		else
 		{
@@ -1340,6 +1341,61 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 }
 
 /*
+ * ForgetBuffer -- drop a buffer from shared buffers
+ *
+ * If the buffer isn't present in shared buffers, nothing happens.  If it is
+ * present, it is discarded without making any attempt to write it back out to
+ * the operating system.  The caller must therefore somehow be sure that the
+ * data won't be needed for anything now or in the future.  It assumes that
+ * there is no concurrent access to the block, except that it might be being
+ * concurrently written.
+ */
+void
+ForgetBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
+	BufferTag	tag;			/* identity of target block */
+	uint32		hash;			/* hash value for tag */
+	LWLock	   *partitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	hash = BufTableHashCode(&tag);
+	partitionLock = BufMappingPartitionLock(hash);
+
+	/* see if the block is in the buffer pool */
+	LWLockAcquire(partitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&tag, hash);
+	LWLockRelease(partitionLock);
+
+	/* didn't find it, so nothing to do */
+	if (buf_id < 0)
+		return;
+
+	/* take the buffer header lock */
+	bufHdr = GetBufferDescriptor(buf_id);
+	buf_state = LockBufHdr(bufHdr);
+
+	/*
+	 * The buffer might been evicted after we released the partition lock and
+	 * before we acquired the buffer header lock.  If so, the buffer we've
+	 * locked might contain some other data which we shouldn't touch. If the
+	 * buffer hasn't been recycled, we proceed to invalidate it.
+	 */
+	if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+		bufHdr->tag.blockNum == blockNum &&
+		bufHdr->tag.forkNum == forkNum)
+		InvalidateBuffer(bufHdr);		/* releases spinlock */
+	else
+		UnlockBufHdr(bufHdr, buf_state);
+}
+
+/*
  * InvalidateBuffer -- mark a shared buffer invalid and return it to the
  * freelist.
  *
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index f5f6a29..a6fe3db 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -273,6 +273,49 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 }
 
 /*
+ * ForgetLocalBuffer - drop a buffer from local buffers
+ *
+ * This is similar to bufmgr.c's ForgetBuffer, except that we do not need
+ * to do any locking since this is all local.  As with that function, this
+ * must be used very carefully, since we'll cheerfully throw away dirty
+ * buffers without any attempt to write them.
+ */
+void
+ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum)
+{
+	SMgrRelation smgr = smgropen(rnode, BackendIdForTempRelations());
+	BufferTag	tag;					/* identity of target block */
+	LocalBufferLookupEnt *hresult;
+	BufferDesc *bufHdr;
+	uint32		buf_state;
+
+	/*
+	 * If somehow this is the first request in the session, there's nothing to
+	 * do.  (This probably shouldn't happen, though.)
+	 */
+	if (LocalBufHash == NULL)
+		return;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum);
+
+	/* see if the block is in the local buffer pool */
+	hresult = (LocalBufferLookupEnt *)
+	hash_search(LocalBufHash, (void *) &tag, HASH_REMOVE, NULL);
+
+	/* didn't find it, so nothing to do */
+	if (!hresult)
+		return;
+
+	/* mark buffer invalid */
+	bufHdr = GetLocalBufferDescriptor(hresult->id);
+	CLEAR_BUFFERTAG(bufHdr->tag);
+	buf_state = pg_atomic_read_u32(&bufHdr->state);
+	buf_state &= ~(BM_VALID | BM_TAG_VALID | BM_DIRTY);
+	pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
+}
+
+/*
  * MarkLocalBufferDirty -
  *	  mark a local buffer dirty
  */
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 315c74c..3c4f053 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -322,7 +322,6 @@ static void pre_sync_fname(const char *fname, bool isdir, int elevel);
 static void datadir_fsync_fname(const char *fname, bool isdir, int elevel);
 static void unlink_if_exists_fname(const char *fname, bool isdir, int elevel);
 
-static int	fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel);
 static int	fsync_parent_path(const char *fname, int elevel);
 
 
@@ -3345,7 +3344,7 @@ unlink_if_exists_fname(const char *fname, bool isdir, int elevel)
  *
  * Returns 0 if the operation succeeded, -1 otherwise.
  */
-static int
+int
 fsync_fname_ext(const char *fname, bool isdir, bool ignore_perm, int elevel)
 {
 	int			fd;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index d7d7335..12c3249 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/subtrans.h"
 #include "access/twophase.h"
+#include "access/undolog.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -125,6 +126,7 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, ProcGlobalShmemSize());
 		size = add_size(size, XLOGShmemSize());
 		size = add_size(size, CLOGShmemSize());
+		size = add_size(size, UndoLogShmemSize());
 		size = add_size(size, CommitTsShmemSize());
 		size = add_size(size, SUBTRANSShmemSize());
 		size = add_size(size, TwoPhaseShmemSize());
@@ -213,6 +215,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	 */
 	XLOGShmemInit();
 	CLOGShmemInit();
+	UndoLogShmemInit();
 	CommitTsShmemInit();
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index bc1aa88..5df658d 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -522,6 +522,8 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append");
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_HASH_JOIN, "parallel_hash_join");
 	LWLockRegisterTranche(LWTRANCHE_SXACT, "serializable_xact");
+	LWLockRegisterTranche(LWTRANCHE_UNDOLOG, "undo_log");
+	LWLockRegisterTranche(LWTRANCHE_UNDODISCARD, "undo_discard");
 
 	/* Register named tranches. */
 	for (i = 0; i < NamedLWLockTrancheRequests; i++)
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index db47843..4b42a1c 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -49,3 +49,4 @@ MultiXactTruncationLock				41
 OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
+UndoLogLock                                      45
diff --git a/src/backend/storage/smgr/Makefile b/src/backend/storage/smgr/Makefile
index e486b7c..ff2e5e2 100644
--- a/src/backend/storage/smgr/Makefile
+++ b/src/backend/storage/smgr/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/storage/smgr
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = md.o smgr.o
+OBJS = md.o smgr.o undofile.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index d00b275..93df85c 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,11 +17,13 @@
  */
 #include "postgres.h"
 
+#include "catalog/database_internal.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/smgr.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
 
@@ -81,6 +83,24 @@ static const f_smgr smgrsw[] = {
 		.smgr_nblocks = mdnblocks,
 		.smgr_truncate = mdtruncate,
 		.smgr_immedsync = mdimmedsync,
+	},
+	/* undo logs */
+	{
+		.smgr_init = undofile_init,
+		.smgr_shutdown = undofile_shutdown,
+		.smgr_open = undofile_open,
+		.smgr_close = undofile_close,
+		.smgr_create = undofile_create,
+		.smgr_exists = undofile_exists,
+		.smgr_unlink = undofile_unlink,
+		.smgr_extend = undofile_extend,
+		.smgr_prefetch = undofile_prefetch,
+		.smgr_read = undofile_read,
+		.smgr_write = undofile_write,
+		.smgr_writeback = undofile_writeback,
+		.smgr_nblocks = undofile_nblocks,
+		.smgr_truncate = undofile_truncate,
+		.smgr_immedsync = undofile_immedsync,
 	}
 };
 
@@ -105,6 +125,8 @@ smgrwhich(RelFileNode rnode)
 {
 	switch (rnode.dbNode)
 	{
+		case UndoDbOid:
+			return 1;			/* undofile.c */
 		default:
 			return 0;			/* md.c */
 	}
@@ -189,6 +211,7 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = smgrwhich(rnode);
+		reln->private_data = NULL;
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
new file mode 100644
index 0000000..04d4514
--- /dev/null
+++ b/src/backend/storage/smgr/undofile.c
@@ -0,0 +1,422 @@
+/*
+ * undofile.h
+ *
+ * PostgreSQL undo file manager.  This module provides SMGR-compatible
+ * interface to the files that back undo logs on the filesystem, so that undo
+ * log data can use the shared buffer pool.  Other aspects of undo log
+ * management are provided by undolog.c, so the SMGR interfaces not directly
+ * concerned with reading, writing and flushing data are unimplemented.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/storage/smgr/undofile.c
+ */
+
+#include "postgres.h"
+
+#include "access/undolog.h"
+#include "access/xlog.h"
+#include "catalog/database_internal.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgwriter.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "storage/undofile.h"
+#include "utils/memutils.h"
+
+/* Populate a file tag describing an undo segment file. */
+#define INIT_UNDOFILETAG(a,xx_logno,xx_tbspc,xx_segno) \
+( \
+	memset(&(a), 0, sizeof(FileTag)), \
+	(a).handler = SYNC_HANDLER_UNDO, \
+	(a).rnode.dbNode = UndoDbOid, \
+	(a).rnode.spcNode = (xx_tbspc), \
+	(a).rnode.relNode = (xx_logno), \
+	(a).segno = (xx_segno) \
+)
+
+/*
+ * While md.c expects random access and has a small number of huge
+ * segments, undofile.c manages a potentially very large number of smaller
+ * segments and has a less random access pattern.  Therefore, instead of
+ * keeping a potentially huge array of vfds we'll just keep the most
+ * recently accessed N.
+ *
+ * For now, N == 1, so we just need to hold onto one 'File' handle.
+ */
+typedef struct UndoFileState
+{
+	int		mru_segno;
+	File	mru_file;
+} UndoFileState;
+
+static MemoryContext UndoFileCxt;
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok);
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno);
+
+void
+undofile_init(void)
+{
+	UndoFileCxt = AllocSetContextCreate(TopMemoryContext,
+										"UndoFileSmgr",
+										ALLOCSET_DEFAULT_SIZES);
+}
+
+void
+undofile_shutdown(void)
+{
+}
+
+void
+undofile_open(SMgrRelation reln)
+{
+	UndoFileState *state;
+
+	state = MemoryContextAllocZero(UndoFileCxt, sizeof(UndoFileState));
+	reln->private_data = state;
+}
+
+void
+undofile_close(SMgrRelation reln, ForkNumber forknum)
+{
+}
+
+void
+undofile_create(SMgrRelation reln, ForkNumber forknum, bool isRedo)
+{
+	/*
+	 * File creation is managed by undolog.c, but xlogutils.c likes to call
+	 * this just in case.  Ignore.
+	 */
+}
+
+bool
+undofile_exists(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_exists is not supported");
+
+	return false;		/* not reached */
+}
+
+void
+undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo)
+{
+	elog(ERROR, "undofile_unlink is not supported");
+}
+
+void
+undofile_extend(SMgrRelation reln, ForkNumber forknum,
+				BlockNumber blocknum, char *buffer,
+				bool skipFsync)
+{
+	elog(ERROR, "undofile_extend is not supported");
+}
+
+void
+undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
+{
+	elog(ERROR, "undofile_prefetch is not supported");
+}
+
+void
+undofile_read(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+			  char *buffer)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileRead(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_READ);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+}
+
+void
+undofile_write(SMgrRelation reln, ForkNumber forknum,
+			   BlockNumber blocknum, char *buffer,
+			   bool skipFsync)
+{
+	File		file;
+	off_t		seekpos;
+	int			nbytes;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+	nbytes = FileWrite(file, buffer, BLCKSZ, seekpos, WAIT_EVENT_UNDO_FILE_WRITE);
+	if (nbytes != BLCKSZ)
+	{
+		if (nbytes < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not write block %u in file \"%s\": %m",
+							blocknum, FilePathName(file))));
+		/*
+		 * short write: unexpected, because this should be overwriting an
+		 * entirely pre-allocated segment file
+		 */
+		ereport(ERROR,
+				(errcode(ERRCODE_DISK_FULL),
+				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
+						blocknum, FilePathName(file),
+						nbytes, BLCKSZ)));
+	}
+
+	/* Tell checkpointer this file is dirty. */
+	if (!skipFsync && !SmgrIsTemp(reln))
+	{
+		undofile_request_sync(reln->smgr_rnode.node.relNode,
+							  blocknum / UNDOSEG_SIZE,
+							  reln->smgr_rnode.node.spcNode);
+	}
+}
+
+void
+undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+				   BlockNumber blocknum, BlockNumber nblocks)
+{
+	while (nblocks > 0)
+	{
+		File	file;
+		int		nflush;
+
+		file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+
+		/* compute number of desired writes within the current segment */
+		nflush = Min(nblocks,
+					 1 + UNDOSEG_SIZE - (blocknum % UNDOSEG_SIZE));
+
+		FileWriteback(file,
+					  (blocknum % UNDOSEG_SIZE) * BLCKSZ,
+					  nflush * BLCKSZ, WAIT_EVENT_UNDO_FILE_FLUSH);
+
+		nblocks -= nflush;
+		blocknum += nflush;
+	}
+}
+
+BlockNumber
+undofile_nblocks(SMgrRelation reln, ForkNumber forknum)
+{
+	/*
+	 * xlogutils.c likes to call this to decide whether to read or extend; for
+	 * now we lie and say the relation is big as possible.
+	 */
+	return UndoLogMaxSize / BLCKSZ;
+}
+
+void
+undofile_truncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
+{
+	elog(ERROR, "undofile_truncate is not supported");
+}
+
+void
+undofile_immedsync(SMgrRelation reln, ForkNumber forknum)
+{
+	elog(ERROR, "undofile_immedsync is not supported");
+}
+
+static File undofile_open_segment_file(Oid relNode, Oid spcNode,
+									   BlockNumber segno, bool missing_ok)
+{
+	File		file;
+	char		path[MAXPGPATH];
+
+	UndoLogSegmentPath(relNode, segno, spcNode, path);
+	file = PathNameOpenFile(path, O_RDWR | PG_BINARY);
+
+	if (file <= 0 && (!missing_ok || errno != ENOENT))
+		elog(ERROR, "cannot open undo segment file '%s': %m", path);
+
+	return file;
+}
+
+/*
+ * Get a File for a particular segment of a SMgrRelation representing an undo
+ * log.
+ */
+static File undofile_get_segment_file(SMgrRelation reln, BlockNumber segno)
+{
+	UndoFileState *state = (UndoFileState *) reln->private_data;
+
+	/* If we have a file open already, check if we need to close it. */
+	if (state->mru_file > 0 && state->mru_segno != segno)
+	{
+		/* These are not the blocks we're looking for. */
+		FileClose(state->mru_file);
+		state->mru_file = 0;
+	}
+
+	/* Check if we need to open a new file. */
+	if (state->mru_file <= 0)
+	{
+		state->mru_file =
+			undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+									   reln->smgr_rnode.node.spcNode,
+									   segno, InRecovery);
+		if (InRecovery && state->mru_file <= 0)
+		{
+			/*
+			 * If in recovery, we may be trying to access a file that will
+			 * later be unlinked.  Tolerate missing files, creating a new
+			 * zero-filled file as required.
+			 */
+			UndoLogNewSegment(reln->smgr_rnode.node.relNode,
+							  reln->smgr_rnode.node.spcNode,
+							  segno);
+			state->mru_file =
+				undofile_open_segment_file(reln->smgr_rnode.node.relNode,
+										   reln->smgr_rnode.node.spcNode,
+										   segno, false);
+			Assert(state->mru_file > 0);
+		}
+		state->mru_segno = segno;
+	}
+
+	return state->mru_file;
+}
+
+/*
+ * Callback to handle a queued sync request.
+ */
+int
+undofile_syncfiletag(const FileTag *tag, char *path)
+{
+	SMgrRelation reln = smgropen(tag->rnode, InvalidBackendId);
+	File		file;
+
+	if (tag->rnode.relNode == (Oid) InvalidUndoLogNumber)
+	{
+		/* Sync parent directory for this tablespace. */
+		UndoLogDirectory(tag->rnode.spcNode, path);
+
+		/* The caller (sync.c) will do appropriate error reporting. */
+		return fsync_fname_ext(path, true, false, WARNING);
+	}
+	else
+	{
+		/* Sync a segment file. */
+		UndoLogSegmentPath(tag->rnode.relNode, tag->segno, tag->rnode.spcNode,
+						   path);
+
+		file = undofile_get_segment_file(reln, tag->segno);
+		if (file <= 0)
+		{
+			/* errno set by undofile_get_segment_file() */
+			return -1;
+		}
+
+		return FileSync(file, WAIT_EVENT_UNDO_FILE_SYNC);
+	}
+}
+
+/*
+ * Filtering callback used by SYNC_FILTER_REQUEST to forget some requests.
+ */
+bool
+undofile_filetagmatches(const FileTag *tag, const FileTag *candidate)
+{
+	/*
+	 * We use SYNC_FILTER_REQUEST to forget requests for a given tablespace,
+	 * before removing all undo files in the tablespace.
+	 */
+	return tag->rnode.spcNode == candidate->rnode.spcNode;
+}
+
+/*
+ * Tell the checkpointer to sync a segment file.
+ */
+void
+undofile_request_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync file \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about any sync requests for a given segment
+ * file, because it's about to go away.
+ */
+void
+undofile_forget_sync(UndoLogNumber logno, BlockNumber segno, Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, logno, tablespace, segno);
+
+	/* Send, and keep retrying if out of space. */
+	(void) RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
+}
+
+/*
+ * Tell the checkpointer to fsync the undo directory in a given tablespace,
+ * because we have created or renamed files inside it.
+ */
+void
+undofile_request_sync_dir(Oid tablespace)
+{
+	char		path[MAXPGPATH];
+	FileTag		tag;
+
+	/* We use a special logno and segno to mean "the directory". */
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/* Try to send to the checkpointer, but if out of space, do it here. */
+	if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+	{
+		if (undofile_syncfiletag(&tag, path) < 0)
+			ereport(data_sync_elevel(ERROR),
+					(errmsg("could not fsync directory \"%s\": %m", path)));
+	}
+}
+
+/*
+ * Tell the checkpointer to forget about all sync requests for a given
+ * tablespace, because it's about to go away.
+ */
+void
+undofile_forget_sync_tablespace(Oid tablespace)
+{
+	FileTag		tag;
+
+	INIT_UNDOFILETAG(tag, (Oid) InvalidUndoLogNumber, tablespace,
+					 InvalidBlockNumber);
+
+	/*
+	 * Tell checkpointer to forget about any request for this tag, and keep
+	 * waiting if there is not enough space.
+	 */
+	(void) RegisterSyncRequest(&tag, SYNC_FILTER_REQUEST, true);
+}
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index f329c3f..db7b417 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -28,6 +28,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/md.h"
+#include "storage/undofile.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/inval.h"
@@ -96,6 +97,11 @@ static const SyncOps syncsw[] = {
 		.sync_syncfiletag = mdsyncfiletag,
 		.sync_unlinkfiletag = mdunlinkfiletag,
 		.sync_filetagmatches = mdfiletagmatches
+	},
+	/* undo log segment files */
+	{
+		.sync_syncfiletag = undofile_syncfiletag,
+		.sync_filetagmatches = undofile_filetagmatches
 	}
 };
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 43b9f17..5d9af89 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -25,6 +25,7 @@
 #include "access/session.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/undolog.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -561,6 +562,7 @@ BaseInit(void)
 	InitSync();
 	smgrinit();
 	InitBufferPoolAccess();
+	UndoLogInit();
 }
 
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fc46360..296fb77 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -121,6 +121,7 @@ extern int	CommitDelay;
 extern int	CommitSiblings;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
+extern char *undo_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
 
@@ -3668,6 +3669,17 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		{"undo_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the tablespace(s) to use for undo logs."),
+			NULL,
+			GUC_LIST_INPUT | GUC_LIST_QUOTE
+		},
+		&undo_tablespaces,
+		"",
+		check_undo_tablespaces, assign_undo_tablespaces, NULL
+	},
+
+	{
 		{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
 			gettext_noop("Sets the path for dynamically loadable modules."),
 			gettext_noop("If a dynamically loadable module needs to be opened and "
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 04d77ad..bbcbdfd 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -210,11 +210,13 @@ static const char *const subdirs[] = {
 	"pg_snapshots",
 	"pg_subtrans",
 	"pg_twophase",
+	"pg_undo",
 	"pg_multixact",
 	"pg_multixact/members",
 	"pg_multixact/offsets",
 	"base",
 	"base/1",
+	"base/undo",
 	"pg_replslot",
 	"pg_tblspc",
 	"pg_stat",
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 8c00ec9..8e83be9 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -167,7 +167,7 @@ skipfile(const char *fn)
 }
 
 static void
-scan_file(const char *fn, BlockNumber segmentno)
+scan_file(const char *fn, BlockNumber first_blkno)
 {
 	PGAlignedBlock buf;
 	PageHeader	header = (PageHeader) buf.data;
@@ -208,7 +208,7 @@ scan_file(const char *fn, BlockNumber segmentno)
 		if (PageIsNew(header))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + first_blkno);
 		current_size += r;
 		if (mode == PG_MODE_CHECK)
 		{
@@ -310,7 +310,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			char		fnonly[MAXPGPATH];
 			char	   *forkpath,
 					   *segmentpath;
-			BlockNumber segmentno = 0;
+			BlockNumber first_blkno;
 
 			if (skipfile(de->d_name))
 				continue;
@@ -325,15 +325,22 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			segmentpath = strchr(fnonly, '.');
 			if (segmentpath != NULL)
 			{
+				char	   *end;
+
 				*segmentpath++ = '\0';
-				segmentno = atoi(segmentpath);
-				if (segmentno == 0)
+				if (strstr(segmentpath, "undo"))
+					first_blkno = strtol(segmentpath, &end, 16) / BLCKSZ;
+				else
+					first_blkno = strtol(segmentpath, &end, 10) * RELSEG_SIZE;
+				if (*end != '\0')
 				{
-					pg_log_error("invalid segment number %d in file name \"%s\"",
-								 segmentno, fn);
+					pg_log_error("invalid segment number in file name \"%s\"",
+								 fn);
 					exit(1);
 				}
 			}
+			else
+				first_blkno = 0;
 
 			forkpath = strchr(fnonly, '_');
 			if (forkpath != NULL)
@@ -350,7 +357,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 			 * the items in the data folder.
 			 */
 			if (!sizeonly)
-				scan_file(fn, segmentno);
+				scan_file(fn, first_blkno);
 		}
 #ifndef WIN32
 		else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index ff0f8ea..ad96fe7 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -80,6 +80,7 @@ static bool ReadControlFile(void);
 static void GuessControlValues(void);
 static void PrintControlValues(bool guessed);
 static void PrintNewControlValues(void);
+static void AdjustRedoLocation(const char *DataDir);
 static void RewriteControlFile(void);
 static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
@@ -510,6 +511,7 @@ main(int argc, char *argv[])
 	/*
 	 * Else, do the dirty deed.
 	 */
+	AdjustRedoLocation(DataDir);
 	RewriteControlFile();
 	KillExistingXLOG();
 	KillExistingArchiveStatus();
@@ -890,6 +892,80 @@ PrintNewControlValues(void)
 
 
 /*
+ * Compute the new redo, and move the pg_undo file to match if necessary.
+ * Rather than renaming it, we'll create a new copy, so that a failure that
+ * occurs before the controlfile is rewritten won't be fatal.
+ */
+static void
+AdjustRedoLocation(const char *DataDir)
+{
+	uint64		old_redo = ControlFile.checkPointCopy.redo;
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	int			old_fd;
+	int			new_fd;
+	ssize_t		nread;
+	ssize_t		nwritten;
+	char		buffer[1024];
+
+	/*
+	 * Adjust fields as needed to force an empty XLOG starting at
+	 * newXlogSegNo.
+	 */
+	XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD, WalSegSz,
+							ControlFile.checkPointCopy.redo);
+
+	/* If the redo location didn't move, we don't need to do anything. */
+	if (old_redo == ControlFile.checkPointCopy.redo)
+		return;
+
+	/*
+	 * Otherwise we copy the pg_undo file, because its name must match the redo
+	 * location.
+	 */
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "pg_undo/%016" INT64_MODIFIER "X",
+			 old_redo);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "pg_undo/%016" INT64_MODIFIER "X",
+			 ControlFile.checkPointCopy.redo);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+	{
+		pg_log_error("could not open \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	new_fd = open(new_pg_undo_path, O_RDWR | O_CREAT, 0644);
+	if (new_fd < 0)
+	{
+		pg_log_error("could not create \"%s\": %m", new_pg_undo_path);
+		exit(1);
+	}
+	while ((nread = read(old_fd, buffer, sizeof(buffer))) > 0)
+	{
+		do
+		{
+			nwritten = write(new_fd, buffer, nread);
+			if (nwritten < 0)
+			{
+				pg_log_error("could not write to \"%s\": %m", new_pg_undo_path);
+				exit(1);
+			}
+			nread -= nwritten;
+		} while (nread > 0);
+	}
+	if (nread < 0)
+	{
+		pg_log_error("could not read from \"%s\": %m", old_pg_undo_path);
+		exit(1);
+	}
+	close(old_fd);
+	close(new_fd);
+}
+
+/*
  * Write out the new pg_control file.
  */
 static void
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 2cb829a..82c8033 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -9,7 +9,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = check.o controldata.o dump.o exec.o file.o function.o info.o \
        option.o parallel.o pg_upgrade.o relfilenode.o server.o \
-       tablespace.o util.o version.o $(WIN32RES)
+       tablespace.o undo.o util.o version.o $(WIN32RES)
 
 override CPPFLAGS := -DDLSUFFIX=\"$(DLSUFFIX)\" -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 617270f..4a431dc 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -21,6 +21,7 @@ static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb);
 static bool equivalent_locale(int category, const char *loca, const char *locb);
 static void check_is_install_user(ClusterInfo *cluster);
 static void check_proper_datallowconn(ClusterInfo *cluster);
+static void check_for_undo_data(ClusterInfo *cluster);
 static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
@@ -97,6 +98,7 @@ check_and_dump_old_cluster(bool live_check)
 	 */
 	check_is_install_user(&old_cluster);
 	check_proper_datallowconn(&old_cluster);
+	check_for_undo_data(&old_cluster);
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
@@ -171,6 +173,7 @@ check_new_cluster(void)
 
 	check_is_install_user(&new_cluster);
 
+	check_for_undo_data(&new_cluster);
 	check_for_prepared_transactions(&new_cluster);
 }
 
@@ -801,6 +804,46 @@ check_for_prepared_transactions(ClusterInfo *cluster)
 
 
 /*
+ *	check_for_live_undo_data()
+ *
+ *	Make sure there are no live undo records (aborted transactions that have
+ *	not been rolled back, or committed transactions whose undo data has not
+ *	yet been discarded).
+ */
+static void
+check_for_undo_data(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn;
+
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1300)
+		return;
+
+	conn = connectToServer(cluster, "template1");
+	prep_status("Checking for undo data");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_stat_undo_logs "
+							"WHERE discard != insert");
+
+	if (PQntuples(res) != 0)
+	{
+		if (cluster == &old_cluster)
+			pg_fatal("The source cluster contains live undo data\n");
+		else
+			pg_fatal("The target cluster contains live undo data\n");
+	}
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
+
+
+/*
  *	check_for_isn_and_int8_passing_mismatch()
  *
  *	contrib/isn relies on data type int8, and in 8.4 int8 can now be passed
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 3823641..523e86e 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -59,6 +59,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	bool		got_redo_location = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -485,6 +486,23 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "Latest checkpoint's REDO location:")) != NULL)
+		{
+			uint32		hi;
+			uint32		lo;
+
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			p++;				/* remove ':' char */
+
+			if (sscanf(p, "%X/%X", &hi, &lo) != 2)
+				pg_fatal("%d: controldata cannot parse REDO location\n", __LINE__);
+			cluster->controldata.redo_location = (((uint64) hi) << 32) | lo;
+			got_redo_location = true;
+		}
 	}
 
 	pclose(output);
@@ -528,6 +546,13 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		}
 	}
 
+	/*
+	 * If we used pg_resetwal instead of pg_controldata, there is no REDO
+	 * location.
+	 */
+	if (!got_redo_location)
+		cluster->controldata.redo_location = 0;
+
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi ||
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 0363309..f23451a 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -351,6 +351,10 @@ check_data_dir(ClusterInfo *cluster)
 		check_single_dir(pg_data, "pg_clog");
 	else
 		check_single_dir(pg_data, "pg_xact");
+
+	/* pg_undo is new in v13 */
+	if (GET_MAJOR_VERSION(cluster->major_version) >= 1300)
+		check_single_dir(pg_data, "pg_undo");
 }
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index d1975aa..09b786f 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -139,6 +139,8 @@ main(int argc, char **argv)
 
 	/* New now using xids of the old system */
 
+	merge_undo_logs();
+
 	/* -- NEW -- */
 	start_postmaster(&new_cluster, true);
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 5d31750..d0d93c0 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -227,6 +227,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	uint64		redo_location;
 } ControlData;
 
 /*
@@ -465,3 +466,7 @@ void		parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr
 										  char *old_pgdata, char *new_pgdata,
 										  char *old_tablespace);
 bool		reap_child(bool wait_for_child);
+
+/* undo.c */
+
+void		merge_undo_logs(void);
diff --git a/src/bin/pg_upgrade/undo.c b/src/bin/pg_upgrade/undo.c
new file mode 100644
index 0000000..48397b0
--- /dev/null
+++ b/src/bin/pg_upgrade/undo.c
@@ -0,0 +1,292 @@
+/*
+ *	undo.c
+ *
+ *	Support for upgrading undo logs.\
+ *	Copyright (c) 2019, PostgreSQL Global Development Group
+ *	src/bin/pg_upgrade/undo.c
+ */
+
+
+#include "postgres_fe.h"
+#include "pg_upgrade.h"
+#include "access/undolog.h"
+
+/*
+ * The relevant parts of UndoLogMetaDataData, in a version-independent format.
+ */
+typedef struct
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogStatus status;
+	UndoLogCategory category;
+	Oid			tablespace;
+} UndoLogInfo;
+
+/*
+ * Read the header of a pg_undo file and extract basic information.  If the
+ * format of the header changes in later versions, this may need to change
+ * depending on "cluster".
+ */
+static void
+read_pg_undo_header(int fd, ClusterInfo *cluster, UndoLogNumber *low_logno,
+					UndoLogNumber *next_logno, UndoLogNumber *num_logs)
+{
+	pg_crc32c crc;
+
+	/* Read the header, much like StartupUndoLogs(). */
+	if (read(fd, low_logno, sizeof(*low_logno)) != sizeof(*low_logno) ||
+		read(fd, next_logno, sizeof(*next_logno)) != sizeof(*next_logno) ||
+		read(fd, num_logs, sizeof(*num_logs)) != sizeof(*num_logs) ||
+		read(fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("pg_undo file is corrupted or cannot be read\n");
+}
+
+/*
+ * Read a single UndoLogMetaData object.  If the format changes in later
+ * versions, this may need to change to be able to read different structs
+ * depending on "cluster".
+ */
+static void
+read_one_undo_log(int fd, ClusterInfo *cluster, UndoLogInfo *info)
+{
+	UndoLogMetaData meta_data;
+	int		rc;
+
+	rc = read(fd, &meta_data, sizeof(meta_data));
+	if (rc < 0)
+		pg_fatal("could not read undo log meta-data: %m");
+	else if (rc != sizeof(meta_data))
+		pg_fatal("could not read undo log meta-data: expect %zu bytes but read only %d bytes",
+				 sizeof(meta_data), rc);
+
+	info->logno = meta_data.logno;
+	info->category = meta_data.category;
+	info->tablespace = meta_data.tablespace;
+	info->discard = meta_data.discard;
+	info->status = meta_data.status;
+}
+
+static void
+merge_undo_log(UndoLogInfo *logs, UndoLogNumber *num_logs,
+			   const UndoLogInfo *info)
+{
+	UndoLogNumber i;
+
+	/* Do we already have an entry for this logno? */
+	for (i = 0; i < *num_logs; ++i)
+	{
+		if (logs[i].logno == info->logno)
+		{
+			/*
+			 * Take the highest discard offset, so that any pointers that
+			 * originated in either cluster appear to be discarded.
+			 */
+			if (logs[i].discard < info->discard)
+				logs[i].discard = info->discard;
+
+			/*
+			 * Take the highest status so that entirely discarded logs trump
+			 * active logs.
+			 */
+			StaticAssertStmt(UNDO_LOG_STATUS_ACTIVE < UNDO_LOG_STATUS_FULL,
+							 "undo log status out of order");
+			StaticAssertStmt(UNDO_LOG_STATUS_FULL < UNDO_LOG_STATUS_DISCARDED,
+							 "undo log status out of order");
+			if (logs[i].status < info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the most persistent persistence level.  While we could
+			 * just convert them all to permanent and it wouldn't hurt, it's
+			 * probably a better idea to keep about the same number of each
+			 * persistence level, so that a system that has stabilized with
+			 * those numbers will continue to be stable after the upgrade (ie
+			 * not suddenly need to create more undo logs of different
+			 * levels).  The most permanent is the best choice, because TEMP
+			 * undo logs might be rewound in future.
+			 */
+			StaticAssertStmt(UNDO_PERMANENT < UNDO_UNLOGGED,
+							 "undo log persistent out of order");
+			StaticAssertStmt(UNDO_UNLOGGED < UNDO_TEMP,
+							 "undo log persistent out of order");
+			if (logs[i].status > info->status)
+				logs[i].status = info->status;
+
+			/*
+			 * Take the highest tablespace OID.  The choice of 'highest' is
+			 * arbitrary (we don't really expect the new cluster to have more
+			 * than one log), but it seems useful to preserve the distribution
+			 * of tablespaces from the old cluster for stability, as above.
+			 */
+			if (logs[i].tablespace < info->tablespace)
+				logs[i].tablespace = info->tablespace;
+			break;
+		}
+	}
+
+	/* Otherwise create a new entry. */
+	logs[*num_logs++] = *info;
+}
+
+/*
+ * We need to merge the old undo logs and the new undo logs.  We know that
+ * there is no live undo data (see check_for_live_undo_data()), but we need to
+ * make sure that any undo record pointers that exist in the old OR new
+ * cluster appear as discarded.  That is, any log numbers that are entirely
+ * discarded in either cluster appear as entirely discarded, and we retain
+ * the higher of the discard pointers in any log that is active.  This is
+ * mostly a theoretical concern for now, but perhaps a future release will be
+ * able to create higher undo record pointers during initdb than the old
+ * cluster had, so let's use an algorithm that doesn't make any assumptions
+ * about that.
+ */
+void
+merge_undo_logs(void)
+{
+	char		old_pg_undo_path[MAXPGPATH];
+	char		new_pg_undo_path[MAXPGPATH];
+	UndoLogInfo *logs;
+	UndoLogNumber num_logs;
+	UndoLogNumber num_old_logs;
+	UndoLogNumber old_low_logno;
+	UndoLogNumber old_next_logno;
+	UndoLogNumber num_new_logs;
+	UndoLogNumber new_low_logno;
+	UndoLogNumber new_next_logno;
+	UndoLogNumber i;
+	int			old_fd;
+	int			new_fd;
+	pg_crc32c	crc;
+
+	/* If the old cluster has no undo logs, there is nothing to do */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1300)
+		return;
+
+	/*
+	 * Open the pg_undo files corresponding to the old and new redo locations.
+	 * First, we'll reload pg_controldata output, so that we have up-to-date
+	 * redo locations.
+	 */
+	get_control_data(&old_cluster, true);
+	get_control_data(&new_cluster, true);
+	snprintf(old_pg_undo_path,
+			 sizeof(old_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 old_cluster.pgdata,
+			 old_cluster.controldata.redo_location);
+	snprintf(new_pg_undo_path,
+			 sizeof(new_pg_undo_path),
+			 "%s/pg_undo/%016" INT64_MODIFIER "X",
+			 new_cluster.pgdata,
+			 new_cluster.controldata.redo_location);
+	old_fd = open(old_pg_undo_path, O_RDONLY, 0);
+	if (old_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", old_pg_undo_path);
+	new_fd = open(new_pg_undo_path, O_RDWR, 0);
+	if (new_fd < 0)
+		pg_fatal("could not open file \"%s\": %m\n", new_pg_undo_path);
+
+	/* Read the headers */
+	read_pg_undo_header(old_fd, &old_cluster, &old_low_logno, &old_next_logno, &num_old_logs);
+	read_pg_undo_header(new_fd, &new_cluster, &new_low_logno, &new_next_logno, &num_new_logs);
+
+	/* Allocate workspace that is sure to be enough for the merged set */
+	logs = malloc(sizeof(*logs) * (num_old_logs + num_new_logs));
+	if (logs == NULL)
+	{
+		pg_fatal("out of memory\n");
+		exit(1);
+	}
+	num_logs = 0;
+
+	/*
+	 * Anything below the "low" logno has been entirely discarded, so we'll
+	 * take the higher of the two values.  Likewise, the "next" log number to
+	 * allocate should be the higher of the two.
+	 */
+	new_low_logno = Max(old_low_logno, new_low_logno);
+	new_next_logno = Max(old_next_logno, new_next_logno);
+
+	/* Merge in the old logs */
+	while (num_old_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(old_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_old_logs;
+	}
+
+	/* Merge in the new logs */
+	while (num_new_logs > 0)
+	{
+		UndoLogInfo	info;
+
+		read_one_undo_log(new_fd, &old_cluster, &info);
+		merge_undo_log(logs, &num_logs, &info);
+		--num_new_logs;
+	}
+
+	close(old_fd);
+
+	/* Now write out the new file, much like CheckPointUndoLogs() */
+	if (ftruncate(new_fd, 0) < 0)
+		pg_fatal("could not truncate file \"%s\": %m", new_pg_undo_path);
+	if (lseek(new_fd, SEEK_SET, 0) < 0)
+		pg_fatal("could not seek to start of file \"%s\": %m", new_pg_undo_path);
+
+	/* Compute header checksum */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc, &new_low_logno, sizeof(new_low_logno));
+	COMP_CRC32C(crc, &new_next_logno, sizeof(new_next_logno));
+	COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+	FIN_CRC32C(crc);
+
+	/* Write out the header */
+	if ((write(new_fd, &new_low_logno, sizeof(new_low_logno)) != sizeof(new_low_logno)) ||
+		(write(new_fd, &new_next_logno, sizeof(new_next_logno)) != sizeof(new_next_logno)) ||
+		(write(new_fd, &num_logs, sizeof(num_logs)) != sizeof(num_logs)) ||
+		(write(new_fd, &crc, sizeof(crc)) != sizeof(crc)))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	/* Write out the undo logs */
+	INIT_CRC32C(crc);
+	for (i = 0; i < num_logs; ++i)
+	{
+		UndoLogMetaData	meta_data;
+		UndoLogInfo	*info = &logs[i];
+		UndoLogOffset end;
+
+		memset(&meta_data, 0, sizeof(meta_data));
+		meta_data.logno = info->logno;
+
+		/*
+		 * Round the discard offset up so that it points to the first byte in
+		 * a segment, and assign that to all three offsets.  That means there
+		 * is no logical data, and there are no physical files.
+		 */
+		end = ((info->discard + UndoLogSegmentSize - 1) / UndoLogSegmentSize)
+			* UndoLogSegmentSize;
+		meta_data.unlogged.insert = meta_data.discard = meta_data.end = end;
+
+		/*
+		 * We have whatever was the highest status (though it probably
+		 * wouldn't hurt if we set them all to ACTIVE).
+		 */
+		meta_data.status = info->status;
+
+
+		if (write(new_fd, &meta_data, sizeof(meta_data)) != sizeof(meta_data))
+			pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+		COMP_CRC32C(crc, &meta_data, sizeof(meta_data));
+	}
+	FIN_CRC32C(crc);
+
+	if (write(new_fd, &crc, sizeof(crc)) != sizeof(crc))
+		pg_fatal("could not write to file \"%s\": %m", new_pg_undo_path);
+
+	close(new_fd);
+	free(logs);
+}
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca..938150d 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/storage_xlog.h"
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 3c0db2c..6945e3e 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
diff --git a/src/include/access/session.h b/src/include/access/session.h
index 8fba568..da0770e 100644
--- a/src/include/access/session.h
+++ b/src/include/access/session.h
@@ -17,6 +17,9 @@
 /* Avoid including typcache.h */
 struct SharedRecordTypmodRegistry;
 
+/* Avoid including undolog.h */
+struct UndoLogSlot;
+
 /*
  * A struct encapsulating some elements of a user's session.  For now this
  * manages state that applies to parallel query, but it principle it could
@@ -27,6 +30,10 @@ typedef struct Session
 	dsm_segment *segment;		/* The session-scoped DSM segment. */
 	dsa_area   *area;			/* The session-scoped DSA area. */
 
+	/* State managed by undolog.c. */
+	struct UndoLogSlot *attached_undo_slots[4];		/* UndoLogCategories */
+	bool		need_to_choose_undo_tablespace;
+
 	/* State managed by typcache.c. */
 	struct SharedRecordTypmodRegistry *shared_typmod_registry;
 	dshash_table *shared_record_table;
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
new file mode 100644
index 0000000..c4a6b29
--- /dev/null
+++ b/src/include/access/undolog.h
@@ -0,0 +1,489 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog.h
+ *
+ * PostgreSQL undo log manager.  This module is responsible for lifecycle
+ * management of undo logs and backing files, associating undo logs with
+ * backends, allocating and managing space within undo logs.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_H
+#define UNDOLOG_H
+
+#include "access/transam.h"
+#include "access/xlogreader.h"
+#include "catalog/database_internal.h"
+#include "catalog/pg_class.h"
+#include "common/relpath.h"
+#include "storage/bufpage.h"
+
+#ifndef FRONTEND
+#include "storage/lwlock.h"
+#endif
+
+/* The type used to identify an undo log and position within it. */
+typedef uint64 UndoRecPtr;
+
+/* The type used for undo record lengths. */
+typedef uint16 UndoRecordSize;
+
+/* Undo log statuses. */
+typedef enum
+{
+	UNDO_LOG_STATUS_UNUSED = 0,
+	UNDO_LOG_STATUS_ACTIVE,
+	UNDO_LOG_STATUS_FULL,
+	UNDO_LOG_STATUS_DISCARDED
+} UndoLogStatus;
+
+/*
+ * Undo log categories.  These correspond to the different persistence levels
+ * of relations so that we can discard unlogged and temporary undo data
+ * wholesale in some circumstance.  We also have a separate category for
+ * 'shared' records that are not associated with a single transactions.  Since
+ * they might live longer than the transaction that created them, and since we
+ * prefer to avoid interleaving records that don't belong to the same
+ * transaction, we keep them separate.
+ */
+typedef enum
+{
+	UNDO_PERMANENT = 0,
+	UNDO_UNLOGGED = 1,
+	UNDO_TEMP = 2,
+	UNDO_SHARED = 3
+} UndoLogCategory;
+
+#define UndoLogCategories 4
+
+/*
+ * Convert from relpersistence ('p', 'u', 't') to an UndoLogCategory
+ * enumerator.
+ */
+#define UndoLogCategoryForRelPersistence(rp)						\
+	((rp) == RELPERSISTENCE_PERMANENT ? UNDO_PERMANENT :			\
+	 (rp) == RELPERSISTENCE_UNLOGGED ? UNDO_UNLOGGED : UNDO_TEMP)
+
+/*
+ * Convert from UndoLogCategory to a relpersistence value.  There is no
+ * relpersistence level for UNDO_SHARED, but the only use of this macro is to
+ * pass a value to ReadBufferWithoutRelcache, which cares only about detecting
+ * RELPERSISTENCE_TEMP.  XXX There must be a better way.
+ */
+#define RelPersistenceForUndoLogCategory(up)				\
+	((up) == UNDO_PERMANENT ? RELPERSISTENCE_PERMANENT :	\
+	 (up) == UNDO_UNLOGGED ? RELPERSISTENCE_UNLOGGED :		\
+	 (up) == UNDO_SHARED ? RELPERSISTENCE_PERMANENT :		\
+	 RELPERSISTENCE_TEMP)
+
+/*
+ * Get the appropriate UndoLogCategory value from a Relation.
+ */
+#define UndoLogCategoryForRelation(rel)									\
+	(UndoLogCategoryForRelPersistence((rel)->rd_rel->relpersistence))
+
+/* Type for offsets within undo logs */
+typedef uint64 UndoLogOffset;
+
+/* printf-family format string for UndoRecPtr. */
+#define UndoRecPtrFormat "%016" INT64_MODIFIER "X"
+
+/* printf-family format string for UndoLogOffset. */
+#define UndoLogOffsetFormat UINT64_FORMAT
+
+/* Number of blocks of BLCKSZ in an undo log segment file.  128 = 1MB. */
+#define UNDOSEG_SIZE 128
+
+/* Size of an undo log segment file in bytes. */
+#define UndoLogSegmentSize ((size_t) BLCKSZ * UNDOSEG_SIZE)
+
+/* The width of an undo log number in bits.  24 allows for 16.7m logs. */
+#define UndoLogNumberBits 24
+
+/* The maximum valid undo log number. */
+#define MaxUndoLogNumber ((1 << UndoLogNumberBits) - 1)
+
+/* The width of an undo log offset in bits.  40 allows for 1TB per log.*/
+#define UndoLogOffsetBits (64 - UndoLogNumberBits)
+
+/* Special value for undo record pointer which indicates that it is invalid. */
+#define	InvalidUndoRecPtr	((UndoRecPtr) 0)
+
+/* End-of-list value when building linked lists of undo logs. */
+#define InvalidUndoLogNumber -1
+
+/*
+ * The maximum amount of data that can be stored in an undo log.  Can be set
+ * artificially low to test full log behavior.
+ */
+#define UndoLogMaxSize ((UndoLogOffset) 1 << UndoLogOffsetBits)
+
+/* Type for numbering undo logs. */
+typedef int UndoLogNumber;
+
+/* Extract the undo log number from an UndoRecPtr. */
+#define UndoRecPtrGetLogNo(urp)					\
+	((urp) >> UndoLogOffsetBits)
+
+/* Extract the offset from an UndoRecPtr. */
+#define UndoRecPtrGetOffset(urp)				\
+	((urp) & ((UINT64CONST(1) << UndoLogOffsetBits) - 1))
+
+/* Make an UndoRecPtr from an log number and offset. */
+#define MakeUndoRecPtr(logno, offset)			\
+	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
+
+/* The number of unusable bytes in the header of each block. */
+#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+
+/* The number of usable bytes we can store per block. */
+#define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
+
+/* Length of undo checkpoint filename */
+#define UNDO_CHECKPOINT_FILENAME_LENGTH	16
+
+/*
+ * UndoRecPtrIsValid
+ *		True iff undoRecPtr is valid.
+ */
+#define UndoRecPtrIsValid(undoRecPtr) \
+	((bool) ((UndoRecPtr) (undoRecPtr) != InvalidUndoRecPtr))
+
+/* Extract the relnode for an undo log. */
+#define UndoRecPtrGetRelNode(urp)				\
+	UndoRecPtrGetLogNo(urp)
+
+/* The only valid fork number for undo log buffers. */
+#define UndoLogForkNum MAIN_FORKNUM
+
+/* Compute the block number that holds a given UndoRecPtr. */
+#define UndoRecPtrGetBlockNum(urp)				\
+	(UndoRecPtrGetOffset(urp) / BLCKSZ)
+
+/* Compute the offset of a given UndoRecPtr in the page that holds it. */
+#define UndoRecPtrGetPageOffset(urp)			\
+	(UndoRecPtrGetOffset(urp) % BLCKSZ)
+
+/* Compare two undo checkpoint files to find the oldest file. */
+#define UndoCheckPointFilenamePrecedes(file1, file2)	\
+	(strcmp(file1, file2) < 0)
+
+/* What is the offset of the i'th non-header byte? */
+#define UndoLogOffsetFromUsableByteNo(i)								\
+	(((i) / UndoLogUsableBytesPerPage) * BLCKSZ +						\
+	 UndoLogBlockHeaderSize +											\
+	 ((i) % UndoLogUsableBytesPerPage))
+
+/* How many non-header bytes are there before a given offset? */
+#define UndoLogOffsetToUsableByteNo(offset)				\
+	(((offset) % BLCKSZ - UndoLogBlockHeaderSize) +		\
+	 ((offset) / BLCKSZ) * UndoLogUsableBytesPerPage)
+
+/* Add 'n' usable bytes to offset stepping over headers to find new offset. */
+#define UndoLogOffsetPlusUsableBytes(offset, n)							\
+	UndoLogOffsetFromUsableByteNo(UndoLogOffsetToUsableByteNo(offset) + (n))
+
+/* Populate a RelFileNode from an UndoRecPtr. */
+#define UndoRecPtrAssignRelFileNode(rfn, urp)								 \
+	do																		 \
+	{																		 \
+		(rfn).spcNode = UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp)); \
+		(rfn).dbNode = UndoDbOid;											 \
+		(rfn).relNode = UndoRecPtrGetRelNode(urp);							 \
+	} while (false);
+
+/*
+ * Properties of an undo log that don't have explicit WAL records logging
+ * their changes, to reduce WAL volume.  Instead, they change incrementally
+ * whenever data is inserted as a result of other WAL records.  Since the
+ * values recorded in an online checkpoint may be out of the sync (ie not the
+ * correct values as at the redo LSN), these are backed up in buffer data on
+ * first change after each checkpoint.
+ */
+typedef struct UndoLogUnloggedMetaData
+{
+	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
+	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
+	TransactionId xid;				/* currently attached/writing xid */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
+} UndoLogUnloggedMetaData;
+
+/*
+ * Control metadata for an active undo log.  Lives in shared memory inside an
+ * UndoLogSlot object, but also written to disk during checkpoints.
+ */
+typedef struct UndoLogMetaData
+{
+	/* Members that are not managed by explicit WAL logs. */
+	UndoLogUnloggedMetaData unlogged;
+
+	/* Members that are fixed for the lifetime of the undo log. */
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoLogCategory category;
+
+	/* Members that are changed by explicit WAL records. */
+	UndoLogStatus status;
+	UndoLogOffset end;				/* one past end of highest segment */
+	UndoLogOffset discard;			/* oldest data needed (tail) */
+} UndoLogMetaData;
+
+/*
+ * Context used to hold undo log state across all the undo log insertions
+ * corresponding to a single WAL record.
+ */
+typedef struct UndoLogAllocContext
+{
+	UndoLogCategory category;
+	UndoRecPtr	try_location;
+	XLogReaderState *xlog_record;
+	UndoLogNumber recovery_logno;
+	uint8 		recovery_block_id;
+	bool		new_shared_record_set;
+
+	/*
+	 * The maximum number of undo logs that a single WAL record could insert
+	 * into, modifying its unlogged meta data.  Typically the number is 1, but
+	 * it might touch a couple or more in rare cases where space runs out.
+	 */
+#define MAX_META_DATA_IMAGES 4
+	int			num_meta_data_images;
+	struct
+	{
+		UndoLogNumber logno;
+		UndoLogUnloggedMetaData data;
+	} meta_data_images[MAX_META_DATA_IMAGES];
+} UndoLogAllocContext;
+
+#ifndef FRONTEND
+
+/*
+ * The in-memory control object for an undo log.  We have a fixed-sized array
+ * of these.
+ */
+typedef struct UndoLogSlot
+{
+	/*
+	 * Protected by UndoLogLock and 'mutex'.  Both must be held to steal this
+	 * slot for another undolog.  Either may be held to prevent that from
+	 * happening.
+	 */
+	UndoLogNumber logno;			/* InvalidUndoLogNumber for unused slots */
+
+	/* Protected by UndoLogLock. */
+	UndoLogNumber next_free;		/* link for active unattached undo logs */
+
+	/* Protected by 'mutex'. */
+	LWLock		mutex;
+	UndoLogMetaData meta;			/* current meta-data */
+	pid_t		pid;				/* InvalidPid for unattached */
+
+	/* Protected by 'discard_lock'.  State used by undo workers. */
+	FullTransactionId	wait_fxmin;		/* trigger for processing this log again */
+	UndoRecPtr	oldest_data;
+	LWLock		discard_lock;		/* prevents discarding while reading */
+	LWLock      discard_update_lock;    /* block updaters during discard */
+} UndoLogSlot;
+
+extern UndoLogSlot *UndoLogGetSlot(UndoLogNumber logno, bool missing_ok);
+extern UndoLogSlot *UndoLogNextSlot(UndoLogSlot *slot);
+extern bool AmAttachedToUndoLogSlot(UndoLogSlot *slot);
+extern UndoRecPtr UndoLogGetOldestRecord(UndoLogNumber logno, bool *full);
+
+/*
+ * Each backend maintains a small hash table mapping undo log numbers to
+ * UndoLogSlot objects in shared memory.
+ *
+ * We also cache the tablespace, category and a recently observed discard
+ * pointer here, since we need fast access to those.  We could also reach them
+ * via slot->meta, but they can't be accessed without locking (since the
+ * UndoLogSlot object might be recycled if the log is entirely discard).
+ * Since tablespace and category are constant for lifetime of the undo log
+ * number, and the discard pointer only travels in one direction, there is no
+ * cache invalidation problem to worry about.
+ */
+typedef struct UndoLogTableEntry
+{
+	UndoLogNumber	number;
+	UndoLogSlot	   *slot;
+	Oid				tablespace;
+	UndoLogCategory category;
+	UndoRecPtr		recent_discard;
+	char			status;			/* used by simplehash */
+} UndoLogTableEntry;
+
+/*
+ * Instantiate fast inline hash table access functions.  We use an identity
+ * hash function for speed, since we already have integers and don't expect
+ * many collisions.
+ */
+#define SH_PREFIX undologtable
+#define SH_ELEMENT_TYPE UndoLogTableEntry
+#define SH_KEY_TYPE UndoLogNumber
+#define SH_KEY number
+#define SH_HASH_KEY(tb, key) (key)
+#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_SCOPE static inline
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+extern PGDLLIMPORT undologtable_hash *undologtable_cache;
+
+/*
+ * Find or create an UndoLogTableGetEntry for this log number.  This is used
+ * only for fast look-ups of tablespace and persistence.
+ */
+static pg_attribute_always_inline UndoLogTableEntry *
+UndoLogGetTableEntry(UndoLogNumber logno)
+{
+	UndoLogTableEntry  *entry;
+
+	/* Fast path. */
+	entry = undologtable_lookup(undologtable_cache, logno);
+	if (likely(entry))
+		return entry;
+
+	/* Slow path: force cache entry to be created. */
+	UndoLogGetSlot(logno, false);
+	entry = undologtable_lookup(undologtable_cache, logno);
+
+	return entry;
+}
+
+/*
+ * Look up the tablespace for an undo log in our cache.
+ */
+static inline Oid
+UndoLogNumberGetTablespace(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->tablespace;
+}
+
+static inline Oid
+UndoRecPtrGetTablespace(UndoRecPtr urp)
+{
+	return UndoLogNumberGetTablespace(UndoRecPtrGetLogNo(urp));
+}
+
+/*
+ * Look up the category for an undo log in our cache.
+ */
+static inline UndoLogCategory
+UndoLogNumberGetCategory(UndoLogNumber logno)
+{
+	return UndoLogGetTableEntry(logno)->category;
+}
+
+static inline UndoLogCategory
+UndoRecPtrGetCategory(UndoRecPtr urp)
+{
+	return UndoLogNumberGetCategory(UndoRecPtrGetLogNo(urp));
+}
+
+#endif
+
+/* Space management. */
+extern void UndoLogBeginInsert(UndoLogAllocContext *context,
+							   UndoLogCategory category,
+							   XLogReaderState *xlog_record);
+extern void UndoLogRegister(UndoLogAllocContext *context,
+							uint8 block_id,
+							UndoLogNumber logno);
+extern UndoRecPtr UndoLogAllocate(UndoLogAllocContext *context,
+								  uint16 size,
+								  bool *need_xact_header,
+								  UndoRecPtr *last_xact_start,
+								  UndoRecPtr *prevlog_xact_start,
+								  UndoRecPtr *prevlog_insert_urp);
+extern UndoRecPtr UndoLogAllocateInRecovery(UndoLogAllocContext *context,
+											TransactionId xid,
+											uint16 size,
+											bool *need_xact_header,
+											UndoRecPtr *last_xact_start,
+											UndoRecPtr *prevlog_xact_start,
+											UndoRecPtr *prevlog_last_urp);
+extern void UndoLogAdvance(UndoLogAllocContext *context, size_t size);
+extern void UndoLogAdvanceFinal(UndoRecPtr insertion_point, size_t size);
+extern bool UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid);
+extern bool UndoLogRecPtrIsDiscardedSlowPath(UndoRecPtr pointer);
+
+#ifndef FRONTEND
+
+/*
+ * Check if an undo log pointer is discarded.
+ */
+static inline bool
+UndoRecPtrIsDiscarded(UndoRecPtr pointer)
+{
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(pointer);
+	UndoRecPtr		recent_discard;
+
+	/* See if we can answer the question without acquiring any locks. */
+	recent_discard = UndoLogGetTableEntry(logno)->recent_discard;
+	if (likely(recent_discard > pointer))
+		return true;
+
+	/*
+	 * It might be discarded or not, but we'll need to do a bit more work to
+	 * find out.
+	 */
+	return UndoLogRecPtrIsDiscardedSlowPath(pointer);
+}
+
+#endif
+
+/* Initialization interfaces. */
+extern void StartupUndoLogs(XLogRecPtr checkPointRedo);
+extern void UndoLogShmemInit(void);
+extern Size UndoLogShmemSize(void);
+extern void UndoLogInit(void);
+extern void UndoLogDirectory(Oid tablespace, char *path);
+extern void UndoLogSegmentPath(UndoLogNumber logno, int segno, Oid tablespace,
+							   char *path);
+extern void ResetUndoLogs(UndoLogCategory category);
+
+/* Interface use by tablespace.c. */
+extern bool DropUndoLogsInTablespace(Oid tablespace);
+
+/* GUC interfaces. */
+extern void assign_undo_tablespaces(const char *newval, void *extra);
+
+/* Checkpointing interfaces. */
+extern void CheckPointUndoLogs(XLogRecPtr checkPointRedo,
+							   XLogRecPtr priorCheckPointRedo);
+
+/* File sync request management. */
+
+
+extern UndoRecPtr UndoLogGetLastXactStartPoint(UndoLogNumber logno);
+extern UndoRecPtr UndoLogGetNextInsertPtr(UndoLogNumber logno);
+extern void UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno,
+										UndoRecPtr prevlog_last_urp,
+										UndoRecPtr prevlog_xact_start);
+extern void UndoLogSetLSN(XLogRecPtr lsn);
+void UndoLogNewSegment(UndoLogNumber logno, Oid tablespace, int segno);
+/* Redo interface. */
+extern void undolog_redo(XLogReaderState *record);
+/* Discard the undo logs for temp tables */
+extern void TempUndoDiscard(UndoLogNumber);
+
+/* Test-only interfacing. */
+extern void UndoLogDetachFull(void);
+
+#endif
diff --git a/src/include/access/undolog_xlog.h b/src/include/access/undolog_xlog.h
new file mode 100644
index 0000000..aa9003b
--- /dev/null
+++ b/src/include/access/undolog_xlog.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * undolog_xlog.h
+ *	  undo log access XLOG definitions.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undolog_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOLOG_XLOG_H
+#define UNDOLOG_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+
+/* XLOG records */
+#define XLOG_UNDOLOG_CREATE		0x00
+#define XLOG_UNDOLOG_EXTEND		0x10
+#define XLOG_UNDOLOG_DISCARD	0x20
+#define XLOG_UNDOLOG_SWITCH		0x30
+
+/* Create a new undo log. */
+typedef struct xl_undolog_create
+{
+	UndoLogNumber logno;
+	Oid		tablespace;
+	UndoLogCategory category;
+} xl_undolog_create;
+
+/* Extend an undo log by adding a new segment. */
+typedef struct xl_undolog_extend
+{
+	UndoLogNumber logno;
+	UndoLogOffset end;
+} xl_undolog_extend;
+
+/* Discard space, and possibly destroy or recycle undo log segments. */
+typedef struct xl_undolog_discard
+{
+	UndoLogNumber logno;
+	UndoLogOffset discard;
+	UndoLogOffset end;
+	TransactionId latestxid;	/* latest xid whose undolog are discarded. */
+	bool		  entirely_discarded;
+} xl_undolog_discard;
+
+/* Switch undo log. */
+typedef struct xl_undolog_switch
+{
+	UndoLogNumber logno;
+	UndoRecPtr prevlog_xact_start;
+	UndoRecPtr prevlog_last_urp;
+} xl_undolog_switch;
+
+extern void undolog_desc(StringInfo buf,XLogReaderState *record);
+extern const char *undolog_identify(uint8 info);
+
+#endif
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index 4105b59..4db68f1 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -44,6 +44,20 @@ extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record,
 extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 									 BlockNumber blkno, ReadBufferMode mode);
 
+extern bool XLogFindBlockId(XLogReaderState *record,
+							RelFileNode rnode,
+							ForkNumber forknum,
+							BlockNumber blockno,
+							uint8 *block_id);
+
+extern XLogRedoAction XLogReadBufferForRedoBlock(XLogReaderState *record,
+												 RelFileNode rnode,
+												 ForkNumber forknum,
+												 BlockNumber blockno,
+												 ReadBufferMode mode,
+												 bool get_cleanup_lock,
+												 Buffer *buf);
+
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
diff --git a/src/include/catalog/database_internal.h b/src/include/catalog/database_internal.h
new file mode 100644
index 0000000..a2b3a12
--- /dev/null
+++ b/src/include/catalog/database_internal.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * database_internal.h
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/database_internal.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DATABASE_INTERNAL_H
+#define DATABASE_INTERNAL_H
+
+/*
+ * We use this header to define the OIDs of pseudo-database OIDs used in
+ * buffer tags to hold system data.
+ */
+#define UndoDbOid 9
+
+#endif							/* DATABASE_INTERNAL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0902dce..de475cb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10701,4 +10701,11 @@
   proname => 'pg_partition_root', prorettype => 'regclass',
   proargtypes => 'regclass', prosrc => 'pg_partition_root' },
 
+# undo logs
+{ oid => '5032', descr => 'list undo logs',
+  proname => 'pg_stat_get_undo_logs', procost => '1', prorows => '10', proretset => 't',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,text,text,text,text,text,xid,int4,text}', proargmodes => '{o,o,o,o,o,o,o,o,o}',
+  proargnames => '{logno,category,tablespace,discard,insert,end,xid,pid,status}', prosrc => 'pg_stat_get_undo_logs' },
+
 ]
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0a3ad3a..1936c5d 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -934,6 +934,13 @@ typedef enum
 	WAIT_EVENT_TWOPHASE_FILE_READ,
 	WAIT_EVENT_TWOPHASE_FILE_SYNC,
 	WAIT_EVENT_TWOPHASE_FILE_WRITE,
+	WAIT_EVENT_UNDO_CHECKPOINT_READ,
+	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
+	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_READ,
+	WAIT_EVENT_UNDO_FILE_WRITE,
+	WAIT_EVENT_UNDO_FILE_FLUSH,
+	WAIT_EVENT_UNDO_FILE_SYNC,
 	WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ,
 	WAIT_EVENT_WAL_BOOTSTRAP_SYNC,
 	WAIT_EVENT_WAL_BOOTSTRAP_WRITE,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 509f4b7..a04190a 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -37,8 +37,9 @@ typedef enum BufferAccessStrategyType
 typedef enum
 {
 	RBM_NORMAL,					/* Normal read */
-	RBM_ZERO_AND_LOCK,			/* Don't read from disk, caller will
-								 * initialize. Also locks the page. */
+	RBM_ZERO,					/* Don't read from disk, caller will
+								 * initialize. */
+	RBM_ZERO_AND_LOCK,			/* Like RBM_ZERO, but also locks the page. */
 	RBM_ZERO_AND_CLEANUP_LOCK,	/* Like RBM_ZERO_AND_LOCK, but locks the page
 								 * in "cleanup" mode */
 	RBM_ZERO_ON_ERROR,			/* Read, but return an all-zeros page on error */
@@ -170,7 +171,10 @@ extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BufferAccessStrategy strategy);
 extern Buffer ReadBufferWithoutRelcache(RelFileNode rnode,
 										ForkNumber forkNum, BlockNumber blockNum,
-										ReadBufferMode mode, BufferAccessStrategy strategy);
+										ReadBufferMode mode, BufferAccessStrategy strategy,
+										char relpersistence);
+extern void ForgetBuffer(RelFileNode rnode, ForkNumber forkNum,
+						 BlockNumber blockNum);
 extern void ReleaseBuffer(Buffer buffer);
 extern void UnlockReleaseBuffer(Buffer buffer);
 extern void MarkBufferDirty(Buffer buffer);
@@ -227,6 +231,10 @@ extern void AtProcExit_LocalBuffers(void);
 
 extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation);
 
+/* in localbuf.c */
+extern void ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum,
+							  BlockNumber blockNum);
+
 /* in freelist.c */
 extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
 extern void FreeAccessStrategy(BufferAccessStrategy strategy);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index d2a8c52..b215201 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -143,6 +143,7 @@ extern int	pg_fsync_writethrough(int fd);
 extern int	pg_fdatasync(int fd);
 extern void pg_flush_data(int fd, off_t offset, off_t amount);
 extern void fsync_fname(const char *fname, bool isdir);
+extern int	fsync_fname_ext(const char *fname, bool isdir, bool perm, int elevel);
 extern int	durable_rename(const char *oldfile, const char *newfile, int loglevel);
 extern int	durable_unlink(const char *fname, int loglevel);
 extern int	durable_link_or_rename(const char *oldfile, const char *newfile, int loglevel);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 08e0dc8..4abb344 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -220,7 +220,10 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_SXACT,
-	LWTRANCHE_FIRST_USER_DEFINED
+	LWTRANCHE_UNDOLOG,
+	LWTRANCHE_UNDODISCARD,
+	LWTRANCHE_REWIND,
+	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
 /*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index d286c8c..0fd3b75 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -70,6 +70,9 @@ typedef struct SMgrRelationData
 	int			md_num_open_segs[MAX_FORKNUM + 1];
 	struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1];
 
+	/* For use by implementations. */
+	void	   *private_data;
+
 	/* if unowned, list link in list of all unowned SMgrRelations */
 	dlist_node	node;
 } SMgrRelationData;
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index 16428c5..59f1da9 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -34,7 +34,8 @@ typedef enum SyncRequestType
  */
 typedef enum SyncRequestHandler
 {
-	SYNC_HANDLER_MD = 0			/* md smgr */
+	SYNC_HANDLER_MD = 0,		/* md smgr */
+	SYNC_HANDLER_UNDO = 1		/* undo smgr */
 } SyncRequestHandler;
 
 /*
diff --git a/src/include/storage/undofile.h b/src/include/storage/undofile.h
new file mode 100644
index 0000000..a5ec30f
--- /dev/null
+++ b/src/include/storage/undofile.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * undofile.h
+ *	  undo storage manager public interface declarations.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/undofile.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOFILE_H
+#define UNDOFILE_H
+
+#include "access/undolog.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "storage/smgr.h"
+#include "storage/sync.h"
+
+extern void undofile_init(void);
+extern void undofile_shutdown(void);
+extern void undofile_open(SMgrRelation reln);
+extern void undofile_close(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_create(SMgrRelation reln, ForkNumber forknum,
+							bool isRedo);
+extern bool undofile_exists(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_unlink(RelFileNodeBackend rnode, ForkNumber forknum,
+							bool isRedo);
+extern void undofile_extend(SMgrRelation reln, ForkNumber forknum,
+		 BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_prefetch(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber blocknum);
+extern void undofile_read(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, char *buffer);
+extern void undofile_write(SMgrRelation reln, ForkNumber forknum,
+		BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void undofile_writeback(SMgrRelation reln, ForkNumber forknum,
+			BlockNumber blocknum, BlockNumber nblocks);
+extern BlockNumber undofile_nblocks(SMgrRelation reln, ForkNumber forknum);
+extern void undofile_truncate(SMgrRelation reln, ForkNumber forknum,
+		   BlockNumber nblocks);
+extern void undofile_immedsync(SMgrRelation reln, ForkNumber forknum);
+
+/* Callbacks used by sync.c. */
+extern int undofile_syncfiletag(const FileTag *tag, char *path);
+extern bool undofile_filetagmatches(const FileTag *tag, const FileTag *candidate);
+
+/* Management of checkpointer requests. */
+extern void undofile_request_sync(UndoLogNumber logno, BlockNumber segno,
+								  Oid tablespace);
+extern void undofile_forget_sync(UndoLogNumber logno, BlockNumber segno,
+								 Oid tablespace);
+extern void undofile_forget_sync_tablespace(Oid tablespace);
+extern void undofile_request_sync_dir(Oid tablespace);
+
+#endif							/* UNDOFILE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e709177..84639ee 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -431,6 +431,8 @@ extern void GUC_check_errcode(int sqlerrcode);
 extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
 extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
 extern void assign_temp_tablespaces(const char *newval, void *extra);
+extern bool check_undo_tablespaces(char **newval, void **extra, GucSource source);
+extern void assign_undo_tablespaces(const char *newval, void *extra);
 
 /* in catalog/namespace.c */
 extern bool check_search_path(char **newval, void **extra, GucSource source);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 210e9cd..7098461 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2010,6 +2010,16 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text));
+pg_stat_undo_logs| SELECT pg_stat_get_undo_logs.logno,
+    pg_stat_get_undo_logs.category,
+    pg_stat_get_undo_logs.tablespace,
+    pg_stat_get_undo_logs.discard,
+    pg_stat_get_undo_logs.insert,
+    pg_stat_get_undo_logs."end",
+    pg_stat_get_undo_logs.xid,
+    pg_stat_get_undo_logs.pid,
+    pg_stat_get_undo_logs.status
+   FROM pg_stat_get_undo_logs() pg_stat_get_undo_logs(logno, category, tablespace, discard, insert, "end", xid, pid, status);
 pg_stat_user_functions| SELECT p.oid AS funcid,
     n.nspname AS schemaname,
     p.proname AS funcname,
-- 
1.8.3.1

0004-Allow-WAL-record-data-on-first-modification-after-a-.patchapplication/octet-stream; name=0004-Allow-WAL-record-data-on-first-modification-after-a-.patchDownload
From 3d3565b64e2c2fd3a47c6fbdfa42d542923d44d4 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Sat, 12 Jan 2019 02:17:02 +1300
Subject: [PATCH 04/13] Allow WAL record data on first modification after a
 checkpoint.

Provide a way to attach data to WAL record conditionally, so that it is
included only if this turns out to the be first modification to a given
block after a checkpoint.

This will be used to record undo log meta-data, to avoid a data
synchronization problem with online checkpoints.

Author: Thomas Munro
Reviewed-by:
Discussion:
---
 src/backend/access/transam/xloginsert.c | 15 +++++++++++++++
 src/include/access/xloginsert.h         |  2 ++
 2 files changed, 17 insertions(+)

diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d4..001d7cb 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -565,6 +565,21 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			needs_data = false;
 		else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
 			needs_data = true;
+		else if ((regbuf->flags & REGBUF_KEEP_DATA_AFTER_CP) != 0)
+		{
+			XLogRecPtr	page_lsn = PageGetLSN(regbuf->page);
+
+			needs_data = (page_lsn <= RedoRecPtr);
+			if (!needs_data)
+			{
+				/*
+				 * XLogInsertRecord() will detect if our view of the latest
+				 * checkpoint's RedoRecPtr is out of date.
+				 */
+				if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)
+					*fpw_lsn = page_lsn;
+			}
+		}
 		else
 			needs_data = !needs_backup;
 
diff --git a/src/include/access/xloginsert.h b/src/include/access/xloginsert.h
index df24089..22b388c 100644
--- a/src/include/access/xloginsert.h
+++ b/src/include/access/xloginsert.h
@@ -37,6 +37,8 @@
 									 * will be skipped) */
 #define REGBUF_KEEP_DATA	0x10	/* include data even if a full-page image
 									 * is taken */
+#define REGBUF_KEEP_DATA_AFTER_CP 0x20 /* include data on the first
+										* modification after a checkpoint */
 
 /* prototypes for public functions in xloginsert.c: */
 extern void XLogBeginInsert(void);
-- 
1.8.3.1

0005-Add-prefetch-support-for-the-undo-log.patchapplication/octet-stream; name=0005-Add-prefetch-support-for-the-undo-log.patchDownload
From 8e9190898eb0cd12afea72f7ad89c192d5943e60 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/13] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 101 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 87 insertions(+), 35 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c742861..d8dc0cc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 2b1d606..5abf42e 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -520,14 +520,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -550,42 +593,32 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
+							  BlockNumber blockNum, char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 04d4514..3be0f5e 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -119,7 +119,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1936c5d..2fff673 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -937,6 +937,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index a04190a..5c0ed58 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -165,6 +165,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BlockNumber blockNum, ReadBufferMode mode,
-- 
1.8.3.1

0006-Defect-and-enhancement-in-multi-log-support.patchapplication/octet-stream; name=0006-Defect-and-enhancement-in-multi-log-support.patchDownload
From 126e9cc8da96ffa1a0c9842961db78b96d920917 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Fri, 7 Jun 2019 15:03:37 +0530
Subject: [PATCH 06/13] Defect and enhancement in multi-log support

---
 src/backend/access/undo/undolog.c | 18 +++++++++---------
 src/include/access/undolog.h      | 20 ++++++++++----------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index f2e0272..b5502f2 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -940,11 +940,11 @@ UndoLogAllocateInRecovery(UndoLogAllocContext *context,
 			context->recovery_logno = slot->logno;
 
 			/* Read log switch information from meta and reset it. */
-			*prevlog_xact_start = slot->meta.unlogged.prevlog_xact_start;
-			*prevlog_last_urp = slot->meta.unlogged.prevlog_last_urp;
+			*prevlog_xact_start = slot->meta.prevlog_xact_start;
+			*prevlog_last_urp = slot->meta.prevlog_last_urp;
 
-			slot->meta.unlogged.prevlog_xact_start = InvalidUndoRecPtr;
-			slot->meta.unlogged.prevlog_last_urp = InvalidUndoRecPtr;
+			slot->meta.prevlog_xact_start = InvalidUndoRecPtr;
+			slot->meta.prevlog_last_urp = InvalidUndoRecPtr;
 
 			return MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
 		}
@@ -1284,8 +1284,8 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 	Assert(AmAttachedToUndoLogSlot(slot));
 
 	LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
-	slot->meta.unlogged.prevlog_xact_start = prevlog_last_urp;
-	slot->meta.unlogged.prevlog_last_urp = prevlog_last_urp;
+	slot->meta.prevlog_xact_start = prevlog_xact_start;
+	slot->meta.prevlog_last_urp = prevlog_last_urp;
 	LWLockRelease(&slot->mutex);
 
 	/* Wal log the log switch. */
@@ -1293,7 +1293,7 @@ UndoLogSwitchSetPrevLogInfo(UndoLogNumber logno, UndoRecPtr prevlog_xact_start,
 		xl_undolog_switch xlrec;
 
 		xlrec.logno = logno;
-		xlrec.prevlog_xact_start = prevlog_last_urp;
+		xlrec.prevlog_xact_start = prevlog_xact_start;
 		xlrec.prevlog_last_urp = prevlog_xact_start;
 
 		XLogBeginInsert();
@@ -2571,8 +2571,8 @@ undolog_xlog_switch(XLogReaderState *record)
 	 * Restore the log switch information in the MyUndoLogState this will be
 	 * reset by following UndoLogAllocateDuringRecovery.
 	 */
-	slot->meta.unlogged.prevlog_xact_start = xlrec->prevlog_xact_start;
-	slot->meta.unlogged.prevlog_last_urp = xlrec->prevlog_last_urp;
+	slot->meta.prevlog_xact_start = xlrec->prevlog_xact_start;
+	slot->meta.prevlog_last_urp = xlrec->prevlog_last_urp;
 }
 
 void
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index c4a6b29..1c79c1f 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -211,16 +211,6 @@ typedef struct UndoLogUnloggedMetaData
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
-
-	/*
-	 * Below two variable are used during recovery when transaction's undo
-	 * records are split across undo logs.  Replay of switch will restore
-	 * these two undo record pointers which will be reset on next allocation
-	 * during recovery. */
-	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
-									 * in the previous log. */
-	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
-									 * the previous log. */
 } UndoLogUnloggedMetaData;
 
 /*
@@ -241,6 +231,16 @@ typedef struct UndoLogMetaData
 	UndoLogStatus status;
 	UndoLogOffset end;				/* one past end of highest segment */
 	UndoLogOffset discard;			/* oldest data needed (tail) */
+
+	/*
+	 * Below two variable are used during recovery when transaction's undo
+	 * records are split across undo logs.  Replay of switch will restore
+	 * these two undo record pointers which will be reset on next allocation
+	 * during recovery. */
+	UndoRecPtr	prevlog_xact_start; /* Transaction's start undo record pointer
+									 * in the previous log. */
+	UndoRecPtr	prevlog_last_urp;	/* Transaction's last undo record pointer in
+									 * the previous log. */
 } UndoLogMetaData;
 
 /*
-- 
1.8.3.1

0007-Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=0007-Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From b4c7d6041f2d7f46814e8bdd3d37e22d5e9c3c05 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu, 2 May 2019 11:28:13 +0530
Subject: [PATCH 07/13] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs.  We
also provide the capability to fetch the undo records.  This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.  Each undo
record consists of a variable length header, tuple data, and payload
information.  The undo records are stored without any sort of alignment
padding and a undo record can span across multiple pages.  The undo records
for a transaction can span across multiple undo logs.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro, Vignesh C and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   29 +
 src/backend/access/undo/undoaccess.c         | 1754 ++++++++++++++++++++++++++
 src/backend/access/undo/undorecord.c         | 1051 +++++++++++++++
 src/backend/storage/page/bufpage.c           |   28 +
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  121 ++
 src/include/access/undolog.h                 |   10 +-
 src/include/access/undorecord.h              |  279 ++++
 src/include/storage/bufpage.h                |   40 +
 10 files changed, 3311 insertions(+), 4 deletions(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100755 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c696..049a416 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoaccess.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 0000000..41e2c0e
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.
+
+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.
+
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 0000000..a8cf469
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1754 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.  The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start header.
+ * This allows us to discard the entire transaction's log at one-shot rather
+ * than record-by-record.  The callers are not aware of transaction header,
+ * this is entirely maintained and used by undo record layer.   See
+ * undorecord.h for detailed information about undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * When the undo record for a transaction gets inserted in the next log then we
+ * add a transaction header for the first record of the transaction in the new
+ * log and connect this undo record to the first record of the transaction in
+ * the next log by updating the "uur_next" field.
+ *
+ * We will also keep a previous undo record pointer to the first and last undo
+ * record of the transaction in the previous log.  The last undo record
+ * location is used find the previous undo record pointer during rollback.
+ * The first undo record location is used to find the previous transaction
+ * header which is required to update the undo apply progress.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undorecord.h"
+#include "access/undoaccess.h"
+#include "access/undolog_xlog.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"
+
+/*
+ * Information for compression of the undo records on a page so that we can
+ * avoid duplicating same values across multiple undo records on a page.
+ *
+ * The cid/xid/reloid/rmid information will be added in the undo record header
+ * in the following cases:
+ * a) The first undo record of the transaction.
+ * b) First undo record of the page.
+ * c) All subsequent record for the transaction which is not the first
+ *	  transaction on the page.
+ * Except above cases,  If the rmid/reloid/xid/cid is same in the subsequent
+ * records this information will not be stored in the record, these information
+ * will be retrieved from the first undo record of that page.
+ * If any of the member rmid/reloid/xid/cid has changed, the changed information
+ * will be stored in the undo record and the remaining information will be
+ * retrieved from the first complete undo record of the page
+ */
+UndoCompressionInfo undo_compression_info[UndoLogCategories];
+
+/* Prototypes for static functions. */
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+											UndoRecPtr urp, RelFileNode rnode,
+											UndoLogCategory category,
+											Buffer *prevbuf);
+static int	UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+									   UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+									  int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+										UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
+							  RelFileNode rnode, BlockNumber blk,
+							  ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+								   UndoLogCategory category);
+static bool UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+							  UnpackedUndoRecord *urec, UndoRecPtr urp,
+							  Buffer buffer);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+/*
+ * Compute the size of the partial record on the undo page.
+ *
+ * Compute the complete record size by uur_info and variable field length
+ * stored in the page header and then subtract the offset of the record so that
+ * we can get the exact size of partial record in this page.
+ */
+static inline Size
+UndoPagePartialRecSize(UndoPageHeader phdr)
+{
+	Size		size = UndoRecordHeaderSize(phdr->uur_info);
+
+	/*
+	 * Add length of the variable part and undo length. Now, we know the
+	 * complete length of the undo record.
+	 */
+	size += phdr->tuple_len + phdr->payload_len + sizeof(uint16);
+
+	/*
+	 * Subtract the size which is stored in the previous page to get the
+	 * partial record size stored in this page.
+	 */
+	size -= phdr->record_offset;
+
+	return size;
+}
+
+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress
+ *
+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */
+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+						   UndoRecPtr xact_urp, int size, int offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	XactUndoRecordInfo *xact_info;
+
+	xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+	cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+	starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Is there some byte of the urec_next on the current block, if not then
+	 * start from the next block.
+	 */
+	if (remaining_bytes <= offset)
+	{
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+		starting_byte += (offset - remaining_bytes);
+	}
+	else
+		starting_byte += offset;
+
+	/*
+	 * Set the offset in the first block where we need to start writing,
+	 * during the prepare phase so that during update phase we need not to
+	 * compute it again.
+	 */
+	xact_info->offset = starting_byte;
+
+	/* Loop until we have fetched all the buffers in which we need to write. */
+	while (size > 0)
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		xact_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	xact_info->next = InvalidUndoRecPtr;
+	xact_info->progress = 0;
+	xact_info->urecptr = xact_urp;
+	context->nxact_urec_info++;
+
+	return (context->nxact_urec_info - 1);
+}
+
+/*
+ * Prepare to update the transaction's next undo pointer.
+ *
+ * Lock necessary buffer for updating the next of the transaction header.  This
+ * function is called for
+ * a. Updating the current transaction's start undo record pointer in previous
+ * transaction's start header.
+ * b. For multi-log transaction update the start undo record pointer of the
+ * current log in the same transaction's start undo record pointer in the
+ * previous log.
+ *
+ * xact_urp - undo record pointer to be updated
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *			 the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr xact_urp)
+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicate that this backend
+	 * is preparing its first undo in which case we have nothing to update.
+	 */
+	if (!UndoRecPtrIsValid(xact_urp))
+		return;
+
+	slot = UndoLogGetSlot(UndoRecPtrGetLogNo(xact_urp), false);
+
+	/*
+	 * Acquire the discard lock before reading the undo record so that discard
+	 * worker doesn't remove the record while we are in process of reading it.
+	 */
+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+	/* Check if it is already discarded. */
+	if (UndoRecPtrIsDiscarded(xact_urp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}
+
+	/* Compute the offset of the uur_next in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+		offsetof(UndoRecordTransaction, urec_next);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+
+	/*
+	 * Set the next pointer in xact_urec_info, this will be overwritten in
+	 * actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].next = urecptr;
+
+	/* We can now release the discard lock as we have read the undo record. */
+	LWLockRelease(&slot->discard_update_lock);
+}
+
+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.
+ * This must be called under the critical section.  This will just overwrite the
+ * header of the undo record.
+ */
+static void
+UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			i = 0;
+	int			write_bytes;
+	int			write_offset;
+	char	   *sourceptr;
+	XactUndoRecordInfo *xact_info = &context->xact_urec_info[idx];
+
+	/* Whether to update the next or undo apply progress. */
+	if (UndoRecPtrIsValid(xact_info->next))
+	{
+		sourceptr = (char *) &xact_info->next;
+		write_bytes = sizeof(xact_info->next);
+	}
+	else
+	{
+		sourceptr = (char *) &xact_info->progress;
+		write_bytes = sizeof(xact_info->progress);
+	}
+
+	/* Where to start writing in the current block. */
+	write_offset = xact_info->offset;
+
+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */
+	while (write_bytes > 0)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+		int			can_write;
+		char	   *writeptr;
+
+		buf_idx = xact_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/* How may bytes can be written in the current page. */
+		can_write = Min((BLCKSZ - write_offset), write_bytes);
+
+		/*
+		 * If buffer is valid then write it otherwise just skip writing it but
+		 * compute the variable for writing into the next block.
+		 */
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+
+			/* Compute the write pointer. */
+			writeptr = (char *) page + write_offset;
+
+			/* Copy the bytes we can write. */
+			memcpy(writeptr, sourceptr, can_write);
+			MarkBufferDirty(buffer);
+		}
+
+		/* Update bookkeeping information. */
+		write_bytes -= can_write;
+		sourceptr += can_write;
+		write_offset = UndoLogBlockHeaderSize;
+		i++;
+	}
+}
+
+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			i;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoLogCategory category = context->alloc_context.category;
+
+	/* Don't do anything, if we already have a buffer pinned for the block. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[i];
+
+		/*
+		 * It's not enough to just compare the block number because the
+		 * undo_buffer might holds the undo from different undo logs (e.g when
+		 * previous transaction start header is in previous undo log) so
+		 * compare (logno + blkno).
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return i;
+		}
+	}
+
+	/*
+	 * We did not find the block so allocate the buffer and insert into the
+	 * undo buffer array.
+	 */
+	if (InRecovery)
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoLogCategory(category));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = InvalidBlockNumber;
+	}
+	else
+	{
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return i;
+}
+
+/*
+ * Exclude the common info in undo record flag and also set the compression
+ * info in the context.
+ *
+ * This function will check what information we need to include in the current
+ * undo record based on the undo compression information.  And, it will also
+ * update the compression info if we are writing the first undo record on the
+ * page.
+ */
+static bool
+UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+				  UnpackedUndoRecord *urec, UndoRecPtr urp,
+				  Buffer buffer)
+{
+	bool		record_updated = false;
+	bool		first_complete_undo = false;
+	UndoRecPtr	lasturp = compressioninfo->last_urecptr;
+
+	/*
+	 * If we have valid compression info and the for the same transaction and
+	 * the current undo record is on the same block as the last undo record
+	 * then exclude the common information which are same as first complete
+	 * record on the page.
+	 */
+	if (compressioninfo->valid &&
+		FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+		UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))
+	{
+		urec->uur_info &= ~UREC_INFO_XID;
+
+		/* Don't include rmid if it's same. */
+		if (urec->uur_rmid == compressioninfo->rmid)
+			urec->uur_info &= ~UREC_INFO_RMID;
+
+		/* Don't include reloid if it's same. */
+		if (urec->uur_reloid == compressioninfo->reloid)
+			urec->uur_info &= ~UREC_INFO_RELOID;
+
+		/* Don't include cid if it's same. */
+		if (urec->uur_cid == compressioninfo->cid)
+			urec->uur_info &= ~UREC_INFO_CID;
+
+		record_updated = true;
+	}
+
+	/*
+	 * If the undo record is starting just after the undo page header then
+	 * this is the first complete undo on the page.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) == SizeOfUndoPageHeaderData)
+		first_complete_undo = true;
+	else if (UndoRecPtrIsValid(lasturp))
+	{
+		/*
+		 * If we already have the valid last undo record pointer which
+		 * inserted undo in this log then we can identify whether this is the
+		 * first undo of the page by checking the block number of the previous
+		 * record and the current record.
+		 */
+		if (UndoRecPtrGetBlockNum(urp) != UndoRecPtrGetBlockNum(lasturp))
+			first_complete_undo = true;
+	}
+	else
+	{
+		Page		page = BufferGetPage(buffer);
+		uint16		offset;
+		UndoPageHeader phdr = (UndoPageHeader) page;
+
+		/*
+		 * We need to compute the offset of the first complete record of the
+		 * page and if this undo record starting from that page then this is
+		 * the first complete record on the page.
+		 */
+		offset = SizeOfUndoPageHeaderData + UndoPagePartialRecSize(phdr);
+		if (UndoRecPtrGetPageOffset(urp) == offset)
+			first_complete_undo = true;
+	}
+
+	/*
+	 * If we are writing first undo record for the page the we can set the
+	 * compression so that subsequent records from the same transaction can
+	 * avoid including common information in the undo records.
+	 */
+	if (first_complete_undo)
+	{
+		/* Set this information. */
+		compressioninfo->rmid = urec->uur_rmid;
+		compressioninfo->reloid = urec->uur_reloid;
+		compressioninfo->fxid = urec->uur_fxid;
+		compressioninfo->cid = urec->uur_cid;
+
+		/* Set that we have valid compression info. */
+		compressioninfo->valid = true;
+	}
+
+	return record_updated;
+}
+
+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ *
+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoLogCategory category,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, category, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nxact_urec_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_XACT_UNDO_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/*
+	 * Copy the compression global compression info to our context before
+	 * starting prepare because this value might get updated multiple time in
+	 * case of multi-prepare but the global value should be updated only after
+	 * we have successfully inserted the undo record.
+	 */
+	memcpy(&context->undo_compression_info[category],
+		   &undo_compression_info[category], sizeof(UndoCompressionInfo));
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be done before any critical section is established, since it
+ * can fail.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  Oid dbid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId fxid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	bool		logswitched = false;
+	bool		resize = false;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	UndoRecPtr	prevlog_insert_urp = InvalidUndoRecPtr;
+	UndoRecPtr	prevlogurp = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+	UndoCompressionInfo *compression_info =
+	&context->undo_compression_info[context->alloc_context.category];
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	/* Extract the full transaction id from the input undo record. */
+	fxid = urec->uur_fxid;
+	Assert(FullTransactionIdIsValid(fxid));
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  Similarly log switch
+	 * will only be detected after allocation so include the log switch header
+	 * and common information because in case of log switch we need to include
+	 * log switch header and also we need to include common header.  After
+	 * allocation We'll only advance by as many bytes as we turn out to need.
+	 */
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_TRANSACTION;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	urec->uur_info |= UREC_INFO_PAGE_COMMON;
+
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.  We don't expect to see temporary or
+		 * unlogged undo data here.
+		 */
+		Assert(context->alloc_context.category != UNDO_TEMP &&
+			   context->alloc_context.category != UNDO_UNLOGGED);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(fxid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start,
+											&prevlogurp);
+	}
+	else
+	{
+		/* Allocate space for writing the undo record. */
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start, &prevlog_insert_urp);
+
+		/*
+		 * If prevlog_xact_start is a valid undo record pointer that means
+		 * this transaction's undo records are split across undo logs.
+		 */
+		if (UndoRecPtrIsValid(prevlog_xact_start))
+		{
+			uint16		prevlen;
+
+			/*
+			 * If undo log is switch during transaction then we must get a
+			 * valid insert location in the previous undo log so that we can
+			 * compute the undo record pointer of the transaction's last
+			 * record in the previous undo log.
+			 */
+			Assert(UndoRecPtrIsValid(prevlog_insert_urp));
+
+			/* Fetch length of the last undo record of the previous log. */
+			prevlen = UndoGetPrevRecordLen(prevlog_insert_urp, InvalidBuffer,
+										   context->alloc_context.category);
+
+			/*
+			 * If the undo log got switched during the transaction then for
+			 * collecting all the undo record for the transaction during bulk
+			 * fetch,  we  can not read the prevlen from the end of the record
+			 * as we will not know what was the previous undo log.  So during
+			 * log switch we will directly store the last undo record pointer
+			 * of the transaction into transaction's first record of the next
+			 * undo log.
+			 *
+			 * TODO:  instead of storing this in the transaction header we can
+			 * have separate undo log switch header and store it there.
+			 */
+			prevlogurp =
+				MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+							   (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+
+			/*
+			 * Undo log switched so set prevlog info in current undo log.
+			 *
+			 * XXX can we do this directly in UndoLogAllocate ? but for that
+			 * the UndoLogAllocate might need to read the length of the last
+			 * undo record from the previous undo log but for that it might
+			 * use callback?
+			 */
+			UndoLogSwitchSetPrevLogInfo(UndoRecPtrGetLogNo(urecptr),
+										prevlog_xact_start, prevlogurp);
+		}
+	}
+
+	/*
+	 * If undo log is switched then set the logswitch flag and also reset the
+	 * compression info because we can use same compression info for the new
+	 * undo log.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		logswitched = true;
+		compression_info->valid = false;
+		compression_info->last_urecptr = InvalidUndoRecPtr;
+	}
+
+	/*
+	 * If we need a transaction header then allocate memory for it and
+	 * initialize it.
+	 */
+	if (need_xact_header)
+	{
+		urec->uur_txn = palloc(SizeOfUndoRecordTransaction);
+		urec->uur_txn->urec_dbid = dbid;
+		urec->uur_txn->urec_progress = InvalidBlockNumber;
+		urec->uur_txn->urec_next = InvalidUndoRecPtr;
+	}
+	else
+	{
+		/* We don't need a transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_TRANSACTION;
+		resize = true;
+		urec->uur_txn = NULL;
+	}
+
+	/*
+	 * If undo log got switched then initialize the log switch header
+	 * otherwise reset it in uur_info and recalculate the size.
+	 */
+	if (logswitched)
+	{
+		urec->uur_logswitch = palloc(SizeOfUndoRecordLogSwitch);
+		urec->uur_logswitch->urec_prevurp = prevlogurp;
+		urec->uur_logswitch->urec_prevlogstart = prevlog_xact_start;
+	}
+	else
+	{
+		/* We don't need a log transaction header after all. */
+		urec->uur_info &= ~UREC_INFO_LOGSWITCH;
+		resize = true;
+		urec->uur_logswitch = NULL;
+	}
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to update the size of the
+	 * preceding transaction.
+	 */
+	if (need_xact_header &&
+		UndoRecPtrGetOffset(urecptr) > UndoLogBlockHeaderSize)
+		UndoRecordPrepareUpdateNext(context, urecptr, last_xact_start);
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * Set/overwrite compression info if required and also exclude the common
+	 * fields from the undo record if possible.
+	 */
+	if (UndoSetCommonInfo(compression_info, urec, urecptr,
+						  context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf))
+		resize = true;
+
+	if (resize)
+		size = UndoRecordExpectedSize(urec);
+
+	/*
+	 * If the transaction's undo records are split across the undo logs.  So
+	 * we need to  update our own transaction header in the previous log.
+	 */
+	if (logswitched)
+	{
+		Assert(UndoRecPtrIsValid(prevlogurp));
+		UndoRecordPrepareUpdateNext(context, urecptr, prevlog_xact_start);
+	}
+
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+
+	/* Set the current undo pointer in the compression info. */
+	compression_info->last_urecptr = urecptr;
+
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoSpace *prepared_undo;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		bufidx = 0;
+
+		/*
+		 * Compute starting offset of the page where to start inserting undo
+		 * record.
+		 */
+		starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+		/* Initiate inserting the undo record. */
+		BeginInsertUndo(&ucontext, prepared_undo->urec);
+
+		/* Main loop for writing the undo record. */
+		do
+		{
+			Buffer		buffer;
+
+			buffer = context->prepared_undo_buffers[
+													prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+			/*
+			 * During recovery, there might be some blocks which are already
+			 * deleted due to some discard command so we can just skip
+			 * inserting into those blocks.
+			 */
+			if (!BufferIsValid(buffer))
+			{
+				Assert(InRecovery);
+
+				/*
+				 * Skip actual writing just update the context so that we have
+				 * write offset for inserting into next blocks.
+				 */
+				SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+					break;
+			}
+			else
+			{
+				page = BufferGetPage(buffer);
+
+				/*
+				 * Initialize the page whenever we try to write the first
+				 * record in page.  We start writing immediately after the
+				 * block header.
+				 */
+				if (starting_byte == UndoLogBlockHeaderSize)
+					UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+								 ucontext.already_processed,
+								 prepared_undo->urec->uur_tuple.len,
+								 prepared_undo->urec->uur_payload.len);
+
+				/*
+				 * Try to insert the record into the current page. If it
+				 * doesn't succeed then recall the routine with the next page.
+				 */
+				InsertUndoData(&ucontext, page, starting_byte);
+				if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+				{
+					MarkBufferDirty(buffer);
+					break;
+				}
+				MarkBufferDirty(buffer);
+			}
+
+			/* Insert remaining record in next block. */
+			starting_byte = UndoLogBlockHeaderSize;
+			bufidx++;
+
+			/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+			Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		} while (true);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update previously prepared transaction headers. */
+	for (i = 0; i < context->nxact_urec_info; i++)
+		UndoRecordUpdateTransInfo(context, i);
+
+	/*
+	 * We have successfully inserted prepared undo records so overwrite the
+	 * global compression.
+	 */
+	memcpy(&undo_compression_info[context->alloc_context.category],
+		   &context->undo_compression_info[context->alloc_context.category],
+		   sizeof(UndoCompressionInfo));
+
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and lock. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+	{
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+	}
+
+	/*
+	 * Release memory for the transaction header and log switch header if we
+	 * have allocated it in the prepare time.
+	 */
+	for (i = 0; i < context->nprepared_undo; i++)
+	{
+		if (context->prepared_undo[i].urec->uur_txn)
+			pfree(context->prepared_undo[i].urec->uur_txn);
+		if (context->prepared_undo[i].urec->uur_logswitch)
+			pfree(context->prepared_undo[i].urec->uur_logswitch);
+	}
+
+	/* Free memory allocated for the prepare undo and prepared buffers. */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Helper function for UndoGetOneRecord
+ *
+ * If any of  rmid/reloid/xid/cid is not available in the undo record, then
+ * it will get the information from the first complete undo record in the
+ * page.
+ */
+static void
+GetCommonUndoRecInfo(UndoPackContext *ucontext, UndoRecPtr urp,
+					 RelFileNode rnode, UndoLogCategory category, Buffer buffer)
+{
+	/*
+	 * If any of the common header field is not available in the current undo
+	 * record then we must read it from the first complete record of the page.
+	 */
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_PAGE_COMMON) !=
+		UREC_INFO_PAGE_COMMON)
+	{
+		UnpackedUndoRecord first_uur = {0};
+		Page		page = BufferGetPage(buffer);
+		UndoPageHeader undo_phdr = (UndoPageHeader) page;
+		UndoRecPtr	first_urp = InvalidUndoRecPtr;
+		Size		partial_rec_size = SizeOfUndoPageHeaderData;
+		BlockNumber blkno = UndoRecPtrGetBlockNum(urp);
+
+		/*
+		 * If there is a partial record in the page then compute the size of
+		 * it so that we can compute the undo record pointer of the first
+		 * complete undo record of the page.
+		 */
+		if (undo_phdr->record_offset != 0)
+			partial_rec_size += UndoPagePartialRecSize(undo_phdr);
+
+		/*
+		 * Compute the undo record pointer of the first complete record of the
+		 * page.
+		 */
+		first_urp = MakeUndoRecPtr(rnode.relNode,
+								   UndoRecPageOffsetGetRecPtr(partial_rec_size, blkno));
+
+		/* Fetch the first undo record of the page. */
+		UndoGetOneRecord(&first_uur, first_urp, rnode, category, &buffer);
+
+		/*
+		 * Get all missing common header information from the first undo
+		 * records.
+		 */
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) == 0)
+			ucontext->urec_rmid = first_uur.uur_rmid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) == 0)
+			ucontext->urec_reloid = first_uur.uur_reloid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) == 0)
+			ucontext->urec_fxid = first_uur.uur_fxid;
+
+		if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) == 0)
+			ucontext->urec_cid = first_uur.uur_cid;
+
+		ucontext->urec_hd.urec_info |= UREC_INFO_PAGE_COMMON;
+	}
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoLogCategory category, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoLogCategory(category));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+		}
+
+		/* Acquire shared lock on the buffer before reading undo from it. */
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			if (buffer != *curbuf)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Get any of the missing fields from the first record of the
+			 * page.
+			 */
+			GetCommonUndoRecInfo(&ucontext, urp, rnode, category, *curbuf);
+			break;
+		}
+
+		/*
+		 * The record spans more than a page so we would have copied it (see
+		 * UnpackUndoRecord).  In such cases, we can release the buffer.
+		 */
+		if (buffer != *curbuf)
+			UnlockReleaseBuffer(buffer);
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	/* Unlock the buffer but keep the pin. */
+	LockBuffer(*curbuf, BUFFER_LOCK_UNLOCK);
+
+	return urec;
+}
+
+/*
+ * BeginUndoFetch to fetch undo record.
+ */
+void
+BeginUndoFetch(UndoRecordFetchContext *context)
+{
+	context->buffer = InvalidBuffer;
+	context->urp = InvalidUndoRecPtr;
+}
+
+/*
+ * Fetch the undo record for given undo record pointer.
+ *
+ * This will internally allocate the memory for the unpacked undo record which
+ * intern will hold the pointers to the optional headers and the variable data.
+ * The undo record should be freed by the caller by calling ReleaseUndoRecord.
+ * This function will old the pin on the buffer where we read the previous undo
+ * record so that when this function is called repeatedly with the same context
+ * then it can be benefitted by avoid reading the buffer again if the current
+ * undo record is in the same buffer.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecordFetchContext *context, UndoRecPtr urp)
+{
+	RelFileNode rnode;
+	int			logno;
+	UndoLogSlot *slot;
+	UnpackedUndoRecord *uur = NULL;
+
+	logno = UndoRecPtrGetLogNo(urp);
+	slot = UndoLogGetSlot(logno, true);
+
+	/*
+	 * If slot is NULL that means undo log number is unknown.  Presumably it
+	 * has been entirely discarded.
+	 */
+	if (slot == NULL)
+		return NULL;
+
+	/*
+	 * Prevent UndoDiscardOneLog() from discarding data while we try to read
+	 * it.  Usually we would acquire log->mutex to read log->meta members, but
+	 * in this case we know that discard can't move without also holding
+	 * log->discard_lock.
+	 *
+	 * In Hot Standby mode log->oldest_data is never initialized because it's
+	 * get updated by undo discard worker whereas in HotStandby undo logs are
+	 * getting discarded using discard WAL.  So in HotStandby we can directly
+	 * check whether the undo record pointer is discarded or not.  But, we can
+	 * not do same for normal case because discard worker can concurrently
+	 * discard the undo logs.
+	 *
+	 * XXX We can avoid this check by always initializing log->oldest_data in
+	 * HotStandby mode as well whenever we apply discard WAL.  But, for doing
+	 * that we need to acquire discard lock just for setting this variable?
+	 */
+	if (InHotStandby)
+	{
+		if (UndoRecPtrIsDiscarded(urp))
+			return NULL;
+	}
+	else
+	{
+		LWLockAcquire(&slot->discard_lock, LW_SHARED);
+		if (slot->logno != logno || urp < slot->oldest_data)
+		{
+			/*
+			 * The slot has been recycled because the undo log was entirely
+			 * discarded, or the pointer is before the oldest data.
+			 */
+			LWLockRelease(&slot->discard_lock);
+			return NULL;
+		}
+	}
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory by calling UndoRecordRelease.
+	 */
+	uur = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/*
+	 * Before fetching the next record check whether we have a valid buffer in
+	 * the context.  If so and if we are reading the current record from the
+	 * same then pass that buffer to fetch the undo record, otherwise release
+	 * the buffer.
+	 */
+	if (BufferIsValid(context->buffer) &&
+		(UndoRecPtrGetLogNo(context->urp) != UndoRecPtrGetLogNo(urp) ||
+		 UndoRecPtrGetBlockNum(context->urp) != UndoRecPtrGetBlockNum(urp)))
+	{
+		ReleaseBuffer(context->buffer);
+		context->buffer = InvalidBuffer;
+	}
+
+	/* Fetch the current undo record. */
+	UndoGetOneRecord(uur, urp, rnode, slot->meta.category, &context->buffer);
+
+	/* Release the discard lock after fetching the record. */
+	if (!InHotStandby)
+		LWLockRelease(&slot->discard_lock);
+
+	context->urp = urp;
+
+	return uur;
+}
+
+/*
+ * Finish undo record fetch.
+ */
+void
+FinishUndoFetch(UndoRecordFetchContext *context)
+{
+	if (BufferIsValid(context->buffer))
+		ReleaseBuffer(context->buffer);
+}
+
+/*
+ * Release the memory of the undo record allocated by UndoFetchRecord and
+ * UndoBulkFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	/* Release the memory of payload data if we allocated it. */
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+
+	/* Release memory of tuple data if we allocated it. */
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	/* Release memory of the transaction header if we allocated it. */
+	if (urec->uur_txn)
+		pfree(urec->uur_txn);
+
+	/* Release memory of the logswitch header if we allocated it. */
+	if (urec->uur_logswitch)
+		pfree(urec->uur_logswitch);
+
+	/* Release the memory of the undo record. */
+	pfree(urec);
+}
+
+/*
+ * Prefetch undo pages, if prefetch_pages are behind prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  UndoLogCategory category)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(rnode, MAIN_FORKNUM, startblock++,
+									  category == UNDO_TEMP);
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set of records
+ * and call this function again.
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the previous undo record pointer from where
+ *					  we need to start fetching on next call. Otherwise it will
+ *					  be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * undo_apply_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ * one_page			- Caller is applying undo only for one block not for
+ *					  complete transaction.  If this is set true then instead
+ *					  of following transaction undo chain using prevlen we will
+ *					  follow the block prev chain of the block so that we can
+ *					  avoid reading many unnecessary undo records of the
+ *					  transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int undo_apply_size, int *nrecords, bool one_page)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+	FullTransactionId fxid = InvalidFullTransactionId;
+
+	/*
+	 * In one_page mode we are fetching undo only for one page instead of
+	 * fetching all the undo of the transaction.  Basically, we are fetching
+	 * interleaved undo records.  So it does not make sense to do any prefetch
+	 * in that case.  Also, if we are fetching undo records from more than one
+	 * log, we don't know the boundaries for prefetching.  Hence, we can't use
+	 * prefetching in this case.
+	 */
+	if (!one_page &&
+		(UndoRecPtrGetLogNo(*from_urecptr) == UndoRecPtrGetLogNo(to_urecptr)))
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogSlot *slot;
+		UndoLogCategory category;
+		int			size;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			if (BufferIsValid(buffer))
+				ReleaseBuffer(buffer);
+			return NULL;
+		}
+		category = slot->meta.category;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just set the old
+		 * buffer into the new undo record so that UndoGetOneRecord don't read
+		 * the buffer again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				ReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are half of the prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target / 2)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, category);
+
+		/*
+		 * In one_page mode it's possible that the undo of the transaction
+		 * might have been applied by worker and undo got discarded. Prevent
+		 * discard worker from discarding undo data while we are reading it.
+		 * See detail comment in UndoFetchRecord.  In normal mode we are
+		 * holding transaction undo action lock so it can not be discarded.
+		 */
+		if (one_page)
+		{
+			/* Refer comments in UndoFetchRecord. */
+			if (InHotStandby)
+			{
+				if (UndoRecPtrIsDiscarded(urecptr))
+					break;
+			}
+			else
+			{
+				LWLockAcquire(&slot->discard_lock, LW_SHARED);
+				if (slot->logno != logno || urecptr < slot->oldest_data)
+				{
+					/*
+					 * The undo log slot has been recycled because it was
+					 * entirely discarded, or the data has been discarded
+					 * already.
+					 */
+					LWLockRelease(&slot->discard_lock);
+					break;
+				}
+			}
+
+			/* Read the undo record. */
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+			/* Release the discard lock after fetching the record. */
+			if (!InHotStandby)
+				LWLockRelease(&slot->discard_lock);
+		}
+		else
+			UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+		/*
+		 * As soon as the transaction id is changed we can stop fetching the
+		 * undo record.  Ideally, to_urecptr should control this but while
+		 * reading undo only for a page we don't know what is the end undo
+		 * record pointer for the transaction.
+		 */
+		if (one_page)
+		{
+			if (!FullTransactionIdIsValid(fxid))
+				fxid = uur->uur_fxid;
+			else if (!FullTransactionIdEquals(fxid, uur->uur_fxid))
+				break;
+		}
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we are reading undo only for a page then follow the blkprev chain
+		 * of the page.  Otherwise, calculate the previous undo record pointer
+		 * using transaction's current undo record pointer and the prevlen. If
+		 * undo record has a valid uur_prevurp, this is the case of log switch
+		 * during the transaction so we can directly use uur_prevurp as our
+		 * previous undo record pointer of the transaction.
+		 */
+		if (one_page)
+			urecptr = uur->uur_prevundo;
+		else if (uur->uur_logswitch)
+			urecptr = uur->uur_logswitch->urec_prevurp;
+		else if (prev_urec_ptr == to_urecptr ||
+				 uur->uur_info & UREC_INFO_TRANSACTION)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, category);
+
+		/* We have consumed all elements of the urp_array so expand its size. */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		size = UnpackedUndoRecordSize(uur);
+		total_size += size;
+
+		/*
+		 * Including current record, if we have crossed the memory limit or
+		 * undo log got switched then stop processing more records.  Remember
+		 * to set the from_urecptr so that on next call we can resume fetching
+		 * undo records where we left it.
+		 */
+		if (total_size >= undo_apply_size || uur->uur_logswitch)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		ReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoLogCategory category)
+{
+	UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	char		prevlen[2];
+	RelFileNode rnode;
+	int			byte_to_read = sizeof(uint16);
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoLogCategory(category);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length if the previous undo record is store at the end of that record
+	 * so just fetch last 2 bytes.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		page_offset -= 1;
+
+		/*
+		 * Read current prevlen byte from current block if page_offset hasn't
+		 * reach to undo block header.  Otherwise, go to the previous block
+		 * and continue reading from there.
+		 */
+		if (page_offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen[byte_to_read - 1] = pagedata[page_offset];
+			byte_to_read -= 1;
+		}
+		else
+		{
+			/*
+			 * Release the current buffer if it is not provide by the caller.
+			 */
+			if (input_buffer != buffer)
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * Could not read complete prevlen from the current block so go to
+			 * the previous block and start reading from end of the block.
+			 */
+			cur_blk -= 1;
+			page_offset = BLCKSZ;
+
+			/*
+			 * Reset buffer so that we can read it again for the previous
+			 * block.
+			 */
+			buffer = InvalidBuffer;
+		}
+	}
+
+	prev_rec_len = *(uint16 *) (prevlen);
+
+	/*
+	 * If previous undo record is not completely stored in this page then add
+	 * UndoLogBlockHeaderSize in total length so that the call can use this
+	 * length to compute the undo record pointer of the previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prev_rec_len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+					  UndoLogCategory category)
+{
+	UndoLogNumber logno = UndoRecPtrGetLogNo(urp);
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/* Read length of the previous undo record. */
+	prevlen = UndoGetPrevRecordLen(urp, buffer, category);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(logno, offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100755
index 0000000..a44bfbd
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,1051 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/bufmask.h"
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+							char **writeptr, char *endptr,
+							int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+						  char **readptr, char *endptr,
+						  int *total_bytes_read, int *partial_read);
+
+ /*
+  * Compute the header size of the undo record.
+  */
+Size
+UndoRecordHeaderSize(uint16 uur_info)
+{
+	Size		size;
+
+	/* Add fixed header size. */
+	size = SizeOfUndoRecordHeader;
+
+	/* Add size of transaction header if it presets. */
+	if ((uur_info & UREC_INFO_TRANSACTION) != 0)
+		size += SizeOfUndoRecordTransaction;
+
+	/* Add size of rmid if it presets. */
+	if ((uur_info & UREC_INFO_RMID) != 0)
+		size += sizeof(RmgrId);
+
+	/* Add size of reloid if it presets. */
+	if ((uur_info & UREC_INFO_RELOID) != 0)
+		size += sizeof(Oid);
+
+	/* Add size of fxid if it presets. */
+	if ((uur_info & UREC_INFO_XID) != 0)
+		size += sizeof(FullTransactionId);
+
+	/* Add size of cid if it presets. */
+	if ((uur_info & UREC_INFO_CID) != 0)
+		size += sizeof(CommandId);
+
+	/* Add size of forknum if it presets. */
+	if ((uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+
+	/* Add size of prevundo if it presets. */
+	if ((uur_info & UREC_INFO_PREVUNDO) != 0)
+		size += sizeof(UndoRecPtr);
+
+	/* Add size of the block header if it presets. */
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+
+	/* Add size of the log switch header if it presets. */
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+
+	/* Add size of the payload header if it presets. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+
+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur->uur_info);
+
+	/* Payload data size. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	/* Add undo record length size. */
+	size += sizeof(uint16);
+
+	return size;
+}
+
+/*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+	uint16		uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+	Size		size;
+
+	/* Header size. */
+	size = UndoRecordHeaderSize(uur_info);
+
+	/* Payload data size. */
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		UndoRecordPayload *payload = (UndoRecordPayload *) (page_ptr + size);
+
+		size += payload->urec_payload_len;
+		size += payload->urec_tuple_len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+	Size		size;
+
+	size = sizeof(UnpackedUndoRecord);
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+		memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);
+
+	/* Copy rmid if present. */
+	if ((uur->uur_info & UREC_INFO_RMID) != 0)
+		ucontext->urec_rmid = uur->uur_rmid;
+
+	/* Copy reloid if present. */
+	if ((uur->uur_info & UREC_INFO_RELOID) != 0)
+		ucontext->urec_reloid = uur->uur_reloid;
+
+	/* Copy fxid if present. */
+	if ((uur->uur_info & UREC_INFO_XID) != 0)
+		ucontext->urec_fxid = uur->uur_fxid;
+
+	/* Copy cid if present. */
+	if ((uur->uur_info & UREC_INFO_CID) != 0)
+		ucontext->urec_cid = uur->uur_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	/* Copy prev undo record pointer if it is present. */
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		ucontext->urec_prevundo = uur->uur_prevundo;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		memcpy(&ucontext->urec_logswitch, uur->uur_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+	else
+	{
+		ucontext->urec_payload.urec_payload_len = 0;
+		ucontext->urec_payload.urec_tuple_len = 0;
+	}
+
+	/* Compute undo record expected size and store in the context. */
+	ucontext->undo_len = UndoRecordExpectedSize(uur);
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+									 SizeOfUndoRecordTransaction,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_rmid), sizeof(RmgrId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_reloid), sizeof(Oid),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_fxid), sizeof(FullTransactionId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_cid), sizeof(CommandId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_prevundo,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record transaction header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordTransaction)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordTransaction;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RMID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(RmgrId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(RmgrId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RELOID) != 0)
+			{
+				if (bytes_to_skip < sizeof(Oid))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(Oid);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_XID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(TransactionId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(TransactionId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_CID) != 0)
+			{
+				if (bytes_to_skip < sizeof(CommandId))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(CommandId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_PREVUNDO) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordLogSwitch)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordLogSwitch;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+			/* fall through */
+		case UNDO_PACK_STAGE_TRANSACTION:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_txn,
+								   SizeOfUndoRecordTransaction,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RMID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_rmid,
+								   sizeof(RmgrId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RELOID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_reloid,
+								   sizeof(Oid),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+		case UNDO_PACK_STAGE_XID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fxid,
+								   sizeof(FullTransactionId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+		case UNDO_PACK_STAGE_CID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_cid,
+								   sizeof(CommandId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_prevundo,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_logswitch,
+								   SizeOfUndoRecordLogSwitch,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+
+	/* Copy undo record transaction header if it is present. */
+	if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+	{
+		uur->uur_txn = palloc(SizeOfUndoRecordTransaction);
+		memcpy(uur->uur_txn, &ucontext->urec_txn, SizeOfUndoRecordTransaction);
+	}
+
+	/*
+	 * Copy the common field.  All of these field must present in the final
+	 * unpacked undo record.
+	 */
+	Assert((uur->uur_info & UREC_INFO_PAGE_COMMON) == UREC_INFO_PAGE_COMMON);
+
+	uur->uur_rmid = ucontext->urec_rmid;
+	uur->uur_reloid = ucontext->urec_reloid;
+	uur->uur_fxid = ucontext->urec_fxid;
+	uur->uur_cid = ucontext->urec_cid;
+
+	/* Copy undo record relation header if it is present. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	/* Copy previous undo record pointer if it is present. */
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		uur->uur_prevundo = ucontext->urec_prevundo;
+
+	/* Copy undo record block header if it is present. */
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	/* Copy undo record log switch header if it is present. */
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		uur->uur_logswitch = palloc(SizeOfUndoRecordLogSwitch);
+		memcpy(uur->uur_logswitch, &ucontext->urec_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+	}
+
+	/* Copy undo record payload header and data if it is present. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which
+ * fields are set.
+ *
+ * Other flags i.e UREC_INFO_TRANSACTION, UREC_INFO_PAGE_COMMON and,
+ * UREC_INFO_LOGSWITCH are directly set by the PrepareUndoInsert function.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	/*
+	 * If fork number is not the main fork then we need to store it in the
+	 * undo record so set the flag.
+	 */
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+
+	/* If prevundo is valid undo record pointer then set the flag. */
+	if (uur->uur_prevundo != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_PREVUNDO;
+
+	/* If the block number is valid then set the flag for the block header. */
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+
+	/*
+	 * Either of the payload or the tuple length is non-zero then we need the
+	 * payload header.
+	 */
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810..f5133c7 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,34 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint16 uur_info, uint16 record_offset,
+			 uint16 tuple_len, uint16 payload_len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+	p->tuple_len = tuple_len;
+	p->payload_len = payload_len;
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052..cc00509 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 0000000..f7cfa9f
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,121 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the XactUndoRecordInfo for updating the transaction header.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_XACT_UNDO_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * Undo record element.  Used for storing the group of undo record in a array
+ * using UndoBulkFetchRecord.
+ */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element.  For stable qsort. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * This structure holds the informations for updating the transaction's undo
+ * record header (first undo record of the transaction).  We need to update the
+ * transaction header for various purposes a) updating the next undo record
+ * pointer for maintaining the transactions chain inside a undo log
+ * b) updating the undo apply progress in the transaction header.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct XactUndoRecordInfo
+{
+	UndoRecPtr	urecptr;		/* Undo record pointer to be updated. */
+	uint32		offset;			/* offset in page where to start updating. */
+	UndoRecPtr	next;			/* first urp of the next transaction which is
+								 * be updated in transaction header */
+	BlockNumber progress;		/* undo apply action progress. */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+} XactUndoRecordInfo;
+
+/*
+ * Context for preparing and inserting undo records..
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	XactUndoRecordInfo xact_urec_info[MAX_XACT_UNDO_INFO];	/* Information for
+															 * Updating transaction
+															 * header. */
+	UndoCompressionInfo undo_compression_info[UndoLogCategories];	/* Compression info. */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nxact_urec_info;	/* Number of previous xact info. */
+} UndoRecordInsertContext;
+
+/*
+ * Context for fetching the required undo record.
+ */
+typedef struct UndoRecordFetchContext
+{
+	Buffer		buffer;			/* Previous undo record pinned buffer. */
+	UndoRecPtr	urp;			/* Previous undo record pointer. */
+} UndoRecordFetchContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+								  UndoLogCategory category,
+								  int nprepared,
+								  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+									UnpackedUndoRecord *urec, Oid dbid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void BeginUndoFetch(UndoRecordFetchContext *context);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecordFetchContext *context,
+										   UndoRecPtr urp);
+extern void FinishUndoFetch(UndoRecordFetchContext *context);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+										UndoRecPtr to_urecptr,
+										int undo_apply_size, int *nrecords,
+										bool one_page);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+								   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+								 XLogRecPtr recptr);
+extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
+										UndoLogCategory category);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 1c79c1f..7ec4cb0 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -139,7 +139,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
@@ -169,6 +169,10 @@ typedef int UndoLogNumber;
 #define UndoRecPtrGetPageOffset(urp)			\
 	(UndoRecPtrGetOffset(urp) % BLCKSZ)
 
+/* Compute the undo record pointer offset given the undo rec page offset and the block number. */
+#define UndoRecPageOffsetGetRecPtr(offset, blkno)             \
+	((blkno * BLCKSZ) + offset)
+
 /* Compare two undo checkpoint files to find the oldest file. */
 #define UndoCheckPointFilenamePrecedes(file1, file2)	\
 	(strcmp(file1, file2) < 0)
@@ -207,7 +211,7 @@ typedef int UndoLogNumber;
  */
 typedef struct UndoLogUnloggedMetaData
 {
-	UndoLogOffset insert;			/* next insertion point (head) */
+	UndoLogOffset insert;		/* next insertion point (head) */
 	UndoLogOffset last_xact_start;	/* last transaction's first byte in this log */
 	UndoLogOffset this_xact_start;	/* this transaction's first byte in this log */
 	TransactionId xid;				/* currently attached/writing xid */
@@ -352,7 +356,7 @@ extern PGDLLIMPORT undologtable_hash *undologtable_cache;
 static pg_attribute_always_inline UndoLogTableEntry *
 UndoLogGetTableEntry(UndoLogNumber logno)
 {
-	UndoLogTableEntry  *entry;
+	UndoLogTableEntry *entry;
 
 	/* Fast path. */
 	entry = undologtable_lookup(undologtable_cache, logno);
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 0000000..0653267
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,279 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "access/transam.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+/*
+ * The below common information will be stored in the first undo record of the page.
+ * Every subsequent undo record will not store this information, if required this information
+ * will be retrieved from the first undo record of the page.
+ */
+typedef struct UndoCompressionInfo
+{
+	bool		valid;			/* Undo compression info is valid ? */
+	UndoRecPtr	last_urecptr;	/* last undo rec */
+	FullTransactionId fxid;		/* transaction id */
+	RmgrId		rmid;			/* rmgr ID */
+	Oid			reloid;			/* relation OID */
+	CommandId	cid;			/* command id */
+} UndoCompressionInfo;
+
+/*
+ * If UREC_INFO_TRANSACTION is set, an UndoRecordTransaction structure
+ * follows.
+ * If UREC_INFO_RMID is set, rmgr id follows.
+ * if UREC_INFO_RELOID	is set, relation oid follows.
+ * If UREC_INFO_XID	is set, full transaction id follows.
+ * If UREC_INFO_CID	is set, command id follows.
+ * If UREC_INFO_FORK is set, fork number follows.
+ * If UREC_INFO_PREVUNDO is set, previous undo record pointer follows.
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ * If UREC_INFO_LOGSWITCH is set, an UndoRecordLogSwitch structure follows.
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordTransaction appears first.
+ */
+#define UREC_INFO_TRANSACTION				0x001
+#define UREC_INFO_RMID						0x002
+#define UREC_INFO_RELOID					0x004
+#define UREC_INFO_XID						0x008
+#define UREC_INFO_CID						0x010
+#define UREC_INFO_FORK						0x020
+#define UREC_INFO_PREVUNDO					0x040
+#define UREC_INFO_BLOCK						0x080
+#define UREC_INFO_LOGSWITCH					0x100
+#define UREC_INFO_PAYLOAD					0x200
+
+#define UREC_INFO_PAGE_COMMON  (UREC_INFO_RMID | UREC_INFO_RELOID | UREC_INFO_XID | UREC_INFO_CID)
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	uint8		urec_type;		/* record type code */
+	uint16		urec_info;		/* flag bits */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_info) + sizeof(uint16))
+
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+	/*
+	 * Undo block number where we need to start reading the undo for applying
+	 * the undo action.   InvalidBlockNumber means undo applying hasn't
+	 * started for the transaction and MaxBlockNumber mean undo completely
+	 * applied. And, any other block number means we have applied partial undo
+	 * so next we can start from this block.
+	 */
+	BlockNumber urec_progress;
+	Oid			urec_dbid;		/* database id */
+	UndoRecPtr	urec_next;		/* urec pointer of the next transaction */
+} UndoRecordTransaction;
+
+#define SizeOfUndoRecordTransaction \
+	(offsetof(UndoRecordTransaction, urec_next) + sizeof(UndoRecPtr))
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information of the transaction's undo in the previous log.  If a transaction
+ * is split across the undo logs then this header will be included in the first
+ * undo record of the transaction in next log.
+ */
+typedef struct UndoRecordLogSwitch
+{
+	UndoRecPtr	urec_prevurp;	/* Transaction's last undo record pointer in
+								 * the previous undo log. */
+	UndoRecPtr	urec_prevlogstart;	/* Transaction's first undo record pointer
+									 * in previous undo log. */
+} UndoRecordLogSwitch;
+
+#define SizeOfUndoRecordLogSwitch \
+	(offsetof(UndoRecordLogSwitch, urec_prevlogstart) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_TRANSACTION,	/* The next thing to be processed is the
+									 * transaction details, if present. */
+	UNDO_PACK_STAGE_RMID,		/* The next thing to be processed is the rmid
+								 * if present */
+
+	UNDO_PACK_STAGE_RELOID,		/* The next thing to be processed is the
+								 * reloid if present */
+
+	UNDO_PACK_STAGE_XID,		/* The next thing to be processed is the xid
+								 * if present */
+
+	UNDO_PACK_STAGE_CID,		/* The next thing to be processed is the cid
+								 * if present */
+
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_PREVUNDO,	/* The next thing to be processed is the prev
+								 * undo info. */
+
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_LOGSWITCH,	/* The next thing to be processed is the log
+								 * switch details. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordTransaction urec_txn; /* Transaction header */
+
+	RmgrId		urec_rmid;		/* rmgrid */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	FullTransactionId urec_fxid;	/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecPtr	urec_prevundo;	/* Block prev */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecordLogSwitch urec_logswitch; /* Log switch header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint16		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_prevundo;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	FullTransactionId uur_fxid; /* transaction id */
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+
+	/*
+	 * Below header will be internally set by the undo layer.  Above this all
+	 * information should be set by the caller.
+	 */
+	UndoRecordTransaction *uur_txn; /* Transaction header, included in the
+									 * first record of the transaction in a
+									 * undo log. */
+	UndoRecordLogSwitch *uur_logswitch; /* Log switch header, included in the
+										 * first record of the transaction
+										 * only after undo log is switched
+										 * during a transaction. */
+} UnpackedUndoRecord;
+
+extern Size UndoRecordHeaderSize(uint16 uur_info);
+extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext,
+							UnpackedUndoRecord *uur);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+								  int bytes_to_skip);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+							 UnpackedUndoRecord *uur);
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+
+#endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 34b68ad..93d108c 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -216,6 +216,43 @@ typedef PageHeaderData *PageHeader;
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
 /*
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ *
+ * FIXME : for undo page do we need to keep all the information which is
+ * required for the PageHeaderData e.g. pd_lower, pd_upper, pd_special?
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+
+	/*
+	 * Below fields required for computing the offset of the first complete
+	 * record on a undo page, which will be used for the undo record compression
+	 * and undo page consistency checking.
+	 */
+	uint16		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		tuple_len;		/* Length of the tuple data in the partial
+								 * record. */
+	uint16		payload_len;	/* Length of the payload data in the partial
+								 * record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, payload_len) + \
+								  sizeof(uint16))
+
+/*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
  */
@@ -419,6 +456,9 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint16 uur_info,
+						 uint16 record_offset, uint16 tuple_len,
+						 uint16 payload_len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
-- 
1.8.3.1

0008-undo-page-consistency-checker.patchapplication/octet-stream; name=0008-undo-page-consistency-checker.patchDownload
From 78472d023fa481441dacabc46d689d9dd5c21a00 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Thu, 4 Jul 2019 11:52:55 +0530
Subject: [PATCH 08/13] undo page consistency checker

Patch provide a mechanism for masking the cid bit in undo pages so that
consistecy checker function can compared the undo pages.  Actual consistency
check should be called under the RM's consistency checker function who is
writing the undo because undo pages will be registered under that RM's WAL

Dilip Kumar with help from Amit Khandekar and Rafia Sabih
---
 src/backend/access/undo/undorecord.c | 110 +++++++++++++++++++++++++++++++++++
 src/include/access/undorecord.h      |   1 +
 2 files changed, 111 insertions(+)

diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
index a44bfbd..5699153 100755
--- a/src/backend/access/undo/undorecord.c
+++ b/src/backend/access/undo/undorecord.c
@@ -1049,3 +1049,113 @@ UndoRecordSetInfo(UnpackedUndoRecord *uur)
 	if (uur->uur_payload.len || uur->uur_tuple.len)
 		uur->uur_info |= UREC_INFO_PAYLOAD;
 }
+
+/*
+ * Get the offset of cid information in undo record.
+ */
+static Size
+get_undo_rec_cid_offset(uint16 urec_info)
+{
+	Size		offset_size = SizeOfUndoRecordHeader;
+
+	if ((urec_info & UREC_INFO_TRANSACTION) != 0)
+		offset_size += SizeOfUndoRecordTransaction;
+
+	if ((urec_info & UREC_INFO_RMID) != 0)
+		offset_size += sizeof(RmgrId);
+
+	if ((urec_info & UREC_INFO_RELOID) != 0)
+		offset_size += sizeof(Oid);
+
+	if ((urec_info & UREC_INFO_XID) != 0)
+		offset_size += sizeof(FullTransactionId);
+
+	return offset_size;
+}
+
+/*
+ * Mask a undo page before performing consistency checks on it.
+ */
+void
+mask_undo_page(char *pagedata)
+{
+	Page		page = (Page) pagedata;
+	char	   *page_end = pagedata + PageGetPageSize(page);
+	char	   *next_record;
+	int			cid_offset;
+	UndoPageHeader phdr = (UndoPageHeader) page;
+
+	next_record = (char *) page + SizeOfUndoPageHeaderData;
+
+	/*
+	 * If record_offset is non-zero value in the page header that means page
+	 * has a partial record.
+	 */
+	if (phdr->record_offset != 0)
+	{
+		Size		partial_rec_size;
+
+		/* Calculate the size of the partial record. */
+		partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+			phdr->tuple_len + phdr->payload_len -
+			phdr->record_offset;
+		if ((phdr->uur_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(phdr->uur_info);
+
+			/*
+			 * We just want to mask the cid in the undo record header.  So
+			 * only if the partial record in the current page include the undo
+			 * record header then we need to mask the cid bytes in this page.
+			 * Otherwise, directly jump to the next record.
+			 */
+			if (phdr->record_offset < (cid_offset + sizeof(CommandId)))
+			{
+				char	   *cid_data;
+				Size		mask_size;
+
+				mask_size = Min(cid_offset - phdr->record_offset,
+								sizeof(CommandId));
+
+				cid_data = next_record + cid_offset - phdr->record_offset;
+				memset(&cid_data, MASK_MARKER, mask_size);
+			}
+		}
+
+		next_record += partial_rec_size;
+	}
+
+	/*
+	 * Process the undo record of the page and mask their cid filed.
+	 */
+	while (next_record < page_end)
+	{
+		UndoRecordHeader *header = (UndoRecordHeader *) next_record;
+
+		/* If this undo record has cid present, then mask it */
+		if ((header->urec_info & UREC_INFO_CID) != 0)
+		{
+			cid_offset = get_undo_rec_cid_offset(header->urec_info);
+
+			/*
+			 * If this is not complete record then check whether cid is on
+			 * this page or not.  If not then we are done with this page.
+			 */
+			if ((next_record + cid_offset + sizeof(CommandId)) > page_end)
+			{
+				int			mask_size = page_end - next_record - cid_offset;
+
+				if (mask_size > 0)
+					memset(next_record + cid_offset, MASK_MARKER, mask_size);
+				break;
+			}
+			else
+			{
+				/* Mask cid */
+				memset(next_record + cid_offset, MASK_MARKER, sizeof(CommandId));
+			}
+		}
+		/* Go to next record. */
+		next_record += UndoRecordSizeOnPage(next_record);
+	}
+}
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 0653267..6a2c5cc 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -276,4 +276,5 @@ extern void FinishUnpackUndo(UndoPackContext *ucontext,
 							 UnpackedUndoRecord *uur);
 extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
 
+extern void mask_undo_page(char *pagedata);
 #endif							/* UNDORECORD_H */
-- 
1.8.3.1

0009-Extend-binary-heap-functionality.patchapplication/octet-stream; name=0009-Extend-binary-heap-functionality.patchDownload
From 85a0f0fe2d10dc524d0d52e53969ab02c0694042 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Mon, 27 May 2019 14:02:23 +0530
Subject: [PATCH 09/13] Extend binary heap functionality.

Add the routines to allocate binary heap in shared memory and to remove
nth element from binray heap.  This routines will be used by latter commit
to add support for undo workers.

Author: Kuntal Ghosh and Amit Kapila
---
 src/backend/lib/binaryheap.c | 118 +++++++++++++++++++++++++++++++++++++++++++
 src/include/lib/binaryheap.h |  12 ++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/src/backend/lib/binaryheap.c b/src/backend/lib/binaryheap.c
index a2c8967..5f7454d 100644
--- a/src/backend/lib/binaryheap.c
+++ b/src/backend/lib/binaryheap.c
@@ -16,6 +16,7 @@
 #include <math.h>
 
 #include "lib/binaryheap.h"
+#include "storage/shmem.h"
 
 static void sift_down(binaryheap *heap, int node_off);
 static void sift_up(binaryheap *heap, int node_off);
@@ -48,6 +49,36 @@ binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
 }
 
 /*
+ * binaryheap_allocate_shm
+ *
+ * It works same as binaryheap_allocate except that the heap will be created
+ * in shared memory.
+ */
+binaryheap *
+binaryheap_allocate_shm(const char *name, int capacity,
+						binaryheap_comparator compare, void *arg)
+{
+	Size		sz;
+	binaryheap *heap;
+	bool		foundBHeap;
+
+	sz = binaryheap_shmem_size(capacity);
+	heap = (binaryheap *) ShmemInitStruct(name, sz, &foundBHeap);
+
+	if (!foundBHeap)
+	{
+		heap->bh_space = capacity;
+		heap->bh_compare = compare;
+		heap->bh_arg = arg;
+
+		heap->bh_size = 0;
+		heap->bh_has_heap_property = true;
+	}
+
+	return heap;
+}
+
+/*
  * binaryheap_reset
  *
  * Resets the heap to an empty state, losing its data content but not the
@@ -212,6 +243,79 @@ binaryheap_replace_first(binaryheap *heap, Datum d)
 }
 
 /*
+ * binaryheap_nth
+ *
+ * Returns a pointer to the nth (0-based) node in the heap without modifying
+ * the heap in O(1).  The caller must ensure that this routine is not used on
+ * an empty heap and is not called with n greater than or equal to the heap
+ * size.
+ */
+Datum
+binaryheap_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+	return heap->bh_nodes[n];
+}
+
+/*
+ * binaryheap_remove_nth
+ *
+ * Removes the nth node (0-based) in the heap and returns a
+ * pointer to it after rebalancing the heap. The caller must ensure
+ * that this routine is not used on an empty heap.  O(log n) worst
+ * case.
+ */
+Datum
+binaryheap_remove_nth(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
+	Assert(n < heap->bh_size);
+
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+	sift_down(heap, n);
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
+ * binaryheap_remove_nth_unordered
+ *
+ * Removes the nth node (0-based) in the heap and returns a pointer to it in
+ * O(1) without preserving the heap property.  This is a convenience routine
+ * to remove elements quickly.  To obtain a valid heap, one must call
+ * binaryheap_build() afterwards.  The caller must ensure that this routine is
+ * not used on an empty heap.
+ */
+Datum
+binaryheap_remove_nth_unordered(binaryheap *heap, int n)
+{
+	Assert(!binaryheap_empty(heap));
+	Assert(n < heap->bh_size);
+
+	heap->bh_has_heap_property = false;
+
+	if (n == heap->bh_size - 1)
+	{
+		heap->bh_size--;
+		return heap->bh_nodes[heap->bh_size];
+	}
+
+	swap_nodes(heap, n, heap->bh_size - 1);
+	heap->bh_size--;
+
+	return heap->bh_nodes[heap->bh_size];
+}
+
+/*
  * Swap the contents of two nodes.
  */
 static inline void
@@ -305,3 +409,17 @@ sift_down(binaryheap *heap, int node_off)
 		node_off = swap_off;
 	}
 }
+
+/*
+ * Compute the size required by binary heap structure.
+ */
+Size
+binaryheap_shmem_size(int capacity)
+{
+	Size		sz;
+
+	sz = add_size(offsetof(binaryheap, bh_nodes),
+				  mul_size(sizeof(Datum), capacity));
+
+	return sz;
+}
diff --git a/src/include/lib/binaryheap.h b/src/include/lib/binaryheap.h
index 21f96b9..ed9e8e8 100644
--- a/src/include/lib/binaryheap.h
+++ b/src/include/lib/binaryheap.h
@@ -38,8 +38,11 @@ typedef struct binaryheap
 } binaryheap;
 
 extern binaryheap *binaryheap_allocate(int capacity,
-									   binaryheap_comparator compare,
-									   void *arg);
+					binaryheap_comparator compare,
+					void *arg);
+extern binaryheap *binaryheap_allocate_shm(const char *name, int capacity,
+					binaryheap_comparator compare,
+					void *arg);
 extern void binaryheap_reset(binaryheap *heap);
 extern void binaryheap_free(binaryheap *heap);
 extern void binaryheap_add_unordered(binaryheap *heap, Datum d);
@@ -48,7 +51,12 @@ extern void binaryheap_add(binaryheap *heap, Datum d);
 extern Datum binaryheap_first(binaryheap *heap);
 extern Datum binaryheap_remove_first(binaryheap *heap);
 extern void binaryheap_replace_first(binaryheap *heap, Datum d);
+extern Datum binaryheap_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth(binaryheap *heap, int n);
+extern Datum binaryheap_remove_nth_unordered(binaryheap *heap, int n);
+extern Size binaryheap_shmem_size(int capacity);
 
 #define binaryheap_empty(h)			((h)->bh_size == 0)
+#define binaryheap_cur_size(h)		((h)->bh_size)
 
 #endif							/* BINARYHEAP_H */
-- 
1.8.3.1

0010-Infrastructure-to-register-and-fetch-undo-action-req.patchapplication/octet-stream; name=0010-Infrastructure-to-register-and-fetch-undo-action-req.patchDownload
From 95d10fb308e3ec6ac8a7b4b5e7af78f6825f4dc8 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:10:06 +0530
Subject: [PATCH 10/13] Infrastructure to register and fetch undo action
 requests.

This infrasture provides a way to allow execution of undo actions.  One
might think that we can always execute undo actions on error or explicit
rollabck by user, however there are cases when that is not posssible.
For example, (a) if the system crash while doing operation, then after
startup, we need a way to perform undo actions; (b) If we get error while
performing undo actions.

Apart from this, when there are large rollback requests, then it is quite
inefficient to perform all the undo actions and then return control to
user.

To allow efficient execution of the undo actions, we create three queues
and a hash table for the rollback requests.  A Xid based priority queue
which will allow us to process the requests of older transactions and help
us to move oldesdXidHavingUnappliedUndo (this is a xid-horizon below which
all the transactions are visible) forward.  A size-based queue which will
help us to perform the rollbacks of larger aborts in a timely fashion so
that we don't get stuck while processing them during discard of the logs.
An error queue to hold the requests for transactions that failed to apply
its undo.  The rollback hash table is used to avoid duplicate undo requests
by backends and discard worker.

Amit Kapila and Kuntal Ghosh, design idea by Andres Freund.
---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undoaccess.c          |   50 +-
 src/backend/access/undo/undorequest.c         | 1647 +++++++++++++++++++++++++
 src/backend/storage/lmgr/lwlocknames.txt      |    1 +
 src/backend/storage/lmgr/proc.c               |    2 +
 src/backend/utils/init/postinit.c             |   14 +
 src/backend/utils/misc/guc.c                  |   22 +
 src/backend/utils/misc/postgresql.conf.sample |    8 +
 src/include/access/transam.h                  |    1 +
 src/include/access/undoaccess.h               |    3 +
 src/include/access/undorequest.h              |  231 ++++
 src/include/miscadmin.h                       |    1 +
 src/include/storage/proc.h                    |    2 +
 13 files changed, 1982 insertions(+), 2 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 049a416..7327502 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o
+OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index a8cf469..4ffec58 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -46,6 +46,7 @@
 #include "access/undorecord.h"
 #include "access/undoaccess.h"
 #include "access/undolog_xlog.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -754,7 +755,7 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	{
 		urec->uur_txn = palloc(SizeOfUndoRecordTransaction);
 		urec->uur_txn->urec_dbid = dbid;
-		urec->uur_txn->urec_progress = InvalidBlockNumber;
+		urec->uur_txn->urec_progress = XACT_APPLY_PROGRESS_NOT_STARTED;
 		urec->uur_txn->urec_next = InvalidUndoRecPtr;
 	}
 	else
@@ -1752,3 +1753,50 @@ UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 	/* calculate the previous undo record pointer */
 	return MakeUndoRecPtr(logno, offset - prevlen);
 }
+
+/*
+ * Returns the undo record pointer corresponding to first record in the given
+ * block.
+ */
+UndoRecPtr
+UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
+							UndoLogCategory category)
+{
+	Buffer buffer;
+	Page page;
+	UndoPageHeader	phdr;
+	RelFileNode		rnode;
+	UndoLogOffset	log_cur_off;
+	Size			partial_rec_size;
+	int				offset_cur_page;
+
+	if (!BlockNumberIsValid(blkno))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid undo block number")));
+
+	UndoRecPtrAssignRelFileNode(rnode, urec_ptr);
+
+	buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, blkno,
+									   RBM_NORMAL, NULL,
+									   RelPersistenceForUndoLogCategory(category));
+
+	LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader)page;
+
+	/* Calculate the size of the partial record. */
+	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
+						phdr->tuple_len + phdr->payload_len -
+						phdr->record_offset;
+
+	/* calculate the offset in current log. */
+	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
+	log_cur_off = (blkno * BLCKSZ) + offset_cur_page;
+
+	UnlockReleaseBuffer(buffer);
+
+	/* calculate the undo record pointer based on current offset in log. */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urec_ptr), log_cur_off);
+}
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000..530887a
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1647 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *	  This contains routines to register and fetch undo action requests.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ * To increase the efficiency of the rollbacks, we create three queues and
+ * a hash table for the rollback requests.  A Xid based priority queue which
+ * will allow us to process the requests of older transactions and help us
+ * to move oldesdXidHavingUndo forward.  A size-based queue which will help
+ * us to perform the rollbacks of larger aborts in a timely fashion, so that
+ * we don't get stuck while processing them during discard of the logs.
+ * An error queue to hold the requests for transactions that failed to apply
+ * its undo.  The rollback hash table is used to avoid duplicate undo requests
+ * by backends and discard worker.  The table must be able to accommodate all
+ * active undo requests.  The undo requests must appear in both xid and size
+ * requests queues or neither.  As of now we, process the requests from these
+ * queues in a round-robin fashion to give equal priority to all three type
+ * of requests.
+ *
+ * The rollback requests exceeding a certain threshold are pushed into both
+ * xid and size based queues.  They are also registered in the hash table.
+ *
+ * To ensure that backend and discard worker don't register the same request
+ * in the hash table, we always register the request with full_xid and the
+ * start pointer for the transaction in the hash table as key.  Backends
+ * always remember the value of start pointer, but discard worker doesn't know
+ * the actual start value in case transaction's undo spans across multiple
+ * logs.  The reason for the same is that discard worker might encounter the
+ * log which has overflowed undo records of the transaction first.  In such
+ * cases, we need to compute the actual start position.  The first record of a
+ * transaction in each undo log contains a reference to the first record of
+ * this transaction in the previous log.  By following the previous log chain
+ * of this transaction, we find the initial location which is used to register
+ * the request.
+ *
+ * To process the request, we get the request from one of the queues, search
+ * it in hash table and mark it as in-progress and then remove from the
+ * respective queue.  Once we process all the actions, the request is removed
+ * from the hash table.  If the worker found the request in the queue, but
+ * the request is not present in hash table or is marked as in-progress, then
+ * it can ignore such a request (and remove it from that queue) as it must
+ * have been already processed or is being processed.
+ *
+ * Also note that, if the work queues are full, then we put backpressure on
+ * backends to complete the requests by themselves.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/transam.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "lib/binaryheap.h"
+#include "storage/bufmgr.h"
+#include "storage/shmem.h"
+#include "storage/procarray.h"
+#include "utils/fmgroids.h"
+#include "access/xlog.h"
+#include "storage/proc.h"
+
+#define	MAX_UNDO_WORK_QUEUES	3
+#define UNDO_PEEK_DEPTH		10
+#define UNDO_FAILURE_RETRY_DELAY_MS 10000
+
+int			rollback_overflow_size = 64;
+int			pending_undo_queue_size = 1024;
+
+/* Each worker queue is a binary heap. */
+typedef struct
+{
+	binaryheap *bh;
+	union
+	{
+		UndoXidQueue *xid_elems;
+		UndoSizeQueue *size_elems;
+		UndoErrorQueue *error_elems;
+	}			q_choice;
+} UndoWorkerQueue;
+
+/* This is the hash table to store all the rollabck requests. */
+static HTAB *RollbackHT;
+static UndoWorkerQueue UndoWorkerQueues[MAX_UNDO_WORK_QUEUES];
+
+static uint32 cur_undo_queue = 0;
+
+/* Different operations for XID queue */
+#define InitXidQueue(bh, elems) \
+( \
+	UndoWorkerQueues[XID_QUEUE].bh = bh, \
+	UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems = elems \
+)
+
+#define XidQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[XID_QUEUE].bh))
+
+#define GetXidQueueElem(elem) \
+	(UndoWorkerQueues[XID_QUEUE].q_choice.xid_elems[elem])
+
+#define GetXidQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[XID_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[XID_QUEUE].bh)) \
+)
+
+#define GetXidQueueNthElem(n) \
+( \
+	AssertMacro(!XidQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[XID_QUEUE].bh, n)) \
+)
+
+#define SetXidQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr) \
+( \
+	GetXidQueueElem(elem).dbid = e_dbid, \
+	GetXidQueueElem(elem).full_xid = e_full_xid, \
+	GetXidQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for SIZE queue */
+#define InitSizeQueue(bh, elems) \
+( \
+	UndoWorkerQueues[SIZE_QUEUE].bh = bh, \
+	UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems = elems \
+)
+
+#define SizeQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[SIZE_QUEUE].bh))
+
+#define GetSizeQueueElem(elem) \
+	(UndoWorkerQueues[SIZE_QUEUE].q_choice.size_elems[elem])
+
+#define GetSizeQueueTopElem() \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[SIZE_QUEUE].bh)) \
+)
+
+#define GetSizeQueueNthElem(n) \
+( \
+	AssertMacro(!SizeQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n)) \
+)
+
+#define SetSizeQueueElem(elem, e_dbid, e_full_xid, e_size, e_start_urec_ptr) \
+( \
+	GetSizeQueueElem(elem).dbid = e_dbid, \
+	GetSizeQueueElem(elem).full_xid = e_full_xid, \
+	GetSizeQueueElem(elem).request_size = e_size, \
+	GetSizeQueueElem(elem).start_urec_ptr = e_start_urec_ptr \
+)
+
+/* Different operations for Error queue */
+#define InitErrorQueue(bh, elems) \
+( \
+	UndoWorkerQueues[ERROR_QUEUE].bh = bh, \
+	UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems = elems \
+)
+
+#define ErrorQueueIsEmpty() \
+	(binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueSize() \
+	(binaryheap_cur_size(UndoWorkerQueues[ERROR_QUEUE].bh))
+
+#define GetErrorQueueElem(elem) \
+	(UndoWorkerQueues[ERROR_QUEUE].q_choice.error_elems[elem])
+
+#define GetErrorQueueTopElem() \
+( \
+	AssertMacro(!binaryheap_empty(UndoWorkerQueues[ERROR_QUEUE].bh)), \
+	DatumGetPointer(binaryheap_first(UndoWorkerQueues[ERROR_QUEUE].bh)) \
+)
+
+#define GetErrorQueueNthElem(n) \
+( \
+	AssertMacro(!ErrorQueueIsEmpty()), \
+	DatumGetPointer(binaryheap_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n)) \
+)
+
+#define SetErrorQueueElem(elem, e_dbid, e_full_xid, e_start_urec_ptr, e_retry_at, e_occurred_at) \
+( \
+	GetErrorQueueElem(elem).dbid = e_dbid, \
+	GetErrorQueueElem(elem).full_xid = e_full_xid, \
+	GetErrorQueueElem(elem).start_urec_ptr = e_start_urec_ptr, \
+	GetErrorQueueElem(elem).next_retry_at = e_retry_at, \
+	GetErrorQueueElem(elem).err_occurred_at = e_occurred_at \
+)
+
+/*
+ * Binary heap comparison function to compare the age of transactions.
+ */
+static int
+undo_age_comparator(Datum a, Datum b, void *arg)
+{
+	UndoXidQueue *xidQueueElem1 = (UndoXidQueue *) DatumGetPointer(a);
+	UndoXidQueue *xidQueueElem2 = (UndoXidQueue *) DatumGetPointer(b);
+
+	if (FullTransactionIdPrecedes(xidQueueElem1->full_xid,
+								  xidQueueElem2->full_xid))
+		return 1;
+	else if (FullTransactionIdFollows(xidQueueElem1->full_xid,
+									  xidQueueElem2->full_xid))
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the size of transactions.
+ */
+static int
+undo_size_comparator(Datum a, Datum b, void *arg)
+{
+	UndoSizeQueue *sizeQueueElem1 = (UndoSizeQueue *) DatumGetPointer(a);
+	UndoSizeQueue *sizeQueueElem2 = (UndoSizeQueue *) DatumGetPointer(b);
+
+	if (sizeQueueElem1->request_size > sizeQueueElem2->request_size)
+		return 1;
+	else if (sizeQueueElem1->request_size < sizeQueueElem2->request_size)
+		return -1;
+	return 0;
+}
+
+/*
+ * Binary heap comparison function to compare the time at which an error
+ * occurred for transactions.
+ *
+ * The error queue is sorted by next_retry_at and err_occurred_at.  Currently,
+ * the next_retry_at has some constant delay time (see PushErrorQueueElem), so
+ * it doesn't make much sense to sort by both values.  However, in future, if
+ * we have some different algorithm for next_retry_at, then it will work
+ * seamlessly.
+ */
+static int
+undo_err_time_comparator(Datum a, Datum b, void *arg)
+{
+	UndoErrorQueue *errQueueElem1 = (UndoErrorQueue *) DatumGetPointer(a);
+	UndoErrorQueue *errQueueElem2 = (UndoErrorQueue *) DatumGetPointer(b);
+
+	if (errQueueElem1->next_retry_at < errQueueElem2->next_retry_at)
+		return 1;
+	else if (errQueueElem1->next_retry_at > errQueueElem2->next_retry_at)
+		return -1;
+	if (errQueueElem1->err_occurred_at < errQueueElem2->err_occurred_at)
+		return 1;
+	else if (errQueueElem1->err_occurred_at > errQueueElem2->err_occurred_at)
+		return -1;
+	return 0;
+}
+
+/* Returns the size of xid based queue. */
+static int
+UndoXidQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoXidQueue));
+}
+
+/* Returns the size of rollback request size based queue. */
+static int
+UndoSizeQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoSizeQueue));
+}
+
+/* Returns the size of error queue. */
+static int
+UndoErrorQueueElemsShmSize(void)
+{
+	return mul_size(pending_undo_queue_size, sizeof(UndoErrorQueue));
+}
+
+/* Returns the size of rollback hash table. */
+int
+UndoRollbackHashTableSize()
+{
+	/*
+	 * The rollback hash table is used to avoid duplicate undo requests by
+	 * backends and discard worker.  The table must be able to accomodate all
+	 * active undo requests.  The undo requests must appear in both xid and
+	 * size requests queues or neither.  In same transaction, there can be two
+	 * requests one for logged relations and another for unlogged relations.
+	 * So, the rollback hash table size should be equal to two request queues,
+	 * an error queue (currently this is same as request queue) and max
+	 * backends. This will ensure that it won't get filled.
+	 */
+	return ((2 * pending_undo_queue_size) + pending_undo_queue_size +
+			MaxBackends);
+}
+
+/* Get the first free element of xid based request array. */
+static int
+UndoXidQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetXidQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the xid based request queue. */
+static void
+PushXidQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoXidQueueGetFreeElem();
+
+	SetXidQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[XID_QUEUE].bh,
+				   PointerGetDatum(&GetXidQueueElem(elem)));
+}
+
+/* Pop nth element from the xid based request queue. */
+static UndoXidQueue *
+PopXidQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!XidQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[XID_QUEUE].bh, n);
+
+	return (UndoXidQueue *) (DatumGetPointer(elem));
+}
+
+/* Get the first free element of size based request array. */
+static int
+UndoSizeQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetSizeQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromXidQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetXidQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoXidQueue *elem = (UndoXidQueue *) GetXidQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			nCleaned++;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[XID_QUEUE].bh, i);
+
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[XID_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Push an element in the size based request queue */
+static void
+PushSizeQueueElem(UndoRequestInfo * urinfo)
+{
+	int			elem = UndoSizeQueueGetFreeElem();
+
+	SetSizeQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					 urinfo->request_size, urinfo->start_urec_ptr);
+
+	binaryheap_add(UndoWorkerQueues[SIZE_QUEUE].bh,
+				   PointerGetDatum(&GetSizeQueueElem(elem)));
+}
+
+/* Pop nth element from the size based request queue */
+static UndoSizeQueue *
+PopSizeQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!binaryheap_empty(UndoWorkerQueues[SIZE_QUEUE].bh));
+	elem = binaryheap_remove_nth(UndoWorkerQueues[SIZE_QUEUE].bh, n);
+
+	return (UndoSizeQueue *) DatumGetPointer(elem);
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromSizeQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetSizeQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoSizeQueue *elem = (UndoSizeQueue *) GetSizeQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->request_size = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[SIZE_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[SIZE_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/* Get the first free element of error time based request array. */
+static int
+UndoErrorQueueGetFreeElem(void)
+{
+	int			i;
+
+	for (i = 0; i < pending_undo_queue_size; i++)
+	{
+		if (FullTransactionIdEquals(GetErrorQueueElem(i).full_xid,
+									InvalidFullTransactionId))
+			return i;
+	}
+
+	/* we should never call this function when the request queue is full. */
+	Assert(false);
+
+	/* silence compiler. */
+	return -1;
+}
+
+/* Push an element in the error time based request queue */
+static void
+PushErrorQueueElem(volatile UndoRequestInfo *urinfo)
+{
+	int			elem = UndoErrorQueueGetFreeElem();
+	TimestampTz now = GetCurrentTimestamp();
+	TimestampTz next_retry;
+
+	/*
+	 * We want to retry this error request after some constant amount of time,
+	 * rather than retrying immediately, otherwise, in some cases (ex. when
+	 * all the pending requests are failed requests) worker will keep retrying
+	 * such errors constantly.
+	 *
+	 * In future, we might want some more sophisticated back-off algorithm
+	 * to delay the execution of such requests.
+	 */
+	next_retry = TimestampTzPlusMilliseconds(now, UNDO_FAILURE_RETRY_DELAY_MS);
+	SetErrorQueueElem(elem, urinfo->dbid, urinfo->full_xid,
+					  urinfo->start_urec_ptr, next_retry, now);
+
+	binaryheap_add(UndoWorkerQueues[ERROR_QUEUE].bh,
+				   PointerGetDatum(&GetErrorQueueElem(elem)));
+}
+
+/* Pop nth element from the error time based request queue */
+static UndoErrorQueue *
+PopErrorQueueNthElem(int n)
+{
+	Datum		elem;
+
+	Assert(!ErrorQueueIsEmpty());
+	elem = binaryheap_remove_nth(UndoWorkerQueues[ERROR_QUEUE].bh, n);
+
+	return (UndoErrorQueue *) (DatumGetPointer(elem));
+}
+
+/*
+ * Traverse the queue and remove dangling entries, if any.  The queue
+ * entry is considered dangling if the hash table doesn't contain the
+ * corresponding entry.
+ */
+static int
+RemoveOldElemsFromErrorQueue()
+{
+	int			nCleaned = 0;
+	int			i = 0;
+
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	while (i < GetErrorQueueSize())
+	{
+		RollbackHashEntry *rh;
+		RollbackHashKey hkey;
+		UndoErrorQueue *elem = (UndoErrorQueue *) GetErrorQueueNthElem(i);
+
+		hkey.full_xid = elem->full_xid;
+		hkey.start_urec_ptr = elem->start_urec_ptr;
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			elem->dbid = InvalidOid;
+			elem->full_xid = InvalidFullTransactionId;
+			elem->next_retry_at = 0;
+			elem->err_occurred_at = 0;
+			binaryheap_remove_nth_unordered(UndoWorkerQueues[ERROR_QUEUE].bh, i);
+			nCleaned++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		i++;
+	}
+
+	binaryheap_build(UndoWorkerQueues[ERROR_QUEUE].bh);
+
+	return nCleaned;
+}
+
+/*
+ * Remove nth work item from queue and clear the array element as well from
+ * the corresponding queue.
+ */
+static void
+RemoveRequestFromQueue(UndoWorkerQueueType type, int n)
+{
+	if (type == XID_QUEUE)
+	{
+		UndoXidQueue *uXidQueueElem = (UndoXidQueue *) PopXidQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uXidQueueElem->full_xid));
+		uXidQueueElem->dbid = InvalidOid;
+		uXidQueueElem->full_xid = InvalidFullTransactionId;
+	}
+	else if (type == SIZE_QUEUE)
+	{
+		UndoSizeQueue *uSizeQueueElem = (UndoSizeQueue *) PopSizeQueueNthElem(n);
+
+		Assert(FullTransactionIdIsValid(uSizeQueueElem->full_xid));
+		uSizeQueueElem->dbid = InvalidOid;
+		uSizeQueueElem->full_xid = InvalidFullTransactionId;
+		uSizeQueueElem->request_size = 0;
+	}
+	else
+	{
+		UndoErrorQueue *uErrorQueueElem = (UndoErrorQueue *) PopErrorQueueNthElem(n);
+
+		Assert(type == ERROR_QUEUE);
+		Assert(FullTransactionIdIsValid(uErrorQueueElem->full_xid));
+		uErrorQueueElem->dbid = InvalidOid;
+		uErrorQueueElem->full_xid = InvalidFullTransactionId;
+		uErrorQueueElem->next_retry_at = 0;
+		uErrorQueueElem->err_occurred_at = 0;
+	}
+}
+
+/*
+ * Returns true, if there is some valid request in the given queue, false,
+ * otherwise.
+ *
+ * It fills hkey with hash key corresponding to the nth element of the
+ * specified queue.
+ */
+static bool
+GetRollbackHashKeyFromQueue(UndoWorkerQueueType cur_queue, int n,
+							RollbackHashKey *hkey)
+{
+	if (cur_queue == XID_QUEUE)
+	{
+		UndoXidQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetXidQueueSize() <= n)
+			return false;
+
+		elem = (UndoXidQueue *) GetXidQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else if (cur_queue == SIZE_QUEUE)
+	{
+		UndoSizeQueue *elem;
+
+		/* check if there is a work in the next queue */
+		if (GetSizeQueueSize() <= n)
+			return false;
+
+		elem = (UndoSizeQueue *) GetSizeQueueNthElem(n);
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+	else
+	{
+		UndoErrorQueue *elem;
+
+		/* It must be an error queue. */
+		Assert(cur_queue == ERROR_QUEUE);
+
+		/* check if there is a work in the next queue */
+		if (GetErrorQueueSize() <= n)
+			return false;
+
+		elem = (UndoErrorQueue *) GetErrorQueueNthElem(n);
+
+		/*
+		 * If it is too early to try the error request again, then check the
+		 * work in some other queue.
+		 */
+		if (GetCurrentTimestamp() < elem->next_retry_at)
+			return false;
+
+		hkey->full_xid = elem->full_xid;
+		hkey->start_urec_ptr = elem->start_urec_ptr;
+	}
+
+	return true;
+}
+
+/*
+ * Fetch the end urec pointer for the transaction and the undo request size.
+ *
+ * end_urecptr_out - This is an INOUT parameter. If end undo pointer is
+ * specified, we use the same to calculate the size.  Else, we calculate
+ * the end undo pointer and return the same.
+ *
+ * last_log_start_urec_ptr_out - This is an OUT parameter.  If a transaction
+ * writes undo records in multiple undo logs, this is set to the start undo
+ * record pointer of this transaction in the last log.  If the transaction
+ * writes undo records only in single undo log, it is set to start_urec_ptr.
+ * This value is used to update the rollback progress of the transaction in
+ * the last log.  Once, we have start location in last log, the start location
+ * in all the previous logs can be computed.  See execute_undo_actions for
+ * more details.
+ *
+ * XXX: We don't calculate the exact undo size.  We always skip the size of
+ * the last undo record (if not already discarded) from the calculation.  This
+ * optimization allows us to skip fetching an undo record for the most
+ * frequent cases where the end pointer and current start pointer belong to
+ * the same log.  A simple subtraction between them gives us the size.  In
+ * future this function can be modified if someone needs the exact undo size.
+ * As of now, we use this function to calculate the undo size for inserting
+ * in the pending undo actions in undo worker's size queue.
+ */
+uint64
+FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+						   UndoRecPtr *end_urecptr_out,
+						   UndoRecPtr *last_log_start_urecptr_out,
+						   FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = start_urecptr;
+	UndoRecPtr	end_urecptr = InvalidUndoRecPtr;
+	UndoRecPtr	last_log_start_urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+	UndoLogCategory category;
+
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	while (true)
+	{
+		UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+		UndoLogOffset next_insert;
+		UndoRecordFetchContext	context;
+
+		if (*end_urecptr_out != InvalidUndoRecPtr)
+		{
+			/*
+			 * Check whether end pointer and the current pointer belong to
+			 * same log. In that case, we can get the size easily.
+			 */
+			if (UndoRecPtrGetLogNo(urecptr) == UndoRecPtrGetLogNo(*end_urecptr_out))
+			{
+				last_log_start_urecptr = urecptr;
+				sz += (*end_urecptr_out - urecptr);
+				break;
+			}
+		}
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+		category = slot->meta.category;
+
+		next_insert = UndoLogGetNextInsertPtr(slot->logno);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		/* Fetch the undo record. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, urecptr);
+		FinishUndoFetch(&context);
+
+		/*
+		 * If the corresponding undo record got rolled back and discarded as
+		 * well, we return from here.
+		 */
+		if (uur == NULL)
+			break;
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * Case 1: Check whether any undo records have been applied from this
+		 * log.  Else, we've to find the undo location till where the undo
+		 * actions have been applied.
+		 */
+		if (!IsXactApplyProgressNotStarted(uur->uur_txn->urec_progress))
+		{
+			/*
+			 * If all the undo records in this log corresponding to this
+			 * transaction, has been applied, we return from here.
+			 */
+			if (IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+				break;
+
+			/*
+			 * Find the first undo record of uur_progress block number.  We'll
+			 * set end_urec_ptr to this undo record.
+			 */
+			end_urecptr = UndoBlockGetFirstUndoRecord(uur->uur_txn->urec_progress,
+													  urecptr, category);
+
+			/*
+			 * Since rollbacks from this undo log are in-progress, all undo
+			 * records from subsequent undo logs must have been applied.  Hence,
+			 * this is the last log.  So, we set last_log_start_urecptr as the
+			 * start undo record pointer of this transaction from current log.
+			 */
+			last_log_start_urecptr = urecptr;
+			sz += (end_urecptr - urecptr);
+			break;
+		}
+
+		next_urecptr = uur->uur_txn->urec_next;
+
+		/*
+		 * Case 2: If this is the last transaction in the log then calculate
+		 * the latest urec pointer using next insert location of the undo log.
+		 *
+		 * Even if some new undo got inserted after we have fetched this
+		 * transactions undo record, still the next_insert location will give
+		 * us the right point to compute end_urecptr.
+		 */
+		if (!UndoRecPtrIsValid(next_urecptr))
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 3: The transaction ended in the same undo log, but this is not
+		 * the last transaction.
+		 */
+		if (UndoRecPtrGetLogNo(next_urecptr) == slot->logno)
+		{
+			last_log_start_urecptr = urecptr;
+			end_urecptr =
+				UndoGetPrevUndoRecptr(next_urecptr, InvalidBuffer, category);
+			sz += (end_urecptr - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 4: If transaction is overflowed to a different undolog and
+		 * it's already discarded.  It means that the undo actions for this
+		 * transaction which are in the next log are already executed.
+		 */
+		if (UndoRecPtrIsDiscarded(next_urecptr))
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (next_insert - urecptr);
+			Assert(UndoRecPtrIsValid(end_urecptr));
+			break;
+		}
+
+		/*
+		 * Case 5: The transaction is overflowed to a different log, so
+		 * restart the processing from then next log but before that consider
+		 * this log for request size computation.
+		 */
+		{
+			UndoLogOffset next_insert;
+
+			next_insert = UndoLogGetNextInsertPtr(slot->logno);
+			Assert(UndoRecPtrIsValid(next_insert));
+
+			last_log_start_urecptr = urecptr;
+			end_urecptr = UndoGetPrevUndoRecptr(next_insert, InvalidBuffer, category);
+			sz += (next_insert - urecptr);
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/* Follow the undo chain */
+		urecptr = next_urecptr;
+	}
+
+	if (uur != NULL)
+		UndoRecordRelease(uur);
+
+	if (end_urecptr_out && (*end_urecptr_out == InvalidUndoRecPtr))
+		*end_urecptr_out = end_urecptr;
+	if (last_log_start_urecptr_out &&
+		(*last_log_start_urecptr_out == InvalidUndoRecPtr))
+		*last_log_start_urecptr_out = last_log_start_urecptr;
+
+	return sz;
+}
+
+/*
+ * Returns true, if we can push the rollback request to undo wrokers, false,
+ * otherwise.
+ */
+static bool
+CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
+					   uint64 req_size)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will check
+	 * the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+
+	/*
+	 * We normally push the rollback request to undo workers if the size of
+	 * same is above a certain threshold.
+	 */
+	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	{
+		if (GetXidQueueSize() >= pending_undo_queue_size ||
+			GetSizeQueueSize() >= pending_undo_queue_size)
+		{
+			/*
+			 * If one of the queues is full traverse both the queues and
+			 * remove dangling entries, if any.  The queue entry is considered
+			 * dangling if the hash table doesn't contain the corresponding
+			 * entry.  It can happen due to two reasons (a) we have processed
+			 * the entry from one of the queues, but not from the other. (b)
+			 * the corresponding database has been dropped due to which we
+			 * have removed the entries from hash table, but not from the
+			 * queues.  This is just a lazy cleanup, if we want we can remove
+			 * the entries from the queues when we detect that the database is
+			 * dropped and remove the corresponding entries from hash table.
+			 */
+			if (GetXidQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromXidQueue();
+			if (GetSizeQueueSize() >= pending_undo_queue_size)
+				RemoveOldElemsFromSizeQueue();
+		}
+
+		if ((GetXidQueueSize() < pending_undo_queue_size))
+		{
+			Assert(GetSizeQueueSize() < pending_undo_queue_size);
+
+			/*
+			 * XXX - Here, we should return true once we have background
+			 * worker facility.
+			 */
+			return false;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * To return the size of the request queues and hash-table for rollbacks.
+ */
+int
+PendingUndoShmemSize(void)
+{
+	Size		size;
+
+	size = hash_estimate_size(UndoRollbackHashTableSize(), sizeof(RollbackHashEntry));
+	size = add_size(size, mul_size(MAX_UNDO_WORK_QUEUES,
+								   binaryheap_shmem_size(pending_undo_queue_size)));
+	size = add_size(size, UndoXidQueueElemsShmSize());
+	size = add_size(size, UndoSizeQueueElemsShmSize());
+	size = add_size(size, UndoErrorQueueElemsShmSize());
+
+	return size;
+}
+
+/*
+ * Initialize the hash-table and priority heap based queues for rollback
+ * requests in shared memory.
+ */
+void
+PendingUndoShmemInit(void)
+{
+	HASHCTL		info;
+	bool		foundXidQueue = false;
+	bool		foundSizeQueue = false;
+	bool		foundErrorQueue = false;
+	binaryheap *bh;
+	UndoXidQueue *xid_elems;
+	UndoSizeQueue *size_elems;
+	UndoErrorQueue *error_elems;
+
+	MemSet(&info, 0, sizeof(info));
+
+	info.keysize = sizeof(TransactionId) + sizeof(UndoRecPtr);
+	info.entrysize = sizeof(RollbackHashEntry);
+	info.hash = tag_hash;
+
+	RollbackHT = ShmemInitHash("Undo Actions Lookup Table",
+							   UndoRollbackHashTableSize(),
+							   UndoRollbackHashTableSize(), &info,
+							   HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
+
+	bh = binaryheap_allocate_shm("Undo Xid Binary Heap",
+								 pending_undo_queue_size,
+								 undo_age_comparator,
+								 NULL);
+
+	xid_elems = (UndoXidQueue *) ShmemInitStruct("Undo Xid Queue Elements",
+												 UndoXidQueueElemsShmSize(),
+												 &foundXidQueue);
+
+	Assert(foundXidQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(xid_elems, 0, sizeof(UndoXidQueue));
+
+	InitXidQueue(bh, xid_elems);
+
+	bh = binaryheap_allocate_shm("Undo Size Binary Heap",
+								 pending_undo_queue_size,
+								 undo_size_comparator,
+								 NULL);
+	size_elems = (UndoSizeQueue *) ShmemInitStruct("Undo Size Queue Elements",
+												   UndoSizeQueueElemsShmSize(),
+												   &foundSizeQueue);
+	Assert(foundSizeQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(size_elems, 0, sizeof(UndoSizeQueue));
+
+	InitSizeQueue(bh, size_elems);
+
+	bh = binaryheap_allocate_shm("Undo Error Binary Heap",
+								 pending_undo_queue_size,
+								 undo_err_time_comparator,
+								 NULL);
+
+	error_elems = (UndoErrorQueue *) ShmemInitStruct("Undo Error Queue Elements",
+													 UndoErrorQueueElemsShmSize(),
+													 &foundErrorQueue);
+	Assert(foundErrorQueue || !IsUnderPostmaster);
+
+	if (!IsUnderPostmaster)
+		memset(error_elems, 0, sizeof(UndoSizeQueue));
+
+	InitErrorQueue(bh, error_elems);
+}
+
+/*
+ * Returns true, if there is no pending undo apply work, false, otherwise.
+ */
+bool
+UndoWorkerQueuesEmpty(void)
+{
+	if (XidQueueIsEmpty() && SizeQueueIsEmpty())
+		return true;
+
+	return false;
+}
+
+/* Insert the request in both xid and size based queues. */
+void
+InsertRequestIntoUndoQueues(UndoRequestInfo * urinfo)
+{
+	/*
+	 * This must be called after acquring RollbackRequestLock as we will
+	 * insert into the binary heaps which can change.
+	 */
+	Assert(LWLockHeldByMeInMode(RollbackRequestLock, LW_EXCLUSIVE));
+	PushXidQueueElem(urinfo);
+	PushSizeQueueElem(urinfo);
+
+	elog(DEBUG1, "Undo action pushed Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+}
+
+/* Insert the request into an error queue. */
+bool
+InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo * urinfo)
+{
+	RollbackHashEntry *rh;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* We can't insert into an error queue if it is already full. */
+	if (GetErrorQueueSize() >= pending_undo_queue_size)
+	{
+		int			num_removed = 0;
+
+		/* Try to remove few elements */
+		num_removed = RemoveOldElemsFromErrorQueue();
+
+		if (num_removed == 0)
+		{
+			LWLockRelease(RollbackRequestLock);
+			return false;
+		}
+	}
+
+	/*
+	 * Mark the undo request in hash table as UNDO_REQUEST_INQUEUE so that undo
+	 * launcher or other undo worker can process this request.
+	 */
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, (void *) &urinfo->full_xid,
+										   HASH_FIND, NULL);
+	rh->status = UNDO_REQUEST_INQUEUE;
+
+	/* Insert the request into error queue for processing it later. */
+	PushErrorQueueElem(urinfo);
+	LWLockRelease(RollbackRequestLock);
+
+	elog(DEBUG1, "Undo action pushed(error) Xid: " UINT64_FORMAT ", Size: " UINT64_FORMAT ", "
+		 "Start: " UndoRecPtrFormat ", End: " UndoRecPtrFormat "",
+		 U64FromFullTransactionId(urinfo->full_xid), urinfo->request_size,
+		 urinfo->start_urec_ptr, urinfo->end_urec_ptr);
+
+	return true;
+}
+
+/*
+ * Set the undo worker queue from which the undo worker should start looking
+ * for work.
+ */
+void
+SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue)
+{
+	cur_undo_queue = undo_worker_queue;
+}
+
+/*
+ * Get the next set of pending rollback request for undo worker.
+ *
+ * allow_peek - if true, peeks a few element from each queue to check whether
+ * any request matches current dbid.
+ * remove_from_queue - if true, picks an element from the queue whose dbid
+ * matches current dbid and remove it from the queue before returning the same
+ * to caller.
+ * urinfo - this is an OUT parameter that returns the details of undo request
+ * whose undo action is still pending.
+ * in_other_db_out - this is an OUT parameter.  If we've not found any work
+ * for current database, but there is work for some other database, we set
+ * this parameter as true.
+ */
+bool
+UndoGetWork(bool allow_peek, bool remove_from_queue, UndoRequestInfo *urinfo,
+			bool *in_other_db_out)
+{
+	int			i;
+	bool		found_work = false;
+	bool		in_other_db = false;
+
+	/* Reset the undo request info */
+	ResetUndoRequestInfo(urinfo);
+
+	/* Search the queues under lock as they can be modified concurrently. */
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/* Here, we check each of the work queues in a round-robin way. */
+	for (i = 0; i < MAX_UNDO_WORK_QUEUES; i++)
+	{
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+		int			cur_queue = (int) (cur_undo_queue % MAX_UNDO_WORK_QUEUES);
+
+		if (!GetRollbackHashKeyFromQueue(cur_queue, 0, &hkey))
+		{
+			cur_undo_queue++;
+			continue;
+		}
+
+		rh = (RollbackHashEntry *) hash_search(RollbackHT,
+											   (void *) &hkey,
+											   HASH_FIND, NULL);
+
+		/*
+		 * If some undo worker is already processing the rollback request or
+		 * it is already processed, then we drop that request from the queue
+		 * and fetch the next entry from the queue.
+		 */
+		if (!rh || UndoRequestIsInProgress(rh))
+		{
+			RemoveRequestFromQueue(cur_queue, 0);
+			cur_undo_queue++;
+			continue;
+		}
+
+		/*
+		 * The request that is present in any queue must be a valid request
+		 * and its status must be in_queue.
+		 */
+		Assert(UndoRequestIsValid(rh));
+		Assert(UndoRequestIsInQueue(rh));
+
+		found_work = true;
+
+		/*
+		 * We've found a work for some database.  If we don't want to remove
+		 * the request, we return from here and spawn a worker process to
+		 * apply the same.
+		 */
+		if (!remove_from_queue)
+		{
+			bool		exists;
+
+			StartTransactionCommand();
+			exists = dbid_exists(rh->dbid);
+			CommitTransactionCommand();
+
+			/*
+			 * If the database doesn't exist, just remove the request since we
+			 * no longer need to apply the undo actions.
+			 */
+			if (!exists)
+			{
+				RemoveRequestFromQueue(cur_queue, 0);
+				RollbackHTRemoveEntry(rh->full_xid, rh->start_urec_ptr, true);
+				cur_undo_queue++;
+				continue;
+			}
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+
+		/*
+		 * The worker can perform this request if it is either not connected
+		 * to any database or the request belongs to the same database to
+		 * which it is connected.
+		 */
+		if ((MyDatabaseId == InvalidOid) ||
+			(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+		{
+			/* found a work for current database */
+			if (in_other_db_out)
+				*in_other_db_out = false;
+
+			/*
+			 * Mark the undo request in hash table as in_progress so that
+			 * other undo worker doesn't pick the same entry for rollback.
+			 */
+			rh->status = UNDO_REQUEST_INPROGRESS;
+
+			/* set the undo request info to process */
+			SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+			/*
+			 * Remove the request from queue so that other undo worker doesn't
+			 * process the same entry.
+			 */
+			RemoveRequestFromQueue(cur_queue, 0);
+
+			cur_undo_queue++;
+			LWLockRelease(RollbackRequestLock);
+			return true;
+		}
+		else
+			in_other_db = true;
+
+		cur_undo_queue++;
+	}
+
+	/*
+	 * Iff a worker would need to switch databases in less than
+	 * undo_worker_quantum ms after starting, it peeks a few entries deep into
+	 * each queue to see whether there's work for that database.  This ensures
+	 * that one worker doesn't have to restart quickly to switch databases.
+	 */
+	if (allow_peek)
+	{
+		int			depth,
+					cur_queue;
+		RollbackHashKey hkey;
+		RollbackHashEntry *rh;
+
+		/*
+		 * We shouldn't have come here if we've found a work above for our
+		 * database.
+		 */
+		Assert(!found_work || in_other_db);
+
+		for (depth = 0; depth < UNDO_PEEK_DEPTH; depth++)
+		{
+			for (cur_queue = 0; cur_queue < MAX_UNDO_WORK_QUEUES; cur_queue++)
+			{
+				if (!GetRollbackHashKeyFromQueue(cur_queue, depth, &hkey))
+					continue;
+
+				rh = (RollbackHashEntry *) hash_search(RollbackHT,
+													   (void *) &hkey,
+													   HASH_FIND, NULL);
+
+				/*
+				 * If some undo worker is already processing the rollback
+				 * request or it is already processed, then fetch the next
+				 * entry from the queue.
+				 */
+				if (!rh || UndoRequestIsInProgress(rh))
+					continue;
+
+				/*
+				 * The request that is present in any queue must be a valid request
+				 * and its status must be in_queue.
+				 */
+				Assert(UndoRequestIsValid(rh));
+				Assert(UndoRequestIsInQueue(rh));
+
+				found_work = true;
+
+				/*
+				 * The worker can perform this request if it is either not
+				 * connected to any database or the request belongs to the
+				 * same database to which it is connected.
+				 */
+				if ((MyDatabaseId == InvalidOid) ||
+					(MyDatabaseId != InvalidOid && MyDatabaseId == rh->dbid))
+				{
+					/* found a work for current database */
+					if (in_other_db_out)
+						*in_other_db_out = false;
+
+					/*
+					 * Mark the undo request in hash table as in_progress so
+					 * that other undo worker doesn't pick the same entry for
+					 * rollback.
+					 */
+					rh->status = UNDO_REQUEST_INPROGRESS;
+
+					/* set the undo request info to process */
+					SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue);
+
+					/*
+					 * Remove the request from queue so that other undo worker
+					 * doesn't process the same entry.
+					 */
+					RemoveRequestFromQueue(cur_queue, depth);
+					LWLockRelease(RollbackRequestLock);
+					return true;
+				}
+				else
+					in_other_db = true;
+			}
+		}
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	if (in_other_db_out)
+		*in_other_db_out = in_other_db;
+
+	return found_work;
+}
+
+/*
+ * This function registers the rollback requests.
+ *
+ * Returns true, if the request is registered and will be processed by undo
+ * worker at some later point of time, false, otherwise in which case caller
+ * can process the undo request by itself.
+ *
+ * The caller may execute undo actions itself if the request is not already
+ * present in rollback hash table and can't be pushed to pending undo request
+ * queues.  The two reasons why request can't be pushed are (a) the size of
+ * request is smaller than a threshold and the request is not from discard
+ * worker, (b) the undo request queues are full.
+ *
+ * It is not advisable to apply the undo actions of a very large transaction
+ * in the foreground as that can lead to a delay in retruning the control back
+ * to user after abort.
+ */
+bool
+RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid)
+{
+	bool		found = false;
+	bool		can_push;
+	bool		pushed = false;
+	RollbackHashEntry *rh;
+	uint64		req_size = 0;
+	UndoRecPtr	last_log_start_urec_ptr = InvalidUndoRecPtr;
+	RollbackHashKey hkey;
+
+	Assert(UndoRecPtrIsValid(start_urec_ptr));
+	Assert(dbid != InvalidOid);
+
+	/*
+	 * Find the rollback request size and the end_urec_ptr (in case of discard
+	 * worker only).
+	 */
+	req_size = FindUndoEndLocationAndSize(start_urec_ptr, &end_urec_ptr,
+										  &last_log_start_urec_ptr, full_xid);
+
+	/* Do not push any rollback request if working in single user-mode */
+	if (!IsUnderPostmaster)
+		return false;
+
+	/* The transaction got rolled back. */
+	if (!UndoRecPtrIsValid(end_urec_ptr))
+		return false;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	/*
+	 * Check whether we can push the rollback request to the undo worker. This
+	 * must be done under lock, see CanPushReqToUndoWorker.
+	 */
+	can_push = CanPushReqToUndoWorker(start_urec_ptr, end_urec_ptr, req_size);
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey,
+										   HASH_ENTER_NULL, &found);
+
+	/*
+	 * It can only fail, if the value of pending_undo_queue_size or
+	 * max_connections guc is reduced after restart of the server.
+	 */
+	if (rh == NULL)
+	{
+		Assert(RollbackHTIsFull());
+
+		ereport(PANIC,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("rollback hash table is full, try running with higher value of pending_undo_queue_size")));
+	}
+
+	/* We shouldn't try to add the same rollback request again. */
+	if (!found)
+	{
+		rh->start_urec_ptr = start_urec_ptr;
+		rh->end_urec_ptr = end_urec_ptr;
+		rh->last_log_start_urec_ptr = last_log_start_urec_ptr;
+		rh->dbid = dbid;
+		rh->full_xid = full_xid;
+
+		/* Increment the pending request counter. */
+		ProcGlobal->xactsHavingPendingUndo++;
+
+		if (can_push)
+		{
+			UndoRequestInfo urinfo;
+
+			ResetUndoRequestInfo(&urinfo);
+
+			urinfo.full_xid = rh->full_xid;
+			urinfo.start_urec_ptr = rh->start_urec_ptr;
+			urinfo.end_urec_ptr = rh->end_urec_ptr;
+			urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+			urinfo.dbid = rh->dbid;
+			urinfo.request_size = req_size;
+
+			InsertRequestIntoUndoQueues(&urinfo);
+
+			/*
+			 * Indicates that the request will be processed by undo
+			 * worker.
+			 */
+			rh->status = UNDO_REQUEST_INQUEUE;
+			pushed = true;
+		}
+		/*
+		 * The request can't be pushed into the undo worker queue.  The
+		 * backends will try executing by itself.
+		 */
+		else
+			rh->status = UNDO_REQUEST_INPROGRESS;
+	}
+	else if (!UndoRequestIsValid(rh) && can_push)
+	{
+		/*
+		 * If we found the request which is still not in queue or not in
+		 * progress then add it to the queue if there is a space in the queue.
+		 */
+		UndoRequestInfo urinfo;
+
+		ResetUndoRequestInfo(&urinfo);
+
+		urinfo.full_xid = rh->full_xid;
+		urinfo.start_urec_ptr = rh->start_urec_ptr;
+		urinfo.end_urec_ptr = rh->end_urec_ptr;
+		urinfo.last_log_start_urec_ptr = rh->last_log_start_urec_ptr;
+		urinfo.dbid = rh->dbid;
+		urinfo.request_size = req_size;
+
+		InsertRequestIntoUndoQueues(&urinfo);
+
+		/* Indicates that the request will be processed by the undo worker */
+		rh->status = UNDO_REQUEST_INQUEUE;
+		pushed = true;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return pushed;
+}
+
+/*
+ * Remove the rollback request entry from the rollback hash table.
+ */
+void
+RollbackHTRemoveEntry(FullTransactionId full_xid, UndoRecPtr start_urec_ptr,
+					  bool lock)
+{
+	RollbackHashKey hkey;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	if (!lock)
+		LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	hash_search(RollbackHT, &hkey, HASH_REMOVE, NULL);
+
+	/* Decrement the pending request counter. */
+	ProcGlobal->xactsHavingPendingUndo--;
+
+	if (!lock)
+		LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Mark the entry status as invalid in the rollback hash table.
+ */
+void
+RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+						   UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	rh->status = UNDO_REQUEST_INVALID;
+
+	LWLockRelease(RollbackRequestLock);
+}
+
+/*
+ * Returns the start undo record pointer for the last undo log in which
+ * transaction has spanned.  This will be different from start_urec_ptr only
+ * when the undo for a transaction has spanned across multiple undo logs.
+ */
+UndoRecPtr
+RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+							 UndoRecPtr start_urec_ptr)
+{
+	RollbackHashKey hkey;
+	RollbackHashEntry *rh;
+	UndoRecPtr	last_log_start_urecptr;
+
+	hkey.full_xid = full_xid;
+	hkey.start_urec_ptr = start_urec_ptr;
+
+	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
+
+	rh = (RollbackHashEntry *) hash_search(RollbackHT, &hkey, HASH_FIND, NULL);
+	Assert(rh != NULL);
+	last_log_start_urecptr = rh->last_log_start_urec_ptr;
+	LWLockRelease(RollbackRequestLock);
+
+	return last_log_start_urecptr;
+}
+
+/*
+ * Returns true, if the rollback hash table is full, false, otherwise.
+ */
+bool
+RollbackHTIsFull(void)
+{
+	bool		result = false;
+
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	if (hash_get_num_entries(RollbackHT) >= UndoRollbackHashTableSize())
+		result = true;
+
+	LWLockRelease(RollbackRequestLock);
+
+	return result;
+}
+
+/*
+ * Get the smallest of 'xid having pending undo' and 'oldestXmin'.
+ */
+FullTransactionId
+RollbackHTGetOldestFullXid(FullTransactionId oldestXmin)
+{
+	RollbackHashEntry   *rh;
+	FullTransactionId	oldestXid = oldestXmin;
+	HASH_SEQ_STATUS		status;
+
+	/* Fetch the pending undo requests */
+	LWLockAcquire(RollbackRequestLock, LW_SHARED);
+
+	Assert(hash_get_num_entries(RollbackHT) <= UndoRollbackHashTableSize());
+	hash_seq_init(&status, RollbackHT);
+	while (RollbackHT != NULL &&
+		   (rh = (RollbackHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		if (!FullTransactionIdIsValid(oldestXid) ||
+			FullTransactionIdPrecedes(rh->full_xid, oldestXid))
+			oldestXid = rh->full_xid;
+	}
+
+	LWLockRelease(RollbackRequestLock);
+
+	return oldestXid;
+}
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1c..19e4f1f 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,4 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+RollbackRequestLock					46
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 498373f..884fa2a 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -296,6 +296,8 @@ InitProcGlobal(void)
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
+
+	ProcGlobal->xactsHavingPendingUndo = 0;
 }
 
 /*
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 5d9af89..ede9c51 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1086,6 +1086,20 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 }
 
 /*
+ * Check whether the dbid exist or not.
+ */
+bool
+dbid_exists(Oid dboid)
+{
+	bool		result = false;
+
+	Assert(IsTransactionState());
+	result = (GetDatabaseTupleByOid(dboid) != NULL);
+
+	return result;
+}
+
+/*
  * Process any command-line switches and any additional GUC variable
  * settings passed in the startup packet.
  */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 296fb77..a7d1db5 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -3041,6 +3042,27 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"rollback_overflow_size", PGC_USERSET, RESOURCES_MEM,
+			gettext_noop("Rollbacks greater than this size are done lazily"),
+			NULL,
+			GUC_UNIT_MB
+		},
+		&rollback_overflow_size,
+		64, 0, MAX_KILOBYTES,
+		NULL, NULL, NULL
+	},
+
+	{
+		{"pending_undo_queue_size", PGC_POSTMASTER, RESOURCES_MEM,
+			gettext_noop("Sets the size of queue used to register undo requests"),
+			NULL,
+		},
+		&pending_undo_queue_size,
+		1024, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"old_snapshot_threshold", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
 			gettext_noop("Time before a snapshot is too old to read pages changed after the snapshot was taken."),
 			gettext_noop("A value of -1 disables this feature."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index cfad86c..592f6e1 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -604,6 +604,14 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# UNDO options
+#------------------------------------------------------------------------------
+
+#rollback_overflow_size = 64	# default size above which the undo
+					# requests are pushed to undo workers
+#pending_undo_queue_size = 1024	# size of queue used to register undo
+					# requests
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index cc00509..01f248a 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,7 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdFollows(a, b)	((a).value > (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index f7cfa9f..24ea97b 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -117,5 +117,8 @@ extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
 								 XLogRecPtr recptr);
 extern UndoRecPtr UndoGetPrevUndoRecptr(UndoRecPtr urp, Buffer buffer,
 										UndoLogCategory category);
+extern UndoRecPtr UndoBlockGetFirstUndoRecord(BlockNumber blkno,
+											  UndoRecPtr urec_ptr,
+											  UndoLogCategory category);
 
 #endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000..64d89ba
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *	  Exports from undo/undorequest.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOREQUEST_H
+#define _UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "datatype/timestamp.h"
+#include "utils/relcache.h"
+
+
+/* different types of undo worker */
+typedef enum
+{
+	XID_QUEUE = 0,
+	SIZE_QUEUE = 1,
+	ERROR_QUEUE
+} UndoWorkerQueueType;
+
+#define InvalidUndoWorkerQueue -1
+
+extern PGDLLIMPORT int	rollback_overflow_size;
+extern PGDLLIMPORT int	pending_undo_queue_size;
+
+/*
+ * Current status of the undo request in the hash table.
+ */
+typedef enum
+{
+	/*
+	 * Request is present in the rollback hash table, but not present in any
+	 * of the queues.  In this state, the undo actions can't be executed.
+	 *
+	 * The request will be marked with this status if a) discard worker finds
+	 * that there is no space in the undo worker queue for inserting the undo
+	 * request, b) there is an error while backend or undo worker is
+	 * executing undo actions and there is no space in the error queue.
+	 *
+	 * Later when the discard worker finds such entry and if there is a
+	 * sufficient space in the undo worker queues, then the request will be
+	 * added to them and the status will be changed to UNDO_REQUEST_INQUEUE.
+	 *
+	 * It is important to keep the request in hash table with this status
+	 * intsead of removing it to compute the value of
+	 * oldestXidHavingUnappliedUndo.  If we don't do that, then the
+	 * corresponding xid won't be considered for computation of
+	 * oldestXidHavingUnappliedUndo.
+	 */
+	UNDO_REQUEST_INVALID,
+
+	/*
+	 * When backend or discard worker push the request to undo worker queue the
+	 * status will be set to this.  Undo workers pulls such requests from the
+	 * queues, change the state as UNDO_REQUEST_INPROGRESS and process the undo
+	 * actions.
+	 */
+	UNDO_REQUEST_INQUEUE,
+
+	/*
+	 * Undo action execution is in progress either by backend or by undo worker.
+	 */
+	UNDO_REQUEST_INPROGRESS
+} UndoRequestStatus;
+
+/*
+ * UndoRequestIsValid
+ *		True iff undo request status is not invalid.
+ */
+#define UndoRequestIsValid(rh) \
+	((bool) ((rh->status) != UNDO_REQUEST_INVALID))
+
+ /*
+  * UndoRequestIsInProgress
+  *		True iff undo request status is in progress.
+  */
+#define UndoRequestIsInProgress(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INPROGRESS))
+
+/*
+ * UndoRequestIsInQueue
+ *		True iff undo request status is in queue.
+ */
+#define UndoRequestIsInQueue(rh) \
+	((bool) ((rh->status) == UNDO_REQUEST_INQUEUE))
+
+/* This is the data structure for each hash table entry for rollbacks. */
+typedef struct RollbackHashEntry
+{
+	FullTransactionId full_xid; /* must be first entry */
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	UndoRequestStatus	status;	/* current state of the entry. */
+} RollbackHashEntry;
+
+/*
+ * This is the data structure for each hash table key for rollbacks.  We need
+ * to keep start_urec_ptr as a key element because in the same transaction,
+ * there could be rollback requests for both logged and unlogged relations.
+ */
+typedef struct RollbackHashKey
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+} RollbackHashKey;
+
+/* This is an entry for undo request queue that is sorted by xid. */
+typedef struct UndoXidQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+} UndoXidQueue;
+
+/* This is an entry for undo request queue that is sorted by size. */
+typedef struct UndoSizeQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+} UndoSizeQueue;
+
+/*
+ * This is an entry for undo request queue that is sorted by time at which an
+ * error has occurred.
+ */
+typedef struct UndoErrorQueue
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	Oid			dbid;
+	TimestampTz next_retry_at;
+	TimestampTz err_occurred_at;
+} UndoErrorQueue;
+
+/* undo request information */
+typedef struct UndoRequestInfo
+{
+	FullTransactionId full_xid;
+	UndoRecPtr	start_urec_ptr;
+	UndoRecPtr	end_urec_ptr;
+	UndoRecPtr	last_log_start_urec_ptr;
+	Oid			dbid;
+	uint64		request_size;
+	UndoWorkerQueueType undo_worker_queue;
+} UndoRequestInfo;
+
+/* Reset the undo request info */
+#define ResetUndoRequestInfo(urinfo) \
+( \
+	(urinfo)->full_xid = InvalidFullTransactionId, \
+	(urinfo)->start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->end_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->last_log_start_urec_ptr = InvalidUndoRecPtr, \
+	(urinfo)->dbid = InvalidOid, \
+	(urinfo)->request_size = 0, \
+	(urinfo)->undo_worker_queue = InvalidUndoWorkerQueue \
+)
+
+/* set the undo request info from the rollback request */
+#define SetUndoRequestInfoFromRHEntry(urinfo, rh, cur_queue) \
+( \
+	urinfo->full_xid = rh->full_xid, \
+	urinfo->start_urec_ptr = rh->start_urec_ptr, \
+	urinfo->end_urec_ptr = rh->end_urec_ptr, \
+	urinfo->last_log_start_urec_ptr = rh->last_log_start_urec_ptr, \
+	urinfo->dbid = rh->dbid, \
+	urinfo->undo_worker_queue = cur_queue \
+)
+
+/*
+ * From an undo log if all the undo actions have been applied for a particular
+ * transaction, we set the uur_progress of the transaction's log in that undo
+ * log as MaxBlockNumber.  If none of the undo actions have yet been applied,
+ * we set it to InvalidBlockNumber.
+ */
+#define XACT_APPLY_PROGRESS_COMPLETED MaxBlockNumber
+#define XACT_APPLY_PROGRESS_NOT_STARTED InvalidBlockNumber
+
+#define IsXactApplyProgressCompleted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_COMPLETED)
+
+#define IsXactApplyProgressNotStarted(uur_progress) \
+	(uur_progress == XACT_APPLY_PROGRESS_NOT_STARTED)
+
+/* Exposed functions for rollback request queues. */
+extern int	PendingUndoShmemSize(void);
+extern void PendingUndoShmemInit(void);
+extern bool UndoWorkerQueuesEmpty(void);
+extern void InsertRequestIntoUndoQueues(UndoRequestInfo *urinfo);
+extern bool InsertRequestIntoErrorUndoQueue(volatile UndoRequestInfo *urinfo);
+extern void SetUndoWorkerQueueStart(UndoWorkerQueueType undo_worker_queue);
+extern bool UndoGetWork(bool allow_peek, bool is_undo_launcher,
+			UndoRequestInfo *urinfo, bool *in_other_db);
+
+/* Exposed functions for rollback hash table. */
+extern uint64 FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
+							UndoRecPtr *end_urecptr_out,
+							UndoRecPtr *last_log_start_urecptr_out,
+							FullTransactionId full_xid);
+extern bool RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
+					Oid dbid, FullTransactionId full_xid);
+extern void RollbackHTRemoveEntry(FullTransactionId full_xid,
+					UndoRecPtr start_urec_ptr, bool lock);
+extern bool RollbackHTIsFull(void);
+extern int UndoRollbackHashTableSize(void);
+extern void RollbackHTMarkEntryInvalid(FullTransactionId full_xid,
+								UndoRecPtr start_urec_ptr);
+extern UndoRecPtr RollbackHTGetLastLogStartUrp(FullTransactionId full_xid,
+											   UndoRecPtr start_urec_ptr);
+extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin);
+
+/* functions exposed from undoaction.c */
+extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool nopartial);
+extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
+						  int last_idx, Oid reloid, FullTransactionId full_xid,
+						  BlockNumber blkno, bool blk_chain_complete);
+
+#endif							/* _UNDOREQUEST_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 61a24c2..1afc4d3 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -426,6 +426,7 @@ extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 						 Oid useroid, char *out_dbname, bool override_allow_connections);
+extern bool dbid_exists(Oid dboid);
 extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index ac7ee72..824f6bf 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,6 +272,8 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Number of aborted transactions with pending undo actions. */
+	int			xactsHavingPendingUndo;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
-- 
1.8.3.1

0011-Infrastructure-to-execute-pending-undo-actions.patchapplication/octet-stream; name=0011-Infrastructure-to-execute-pending-undo-actions.patchDownload
From 3ea90121dd8f7df14b08ffd9a1fde2f944615495 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:27:58 +0530
Subject: [PATCH 11/13] Infrastructure to execute pending undo actions.

To apply the undo actions, we collect the undo records in bulk and try to
process them together.  We ensure to update the transaction's progress at
regular intervals so that after a crash we can skip already applied undo.

This provides a way for users to register a callback for processing the
undo records based on resource manager.

Dilip Kumar, Amit Kapila, Thomas Munro and Kuntal Ghosh with inputs from
Robert Haas
---
 src/backend/access/rmgrdesc/Makefile         |   3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c |  47 +++
 src/backend/access/transam/rmgr.c            |   5 +-
 src/backend/access/undo/Makefile             |   3 +-
 src/backend/access/undo/undoaccess.c         |  43 ++-
 src/backend/access/undo/undoaction.c         | 524 +++++++++++++++++++++++++++
 src/backend/access/undo/undoactionxlog.c     |  60 +++
 src/backend/replication/logical/decode.c     |   1 +
 src/bin/pg_rewind/parsexlog.c                |   2 +-
 src/bin/pg_waldump/rmgrdesc.c                |   3 +-
 src/include/access/rmgr.h                    |   2 +-
 src/include/access/rmgrlist.h                |  52 +--
 src/include/access/undoaccess.h              |   4 +
 src/include/access/undoaction_xlog.h         |  39 ++
 src/include/access/undorecord.h              |   2 +
 src/include/access/undorequest.h             |   3 -
 src/include/access/xlog_internal.h           |  18 +-
 17 files changed, 773 insertions(+), 38 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/include/access/undoaction_xlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef..640d37f 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 0000000..c396582
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b05374..c57eca2 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_status, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 7327502..68696bc 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
+OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
+	   undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 4ffec58..c215ba1 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -84,8 +84,6 @@ static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 											Buffer *prevbuf);
 static int	UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
 									   UndoRecPtr xact_urp, int size, int offset);
-static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
-									  int idx);
 static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 										UndoRecPtr urecptr, UndoRecPtr xact_urp);
 static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
@@ -285,6 +283,45 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
 }
 
 /*
+ * Prepare to update the undo apply progress in the transaction header.
+ */
+void
+UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+							   UndoRecPtr xact_urp, BlockNumber progress)
+{
+	int			index = 0;
+	int			offset;
+
+	Assert(UndoRecPtrIsValid(xact_urp));
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't
+	 * need to do anything.
+	 */
+	if (UndoRecPtrGetCategory(xact_urp) == UNDO_TEMP)
+		return;
+
+	/*
+	 * Here, we are preparing to update the undo apply progress of a
+	 * transaction being rolled back.  The undo must not be discarded
+	 * till the transaction is completely rolled back.
+	 */
+	Assert(!UndoRecPtrIsDiscarded(xact_urp));
+
+	/* Compute the offset of the urec_progress in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+					offsetof(UndoRecordTransaction, urec_progress);
+
+	index = UndoRecordPrepareTransInfo(context, xact_urp,
+									   sizeof(UndoRecPtr), offset);
+	/*
+	 * Set the undo action progress in xact_urec_info, this will be overwritten
+	 * in actual undo record during update phase.
+	 */
+	context->xact_urec_info[index].progress = progress;
+}
+
+/*
  * Overwrite the first undo record of the previous transaction to update its
  * next pointer.
  *
@@ -292,7 +329,7 @@ UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
  * This must be called under the critical section.  This will just overwrite the
  * header of the undo record.
  */
-static void
+void
 UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 0000000..2c6beae
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,524 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to
+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+static void UpdateUndoApplyProgress(UndoRecPtr last_log_start_urec_ptr,
+						  BlockNumber block_num);
+static bool UndoAlreadyApplied(FullTransactionId full_xid,
+						UndoRecPtr to_urecptr);
+static void ApplyUndo(UndoRecInfo *urecinfo, int nrecords);
+static void ProcessAndApplyUndo(FullTransactionId full_xid,
+				UndoRecPtr from_urecptr, UndoRecPtr to_urecptr,
+				UndoRecPtr last_log_start_urec_ptr, bool complete_xact);
+
+/*
+ * undo_record_comparator - qsort comparator for undo records.
+ *
+ * This is used to sort undo records for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else if (luur->uur_block > ruur->uur_block)
+		return 1;
+	else if (luur->uur_offset < ruur->uur_offset)
+		return -1;
+	else if (luur->uur_offset > ruur->uur_offset)
+		return 1;
+	else if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+	{
+		/*
+		 * If records are for the same block and offset, then maintain their
+		 * existing order by comparing their index in the array.
+		 */
+		return -1;
+	}
+	else
+		return 1;
+}
+
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	/*
+	 * We don't need to update the progress for temp tables as they get
+	 * discraded after startup.
+	 */
+	if (category == UNDO_TEMP)
+		return;
+
+	BeginUndoRecordInsert(&context, category, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	UndoRecordPrepareApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,
+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecordFetchContext	context;
+
+	/* Fetch the undo record. */
+	BeginUndoFetch(&context);
+	uur = UndoFetchRecord(&context, to_urecptr);
+	FinishUndoFetch(&context);
+
+	/* already processed and discarded */
+	if (uur == NULL)
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		return true;
+	}
+
+	/* already processed */
+	if (IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		UndoRecordRelease(uur);
+		return true;
+	}
+
+	Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+	UndoRecordRelease(uur);
+
+	return false;
+}
+
+/*
+ * ApplyUndo - Invode rmgr specific undo apply functions.
+ *
+ * urecinfo - An array of undo records sorted in the rmgr order.
+ * nrecords - number of records in this array.
+ */
+static void
+ApplyUndo(UndoRecInfo *urecinfo, int nrecords)
+{
+	int			rmgr_start_idx = 0;
+	int			rmgr_nrecords = 0;
+	int			prev_rmid = -1;
+	int			i;
+
+	/* Apply the undo action for each rmgr. */
+	for (i = 0; i < nrecords; i++)
+	{
+		UnpackedUndoRecord *uur = urecinfo[i].uur;
+
+		Assert(uur->uur_rmid >= 0);
+
+		/*
+		 * If this undo is not for the same rmgr then apply all undo
+		 * actions for the previous rmgr.
+		 */
+		if (prev_rmid >= 0 &&
+			prev_rmid != uur->uur_rmid)
+		{
+			Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+			RmgrTable[prev_rmid].rm_undo(rmgr_nrecords,
+										 &urecinfo[rmgr_start_idx]);
+
+			rmgr_start_idx = i;
+			rmgr_nrecords = 0;
+		}
+
+		rmgr_nrecords++;
+		prev_rmid = uur->uur_rmid;
+	}
+
+	/* Apply the last set of the actions. */
+	Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+	RmgrTable[prev_rmid].rm_undo(rmgr_nrecords, &urecinfo[rmgr_start_idx]);
+}
+
+/*
+ * ProcessAndApplyUndo - Fetch undo records and apply actions.
+ *
+ * We always process the undo of the last log when the undo for a transaction
+ * spans across multiple logs.  Then from there onwards the previous undo logs
+ * for the same transaction are processed.
+ *
+ * We also update the undo apply progress in the transaction header so that
+ * after recovery we don't need to process the records that are already
+ * processed.  As we update the progress only after one batch of records,
+ * the crash in-between can cause us to read/apply part of undo records
+ * again but this will never be more than one-batch.  We can further optimize
+ * it by marking the progress in each record, but that has its own downsides
+ * like it will generate more WAL and I/O corresponding to dirty undo buffers.
+ */
+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	while (true)
+	{
+		BlockNumber	progress_block_num = InvalidBlockNumber;
+		int			i;
+		int			nrecords;
+		bool		log_switched = false;
+		bool		rollback_completed = false;
+		bool		update_progress = false;
+		UndoRecPtr	progress_urec_ptr = InvalidUndoRecPtr;
+		UndoRecInfo	*first_urecinfo;
+		UndoRecInfo	*last_urecinfo;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update
+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords, false);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		first_urecinfo = &urecinfo[0];
+		last_urecinfo = &urecinfo[nrecords - 1];
+		if (IsUndoLogSwitched(last_urecinfo->uur))
+		{
+			UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;
+
+			/*
+			 * We have crossed the log boundary.  The rest of the undo for
+			 * this transaction is in some other log, the location of which
+			 * can be found from this record.  See commets atop undoaccess.c.
+			 */
+			log_switched = true;
+
+			/*
+			 * We need to save the undo record pointer of the last record from
+			 * previous undo log.  We will use the same as from location in
+			 * next iteration of bulk fetch.
+			 */
+			Assert(UndoRecPtrIsValid(logswitch->urec_prevurp));
+			urec_ptr = logswitch->urec_prevurp;
+
+			/*
+			 * The last fetched undo record corresponds to the first undo
+			 * record of the current log.  Once, the undo actions are performed
+			 * from this log, we've to mark the progress as completed.
+			 */
+			progress_urec_ptr = last_urecinfo->urp;
+
+			/*
+			 * We also need to save the start location of this transaction in
+			 * previous log.  This will be used in the next iteration of bulk
+			 * fetch and updating progress location.
+			 */
+			if (complete_xact)
+			{
+				Assert(UndoRecPtrIsValid(logswitch->urec_prevlogstart));
+				last_log_start_urec_ptr = logswitch->urec_prevlogstart;
+			}
+
+			/* We've to update the progress for the current log as completed. */
+			update_progress = true;
+		}
+		else if (complete_xact)
+		{
+			if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(last_urecinfo->urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(first_urecinfo->urp) > progress_block_num)
+				{
+					update_progress = true;
+					progress_urec_ptr = last_log_start_urec_ptr;
+				}
+			}
+			else
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				rollback_completed = true;
+				update_progress = true;
+				progress_urec_ptr = last_log_start_urec_ptr;
+			}
+		}
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(FullTransactionIdEquals(full_xid, urecinfo[0].uur->uur_fxid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urecinfo, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		/* Call resource manager specific callbacks to apply actions. */
+		ApplyUndo(urecinfo, nrecords);
+
+		/* Set undo action apply progress if required. */
+		if (update_progress)
+		{
+			Assert(UndoRecPtrIsValid(progress_urec_ptr));
+
+			if (log_switched || rollback_completed)
+			{
+				/*
+				 * We have crossed the log boundary or executed all the undo
+				 * actions for the main transaction.  So, mark current log
+				 * header as complete and set the next progress location in
+				 * the previous log.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else
+			{
+				/*
+				 * Update the progress block number.  We increase the block
+				 * number by one since the current block might have some undo
+				 * records that are yet to be applied.  But, all undo records
+				 * from the next block must have been applied.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										progress_block_num + 1);
+			}
+		}
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+	}
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{
+	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(UndoRecPtrIsValid(from_urecptr));
+	Assert(UndoRecPtrIsValid(to_urecptr));
+
+	/*
+	 * Here we compute the last log start urp which is used for fetching the
+	 * undo records and updating the undo action progress.
+	 *
+	 * For rollbacks of subtransaction, we won't be able to calculate the last
+	 * log start urp since we don't have the start urp of the top xid and hence
+	 * we won't be able to follow the transaction chains to find the last log.
+	 */
+	if (complete_xact)
+	{
+		if (UndoRecPtrGetCategory(to_urecptr) == UNDO_TEMP)
+		{
+			UndoRecPtr end_urec_ptr = from_urecptr;
+
+			/*
+			 * For temporary tables, we don't push the rollback request in the
+			 * rollback hash table so we can't directly get the last log start
+			 * urp from there.  Instead, we need to compute it now.
+			 */
+			(void) FindUndoEndLocationAndSize(to_urecptr, &end_urec_ptr,
+											  &last_log_start_urec_ptr,
+											  full_xid);
+		}
+		else
+		{
+			/*
+			 * It is important here to fetch the latest undo record and validate if
+			 * the actions are already executed.  The reason is that it is possible
+			 * that discard worker or backend might try to execute the rollback
+			 * request which is already executed.  For ex., after discard worker
+			 * fetches the record and found that this transaction need to be
+			 * rolledback, backend might concurrently execute the actions and
+			 * remove the request from rollback hash table.
+			 *
+			 * The other case where this will be required is when the transactions
+			 * records span across multiple logs.  Say, we register the
+			 * transaction from the first log and then we encounter the same
+			 * transaction in the second log where its status is still not marked
+			 * as done.  Now, before we try to register the request for the second
+			 * log, the undo worker came along rolled back the previous request
+			 * and removed its hash entry.  In this case, we will successfully
+			 * register the request from the second log and it should be detected
+			 * here.
+			 */
+			if (UndoAlreadyApplied(full_xid, to_urecptr))
+				return;
+
+			last_log_start_urec_ptr =
+				RollbackHTGetLastLogStartUrp(full_xid, to_urecptr);
+		}
+	}
+
+	ProcessAndApplyUndo(full_xid, from_urecptr, to_urecptr,
+						last_log_start_urec_ptr, complete_xact);
+
+	/*
+	 * Undo actions are applied so delete the hash table entry.
+	 */
+	RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 0000000..8d4ff7f
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, category, 1, record);
+
+	/* Update the undo apply progress in the transaction header. */
+	UndoRecordPrepareApplyProgress(&context, xlrec->urec_ptr,
+								   xlrec->progress);
+
+	UndoRecordUpdateTransInfo(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index d3a9c4d..272edcb 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 287af60..b26c45e 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150d..976f80e 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56..0a3794a 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e..b424c3c 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -24,27 +24,31 @@
  * Changes to this list possibly need an XLOG_PAGE_MAGIC bump.
  */
 
-/* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+/*
+ * symbol name, textual name, redo, desc, identify, startup, cleanup, mask,
+ * undo, undo_status, undo_desc
+ */
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 24ea97b..7c31332 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -13,6 +13,7 @@
 #ifndef UNDOACCESS_H
 #define UNDOACCESS_H
 
+#include "access/transam.h"
 #include "access/undolog.h"
 #include "access/undorecord.h"
 #include "access/xlogdefs.h"
@@ -94,6 +95,9 @@ typedef struct UndoRecordFetchContext
 	UndoRecPtr	urp;			/* Previous undo record pointer. */
 } UndoRecordFetchContext;
 
+extern void UndoRecordPrepareApplyProgress(UndoRecordInsertContext *context,
+					UndoRecPtr urecptr, BlockNumber progress);
+extern void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context, int idx);
 extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
 								  UndoLogCategory category,
 								  int nprepared,
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 0000000..b9e65d1
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 6a2c5cc..9232cf5 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -260,6 +260,8 @@ typedef struct UnpackedUndoRecord
 										 * during a transaction. */
 } UnpackedUndoRecord;
 
+#define IsUndoLogSwitched(uur) (uur->uur_logswitch != NULL)
+
 extern Size UndoRecordHeaderSize(uint16 uur_info);
 extern Size UndoRecordExpectedSize(UnpackedUndoRecord *uur);
 extern Size UnpackedUndoRecordSize(UnpackedUndoRecord *uur);
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
index 64d89ba..82640b8 100644
--- a/src/include/access/undorequest.h
+++ b/src/include/access/undorequest.h
@@ -224,8 +224,5 @@ extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin
 /* functions exposed from undoaction.c */
 extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool nopartial);
-extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
-						  int last_idx, Oid reloid, FullTransactionId full_xid,
-						  BlockNumber blkno, bool blk_chain_complete);
 
 #endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b986a385..35aff69 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -19,10 +19,14 @@
 #ifndef XLOG_INTERNAL_H
 #define XLOG_INTERNAL_H
 
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undorecord.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -271,6 +275,15 @@ typedef enum
 }			RecoveryTargetAction;
 
 /*
+ * Return values for undo status callback functions.
+ */
+typedef enum UndoStatus
+{
+	UNDO_STATUS_WAIT_XMIN,		/* wait until the xmin passes an xid */
+	UNDO_STATUS_DISCARD			/* the record set should be discarded */
+} UndoStatus;
+
+/*
  * Method table for resource managers.
  *
  * This struct must be kept in sync with the PG_RMGR definition in
@@ -295,9 +308,12 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	void		(*rm_undo) (int nrecords, UndoRecInfo *records);
+	UndoStatus	(*rm_undo_status) (UnpackedUndoRecord *record, TransactionId *xid);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
-- 
1.8.3.1

0012-Allow-foreground-transactions-to-perform-undo-action.patchapplication/octet-stream; name=0012-Allow-foreground-transactions-to-perform-undo-action.patchDownload
From 6973e1d5edba3025aad7d4023bc19d89951b6e86 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:42:46 +0530
Subject: [PATCH 12/13] Allow foreground transactions to perform undo actions
 on abort.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

Amit Kapila and Dilip Kumar  with inputs from Robert Haas
---
 src/backend/access/transam/twophase.c         |  82 ++++-
 src/backend/access/transam/varsup.c           |  24 ++
 src/backend/access/transam/xact.c             | 497 +++++++++++++++++++++++++-
 src/backend/access/undo/README.UndoProcessing |  39 ++
 src/backend/access/undo/undoaccess.c          |   7 +
 src/backend/access/undo/undoaction.c          |  23 +-
 src/backend/storage/ipc/ipc.c                 |   7 +
 src/backend/tcop/postgres.c                   |   8 +-
 src/backend/utils/error/elog.c                |  23 +-
 src/backend/utils/init/globals.c              |   1 +
 src/backend/utils/resowner/resowner.c         |  11 +-
 src/include/access/transam.h                  |   1 +
 src/include/access/twophase.h                 |   3 +-
 src/include/access/xact.h                     |  11 +
 src/include/miscadmin.h                       |  15 +
 src/include/utils/elog.h                      |   2 +
 16 files changed, 727 insertions(+), 27 deletions(-)
 create mode 100644 src/backend/access/undo/README.UndoProcessing

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 477709b..4d34d11 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -909,7 +910,7 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
 /*
  * Header for a 2PC state file
  */
-#define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
+#define TWOPHASE_MAGIC	0x57F94535	/* format identifier */
 
 typedef struct TwoPhaseFileHeader
 {
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need its start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoLogCategories];
+	UndoRecPtr	end_urec_ptr[UndoLogCategories];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1012,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1044,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1485,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	startUrecPtr[UndoLogCategories];
+	UndoRecPtr	endUrecPtr[UndoLogCategories];
+	bool		uRequestRegistered[UndoLogCategories];
+	uint32		epoch;
+	int			i;
+	FullTransactionId fXid;
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1528,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(startUrecPtr, hdr->start_urec_ptr, sizeof(startUrecPtr));
+	memcpy(endUrecPtr, hdr->end_urec_ptr, sizeof(endUrecPtr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1518,6 +1545,13 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * TransactionIdIsInProgress will stop saying the prepared xact is in
 	 * progress), then run the post-commit or post-abort callbacks. The
 	 * callbacks will release the locks the transaction held.
+	 *
+	 * XXX Note that, unlike non-prepared transactions, we don't skip
+	 * releasing the locks when we have to perform the undo actions.  The
+	 * reason is that here the locks are not directly associated with current
+	 * transaction, rather it has to acquire those locks to apply undo actions.
+	 * So, if we don't release the locks for prepared transaction, the undo
+	 * applying transaction will wait forever.
 	 */
 	if (isCommit)
 		RecordTransactionCommitPrepared(xid,
@@ -1526,10 +1560,36 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 										hdr->ninvalmsgs, invalmsgs,
 										hdr->initfileinval, gid);
 	else
+	{
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id in
+		 * TwoPhaseFileHeader instead.)
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		fXid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Register the rollback request to apply undo actions.  It is
+		 * important to do this before marking it aborted in clog, see
+		 * comments atop CheckAndRegisterUndoRequest for further details.
+		 */
+		for (i = 0; i < UndoLogCategories; i++)
+		{
+			if (endUrecPtr[i] != InvalidUndoRecPtr && i != UNDO_TEMP)
+			{
+				uRequestRegistered[i] = RegisterUndoRequest(endUrecPtr[i],
+															startUrecPtr[i],
+															hdr->database,
+															fXid);
+			}
+		}
+
 		RecordTransactionAbortPrepared(xid,
 									   hdr->nsubxacts, children,
 									   hdr->nabortrels, abortrels,
 									   gid);
+	}
 
 	ProcArrayRemove(proc, latestXid);
 
@@ -1614,6 +1674,24 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	RESUME_INTERRUPTS();
 
+	if (!isCommit)
+	{
+		/*
+		 * We need to perform undo actions while we are still in a transaction.
+		 */
+		if (!ProcessUndoRequestForEachLogCat(fXid, hdr->database,
+											 endUrecPtr, startUrecPtr,
+											 uRequestRegistered, false))
+		{
+			/* Abort the failed transaction. */
+			AbortOutOfAnyTransaction();
+			FlushErrorState();
+
+			/* Restart our transaction. */
+			StartTransactionCommand();
+		}
+	}
+
 	pfree(buf);
 }
 
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..fd01989 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -566,3 +566,27 @@ GetNewObjectId(void)
 
 	return result;
 }
+
+/*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d7930c0..fbbe585 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,16 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each log category */
+	UndoRecPtr	startUrecPtr[UndoLogCategories]; /* this is 'to' location */
+	UndoRecPtr	latestUrecPtr[UndoLogCategories]; /* this is 'from'
+												   * location */
+	/*
+	 * whether the undo request is registered to be processed by worker later?
+	 */
+	bool		undoRequestResgistered[UndoLogCategories];
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -339,6 +353,7 @@ static void ShowTransactionState(const char *str);
 static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
+static void CheckAndRegisterUndoRequest(void);
 
 
 /* ----------------------------------------------------------------
@@ -362,9 +377,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +738,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +925,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -1968,6 +1988,14 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2264,6 +2292,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2433,7 +2463,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, s->startUrecPtr, s->latestUrecPtr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2566,6 +2596,7 @@ AbortTransaction(void)
 	TransactionState s = CurrentTransactionState;
 	TransactionId latestXid;
 	bool		is_parallel_worker;
+	bool		undo_apply_in_progress;
 
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
@@ -2622,7 +2653,10 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	undo_apply_in_progress = (s->blockState == TBLOCK_UNDO);
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2666,11 +2700,12 @@ AbortTransaction(void)
 	 * Advertise the fact that we aborted in pg_xact (assuming that we got as
 	 * far as assigning an XID to advertise).  But if we're inside a parallel
 	 * worker, skip this; the user backend must be the one to write the abort
-	 * record.
+	 * record.  Also, skip this if undo apply is in progress as in that case
+	 * we would have already written abort record.
 	 */
-	if (!is_parallel_worker)
+	if (!is_parallel_worker && !undo_apply_in_progress)
 		latestXid = RecordTransactionAbort(false);
-	else
+	else if (is_parallel_worker)
 	{
 		latestXid = InvalidTransactionId;
 
@@ -2780,6 +2815,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2882,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2945,18 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ReleaseResourcesAndProcessUndo and for
+			 * subtransaction, we promote the error to fatal in such a
+			 * situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3035,13 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3087,7 +3137,9 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3159,9 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				CheckAndRegisterUndoRequest();
 				AbortSubTransaction();
+				ReleaseResourcesAndProcessUndo();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3175,7 +3229,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!NeedToPerformUndoActions());
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3190,7 +3248,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3261,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!NeedToPerformUndoActions());
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3215,7 +3279,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3226,7 +3292,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_END:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3255,7 +3323,9 @@ AbortCurrentTransaction(void)
 			 * Abort, cleanup, go to idle state.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3266,7 +3336,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_PREPARE:
+			CheckAndRegisterUndoRequest();
 			AbortTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3277,7 +3349,9 @@ AbortCurrentTransaction(void)
 			 * we get ROLLBACK.
 			 */
 		case TBLOCK_SUBINPROGRESS:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3291,7 +3365,9 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBCOMMIT:
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
+			CheckAndRegisterUndoRequest();
 			AbortSubTransaction();
+			ReleaseResourcesAndProcessUndo();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,6 +3380,19 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ReleaseResourcesAndProcessUndo and for subtransaction, we
+			 * promote the error to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
@@ -3633,6 +3722,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +3916,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4034,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4176,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4157,6 +4254,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4266,6 +4365,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4485,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4473,17 +4576,25 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
+	/* Try to push rollback request to worker if possible. */
+	CheckAndRegisterUndoRequest();
+
 	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ReleaseResourcesAndProcessUndo();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4529,7 +4640,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!NeedToPerformUndoActions());
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4545,6 +4660,19 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_ABORT_PENDING:
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
+				CheckAndRegisterUndoRequest();
+				AbortTransaction();
+				ReleaseResourcesAndProcessUndo();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
@@ -4572,7 +4700,9 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBCOMMIT:
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
+				CheckAndRegisterUndoRequest();
 				AbortSubTransaction();
+				ReleaseResourcesAndProcessUndo();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
@@ -4592,6 +4722,17 @@ AbortOutOfAnyTransaction(void)
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 				break;
+			case TBLOCK_SUBUNDO:
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
+				AbortSubTransaction();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
 		}
 	} while (s->blockState != TBLOCK_DEFAULT);
 
@@ -4666,6 +4807,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4722,6 +4865,14 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4743,6 +4894,7 @@ static void
 CommitSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int		i;
 
 	ShowTransactionState("CommitSubTransaction");
 
@@ -4765,6 +4917,19 @@ CommitSubTransaction(void)
 	/* Do the actual "commit", such as it is */
 	s->state = TRANS_COMMIT;
 
+	/*
+	 * Propagate the undo pointers from current transaction to parent so that
+	 * if parent transaction get aborted we must not skip performing undo for
+	 * this transaction.
+	 */
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (UndoRecPtrIsValid(s->latestUrecPtr[i]))
+			s->parent->latestUrecPtr[i] = s->latestUrecPtr[i];
+		if (!UndoRecPtrIsValid(s->parent->startUrecPtr[i]))
+			s->parent->startUrecPtr[i] = s->startUrecPtr[i];
+	}
+
 	/* Must CCI to ensure commands of subtransaction are seen as done */
 	CommandCounterIncrement();
 
@@ -4852,6 +5017,7 @@ static void
 AbortSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	bool	undo_apply_in_progress;
 
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
@@ -4909,7 +5075,10 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	undo_apply_in_progress = (s->blockState == TBLOCK_SUBUNDO);
+
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -4943,8 +5112,13 @@ AbortSubTransaction(void)
 								s->parent->subTransactionId);
 		AtSubAbort_Notify();
 
-		/* Advertise the fact that we aborted in pg_xact. */
-		(void) RecordTransactionAbort(true);
+		/*
+		 * Advertise the fact that we aborted in pg_xact.  But if undo apply
+		 * is in progress then skip this as in that case we would have already
+		 * written abort record.
+		 */
+		if (!undo_apply_in_progress)
+			(void) RecordTransactionAbort(true);
 
 		/* Post-abort cleanup */
 		if (FullTransactionIdIsValid(s->fullTransactionId))
@@ -5336,6 +5510,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5530,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5557,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6157,284 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoLogCategory category)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->startUrecPtr[category]))
+		CurrentTransactionState->startUrecPtr[category] = urec_ptr;
+	CurrentTransactionState->latestUrecPtr[category] = urec_ptr;
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	/* initialize undo information for the transaction */
+	memset(s->startUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->latestUrecPtr, InvalidUndoRecPtr,
+		   sizeof(UndoRecPtr) * UndoLogCategories);
+	memset(s->undoRequestResgistered, false,
+		   sizeof(bool) * UndoLogCategories);
+}
+
+/*
+ * NeedToPerformUndoActions - Returns true, if the current transaction needs
+ * to perform undo actions, false otherwise.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing them if required.  We don't release the locks
+ * till we execute undo actions otherwise, there is a risk of deadlock.
+ */
+bool
+NeedToPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (UndoRecPtrIsValid(s->latestUrecPtr[i]))
+			return true;
+	}
+
+	return false;
+}
+
+/*
+ * CheckAndRegisterUndoRequest - Register the request for applying undo
+ *	actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+CheckAndRegisterUndoRequest()
+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	int		i;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  See ReleaseResourcesAndProcessUndo.
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}
+
+	if (!NeedToPerformUndoActions())
+		return;
+	/*
+	 * We can't postpone applying undo actions for subtransactions as the
+	 * modifications made by aborted subtransaction must not be visible even if
+	 * the main transaction commits.  See execute_undo_actions for detailed
+	 * explanation.
+	 */
+	if (IsSubTransaction())
+		return;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (i != UNDO_TEMP && UndoRecPtrIsValid(s->latestUrecPtr[i]))
+		{
+			result = RegisterUndoRequest(s->latestUrecPtr[i],
+										 s->startUrecPtr[i],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undoRequestResgistered[i] = result;
+		}
+	}
+}
+
+/*
+ * ReleaseResourcesAndProcessUndo - Release resources and process undo request.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ReleaseResourcesAndProcessUndo(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	/*
+	 * We don't want to apply the undo actions when we are already cleaning up
+	 * for FATAL error.  One of the main reasons is that we might be already
+	 * processing undo actions for a (sub)transaction when we reach here
+	 * (for ex. error happens while processing undo actions for a
+	 * subtransaction).
+	 */
+	if (SemiCritSectionCount > 0)
+	{
+		ResetUndoActionsInfo();
+		return;
+	}
+
+	if (!NeedToPerformUndoActions())
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ReleaseResourcesAndProcessUndo: unexpected state %s",
+			TransStateAsString(s->state));
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	/*
+	 * We ignore the return value here as in either case we need to release
+	 * the resources and allow caller to proceed with further cleanup.
+	 */
+	(void) ProcessUndoRequestForEachLogCat(GetTopFullTransactionId(),
+										   MyDatabaseId,
+										   s->latestUrecPtr, s->startUrecPtr,
+										   s->undoRequestResgistered,
+										   IsSubTransaction());
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	/* Release the locks after processing undo request. */
+	ResourceOwnerRelease(s->curTransactionOwner,
+						 RESOURCE_RELEASE_LOCKS,
+						 false,
+						 !IsSubTransaction());
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
+ * ProcessUndoRequestForEachLogCat - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ *
+ * If we get any error while performing undo actions, we just insert the
+ * request into an error queue for later processing by undo launcher and allow
+ * the main transaction to continue.  In such a case the callers are
+ * responsible to release the resources.
+ */
+bool
+ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+								UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+								bool *undoRequestResgistered, bool isSubTrans)
+{
+	UndoRequestInfo urinfo;
+	int			i;
+	uint32		save_holdoff;
+	bool		success = true;
+
+	for (i = 0; i < UndoLogCategories; i++)
+	{
+		if (end_urec_ptr[i] && !undoRequestResgistered[i])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(fxid,
+									 end_urec_ptr[i],
+									 start_urec_ptr[i],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[i];
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/* We should never reach here when we are in a semi-critical-section. */
+				Assert(SemiCritSectionCount == 0);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..e0caf9e
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,39 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index c215ba1..2d97e7a 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -1013,6 +1013,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.category);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
index 2c6beae..f521ce9 100644
--- a/src/backend/access/undo/undoaction.c
+++ b/src/backend/access/undo/undoaction.c
@@ -457,12 +457,30 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool complete_xact)
 {
 	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+	UndoLogCategory		logcat = UndoRecPtrGetCategory(to_urecptr);
 
 	/* 'from' and 'to' pointers must be valid. */
 	Assert(UndoRecPtrIsValid(from_urecptr));
 	Assert(UndoRecPtrIsValid(to_urecptr));
 
 	/*
+	 * We need to execute the undo actions in a semi-critical section for
+	 *
+	 * (a) Subtransactions.  We can't proceed without applying
+	 * subtransaction's undo as the modifications made in that case must not
+	 * be visible even if the main transaction commits.  The reason why that
+	 * can happen is because for undo-based AM's we don't need to have a
+	 * separate transaction id for subtransactions and once the main
+	 * transaction commits the tuples modified by subtransactions will become
+	 * visible.
+	 *
+	 * (b) Temp tables.  We don't expect background workers to process undo of
+	 * temporary tables as the same won't be accessible.
+	 */
+	if (!complete_xact || logcat == UNDO_TEMP)
+		START_SEMI_CRIT_SECTION();
+
+	/*
 	 * Here we compute the last log start urp which is used for fetching the
 	 * undo records and updating the undo action progress.
 	 *
@@ -472,7 +490,7 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 	 */
 	if (complete_xact)
 	{
-		if (UndoRecPtrGetCategory(to_urecptr) == UNDO_TEMP)
+		if (logcat == UNDO_TEMP)
 		{
 			UndoRecPtr end_urec_ptr = from_urecptr;
 
@@ -521,4 +539,7 @@ execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 	 * Undo actions are applied so delete the hash table entry.
 	 */
 	RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+
+	if (!complete_xact || logcat == UNDO_TEMP)
+		END_SEMI_CRIT_SECTION();
 }
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 05d02c2..22f0bac 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -193,6 +193,13 @@ proc_exit_prepare(int code)
 	/* do our shared memory exits first */
 	shmem_exit(code);
 
+	/*
+	 * We need to clear semi-critical-section after exiting from shmem as we
+	 * can again use it for executing undo actions via
+	 * AbortOutOfAnyTransaction which is called during shmem exit.
+	 */
+	SemiCritSectionCount = 0;
+
 	elog(DEBUG3, "proc_exit(%d): %d callbacks to make",
 		 code, on_proc_exit_index);
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index a6505c7..68eaacf 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -594,7 +594,9 @@ ProcessClientWriteInterrupt(bool blocked)
 			 * Don't mess with whereToSendOutput if ProcessInterrupts wouldn't
 			 * do anything.
 			 */
-			if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+			if (InterruptHoldoffCount == 0 &&
+				CritSectionCount == 0 &&
+				SemiCritSectionCount == 0)
 			{
 				/*
 				 * We don't want to send the client the error message, as a)
@@ -2985,7 +2987,9 @@ void
 ProcessInterrupts(void)
 {
 	/* OK to accept any interrupts now? */
-	if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
+	if (InterruptHoldoffCount != 0 ||
+		CritSectionCount != 0 ||
+		SemiCritSectionCount != 0)
 		return;
 	InterruptPending = false;
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..d24828f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,16 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. If we are inside a semi-critical section, all errors become FATAL
+		 * errors.  See miscadmin.h.
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				SemiCritSectionCount > 0)
 				elevel = FATAL;
 		}
 
@@ -454,6 +458,7 @@ errfinish(int dummy,...)
 		QueryCancelHoldoffCount = 0;
 
 		CritSectionCount = 0;	/* should be unnecessary, but... */
+		SemiCritSectionCount = 0;
 
 		/*
 		 * Note that we leave CurrentMemoryContext set to ErrorContext. The
@@ -1165,6 +1170,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de..9faeb84 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -36,6 +36,7 @@ volatile sig_atomic_t ConfigReloadPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
+volatile uint32 SemiCritSectionCount = 0;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c4..7fb8329 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ReleaseResourcesAndProcessUndo.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (isCommit || !NeedToPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!NeedToPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 01f248a..7796f72 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -231,6 +231,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern uint32 GetEpochForXid(TransactionId xid);
 
 /*
  * Some frontend programs include this header.  For compilers that emit static
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..497b92f 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,6 +14,7 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undolog.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
@@ -41,7 +42,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726a..2ca61ea 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/undolog.h"
 #include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
@@ -428,6 +429,16 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
 
+/* functions to allow undo execution */
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+						UndoLogCategory category);
+extern void ResetUndoActionsInfo(void);
+extern bool NeedToPerformUndoActions(void);
+extern void ReleaseResourcesAndProcessUndo(void);
+extern bool ProcessUndoRequestForEachLogCat(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool *undoRequestResgistered, bool isSubTrans);
+
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
 extern const char *xact_identify(uint8 info);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1afc4d3..5efc7a2 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -74,6 +74,12 @@
  * *critical* code should be marked as a critical section!	Currently, this
  * mechanism is only used for XLOG-related code.
  *
+ * Similar to the "critical section", we have another mechanism known as
+ * "semi critical section".  It generally has similar behaviour as
+ * "critical section" with the difference that it causes any ereport(ERROR) to
+ * become ereport(FATAL).  Currently this is used by undo-machinery, but it
+ * can be used in other places too.
+ *
  *****************************************************************************/
 
 /* in globals.c */
@@ -90,6 +96,7 @@ extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
 extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
 extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
 extern PGDLLIMPORT volatile uint32 CritSectionCount;
+extern PGDLLIMPORT volatile uint32 SemiCritSectionCount;
 
 /* in tcop/postgres.c */
 extern void ProcessInterrupts(void);
@@ -137,6 +144,14 @@ do { \
 	CritSectionCount--; \
 } while(0)
 
+#define START_SEMI_CRIT_SECTION()  (SemiCritSectionCount++)
+
+#define END_SEMI_CRIT_SECTION() \
+do { \
+	Assert(SemiCritSectionCount > 0); \
+	SemiCritSectionCount--; \
+} while(0)
+
 
 /*****************************************************************************
  *	  globals.h --															 *
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8ef..ed31351 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
-- 
1.8.3.1

0013-Allow-execution-and-discard-of-undo-by-background-wo.patchapplication/octet-stream; name=0013-Allow-execution-and-discard-of-undo-by-background-wo.patchDownload
From c45e82d283e86570b7551f2eb2e57107c0fae921 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 16:03:49 +0530
Subject: [PATCH 13/13] Allow execution and discard of undo by background
 workers.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

We don't allow any transaction older than 2^31 to have pending undo actions.
Also, we have a hard limit on the number of transactions that can have
pending undo which is proportional to pending_undo_queue_size.

Amit Kapila, Dilip Kumar and Kuntal Ghosh with inputs from Andres Freund,
Robert Haas and Thomas Munro.
---
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/varsup.c           |  35 +-
 src/backend/access/transam/xact.c             |   5 +
 src/backend/access/transam/xlog.c             |  29 +
 src/backend/access/undo/Makefile              |   4 +-
 src/backend/access/undo/README.UndoProcessing |  77 +++
 src/backend/access/undo/discardworker.c       | 215 +++++++
 src/backend/access/undo/undoaccess.c          |  58 +-
 src/backend/access/undo/undodiscard.c         | 490 +++++++++++++++
 src/backend/access/undo/undolog.c             |   2 +
 src/backend/access/undo/undorequest.c         | 205 ++++++-
 src/backend/access/undo/undoworker.c          | 841 ++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c              |   5 +
 src/backend/postmaster/bgworker.c             |  11 +
 src/backend/postmaster/pgstat.c               |   9 +
 src/backend/postmaster/postmaster.c           |  11 +
 src/backend/storage/ipc/ipci.c                |   6 +
 src/backend/storage/lmgr/lwlocknames.txt      |   1 +
 src/backend/storage/lmgr/proc.c               |   2 +
 src/backend/utils/misc/guc.c                  |  22 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/access/discardworker.h            |  20 +
 src/include/access/transam.h                  |  10 +
 src/include/access/undodiscard.h              |  26 +
 src/include/access/undolog.h                  |  13 +
 src/include/access/undoworker.h               |  29 +
 src/include/catalog/pg_control.h              |   9 +
 src/include/nodes/primnodes.h                 |   3 +-
 src/include/pgstat.h                          |   5 +-
 src/include/postmaster/postmaster.h           |   1 +
 src/include/storage/lwlock.h                  |   1 +
 src/include/storage/proc.h                    |   4 +
 src/include/storage/procarray.h               |   7 +-
 src/test/regress/expected/sysviews.out        |   3 +-
 34 files changed, 2131 insertions(+), 33 deletions(-)
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f3..4b00d7d 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest full xid having unapplied undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUnappliedUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index fd01989..e74155d 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -127,14 +127,16 @@ GetNewTransactionId(bool isSubXact)
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
 		{
@@ -147,14 +149,16 @@ GetNewTransactionId(bool isSubXact)
 								oldest_datname,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(WARNING,
 						(errmsg("database with OID %u must be vacuumed within %u transactions",
 								oldest_datoid,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -334,10 +338,24 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
 	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUnapplied Undo because this is the oldest xid
+	 * whose undo is not yet discarded so this is still a valid xid in the
+	 * system.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
+	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
 	 * probably off by one or two counts, because the special XIDs reduce the
@@ -433,6 +451,9 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 		 * Note: it's also possible that get_database_name fails and returns
 		 * NULL, for example because the database just got dropped.  We'll
 		 * still warn, even though the warning might now be unnecessary.
+		 *
+		 * XXX Can we easily distinguish that the problem is due to unapplied
+		 * undo or some old open transactions?
 		 */
 		if (IsTransactionState())
 			oldest_datname = get_database_name(oldest_datoid);
@@ -445,14 +466,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 							oldest_datname,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 		else
 			ereport(WARNING,
 					(errmsg("database with OID %u must be vacuumed within %u transactions",
 							oldest_datoid,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 	}
 }
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index fbbe585..c06436f 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2273,6 +2274,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5dbe485..99e4322 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5159,6 +5159,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUnappliedUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6622,6 +6623,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6638,6 +6642,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7326,7 +7334,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8723,6 +8737,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUnappliedUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9635,6 +9653,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9692,12 +9713,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9737,6 +9763,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 68696bc..b4e7bab 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
-	   undorequest.o
+OBJS = discardworker.o undoaccess.o undoaction.o undoactionxlog.o undodiscard.o \
+	   undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
index e0caf9e..4d6973b 100644
--- a/src/backend/access/undo/README.UndoProcessing
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -37,3 +37,80 @@ tables are only accessible in the backend that has created them.  We can't
 postpone applying undo actions for subtransactions as the modifications
 made by aborted subtransaction must not be visible even if the main transaction
 commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue will allow us to
+process the requests of older transactions and help us to move
+oldesdXidHavingUnappliedUndo (this is a xid-horizon below which all the
+transactions are visible) forward.  A size-based queue which will help us to
+perform the rollbacks of larger aborts in a timely fashion, so that we don't get
+stuck while processing them during discard of the logs.  An error queue to hold
+the requests for transactions that failed to apply its undo.  The rollback hash
+table is used to avoid duplicate undo requests by backends and discard worker.
+The table must be able to accommodate all active undo requests.  The undo
+requests must appear in both xid and size requests queues or neither.  As of now,
+we process the requests from these queues in a round-robin fashion to give equal
+priority to all three types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.
+
+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+
+In a running system, scanning the rollback hash table will give us the value of
+oldestXidHavingUnappliedUndo, however, after startup, we need to once scan all
+the undo logs and populate the rollback hash table.  After startup, we allow
+connections, but don't allow transactions that want to write undo till the
+rollback hash table is initialized.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+To apply the undo actions, we collect the undo records in bulk and try to
+process them together.  We ensure to update the transaction's progress at
+regular intervals so that after a crash we can skip already applied undo.  The
+undo apply progress is updated in terms of the number of blocks processed.
+Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED indicates that all the
+undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED indicates that no undo action
+has been applied yet and any other value indicates that we have applied undo
+partially and after crash recovery, we need to start processing the undo from
+the same location.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then starts reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms after starting.  Also, if there is no work, it lingers
+for UNDO_WORKER_LINGER_MS.  This avoids restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 0000000..f324643
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,215 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be
+ * processed separately if we encounter the corresponding log first.  If we
+ * want we can combine the log for processing in that case as well, but there
+ * is no clear advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/discardworker.h"
+#include "access/undodiscard.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* Normal exit from discard worker */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 2d97e7a..9faae13 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -56,6 +56,7 @@
 #include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/proc.h"
 #include "miscadmin.h"
 
 /*
@@ -77,6 +78,13 @@
  */
 UndoCompressionInfo undo_compression_info[UndoLogCategories];
 
+/*
+ * Defines the number of times we try to wait for rollback hash table to get
+ * initialized.  After these many attempts it will return error and the user
+ * can retry the operation.
+ */
+#define ROLLBACK_HT_INIT_WAIT_TRY      60
+
 /* Prototypes for static functions. */
 static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
 											UndoRecPtr urp, RelFileNode rnode,
@@ -672,6 +680,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	UndoCompressionInfo *compression_info =
 	&context->undo_compression_info[context->alloc_context.category];
 
+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo
+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}
+
+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+
 	/* Already reached maximum prepared limit. */
 	if (context->nprepared_undo == context->max_prepared_undo)
 		elog(ERROR, "already reached the maximum prepared limit");
@@ -1828,12 +1880,12 @@ UndoBlockGetFirstUndoRecord(BlockNumber blkno, UndoRecPtr urec_ptr,
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 	page = BufferGetPage(buffer);
-	phdr = (UndoPageHeader)page;
+	phdr = (UndoPageHeader) page;
 
 	/* Calculate the size of the partial record. */
 	partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
-						phdr->tuple_len + phdr->payload_len -
-						phdr->record_offset;
+					   phdr->tuple_len + phdr->payload_len -
+					   phdr->record_offset;
 
 	/* calculate the offset in current log. */
 	offset_cur_page = SizeOfUndoPageHeaderData + partial_rec_size;
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 0000000..dfabbfa
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undodiscard.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlog_internal.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+
+/*
+ * Discard as many record sets as we can from the undo log occupying a given
+ * slot, considering the given xmin horizon.  If we encounter a record set
+ * that needs to be rolled back, register a rollback request.  Set *hibernate
+ * to false if work was done.
+ */
+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	bool	need_discard = false;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		UnpackedUndoRecord	*uur = NULL;
+		UndoRecPtr	next_insert;
+		FullTransactionId	undofxid = InvalidFullTransactionId;
+		TransactionId wait_xid = InvalidTransactionId;
+		bool pending_abort = false;
+		bool request_rollback = false;
+		UndoStatus status;
+		UndoRecordFetchContext	context;
+
+		next_insert = UndoLogGetNextInsertPtr(logno);
+
+		/* There must be some undo data for a transaction. */
+		Assert(next_insert != undo_recptr);
+
+		/* Fetch the undo record for the given undo_recptr. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, undo_recptr);
+		FinishUndoFetch(&context);
+
+		if (uur != NULL)
+		{
+			if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)
+			{
+				/*
+				 * For the "shared" category, we only discard when the
+				 * rm_undo_status callback tells us we can.
+				 */
+				status = RmgrTable[uur->uur_rmid].rm_undo_status(uur, &wait_xid);
+
+				Assert((status == UNDO_STATUS_WAIT_XMIN &&
+						TransactionIdIsValid(wait_xid)) ^
+						(status == UNDO_STATUS_DISCARD &&
+						!TransactionIdIsValid(wait_xid)));
+			}
+			else
+			{
+				TransactionId xid = XidFromFullTransactionId(uur->uur_fxid);
+
+				/*
+				 * Otherwise we use the CLOG and xmin to decide whether to
+				 * wait, discard or roll back.
+				 *
+				 * XXX: We've added the transaction-in-progress check to
+				 * avoid xids of in-progress autovacuum as those are not
+				 * computed for oldestxmin calculation.  See
+				 * DiscardWorkerMain.
+				 */
+				if (TransactionIdDidCommit(xid))
+				{
+					/*
+					 * If this record set's xid isn't before the xmin
+					 * horizon, we'll have to wait before we can discard
+					 * it.
+					 */
+					if (TransactionIdFollowsOrEquals(xid, xmin))
+						wait_xid = xid;
+
+				}
+				else if (!TransactionIdIsInProgress(xid))
+				{
+					/*
+					 * If it hasn't been applied already, then we'll ask
+					 * for it to be applied now.  Otherwise it'll be
+					 * discarded.
+					 */
+					if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+						request_rollback = true;
+				}
+				else
+				{
+					/*
+					 * It's either in progress or isn't yet before the
+					 * xmin horizon, so we'll have to wait.
+					 */
+					wait_xid = XidFromFullTransactionId(uur->uur_fxid);
+				}
+			}
+
+			/*
+			 * Add the aborted transaction to the rollback request queues.
+			 *
+			 * We can ignore the abort for transactions whose corresponding
+			 * database doesn't exist.
+			 */
+			if (request_rollback && dbid_exists(uur->uur_txn->urec_dbid))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr,
+										   undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+
+				pending_abort = true;
+			}
+
+			next_urecptr = uur->uur_txn->urec_next;
+			undofxid = uur->uur_fxid;
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) we need to wait for a transaction first. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if (TransactionIdIsValid(wait_xid) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If we don't need to wait for this transaction and this is not
+			 * an aborted transaction, then we can discard it as well.
+			 */
+			if (!TransactionIdIsValid(wait_xid) && !pending_abort)
+			{
+				/*
+				 * It is safe to use next_insert as the location till which we
+				 * want to discard in this case.  If something new has been
+				 * added after we have fetched this transaction's record, it
+				 * won't be considered in this pass of discard.
+				 */
+				undo_recptr = next_insert;
+				latest_discardxid = XidFromFullTransactionId(undofxid);
+				need_discard = true;
+
+				/* We don't have anything more to discard. */
+				undofxid = InvalidFullTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/* Update the slot information for the next pass of discard. */
+			slot->wait_fxmin = undofxid;
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = XidFromFullTransactionId(undofxid);
+
+		/* The fetched undo record must be release by now. */
+		Assert(uur == NULL);
+
+		/* If we reach here, this means there is something to discard. */
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+}
+
+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass
+ * over undo logs is complete, new undo is allowed to be written in the
+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before allowing any new transaction to start writing
+ * the undo we need to make sure that we know exact number of pending
+ * requests.
+ */
+void
+UndoLogProcess()
+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();
+
+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* We do not execute shared (non-transactional) undo records. */
+		if (slot->meta.category == UNDO_SHARED)
+			continue;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			TransactionId xid;
+			UndoRecordFetchContext	context;
+
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;
+
+			/* Fetch the undo record for the given undo_recptr. */
+			BeginUndoFetch(&context);
+			uur = UndoFetchRecord(&context, undo_recptr);
+			FinishUndoFetch(&context);
+
+			Assert(uur != NULL);
+
+			xid = XidFromFullTransactionId(uur->uur_fxid);
+
+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(xid) &&
+				!TransactionIdIsInProgress(xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_txn->urec_dbid,
+										   uur->uur_fxid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are
+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) ==
+				UndoRecPtrGetLogNo(uur->uur_txn->urec_next))
+				undo_recptr = uur->uur_txn->urec_next;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+
+	CommitTransactionCommand();
+
+	/* Allow the transactions to start writting undo. */
+	ProcGlobal->rollbackHTInitialized = true;
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 *
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.category == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin then try
+		 * to discard the undo log.
+		 */
+		if (!FullTransactionIdIsValid(slot->wait_fxmin) ||
+			FullTransactionIdPrecedes(slot->wait_fxmin, oldestXidHavingUndo))
+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+	}
+
+	/* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+	oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+
+	/*
+	 * Update the oldestFullXidHavingUnappliedUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	Assert(FullTransactionIdIsValid(oldestXidHavingUndo));
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+						U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.category == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index b5502f2..09d9b30 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -145,6 +145,8 @@ UndoLogShmemInit(void)
 							 LWTRANCHE_UNDOLOG);
 			LWLockInitialize(&UndoLogShared->slots[i].discard_lock,
 							 LWTRANCHE_UNDODISCARD);
+			LWLockInitialize(&UndoLogShared->slots[i].discard_update_lock,
+							 LWTRANCHE_DISCARD_UPDATE);
 		}
 	}
 	else
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
index 530887a..0222c86 100644
--- a/src/backend/access/undo/undorequest.c
+++ b/src/backend/access/undo/undorequest.c
@@ -54,10 +54,12 @@
 #include "postgres.h"
 #include "miscadmin.h"
 
+#include "access/discardworker.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_database.h"
@@ -880,9 +882,6 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 		 */
 		if (UndoRecPtrIsDiscarded(next_urecptr))
 		{
-			UndoLogOffset next_insert;
-
-			next_insert = UndoLogGetNextInsertPtr(slot->logno);
 			Assert(UndoRecPtrIsValid(next_insert));
 
 			last_log_start_urecptr = urecptr;
@@ -898,9 +897,6 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 		 * this log for request size computation.
 		 */
 		{
-			UndoLogOffset next_insert;
-
-			next_insert = UndoLogGetNextInsertPtr(slot->logno);
 			Assert(UndoRecPtrIsValid(next_insert));
 
 			last_log_start_urecptr = urecptr;
@@ -928,6 +924,138 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 }
 
 /*
+ * Fetch the start urec pointer for the transaction and the undo request size.
+ *
+ * start_urecptr_inout - This is an INOUT parameter.  If a transaction has
+ * overflowed to multiple undo logs, the caller can set start_urecptr_inout
+ * to a location of any of the undo logs where the transaction has written its
+ * first record for that particular log.  Given that, this function calculates
+ * the undo location where the transaction has inserted its first undo record.
+ * If a transaction hasn't overflowed to multiple undo logs, the value of this
+ * parameter remains unchanged.
+ *
+ * The first record of a transaction in each undo log contains a reference to
+ * the first record of this transaction in the previous log.  It finds the
+ * initial location by moving backward in the undo chain of this transaction
+ * across undo logs.  While doing the same, it also calculates the undo size
+ * between the input and output start undo record pointer value.
+ */
+static uint64
+FindUndoStartLocationAndSize(UndoRecPtr *start_urecptr_inout,
+							 FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+
+	Assert(start_urecptr_inout);
+
+	urecptr = *start_urecptr_inout;
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * A backend always set the start undo record pointer to the first undo
+	 * record inserted by this transaction.  Hence, we don't have to proceed
+	 * further.
+	 */
+	if (!IsDiscardProcess())
+		return sz;
+
+	/*
+	 * Since the discard worker processes the undo logs sequentially, it's
+	 * possible that start undo record pointer doesn't refer to the actual
+	 * start of the transaction.  Instead, it may refer to the start location
+	 * of the transaction in any of the subsequent logs.  In that case, we've
+	 * to find the actual start location of the transaction by going backwards
+	 * in the chain.
+	 */
+	while (true)
+	{
+		UndoLogOffset next_insert;
+		UndoRecordFetchContext	context;
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		/* Fetch the undo record. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, urecptr);
+		FinishUndoFetch(&context);
+
+		/*
+		 * Since the rollback isn't completed for this transaction, this undo
+		 * record can't be discarded.
+		 */
+		Assert (uur != NULL);
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_info & UREC_INFO_TRANSACTION);
+
+		/*
+		 * If this is the first undo record of this transaction, return from
+		 * here.
+		 */
+		if ((uur->uur_info & UREC_INFO_LOGSWITCH) == 0)
+		{
+			UndoRecordRelease(uur);
+			break;
+		}
+
+		/*
+		 * This is a start of a overflowed transaction header, so it must have
+		 * a valid pointer to previous log's start transaction header.
+		 */
+		Assert(UndoRecPtrIsValid(uur->uur_logswitch->urec_prevlogstart));
+
+
+		/*
+		 * Find the previous log from which the transaction is overflowed
+		 * to current log.
+		 */
+		urecptr = uur->uur_logswitch->urec_prevlogstart;
+		slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		/*
+		 * When a transaction overflows to a new undo log, it's guaranteed
+		 * that this transaction will be the last transaction in the previous
+		 * log and we mark that log as full so that no other transaction can
+		 * write in that log further.  Check UndoLogAllocate for details.
+		 *
+		 * So, to find the undo size in the previous log, we've to find the
+		 * next insert location of the previous log and subtract current
+		 * transaction's start location in the previous log from it.
+		 */
+		next_insert = UndoLogGetNextInsertPtr(slot->logno);
+		Assert(UndoRecPtrIsValid(next_insert));
+
+		sz += (next_insert - urecptr);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	*start_urecptr_inout = urecptr;
+
+	return sz;
+}
+
+/*
  * Returns true, if we can push the rollback request to undo wrokers, false,
  * otherwise.
  */
@@ -943,9 +1071,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 
 	/*
 	 * We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
 	 */
-	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
 	{
 		if (GetXidQueueSize() >= pending_undo_queue_size ||
 			GetSizeQueueSize() >= pending_undo_queue_size)
@@ -971,12 +1114,7 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 		if ((GetXidQueueSize() < pending_undo_queue_size))
 		{
 			Assert(GetSizeQueueSize() < pending_undo_queue_size);
-
-			/*
-			 * XXX - Here, we should return true once we have background
-			 * worker facility.
-			 */
-			return false;
+			return true;
 		}
 	}
 
@@ -1417,6 +1555,12 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	Assert(dbid != InvalidOid);
 
 	/*
+	 * The discard worker can only send the start undo record pointer of a
+	 * transaction.  It doesn't set the end_urec_ptr.
+	 */
+	Assert(IsDiscardProcess() || UndoRecPtrIsValid(end_urec_ptr));
+
+	/*
 	 * Find the rollback request size and the end_urec_ptr (in case of discard
 	 * worker only).
 	 */
@@ -1431,6 +1575,14 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	if (!UndoRecPtrIsValid(end_urec_ptr))
 		return false;
 
+	/*
+	 * For registering a rollback request, we always store the full transaction
+	 * ID and the first undo record pointer inserted by this transaction.  This
+	 * ensures that backends and discard worker don't register the same request
+	 * twice.
+	 */
+	req_size += FindUndoStartLocationAndSize(&start_urec_ptr, full_xid);
+
 	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
 
 	/*
@@ -1446,7 +1598,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 										   HASH_ENTER_NULL, &found);
 
 	/*
-	 * It can only fail, if the value of pending_undo_queue_size or
+	 * Except the first pass over the undo logs by discard worker, the hash
+	 * table can never be full.
+	 */
+	Assert(!ProcGlobal->rollbackHTInitialized || (rh != NULL));
+
+	/*
+	 * It can only fail, if  the value of pending_undo_queue_size or
 	 * max_connections guc is reduced after restart of the server.
 	 */
 	if (rh == NULL)
@@ -1494,10 +1652,16 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 		}
 		/*
 		 * The request can't be pushed into the undo worker queue.  The
-		 * backends will try executing by itself.
+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with
+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
 		 */
-		else
+		else if (!IsDiscardProcess())
 			rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
 	}
 	else if (!UndoRequestIsValid(rh) && can_push)
 	{
@@ -1525,6 +1689,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 
 	LWLockRelease(RollbackRequestLock);
 
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
 	return pushed;
 }
 
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 0000000..3741445
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,841 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then starts reading from one of the queues the requests
+ * for that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+#include "access/xact.h"
+#include "funcapi.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 4;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.  This has to be more than UNDO_FAILURE_RETRY_DELAY_MS,
+ * otherwise, worker can exit before retrying the failed requests.
+ */
+#define UNDO_WORKER_LINGER_MS 20000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t got_SIGTERM = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker *worker);
+static void UndoWorkerSetLingering(bool sleep);
+static void UndoWorkerGetRequestInfo(UndoRequestInfo *urinfo);
+static void UndoworkerSigtermHandler(SIGNAL_ARGS);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+UndoworkerSigtermHandler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		if (rc & WL_LATCH_SET)
+			ResetLatch(MyLatch);
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("could not attach to undo worker slot"),
+				 errdetail("slot %d is empty", slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("could not attach to undo worker slot"),
+				 errdetail("slot %d is already used by another worker",
+						   slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static bool
+UndoWorkerIsAvailable(void)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return true;
+		}
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return false;
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerSetLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetRequestInfo(UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	Assert(MyUndoWorker->in_use);
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible.
+ */
+static void
+UndoWorkerLaunch(UndoRequestInfo *urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock to have
+	 * a consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (slot = 0; slot < max_undo_workers; slot++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[slot];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			break;
+		}
+	}
+
+	/*
+	 * If there are no more free worker slots, inform user about it before
+	 * exiting.
+	 */
+	if (worker == NULL)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(WARNING,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("out of undo worker slots"),
+				 errhint("You might need to increase max_undo_workers.")));
+		return;
+	}
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo->dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo->undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker for database id %d",
+			 worker->dbid);
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		ereport(WARNING,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("out of undo worker slots"),
+				 errhint("You might need to increase max_undo_workers.")));
+
+		return;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker *worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request.  This is required because we access system tables while performing
+ * undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo *urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.  If we fail to add the request in
+		 * an error queue, then mark the entry status invalid.  This request
+		 * will be later added back to the queue by the discard worker.
+		 */
+		if (!InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTMarkEntryInvalid(urinfo->full_xid,
+									   urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of UndoWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+	{
+		int		slot;
+
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+
+		/* Initialize memory for each worker slot. */
+		for (slot = 0; slot < max_undo_workers; slot++)
+		{
+			UndoApplyWorker *worker = &UndoApplyCtx->workers[slot];
+
+			memset(worker, 0, sizeof(UndoApplyWorker));
+		}
+	}
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("cannot start undo launcher when max_undo_worker = 0")));
+		return;
+	}
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		/*
+		 * Here, we have a race condition, which is that even though we have
+		 * checked the availablity of worker before launching it, there is
+		 * still a chance that we don't get worker by the time it tries to
+		 * launch.  We can avoid that race by doing it in lock, but it doesn't
+		 * see worth as this is just a corner case.
+		 */
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			UndoWorkerIsAvailable())
+			UndoWorkerLaunch(&urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+
+	/* Normal exit from undo launcher main */
+	ereport(LOG,
+			(errmsg("undo launcher shutting down")));
+	proc_exit(0);
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the worker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetRequestInfo(&urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker should
+	 * start looking for work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	while (!got_SIGTERM)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * We don't want to restart workers frequently, so allow to peek few
+		 * entries ahead, if required.
+		 */
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, the requester
+			 * can wake us up.
+			 */
+			UndoWorkerSetLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerSetLingering(false);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	/* Normal exit from undo worker main */
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0c0ddd5..e774c55 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14547,6 +14548,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index b66b517..b3db6fb 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index d8dc0cc..4c906f6 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3339804..6521efa 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher = true;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c3249..56b5d08 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 19e4f1f..1f65056 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -51,3 +51,4 @@ LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
 RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 884fa2a..61406cf 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -297,7 +297,9 @@ InitProcGlobal(void)
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
 
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo, 0);
 	ProcGlobal->xactsHavingPendingUndo = 0;
+	ProcGlobal->rollbackHTInitialized = false;
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a7d1db5..8b4aa0c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1955,6 +1956,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		 },
+		 &enable_undo_launcher,
+		 true,
+		 NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3031,6 +3043,16 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"max_undo_workers", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
+			gettext_noop("Maximum number of undo worker processes."),
+			NULL,
+		},
+		&max_undo_workers,
+		4, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
 			NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 592f6e1..e86507c 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -612,6 +612,7 @@
 					# requests are pushed to undo workers
 #pending_undo_queue_size = 1024	# size of queue used to register undo
 					# requests
+#max_undo_workers = 4	# maximum undo workers
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 0000000..5b065bf
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7796f72..e68e743 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -73,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 0000000..282afc0
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+extern void UndoLogProcess(void);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 7ec4cb0..b19b931 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -279,6 +279,19 @@ typedef struct UndoLogAllocContext
 /*
  * The in-memory control object for an undo log.  We have a fixed-sized array
  * of these.
+ *
+ * The following two locks are used to manage the discard process
+ * discard_lock - should be acquired for undo read to protect it from discard and
+ * discard worker will acquire this lock to update oldest_data.
+ *
+ * discard_update_lock - This lock will be acquired in exclusive mode by discard
+ * worker during the discard process and in shared mode to update the
+ * next_urp in previous transaction's start header.
+ *
+ * Two different locks are used so that the readers are not blocked during the
+ * actual discard but only during the update of shared memory variable which
+ * influences the visibility decision but the updaters need to be blocked for
+ * the entire discard process to ensure proper ordering of WAL records.
  */
 typedef struct UndoLogSlot
 {
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 0000000..1bdc31f
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+extern int max_undo_workers;
+
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e..babcc6b 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,15 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest full transaction id which is having unapplied undo.  We include
+	 * this value in the checkpoint record so that whenever server re-starts
+	 * we can use this to initialize the server-wide value for same variable.
+	 * Any Xid prior to this should be all-visible, so if this is not set,
+	 * then the scans might try to fetch undo which can suck the performance.
+	 */
+	FullTransactionId		oldestFullXidHavingUnappliedUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 860a84d..cca575b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2fff673..f9dc0bb 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -785,7 +785,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b692d8b..b9af96d 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,7 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4abb344..b220a05 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -223,6 +223,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_UNDOLOG,
 	LWTRANCHE_UNDODISCARD,
 	LWTRANCHE_REWIND,
+	LWTRANCHE_DISCARD_UPDATE,
 	LWTRANCHE_FIRST_USER_DEFINED,
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 824f6bf..bcee3e1 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,8 +272,12 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUnappliedUndo;
 	/* Number of aborted transactions with pending undo actions. */
 	int			xactsHavingPendingUndo;
+	/* Whether the rollback hash table is initialized after the startup? */
+	bool		rollbackHTInitialized;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672..1d12994 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb..6034c5e 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
1.8.3.1

0001-Move-some-md.c-specific-logic-from-smgr.c-to-md.c.patchapplication/octet-stream; name=0001-Move-some-md.c-specific-logic-from-smgr.c-to-md.c.patchDownload
From dfd0121dc73aab491bcaad2d2b7a2a749389add8 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Wed, 17 Jul 2019 12:14:08 +1200
Subject: [PATCH 01/13] Move some md.c-specific logic from smgr.c to md.c.

Potential future SMGR implementations may not want to create
tablespace directories when creating an SMGR relation.  Move that
logic to mdcreate().  Move the initialization of md-specific
data structures from smgropen() to a new callback mdopen().

Author: Thomas Munro
Reviewed-by: Shawn Debnath (as part of an earlier patch set)
Discussion: https://postgr.es/m/CA%2BhUKG%2BOZqOiOuDm5tC5DyQZtJ3FH4%2BFSVMqtdC4P1atpJ%2Bqhg%40mail.gmail.com
---
 src/backend/storage/smgr/md.c   | 37 +++++++++++++++++++++++++++++++------
 src/backend/storage/smgr/smgr.c | 33 ++++-----------------------------
 src/include/storage/md.h        |  1 +
 3 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 58c94e9..52136ad 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -28,6 +28,7 @@
 #include "miscadmin.h"
 #include "access/xlogutils.h"
 #include "access/xlog.h"
+#include "commands/tablespace.h"
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
@@ -120,7 +121,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 /* local routines */
 static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum,
 						 bool isRedo);
-static MdfdVec *mdopen(SMgrRelation reln, ForkNumber forknum, int behavior);
+static MdfdVec *mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior);
 static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum,
 								   MdfdVec *seg);
 static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum,
@@ -165,7 +166,7 @@ mdexists(SMgrRelation reln, ForkNumber forkNum)
 	 */
 	mdclose(reln, forkNum);
 
-	return (mdopen(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
+	return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
 }
 
 /*
@@ -185,6 +186,19 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 
 	Assert(reln->md_num_open_segs[forkNum] == 0);
 
+	/*
+	 * We may be using the target table space for the first time in this
+	 * database, so create a per-database subdirectory if needed.
+	 *
+	 * XXX this is a fairly ugly violation of module layering, but this seems
+	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
+	 * should be here and not in commands/tablespace.c?  But that would imply
+	 * importing a lot of stuff that smgr.c oughtn't know, either.
+	 */
+	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
+							reln->smgr_rnode.node.dbNode,
+							isRedo);
+
 	path = relpath(reln->smgr_rnode, forkNum);
 
 	fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
@@ -425,7 +439,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 }
 
 /*
- *	mdopen() -- Open the specified relation.
+ *	mdopenfork() -- Open one fork of the specified relation.
  *
  * Note we only open the first segment, when there are multiple segments.
  *
@@ -435,7 +449,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
  * invent one out of whole cloth.
  */
 static MdfdVec *
-mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
+mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 {
 	MdfdVec    *mdfd;
 	char	   *path;
@@ -475,6 +489,17 @@ mdopen(SMgrRelation reln, ForkNumber forknum, int behavior)
 }
 
 /*
+ *  mdopen() -- Initialize newly-opened relation.
+ */
+void
+mdopen(SMgrRelation reln)
+{
+	/* mark it not open */
+	for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++)
+		reln->md_num_open_segs[forknum] = 0;
+}
+
+/*
  *	mdclose() -- Close the specified relation, if it isn't closed already.
  */
 void
@@ -713,7 +738,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 BlockNumber
 mdnblocks(SMgrRelation reln, ForkNumber forknum)
 {
-	MdfdVec    *v = mdopen(reln, forknum, EXTENSION_FAIL);
+	MdfdVec    *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
 	BlockNumber nblocks;
 	BlockNumber segno = 0;
 
@@ -1137,7 +1162,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1];
 	else
 	{
-		v = mdopen(reln, forknum, behavior);
+		v = mdopenfork(reln, forknum, behavior);
 		if (!v)
 			return NULL;		/* if behavior & EXTENSION_RETURN_NULL */
 	}
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c39..b0d9f21 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,7 +17,6 @@
  */
 #include "postgres.h"
 
-#include "commands/tablespace.h"
 #include "lib/ilist.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -41,6 +40,7 @@ typedef struct f_smgr
 {
 	void		(*smgr_init) (void);	/* may be NULL */
 	void		(*smgr_shutdown) (void);	/* may be NULL */
+	void		(*smgr_open) (SMgrRelation reln);
 	void		(*smgr_close) (SMgrRelation reln, ForkNumber forknum);
 	void		(*smgr_create) (SMgrRelation reln, ForkNumber forknum,
 								bool isRedo);
@@ -68,6 +68,7 @@ static const f_smgr smgrsw[] = {
 	{
 		.smgr_init = mdinit,
 		.smgr_shutdown = NULL,
+		.smgr_open = mdopen,
 		.smgr_close = mdclose,
 		.smgr_create = mdcreate,
 		.smgr_exists = mdexists,
@@ -170,8 +171,6 @@ smgropen(RelFileNode rnode, BackendId backend)
 	/* Initialize it if not present before */
 	if (!found)
 	{
-		int			forknum;
-
 		/* hash_search already filled in the lookup key */
 		reln->smgr_owner = NULL;
 		reln->smgr_targblock = InvalidBlockNumber;
@@ -179,9 +178,8 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
 		reln->smgr_which = 0;	/* we only have md.c at present */
 
-		/* mark it not open */
-		for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
-			reln->md_num_open_segs[forknum] = 0;
+		/* implementation-specific initialization */
+		smgrsw[reln->smgr_which].smgr_open(reln);
 
 		/* it has no owner yet */
 		dlist_push_tail(&unowned_relns, &reln->node);
@@ -330,33 +328,10 @@ smgrclosenode(RelFileNodeBackend rnode)
  *		Given an already-created (but presumably unused) SMgrRelation,
  *		cause the underlying disk file or other storage for the fork
  *		to be created.
- *
- *		If isRedo is true, it is okay for the underlying file to exist
- *		already because we are in a WAL replay sequence.
  */
 void
 smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
 {
-	/*
-	 * Exit quickly in WAL replay mode if we've already opened the file. If
-	 * it's open, it surely must exist.
-	 */
-	if (isRedo && reln->md_num_open_segs[forknum] > 0)
-		return;
-
-	/*
-	 * We may be using the target table space for the first time in this
-	 * database, so create a per-database subdirectory if needed.
-	 *
-	 * XXX this is a fairly ugly violation of module layering, but this seems
-	 * to be the best place to put the check.  Maybe TablespaceCreateDbspace
-	 * should be here and not in commands/tablespace.c?  But that would imply
-	 * importing a lot of stuff that smgr.c oughtn't know, either.
-	 */
-	TablespaceCreateDbspace(reln->smgr_rnode.node.spcNode,
-							reln->smgr_rnode.node.dbNode,
-							isRedo);
-
 	smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
 }
 
diff --git a/src/include/storage/md.h b/src/include/storage/md.h
index df24b93..c0f05e2 100644
--- a/src/include/storage/md.h
+++ b/src/include/storage/md.h
@@ -21,6 +21,7 @@
 
 /* md storage manager functionality */
 extern void mdinit(void);
+extern void mdopen(SMgrRelation reln);
 extern void mdclose(SMgrRelation reln, ForkNumber forknum);
 extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
 extern bool mdexists(SMgrRelation reln, ForkNumber forknum);
-- 
1.8.3.1

0002-Prepare-to-support-multiple-SMGR-implementations.patchapplication/octet-stream; name=0002-Prepare-to-support-multiple-SMGR-implementations.patchDownload
From e3f1fb93eb7a8c5fad276c7dd6d82087135ae015 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 17 Jul 2019 14:15:27 +1200
Subject: [PATCH 02/13] Prepare to support multiple SMGR implementations.

Move the "which" decision into a function that later patches can add
to.

Author: Thomas Munro
---
 src/backend/storage/smgr/smgr.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index b0d9f21..d00b275 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -97,6 +97,18 @@ static dlist_head unowned_relns;
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
 
+/*
+ * Which implementation should handle a given RelFileNode?
+ */
+static int
+smgrwhich(RelFileNode rnode)
+{
+	switch (rnode.dbNode)
+	{
+		default:
+			return 0;			/* md.c */
+	}
+}
 
 /*
  *	smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -176,7 +188,7 @@ smgropen(RelFileNode rnode, BackendId backend)
 		reln->smgr_targblock = InvalidBlockNumber;
 		reln->smgr_fsm_nblocks = InvalidBlockNumber;
 		reln->smgr_vm_nblocks = InvalidBlockNumber;
-		reln->smgr_which = 0;	/* we only have md.c at present */
+		reln->smgr_which = smgrwhich(rnode);
 
 		/* implementation-specific initialization */
 		smgrsw[reln->smgr_which].smgr_open(reln);
-- 
1.8.3.1

#225Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Khandekar (#145)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Jul 22, 2019 at 8:39 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Mon, 22 Jul 2019 at 14:21, Amit Kapila <amit.kapila16@gmail.com> wrote:

I have started review of
0014-Allow-execution-and-discard-of-undo-by-background-wo.patch. Below
are some quick comments to start with:

+++ b/src/backend/access/undo/undoworker.c
+#include "access/xact.h"
+#include "access/undorequest.h"
Order is not alphabetical

Fixed this and a few others.

+ * Each undo worker then start reading from one of the queue the requests for
start=>starts
queue=>queues

-------------

+ rc = WaitLatch(MyLatch,
+    WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+    10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+ /* emergency bailout if postmaster has died */
+ if (rc & WL_POSTMASTER_DEATH)
+ proc_exit(1);
I think now, thanks to commit cfdf4dc4fc9635a, you don't have to
explicitly handle postmaster death; instead you can use
WL_EXIT_ON_PM_DEATH. Please check at all such places where this is
done in this patch.

-------------

Fixed both of the above issues.

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+ MyUndoWorker = &UndoApplyCtx->workers[slot];
Not sure why MyUndoWorker is used here. Can't we use a local variable
? Or do we intentionally attach to the slot as a side-operation ?

-------------

I have changed the code around this such that we first attach to the
slot and then get the required info. Also, I don't see the need of
exclusive lock here, so changed it to shared lock.

+ * Get the dbid where the wroker should connect to and get the worker
wroker=>worker

-------------

+ BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
0, 0 => InvalidOid, 0

+ * Set the undo worker request queue from which the undo worker start
+ * looking for a work.
start => should start
a work => work

--------------

Fixed both of these.

+ if (!InsertRequestIntoErrorUndoQueue(urinfo))
I was thinking what happens if for some reason
InsertRequestIntoErrorUndoQueue() itself errors out. In that case, the
entry will not be marked invalid, and so there will be no undo action
carried out because I think the undo worker will exit. What happens
next with this entry ?

I think this will change after integration with Robert's latest patch
on queues, so will address along with it if required.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#226Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Khandekar (#149)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 23, 2019 at 8:12 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

--------------------

Some further review comments for undoworker.c :

+/* Sets the worker's lingering status. */
+static void
+UndoWorkerIsLingering(bool sleep)
The function name sounds like "is the worker lingering ?". Can we
rename it to something like "UndoWorkerSetLingering" ?

makes sense, changed as per suggestion.

-------------

+ errmsg("undo worker slot %d is empty, cannot attach",
+ slot)));
+ }
+
+ if (MyUndoWorker->proc)
+ {
+ LWLockRelease(UndoWorkerLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("undo worker slot %d is already used by "
+ "another worker, cannot attach", slot)));

These two error messages can have a common error message "could not
attach to worker slot", with errdetail separate for each of them :
slot %d is empty.
slot %d is already used by another worker.

--------------

Changed as per suggestion.

+static int
+IsUndoWorkerAvailable(void)
+{
+ int i;
+ int alive_workers = 0;
+
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);

Should have bool return value.

Also, why is it keeping track of number of alive workers ? Sounds like
earlier it used to return number of alive workers ? If it indeed needs
to just return true/false, we can do away with alive_workers.

Also, *exclusive* lock is unnecessary.

--------------

Changed as per suggestion. Additionally, I changed the name of the
function to UndoWorkerIsAvailable(), so that it is similar to other
functions in the file.

+if (UndoGetWork(false, false, &urinfo, NULL) &&
+    IsUndoWorkerAvailable())
+    UndoWorkerLaunch(urinfo);

There is no lock acquired between IsUndoWorkerAvailable() and
UndoWorkerLaunch(); that means even though IsUndoWorkerAvailable()
returns true, there is a small window where UndoWorkerLaunch() does
not find any worker slot with in_use false, causing assertion failure
for (worker != NULL).
--------------

I have removed the assert and instead added a warning. I have also
added a comment from the place where we call UndoWorkerLaunch to
mention the race condition.

+UndoWorkerGetSlotInfo(int slot, UndoRequestInfo *urinfo)
+{
+ /* Block concurrent access. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
*Exclusive* lock is unnecessary.
-------------

Right, changed to share lock.

+ LWLockRelease(UndoWorkerLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("undo worker slot %d is empty",
+ slot)));
I believe there is no need to explicitly release an lwlock before
raising an error, since the lwlocks get released during error
recovery. Please check all other places where this is done.
-------------

Fixed.

+ * Start new undo apply background worker, if possible otherwise return false.
worker, if possible otherwise => worker if possible, otherwise
-------------
+static bool
+UndoWorkerLaunch(UndoRequestInfo urinfo)
We don't check UndoWorkerLaunch() return value. Can't we make it's
return value type void ?

Now, the function returns void and accordingly I have adjusted the
comment which should address both the above comments.

Also, it would be better to have urinfo as pointer to UndoRequestInfo
rather than UndoRequestInfo, so as to avoid structure copy.
-------------

Okay, changed as per suggestion.

+{
+ BackgroundWorker bgw;
+ BackgroundWorkerHandle *bgw_handle;
+ uint16 generation;
+ int i;
+ int slot = 0;
We can remove variable i, and use slot variable in place of i.
-----------
+ snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker");
I think it would be trivial to also append the worker->generation in
the bgw_name.
-------------

I am not sure if adding 'generation' is any useful. It might be better
to add database id as each worker can work on a particular database,
so that could be useful information.

+ if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+ {
+ /* Failed to start worker, so clean up the worker slot. */
+ LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+ UndoWorkerCleanup(worker);
+ LWLockRelease(UndoWorkerLock);
+
+ return false;
+ }

Is it intentional that there is no (warning?) message logged when we
can't register a bg worker ?
-------------

Added a warning in that code path.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#227Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Khandekar (#168)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 26, 2019 at 9:57 PM Amit Khandekar <amitdkhan.pg@gmail.com> wrote:

On Fri, 26 Jul 2019 at 12:25, Amit Kapila <amit.kapila16@gmail.com> wrote:

I agree with all your other comments.

Thanks for addressing the comments. Below is the continuation of my
comments from 0014-Allow-execution-and-discard-of-undo-by-background-wo.patch
:

+ * Perform rollback request.  We need to connect to the database for first
+ * request and that is required because we access system tables while
for first request and that is required => for the first request. This
is required

Changed as per suggestion.

---------------

+UndoLauncherShmemSize(void)
+{
+    Size        size;
+
+    /*
+     * Need the fixed struct and the array of LogicalRepWorker.
+     */
+    size = sizeof(UndoApplyCtxStruct);

The fixed structure size should be offsetof(UndoApplyCtxStruct,
workers) rather than sizeof(UndoApplyCtxStruct)

---------------

Why? I see the similar code in ApplyLauncherShmemSize. If there is
any problem with this, then we might have similar problem in existing
code as well.

In UndoWorkerCleanup(), we set individual fields of the
UndoApplyWorker structure, whereas in UndoLauncherShmemInit(), for all
the UndoApplyWorker array elements, we just memset all the
UndoApplyWorker structure elements to 0. I think we should be
consistent for the two cases. I guess we can just memset to 0 as you
do in UndoLauncherShmemInit(), but this will cause the
worker->undo_worker_queue to be 0 i.e. XID_QUEUE , whereas in
UndoWorkerCleanup(), it is set to -1. Is the -1 value essential, or we
can just set it to XID_QUEUE initially ?

Either is fine, because before launching the worker we set the valid
value. It is better to set it as InvalidUndoWorkerQueue though.

Also, if we just use memset in UndoWorkerCleanup(), we need to first
save generation into a temp variable, and then after memset(), restore
it back.

This sounds like an unnecessary trickery.

That brought me to another point :
We already have a macro ResetUndoRequestInfo(), so UndoWorkerCleanup()
can just call ResetUndoRequestInfo().
------------

Hmm, both (UndoRequestInfo and UndoApplyWorker) are separate
structures, so how can we reuse them?

+        bool        allow_peek;
+
+        CHECK_FOR_INTERRUPTS();
+
+        allow_peek = !TimestampDifferenceExceeds(started_at,
Some comments would be good about what is allow_peek  used for. Something like :
"Arrange to prevent the worker from restarting quickly to switch databases"

Added a slightly different comment.

-----------------
+++ b/src/backend/access/undo/README.UndoProcessing
-----------------
+worker then start reading from one of the queues the requests for that
start=>starts
---------------
+work, it lingers for UNDO_WORKER_LINGER_MS (10s as default).  This avoids
As per the latest definition, it is 20s. IMHO, there's no need to
mention the default value in the readme.
---------------
+++ b/src/backend/access/undo/discardworker.c
---------------

+ * portion of transaction that is overflowed into a separate log can
be processed
80-col crossed.

+#include "access/undodiscard.h"
+#include "access/discardworker.h"
Not in alphabetical order

Fixed all of the above four points.

+++ b/src/backend/access/undo/undodiscard.c
---------------

+ next_insert = UndoLogGetNextInsertPtr(logno);
I checked UndoLogGetNextInsertPtr() definition. It calls
find_undo_log_slot() to get back the slot from logno. Why not make it
accept slot as against logno ? At all other places, the slot->logno is
passed, so it is convenient to just pass the slot there. And in
UndoDiscardOneLog(), first call find_undo_log_slot() just before the
above line (or call it at the end of the do-while loop).

I am not sure if this is a good idea because find_undo_log_slot is
purely a undolog module stuff, exposing it to outside doesn't seem
like a good idea to me.

This way,
during each of the UndoLogGetNextInsertPtr() calls in undorequest.c,
we will have one less find_undo_log_slot() call.

I am not sure there is any performance benefit either because there is
a cache for the slots and it should get from there very quickly. I
think we can avoid this repeated call and I have done so in the
attached patch.

-------------

In UndoDiscardOneLog(), there are at least 2 variable declarations
that can be moved inside the do-while loop : uur and next_insert. I am
not sure about the other variables viz : undofxid and
latest_discardxid. Values of these variables in one iteration continue
across to the second iteration. For latest_discardxid, it looks like
we do want its value to be carried forward, but is it also true for
undofxid ?

undofxid can be moved inside the loop, fixed that and other variables
pointed out by you.

+ /* If we reach here, this means there is something to discard. */
+     need_discard = true;
+ } while (true);

Also, about need_discard; there is no place where need_discard is set
to false. That means, from 2nd iteration onwards, it will never be
false. So even if the code that explicitly sets need_discard to true
does not get run, still the undolog will be discarded. Is this
expected ?
-------------

Yes. We will discard once we have even one transaction data to
discard. For ex., say we decided that we can discard data for
transaction id 501, and then next transaction 502 is aborted and the
actions for same are not yet applied, in that case, we will discard
the data of transaction 501. I hope this answers your question.

+            if (request_rollback && dbid_exists(uur->uur_txn->urec_dbid))
+            {
+                (void) RegisterRollbackReq(InvalidUndoRecPtr,
+                                           undo_recptr,
+                                           uur->uur_txn->urec_dbid,
+                                           uur->uur_fxid);
+
+                pending_abort = true;
+            }
We can get rid of request_rollback variable. Whatever the "if" block
above is doing, do it in this upper condition :
if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))

Something like this :

if (!IsXactApplyProgressCompleted(uur->uur_txn->urec_progress))
{
if (dbid_exists(uur->uur_txn->urec_dbid))
{
(void) RegisterRollbackReq(InvalidUndoRecPtr,
undo_recptr,
uur->uur_txn->urec_dbid,
uur->uur_fxid);

pending_abort = true;
}
}

Hmm, you also need to check that transaction is not in-progress along
with it. I think there will be more movements of checks and that will
make code look less readable than it is now.

-------------

+            UndoRecordRelease(uur);
+            uur = NULL;
+        }
.....
.....
+        Assert(uur == NULL);
+
+        /* If we reach here, this means there is something to discard. */
+        need_discard = true;
+    } while (true);

Looks like it is neither necessary to set uur to NULL, nor is it
necessary to have the Assert(uur == NULL). At the start of each
iteration uur is anyway assigned a fresh value, which may or may not
be NULL.
-------------

I think there is no harm in doing it what you are saying, but the idea
here is to prevent the release of undo record. Basically, if we have
fetched a valid undo record, then it must be released. I understand
this is not a bullet-proof Assert, because one might set it to NULL
without actually releasing the memory. For now, I have added a
comment before Assert, see if that makes sense?

+ * over undo logs is complete, new undo can is allowed to be written in the
new undo can is allowed => new undo is allowed

+ * hash table size.  So before start allowing any new transaction to write the
before start allowing => before allowing any new transactions to start
writing the
-------------

Changed as per suggestion.

+    /* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+    oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+   ....
+   ....
+    if (FullTransactionIdIsValid(oldestXidHavingUndo))
+        pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+                            U64FromFullTransactionId(oldestXidHavingUndo));

Is it possible that the FullTransactionId returned by
RollbackHTGetOldestFullXid() could be invalid ? If not, then the if
condition above can be changed to an Assert().
-------------

Yeah, it could be changed to Assert.

+         * If the log is already discarded, then we are done.  It is important
+         * to first check this to ensure that tablespace containing this log
+         * doesn't get dropped concurrently.
+         */
+        LWLockAcquire(&slot->mutex, LW_SHARED);
+        /*
+         * We don't have to worry about slot recycling and check the logno
+         * here, since we don't care about the identity of this slot, we're
+         * visiting all of them.
I guess, it's accidental that the LWLockAcquire() call is *between*
the two comments ?
-----------

I think it is better to have them as a single comment before acquiring
the lock, so changed that way.

+            if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)
+            {
+                /*
+                 * For the "shared" category, we only discard when the
+                 * rm_undo_status callback tells us we can.
+                 */
+                status = RmgrTable[uur->uur_rmid].rm_undo_status(uur,
&wait_xid);
status variable could be declared in this block itself.
-------------

Thomas mentioned that he is planning to change the implementation of
shared undo logs, so let's keep this as it is for now.

Some variable declaration alignments and comments spacing need changes
as per pgindent.

I have left this for now, will take care of this in the next version.

Thanks, Amit Khandekar for all your review comments. As per my
knowledge, I have addressed all of your review comments raised so far
related to undo-processing patches. Do let me know if I have missed
anything?

Please find the latest patches in my email up thread [1]/messages/by-id/CAA4eK1+McY0qGaak0AHyzdgAn+F6dyxcpDwp9ifGg=1WVDadeQ@mail.gmail.com.

[1]: /messages/by-id/CAA4eK1+McY0qGaak0AHyzdgAn+F6dyxcpDwp9ifGg=1WVDadeQ@mail.gmail.com

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#228Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#217)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 7, 2019 at 6:57 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Yeah, that's also a problem with complicated WAL record types. Hopefully
the complex cases are an exception, not the norm. A complex case is
unlikely to fit any pre-defined set of fields anyway. (We could look at
how e.g. protobuf works, if this is really a big problem. I'm not
suggesting that we add a dependency just for this, but there might be
some patterns or interfaces that we could mimic.)

I think what you're calling the complex cases are going to be pretty
normal cases, not something exotic, but I do agree with you that
making the infrastructure more generic is worth considering. One idea
I had is to use the facilities from pqformat.h; have the generic code
read whatever the common fields are, and then pass the StringInfo to
the AM which can do whatever it wants with the rest of the record, but
probably these facilities would make it pretty easy to handle either a
series of fixed-length fields or alternatively variable-length data.
What do you think of that idea?

(That would not preclude doing compression on top, although I think
that feeding everything through pglz or even lz4/snappy may eat more
CPU cycles than we can really afford. The option is there, though.)

If you remember, we did a big WAL format refactoring in 9.5, which moved
some information from AM-specific structs to the common headers. Namely,
the information on the relation blocks that the WAL record applies to.
That was a very handy refactoring, and allowed tools like pg_waldump to
print more detailed information about all WAL record types. For WAL
records, moving the block information was natural, because there was
special handling for full-page images anyway. However, I don't think we
have enough experience with UNDO log yet, to know which fields would be
best to include in the common undo header, and which to leave as
AM-specific payload. I think we should keep the common header slim, and
delegate to the AM routines.

Yeah, I remember. I'm not really sure I totally buy your argument that
we don't know what besides XID should go into an undo record: tuples
are a pretty important concept, and although there might be some
exceptions here and there, I have a hard time imagining that undo is
going to be primarily about anything other than identifying a tuple
and recording something you did to it. On the other hand, you might
want to identify several tuples, or identify a tuple with a TID that's
not 6 bytes, so that's a good reason for allowing more flexibility.

Another point in being favor of being more flexible is that it's not
clear that there's any use case for third-party tools that work using
undo. WAL drives replication and logical decoding and could be used
to drive incremental backup, but it's not really clear that similar
applications exist for undo. If it's just private to the AM, the AM
might as well be responsible for it. If that leads to code
duplication, we can create a library of common routines and AM users
can use them if they want.

Hmm. If you're following an UNDO chain, from newest to oldest, I would
assume that the newer record has enough information to decide whether
you need to look at the previous record. If the previous record is no
longer interesting, it might already be discarded away, after all.

I actually thought zedstore might need this pattern. If you store an
XID with each undo pointer, as the current zheap code mostly does,
then you have enough information to decide whether you care about the
previous undo record before you fetch it. But a tuple stores only an
undo pointer, and you determine that the undo isn't discarded, you
have to fetch the record first and then possibly decide that you had
the right version in the first place. Now, maybe that pattern doesn't
repeat, because the undo records could be set up to contain both an
XMIN and an XMAX, but not necessarily. I don't know exactly what you
have in mind, but it doesn't seem totally crazy that an undo record
might contain the XID that created that version but not the XID that
created the prior version, and if so, you'll iterate backwards until
you either hit the end of undo or go one undo record past the version
you can see.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#229Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#228)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hi All,

Please find the updated patch for the undo interface layer. I have
rebased undoprocessing patches on top of that and there are also some
changes in undo storage patch for handling the multi-log transaction
which I am attaching a separate patch for that
[0006-Defect-and-enhancement-in-multi-log-support.patch].

Mainly the new patch includes
1. Improvement in log-switch handling during recovery, earlier during
recovery we were detecting log switch by adding a separate WAL but in
this version, we are detecting it by the registered buffer in the WAL.
By doing this we are avoiding extra WAL plus this method is in more
sync with identifying undo log in UndoLogAllocateInRecovery.
2. Improved the mechanism of undo compression therein instead of
keeping the compression info in the global variable we are reading it
from the page in which we are inserting the undo record.
3. Improved readme file.

Apart from this, I have worked on the review comments posted in this
thread. I will reply to all those emails separately.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

Attachments:

undo_13_08_2019.tar.gzapplication/x-gzip; name=undo_13_08_2019.tar.gzDownload
�`5R]�}iWI���
�"�ye#0`���� ��2�n����NJ
A��Luf�@w����n��"��zf�y�SeCfDd,7��lnn��������������p}�z����,/�'�0�����Q4*t��oL�bp�������������gO7������3������������js{������6�w���E��T��8�~�M���?w���(���#�f�D=>y�����������h��`w����������O�;;�u�&�\O��3�����S�p��8�Kuq�N�\��,U?�����Nl�Y��ac�N^/F�~���������F	����������jm��������zP�T?��_�W�;�����b@�b�U�
fG�|�U�*ZV�+=�2M���X^>����P�H]G��HUT����_��i2�q�h<�m�<U�UT�����d0�
�>>�U����*fY��tV�P��V}]75������d�?N_�\��^����x���`���m�S5N/�D�����i��i<�q��r~���4���_�fi�'0dq����}0�/������@����X_��z����a�fy��\^___Vy6x��_t2|
:�Y�����
�/Nr����z���?�[v���z])��W�2��6l�X�0�(���6�$��)�����e���Z_��s�/:���-����F=���g���
��g���d���{��W�a���?�I��}m��5�g{K�����3���<�8����2�U6�afp��a�Fi{Y---%Z�A�+5������~@(n�2}����_���e�����7��~�tN{������Wjs�:X��`������{�9���A��?��%Y�i�-M�K��	|�~|����vh|�
��^�|Z������<�*�q	���kO��o/=~���G��]:���*���.=�*�Y����tD7~�-/���=��Y
!4���z���?�������W��|��������W���L[^����k[����3'^=D��� �z��]��]-��G�;��_lllo��<>���yc�[3���'���������b�������x:��U�z�/C=B�X������M%dj�#Dj4��q�U<���/!Y��r��6}���y�����B�q%�D������y@G�Z���iq;�9J35����f�u����m^RO}&��
�

�>��!\&8�����O6������������U������H����O�@7���7��`/�l���l�?�������Ov������>`b���v67�>�hk��bE������$��>��	��]ue��o����}�����?a���������M����~~����
l�|C���g�a�}���J�e=D�����8��M��+}�@�t?e(x���ev
e
��#����i3������q�
X���`T��08;H����a���W�9�UPr�����H��~�:z?^Npx��at���O�!4��i�ZH@�/"�&�i4�J�m}WY�2�2
G
K�1�C 
e��N�	���a����$G���HE$z�0(m��r��n��|d��I��n�{�HZ�"�}��_E��DO��6N]gqQ����yd��ev`z���}!�������e��KOa2 C l�.�����l,�l��;�`
��S(5xn�������>�l?� ��Y��3/��?cj��w��y��Y4�#����H��,�)W�#7l|�O���Q�SWE1�_>~<M�&������=�|��p�������k��/�����(��o�����o��S����s�<�y2�~������MBI4��G�@����������2������v�m��I|U���Z�+�f|�����s���y�@8�q�������:j�;:���q���l�n?+K�u2����E������}R���|�1��a���m�����I'p%��Eg���]��/\"`p��7Px=�O��2=�M��$����y� �N�a�~<irHs��Dk�a3!xl��������:�S��,���NeL����a���>O���?V�gy<�F�W����%�(n��v.�>�o��A�<��M�������hg{[��	��6�_�oT�C���� ����<�3���r6(,���'4��/����}��$���&y�K��q�L-��{��\Z�����f��,aAZv��UU��>HQ�12!~OZ���>�FW�ZU�V���X���n���*�i�17�s�~��t�#�&SK�P��}%M�L��>�&�����<k�chT����j��Y:�n��������9�{U���N���b=H)z1ZB�+U�.�>���,l@�;7BHWi�������
����wQ��������}��>m�P^3�2q��At����CwW�������">��������e���o� �j�f��l�H�m�&�y��l����z�7���[�`}uYDTj��.��.�;`"���r�w�j������[c�&�q��0��5�����
�w�,Q�5'��c������z3����v�OF���+���`e[���j��/��H���,�q�^��������o����d�7�m\��~4F
���7����%�2�T?��~�/c�?/�U�_�+M/3����@����������W$*�� TM�g��H�>�=��8�p	0q�_.�6�r�V���}�4�#}�tkg����x�
����#��x���x�[[x��7�z��<y���r���R50)�S��"�_�x{��K��,�o]V
�v<]�A��!?��5�/�!&7������2=���(��Y�>����A�q���I)�����+��q>5���ycW��
C�(�i���j����T��{x� (���1z�b4�?!0����d6/���\7�@c����h�������gyM=��U�MxG�R�$����Ut?N�WO&�p�)}u�No��
����/v�Qs�������A�����W=N���}���1�N[��KR����c�Y��� �0�$���h��a�����{��=9��.~2�zhZ5����I���V�1��(����B�i�t.�����y�����R����
����@����tV�����/6>�'f�w�8��%M�@O�KK�����v���?�?����oOzG��?���p?�x�������=8��_t�3��n�=��C��y^��w��q$�l)qKo��W�y���g+mE#���'� �7�� ���|���M����~z��=��f�B� ����(���4�DE�������@��r�=?�?�]���Wc_�g9�S���)-��������a�?u/���B~���M0���h�-w`�
/�^~:��W�@R{���&���,��-Z^���W�@�!�3K���s��m�ygL�����C�?|�����-/���`����K�z�!W��
{�������w���O�*W��+������'����K�F�K�p�u�c�\�;n#L��'���Ow76��7�>y������r����#��91�����q�B�&�q#T"���&����K`)o�~���_�!��rn��uX�����Ys7M{Z����'O�ll<������r�\�5SN�	�B�w�iI��]+f�����u:qF�=�?��j��Vh����6q���V�J}��gS���������$���uz��'>g�	���})������X�;E����-�q��E���|YP{�:`]�[�'�_S�Sl��F������K�I?eq��+��o��w8_��9m2KvP��_�?e�����7<�d�����g�����s$eE< W�eu`���y���t��G�������0[��O���{f��<���MB�E�*=?����l�������gG|q�;o��w^Ei(�k3��}���lo?l>���7C-�Lc<�m��L����9;�!�,����Y��jSD��e��n���V�!1�=4"��Sr���@6�� �%(b�$�%�Mh4t��.I��Twd~A�q6~�O�g����K)����-���jc�1F/p�G)\�
��i
R;�WAw����,��&h<�����9����,H^x�5��7�����
���V�����n�����a\"�^O��������b�i�	�����,��L_z���"�,2��l�u��f��<x@3�����,��/���jZ�;Y=r��Gi`z��x��������S��o����.XQ�|W��x���K,��0�+p�e(E�48�a���a�`
���B4B<��5q���i�xf��F�QMK�f#�0qa`��m�������t7i/�L�
�&���� Jo��TF6s:��'���*�K]�c%�iG�������J-Y0�����4�H��L�NS=�w� E��v+;$�k;�����!��q�Z~��V�����[����3�G����J���2h���������@���5F�~�wc#���e����`���-���F���S�����	:{�%is�]	H��Q���r�P�r>��x5�8��)��)`�tDez�:b��G�
������������S
���,o�}
�*�eZ�F���i2��&o�O�@z�^�}��<�e*�����Y�}���1��(�
�6���������T��}������sv��?>�}89�	�_������������y�5�"�`�\�;j	cPp����0���
;����7��g����p ��v��wf���t��{�O#<b ���~�����
GbXC6�(�WV�����F�y���Ll=l���G�%�`�^P,�����S�L@r��\�W����i�Gx�ZU�E(v���1
iW$ ��U[�u>�v�������N~>�?�_��a��Q�����3���gu��������Gh������
�����w���n���&�r���S���cl����#�fa�>-���0����J�"�/4)��5T����LB�j��`��|0O��a�&�J[����m���w�����s���y]�����ouQ�s��k����d���������E��<���e���48�������%�
(Y��}�lF}�\����������������
��X���>&�~��J��i�.��7C���.dx�������{�����?��l���B�_?N��)[�q%��B�r��lM��;�����45$��J�h�x�����A9�)r�p����<�Q�c�����p��9��`����r�V�u�U���q��Rsd��*����03�a����F����I6��X���n���S��^{|����Msx�� ��^|}����!�6��^��@�R��P���dG������$.o029���O���=8x���6�!�����\�"�������eYt��>���2��w������k_9���5��"U�S�L����9L�b���(�$*�����1��k��,A_6zhiG�<��^�E6(j#j.!sBE��HBr�~>N�DP�-�]����1v�a���z�b7��&)p����q��p )j��%���L�~���6 VH�cf��F��=��Q�/�,�9jNK���u����W��)�����{�[��DO~������C��v����Q��M���[u	{|$n0P�KJ�C���]Bo0�*R6`U�U��G�A}�(z���(xD������
�,&�B��^G��CH��V f'.����2z���Z�f*q!]$^lM�!uC	���$w��g�R�_���i�o���
��V
���:��n��5�3�e\�E������<;&U�7.�H���8��#���k����o�����Is4�	��xVY4G�)
��eNk��������3���=f��Q�o�\�i.�v���f��]��y$��v�������H������S�gW����������n=�r%�? u�IU���0������+
�[�k0�5�n�	Z&����9�/7�L�)6X�#�:�>���"�����da���l���?@7��&B���������/���L�� ��%%l����"�<��Q��z0�M�	D���l<��2x�U�i��B����8	`Q8bU[��l�Y��2�
�������@8^4�G����Gj�d26C����*��k�H��$�D��$�aZj����fV8�t�� �UEv���V����������m���MM"��������@��?�����1�����������5t�,5v�s������J[Uvm������!$�EM{�y6L�����[��mu���n��`-�����!v	��eE�;����6���?����vb�b����7DRve������Q���Z�F�c����y�7�K��f�t�2����V�m)!=_e�\�l�53%pn���%��i��#k����1U�/��zj4���w�6,kPj��T���

Md?��6�7�T�����O��u��q6���G���O��r��c�
W�;����=�4�q�M�l.pP^�`�>�8��4"[�R0���M������
���N�&	�[���.(z��RRW�����s~������k!��KC�R��o�K�k0B��*�������+`VP���+EQz4'M�a�C3F+���k�H�7�*/���W���v�����}��O�:���������'�	 \$S��QFo������N�/���$�Y!�0P��5
�C6k�������f�g��u�=�\������@��n��8*�0�20��B*lo�}]^=���0N=��(��?��N��X�]��n�/���	 �+������3����a��n�AU��+���;#r`)���1��h���tI�o����n����:�z�o����h,2x��:�����}V�Z'��L�DSJ�5!W�pV!h4}���y������^��������<����
d="��TH^�&����d�>�r��"fA��j�yoBcj�)1�|�������f������-NrZ��z�H���Xl�C"G6�U���SE���o�OqNt���dV�"�=���HP������5��Q�J����Y�I	��=�<!�~���E�T����9�h�%�P��!���-~���T��T��Q����_,qs!!kk�c��j9�_�I�"�%�[���.����d'�GX��Yd�}�]���)���X���@o�:vc�s �%V]��)����K��I[pA^~E"�����;��N`JeD�(��02p�$&a�+\~����!1�:{h=9�����?5�7��������MZ�F(�RF��8��W)q�M�MK~��?������%��kK.)��Vk�����9���i�t+�������	�%	�a���x	�"��U�2W�H���.�H�F/�������*��m��2=��q��<'��+��C4�
POe"�H	dn?�$1��4g�qI�#�����T�-'4��d��:�=�[�U�I7H��I �Kq���2�E
��lB

��.'�����'��(H!�Ml��/�r�7�4�[m����'������"��s�)G�
�2����7s�!j~�(���n&�%��q|��X�C2��c����{�
Lxg�i@ET$u��Q�~�U���5�����oPy� }MV�\�*x��0���#��.���B�&b���m�	�/��o�vGFE�A��x�7���B�,Ow���3$�O�H��]<{yF3�����H_�H5!��*�mD�]d/�L�bM��\��60�r�����V�_�����	1���{�Y��	�g�k��rIpA�sQ� �54J���R-�G|�=�����:i ��U/����yw��3^l&)1,���0�����G�y�y���*�����*�T�|p�Iy�
�J/1�i��^��(���2�I�?MV�Jc�y�CR�����k�i`cO�"m��?XZ�Xj:�RFPL@-�B-�6�,/PQ�0�������c�i���8�����a��Y�YX3�������{eu$��^���w@a�������?w���b5N����n�v��w�B�l���aF�����E�^�_|<��~�N�E�~�����Wj��
P6�l�1�M����d3��\��}��������������!������Q�bT���sc#tx��b���C76����������<�;�j�R�������������{e��7\������;�\�	\O��m��B���/6>��-��p{��?D��vS���������Z��j�jOC�*�{��S?�D6N6��*����G�3*1�B��Y|�zD������������=�z��g�|T2yj;Y�:�k�=�@gG��PCf������
����(GZ��3��C��#����&9�Ect��5v�\��ALY����)*��R]�����A������e����"���9s���#WB#����HK)�y03�����������<���w%'��eLk�b��&�;#���q�D:t�?8������V�:�'��(�0�YW,�'T�&�����B���rF�	\`�l	pwx NZHZ��t�=��	��T'De0�=�����wv��L�
?PB
���]�M�x�5�L���j61�����ZA�hHwL��z9D+t���L��0Nk�x����T��u���N��|�a[�E[���g�>�X\��2�
;����O��:h�I
H�@#�~�'���C���H$����|�g��E"������z����;8�I~�Z!��w��m�_W�����R}?Y��BnVm&'�vs��#b�D
u'k?f��xQ�� �"������.
,�^#��3���G�		���%:�y�-R3�R�8O"��F�2��r�������������F�)3)������g�m�����xm��;����$.������l��4��Dx2A~O!.�����W�aG�.���������qz�\^�#��J�#���������c���rI�Q��L���UV�y���i�����������������l�0*�l_)o4�({���p�ob��yD�0��y ��pn�y�E����������6X`Fbi6?�4
[�3mJ���o0<q��C<~[���X�fd�Nj�U�+�����v5JpK��Z[|
��M�]_{���*@��1
�l0N
?��-R��8j
��T<8-�)����Po��}d	�+��o����0N�6������-�-��C{d������F����f>8��I�
>���8%h�q����!:��E��qux`�DP�tqc�'����������^ �D�d�����s�n��K:���hi�����(A�0 X��.Z0����n7)b�>�����"�Q�x��P��j����=+�Q��D�eIbxZ��F4������P&�+��+��5�����%��`�b]���X��@?eTq��Np:&�3QV�"�Ek�]
`n�x����@���?x���}������,�������`���4���xDF��@���j'V:h��<���zx������*�X���?I�ib�?l���[���t��x�>������/E��Y"�X:>@U�y�I����(0�#��gl����:<���U
O�j��1�<����sB[��$jvs@$�GI��r1b#�����Q��H@(W"���a����&����-��^����(N��)�X��A<;RR?u53.�'%h�K){���^�Na���bYt�T.���
�Y�uF	�Z�����0��~�gj��XgG6�g��]��~��Tk*E�CS.�hL^�#�	p��!�� 
�c6:�J0�>��KY����������X���pf{��Z�}a&}�0ab�f|����������WY�����YAo8��"�]u�(�"f��QPx����<�2=���O��p�e���a���WI�-�If@��$��������yk�
�A�������;lR>3Dx�TC�n9��0�IXf��1r����1�Q
��9M������e �;&���\���z4���*Ya<��mQI�D�?���<��k@T=�O�����]c�)Lq)?���B�~��@��U����<(�.Nl�O��yy���;E�u�`�Cb@�����[f���m��<�]1�:+x0l�e����Q����y�[����U`"Jo����)�@n�&p;ou!=��c�kj��-�XP@6A�Y R�
�k���a7Mz��f�T�����37�7�#>���K'X�;��@Fev���h|��j�����NeL�������`��4��I�1�G����/4��0���������D�� 8J�u�-���nr��zJ���F����$N�����r�d
!a���,a'���5�-�����!�}HfM��12EG1�-6Fwtm5��McQ�qQ���8�|��N%�������+^+��6��T
�
����o�;w�Jy>�k�$h,����D<�S�nb?'Iob=+���' �Q���|'L��4a�E�0�F������<��z��No���oW�OK�0%l��P��������(mz�}���%IY����S��s��n.���z����m��&�����4\�`Q<� ���I	�)����"kt�)���,�t�i�4/�+�fnIkpGN��b�X'b�1�M��$2#:-�������a����D�91��H� �8��s`�=�j���i�o��+�q���r����D�����7ZH��W���`�s��=�� ols�h6�����8t���%�\����#d����Xy�yO?*y���h��P��M����z$�d*.Ys}�C���7'<��8�%-��Uf}~�+� ����&��&m�cBvN`p���k�|�z�F���
(>�_wjw���O��"��_j
����}��7t�"��7��5g$�b��7&;��
��z,�/����%�H����y�i��5N��s����x��W�y
�WP�O���G�&^�%3 z�
��,>T���t_��`��y&"`������r��
���l^'��P*���c���0�����&�y�^B!_��?�����2`�4@��j(�'�B2�u��it*�X��|�1���[q�3J,������4�2v����"7.��M,�W��C�I��r������''�?�������%_lu���� )�E\�1n����D�`�	����Z{,{��`�� ����1�pi���sfg�IF1|D^�k��p����w�������|�u�6-}v�q����M��[5���[���1�g��%��,����t����^x�H��(�p|��	G���VN1�?��|�������uQ�L�BT��&��g��lUNiO���������]��
}�K��U&b6#��W�|W�'�b��A5��f4L�(������F�N�5��2q�S/<��w�I�l��;d�p��\����+5^��5Qx���A�/g�f�C���
���y���Y�� �
�7+�kc��f��X����g����T�i�&�!������R�x�Gqo`3�!�ixJ�;��\gd�:��TM���V��||�+x�0#It2U���8
��'���	o�N��C�������ld�O3��o
r;�t�����
�P����839^p�,�@�w�C���?
�d����� ���
����T#dz�T����^�h.@OL*�qm�����<2�%_{���T��0U�����J��Z��KSLA#������V��?����|>�#$0��I+�b2RF����%
�E�Q~�������,�4�shp�����V��@8i�(Q��to5�x���{(���z���r�r��-O%|����
�-<��Yy����l�SE95�t��]
Q�vtQd�������C��-�.�j����ca:���;�\����������y�WJy���m<g������O�P�opm��
�]-g\G��������*]I*�%q���z8�0��%��S��5@J�o��F��������HnF�j3���h���)'>�q$%�����p9�L*.��.��U!,U4�����*��5m	����t�����S��}�&dw�F���u�.g�5��U���yI���~Pa9`p�5�6�w�����a�|��7������r�*s?C4��w�;��u>i��0_d�������by���L�Gzp2���`�
t�N*e����U����s#��X���(����#�0Cx�����:���/�V5X���Flp����j^����������Z�^��+����{tA�,40`���H/�h�f���[�,c�W��-B���Rs,-�yh�����_�����\ �������<���n�0e$�#��&R������ ���&�8��C&
�2�j�Zx�C-��^^�Y8bx�1�M�S�� �;�
��:�G�X���SR������OO�"���kU���@�j����t��m{���r�V�(��� X��DW�6����{�0��v?�V�<�(_���"W���� ��2kW�0�L��I��&��I.�l�0L"�rM�����::{���C�R���gS�dFi���i	J�x8������q�0$;:?�$_�-� �$r�����5P��0��b�FI����G��l~�5pQ�X
����D��U�1�L)���wf�$7�K��q[�����_�<<����ew���k���e�������z&N���l��yIy��%�b��_�W���5��1(,�Er�� ���'Ru�Y���c<B*:����C����'�j�)�������\���l5m�je?��������&M�)�������(�������PyMH�� US�� ��Lf����)8Z�?��N]���s��0�%�)E
�B
�����4EYX&;���C�v���*V<�MiE~���7`4C����"�v~��z=���(�|h$�e�����#2�h�Q���t\����,QN����6����|����v���s��F�-���F��f���PF����c	��1~a��R�G�8����6��,��AGE�f<tq�h��Kt��h����3��������
���U76u�a�7 ��C��J�����Ec�M3*���SHL�/�j���vq�%%&9�+Sd5��1��M�C�MS��d��n�!^����c]^�bO�m�����6T�:�?_h�^ <���hZRE�����������h
u��0��U��������Jv�gbX
7l#P����vI����\����Z�d�L�
���b�������b�j��-�������{������Is��������s�/d9{u��F���V0��c���1i���.��&|��6m��������p	)���q5�jd�}�����+�
|s�9!d�<���_�+^��.��~�3@1'tJj9��U���'?[.	���L���vy��#������~v����;f�*;����� �����rO����
Uo�$cdu.�n�QiFO8�H BD�������N%�,IP��`����5���T8�� �m���z����ng*Jb�]!���hK�YU�?�����v�'�U�A�8Z;�H�����&���@|WX���(f�y�,����� �O��+��o�QW]�{�����*���c���.�����X���~��uNs����c[j�!=l=�\V���S�_��Ik�m���&
�`����H�2=�L�v�P�4��o��-�b�Wq ����2��C�p5~&�x�W���a�R,�_����6�:����4c�e�����"~�)���:k���RS���A� 7"���$3m=S�C��
~���L���d�����M�
��R{B��)�&�.0^�;����i.%���X���b����%�c3c�����C���<D�a��Z��#)�+-}3�S��*���NE���
2b�s����SeN@�Z%K��j���	f�J�L���%��o)��������s|k�V���E���[�J^
�����%��a�bH�3�;�3�"��m�y�������7�����[+�x@��V#g5�j����H)���Gw�f_$�4��I���!?<���#gKSm�n�Pe/	Gv�!���V1�c<\�v��|��#�x~	�C�4�u����^}��KIsPG�K���J4�9���&��]���G�����fpm��s���_�?j�������jdM��>T��W
���W{|���������s�.�����m�V�������ekWu4"����]��Z�Y����hu2�l�����~�,��y����4en���$�%��)����(���K'Q��U��\f
E[c�}���t�C�!���3������_�F)*ok*�����Y�1#�k��E����=5c*����[j���������������deA�hc7�n�P��Q.�����WO���!�b������Zh����.�W��yQn �\��:���xvac#0�D>FjFB8jK|EdI�\A&�N�;��{�>�/fo`7�����~����7�.�G����9M����/��$�~7�F���{d3�mJv��4�}�" ����3#~�1�<��6��eC���B�Bn_�������b���g��Hf�����qk"f�f��J�]��DV&��1��etg�/<�_�A�}��e^ST]��_B]��������$d)	'��	�����g��e��*K�t&�y/i�e������7�����iYWW��-!Yj���L6K����*��J�A�e��O��~1Y�d��h�A���l�=���v.{n�������%���B�@�)b��O�J��$}"'*���5��M�-pSS����H#����(w��w"�A�B�e��2���Tba@�1A�Wd�����n��V1w����.����T=�\4���m�#��		�Dva	���;��HMz���������7��:�6SPN,���c��'���M�����y�Y<-�l�:�^KA�!x-��G�+�@"k-��������pG�Y��������
�����
7��$��/9����!��+�C~����
�k9W�w���z�;\���5����r>tM�ei�27�Q{�]%K���7?n����+X�==l�"A���g�>�T@�(�`�A`
����j�Fc�U\M�e���&�qO�^�T���gCL�����p�W,��[������6��������z���Jm��( .
�����B�0�g�:N�{�l��K�'�e��9��e\�*s�����z�����vm�b��$�
R8UZ(S�J����`��0~&l3Nm6lg��O>k-�a_�j�b�����������A�������UL��&��8���������_:�H���7Uv�u�.P�m���#V��v~B�������n/������;��-�����&"�l�'Rj���9W�0��(�2����67�����5�37�1�Y_��=����H8�j��2K8��:
����,U����'�lp����dl��]�|s�%7�U���ZB�H�s��w��e/������Q�	^�`����V����CZ�J�������
���QaT����%>����,�'[��CRs�b����Kv}`o����tb-d����Z�j��r��"�3�i:��If��StJ<�"0�rx����mT\1�#{�3g2)�]R>O�/$@h���C?/�����������3��/{B���H��8f��r�^�[���_��cch����ZQ�H������j�e��������&�c���Pmo��yC�j�$�E�h#|5��:��i�=f���C�~eM��t��<�����$�6���_��~Q%��}u��QQ~�P���*��#L�g����z������>��<����R����D�����8��n��<��i���� ���{�6�-U��%�3�5*�iG�i�kR���k�j���T�l��������z�Q~8���5������u]������]�(0i���9��WYj_���_�����?u/�v�����b���sy�c��_GWs]�x.B�=��F"���Z"�LGsl��[c�	�X����w�qa�z4��B����U��iGM/i�������3v�����1��M�-�$�rj"BH	2`�%%s�C�S�:��&)�'��N�;�<�[�6������a����r}XS@���
�����
�ka<r�,�&�J���,�D��O?,�dM��<�����u�V�f�myn8u��8���������D���$�fU��� �7R���
=�&����W:��_�Y��M��K^�j:��9g��K9��fS�Z�������Z�~skwEu�/vwzG'������Z�,��2�r-Q�pzg�f���h�M)��P�O�n�/�^rf`V�
?�
�:���
����q�o@�2�4d������
[;<%�1R���Jj����0�`B	��!2�#�.%�1
�rO��mH�j��E���`$�����#/����r^��Z0��N`G�_{��Z�<�����O�'p��������N�yw�^�!��'��h"��[�c�"�P�}��<�Y����������sT(��:��~���;������S1�&�}��@["[;9u^�
���-�~����&���_\	]�J�.J�#���8\e��9��}���������W�N�<�
H����2v�j��St��y����������W�eq���D�b���K��8)���I.�o�B"�#!���p���p4�?b��%8�A6x�=P�OHQ��/iLe��s���K�z�+l:e������w��k���?���=�B������"��<m��
��(qEhvW�J||gZGl����I�}sYj���H���qd�����}��������r�k����E[�����A%GS���pH{�9/����(1|���E$��Q��SX������3f?H������O(�W�}8���T�6���1�N� E�8g	 ��^(���/9��	Tq�Qt�B�4�K`�\��1�YsK�]+'B�N7H~V��?0	�������N�K<e�O�W��:��$u����j��+>j����5���X��]
�w{������_yaks�4�<�vGz$��oI��9���\���J_s���u�������u����P|:�^�F��
����5����{�\���1Xu�~�N�pV���w�/���1l�������P
��������]����g�^�4X]l@����Z4
i@��`p�����:E��F��f
	�g9i����gk(����8�n�'��y�
ZU�:g�1���!��LG8�#����[���;�c�GR�V���G���*�B�k�ZU�j���&�������A�+�HWWm��dQ\������P^&X���X�r��|��,U/������o��+o�K|4k������Y��P�_��^Qkj��OX��8B}�;�����{����x��jH��m��Q*V*����o���!q�#;Th��y�X��H�f3@hy���V3E����[�����{A��s����t���It�^Y�K�\X�������d����M�7`
������V��oR�s�,��f)$�>��m�����`��+�$$Y)����-����q�'��r'78�{��5�����"@��w����:��04�u�s0x����!U�}����������:>�}8	y�*r/��������_]�������	���|Y�]�]a�����7��Xw7lx.w��qo��7<��t*�j���@���;_>$�P{H|aU���t��W&�����l].T��� Fon��JV��R
����*�@����	�����jU18��
)������+�X�j����c��c�YB\�!�)�C��@7R%M��DM"ke 
���U|yE�oz�������yO��S ]�/��-��o���]���Fv��f'�@����y8�g��Q���F��d�J%mH������o
�4��0���������\���,���`����D+���3����M^����R&62��:_��Y�!c���(�=Z
p���w�G�@S��9�
V���$eT�.a�a�����	?���SL�%�]u��a�;�T� ��h����1C&l�X`��N���d��W>�h���,��^�S���*3}��s����W��{%V5�������5b��^DNh}��[(
y������/��U�������5�5�����+���m�K�0��������)n��y�p$=r��J�D�����|D������Q��������=���{=��=����f�-qOK������'�M����j���`�D�5���^U7L�$��;h#����[����S�d2g��m������fu����g����.�Al�3M)�����c�5���sL�E���J��Z�#���� ���pbj���@LU����y��W�zX����������{:4�v�\���Xr)I�v@NW��Q�U�����s�#���Z���������f9�+�x�,~j]�y"�����5�/�9����]�O\6�j����9a^&��-�I�<���*�l"Pcjw���n��������/(16������T�[�/�aQ�K���i�0j.�_��iBj3�a��4�5VL 3g����z�t�Z��a��[Jr����\��FP�[�m���ML���2�o!�\a�}]����3�
K�-j`m>�[�q\���$x��O���yLJ��\��m���%RjBVO�L��Q����d�'�������xL�H��O�� ��F��N�C���e�?�������VP���`�u?-+�����R����Ot��Y�����.c�i<<*{��-�����d�$����	��q$����l�����f�\{H`�������suo�N�����48��S�FW\�S���5���
����
��m�&{!��`���~$��
v�Q���Qq����&`�qOC���e+C�R��Y2�N���r�t��������`��g0��m%] v6��vN��.P	��?�\�&�b�a�i�2����E�1�K�qA�
9�8�Nu����)%��@�Z��#26�;JO�W(	\���!��r'���q,���x,��`����"��$`�A�=t�M[�3��~#�H�9�,d����dl��K���8��t����DGXE,�W!�(�!�v�;{Ke�f���{���{e���	�K:���-9��D���(<LX�9����������/n�yF�{���h���D�}�I�y�t��U�EYi �_�;MX��rg0�$>��)��q��A?%*����Q)�W����.��Q-�r�D3���N��3@
���{��ED=���@���"!�f������G�t��;��#��|�o(�����?�&b]�*3��C�5��t�F
PN��e����
U
�)��yrVH?{�h�)O�����Bx�{>�B��n
N����1_�O&����_cP)G[*�@�f�p6p�k]?��8������i� ��+%�]2M�Q�$�P8���c�d�R�vTI�E���	c��e�g���XvW��>���hCY�	�s_�\���
[17�~��F4�G�>�6*�M+V��nT���4���-�&�R':��U��p�%�a�~�.��n�X�����A#��heN�s"4����u�En���I��y��[c(vx�xml�c�d;3��1,�������4WQfK�������WG�Zh����6��Qg�P�SM��8)[�}��H��,io�y�IL�@/���%��Y����`��M���Tu�]pw+�tJb:<�rR%��#|�j�P�l��Zu��3��^j�e[a)B/��=+T8�>~DeO������y|��{������y��Ax�!	��bq�3+����R4���hk�4?�J�,��	HT��PS8�� �L�9����V��mS?�$�p��?_�P�{o��-Vm�L
�Rl)16p��t�������z��^T��S���"$K)��'-(K�����x�/Z
�zS3B�lX������d�'mJ��v	#Tx0�52���	1L����t�ZOrNi���d���{G�E�����f��J�������:-&�������,0���+p�^r�.Z�'�x���"��"��0�����RY,��%�0/�_��JA�$��|���ee3W0�zc��5�C��/�{M�60S��5��������x���`��-	TFU~����,�7���Pj�Ra49WhN�o�_��n����[�\��U����-�]��7;?}d���k��&N�!������Jdh425�K����� J�Y��(d/K���D��D���C�0�����KD\�;8�]�VG�M���	�9�<�En��:#�����:2�w�ey�3�a��x�B�m��� O��O
�7c|���'�"��e���o���C������g�v��A�no�����Nv'Z���[�Wh^	\�k����_��&��&��{����}��9)�G9m�����Pe�m��`2��s}PI\flM����Cu�N���1G�RNl�������������������$Z����R]�p�6���zb,?Q��G��~�]�J��i�6�aU3�}�G������g�Q&)G>�M��O�����a�r�^�
��+iMc�tj.����Pc���\H"�!S����9�4/��\3?�Oc&yl����v�^YGm�S�|[�E��P�d��
�E�S�!�������@gcQ'����t	��z������#<���Y��c�
������c"�����:���J�+��zWi��%�W�1w���]u&�Sn ��:E]����Em�n68Og^N���*�� ��1
��0C��re��s���$���(Bn
�4����0�<��-�SI�<�2N`B��1������$F�$��Avd�\��x��$H��d�o��pX�.���U�k����q�l"��%��v��q\t�\�i�-Y^[=l?D�w���S~�����k��:�%�>��=j��nzRB���0�$�G8 1�-;	z^_��l�Yhf���#y�����3Ayt_LB�m|��O���{h�D�����5�����\9�Z?2!�Fk���.��c�����z���t8�uG��mk�D��w������N����v.z�'��q���s�������E�HW���Yv�r�����r�����B���������A	Z�m$$���#�z�R2.����"�K�3j	1�Yr�r�w��Y .VG9e��sH�^�TsR�eT�������XE���v�{�p��B�j*`���Wx�!�e?�j
yj	�����e��B���6�/����AT�8hN�S���)�~������ ����6y��!	�KQ�j&E�@*�e0qY	E�j�U]�)��Nt��RU��&�N�@��;3�Y"�< )��+�:�,�����g�l9�F���D�����!��
�.�����LUl
o2`&_�<��L)M�o��P��o!!���z�6M<�{��j}�T�c�Ir����I���|XS��|��W
g^�e�2���0���E(��5R%����kR��������P��o�>�������b����������!Q�������2��{\b3�X���g`��J,�*��DUca�
G
�uo��D��p��)���D7������Ea-��S�_���0H?G�@v�?f�K�vt4
L�P�P�����^�;'�����LK��^��_ZJ�h�����w"�,6��K���g�2y����2{.����8����<_^Sj����"���HPv:��xJRskC%�"w�UoF����	���<Z�cOg����\!���\�������ho�/���y�t�1�����U�
k����������g���O�����$G��|P]*�������5���]���fV�sj!�hr��"���
�	��M�=�Zv�(��@���i$��WO!�?`�����=L/+ERp�dE��wWlM�:�_
�
z�m��s(����3�������HsH���6�t��h����_�4���2"s,W
@��y2�X�_�6�����������!J6�"�{R������?t{��g�G���Y~��q�S>�nM������rz�������u�z������f�)�=1|(����uq��we�J�R	��'/;�r`[ �v�y~�k���;F<E�����P"���<�<d���)B)���,W��)�X�eg�:�"�����;�d��/����zM����&fj�aw9�6Lr��I�u8�IMd��M�d��
|��_`�U���-�X/V��wG[�.��3��� M�9�g�o�/]��
���U������50�%������%��O5d��BxC��l@4dr�j�a��|��e�*	n)#-������+nP��&X���
Uy�nH�~[�s�h������'�{
�-���T1U[���������&����a�� ��7`!��i@�NK��\�W�d�P���[��T+��m���,"��n����h����������n"V���,T����(�)��H�%�Q��.����_�6�L1�WV�R���(A5��M������">X�#��rqQJ>[/>�
�,W]��Q6����>w3�tz��4�$����=����V+���f`�8�.'?�6���=K�Yl�S�1���zJ�w���e��.������w8��MJi[�����m�<���	����P����u���j�_��G��>{���2�^&G�[�h����&��uf�����|p���~�C��)e)��r[��L��5^<=���L�u���@�sk\�%I#:5��2�����-�4�	�������R�,<w[n���U��/d]mx�����?����i�Cmr�`e�����x.[��lij"�����~���CT��E���,�9����P9��A�e�DU��q����s'����bL�[��L����}nY���h*���':�j����Du�i�3E�>�/~�nZ�*@�%����M&�/�X�����\n�7PT�������j:���re�F��X����q�4�3�
%AB>��\����
>��h��)��!W6���	����x���j�*V�j$A����U��@3�62�D�j�l�H
.;��p���T��Z��7��G0�K����S����� ��,��pP|���u�\>
�tNm	��,���:��|��=�	OX�C\�	��{�b��K��kUW��}�Y�\�0�x�*��d�e��(��������v&��]��g������hC�Y�_�7cU��U_p����4.����L���r[��o����c��j}�4
R�B{���N��\�|�'4e��*���U�=&����yc����&gveyX\_�4�m���ZK4h�Ndi�b���l���)Je��{�o�O"8���^�&�E+��?I�WR�(_T�G���$���Vn�,-(v���/�-J3;�
��L	����6������i]�����s�6)M�JD@�<�l-1�P����O�����]�-(�����_u\�NN���(���N��7�KR�$�$
�����I����p�Y����Q�2�g(������!$h�
�����k]�����5_�Q��nj!�,�M/{��J��'��w���.�'����w�L��eW$�S&��wI�
@�;8�p�^�8��$�~��T��j�^���_a�����q���X�|�T��C��>���0G�<�w�+�<�<
6�Y����7(n����p�?Z��g���
��3�hA�\`�1����I2��&3�	��MQ�J��B��c�4/�l������������
v���
�$����Y��)�?����]�T�$,�:�@��$��"Q�y�b��X�iA:���f�!�x�5��k=����z������� �^����}���P��M���*�ciE&iJY��_����l�SS�Y<-�/����H�@�S�<�z���m����t�m�@�����_N;x{����]��g�����N*K����8n����8�	X^�����	���>F�%�R�i+]�hn&,h��)������l�D��1�~�`�L������a�o�vf����{����
.)c��^w���Z�T�
�n���������wN���u?o=�g���n�����g<Lf����0�����[h������p]@CJ7�����)���M��-�����S�%3}�b��O,�_�9G4C������M��F�z�iR��1�T
X��8Z4G���|����Mw%[�]T�QT�P�������Z�W�R����>�+�N�p]�[y.�Jr�9;�?�_��
��$J`#W��;{~<�Mx�9���-Z��E���z�%�n��0��X�}����x-Q!�b(_Y+�����,�S:�E���P�{&���I��W��3fV�_���w�!�����.2��Jc_�����[��"Jf@o���Z69�&5
�a=����~��v�����1�j���|.'�Y��s��]�����?��i�W�1\8���jG�t=��M��Z������.-�� ���&��?Sfkpd%^4Y�$+��3V��3���S�����W*�U;^�ehaJ8��S8�����3�x)4����Rf���$��+�czT�J��T�,4A��V
���:}7������f��N%{��&7D$�;����aS��%m'�f��3T4-�yw�/������P]��E�R�4?���8��hG���.}�	��qQ�R��4,�s}rQyW��QX4�j���Z������K�3�0/�����d��-z��@�!
S�g7��]��M���(�!��D��zN*5y�4�;G@C���=���@���/�/�B������5UnZ��`pC{N�DwN�?%�w���0/�Q�r�����&���W���_A�n������3��~��~M�S��sx��W�&%1upI��hP��(��1t4f*��1��9��s3��\�mVU����3��k�����E����G��

7�,�P�1�0h^gO��rv_!���
�Q<�.@�l� |��k����S����T��?1���I�&�-�mKbt�I/y�|���5.Bh��p>�0
��]c�	x$��N,�}���,1���W8��
n��9N�z)KR����}������Y�,'�����x�M�?<(z��|8������7+��_l��Lx��1kJi=b������f�;b_�7t0fI���
w��o#�3�	PP4^�����J~+#,���2kf�p'�����(3~-��/��(� �\����<��2�<RsV���U�x��P������"�8a���&�M$vG�OJ���W���b0%�.�g+��o`���E8�d��%M{[2Rz�a�������,�u��YB��u�#�#z��b�pv�]c�S�DI^sS;���x�\	8n4������:C<�kl�8��M��L_�4�X�d�2��>G���o^r[��������	l���T)���(����[�L��x�q�&����H��R������Q:�s[.����S�]�D���dZfe-��	����!�<+���t����g#�g�Bc�����IG�Q�9w��X�7��NR6����Y����&�|~5�����I'�!�����oD�&��\���,���+�O���H��.!u������@�y�#���'���U�^�����Z���������U����r����d�F���R]���}Tx�UY�/�K���e�X�����M��Z���O���R��W�M'�n�=[�?}o�{nj��
�v2����a�e���]1���A)bot�'���N{�@8��d����e*p�����%�)�U�2���
��K*F]G��F�o�f5g�T���Uw��P+4-[��V�{~N���Yh�]�I�%|��X��
���H�[��/�8U����cu�bq�;v�q}��=''�����R[�]��Rr����j����k�f%sTs>�'��_����j"��.�����E��>���)k�	���R���$@��`}/G�2���Z��UG��WoJ��VR���e���'|l��+�b��������U�C'��<~	�[��s��4�g�U��+K��?r\s(�� ����{{z�SJ�G�K��q!m_I�R�R3�����/����f���6gj+u��e�eVj��x.5E+�{�Y#&�&��-���N�K���Z������9�W?�0S~I���-h���u��9�5�������n<�:��z���7&���0���^���F���c6��`�]�v��$��,�V�0o��zs*��w����sLZC.����q��z����g���������Orpb�����B��&%+�� ��yG�u>_t�k5����H�HF���P����kF���R�8��wL�4<��6�$��H��N����bP$�C$b2CS6�sM�O�%��E�]�;k���wl�R.�����pYFk��\g ���hp�j($��s�����	q���
JF����cm
f�.�e����&�}�;�a�4��S�"�?�#���s�������hW��nGh#H.EJ��3����X���{m��E��j�~��{����Yl�{��f��<��J�.���e�����/Q�8��E����@����ic��w�Y�$T/��<���������O������������E����������'�����Uk��3��?-����xWr�/��K��7�Vj^�g��b6�qj�[�UZH�����05��$��
"%_�h�Mp�O�^��������Yz����ZVK�$.N����-��u^��G���Pm���L��`
��%�F���Sy1�A�'�N��T��|���  �_\��/kk0i H�p(���6��5���F��q	8��	4�hg��)@���7w�N346T���xb[���xd���&�����u���9�P�|��)���/G�>���d��g}����t����
��c^1lE����.N�����d�����|R&L��H��s@]�G&��{{vr��c*������OeR�E��;T$b��t�:�g.!9����9�1��J6�H�y���-�8X�s�9Q��L:�l�r�1���j��<���������������h��Tj���~J�i�*#�j? ��H����������y�����y{�.�d)M�9�����ds�o�
���R�b�����6�^�Q���:��*����R����U)�����s�w�>���	-�����9�5�9��)9�PU�JFo�0H�&k���xS��nC(U(I�
e~�� �h�\P����
E������UK����1Sc*�c��7'�t�M����*��b��=tW��
Jl.��
8���$"����
!���E��)��=�L�-T�f���Sht;1z��{fH�*���[t���>���W������Y���"%��L��G�	����eT�T����z������H��+3�|03[y�@YsQ�o��Z@�v3�AC]Wm@f��\��8�g���}|(Rx������@W'�q���(��"e���S�D���\�r����CJP��5S�)�����=�^"�Q��5-�J�wF������=��~���L�������VD�6��|A�n#�V�����(.zq���v0/t�D]�5b����E�����E��������N�m�C��������������uJ>e�P�)���G�J�
o`ro<x�9������'*�pd�V����Vo2gh��]c�y�o����=��m2Xlh��y��[��6���<�=�N���~��������v���_
��8�v�z�����q���{~qr���*�bTe1��N��U����7b���h�<F�|>����	^>��������i3^h����}����5���7;(~�~�?Y���Q�^H��0�GE6���u�0%��d,Y�FC�������j��-�K8�����o���(I���/���!���6|N�1��>�H������E(�������6������vU89�`��\�*z����7�IBYX��J[it���{����Yl��gb�Z�iEpZ�ZI���j���'��t\�~[=���jkw5p����is�2���z���x��E�6Q�
��
��.Fy[�HJMY�����y�m�������������i��/�)�|�%@@\�$K^��$�y�,y����Y#\�Dq�����DDfe��������7���<###�x�����Q���=U������K���_���?!�������?mOol��/3�l���"�/`��Z�GJ�>���gdo��|&�#D�a������|gY�_%��������Y�o���X��
bH�8�<����1v�)���c�d%�]��E�����Pw��~~��,*���>���R�7��^���!��l�A�*��&���Lvg\�e����V�$�/52����K�������}L�su��\wN�L�/�������[u�I�y��h�O���v�8�������Q���	!$7N��
,R��v�F��}[��1�T���@/K����bY��?��*�F|�=�D��<��n����ct;KQGN)!��A�oV��J�[��U��	#��4M��$r��k���B�%p5#��v08�,���hD���zQ���L���Z�<��E�=L:PiXu���AL��Q��U���"D��?��������]��6TY�
����L5�jB���k��t�M�r4����0Z at���o�@�!��3R �r�����o��k��#�@�H� E'Pt�%����E�
��S4/0���P��Ut����������[�l@���rY�qb��{���TCi3���}��k8���
j�,����������e����svy��x��bT�:u@KOfJ����r��jU����C��!>����yBaJE57���~O��b��UgE�����
������
XR�F�� �Nw.��� D�������?�m��Xf��'�1A���8�td"���|�P7�����'�u9����\c
�A]�)��=OQl4����B��{��(i����c�]@i%��Og�I�%"��R�`�
&c�T��H->m��f��T[N�H�1�[�&���a�
t�P�v,F�����F&G���i=�Ut������66|t������?=�
v�����a0���i�d�o"��9���zfILmQ�C
�B����%��x7P�/�6�yF�$VO����>���}���o�H@�Ph��G9��Qw1������(�kRQB��f��R�G����/{���	������x��#�r]�����
�/�&�����M����R�4���}�qJ�7��og�v#%�f�~L��X��������S����S��8kG8����0�F%����%\�����7�����X����?�~x����Q�/O<��v(�'�dm�o�x�n���CfZ�vj�{�}����j���C���6S�[�Z�\N�w_��6�?�}V�Oq:XZ$<8��/�����I(}0��l����.�*���/V8u���������ut���
�dI
����nX��`t�������W-�j���t��pgd]����g��W#'�����[��"P�C��d��@F=�"?0V�������f�G���� _��K�h��Y�T�9.8�:61k?@c�9���k#7���a����@:�
�j���f�$�>�����=�%G���8��Y�[�yX������Sc��q1�6�mc�b,����� %���4"�j:i�N(o'�f��i:����}N��z�O��O������7�7�����}��n�<���s�Ps��O�2u����|�_��E������lN�4���G:�~��u�>����zV�:�,����������q���N�C1"��Y��Z;�8���~��M�}s����)��_����
�d����}���+�8���(���	7L�^���E����F�'��F��q�s�<J��7T�D�V
�JN�D
������,���3�N�m��]H�����
��O�����x5G���$v��V5�4����(�k��,S���[��NT��m���/=t�5}W���7q{��[`�9D���;H�q4}9�
0��qX�������aMJ�{J'{<��:����t,�3)0�����'��]E���4�G�a���n}/��|���)�_���~[���L&�nn+��80[R�������n~O��HIk�W���G�#1-Y�N���T�v�==sW1�;:��{��N��?�`���S%��H�5�8�!$kK�UCB,B��B�U�`w���+o4���U�VM+��VYr��o�����<d�������q�N�RV^�8E=LISZ��p�9��e������\��b���T���3J:O�����p��P;��->@.�%	�Puixc��S6,%�A��H<��n����������7`�j��\�'��)���L��0FrQu��N��Q��-��!�X������ w���
B��Nr�EW��&a���	 ������@q�(�(H��I�G���s������\):�W���J���!)-�O��hI)�*0f/[Q��N����3��G�
��������]��:��k���E.���)kR��'�~J�b�$Rk���%����)����2�����(�����,��]��]�O^z����������S��&�?ON�7cI������~��DF+K�s51�����2�4=}j:$��ty+D��w�������Q���7{�J��Tw������/.��Q�C��{����Q��9Tz@H���v�����A���[>�����*����8n�:��}K}�ec��oX�B������1a���TN_��aq��1Yu'z�z�F�I�8�S���=l��X�VyU����-z=�U��r����0
DWmM��6�����c�0���5���������).�=������n�R��u%���OV��*D"d�$�Z^���;WrtN���K�9�.�:��h��c�/G���Ga2�O��%��V��Z}�I�]�e��3�������!������������)�T�)yp�x;T���
�z(�'S�0��Y�8Ut;��]�b5���&Z�����7����5���O���nq&(���v?���bf����q�����G?vF?>��G�7w����C%���I�����2�G�W����Je�?��=X���]K��v�Q$���q���[��g�NB���D:P�h����.�}���ku���Ggg�����U����v�����Cbs�<���d�?_�^�5����p�^e���o���dp���!��Y���$�q3Z�;�B���>�����&<�>��a�+��(��L[�JP�cH-F����jZ1E���m�4�J�������4�l-:��sdb�����F�}y3S�v_�;qm�6��a_���������M������	�y��T�vr��sC
Q��fx������k��`�36{N�2:B�y���W��`Pv�%1������[�L>/E�^t9]d�����6�P�".X���������o�T���[���Y
������U�~eB������yI���m��Mv��d\}Zs��e��V	�M��n}_If����`w�mb��bm"���T����Tk�tF��|W��![����L�A0�*����t��sK���d�TBd��t�/"�m;��,���b��;VCd�#����<{��R|AdQ_������_($�
)[��;�����R�v�L#����}8R�R����_K�����IwA�?��<�w������b���UD?��A�g�����p�{z``��]��XE�2g�'V������9�Y���UY]�$Y��3���YE�������{z�,@��d!<�
��a����S*&��,R��<��z�jBx�r��e9U��)�*�K���b�a�{����9����e���4����mvbO��
��J�Q_���=8d��&�Z�����]���.���D#k���=V}�H�����l�{iM��5������!k����q\/��� a�M�^8 ��xc���'Q�N��_��[��u�tLX���0�KmO��������*��T��~s���N?����1�,�
P@�TU�Zj��om��b�nlB��s��3f��9�*�i Fs���s����o�e$7PS��|t�
�	�M����v�x'�����r7
Qu[����^"�G(�U/��G�<8�2
�"8q!Vi�GO�h\�@$�A�L@�d�/�Y��q��
��?����ah�0������d�0#w�W�j��{�z�vX����	�^�)���l"9.�kj�����;H��8��-6 �
��A��7���Q���^g?��L��_�o��L�9Z��,-T�������%O��s^�)3m'��Q0�_d~v�X����S��`��D�]	\p�b�f�v����U��"��������/�n4��U�xRD�e�U]��y�I�/�_����yp���C�Ro+w������C�t�����Y�Mm����Mr��i�,�z#E
�w�d�s�2�������xPE�����)�q���?i�7lLv��F0MK���w�[f`��d��>�����l�C�^���b �YuY������)l��1{��`�
���>C�8@�kA�j������<\�
���$��j�}��e��.b>��ba�K�L�5�����INg2�k�����������#-��P�iB�a1�0�F�/"�w�'�nX�����(w7��.v�en�#W�6�x�����Z��B�v���z�S_�!	:�OV|��&���M:��'��.���~�����^ ���`.�����d���=���@�2'0x�������u�9i�9zvC���������X��*F�Tj���H��s����@h���&ks*c�}���[��-���V+��1�.���2�U�1s���$�`J4e��A#>�[I�3�O����s/��7%9����r�Y"�qG�4��Fa�����1���E�T�3VE�)�A_�/�{�%�8�8��jz��fAa�.3�u.�Lg������\�3{����]o�y������l�HR.H}�>��7��������Y����.�f�yi���`�.A������y�DJ����Sfc2$$
�OJ4|a\����y1eJ��4��������������s�1$��\�����{���$(O@���:U�h
�s����B�o����+#��1]]�i�jY#�RH��2��%O�,�C	��TR�^���`�^j���c�<Bs�a���F��>(y�}�~Z$0��jC���Q#��>4����Xa�����s|�n���i��������H��(.�'�x5Mq������5�>En;�����v�e��0,U4�e���[�� ���ao�������q;�$���T�z�����b�
l��O�������T��T�5��#����K����O����K6��I�����?q����R@%�?��I�D��Vf�?����?��p�5�����2��,�j�3���v���-i��K�k+�����ry��������$��Vu��mE�C�Ns;fncR2o���w	%�]�m-D?�(����=��	Y�j�gr�~����1�R�������S|P���Ybi�����.vg�J���%H��i������x!YP�XS���IM�����[��L��'j�za?���5�%��5se���T=qI�
@ �-�0�#��%R�!�����1*-k�hK������������c����������\��%"v�Ff�87���3��tW<�L8�>C.�p�`���]v�'�������������dK�L�O�����H(�!�8B���7�@�h]\��YLg��_2������������������o�����0^/E ��f�S��H���`�V&������|
�����VU��F+���5�G���m�|��a��c��.���V�UXL�1���:��AF�j�$��<�n!��,����PK|��v��i�v��6�����e��G�i�������u�����dS�)���^*�$���L��H�`�,�$�p$!��e��S+Po��v� �=��!v&��I1HU�5��;ISC�d�~��W'
f]$�!�"����&���6�H#�Pnd#Tk�u��Z_X\s��X�����?d��������7�}1�/����j�1�|��d���<V��a�!��ZD%���B��5#�H�X��J���+�b/������|�<�������e�
�)([<�(�*>�����D�_�-�`g��i���+��y����T��v��c5f���@�"��@��t��>��8�6�/)�7�O�D\��J%��}qz�V_tl�3X?�r��k6=�%��	�f\�"j{
�������/���X!)
��-j�"SX���c�+��Y����\*u�u8T<|��w��wF�q��M��A����
A�Q,1C�����vf8��F������>S�T���W�;��K�7"8q^O+�����@I9��+26~A&���7��F�l�[M���*'c��U����l���<I��!�C~�r��������M��r�����������8�@��v��G�>�O��]=����T7:��.qbBt!'D�4�������)�!�T��H<���1�#���r����KB�~W�����K�B0T��WM����T����p�l����I�8��&�r���Cm>��"��zo���l�m���gD\gQhLj���
����J���8l>'M����Ac�������{7��a�����������15hdr�����[T ���RK)����M{����w�$�c�*��@������>��U�4�.��q���~�� ��j�K�������?��,��Na����L�P�V�V���j�a�<�}:.�A���^@���n���Fd�U�Gm�����A*���D������5?�c:���)f*�����F����l
��P���w).��
����*���T�}���
r$L7D(��,�W�������P�\j��)%~I�sf��f�{8��#���,A1��%M�����)T����si���wjt�i�
�QP��^��j^�Qp;ir�a�iR�4���q�;���n�GbS�"�M8<�}������.�L-�Zs�^s��W�T��{������ o��"�Z�)	9�)`p����C�A�/��:��
|��X�$O���,d/�5\G��������"&��@�'Ja/��&cd����"u��tXXco�X��yX���(�Z��A����D8��T��[=(Q@��l%;�����������u�p�P`_^\p��s A�a�l8�v�����u�����5:����%,��%"������?���^#�����w�<������Z���d���d����J�V�@�t@x3�_�o��� �u�aH��#�r�>5)�7�Zm����J��	����~8�����#�xN�������;�[�����=�����11�~W~�;:�F�5���~�R�v{�� �"�[kg�e��*E:��F���=��s|zd��-d�0x��A�14
P�/�v�B�H=����(��d0��h�+��vj���4d�{F�[�����?2��J�98K�������V��a�r4�A����wI��$��O���t��O����.SQ� ?k����(�R}p�,u�Y�;1��^�=*��]�p�D~����4N6�%W)� ��P�}�/�q�1n��p��
�����7z9��t��'���b��(�RL�{�.hk��_����{���c�����
�z��m��2�$���23�
�d�H�2���t1/�
�
2wH��T�E���\)m=v_[s#q�l+�����N��M�+��v���.�vK@�#�$��QI��������V�F��'2,�P��S��S�X����*���������L��o�����-�d�Y��I�H	]��cB,y��w��=�jc:��$��u7��?���������Aj��L9Cg�v�3�t�?nH��P����DaZ���/�8L"H�M5�3-�>�nY�u�[~��o0F�����F]'����p�9�>9m+j���F�����]�l?�����G�{h�����+��:8�J���;[�ue[ZKJ8�)Gp-�������EC`b�f����n]��B]f��_�5!�,v��;s�5q�����������Inw�G����
��2OU����z��|dW
��Z���9^f������07u�����F3���8F~8&�=v��������Z$�2���Zd���V����~@	�Dm�1dk�c������)~&��)(�<�>��J&�dmH{�����?����Lu���H���0�����#�v�IGZu������?f���+ihp���fz��C�)��s&;�����H��D�#����@���	Y�%�Y��&���H�{��
��:����e���w5��2}��D��od�:f,.�&`Y!�AM��z�>��#�B{C���I����R����������P���H��a�B/����"lJ�F�{��`���}����$�|<�G�t��������G��%~������^�;)y��zy����b�c;�� ����jDmPq�����vWdd|���l���\��m��K
��)���=d��"._�������!y��G�)����$5������[�y�J��S�����^��5q�8}s�j{[E���Tp�n3I�n����ms��m��$y�����//��VG=�����$sW�������/S��Z�c7/���W�Y���/;���������0�|�
u�����.J0La,X`��3G��BJ`n����M��F3UG+������h��cl�R�@%�d��:���7�&S����� z��9������,�L�'F2�����������7��pN39��}��"nV�wh�xy2�bz;��A
j3�����^��~����z�������>%��J�A7�C����	&q��D��������T���
�^���&$L���oj���T�h�]�j:]A������`F5����n��
�~�U�o���[���2�Pd��C5jx���ns|u���������W>99�~��������������-�|�o�����aw��U��jT�g'�[G1�V���z��Y�j�����pz;�w]�����W�!/\/�H��Na^���W�J�o6j�,0���s�PH��;�6�Pv����N0�������� ��"�~���?��w�+]��s�Y�0�KUt�@�����8��'�����F�)j�(�bV8.���=���_��9�Q4�p8�W�����N��cbr����;���C���uWG�i1�,t��O�0"!��u�� ���y���������:���8����P]�XS��g���[���/R���F�����?�K>��TiR��Px/)�|i2|/�����4��i��y�gFo�&%Ql��$>0	u���[m��{q�I��E��
M�����������m�C����H�`�����.�Lzv��
/N�2���Pt����G&����wZc�Q(���������k-f^��f|�z+���|3#��[�4��%A�[����/���"��E����������px�5����������������TR��������c����
['qv�4�(Jp/�$��Y����IH��q|���n���o���?{��<'�O|]���SHh�V��Z��^h��6	fs�
���e<_L�+�NQ�����pqv�*5�'-Ka!5�dE��B�?FEb�>�)X���Z$E���p���_z����^��A8#J�3�gLo�O#Sk�zmve��j������`Ri/�/�]��5g��;�4����9�0�V����'�EtV��zc�Y�Tv���`/X-9��IN1��r������2�Z��q�9�o's|0OpC�%���U�qvQi�$'�"�V(:FQd�b�u���p�5�����<��h2M?������|��{�z���mcC��_���7G�|V�J�0c��	���X_�-sKa����n��X1�35;$�j�����n�\����b16V�����p�N$Xmc���%����:����T���GS2�B�jE�R��S��}'� �0Pr8�5#�UJ^�svz��tL3��mJ�H9[I�:!�����&�:������>������?�	����C{�$5����z���Ij.
��.zw���?z�z�����������l�$����()��b�m%������� b�����^R�Z�/n�R|����B���3%�dj��8kMi�M���N	&�����%���|
~��3��$��>|QwE����~�t���2���Ri��N;�5���a�2�7��5�� _���M�$�G���Z��#�jwC���F��S�_�HS��C�+��DFl��vPEp�����UCi9�P@BH|�2�����+�8�Shi�r��.�S���w}�R�v��+��#w9�Rtk�����P��u
�5�qG��� �����il�Z�z��J����q����]����a����	/y�d����;���~���[�T���a�W}�d��5�w�t��6?H�r��Xc�G��3N|X;���gx_������q�aq ]m}9!=������K��$M nr��TdtG(ywq,Q��b�h6#��z��g3� ��\����=j��X���������}`j���4���!�Y��'��3
�-��n��"T�XEnRS������K4���j~~������C;������k�T��*�������J��
��`#��:AAV��� 	�oo�!%VT!�o)9oL��V�([J6'{?Q����(c�����&3u�FN���a�V����M�z�~�
����
6:a��)n���N�(��k��"�+�����n�n|Q;���e`�qBZ�������X�q�Q���-C02�L\���	��`QY��'���-a�[�
"���g�*>�,f���*<N�%���D������+�(A��|�$2�5�=O�0�Eb�K�h��#9�'�b*����r, c+�O�Z�.�m)�!i��{�t����z��F�kU�g��B?MV�DM��X��|F��T�>-*r�Mf��T���"�
��*���I��=������t�#��

k�MR,���j%,��E4������o"��8>S�#l~�}Zt���(����d�*��u��f~Z-S���yR4��m��.a�6��������Mu���(&��G�����O��`�U
�w@�L�t�%O���?�4�_1J��x�*�ys���26[�b1[v
:�Z�j��]�:�	NsJ���LO��Rp���u38�l`\Ag�uh8�	�|;���Fm�	���dW����v�����������K�Z�L��XD�+9�(=�rdy������#q2�&J�	��i��D��VE#c�?v`l.n��o��=g�E�,��-��OKF'�e`�M�8{�bP�������uN������z>�l���-�+���������|�������C���g1Q=���uk�4�-I �:yl���H1�,�gbU����,Yd���8J"d��U��@3�a�I��P�9]�]����!���
i�*F�����0�����3E�@���@���:��|T[�����f�6������a�Hv����{�
I`��C�\9���cNfA�?bL5�#�&"�������(��Z���$��u���h2��t��,��S��"�i2+#sr4i�������i�_�J�&Ft�!QD�t���3���AW����E��FS��E�os(�������Q��a|K�{����#�oY'����,���T�=Uke����F��������D���4�&���L/1u��	������|mD���,x�A�<ow�Ka�[�Nd��5�����~K7�cN����������������{�Q#Y����f����-��������Z.(��9���1L�@B�����a� �9�[z]bu��O?AEh��m��k�Eh[��{C�2����D2v������p�~�2��&��������~d���[�D
`�i���������s.�����1�f�{0 �����~��[��#�����~���r��,�!�Z� ��P���2G��N�ItL��0����S�%�Gjd�%F�����'	�I'3��� ��i��)k�L�����h��H_�TV���C9��� "�<��G�{�YE��G���$�����c?"�#%�[5��3���o��(�_����������Mn�x���p���Y��5+��Ke���8?���P�%���{�@�$c7Xk��������^c��*c�����At���R�h���L�-���!���|��p�Ue����l���N��Q.�����&?����&z���?�"�>�C"T���5Yng��;��1,�*�]I�w�AfL�z!+v�(��u!+n#S�i���"X��t�������"}b�SLV�:~��O~(�*��4���0R7��'�����B
��o��&U}�T6r�A������}��������X��u�>8�pWN.#J���%������V�����I��-���q��[h��C)l��$�����8���G�M�g���l��2lYB��tu��g���9f��*#����� +���@1)X_����	��hK�	����${y�0,K���
����~9g�����9�B�#�Il����I��k�PpfE���
���6[��$��xtH�����0���sl�c�O,�L�=�jq)5)x�`��p�����4p�j��!��M�+���
�c�&yrt�������{�>n�����_�_Y���:�-�X��������8i���_����E��<b7O�m���B�78�N�|���Z�w�^�UN��sbW?��f�Y0�M��c��]�X�K�I�D�="ag�p�l�"]����hk�� �?��F�A���U�r	��D^���/����lQs��|����PW����:��g5���HYC��� ��#��v�74�H�;�[R0�`ECG�L�"zO��
�y�}rguMh��b%�2(eZ���(���h�2�$&x1V�T�W�&�,u�3�\��f>���^�I�����eq
�;���$������Y�:������JZ
����bhK�����|;����y���k���#��g(�8����(}7���f�{�����VI�Q��7�^#���=�R9l�v�9@��5���2�0T9)V�����yD������5���V������x9^p��c���mSja��k�0�'���:�W�w��Je���42�q�k�&��f��g�~�I�?o;����B���6��:�n�;�'%o�A|o�Q��}�y�a���o,��w�i��n]�]�O��-GU���0d��%Y�U�@�ds�Tn=���!����c���-:
�m]�������`���j�����_���+�	�v�G����.����_������:��m�U�'�(��v����`���Va������cJ��77���V�_��XE��;���k�g]w��R������jr�+����sp����(�����a���C���.�����m24�<LG�>g*���G���z���8iQ5Z@N���.�G��@]���b�9�(yT0 LD�i�8�����!���6�0��F���3G���h:�@�y9��o[%���p
����O��jT%���O�����F?��\���I�&�-G����;�>��_������Q��#�
?_l�{���hOd������:Of�'�����f�s,r��V G�4�>������g_o�;�I�w7Q�p����:e��W��+K���x�c��k�S3v�Gw��)�f0~��C��1}fn�z-O�^����s=�s+������L^�Q��+z����<����i;�`x
L1�V����p�z�@���L��u���w��g�[�������� 1�>
�v�����	b	���l�������]�����^�Q��Kjj���E�$\:��C�w����W�3��C�tk)
H���;���S}��#�w8�GR!�d��(7#�L����p{�#f��4E��J4��wf�N���b<h�G�}����i]�d}1�,2W_����Pd<��) R�����ic����T�������!?�<��Ef�@���`|;���u��t]��\�����[^��6/F�[�p�{���u�L)�-�u��hG�\�A+\�:��?��n��'��gc�����<�6��j����m��� h-�A��tB.N���*���(��I�N�F/��#5�c�����F�u:�5��L��UMO��yj�A��
�����G_�\o�~������A����T:����dT�?��t�8�.X��25-T�t�s�Mm�a�S^K��}��D�zE������%�G��$m���dO�c�=����K�f��N=������T�a�q�,n��������C(9���XTVx:}Z��.���SJ�f<����!=�d�������N���*n�0����jh�	�a�uvW�k���[�L��KL�s�����L��\�H�y��X���d�f 9Kd(���D��D1������cy��qpA������Q8���!�Ach
����}����y%��������3�
���$��y����-�4a� >~�(!lP��hG_+��^\w)��&�������]��$��O�k�\���Z���~���������gc��[V�:���d��D�{:��Z;�A��M�)M��mD��_� ��O���E}���
�=%8������]�{)�Q8|�qO������������f+���
����2�������K��7���qS�,$�r��m�N�vK�~.}�:��"8�ue�2���S!����Q�v�B��������6���u���h5N4�
��Ro���S\��*�#����	O�k���VS#�K8Z�<Nj�h������z�
5@��Z(��e�x����Yu�s���Vr���SP�^���^AI�*�{��n*�W]���"c�',g|p&c����;C����"Y�����_��P�J��5f�cb; �!�q��ob�+�=����^���d����&��2��Wj�)�b2c'ck1��������=1q���)J�R�\UX��a����E��Y1u��h&^��c
tc�F��Hg:4�kI��m������x
N���<),��#"$�W��G`7/4��V�F����L&�������B��=���tw����j�t�����n���/�E�%��wD��u�w� _��!�7Z���8xS���u�#1��dq�3�3H�1p|�V��.MY����-��	1����w���:���W�^����]��~����P!��������. �n�����Q?������%���^��_�4ea�f�@�-�(������=�TQXX������r=��^z).Y��%J�J2gj��R�Qr�����$9[�Zq���C������I�}�*i9�����#?_�?����D0���b�&�6A����t,��1
�z���aBdyH�@o2(����R�i��r����)�sP,�c3��#����
EZzK�)v�{#��J��|�
��V���E�!��FS��x�Q�>���;k���������,���OK�V�ac��(1,:7��z�����v��l���1:�g�Q�?-�^��:�G�I-"���HZM;</K�~3qF�z����E>���d@�#C���%��������S��+���U�����{�}�5��b�u$:��."����%��XNkL����f��&������_�\[�8"/-���Y������?�H����y�����h���t�3�n���9��������*:�^��9m��D�����3���=�����p����^Y,�"�S�/�lP��GgF	�����,�����>�<.F���2ja�u��+�{32GA]o0&/1���b�3h�#�p�1�v)x���V�������60A������;�:�������X�#
xo��Ew���f����Hc�����c}���G�-i����g(����$�����Z2�@��������NL�����*O����S�Pq�v�����;��U���[~����"�B�KVH����&.y����$�<��'��|�j���!Ju��k���H�Y���3��H<&hF�_c�b�lJ�o|�M2���"'��A>����(RT�����yE�����[��z
tZPm|��l�����+!��,�L~d�J����"��gtf3>���*b��2�,�X��j�Jq(Zx�9��,}{��
f��5oO�p���?
�I��Hr���X:�3�7���V$�@�:H0����C{h0�+���3� 3�>�E��v�|o�{�"JA��K�����c%�F����}�h`~(�N�ph��^^�������O��{�Y(�#�!��4L��A���t.S�]@:��Y���F��
X`'R�<q���.I��"���c����%�DC�����G��d���e<G�i���G��	�g��������<wm�ji���������*��^:<b��
�0;!}���;��V��n�m��6"h,��$�'�L/W��3)��&<p��(6W�������0�����i
v���%Hes1�'����|��#8�nfD�~��I��f���	�3Z�������Q��H�����m�T^�u��x'����q1Ku$�n.���_�d��d��_�H�E�7���
�e��<�9mZ�bb|f\��`y�����t�.�������))�I�?R�����j(OU���0j��w���d�?dgG��{�u�]��~��0D���/"������P�y5Ed�"��L�:�����;a, �8��S��J~�>S�4���e4e���9��676���<����#�q������XH���
�g��]^�����}�23c�y�M
g6���7���R7_��(��g���,�t6��qn-����bI�l����W/$c0ZP7_������tk����P��.<W����_�X%�u�9�����"	�����3�C�T�w!�Zr���'�U�Tu���u�K��!Uy4:�e��X��~hi��_�����!<����i�H�������\�!�@v���1��z����=�8Z������Wj�(r�`o$#��b�(�h�)��I����z���C�qK%���xCHQ�q�~H�H\��H�>��n$���>q��+�Z�jYC2s�����9HsS��;~zJl�2r|�#b��X�Oit!���u1�&&��Y���>C!���T����K��^�*�*��4�)��ck����-�`�l�q��f�H��x#�T8W��cm^����2�D��{y}���`L`��a�6DXg�yk��tD[�a3��v��B/D�!�x��%�s]
�)�#��E�H�c�i@����O,�-�(���f����!��a$�4�w��s�n�9��e����u�:o]�tn�v��ZH�����9�xa�E�8�����]a�-y����W����������M�'�B���>�T}hV^	��I�����J<{sz�����aw'�q�|���������}�L\�	�yD{�&�7P{_Y�����*F�J!�G
Ft����]�������b��x���<%����%��^�����--|��`���?�:�\�Z��Y`v6�t�\�Y:`�����bh�i!=�%i��.��s-P%EK�s���>������mF�_YW��B���F������N����;S5@�����+1����X_Y�K��W�
�WPz���\.~hc#'[7F�L+����t��2n��������K4��4���7���1���0��ta���({>�h�k"�8��w�0H�����6��^C��}������K�Rn�&-�]h�������p�X0[�'�����y�
/�5������IX��s��)��4����.b5�,aE�-����,���������-��u'ac#mpq_gO���r�d
WOS���y�)�
9$y���(�����W+�X^�/�������C�^U4eR�	�v-��.u�&�&��t�a�p�^�S��[n%�U:9rC�e�f��T9x���������d�Q����)�A,�w��$?85������B��u�.�~��E8�����jm�[u��}��/@�������[�����z�������f=������(�}��EC�����#o[��8��)����1u:��l�'_[������*�.��i�-g�����
x}7
FhO�����}�.��BvMY���O���#[�U�V��oi���N�}�l��	V�(�_��Go~;m������&���^�#��4���m�r2��:[���WS����K�������Q�3q�Lg��a�?xF��L�����(��-e��X����iNf�)Z������Ff���l�����g����x:�j S�]���f��=�2/��!LrN��W��<c �L
��[�m'N�U��<w����/�EF�k��eGe����4��*cj�IS�\	t���wcQi`�w��k�FFS�E���=��|3
C\7����j�}��'O�?	���-�01����Z���Rf-��?�V�G�.�,����?xA7
wr��+g��������������3����4��l�1�@:�Ht�R��L"D^:��eka��;����Mkc���Z�)���EjyE$�^����pzs��4��4�Rh��di��;zYK�4��q��k���`���%2�}�,r�m��%���G��d`���:t�4�N/8By�`��M �Q(���������t�Kg,�������S#�ZR��)��t�QV��3���)��2��[{$�Kj���rrO���30�Yn��*;g2��
���)�Dw1(-9��
[��5i ���42���&X�b���* 'c�V���V*�~w�`P[	fW�{n���	P,��:�b��$�'Gl��s�!�����NW7��-m����D-�s�w����'F���i���=����O���z�Q�Ju�\M��v���%�ri�k�UV�;��w[&fz�5�E�x��v�|u�y���,F�d'�[IM~��?e��x1%'�TSL'��6��2-Z@N�7��@�E�%5L�����������z����z-�DJl�%%�����_�����c���������I��&��,�����u�szq�j_���o�K;�41dz�X5(��S����	��4
}J�C����?!��$�d��F��Xr��jF4,y���'��U���|����v��VQM��UB���j�����~����u���:���]�6���Z����R���yp���������0���M&��%��s
C�YpK�hOK��?���(��"� ����Z������%hy�o(�[m�U
�yf���LQ��v��P�w��I������ Q�&c�]5����GsS�;b�G\��)�����������X���l�__M�����R����M�n1j)��c���?"��� SR�M�N����kj�2f���_�)z��R��~�i�o���_��{������i�6�@��
������1P	����ptz�i����>\^��fF�}G�xOJ�
\���xi������J$�����E���Er[�F��r��o��_��}�1���['�v����uvz���;���l�-kBU����������UNuV�k.�Y��'����k��������W2H�Y;���4�� M{�"���~���n0f�C�"��EE���A�������yi�>�\������$
9��:w�T��[�����I������T:3j����w��8����F&}F��>T�#x�2��F��n��fu#�9�8�%���N�i,(����Vr|�:�x%�%����:������m���R����E��n_���m����d-�������2������:>P�Z7��_�f��}�dI����s�$3�2����j.�U�qs�n��":��LQ���GYW�������K)=�k	���:���5����2s���Z���J�,�&�"�������O\�$��R�	%�a����QG�+%������D�e� �F�m65������Fw�y�����/%�&`������1%��NVz){����G.����(��NY����(P���H���`�>)��t���uM_���W��~���Wr����Ao�^�t��]u;]}����g�&f�lH���o�"��?��%M������@�/}kjtQ\�P<]�&E�����r����=
��1*Z�{����c\�'�F�pfqt���������4�����E��/f��;R�QWTN$����C��d�eu.�j�)��k|�O;��wwl%�j������M� ��{�J��v��j�5�S�)�\��d�p�'�����
u.��#u�}Pb����;7��K�������Y��stu��c����������7��k%j_+A��N(@M�sY)�PLl��v���E�Y�����;�F<~�����	��-r��{��^�R���������X���DD���Y9/�o
n+�	x<�w(w�4#G�
�����~}�|������t!��*��D ����(t��]$��_�"�)����7��F�D"R{r?F�
�7(�g$�L�p�K8��$9�up,n�?�[_S������_]z)�[�k���R��j}�p������.��T�R�� �'�U�6���& ��;�W����{v�������1�������x�2J�V����,rA3�bR�Y{��*�E�[���z
�����V*�3������� ����"���?���;$�Z���zC`�;n)�U����/�K!�N�'E?[��(B���wJ�'�KE�9%"I���X�V��v�<wUS"�[���m./Ey�m~I�����/U>���_����$���������������{"NZ����������n�����gXl�z`����"�G����Nn��lb��{L�7�e
�#W7�w���q������IM#��1��n�~:f�w�Y��i]�}'�����L��&x�h����vO�5N�~��(Wt]�q�y�z�_���\���o�[��z������|��Mb�����zJ�A�~��d��!����guk�PL���=�7`����3����[[y|#��F�������Yf�A�h��Km��2�s�HK��#���am�R9h�5�%y�o�����\�5��R?������;D9�`6��������>���*��_��\3�'"*8����]��'�bG�#�V8����X����tu�G�k�(�Gv���xZ����<�!5����z���%N~t�Sd
�Q%q*y^��F�wfdGD[w�RB��Z58��+E���3T����IUIN�\����gm;A����H�w��u�:�1��� ���0U�6�x�/�������O(J�����yFi�����ny�BF]�Z;���q�]��+��G����O�?g��S8�"��=�Cy�k��]��
�o���R%T����~��2
��q�*:�������_�	K�/�].��w�eE�[�ZZ�C���OW�g*���S�P�y.���9'�H���<
ES{���U�q��T%;�&^�
���	��P��2��D$�ML2zT�W�|������U*�Jms����o�W�Vk���������Q��+GH�3{�!r_��P��}�<�P���#���kz�gm�j����~c���������{^������^��9p�o�~��~8��#�G��U�y0���_��r����z���o�~�yX��|�_����~��o6�~��;W7��`����j�9��WWK��j�{7w��y���l��q>���9e���*J~~�y���s�T���?/�4x^����|^=��k����a�=�~�:�9~�Uk;���(�h:��Y����)�D���W�9�#�����,�������}��(�=�����d!��%h:d|�1��z�S[�+w����5��(���� v��{��b��f\����R�����FR�RO���k~�~���Mf��l���ap�����H�}?�N��X�m^����l.x�J����b�#�Tq!JJ���������^�J��v��~���{��[�����/'�������_�}����wM����_�����W5>��z����O�����[�oo2���I*V�t�G;X\u:��5�����D{�_��������ia3��#������W��7���I��������E�d����N����,�?�����e��w�+��z���g�i��K��eH�= ���\6�X<����ch��H���{������+���V{����-�.-;�[�s�%l��-��`�����!X �R��U���_��\*H+5z���q.qoB\�a���.�c��5�=i�g�-�G��mm2�U��u�c3h��<SAF)�B�zvm��o���L����?t$�q]e^�C��Tu���Sm�y���hdo�hdo�id�_[k7&�c�����l\R��E��QL
�nN��G������v��}��s�����z�//b���T����*�N��.���j�<��� ��M��w�("p/|Q~�2��w���Z��z}�h�����j:�������ss�����jtJ	Y�5�"�����h�������`3\	���^��~8C��[$3'��x���cC���&j�X�Q#��R�2D�N��%�1)] F��\c�!h']�CP����`�g�9��f�����^d�=C�+I���
���E�k������p�����xr_���Qyr:+��::��*�IRkk��q��Tl�R�����G��J��n8oc$$��\����"C0�>-y������:�/9��~k}<>S�\���>�8j�M\5�ub��a�a,��"�[��=�����<��s����cy������?p/e����2>����yJ���xLts�����$�����C�	0Q�T+�y~�?v~��`�16��t7Q}PLv~W_��I�Y���qs�;B�����H�?&B&�#E�BY�������L����<<�������e�J�����	�0��$0���	y$�~�|��7�?(��:/�|�Y�Z��������c:1TU1��G��OZ�1~�!��<e+���8Y�
���jp���ohR~�7��������YH�W�&��j:RB��=�������aBx�������&�
���c����'Y��W�I�`@Q��~�LR0��
1�'�V����_VQQY<
�a�X��F�d���].k�l9x������Hr�m���z�^����l��e��k�!��^r�����������;��:V�$�G�ppMi�cY1&�"e��6���U'�H�����|N{�^�d3g��4��:�`(���Ut���$
�y���/13$�5^7���wZ����	hL�rJTN�,�h�C�s�������S��)��6�'y&I���+��=U[��(m.�R�������B2a������4��~���U����X���G�����;��02�e]F���2O�:�F�����'��N�)����=�%�q��d�_cbi�|u�G�w8)�i
�� 9�$'��8���n[����L&�E��[�g�$��������!&��}=)v��G�Y(�E�8'�I�y��+�> 5�O-��9��U�,�<VP)o�+�0�w4���;�)&�!�#9�_c�/h& �"+*2sx���\�����A"���T������PA0!�F��i�~ �h�vJ�jT�$�m��T�qIY�|��;1Ve1������/|�������.��[2�|b7j�^��J{U��y��������V�aV�kz�@����|�L=V���������Z7�~����\%w����D�,����1��	�
�.���#(��m�>��8y�����qn���������)�?��-MG��L4�:��]�R�,`N2�O�x�6�+��.k��+���ky������\���������<�������F-Oy--O9���)���>J�������k��`�A��=lT*�����W����k�t�f�DO1H���Y���B���_K,����W��*�:����yZ����v��;�$��7�|�++.Q�wH�����P���-��
��������h�w)��F����������S��j�������jy����Z=�z5�w����{��*�����3&��#MX�f�p��B%�ua��^ ofa���X�@v�W�a��*H;����+�q���3��	�nn^����"�"PV��1��F���E������6r����W6���	d��?@*�@��\�%}g��5����K�(��s��m��5v��t�TX2��3i/�gu�����Nq�kJ����}/�g?����L��+�8nF[�� �`1t������j��
�i��&��U6�E�$���e�Q�����:!�������y�b����)��v�>T�%�
p�L�m���.'GF�K|WI�����L�@9��v���K����(���hX�������OJ�Wh���_�j5/�����%�9��u}_���\�>����h����-���������'�gj��h�/Ow��"�
�������jF�z��U���,n�qqI��P8��J�������|�cg�/�:�+�a��x��P�� �s���]����/�0�q�#eU
+)�T��K������W�
�!��[��nQG���5{[Y�SO������.��'�f�q�)b�o*c���f�a���^�]�,hg���gB�?�����%��H'��Ur�'t"�c^��4���lQy�)�z$�N �����`l���������F�W�Tv���n���Zr�U��'�f�YZ��Q(`�Z����>R�nV��y7���,�k\���$�[��N�@���:C�:�)[�v�����{�0$�4��Rs�+�yve��>����3D�>�����^h�t���F���j~������
��R		�
z.�
Y�j�`�F�D�ZS���J��%����������{#UV���S"T�Ltr*-�Y�.t��U�����`�0��iUM��@�k'T���0�xm\#6r.r�xg6��F��>��h'�q�K�$$^t%����@����P^�E=�G���sL�st��n8W<%p��������I�oIho���`�����L(�4��m���s�A���bQC��k��M�F��������E�n�c�E}E!_[+�]B�d?n-L��V}�^�������U�.QK.�K�#�[
���A�J�5SL�`��M��m/#a"��R&�3%��%r���BVIv�Z^���\���P���'J���e(3��g_�-o������
��-8CX�^�����L�RZP�?u���0�7�C�K`%!�2��EY!��JJ��fL��R��s�S�]�����9��Y�{R��`^�����r��i��2��*4�d�@�eCp���&�+�7Pe
'�������i�J���}8,�����:&C1�-=��d.Cmxb��DR�����[�7�����JM���9NT�p�=�G������|TL/4Q�����b\��a>����%j��"����y����x:wr�����S:a<Rjs�z���IJ�����CA�����XdP�����pul)���"I;�������j��m��69�
�`���N���b^����Cu�Y��_���4��]g�E;�\��?��Z��d]��W�D<2E�����(1
a�������<1��L�/�^��\���X}����yiAsY��l�:Q3K��]
�?P�����Qsg��>t2���WbS�?�<��o�����8�-J!U.��5[����>���I�:XMs�44%��N��������7|�FH��ur�9l6�B�=S�0O������
^��9�e�W�f����|����Fr5\D�#�}_?�����s
����^��!����A��X�-�n��}}m�	:�-��y� {�.�E�a�#��&o:�x6�yyM
����������5�E��
�������$EP S�f��h�Sl�E.����xjY���I�p�|��{�2�=t����uX.KO�P�u[3�AY;����Rl3h���eJ��������i�����9����u�>�$��N�������.e}m��o�x"�/�`���+5�<p)L�N�C�Vl�����2���|�,��W���~���H`���.�����ZX&���)r!g������mm�%R��t��CK�q�����h�D:x��D�����'W����j�^pz�J�<�Gf@lr��>x�	�)<Jr]�r��c�[�t��.���%	?>I�7kxD~E����3EEG���,(<b\(R+yg(�����_[t�]{7el�u�R���I�1�t�L����+��E�c�!��
���Y�0A�|�Q&�g�aq��+Rr>�+���!�P�p��=�&X��
�W1�Hji����I)�>%�k��sU��hR�K���s3�N�D���+�w���n#a���T�3���
��Ft*���#��$���#	3���7�NW����/x��N��+NKKe��~[qM�Y���Z��sY��y���y7��'v�i%l�CN�%���E�����
r-���	9��L��G�Weix\��bI=�(�����W�$3	��c�YtkW$���c���������h �����:�&S��O������E��+�,��
�5��n��R����%H��
Ve�7��5�
���+���{!�sE[b�t�q^R6�l��c%y��0G�G�C�����*L��VT�f���,��e���+,v�� ^+b������?�u�!���H��2�������h:��f�(���d�92��-G�3)����:d{Q	�*`�B�Y7�4�_���������y���h��^fN���@q����"�A��6R�]e���N���y_�����W�����:����WP|�-��l8���5ty�j����+jY��\\+�z��u�4=V�I�R[�}�=�+,�����)�X���������v��a<g���a]>�4!�9+����&�CK�[3:��������N�t�j�[��ZE
z������u9v?�a�����L$�5�3o��_����	P��gj`!��,����U-�%_�}����1�k_�mJZr�^Un%���]_��9�(n�.t�_�Q���E�f�
�GE��"���QY
I���L��2"/��a���V*��a�:XS�]�*�I�#�=(��������{�l�|�yp�C�~�)iO��|U��r����f~��)m+b�5{����7�et��B�9��%A��9�y7��NmOG]S�[������p���;M?��~}��G����'v���yOE���xw�|��:���RPv�s�@�Kge����7G� �� W)�%��"�������5��f�W�~�����|���������MG��H�.8n�:���]����k� � r��m b�����-Z���G����_d�!8"���:=!{��)�~���[gp��5^�����3�P=�G�x�.���\N����q|�.QZco���HG�i��4y>�bG�	J��-�������G`�h�������U�eyA~�GN/s������|f������Z����X���$���7��T������e�[Y?pi�_MI���D��~�V�����?��S����0�����aPkz�������������[���U�G#ul��O�����WT>���7w�����f������Z��^��^m5MDN�L�|:��MhN|��������):�����]�)�8�*���:�Y������Y^����!�&������j����	,���L:��FB"o���L�:6({P]�p-�h������m�X���W���D2)�-Yq;3�,���-�$�s�OW�,I�&YiY�����l�P$%;��������
������3����	be�]<��C����U�W�%�{�o5\�^A|=��9&^�@���&d����d�y��%@l	�+!�0�
��9w�;-
��������@]�K�'g�+�M�`/��9��J{X�����	���U)J<��F�>+.���:M��\?I%���39?s��&�I�l�)���j�M7�:O��I�v����`��q�����x{��{�����8���������z(�����m���o�����T\���O��s�n1Z�ha�F��~���A\�A�i|�������d�����p����x�_��
��@'�jE|z�sa3!�h�e�U����M5L�����6���~3��~5U�r�mTrD��&�n��9����J���d��2�0���h�=V_������������m�����in�zC571Rs��^Ub#-M�z�W�����i5�0R�,����Q���n����~��)�<����vy�j�&e
��n�>�;9.�5�����l�r�P%���y������������W��K��w�^�o�lm��KjIC�)��9��Nm��QIb�� 
Z\��d��@�k�v�������$���zr#if��_$����D��V������KF7;���C��4�~�����J����A#���.��v&�9�t4g#�_*�4}<q�0�h��A��+v�W��������$5��%L�����P�D*N�T���6ca����\����YZ9��`�c+��c�2��;�`1W�Z���O�����s�:�
:FJ�vF�q�2��� d���MZ�S�4t�E���(����UQw.�GV������M kD�S��e��f����V�R��R<��U�]��/S�j�y�k�x+��"�y�~�����`����������k���-�B���F������0����"����!������`��4����������]'��s'Y�������x�� ?#�6�"4�Iap2
���'P����)���EYR� tGw��q�'I���V}�ZuOz�Z��/���������&���_0��1�L���M��w��.�mEf���'���f�B������r�P�uo���y�l�]��}�]�����d���	�����
��lz�-�����4C�
9�������6�~9���9�y�[#S���~����[{?�,��f�K^���E���j^'v����
p�4yN9��|��
00�_����&����B
G�C�����+,^�X@��H��Kb�A�d���C����Y��h�(�������o�_��s����u�XLm�+.g���_E+�!�~��w��p�S��r��`M'd*'?Aa�A��}��J��d)��3��M�e��i����'x�7���l�p���l��_:����SN�'k[	Y�ej���4��F~�
��S62�d�x���?�	��jF��������s��J",�a�,�����~���t�p5*�lb�p��`h�#���y)#hh�#�����;��5�sn]� �B�A�B�]Y�={h/k(���A��X�`��m�v�5��`��G��-6����\'C���9IXl�zSp�n��N,Z�!����Q�e�(*�gB�x.��7w����]om�~)��?����k�5�A������kriQ��%��7RwhuS���t����+�uF@�����fri��X^H��8!�(!l7�+���,��7�^S2�Tq����/�m�zS���~>���,��q�h$�������
lu0�A+��Y7��I.^�����XQC�Ir��{ H].-�+Hec�5@�c�����7��R�q�!v��Tf�22*h<p.��c<�gJV���xy��5���g.�BY�p������b�|��o8�����������09d�G&�,�n0<+���{�<�X�l����WO9���C���Qu�n��kV�m%�����<,J���`<N�������A�i�K�]�����������B�������G��wGgg�O/���
�w#�D�1��f����E�A���&WE����"�e�J>���N����a�kU��u��4� �7z����N��*���T��?-o���@1�o(����b:��b0������"� k0�j�����fZ��-)2z����u�m($��$��0����j�&���nk+i�6�]v{��b	��HuPJ�?��h��%���)!�����K�iT�����j�+E0U�,��\}�l��#~jn�'��d��P�J�����y��n����;l!�'Mj~>�(�A6�A�3� }�� �i)��2sp��E}�0|��w�p�l_t���Uo�,���G_
E����ev�d;[��(B#AB�n���7������������2����}2��D��m8Y�8;xwn�����W�R:o�4��=(���'G�G�Q�"4a�;s����=x~rf_%�/xs��<=;:=8;�W�����_n-�6�MK�*��(S�C�U��g���"�{�G��eu0��oN�Yw����u����g'o�<?8���
S��z�������W-r���j��j�O�fMi�#m�������4��~04���IT�������G����j�-L%0����][R�7B��7_�k�&�"���������-��9GD���/N����p/���������^��D��#�_�y�h����A��l$��k���\��Qk��8. (eF���L�
�{��y��D�@��}7)�s�vG�������]���w*���4�7��e2o��s�?&�������]r[�?B2D���dP�R�N�1�L+�o
����x\[;[^�=�z�����r�3�����cm����Z�AV���t��o��N,�����%�������z"3L*60�k�D����6b3����]�.����F���T;��5��j�|��0A�5�D!��k�$)���H)��
�D,	���&A�)(J6��������Z���������3o'�k(nFvY���e�G\�
�$��;��-���a�Nx��-x_���7>��E-h���#�>��f-w���l���>d�����(�?e��$�������������@"a�J�#�J�K����Xe��(��F�&����,����$�N��t+@��pbX>��
2�Q�M�����t�����&%X5A�����\)�������"&���3v�B}�Y��	����[�����Mm�ab���H��*���$l?� ��g�]��v�O����
#r��]��;�$��o�j���?��cq�Q�=PF�+8��-xNZ��'6�(��&��l�x
��Kl�QbC��*%����Y���7��D���j��b	�1l��~O���b��V��U�l%�eI��vSp%�
���O��Z�i��:I���U�A������dA����]� 0�Z?}���"U������l�K���GZ�A�%��{��0��D'N|���b�v�q)�
�A� ����BB���ZP�P����*��[T�[�t�!k�P�7��YE&�Al�A��(FU��u4UE��#�(����R`~��p���l"Z�BEO�!����������G26�bb37mY{��=`v�N����Q��6�K96NM1�+���
��������7u���f�^t'5y�v]�U���TIH�\n�@v���M�!���]W.s����*o�+���G�-ZUF���+�A����j�y���C����/�[w�'�S�������Z/�H�OG�y�2�:^��y8�����������!S�Ka�O���Q���c*�8*�mN����mtgo�����U���&"+�mR}��
�V|-g
���"�W�����^D��g����<
��7��������J9�l�gV��X��;B��}7��3�{P�fs���>Az�\�])������'�iYN���kR.��+�-�%���H�F��J1� �YiS�`��&l6���<����aJh�y�R��A�	=�\m�s�!�^l�%J	��$FV���������)�H�p������2�T��a���C]��*d�M��8�x�p���M����t�]<�`��J��+�	��_�8�K�(no�37���x�t���i_����dw�&��BI�C]fOP����G�Y����������T~d��B0$<��Q�x�0�<����)I���9�c�+>#�����36�w����*k��{�&F^���X�HG/�=���z�b4���s��KIU=7�����t�
{B��xq-4� �b����<���W��N��V$s@(������]t�{<R�4�>�7QfX$_�"�0�1�X9�Y�����G�S(i�����H��P�D��[��E;����>�3������,����t�#�6��|P0��1��*���SN��6�'S��]�%4��8zy��M��Tw��'�����s���������M�����zp��>������;+#�q��U�{]����DPp�f|�2	��L�Hk=kFE�M��%d8��M�_��
gC�g�D�+t��h�
:���w������$E�X�e��}�\�5��H���)��|�6�wz�e[����l�Cnskcg���1���S�������4 �]��=xP]�]���|�e�-�Z�j��n"�oc�W	[A��!���\��!_L�@�.Y���H�Yl�f-��k4c���L>�1.+eckc�e��lX]��>�uAod�@��X�H�w���j��� �On2�����*��"Th�$�t����sx�wk���X��&R����Y��d�|�%������@s�������������+�|�.�����`�������#A��qE��Dl.n���m��&������o��*�����7����u�G����on�hoVhoG+�s���/9��Jb���l��1J���(s�����P�=�^C�,�&���	d�K7%���d��:��t2��#���%"X��H��6�vv�Y�����&������k����S
#J:]�W��H�a�����N�6f�o�u~��e��Z�1_��1�="����{,	��"�t:j�\����k�To��0�������%�e�'���{�{Hu�i�X�P��%2y�"����G�[�u��_p�yR�R�����&�?��R7���bds�����nm,H=_���|dM_V���o�4lI/��[��Z��0���kI���[��t��yK���NYX�6�<�T�����H�Y�4�T'2����.��7���� ������?�9\,:��o�{�Q��[��JG�
�2�T$l����Td�_��w0}i{�P��5Cx7�V�v�_}����i��0�kZot`3X����������fflo�2e1��s-S8���NT���
���\��������������W��1X��i?�Y�������.��D��2n?y5���;��m�h�C�����`Q��������k�p-{L�����iyE�8#����/�C��wt|�3�*7��C�sa�l��ky�)nj�j��<��u��������w1��y�A�	���Vp�3|$H�;����n��lk?H�A9B���pW�_�u�|�7d�;��C��Y�#��K��'��CA=������[9�>�,������
jYX��]�yG�nNz�Q�Usi��$&��fi���+e^L�NHrD��m�x��`�n)�N����J�|���t��C�W"qN��`_g��b��k�z���Z�ZR���"�It��w�!��!d�����3
_�|_4����lDf �v�H#qTx���:�So��bcoQF�[+<-�=�����G%���|�:k�����x�������}���'�$��Z<�0-�^V+��3%y�	w��dW
#�SD�b�3�����=/��F�>�����i#cD�A�K�)�����<�j��C2:m��g����T�����x2��D��iQ0�G�/>ro�2����6X�5D����E�����a=j���d?Rze�������52��-�H���bQ�@�,0c���*���^����5�%�|��$�WX���^-��0t L;�U��>BF�3Af-�������T��M�3�Y��0V���qs���w���[X���c1����C���0�$m��)@Z58$t��aw�9�J�{��lrg}k#r�bW�(L!��2���7Q��Y��3�j��?��vt����=,��/@F�{p���t.>�����StsgP��-���s�	H8��;�3a��3��j/-���h��5�y��Hr�K��~��,t���� ?+���=���>Zh]������g�D*poY���7W�*�vP ��1�����h���'�>zZk��m��GOk�P�*�R�U���n����:J�`w�`�������VM����������Ue�-������w��y��"��<��i�{Z��ib$�����,��;ky`��.0���MW��0S�|���P6�n�\W�����xO��k[�����������L�l�o��znk��(�eebS�|a,� &ZHD�`W����/)�*q5|������H�j���3�-]���`��Br���9>:jD$��>QKF�r�=�{[h���/J�4�[3;$��2�-��R��F�t�6�S�����h�z���f���1�	����1:�J��m�����qk$��e:��zb`?@2[ah�����Do:��F���R�tc��]>���jZ�|�.�qG\cv6�6F��4��G��\�[:�#�f�{���B����Wu��E���?y����TrPl�x���1&/W��X7^>����<8I�$	�\�����<u.a�]r��P0�q�f�b���q��%���`A�^��D8��TB>��(4��I�P�$��g:=(�:���(��&���?��N%���B.�=B1x�~2#|?d����WJ8��r�u�i� {|u�<a���s*��	�B�2ZO�MZ�$���X�f�V<�T�P���!�3�boC�\g�(/��rp��eO39������y��E����;�_P3�6�3s���e5ICY*�38\SA.X�,����<�� ?4��y��E�N�C�nY��1
�8P��������Pf(
5[S�ny$����������W�T)r����+���SN����+�(�!�U��~Z�2�-�!�X����Hu�����m �[��2���+t������B�K��P��#>7�B�J�	�w�+����T�
�F	��O�:�D0?��W�*�i�������|�{\b��M����M��W�$b;����{ak^�O^eQ��e	���h�x�����q�����+Q�g(lY�hE\�����\�c7&`"#"�
w��d��0o��|��I�:��fe�����x�E��l	�����Q���\�w������M��H��h���g��F����u����hU3C�P��]n��a&��4��]iK��'G@����Ad�*���b������2B�n[�����o��+�+R>h�)��P\/3���j��?��E�a��B�khrV��������;��)+]y�j��������V(�E�GW��R��&��J���l�t-p�F�4�~C>&�������<����`���0|4��hf�$Q1�'3Q� `��@�A������&R����N�NsV��]�]{��p/S�3��1cS�\PA��{;�dj���V����������b�����`��c�D�'����8!�����#�:P�D����R7���Q�O�d��5|�$�!�$�tp����T�9���,�j��p�:�:n������:�S��(1�] �T���S;���������Q�������Q��=w�9��73fL"SQ���2U.�7"]1{��(�5�n���uGN�3����l9���py+4��&T�jn
e�E�`=���������+�E��S��x�RU��8�06��*��'�e�v�1!���&�7F�'�����i?��p��y����h�$o�YjS[�B����F<�r�^.��-��M���F�49V�;�{I�2��Xyf�%��������c���:�6����6z'@r�*���R�`�:7E�W\]9a&)C�l�|��TOt3�jnJ�������K^�O(-J���������M.���8f����Ym�r1��Z�K1��2����I0-������o�f���0�������i4$2�������#&<�����f[�
@T�4@&�UZ��Q�*� ��n�������Y�
�X����U��r9��B/��D��<i~x	��UeH�,�#��}���w��UqD�N���v���bu����s$����6wC����c.q)�4�C�J �,$~u�c��v,�K�����cG���&�`�/��-9�]`�q9\PT6I���)+r��l�1DN+V	�p(�#Vlm�����{��L���"��!`��\E|�70�1`�}ng��9��,�X��f����VVq�X�x�)�����t�9D�	�1>>�\�3,�J�*�*^ ��V
B}�X~*p����W�Fu�:��e�	\�[T�~fxw�v���P�S��Z�z�j�pP���^�=jq��{�~�7��%���I�R*m�N��l��^�gX�t��}ZN�&J]��_� ���z��hmAp�y��f���K�i/hU�/���V�C&63��������h�xK��n4#0�x�cr����"�c�Y���M���q���MH�����aRv	$6I!Py����&X��MDf p������!��8%ss��������[iO�f$>�����AU+4��ss�^2�J�������i�2�>�������<�f�h���5�9:��R�����a^9,�I2I�'WZ�*m�[zL}65I+�����\1����THg�H���M�~������[�J�I�M��%UH�P���447.z�n�SE*��R�=p�|�Q��V�+����D�\�8�$=����!<E�&jSr������)�{�3dQ�e@�Rj�������[x����Q"�M<5�\�'�5���"���*\������t�q��G��%<!��{����j�����o,�Z6g�b6�0_)+L�%P����K=�����h�]ec��������[P���<3����.�+�o6nA�q����t|mDu���
�f���o	���l�E��w�)��>Wt?�I0��>���P�0��2=u ��������:������\���)�2t����������CU����._�3�:"5�V6�2~9if������y�Kt���a����13f�v��pa���r# ����v������B��qtuIW���=s8�y����]��F���>��c������[��I��?��vw�h�_cQukw����N��K�w�v�D��u���g�W��#a��(��TG._��H�V�{������$y����pN�����(�6'�S��2�nI��Q8	�EQH��I��S�A�ID\e]��H�q+k��zoZ��&���������F5�	�������mT5�Ir�dF�<�B*�����2�r�1�[���i?6�$�
8�
E����q=��2���������|y��m�v�C���"���F�>�a�Qk3�����-V���
�\4h�}��A�s���S2�
b
�q2�z�o6��/�;S���)cU>��6$c�s��2����]��o�
��P�M��5���>z�*_�W� �(��O�v��T�_T�	�)lr)G����t��:�3^ ��c7����Z��PR���o�T.)AU���l
,h3�����Ud=IS���)� ]���!��JW��1��:p���#��i�� ������dyu�z��Du���t�8E1�d
e����>�q
R,��XK�lq��^fJ	RL��
�d���8��bU����q����vxq|��9F	fJ<�/��=0n+�4��(�|�$������]���vj:2Zm$��i���Q!���&��~M����BF�JY��P��f��O��������fQ5DIY���jm'�I}��K@���I	��^��C�O���9S����}�}^_+�N|o�#m�j��>X���l-u��+~��Xwz����v{s�j��\�W{�^��w����S�b9�BJtX�Y�!��(E���bj��
H?�
JNM#����Y��>�����n6�������9�0���3�������,�Y�t>����P�'aw���U�cxq�>)lbFcq������W[���]����V�O����I�6k0H?r�8.t��"�H���6��5P��qU�+x�������t����'Y�v���+l_���
J%<�}�b��!�j>@�N�"@_4f��^7�����\�!2[7��q��v�)��r7�}�u�8�*{�j�Sf�E���*2�1),���z���cH�H�k)�2��)#$������u�` �_�RA��5�������&ns��(2��� >�_]����|s6L�2�ox9�6�����g���FRBN��<5�[��=\����5\�58���U}������������m����#��z�"/Q�kR����x7��(!��1��hZ��-$Qa*f�����d����b�vQi����t�]O��1��y`�x�]����+]�O��'����&�KVM�9�)��|x>��� ����
��������T��;d���WN5���+z��/I�
�a��Mc���pF��������+3g8%����v?J�f�S<�GfMN���A�4��hIs{}"�������$���
*���"���C���.o�hz�����n����f��C��	����|��w�2���06c�4������&eT
&/c��
FKc��[�B�~�	�EF�om���������76��[�X�+g�c��3������z�pta%� ���*�y3#��P����oF��X7��V��V�~��2��&��|sf���/�?�f�F4��������Z}�C�����!��F���~C���������W����x���dX���^p2p4�	���Dt&��+�w;%F��E'��z��Oy�����F���J��p	3U����}q|�����g��@�3f�e|i��_\����:�_;����n�\�Wiv�d{��tRw%�Qq,:�K��S�7�.����;���E�$���93WI��^��
(�c������f��5Rt��L6A�o�@�9� /��������`��_O��,8�!l���f{#���]f��v{��������=��T���wQ������@����s��Ss%>����#�����"<sO�n�������������!���	�T�cgg�a�cq�4��=L

^'�[�~tFCs�����h��/���^�"Dz��4� �0c ��~��0��] �O,'Y�k0��������2��H���K�*i�1 wvQ���BY�1����k�;������
��������pWT`��Z���9}&6�O���Nm�-gJ�&vI��@j�b���-�](;$����7x�B6\���!��i����o-��\�]m ���V!�d��!z��aNT�X�UWF�I
�������������}���m:$��������or����)��:Fd���
1�����~'�����~�{�;�E�^Pb3,��UG�����Z����W��]v{r�ws����&����y�hTtoL��Q�(P@�8���U������F�������a�`�1W���"�I�����r-���L)�����F���������h��=���{�|Tn6��B����|�*��pr�n������~�\���A��/;�9��MY��z���<�ek ����������m�B�N(!����0���W����U`��|����;��/A�14J���?&�U���d�Q
���^�+���m���_��`Gp?��wD����;o��K	��9\�mw�<��,�������O6�vz{����k���� ��/4����K��#��=*cb9B�[\@i]�V�C��[
��G�hF��|M�������.��|hB�Z@
'C��GY�(����s=�iQx@	m	������s/"������:�!��+%�}Ab�����3cv��������B�l���1m��;�d�p����4�-��D�*k��2;�\=a��^u�{[��Nv��KgH[�m����p&��}@-�M�$��bK}L����ifX\��G
���2��jC^���q��c����W�F]�F�b���O�t,Q���o=j����C�i�phJ�_��8\�#P�&\�Rw��]�G/3�qO����e,���������	���D�csck�<DK���]?�$��uW)8dlL�0{�r�!-���B���((�������F�'|���������]O�9}��������Qc�B��a&cX�7E9q-�~��j��5F����)�*g���������!��q�������������%�?t|qu�)���R��y`��j#u�8v&��5�{MOsf��2��[��4��y�V�k�����X��1�{��3�e��E������.�zO��v;�mml�l��u�q��{�)����?O�Y��!Cd9
(�1���.7xO�I��~�_,(�� x��|�m��:TJ0�j}�����z>O�b'�����V{���_����������aA�����{}����X�|�U�c�b �*�X|������������Ui]�������3������������d}sww}�%������dR3�^��G�F��7�9��&���?/!kc����g���^��d�����;�������'��Y��ex�[�$�g�dc/Y_��/�4���n�O^�%?�"%�k���[�5�F��lI���#"~�����`zm���O������~{'i��l�7���������C������s���8rE�!��CP(��+�n4.n,x7�J���C@o �:���Q��\���A�FI���Qz-���X����R�^�knHr�e7)J�"�1�qX���������������23��%zC�)t� z���\rA�!�1���r=0g$X-�����Ed���FP�7rI�+�5��X4���N���%���� ���_�e|�	�.�@��]������S����6l_��k�2�p`�[�1Yt������+{�yO��S���p�\gRg�a����>��`\"��*3����{��mLMc�����i���T�yV\��N����E��tdt������M10�z;��V���0+o�C����*O���2�i�e���6��^�}��c#F�!S�q��0����@�6���l�"+'��;�&�F���F��b����d2*�_[���m���_�v1�^�x����vx�2�x�:������p���n�e������wyt�����?�������b�<����������l�������5�Ra#�K�y�~��A����f���������A����Y��K�s����1���3�?����[77��^�=bW5�^7X���L��+��k�Q�LM���lc�I��oG�A�s�v�"�xy�����5��*�=�I���0�0��uD�(��f+��x�%�l����!$�����2P5T����^x�a��������3v�>md��%�#[��'����������m��������m'��k�?n�����������mF�u.�y�G���k�
�����[+8�h��m�7�<������T����q�V��� �9��19�eie��^�:>���u���"�:��j{���`�c
WgC�Z�6������UI��S�X�V�PV�	�.l#FU��X�|NE@m4�!�B�h�(Y�h��������@�g���V�j���yo�w1n��$U1�`P��(Y6����'��8����R�A��$<��%��1�B�@jd~m�i5�8)��Fl^�a�Y`�+@|���-���im�+Z���q�����%+J�RDn����=����f��\����,Y�D�
�x|G�<�b��A�$���$��J��5;�u!��-~hwX�D��`��
����"I�2��V������u����K��
tlh
��=�Qj[�0D�a��N�, vT����c7��+�����4i�X���E�i����;*���:I%1����p���9��f�i���$������X���

`����i�\�j'�Wz�c`��2�j�����-��|8xi�P2�r�`�7��b��9��� @
ue�$�����^cH�/�I.N�+�~�B�:�t�?�Zi"���6]tF�u#�W��!��Ba�x�1d8U�z"{�O���E"�K����'��m���kP]����#�NH'@�2<��&�440N��*Cu���2��2N��x��^}K1��g��{��THL1R)3���g9��
J(���;6�!L���8�3��JABo����YKo5�)`������A�C��t<���n�%5�� >���4CW��>���}��V�k)���vH���mP�K��(�Xs�^W��������C�o������8��k�PJ+��r����a�/Q4��2�)e�M2���
a��o�����
�
�O
�Yl4��!�3�Y~>�?���l��M\_�)�jbS#S�%��q��
i!����{�n�K��>��
������xq��9,Z���&�h	�9e ������M��N��rX���xb��+�����*�ZX��z������L^�!(F�9��P?ffO�-(��
� ���a�V�	������������Z��^^���/��������������u>�P*�5��
���P�� �@�pL���8@��^L7����"^������KLC�X�8�/���j�(n,-���s�$@��W����F%���A�/�x(7�tR�C7t��;h)U=�Pi�w�����!���&/CW�2�5\����������r7*����wU�	�cW����p����
%��
�u@sWI[��gbX�%Qz�
���[$��m:�_B���D��j��:��[8���D�
&��4"0"\�<]b�=�%!���7r�3f�^K��m�YS �F�;�,{��"J0RRV����T�-�E�@b(��
�2�`A2������a/���u�h���0�2,���[�8d.yVi],���H�
oU�G���>T�J��^�����/���4or`�+JHH���Q����)�$�P����-^�@D@�]2�14{�f��^/$B1k��=#�	O��-B�9~d������������'�#%P� �3��cW�t��!��:th�f���k��%VX���d������/|��Q�W`?>�QV+���Y������d���o���f$�^����h�x���$��s4~�����o�Q�=������>�	����$5�� ea���C��fQ����v�B�������k�M���t$�-�b5� :���F���D�c����cg��s��%�}����r����@��4-��	{r����1+�-2�F{N�;W�"�����S���X1W�=�<NFJC��SjU���n�.-�f�JL�t��:d�����{{�S����/��:��������	�H6�&�������O�vC��
�;��0��BJg��07��r9����rbU[��Z�6R1=��=����E���&����mg�5�~��"%�A>�+���"*�"X�����s|�������tpIe@��S���")
���sX~����i�`����KKf,��~�p95����f��l\p#�a��)s����Td���c�MN�g8��
�D�;i	������R��b?�X��Xr��-H�.I�i�#���#�����d.�����0	��;����b����>o�U��3%(~�M����r6��n���b�wf���@`����;	�b�BBx�A��������Nqu�������=x�)Z�fw��w��Y��&�Y�s7&����y�!x�(�C��3������)��5�(a����Z�
��\�M����CV������d�Oa����c�d1GOH�V��U���+���\���R��_���ZA��X*�@�����BT��f��G���6����.��q���%���X�hY��mC����2D��M��TJ
0�1�
t8�V�����<s�����@���x���x�
��\J���._���[Aw
�
`����Y@���1��9���/'#�E/��i�����	�
�n
�7�������2<��5�"�T}��,Y��N0���o��j�� �MQ���������g1"�� �����+�J�%}S#�Q�hX?AK���F�������H�K7�lM����N�C$R4��K���	�"7���f�#am���N�T���3$>����T�"���K����I:n#��8Gs������1?��U��@e[|�e2�,o�s�����g��G4gqYvZ&A������/���y#!#=�ywr����T~��r��a��a�����4��JM6U?�Q��LH'UR73�(U����l�l��nD�n�Us����
��M,Dd�u�{��C�G�Q��s?U�h�����'
�Y�V���";YM�4{���}��q�_����CL��'�E9��_�ivZ,���:���JG�)P0�5�f��<g%��p�Bs@?yxK�zj�
#1�:�����=��m�"Y���!"���b}���K(��p���Z�X�Tdf���k���q'����o���e�c�
"���*h�Uv�+[���o4���(� ��$���c�p���v;y�;PEs/�Q�{�h"��K5���#���S��p��Of=�A�;��R�>C�W�-x7U�����G�cP'�^�H�o#T{F�0�,�<�=,_
�U�z��L�D�R�uT�P�la��M$^+f����6<,�e��b)VU�5��%�Z
�h��I �b�5�����<0�h[�5����RhM�
��=�uV��<����C����*�/�������Z�B�(��H1 �>_<%s�I��)J�em�D,�.���,dd�x6L�`{��J[tH%�[a{��&�J���r��0�"� ��bi���F�CA���3��P��B���c�N�a���`�<��oa��?pwrk�P[�����gG/��{x��IP	Gt��xjG�v�����<���	�����EDd��K3L�vO�h�i��������`�1�N��9$�,A�^r��y�q0d�P��R|���V�p�'4jVU=m�d-����'��.4aK-<�z�
��6Ka����Z��wX�z��r.h�K�V����T{NeM����#-{�w�cL�r�T�����P@�1�X���G%�#���D��+}������ oXXc������`�4�_`
[Z�P=BpWW�=�z9�����K?����*I6��9��h����?8��@ �te��<�?�.}L�p��N�k�P5V�o���?��I����+�Y�^�����j�c�����,�Ke����z��7��[>�8�z���'DJ�;5*AwJ�X=��H}�IHL��{�/��
��,��(���/.�5�w�6A�V���IK��,k�X]h<�p��������[5�����}�JFze�����m��a�6l|�������`��lR&��W��|�m>\vM'F�RT�lK�W��Trj�dh�&*�m���|��t�V��L9��+I������5��k<�P��Q�=��%�X����54=���,	]�s�h&!�\Zd���nY�@� ��Vb��!����>�&�����)3]��~��Vh�/V�%��C#�\���(3������[��[h1k��\1��|8��>�����(k�j�Y6�Y,�<j��-��6-D��!6s�!k���5f�����we�0�ANa=�����pw	'��[�}��@�'��l=�R�xv��u�f�
���RIN�eC
\��T�3:�*�B���
��q��A4���b$r	y,�������O
cdGX�i�Y�c$��2�CL��jJ(�\��$`��2]]M�~�����-����	h�����)^�c�SAF���� �����(���
���B���[�m��n!"�g[����W�	2d�J�<� ��X��wnT��/���	��yQ��'����e��1P"����JX���*u'�S�c��������AO��j<g��:����7_�Xr �d��c��xG������3tV��P��1M�H��#sT�9K�<��eY=������c��{A��V��k����
��g2�� TdA��H�������h��<,���\�ce>�8#�R�;>��N&f��"�)�1$�Y�~������[�<��M;<m�������c/'�}���e�nV�q�Z�����#���~��l]+#G�������������"+��|d�}(��(�xyYl�^�X���E?�n��`4���O�o�����
�L������kh
���������V+�����%���
.8����O�3�H���_��0_1����I�;��6��Vbq>i�q"q���,3�dZ���8������2#�ai��l�[���3/��_o�!���[%5�*�en>���8�i�F"st���f�<
�i~
j~�C����)���0@����l���Z�&���h~����J86
�(���^_qv<���`4������[��
����+����V~���
[��������������7��x%X���{bO���S�h]�]A������%j �l��5��&�J���\�����bW,��>����qr���������LQ���������a��a�����;U�Yxna�C��X�	����f}�B�PGX3��)^.:��c�a�6�*��FX��W,��s8��Q�k4#��V�k��6t&����"%Nz�IJ�"Ah[�����*��A���g7�21�|IL,����������`Pa����7����\�l#���[
�������2�I'�Y�!�����V��f$� ������9
_��r��:sR
��Dq�qf!�yT���<@g�3�����1�x����f����� ��>�He��g�[�{�OkdW{:��W�,$�Zo����l"y���s�s��>;1��W�WQ��t��
�#�Y�(���[����X|a
�S_

��#�EJ�vA��t���
�M{�����U�UPt�C�L#�j�Z��TV����,9.8��bYt��lz�����B���#�juU��T�9�/#����������������Wj��y�Y��e��:q�ILaT3 mu�
M��>K���gG���w/O:/:~�>p3m�*��<{V�<������mM�s�-�����x������t{(}��9�f�H�<���$!Fb��q�I���D��n_M��]c�|��f�9K{�����A������%tc���	��C��6�!��XV����"`.��:���p����7|����
�g�Ky(pht&3��^h���=�����N��-E(3;�~�ei���iBu��`a��a�cO�/���
���@I�#~����
@������'��������9�X��y(�$�O���L���DG\�as	�<����k&)�!���p���P��&a	��\)��v��_�q��4d��^���^Nq&��S�r�������}A}*��J��[Ou����:� V@����)o/a���R��`���ij����:�$s�:��h$0�O�5@�O�� V�����M���0kb��tj����[����7>�6�qh�M$���~e�"�:~��A�8���	$�C6x������#��V���0>sY"f�r��t����� ?>���9�����%�������x9��L��rzY�4��m�+�|�A�Y������u���q��	�{u��*������N2��:Q>�?����w/��~�r�@^���+�y�&�D�E	�����&��Bnf��i�����bt����H4�3LP����"�/U]��m�k;X�����M�q@�SE��B��E��w����P�1�hd��9L��"����jY���+|;rW��P�a����:�l�3�V��`Z��bK�	��s����`���[��"u��J�;#:C�E�~�����0L����g����t�0���(�6@�Iu��Y���8Z�3��P�����{��U�W���C�Z���/�\7C����.�������I���x(U���)8#��blaX�_i4�.F�^M���]�nOI�W�)�"��Y(1�������b������W����|���f�� ����|S����`4�h�0&��F]�1��cN�V�.����m_����g��3_���?�����W��/_�{����Q������w�;A,�
���7x�a !��D�t�s��^���*�E��NS���)��6#NI�6J@�������M��`T����\4�f:diJ��_���dN�I��YA�.���JLB���X����3���5��]�����[�Z���2�{P����	��"J�����
<n��i���d���s������Po2c����+f]dx�l�4l%0��em�#k�%@f�h�w����D���H$Ke���j�� ���?3�R)���t���v*��F��:E�	�-��Ct�y��rG'��!.�����k��V_�de�=�^����%�/����(�f���m=F(���z��
��w*�a[�}�o�Z��[�u����6s/����LN���Q��-��"��� LM#pDPAk���N��K*�R\3�:K�:����W��h�U��%�_��3��Y/PBh��uH<��&�y��L���Ct��uV}3��G]D����e�k��M.�����c4OF��[��SU�Ay�{�933��4�_�����!�5���6;���G}��"���E���ag���:�� =>�P2Q���*�� ���x����ga"w�x�#�g7MF7w\�Zv3:�!�k���[F�R+|a����4P�:]i����������z1D5�Au������@8+-��,�R6�Q��EU��GM��7_�����#o���S�P/�@o=y�p��{��
���a�OX ��$�]~��.v]�
�J�����D�wO(|�*�!�]�D�Z�c:W"�4��v9�������`I�l�0[:����Y7���eD��c%a��Fyb`H�zw�P�����8t*A���T���J�X�/*#Z|7A<TY���#�@ME�T��?6s���R�M.����0-o;9d�������<;�R�pnT4���;B������N/���� @�;��8�
�z@(��p��W��Cx
6<ql���
���G�@���H��UZz������'�
k����z���0F�yn��12�s���+�G��"3<S�K%��#����#!�LW�9�����<��9�Yq��0� ��u�}b��
�����:[]�(��v�S2�T��]���K�+Z�%�	�*N���edS!�g�/����k�=�&��1�~�/�>��%���T��2� h'����[����D�*Cg�$O��`*2������Z�d�3���B�����vJR�~)����n����G%�����9�
���	�|
p��DI�8R/�|'�9����bX���(����p�u��y���6r��P>��J���o�����K����#M8���-��]C�/�����B��,��P�R>8w����������m�P�[P�5���Ni�4����x�>9��I�����tb�GQ�`^�:�#�(r�b�"qz�[����K6�f����b��7���pY�6So�!���z=������y��G���mq9�`PA��������p�i:�}����<�L�N���Q������5-9xvN��_�������*���"��4���|�Ch����![IT�*<SLwWX
�m��UR�2�)���f���VPC����jce���s%������hp\�2x8v��`��i�����q � 8*B�uF��ec``�]I�]�{��?<W���a"�������n*�>N�q��[<t��$?����q��)�(�k�nL��u�z��J�����ttN}Ssp�80)_���d�����X��k�����E�g
v��q#b���=��0|<+���S���Ok��^*��_�{�u.��Bk���Q�n�9X����O��p;y3���QHr��
T�[�<�AtC	w����(	:`f�w����Y���RIXY9w����XU��9Y_9
�$F���;�?��^���5"E&C��r�V}��A]1Z������@���.�/��W� ~n�iZ�D�����_����~�5���_�
W�J�!�� �����gl�Qq��3c�B5�@8���`�?�BfM&[�9�i�� �E��tk1��Ge�����=�S�G;������)zt��x���
�gdj�L������R:�����H�������Ft�y���~��,j�
���H�w�Xm��@nzx�90#���V�"hF9������6�n��a����q2`������|��/r�q�{n|����X��[]���������/f"�U��+�����l���O1t��9D[��<������
P��!?5��P�\��R����cvxP��On�����8���a��#��jFG��7���S���E<��{�
SW�th��&IN�\=4?�F*��� f��r�����r,�W3�1��&Tp�k�3��(����,b���	�nQ~�|N7�T��1��J��s����S������P��������#�����6���.��">Y?�5R�r���O�xRFB9�;�2.�x$-0��$8p��6��r2-���cx���83��I5XZ�u6��?}DgE���
��0��bpAq�~�;�
��-��|���m�����+����H�6��n��,��tP|����z.�IC�&�_B�}x�x�X!7�L�t3Q��#��E��Bt��fP�*�}��\����:1��G�r��)��8�J ���G6��`���^���B W�Q=��c���N?�y�\�b,�A��81N4OQ7�%\�|�E�������I'"^G�4Y����i:67u�0�f�E����66V:qgC0��mzW�4%6@�)�e
b|zq�e���dG�e��>���A=��(�����e�Bo�%Q|���Z����,|�x����V�������v���~f�7���2�>�e\��u�L��`���hUT"��J%�i[�j.0���3��������1�Z��vu���{�7.���j)VIS��7�C"����<�>0����H�>W��]��G�M$�^Z���t�#W��~����b9-Ha��i�mG��\0NQ�2_��j�)�i&@n��U�SW�n��20b����$�n�l����x1��0��|�v�KEi�6�/���-������H�u
�R���w��1��(|M��5%����t�"��;�i\T�K��x%/l��T�j�4����Af.X�����'@r7p�SsKM��U���1�������<���&�q��Det�+�@�A�:3
��������h
*m=0�G]��AQN��������-�4WL��Z����NI�*[��x���Y��3Ly��P	4��+�oI=��
� �P����$5S��a������0
s@Q�������z�\I�e��\����/�IU�B��2�[��
H�����W�J��f������K]�������'����6�W+��J��x�L�}��e#�A����W��ePc��_�k�����G�$��)�k�\$@�u���jk~��')�El��'��,z����Tw�G�����!*C@a
�`�q�0�^a�I��������9�4,y#q�r#��wR��nx�c�q ]���Nt%�qf�H��%�75��g������o|��8���5���r+Clw��:6z������L����QYQ*�!�����������\5�p#��1�!f1�qj��`%�����bH(���
K��7����W��x]QJ����
�5J�u4#gt�V�iqQP�p�W��'��[����a=;m/���-��>D������ � �g��rue;ta\I7,gBsg�&����������5�{��\p���;������kt��
��U�b�������P%)��/D�
n��3�\�Ak�W�c�<Z�F�6���.v��Z��__��V�"T}��V8s-n>��,���� �t���J�^�}�.U"�������PQ���kYa��>"��.*d�������w�_���|����t��������.p&l�j���!0a;/��zz�wY���"��S�	L2k�
8�g�=�}���.@��N���\k�N�+b�+��<�������������~�+��FuB/�d(_�{hcBT��p�U�s�#RH_H��(�g	�
�KU���+���%�����~
��$J�v�
��9���C��3�����|$%�Pn��J�!��c�
�Tg�
���a�V��Y��u��t�U{���>Q�������g�)����<9�����-ED���7'����j�K|9X��h�z�b �K�tk#�@�������&HhY+�?m����M:����F�1�(b����r����	f�r`:�h����(V��4��@�����V�ima����F �$6���������S]��}?�R8�������
������e�:�{m��J
<��!b��bw��5�A��B�y6�T�gT��b�p�.�t����JN�E�&��x
�!�-������-v�0��`E;���Ts/�l��t/���s?cl|���������
$-"t2x�p}�����j�������O��B�!-B����DILh37>b�Z�o$w���	���%f����-*�?��Su���d}Oh��D��������&
e�t���AqJ>?1��2�����B���f���	x���G]��n�|8�3b���<��������i'��@2�Y6p����/��AiQ�wPf�l�1q*�T�����%3�V��9���I�#6�7���p�PN�?$Q���piiRL���m�x&���x������B��-1W��;4|�u���\��E[�����0s�o�f�.�%�F!��MZ�E��-2��5�;���s�et�;[�C�,�0*���j'��;QL�����z�Yc<�*��|����,��'~C��C�� �W�7�FL2��\���%wJI����#"[��{O
�2�nnHd�{!�<B��xF��#k��=>�n��J�������y%�Koyh>'�CS����$��H��*b�.1VK��J���x�~\	DA:��-�K�q����t:d��|�Kq���g��
B���m/�Z�+�
��� �S�I�l��a��.�P����%��Qy�1�7�������!�c^\�D���]G��9WQ8F��r.cpU�|�����I��=J{�p2��w)�"�lx�?6�8��Z�\��E>Vm\x9FU;����*�*q�l/*U��A���^���3�Y��M��8���=�3$(?�_	��A�
d������z�]x{� B*�#�VD�lwd�
���V�~�;*V�oC��5�Y�H��y�j�����L�:���r�<~x�Z�M���Z+�w���r��yG9�<rG�,�}���>k0O���
�����	.��I�xv�!0����u�B�,"����.����g������c4����e��Q��at�o+���q�OG����P���#�&o�[�DGjB��@4R�\�%�#�	���W��D�0����(�Cx
����=P��2�dR�#V�-����V�jy?<%S�
����Y����p@0$���k�����`���qIh>�K��������m�W�OOk_%�cEK�y%rN*�X����?4��zl5A����^�VQ��?f�gAW%�5�]i�G�@�z��'#S�{�YR/�%MEqJN>���"���#Z:i��t�6��J
����Au@i4b��E$���W�6Smx���06=$�pk��gW ����WA���>,�f{��V�
���~"����XP
�E�3z�� ���K#������tB
.��E1���{�	�������`R������U��6��������B(�(��qI��3�w�{b�5<��"��]�w
qED���@��u�C���w��
���r�A
�c�rE�|���5[G�t�@?<��l~�g���q��59;z������GG�����Gg���������7��w�����&HT����7�_��� �8������G+�9�h+�|��t���2�U������p�7���	�HCG~��#��y{"�D���rB
�R��t_�����f���N���_����H�!2P4�&�C0�T��A�f����#����]���n/a\6����x��$"�u@qjXO�l��$5Y/�R��y�A�8���]�'b+���l�=,y�\fix�l��91��D�=���&y��j��LL���S����(�l�&�=��1��=�)OY"��d����m�L�".&>�S�n!����
��c�;$5��#�1���/�d�!������������|�>�Mh��1;|��mfU{����IH�x��a
��~�%�5�H�f�Y�SX9���D�J2YB�b�*���d�������H��b,�W�^��Y1���@������Z����
j�����J�9���=�aK�/�����a�"���`X��\�}?C@Q'k��&�/-@z3i�b�����sN���8�3��S�����b&������=_b�q���9�|�������<��cz�^�3:h�F=��B���3b�,%?�O2�����A6�Z�����8�v�����g�j~`v�nl\��'�����,��L*PW��w���r5%?KR�I}���������`^��VQ[^q��r��v�-�0�@�M�V`�'�HY ��=U�zh�l�f�q��;]6�G�����v����]�U����+8���@i�3�z�V���GH������X�Td��c���u-kS4;�}��>R ;0��D�T�X���n27�yA77�B]��Im9�����r��hp�2Ja��[.�4���t���Hg�"�������A��x8��+c'�����`sI�0��.��uO����PK���G�:���5F�_�
A��)�A���n"%�����9i�'	upP��!��q�	��+;�)�^�NqGpqM}�`�!9p�g>�Yl�J�k���8e��wq�]Rv��j�0��2�k��X��[��]q�h�"X6\���z�����L��f�ph�vaFte��c�|�t
��&�o�6����_]%��� ?����D*l-��M
������&���|4P�)3yc}}og�A��u��no��6�������Z/��6����f���G�������z��hm�ol'��op��~�?���>
1�	F��D���_�O��<-�pAi�����%g%����]�\��o�7���8;��7��~q������_�0��U����6�^k��a�����2RGn��a��a-�������k@��"��M�l�<������G|\yXN/��?U�������6��L�w����;pk9u��T��br7�����<�p2-�zY����?��u�������!���i�������XZ���V��j
�~LN T
;��(��Z9%�vg]
��p`�on���T��x��$�QA�`H�������!��I<�S/5�����J�
,���H��	Ur����W�g#.��KJ�S;�c�!��
j4-�~��J0'A!m��Zw5f��/"7pF�xg\��}�g���qoen'�\��I>�B�������N�^`L���;��Lt�j3�E��K���wtzv�X���3w���9��'����9�3[�{�m����=��oNb[v�q	�V����������s�){��u�Tx����Q=#��3��q����D�����B�=����b��������o����g���mk]����=x��LK�����X;�hS��I
��_��������t�55.=�l�����l�X�nw5}�lz9��<�<~i�XIt4z$���3�[,��F�
����;��u�������QB<�.��Ro��_,��JRmM�����d
���TT�w�a�3*m������������"j�S�^���P�M�4�Q�8(%bd�O�����������f�#,��w
��`<T���B{�/����$^��6k�w�)uU\��b��L�<�mV%���pV�]����C���p�F���j��P�'��	H������D ����e�c��B��7�XQ���_lWZ��E�����Y#�w6����B.U?�KE�p{X���`OS��1��;�<a��9{x+��c�����C�	l���������hE5�����\��"q�3����q��U��}]zH�G�Q�j��>�*9��h�(:�}�S�����r��UKVa�j��q��Gl�����Wb������R��6"#D�	y����]D{n�1.-|�"�+�W{�/	n�o�����r��X��B+��Y�B����Z6��'�H��E�YZZ@�B9��,����o��W����{�RW�	��I���.chw��[�}x6Q[�����Zx��o��W�.�c ~=`���%����M��;�
0$�X���=�]U6y������VZ�����O(�>d��a�W(��F�J��/��1�,�i�����O����[�R��<p�@�q;T���i���A�uX���o�_�_c��z��a�O�K(�����1z����4�>T��.�����4�%V�l�4}��El�w���,�1�j�vM9z�4����l�{�~��y�����cA���/�O�_���=�m�o1����D">t�	m�6Z��f+�t�~I-�.+������&�4����wy�O�{�-��-���{R�q�Mq1[��p��i� u�S��o��
����� Zh��-�m���q�sl����7Gj�~����.�y��|���V_�q���g�6����w�.^?x�TQ������c����������� ���`_��.��bB�� �S�=��^v�N�jvP��.��<�M��t'�S�?�#��$�+/})w.��;�����A	��tJ%���8+�qW%Xu���y'�
�{��b�CW�Cw�s��T`��2�E\+9��A�������5}��+e������'<I�>`���i@��@��\�r���Q����+[Q����-g���]Q	)]rH}��7!�����7��e�g��;�F%(w���*�m�b�� ��
��������	_����}������q*A�����h��M����_s����/����2��XQeh�����.������k:��
+�����~��=���b_DXD��?AX|n�-��V�E����	��Sr��#��]y�U��W2;/�l�'���a��u�0",�����`_��z/n�������/2��[{"��+o{�'���M�c�+��/����r�T?�<�c/I� �1�=*D�C|sx�����f�P�	��.����:9��E@�?��`4�U�����)���D)���{���w6�;���H<����Xkq��w��~�-���~���2$>DP/�$Kif�����������b���s��N�#�w-JU���f�JDHPAu�Nm#|� ���w~�=.Q�k��1j��kU6
��z�3�*�������]s�����R���l���#�2�c����t���uUE�z��������TSPf �d;o8��&���l(�Y�{gN��M�����W����P0o�)
�Pm(����8�v0}c�w�N�������S��E\+@&4��/h�C����04lp7��O�O�'���Q:�*X+�
9$|�8sU���<�|�~ ����m)�-h%�v�6����F��G��I��'������=���>��O�����,��n����%,s��R�ywrxh$��&4XV2���Z��u5 �&��O�I,	����j���o:��5C��,�A��m�;mx�T����W���H����I2��q��Fv5c����c��Mhw��d.���]*E�3�oCh������w��!�Kk��p$�^�3v?M�sF��[TV�}'OMMG�Y����tY��??��\�����,fJ?^��l(���3��s����u�����L���~��`���1��Ry�Dd�n�1To����y�j��d@��`U����{E^���g�\y�8�����a@��Rb�aaf�F��"T���"������x���S�������k�r���
2��z��,�l�U���*�g���^�<^�u���b��9�����e���|��x������V\j��
.�������/t	/s?U�����-X�`��q���~�����i�M��h=���{\����h��cV?�Vev �W�{�����x��w��r�� �{\������[�p
lD(����K�+���p����W�Q������~�;��I��L]��!�	�H��/�����s���I4�n\B��jc:Ae������2�:���6��S@p��"7��W�JT�D�UtE�c2�P�I�zB��0�S�
n�M'��FQ&���OO�R�q�?�����&7�����mv?(�l�L��V���L�������.r������o�X�}��7 ���%��6�i���X�����vZ|����"�/S�����������;��kxV�I<HN�xt[Vr�\��[}QJ7���� B��8�!K�C����������
���j	��B�I��\��]����l����
��c�5d�s����y�zJ#������]�t|Vn50�_��C���_
�����_���:M�jA�_�����`-8�&�7x@�&�LI[����<���(��dj�
��H�)$�3�E�t��]&18t�v�_G���p\�m���E�����%&�Q��?������������m�����#7������d���|���������./�*[�Q��k
���������h#�zw��.��
�yj .E�f;8�R��yi�� ���Jh�^e�����p�����X��*n[X������*�R
�Cn{������k�[��.��B�%�]W1�~\	hy�(W	Z+Jr7������������K���C1�!�����'�3���r)?kJv���a�����X�|�%��Z������>'��(��Pb�vAJpi��0�����bTm�+y ��*�JE���g�.�3�v�� ���Z�����U������e�P#���pO��WD{Z�v�'�����Kz�TW��-R���i�����������������N���;��F���'���V�:����:���IqTgKMK��W�q�l�d2M'S��T���AX���+j�/Lv�p����2{������;Ex��	~'�-�Bq���E��d:���s�k-=P��g�����]u��=��d���N6)�Mn�j�����N'���K�ey���fR��Z\cn<����<:�����%�N�%C�������
�96a[�o�-��9����{*���.�����K�Y{�k����[9�c��Tk�!�����}}P��V�vv_��P�uwK����@����f����~�����E�oPe ��i�<�
����$�*6���E@t�����"��8i�-���	Q:,�����q'UZ�Y2��+�e-�l�l�4,�Zj���D�s�U�S��1J�s��J��G;��3��/�\���`�t?g����iBy=��qR_@=�/ �����q��
�����`k�(����%� ������i�t��t(��������%��L�V(��GY����p�*PU�>Q�M>������1�=,O*�VE���Ad|F����az�L�R�f�����������7L���l�����8��R�����,]�����1?�[	��a2VV������d�#P��S9�<���Aw����+��/���Lr-W;^��a�9qJ����/\��0���H!���.y/������q:.�I0����l����.n��j\��d��%Z�j�P��S�z(����.W
���X�8U���}��vg������V�������yN[������w�}�U��

bG��K�����{�^i������R�
w������J
�����d���i�+���Bz�����.jjZlH�������8[�+Q�0�)w�o���T��(=!���u��.r���c��wT��R)�����Zu^V�.t��/�R8T�k��O����z��j{�Sn�|����������p�~�n�M�~���|�Of�;a�_��������tm_B�����>:��GG�V���]���
������������z�_��*���Y�k��:}��):NQ����;���
�+	�h�������b��H���8d��oP�Ld�7u�i�E�l���X���"��b��?������j_����E��mk�g���H����C�!�.�1��n�I�(� ���d����K��`���M��QMH���`#��jD�;�Jfx7G�R�&��|��D_�+�#��k��2QBF�`��|�����\�����%!�Q~�[[�����N�3Mu����NB{�1(7�����n%�A�o��B������\GF�Z)s�.l������-z���2&#�~P�f^��<��q*6T���x:�	`�QJ,�j��=��+'��/f+?3&�:��^t3��8:���LN��]�4�N�0�����1�K���n4B'y�`j��=	{iQ���
�]
���h�a��F�G2&��6����^�Z6�����$�u��������3/�=�G;V��rCY�[�1��z`�d�x�d����	���
?�7'��L��x>�l�	l4��x-`��C��5��.g�4�=h���c�]r������j��=�0�.�h%��R��^?)�#�!R�}�,��q�Hh���+�D�����dJ�U�, `w����D��ys������1~m���M��M���9D���{1����}m�J�7�����Z'�Yp��I��4��T)��gK��0=�[����-����0���������&�N9�|�C�	0r��,���E3_�����k"74A�O�j��!�`s�3���(Y�����;ab:���3X�vs%��K���-�����dj;eB������`���K�6���k�������)"�"����D�:Ls�w��o^�\�DJ%�9�KG�~4�������pk�f���C��hxh!���2�U*,��P����">�Gj�o!����8��T����'��
�Yk��C�&��Y�����7,j��U�����~+��i3��/�����G�����-��rx�C�C�����zs��D��(�,��c�����WJ�T�H�u��-O"@��\����Y4p�f=4K�O-#3���������H��\�}���|T��x�1C��	�sk�I�}�@O[��z
�F�=�<|�����������[���~����*�L�����{�>7)N]���V������*�5�e���k�������������������`��B�=�������h�~��>*���[�p-*��l����U�t=��y�e�w�Y���IDS&�1 gl���7�6��k@�!w/�"��V�T��'�F��q�3��iQ��_'<`{�WB>p���F�Ko��]&St���8ml��e������lc���������lQ���a�g���?�����:H�w����:����N�_�+���Ws�HW���4T�j�@k�V'�\�/���#�6������9�"9�O9���)��Q�p�(��5�� �"��G��Z�W���Q�]��V�v����J��&�7���
��f��'V2�����~'~��]�o��_����w�N1�waJ@@��������omj�j]\��v��cM��s�|9("��v������u�,���=
�-�F�t�j����^2�l���i�L��g��.q��n��~��L�a��}\��
�� �_[��64�[��S%Q~��aX�3��j��M:!J�J�)E"�T}5�I�S�F�bz�i�.���g1�4)
.S���]�,RB�Q����jn��G�slp�B���x��G��d�q;�.�Nw�s�s�R%�@������P�n�����A,jzd��Vk��.���'dG 7�;�l�@5_���7���6D�`�;�P+u�Qm��H��#��#���cDI��0��]���4J���_�G�����$��9����@�#��r�W6����^���0|Cb�Y�^h�q�����wyG����[[��3p2�#��G�4�3��g���#�G��'j[�NQ�G����S����DuO�g4�cJ0
��h�������4�(*{����b:��9����z�il+@��;��9�J�iE�9�(��i����
85���
������`�H���1q��*(��T9��F��@�5Jq��@���1
[�:OO*�9R]���p�O�RR:�H��g�g��������������;�SJ%�N�w ����s5�������='����B��i��	���J����b��,�S�J�i�����������w���:7�:h��A����h��o���3	���&�t�����x]S���1��0K���sP���$b���K��Ex;r��/'���2���A\���qgJ�B �<%��[S�N��l���z��Y�S���=�����LQ��>PG�����q��)1��-�F�C���e�
���M�&
XSs��vpi�����EK�v��
$�@��H��;=��l��9����wL���M�7�"����F�H�3g�����;,������NI2E�_J��������2�qQ��&��f?.�b�,������%$��h		��~��|��������
B�gAK����F �����`*;B!�h�f���V�*�>,J`���D-�V-�#0�F	g7��Y����������/�]pv���7z����(7����������	#��j�	�U`��Y*7�(��q
�4f��h��8k�$���PC����n�����rW��D��=�K6X�^J���D6NP���Bo�K(�!�&�P}R0U��	A��"2���MZd!��8����0I|0������W�Z@��J���%D�:t��k������k;��N�����F�2��d�[@r/F���(B%��������A�C�@��x��?C�,���M����q��t�N��|�Biy@n�@t����|�y5_�mx���Y��"�Kz�b��
S���#��.7���~i�������o����a����K�.��@V���Y��]�/����,�����I��d�B��<1Sv��i4�/
8�Z1O&�����Ov>����$!��A}�Ho7>�LH);�x����[rChF��u�6���y�c'�ro����^t'K�~�~o�����/>������#�P=��X+H]��K�-����;@���C�k%�>���x�N�+]��1�f�a��<	Yk��7�1�B�	�]c����G�yxXo��i|�|�}#xf��a�1��&�����n-��-Bb����M -�K�J�c���e;��I���C�C�b(IL�g^J�3:�v'n�����@Gz�4G"|N+13
h�/>G8,P`�P�1M����S��FN���#�/WQD�d�[E:��M��5�pb����j�t%E�����~L�Z�/��r��*�y@����
D�(��Qg�0�ba�������������i(���}��q�L=�1A���@��YJ�d�k#6�cv@��V���f���+z����^[�	�f;�����5�C
�]o%�zR8���d�cO��S
��]���{�:�_������Br:B�
x�@7R�BP[�K�0��])�5���~h��<�������+�%U>`���Q��x��{��>���sI0�v��`A� sVde���5�����g���O�h�B*c��1��-���Qq��P�M��@�<pP�)��R�a�F�2���|��X�������:tPq�!c���OE�]�]�m���m/4/N���*asa���W!�y�!����w�[4|����C�pF�[zO#�S���R���7&=�����h��M�,}F`G��1�m��_�TW�
�����D�bs�n.�o@�6Q����RP8NZ�^�Q���@�G�������-��?����^��B��\����\>f���#�.&/��f������/_OV�.Q}c�sw^�g�{�!�����j5����Z�8
=�C>�4���n��!�~�:��npv���m>��#c�v��D��3O$8�p�80��d���X
��{r2S�Lt���H���#-�o~:kDot.M�R�T]����}��0�,{3H���<#�;��o�O���L"nP=��B�i����^���U*h���&��No�1
�J�aq���<`��(���7d�B�n�a��D����C���J�K�b����D�T�l-P4���bCy"�R��6��O9��r�e��l]s����&	#������avic�����*���5]�����a#d5XuH�p�$��1�N����\��yb�#sX��g$C����}��%���"}��PbQ
�g#kU������5��DV1�h�j��/��yh���_S"��=�~�B���b��&�l��%��Mf����f2
7h���?�����
�Z���_�U
sev�v*\���a��� k+�yp`��f����%���
`����/���gO�BA�=j�PP�=2���FI94���@B�z��T��P_+>�������a�����Yj�ArP�n%/�����jmm�}�[[[{�j����c��&�l*��(cHr��I����w���F�g������S��/k;{���;����hk{�������oy�c��WC�
�����H��o��4�H��� ����u�������������~�����{���������;��Vt��EK�P�W����_�
�������(�#,R��Y��^3�K�Y����vM���M�o��Q}2� �(������}ml��l��������7��\�!~k�em�/		�j0)�^yf�~nR�)�bb&4��0����Jqg��������H�i2������Ys�}-�QX'j����_N �gV�b�����;�����%��U�C
M��Y�5�t�?'g)���0�S�
���H��"�?����5Z����.�m�3IpT�|�J\�J�(o�W��=%��?�\_+F�
-eg��nu_�n�����A��������������ks�����5\��u�6������=H�����d<k^D���K5�>���������N>��8���ES������
^�3kC�d�c�uc�~�zw��c��Z����S��x���W�CN�%��3*�����j@!��X�K��pG�W�?a�c�)���N����_������v�Y���Z�����*5E��s��9������|:�W� P���d�9$D�P��-��F�^'�maA�LDo���[���Q���2u����?���X��1��0�2���f��#����"����"��i:K;"�3��W�
P�����|�Ao��L�������=Q�����V����x���o�hp�kN �����*��������	�H(G9#2z�bu-�/�z�\%��M��lu~"D�'`8�.�����Kkz�CV�at�n����B�jE(�!Z��$�K%��/j�*��:��t��U����T����W"�wAO���%c��]y�~_��m����"1���)u7�g���j�a������j�!��P�Sj�W
�e��X���t���W_�vC?EG�K�CR�pN�&n��^l�\��)'��h�^���?L��s
��������d�u]?)��*���hf��k�T5��N3��N���,�2^��_��<R�����X_�_z�,�@���2�hF���]`�3ae9A�����>��h~(�o�D������1��V���).���Yc�������I��P��0N*�i	L���~��%@��P���x����,��A��y5b������KTKd(�#�@��7VO#�sW���p06��%_������q6�|5N�x�I5J��x���C����?K��LCc�Zb���=����GI�������d�@)�:Q�(�%R������^3��>��d1��G-!�sD���t����Z�����<�[p��i��J	�d��a��4�R�I,0�n�*�����/	�e]����.f�<�_y�,g��$�M>����0���IC�V�@�P�}�������R�Y�������6��p5X71{q�|�d���5�A����P���D����d�����6T����lc)��zA	-�C�?�f����.>�QI�9�d�%k������5�Yz�=�����f���6��G^e�E�]f���[����[/9hIAhUe(���6K��w���/<��o<�c�]y����y��E�^����l�����7x�-e6��
�z�n�r�/��s��pN�L�#�W���</%���?��1m���8�`�"�KtK�,@H�.��qB��K���O���>�����:�Q������G:I�e��p��rW��K(���n�5����K�������*��Eot��|\���}
9��yA�:���u��u��e%����%j��������K��������W"p]����>���k�9�Q����E�����d��N�c7������h���7���V�S�m������V�;�d���������R�?�p|5�IT?�I$�����.� ��{Y����-�Z�<������4�-7��.�*�������!�=��rt�DJ%h������'�<I3�p���S���G3S0*g�-�����2MR&��8�V���j��
��_	��p�����kY��CT.����n���J�?�;�_��a��#����~��T�ye��z���Ga���wt�������������~���j�8XK7��h����_�`�(��@�B��7\`^�,~�K�x��n��p�C�h���w���OoZ��U���T��_��[���[q����Jz�@]s,���G<t��C�<�}��'������������.WI�nr/��[��^H�c�l�,(��z�������r���>���"�`���L����X})�8(��^�`�F�^��8��������&���o&_�.���J��d���O ��M��!�^}{�>"da���������J��w��?�?��P"�Nm���%���u����wz��W[�d{��t_���������b��'�a��?�����J �d2dI�S��#�����W ������fk���������~ D��WB�%z55'������Rf������
%����;���R5������wv�
1�uL�P��|cr�@��dC�4�/�j5��@�9����u���5��3�sx�����0Ce��Hhq����5�R��^d-Lo��	�RU�����b������=�n~Pw0��4�za�#0�p
�i���/��8���y������5!���`�)j��
��$�H���.�8ze1 �'<�T����b�jm��}�*�1����Y]�m���"���S�}��� ������j5*>L��RY80�iU�����p��\��B�Z���8@OQY\G�d��d����K�G%��N���B��~L�i����q�O�me�*U��E����.�_���iZ�GB1�1�+����U�.D���p;FJ~�%�*}�4�v;{��}U������E��A�c�����f��/��2�j�����T}��b�+�s�F�v�^A�VqC&p:�z�?G��=	+n�p���3���\|���_l5�9�}��G�-�$��������8����	/Xf��[*l|�[b���3[����z�7�0qYac����Y����D��-G�v3��r�o�g0����������
��3�G[B<R�{�uf��
��Apn9jP��0{9R���v�oC��5��}����G���:��M�td�q�������f�Buh���f���5��_��������Yhb�����]�5���d�k�b��y0������S�v{�v�3p��+m%:.X��fH����-P��~�� z�'���K%XA�����~_f��Ar�U���[L�ay������\4���+�g�������:���[.}��s�j3�@>��~Q��t�Y�A]�~k��S��v�N��Wl�_�U����a|>[��-�X�
�-�pV��(%g]��������C���?�����! �����G)�zHP�������L\��Z�~q��������M���qr�P�H�����f���N��&/������|<	�+���3��/z�Y�	,��s����/���<�n������)8�_G��E2�2���GeS�g��4QM�������������j
N��y~������u�L���F��"��\g���xh�>F��Q���d3n�w�����(�[�3�����,����]���T��8q��n��/>�Cf���n>S�c�,z��a<T����q����������_�jr
U���O1����j�^W�������r&�������D���+�����@>'��<�w�-���5�|R�zI��Qv[~f���qo��3]�y��c���O�I��#������??�C��g�,s0m[=������w�@��Z����,�����;�38����o�3���?�5��S:� 
��H�;��t�6����CWT��*��P&F���.���YV}��N�J�����a��k��:�`����eCLs�k�E�,S	O�R�O�[�����{xur��.j���.��vqrU?k�6�������8n�������@X��<Ld�I�7��.i�����zO�t.��������b����1��O���|�lk���v_?����""x��?|_�	�Q���jlFn�
/Cb}�yy������!�"���o��>�����'�ry��Gt��	�Q;���'X�o�MB��$�11���S�3�k��p&_(�8���.�;l��T��Ve�Jf�A
py\d��?M�����An��R#q������.C�j����O?QE��X,�����}A���h'�E3;��a90]\n�N��V�<�s�N���<����z�JP�I�J���N��5�0�Y5���G�K� YZ��r��9E�S�x�#����j�<�}P"���,c�#sn�A�R�Uyr�V41s�k��G��	B�EWP����:Rz����
��]�.�f�'�9��uL@P�z��`��J�.MzZT����!�	�BK:�"�������-�X�a�>~�bv{c`0�D0I�:�:�L�2Q?5����3����Vf�S�Bu����j;���b`YV<���s�n����w�s2���L�r�����0�� �A�t��j<h7)�
����Y c�H?7����x+��9�+
��o��>I����{�t�$���� _3x�����t�@ ;@4��	(���V	��'�;RlB� ����������a��;G�.m4�����iaP7\S�Z�\Q�I�	?�043kC�0S&Q�3�����n�!Q��
����,�>1,��=��n-��#$���zk�`�J~�p����o���o��W��_(&|U�(��,�R�3l�$����V���vk���+[E���6Q~�=	����kQ��x6�`+������;��>���K�;^�Ps;8#��"�
J�	n`���y�!�l�7�D� ��`�h��N�)���w�Qx�g�am��)o��N�V���Ut����I��)�#����IY���)�.�^j#�� ������c}8,����0�{v�d�|�M.��	$����w>?mR�N'�R8O������/Y����|
(>g1]�3|
�z�d�`�Z}�:����^�P/�O=���=�[���Vz����zr�M���/M5�	R+����q�6r��������?����������F���%������%�?���$A�>���J����
���m/B�.�
:����������<����K�-���g9O�'-�������U�+��+E���/3��nNj��������f�xa{���j�d/t����|o!C	�
u��^���.����7�x���$�+�B���<����~���~w��sx��m��G3��w?OV!W��W{a����x�k��[�2c����
����J�$�7>��?���m ��.M��:I?E+���1�����4�_�D#
O�������vPz�x�6���-�D�1f��+���l���\�^2�6�W�%���W�R2�+�}��$�&+���W�4���b��yq��
4�	^����E[�~����>~��vcNF��c���3oo�?��:����&�-t��� ^�5��������;S����9�\��������q�j��H.+t�)�l \M/�`���SV�"ie��jt��D�����7�!a�A�
jP��?*k�����������'�\�7y���'����z��l)/�1�������������?�(C�#��xG���<)��@63S�����U+���W���px�5S�������Z�vV[��cD�Sb ����K/1.��ku�l�m{U���^��o��8�`T�OT��^��t���0�<��L�8v):K%���Q]M��X��j��1'�P/����u�@��T$U��Wj�u%���$��&���6�s}+C|Q{H|������,�*��?c���1��F�C�#I��z������)�/\��eQ
2}��"��+N�~����T=��}���B���
�/�k��woCf?����0M��#f(T�#[;��~�]�&c�c�P����s��/S}�,��.�<��������N�Z��&�:qg���vR����Q8"gl����;�B���)���;���G�U�/�kR�*���S�8yi��e��x��[�A�YC)��,���t����g#����	W�m-J�6�{7B;}���>�b	~K`��#+T;v���Z�.v����j$N�*����Ll�V7��N������1+[����n���*;���������QB%A�N��pG��)���8�����J_���	fr�A���D��X8����t��KB1��b��8��mE��|Vt�1���;�����t(%���s%I��d�'��� ��[��N������@��VX�,b���������Z��[#�����;��:m�����	`�^��D��dJ�gr�Ue��]�o��Wg�� #'�4B�sy���.KxA�������1����V6��y9����Z����Xt��%46ij<#S���MM��g}a^C������`��B�H���W����*��u�6�L���������
T-���t����1_��/0dcng��:���7�%xD��\�V6��(B��t�-���UA���Wr�|!�vn=�GE�P���w�+�
��N�<F��6�K�'5@��tv��4��
��d�G��?`)q����M��6��+��>=���h1M;�/�i�	G	�i�Q��B�&>���Y�� ���"\D����*
���
|����|������@���KM�"g>;>�������	���|��c��`&1$��k6�u�e8R���s0��>;CW��{�~�;Y�1��)l���~<~���0	�X�x@Ad�'��Y�9hh�	*�0�#�<<��CL�4���9���2��~�<���y|�0N����6������� �$V������"`Yf���.d����c�d��=Po>�����"�)�V���X�����������s��J������XX1z	�y������)V�Wc�V�L]FvI; �\o0*T�8��6��j�xF�w\��
E��D���6jp �{~��Q��a`�.gha�V����NC<�^z��5gb%z�x
xTt�z,����xS�
�G�d�&c+�!	�n�_
���80-���q��x����7�����3�Bkgx=��$`#����d���I�����cjR-']]BI�d7���~���T3�-��P)eE����Rc%��<���jS���G<qm�&�����[C���t��N����<5���@_t�>���������(���j��.�.O��c����"�+��<�J�����v��~�h�������x! �����o����L��Om*�=�����?��.(�������G�kC��h�'�
�o��UXA�����o2EQ���q�����j�<��
2O�J�5����T�?��N��3E�&0�ER�%��e
��e�`�9��w�d7
���c"g�j+�N]|�H�,��!�b�X;8&�����P����3����W������q���%l���!����<���G�D����HB��Avrn. �o�v���NZ��vw���I�n�����I	�9p'����zU6��;�|<�:B�/E���&�Y��)�-��ZQ�
G �T���5[�	Uf{���� w�����T�����-B��+k#�d��C���y���C�]:#?������;�gY����f�9q}yd���F�=a*�����P�I���J�=b?�Y9�[J��2��g�)@����W*B��	����y���i�
Q#���ttS�u'I�'VWco��/R�)�j���AEQ�-\_+�}��b�����4�[� M�U��������E��%_^��p�Z���}q��m�*c�_����V�O��_e�q�������+��(�]fS���1��w5�x�rw=��~��	8������Z�}��%:V$>L'�Qt�8�dIO�5z������x�������.����X�y�8�n�oa���3*�4����E��3�g��c�r!agA�w�A���
qp�2���J`T��z���;8d�.3����g����x��^�3�������z�-�� ��Io7��_pD���~��$v��W\g`]qd	�E4����p���$�v�F�'���;��^u��'����	J�87���X��^����t[j�z���{����\��������',$��o0���;�>~Ig�_1�Y������f�t��Q��yf���>��F�k�/����?��
n���&�xZn5O������I�b�^����FJ'$c�W�=bz%�M-�J�6 ���%}����y��~y�<k�/�x���i��S��q���S�X�����V��O�~��Z���C�l���Sl��_7������3�&7A,��AB�gI�{4#c!c����/�y�X���������x2����_A��Qr7I�������+n6U6�:k��X��t:��z�I���A��P�1����;�����+7
E�b�i+���_����)��V3�C,��b�-�LW,�@9,�{�P��V�N8��*�j|lz&R��8����\}P��*�I�e�'E��_���m�a��h'li}8k���GG�V+����W��Gl������w�V�}tq~�8I��oF!�w�42�LnKx�{uT�����d�����1�u�O�����H����l8�@�07��?�������>S��3g��$�����P��=�(�-������N���|���cH3����_��H	�d�?�i:�6��Q��AE2L�)�-8�8q+��.�Es���:_����1���K,����y��ge���W�f��������!��{��K��?�G���3��M"�Tg�(���-?C�5	zH0�*P�������|�)�SH.O��7��U�q�tE�G��RGJ���j�>���h�a�Ka��t�2%���[^��;��$8��5�<�bW��u�+C������ vc-PP�I���I��)��T��r��H������]7N��X-�eZ	i��X������d�.�������<�g�����w���k��9�p�I	$B
 &�A1�����.����LHm�m��A��0K�������
�o�
������c����_\�PrG^:�����P-%��d>U<;����YGu�O����������~uU�����~�j�o�/8��g{'�=H��/�<��1O���!�#9����u���k >��J����DIG�4a��H����p�d��9.����4�&�e��b��PY�N���;����V��Z����p�Kp�C������4F)@��Q��aE�`�z�|#8�!B��-K�-0 ?$�h_\�Xh(r�!�z,Zw0�����N����	2��Z�� ��g�R�-�H&����7�IiM�����h�5�*����dF��t�V�(J��Er��Lz�?����n������gp�6��%�	KU���i������:����^������zK1��q�~��j��k���;;n��8� T�"������3�\1e�h�����w�n��!^�\^����:	�w�(T�u�a2��aU�������y��0���f���%@VLn����k�=�TS��:���x T`��z^j�fw���[����������,����gAd8�Y4OuD��1�S��n�7��*��V��������A!���#��i3�(6� o��!g�O�s�s��	���t�6l4��s�QS[3�����+���fa�BTG�G~a�D#Bn�r_������Lz?�L��G�w��(k%��}�W��_,���t��DMbx
�B	�A+�����*--��������V�	y*o,�Q�����$�����u�����{����n\����e8-v�9�R�nC�X�=���������/����@�Q�^w��`(j�=��k(jf~1�_�w Y4��2J����Z�[{�W��*���y%�R}��0����+�^�~ 	L^/����a���vF��,�>�3?FP,���\�!=
������+I��#����h2�4�d��b�G{�A=1���tn��)�.�����S0�?\cNx�����������j����9_�#��c�-x��l��7���P�$$�1\�+"������cz�W�:~�r6-AIH�C�ri��<9�6u�&�B���(���ft�=\@A��������S�%%���:�q/=�n�O-��MB��������;�*��JI��[
a0�0�'.��dY41z����@8����a���iCE8��{���$�����}��Ey��39>��)�g]�`���&�&�����������j��f�����=�rr���������q��O���v��O������?k^_#��Hr�D.X��%�TP	�(}8��o.`[��Z��?yb�5����=���4�96,�(6��&��"�h9��A��<��9�ks���p��# ���%PO�_����q��W���U��PdmxZ��{��c���kI�3�8W��!p�%���rv��_���&�B�AQl��Z�#�&V/�R�[43�^�G>h\XI	��!���I�w6��u*0f������@4��K�(7h��/&��B��|�l��%c
>���L5���g"i��0��r
v�b���(�P������61K�y\�;.i�������#aJjC�a?Z�����M�2���G�y4�[7��7��
,>����:j��YhV1��rj
��L��,���:����7�Z-������o���c���:)�+�t�%p$u���D�b��3~
�<>+K��p�b����Z%�n�����~\���+��8�VQw�K)
MT(;���N�����O���BUr�W
,��Bt�? ���o
;EQ��G����T�r�X�.�^a�2��r��W��~Y0�>�
L��2�����L�;��cn����V�����G_���	�:.�x���9T1��*:k�7[���s�ek��h3u�R�F����LF����	��L��DI�e��AY�^�<��h��P������c�@�T'�c9�@�I{N_�U��t_�E����:(J�*$�}]���;'m]��D�� ���J�M�b���a��y�nq�)�O�b5[��W:G
�?GK�}�@�������B���	5��:�I�����`=##*
�$$
��3�3%�Y!�D����)��F������F��j� �T�����I�h��2�(K_�7rw�K��*J�1X��|'�M(���� -���FJ�o�����~l�>�����}�I�eT�I�z� O��Z��HX�f=����X]	L�{^��7&^�Z%[H�GQ����t��'��� ��`D��';k��u_��i&LC���uD5�,��)����a:�f�(���"�Ye"���b�'p�n����W���^��e�1m>C%W��{\���1h�����/�3��t]x�����f6��|�~mQ�S�1�Y��g�&�>�u��Vb�U~k�����,gju�l�Z���%��%5Z�%V��b��;�+b����C6�+_����[-���
8�Y`��s�,��������<S�G�������G:��h�20�� ���M�e�%���C��6��S���;Y9��ag3�7��m�Q���tD��DP>��g����3Ui@��?�ct�d.TV�GMt����=
z���2T��B���${^�e�����Y�>�5�%���(��;^�������d������w���_�_��zr��Ep�5�;�`����,a��Z����	rX�tL�
h�V����+v����[�������K���+���l�hg��`h�B�,��-`��,�$��rEl���	>#�#K� N��_C�^�"���d���1 �BtR����m�k�-���+���UK�@21h����p�@ a��#����2M�������@�����>a���_I_��ff)d��xs|�������C+aa��EC����g��� ^:`����5d.������	��w����*�8b���A��D����-�i�-p]�8�����"!#�&��I�b����ih�tK������}^������3��{=����#��=��c���s���3~E����$�C'����[���r���y��zm|>o?<��\�����tc���i��K%$�:�U��t�_�3$)������3���S���Z#���`��b?<��M:�����>���*]?�#�F����(�������������(�k����ZM��SRM�`��~�*���h�G��$��g�%��q� ?�h���s`y�������� U�r�
�q�������H+��;	�����W��{J-�x
����_���m�e
��
��x�Ip�&��Lr��K�Cq���K�)�>�P�D�����&A�P�^fJ|LS���Z8���<�1�
h��[L?a|coe�I���w4��npmiL�0&q�Y��/Y��_.�f���#��<���
��pr��-2z��:c]E
��D�F��P�o�a�������NoZ�?5�q��0�iL%2�aR�*��
o���3s0��tl�1����1�@P;2�;��J�
��W4"�J%�}^��C�6�WZ)�l@��d��B<����/;yh=`Y�"�4 k��<����X����@��/��ZQ�J_�rgE-���uw��~�����p�����OHpc���:�����&-��D�����p����;S�I X�p8��#V�)�+[��Pn�Q�1�K���!�1���nf�~����0�*��GW���s%�l���� 9K;l
$���h�M����L��x�<������CJ�
�m�D�>�iUL����,�3��5��rR|"]j�����6���3��yHg	���(� 8)9f'B�d�L�E����F_��T�F�Y4�x�4��%�@a5PcJ���tQ`!,�Ph����d4�!c��������n,�[+-�O�3n7�gg�5���B���5-�_[��rF�������4���_h����
#���~�z�t5X0�7dX38��i�=Q��"\�8oR�\��vsg~D}�/#���g���;&�I��1T>^7��b��r�3S���H1T���0Mkv�p�	
�Z:�r���U1Z��Yl^�����"\��9��.���j��*p�YS��F\gT��)�l<M�#5K��=
|�'�|�4��a�^F*O�#i��]	N:�FW�b_���)K�)����{�����nE���l����Gu�:�VV-}h`����c�RuqK�2����Un\��tA��LL�p�,c�R$�P���q������N9D;A��@�CF#�w�'�3�Y-!5�g9����5�k��c�y�W[���\��K!�IjK���?�hK���_����P2�q/���/��MR#����Ll1���j�5) "=d��LW�-�lyR��<��Q����E���J�T�p������J'6a�by��62��X��P�X��C�}.aI�3r�o_�"aT�L�!!;`<�
������m�����*�t1����"������4W'ag��%�\YQ����6�gs���9��#b����Kb���D{mG�}�������0��h7���(k�AF� ��p�\ux���t��i�Jz�G���-�f� ������P\���W"�3{}	�1'��h���jD�<cNw!Cp���"y��%V�+	���[{���2�������@��
�K�F,8?�4<'a�9r
�T_�(���J�v���VZ�O��W�1���P�u���dJY���;J��~�����Rx��D�
!�M�M��s��`DznM��,�����l,��0�U����%d��^�D5-�I��8�'� �I����j����h&�Mi�����'������7��"ml��A�t�uy�d��T����b��J�wxq��a�/�YX�w�f�E|�z�K�DVpP8(T;�
m�H��	�!�����r��
�4�i������&~�J�����K�T��	��j�@�w8p(nZ�n�q�2���*�m��A�����Q�9��Y��
4���0^<�n9
��y�v�x�����8B�h�\�6*�*�(���b���Uk��
#�RFk�}LI�h����.��1��ZHZA���{�"��u>��������gb�5������2�(�<�1�,��!�-Q�z3r�T�^��R�����l�[�]>��%������Z�d��]��[4���\��:����{�[n� �]�
)��A�d�P���{Fr�i�`A����MD�B�e2��pg��������*���Xq�xZ�QShh���x�Q����������q��9���p��T�'�H
\Q\������<�WXagHb�RYj�]���h�1z,
nY�+>#�m�����~���jg{��@�ey�+=�p-�{��~��WH��_�����U���Cq�N/N�0:��1t������[L?���_�v��_�1��������m��!�d7��������-T+o�.��d��s���W����^�z�������7���|�����{���V�?�=�!;�6*H4�V�|�r��&�x��.�'E�����K{n�i��%Y�Q>��R��_+�RW�RO��lU^G��,���.�B��)G���=#��	y.v��D����j�����/������	��B�nx�&>�Z���X�����Y�S	V�YR����eX���Z�p�^����e�����&,eJ��e��_p�^o�~��N����/�/V�qX�)���
1��#M�J��a�������gS�:Jql�P ��PW.�����������j�N�jK��Xk2�� )���.M)N���d�]���C�3�����8y!��T�#o�����P0���H$:I�F��1��H(!4`��r��=�Uyp�E�uxa�T(EvL Q
���kmk��I�Q(�.���T���s��5�M$h�.�<�����b/�m��vb�&������`��������jn�q��T���X��#���T�4���f��+��`o�dv�PB�T�Fc�|�����Lb#��ewmC��w,�/�����~�{��oQ��(����]���gc��J��o���l`�#�>C�<4]������yuA7P#A6.�'�
��J=�S�0c)s��'��^����	;E��)h�.h�w9�M��2Ha�7wb-���"5���+3*��h���gM�-MX�,�����ER��k�0l�/d�{������k?�}����+]�w@6����@7-g�R|X*U�e��+CoS�{����W�y�����`��X�yec��'m�2W���;����U�a���I�	��9-�~�9�`���2��6����#����r��w��^CE5����o�$Do�<�,��iR��^�����E)�:�)�����EBG1�������F�84�"\�(�����8���;��~���xQA����{�^*���7E3
2'�a����x�7��=|�iE�j���v�����D�����Q�y���}zq����>���
[^���4�%I���~����%��`���nE�)� c���e���4�j�������-���u�B�b�LS.��+�
��R `�~��7��i�d����7��2Y�����������\< ,D�s�R�-2�#��w�40�#/����;
��PV:�3�+�H�q���v��X��)T
9�v��EGh���� 1p��qu37����[n�
@Hai�n��P�!�$|i��+B���yugB2��N��'�FC4:p�t��4��Ty��.%�C��U��%����Z���`[Z�[<�X����y��{NeAO�<�s�L������~����O{�J*D��h����Xm�U���6��x|���J�z��T��Y�����8�+���/��3���4q����n�[�D���6QD��VYQ���v)FR� �UY�_��U�5_?��������#V�������m"�����/Db�|�`�P�"�6�H�n�]�U���@���Q*�#:$�m�%�C0>rVjM��'5kpZ�1�u|��Y�+�F�k������z=��#�Q�|��Z=DI,����:�$:i��$��B����P���FIo@�xi
��;���$�DD�"��QS=/��!h�X����2w�$

]u�~����������5��,����(,A84����1$�����@g��@��)|��/2UO��h��/���i�z�����p;�������������G\3=��@����Z���^u��km�R/t��K�9����W�F2��H
������!�Xg��c�q�Y�)��=-���4��7,5�E�������@���r��s�F���k�m+_��b��'e�,����C���}d�-�*n.�u�l�K]�0��(8�/U�;��cD��utIF���Z?�bU�*����!�JV������5,`Q���#gg0��hA��MP/�����nx�i&|�n��&����%,[�@�q=2p�M,���s�sy��L^�;1eIIn�*LNV�c!�!����`dYb�Pg����h�vj�i����>��d���2r	-������H"b5*�=������G@��X/[^u7�CM��w��)H��3���@�%;��h��0�t��(�x�T������X�gp�`�n��������� �<y�B�$E��������:�����n@a�6��|�6eF�I��4���W1Y{��.�tJ�����'����*J�����������Y`>V����X��.h���i�S.�$0��g��
���`��Ao��=\4Z���P�Txf5H143y�������v�������Ca����tF�d@>vF�H�:��b4s�dLY��)xo2�KI���BWV��^�"���o>L�WX��a��U��o-�������]
�sL�b�GWF�%�Q���rh�a�G �|���$#�5�#- H^�l�}���m������.�.N����tE�����V�Xd�4�(r�L�2K�K(��P���m
��?x���M3]�����{��ZXR"G�����Rl�������|��<��lo�<����\"3���	XE:Pha�����d�$�[�olz��"]V��cJZ�'��V�������]�D��*Er�[�MK��wR%:s1Dx0��}l������$�cu}7�7�����s�������K��g�z�!y���K�I��1�I�bFA��
��Q�%�������B����.�����s�\
>C9vVGuY�9���|/�O�'�Qw����0q���"�t��^�����`�@��U�����c� O�bQ��������P��$�g��0�;��5��KS�zE�e`� w	T3U����d^���F���x<����/
,8s2���YZ1u�\��� �6+1�u�e�O��'P-�%vI4�d"
��FBP��?��C����vt�o�+B���;n4�J5x �.��O�����/'����;EUu&�8���8H�686�	�����<�2�z�L��p@��K��,�������T�L�j��
@�}p�g�t����v�wJ�f/��$�v������� ��IUg;������i���J��X����C�H2��^$���>�y�d��s`���;�%�x>��{�yp;�SrW�T�+9^s@��� �$I���������df��c{+��#y@y�j�TE���JM���F���������������@mo���Z�k��K���!����a����!�<�f ����7Oo�J������U��P$�x�*��L7�����}������
�}�
+��~�Rda|\pd�zV����������3�'n��'n.-��UZ\7��Tbn;�~M���.x*��Ex�>={�$j��r��4JO���5���z5�=�S	{�o���pu�I&��*
���%cT��%�UC*D�;�)=C�@Ng�L�Aim�T2�yM:�0�v#beh[����e:G�4�y9Q����^@���z�l��M�.���AO<Pdtd�s��[b��������\��|[�y��������uf��FA3�:�	x�.!��p��g������x\DZG��T�\SWJv}d��Sh��A�=�t��k�������g5���q���6NPw��uW��CC�3+%��+�?�O���N����������_�%��tWM��?��.z���^��i����u��By����aQ��a���rn�h�
[��T����Bxi78Ifb-���m=����)�����PZ�[u�G�S�G;���u=i��8O���n���Z�9���sx��;����I��=�|k*��{O���]���������L&��+� b�I_�1�#
Nb�����`��	�d
<�T/��(��fA8��|�����\wg��7wR�d�����������&);��y�>?=���K�N�S�g�?i�����sW�K�<�����C�gD�L�>��,&�GJ����vJE��������p��d�<��g�TR�!0����#Sx�N
W���y�#^�5�V��=27����djK�� ������=}���F�>�&_><�a�|����t��q������v�GW7��-��|_f�����kD��
9��H ��f&�C�{Eqbs,\Z���$I�G>�+�=r�n���0�e�x*3�5P�NHp�q�Q�A�) 1$VT�yV[��������W�}�D�'��t!�w'?�[�����kO~����jR.�H{���K8�J4��=�m�,�L��|�(�������ov� ���(pj�4����%�,<���}�����%��t����|f����+�Z_�oG�N�j��8��B7�x�>�_��l\G?���RB�)������y��L�s��V���i%��x�?A]wE�����W�7�6��a<w^E;r�d.�K�����u�pcTU
E������6Z�}��GD0�S�b<���+b��
��5@vb���9���!�+�����mR|xtq�h_������������W
p�5[��lh�
�p����Q9\D���hc0�gv+��.?C��k��&��g������r�$l�w>#�A������/>�W�"����Vp�p���)�Q.��`3��H��De�a��-��e\�>
P-*n�
���a
��Aifuy����{{`�l�S"&ZI���I|�%���� �!�
����
'}�U�����/����>a���M�3e��u��i�;�e{��2G�X��H#|*���r)��Gz��hV��a���%�	[�L��cA(X������KVc��K5�}�-����$[��C9�O�E�6�H�AtB������I*������%+��Q���0��(��6	�����;�>IiX���Y����7J{J.s��\�l�}��'��S9�l�e�����p�^r��|���u���S����!�	_|#��b&���>���a?�,|��D�������
W�"�B)8��
u7�cB���HL�����W��c���y~R$�]��o��\a$D��y����F�X�_.����(�9��R�f�g?�s)f��8`�|n;��������PnX ��,z��WS/��a�d�{�=az�aa���}�#�����Q������ol�]�R����j7�K��0���8)���8�IW�P��b@�h����i�����W�T�9�����0�>�g���Q���0iB��~����q��~]Wo!A�+iWQ��%���M�{��>~x�)P��&(4����3�:���#v�z{�����q^11���-��jj��]t��u��8nxe�)e�Z��?�X8�lj����9�C��z���W��W
�S��G�����$�_����������2��V����jiwBg�D^�
�����p��{�~�
	|
�Xt�������6�U�R�z������=Q�T�@��D�"��l�e7�����O;NHk5{Y�
��F!j�F}����kyj�e�o�`�����.��2g���r��d�{�p�4�g@�'���]���m��i��~�}ia)���[���q��n}m�ub����;g/��O��/#u�7_<(����\;qQ�F��F���#����0k]��n���-O��/RpPW�)����!���q��g�U'���AB ���8�rQ	���I���'���oT��!O�,�������I���#_��IRje~��#���o��
*k��#���&�]W��/*����-���8 <hh@��������#rCR>�2�m�4�_���):;�5&�QG����O�pad�#DMy��#jD��{��O���u����s��T�+����Ii_��8��|2�f93E�pqzr��Lq��3�1w��d�P���0�����|�e
�R*5F���I 9P�:������cty���=aG�D��b>������+3H�p���x�����(I�������	�������{�r�^����C#b,��@��1��Vf��I�o[���K%��\���x������N�6����a �nkTzLu�Q4�|����������2'T�/����9��P���/Z����;Y�!�O�Mh�w�i4�M��$��	�C ����O��I�F^�uS�X������>-��'�sy'���H�,t������tM����;�����m�����X���]���0�!�"=�vf�r�-�+��dz�,�B��l�Y���I�o�P�
�\���� �I�6�qy����m���Q�"�M�L}����v�;^hD���'`�RX`���$�����j:Z�E�}g����U`�a�+�
�
6��k���t�����N�3�����,-�V7H>;�H6:L��w7'5Z����j�0'c�;��0��k��+
���^�i��H;jd��)��C�%������	k������Jah�z��t3�����}dV	S3>�q��u\�@F�wk��#��L����~�M��:��@~��Z
�-�*M5��XG_���z"����6z�,�9��;N�@����������Yq�������al�G���&~��5XN���A��R����-�K,�2��Npv,�j��3i��������T�:Db�A���-���H�C�5�0'i&��
2�`�M��R����"�`���:�-c\��fw�J��y��r:F�r����'�h��.���x��T�G��N�s���D���]��~�(��\*�w���G��(~s�.����07b��c�!�:2��3���>g��#��~��c���H�����?G!�����x��/_9�'����9UtV�w,���k�B��q/E��EW�������G��������v��=e�����R��b�^�|�*J���������z�d6�8W,Z��{���P�4�$�����Z��q�����������]qN(���!�eNY�a��o;0v�K�e���.������%�Qic������Y^|��� *��bsO������.���
����/!�x)l�s�R8�4\�5�B��(�x�"�h�-��%2�����;/,��u� x;��Ti�_3P�J_ ���hN����`g
�l0���@_��2��<W�U���������3�av�5�f�P�e��4_c��p�W
9�7�W��e��*)�:�����`i�!�xrTn�{W>�������,�������w�Ck���$�5���0�.���P�D���i{��a��i��_�+
�%�x�
���H�v:���H��Yv,@f�U�C_������O��Iu���'�3[�3�T��NO�x��zB�����a����������B��e���h��
=��G�<��k�k�)��[ ��T��d�"!����������:A����z�r8��,��rx(>���e��8�@���@�z�W�	��k��<�K��w���r&�H&�C�z�zCk��(��IY��������x�r������LZ[�����8Y�`����T:u�X
�9�sB����A���D[���J�ZHacR��5��4������.Y	�~���&��B�o����&��I�E��
�1������/O���_�_Qq�q��0 �$;�Y��&�2B��'����B�1[(��$~�;���nA�K�3���-�n���{�����WHq?������fW��v�����#��*��d>WYp����ND��5��_��_e�}��@�V�rm+��.0��?��@���?Y{E���O�a��ae��0��w��|���)-�������|������.X.��!�O�RE������R��`����2���b�"�p{���,�F�f.�(\�����z>8���2AGx�����������2)A�"�����<��
�"������
��=��;��=S������x��_K��i��fS�H+�]f�4�����a%�CP���������Z��;���f�Q�W2��8�2v�����j��7���@��������"����A�I�k��}�k��`�Z���������^ �|��f������
.��f��%�b�S����V�^�(A�|E�=��*\H[=A(
m���g����=)C���$����<b/��t���'�h��S������S��_�_<�l��8��s8����cY�A~.��*"�<q�~|����_�O/T��'��`�'��[D���NfU
&.&5�~�h�t?"���e{[�(���X�KE�Fco��^�����U���A�5����J���8�1�p�����r'���1gq����k�c���(�����w2B��������1'VK4f7�w���.b���b��B�p{+�"�����B%H�����H~����1�_p��?��;����=?>-���v+T�����~�Zm�j�a�zj+�S����i��f�_�-~���]�� ��>^��QLl�G����="�a�q?���xyr%R�&rE%� L~G?b�9^_�A���\(4�����-�b� ��5=8��<��[`�y#
��N�*[������J�����v>�RE)��|ujZ�=wvv�k�j���������
g5��-���R����aI��I�_P�bZ�O���f����K,����t�Z��O9���kC,������f�y���>I�NG�O������K+j}���On��������B]�����a���iE�N~rL���Y������S�r�O�����3G6
���Q���,d����k��G���.��~K�\h� �1F����~�f<����ySW��$�������o|��&\��8�J����`�>�����!W.S�PeMC�{�����LIICI����"0�H�Z����+O2�?���l��F��<�Vw�������D�x�� ���s���9Ir���tv�r��)n�����m�p%�����o>K*�py�:��# w��C:�S���:D�8��� :�����w�~5�"�9;�m�������q�)������sQ��|�rX�!&Lm�m)?���w�3�b`�����#�*]w����y~uyx�1���j�h�~U�w��|������Z��"
�Y��,�D�C3������[;�:�fx�~���O���X��V6�`��YU8��#E�L�E7����'a�������������!2����D�a}X�������
|%�|�m���}'������u��
�fF���rsg#8��WsL��J���Te���������S)���2��Rs��nP"���j!k������z������X����N��r��YK�N1�����hZ�g�����&'6T�0y���3�ic��������o�]����v���h{�����U������E���R��*���Z�D���/�f�K&����g�K����ev���e���A}���V�{�0���w8 ���&$���d���@�8�O�>�%(�AtJYx$�H
5�p����v�_���.��i���|$)��3�~����^���;�+Z�����~W�����y����R%8���9�{�/0|����h��v���tV~pb� 2	���+���I�����7G*�`����u��cH�S
�����>ef�* ����d�=n���Xb4+<V���$��g|T_�+�ad��}���;R���Mx��t2Y�&}�g�!i��g3.���7�G�N������W��g�o�;r~���q0Y�{�<��CKz�
���
�l�B��U|w��[����b�>�/�c�+�Sq%fb��S%����}c��pl0U9$)����.���
�d�A������Y�^����^���Ac����j�L�^12�q%��y��U'��0)���/V���.�&�L�z���|F��W��/,m����L^��.���
�@���(`_�D��
���I��*0a����������
)����
�����Z��an�5]XE���V�/�+��k@DU0���������
��4��K�5w���`�-��+S�'�dc(&p\^���">�����@�����<�a:�����e�#����34I���sz�^}Vj�5��?�������p�����5w��6@�!3M�x�[������@��l���-:���D�S%p�X<�D@������*IN<��g�������������M������s��!?��lw�������{�������}8��y����y89F�?����btG���hg\����f��`m��-���h������4)D���p��%�n6)n}�r�\p]�a�9U�Zd��L��d�]��Lt��������g��9�b���A����g�Fa����w�vI������H��q�9��4?��{(�.�4���@8��WD��C�:#�"��[�Y�"W�^��cG���������V{�z���q
���hs�:�X?{P����S���tJG\��T0�Ta�'�"���0������@9�����gMy}���P:������S�� ���)����W�9+��dX"/��]�����*�K*8����R�n�3P�
,\6H�������*���]�Yg�E��j���r5�+��.���c�C��Hz��f(��-z�.���1��FH��������^��\��}<]&������	02���uu���#��Rn���/&��yP�)F=���or�j�&�^������Z�aJ7��n�%h�d,��,�����S�� ����{R@���>���H����
��-O�2V���4����{s�8<|
��a�T��y���h
��Be��f�(�*�{��N������9��?�p�Wb���)E�����|�w�G8s�:o����g� ��Da�3Z^q�:��k��Ns�������������������\)k�=���������$?�/��{��+����N���P���`pw����
��$KQ��X��Z?�L�L�nFEd��K�YS���a)��+��4��&o����0�Q��0�8KGJ�jpC�-�w1��3�#�cX� �x*��<f����A_����uvU��y�MW�/�F&�U���I)>�������s���PL��"%(�pr������sNH���
�4m��9b�?U@X�,`�+j~O.E �b[��8��I�?Y��I(,���l"02�~����J%���0x���%����`�p�q>���<%�+���4c��9"���r�����A�����{�+�GB�pd���h�����F4*s>a�CZ:@y�Iv~~Qbq6
��~����T��rV���|Z����N���������
A0���>\����}������]r�d�Vs�Q�9��*E������F�\>���i�iZm���J���YP�1[{;���`���V��~1�)�������p`��#�Oc|�;��yx)��!�R_���'��i�������#6�B����4��������5���=���+ja�K�;���l�����8h�{p�����`�~���lNKv2!i����l�7��o��8���\o_���]S��������g �!(�E����NC}uxv��m����2O�{�V>��*�Xv��z;�&�J�Qk���\���4a�����8�p?_��E<�{��d��I{�#z�nS&���rF��mX��
�����azsq|x�v��8���Y2D���Y�}D�����'�I�S���CuY6��Vo���(�����\^�_qi@�����[��������G�W���RL��U�I��`�H��D������?��`�
s���+��]����D�-�	�#�rB�Zk�s-���&��k�-1�`��	�n!&+G����/n�T%�
�=���L�G�Z�
 U���vE^�������G��_��������S�H4D�������ON/�/������;+@>pOIQ������
�{-4�����A�B+��Z�b�&J�'����Y���+�*!$�I����70�A���;,��U�X9K���[*��T����q3V��s�m)mB���^^��{������Go��v_�>|Y*�>���!Rjft�B6Tf�U��5�2��������_�������������g�������:��;fW.^�=<����;yA�������ZYJ��&u���5����}2ed2����q�5����6l{&C��3�3���T��a�{�_�]\�zj������V���hW��0���#�	�^R�M ��L���j7<����L�~�LL��5jl��#S��>�����5���@�M��b����+7���A��lV��6�^�p��~�
���j���q������'��o�����<�|y�� z4�~���T�
{������?�E���j;��n�%�@�_�1"�>���"�'�����I��TIL�|O$e&|��wQ�-3���-\��O�_Py�q��=@-�n�5R<���� �v����m������/l����\���W�)�L���N'I#l���~�O��*\���������mp�0��J��-��^�xNFN��S}����Y.�l�����j}c�_�����j�z}�d2����%��b�-e�����F��V������Za�ko�f��a�;�;��m4�;u���5Z���	j_m��K�na(�x��-�������l���?�����;�=`�wv�����AT;8���v������5l����zp
�O'�@4j�'���{��`��c��?��x���������<��c`�z��t���q���W7�
l}���I���������d����Nm��$O�'�O��-���w~\|%s/��_B���R	�M>j~Fy�� �p�>�����Haz��;�{D
�
mb��Ju�k��R��������:Gl����e9
�\���DX�
��C�.��������v���B(B�{��bN�i��Gb���i��<�a��0��r��TP!tx��!�OC���
��t&��r2���?,'�p����\P��'����7.� �.�Wa��U����b�E�����4|����HA�l�y
g�;�_�}|�	�����=������s�n^w�����Q�k<B
0���50���Q�H���zg��_x���������� G>qP��A����A=Po��)����;��$��H�V���v���L���5��)c>m���U�{���a?�W��]|�����7���jI�oQEN a��sl���G��YcEg:d�����������/�u��O@B54��A%�k�v*��U[��`��(�����SL��s�#]@��|�����/�������\��+�L9-��A=�4��
v�V�����6��Cr��Y��T]�������e<��&�S�U�I���?��N�,��c�M,��^���|�D��V��T�9��7TJ"^k x��GTr��Q8��B����b�ZT�i�S���h�_E�7���3�4�m*E�(<�����zw�m�%35|q	�o�>�_�"�i���2%UP�$<w'a��>�)��(��G�����2���]������u�+���O�����tN��SG���<�-�a�O��x"�+i�?��<9n�]��89���2�{��Ws�3�8R|����i�f���z��-w��J�kz�a%x�zz}I��Tu6x�����4N�+PGn�-��gY��e�L�X�`8u�Q�i<��������9i�M�~���������5��_^�;�[���4C$Gu� ����X�y���dZ��f��T���_GK#"���,
����M�j�A�V9W)�*�d���
 M�� �4�V�o��t'���QBK�F*��zu(�[�r�7����8�9��.E�p+���/�s�(�o$��&����6��
)(�5���JD���`�.���2��VS	�f��9��i�V��G�
��,M[;�Uh��L�B�N]����4&Pb��VO���
��ej
.^v/O_^���1����y4�V�U�a��y�B���/���6���Mt��]C�@�����@����x�K���A�t�f;�7�����U����@�
8�����
5�?]�b����q���������`��u��c�4Qa�;���Z����A��"
�H��u�q�V:��$��X����F`����Z}���f%��"X���������K��&��)B�������k���f��
�����)I��<h
{��N��_�����h5:8M�O}�yvw$O�,�G�jrIK��R��B��xB*	`��C��"�A�����%�fT�xdO�@(]< 3*�Pv!����H��Q�!�9�����fC���?�b�~��I$Q�VE^l*
|U�W9�c��+E[N��	}bM�����3��o�Z<�"�4�g��5��z�|�<$��[p=���a!�H,z�R�K�e����J�@h#�	f�������K/����(b#;�tgR��#�RZxts�L��h���T/�l�7��+���h<[��x���k
g-5�8��K�(��|hp�!v�U���$�U�~Tu7A���YV�nP�*k��v��q����
����R4:�����$�������"�l�bsh��
7��D\z�d"H,'�������3=0��M��u���Gc��������4����~�]
�h4��7Al��(L��C4�b�2���(��.�`<���(�s�+����W��$�F�7�`�Z�H�f���&�E���:*�f��$�{\wb�Xw�����������w��|��X�����M���d��2`���pN1��M�b&�-+���a�����2���y/���S�!���R�k���s����|�)xN��)}B����]��%�z�v:�M5�;�����'P�B��z����X��C�� �IU�r-�)�N���}�tY@k= c�[�&(�����T�J����#�S%�yb�����d��~
�}T��|]Xmq�|!����PDT�T#*L�sG�a5�@��������=Jz:A���r���,��C�d�[4����&�pv���8�=�&�,�j]��M���
(CP�M\>���ZM��t�Z�.�d��'���>����*
TiR
��b�U��Sw4tl��Mj�G���8d&B��/K������Y;0kXxP��)��7�R���R)��J�n>�j���'�5��LZ��_������R"�#L���U	Q�M�#s
.F�9In�ha�G��:m������Y[E�h
�I��;^��7������K*Qv�y3@H��Tc����y����zU�J��~���0������%H�u�Q��M����7s_F(�)�T�[~���o��Lg���~)�1����w�^���W��"����|�}9�A�.K��Ui|��_���AI������������a���N�. J��`0_�ab��p�"�q��8�Te��U�+�� '�$	n� ��s�p�Kfw���0�f�9<W1G�N�\���y����$h�(D��e>i�����t(����3��S�����o�:�_���Q���
��A����"��������L�����
U�E�&��`�v[L����uw�b��`bTn�� $��.����nk��\�����6�;3�Uj��pv��)?�R��R	���95!���3XLM���V�Z�E�VC\�`���=�<����Zx�x�=|<�����f cN):,�UXD��@T%\_�o9'���sU���e���if)�����6_�������C���l�D��o
�K�9+Bk�W���4�1Az�<%#�!�O�����|��:��VJ��Y�|��j��8o��X�=�&[�"���M��H��r�@�/��|9I8��w�d����2���G2��t|K������6�s�~�y����<X(����T�V���0G��	5r�����������Pb�LD-�)J�t�F4q��y�Y��������Z;�L�e���6M�;����<��������Q_(�����@����]3�����<I���C>�D��5/G*k�K�L�1x���lV��Xq2����Z������X������P-�A8��G]5��Q��!h�z.'*BEk�E���'��j6M1�	�����w��G%���Y�T2�(>7���O�����@D��������9�0�%�v���v��?�%���_�b
�_�/�zL	���j�?�?W����(%�>���#���*�*Z%��6����������yG'��9Z'�Y8K��!����o����L�g���n�����g��*���K�~�su>&6��l�m@,�����3X?5��1H��pk��x���!��PX�[;������=�k�����f�C��dx�4������G�Jn���������.�����r�LG���6�Eh����|
���k���[�'Z	��tr��L��
�9<6��~�>�N')�;�e���������
��$)�+���-,���r�)�.�9�`�<�������
 �d[��D�T���-%�/<�)�1$u�����X��U�m�t��(��9s�m���[kN�LC:���9��`y$c�6��t�z�H��Lh-��<��	���$��EV|`\<��QQ�G��#���O���o�OK<_��������~�f����6���~k�����6���l��(DZB�=8s����eK�2����t�D�>�&��Q�X�5���Rf��R��b<M�t���TCN����C���\�Ae���){��7Pw�=��3I��Y����hz�H\Q��S��T]=��}!*1
�6��&"�T���UyD��^�+i�!M�����x~bt���H��c��Rxr6
�\R��$h'�2�E!�h���,����?.�_�.�&�Yo+�@�{,#���)K;W�j	�x�5�$�V;��
��>kv
�4Sth���B�+�����k$>A�Z���RF�b�nZ�/��,=�����:��s:��clp��}�C��9\�//�\\u�n�1��M!fb�,��@�p��+9���'C�����h��j
�� W|X����X�^17�R����q^�����	�Y^-����r�����:�*�t�N�]�9���N���I�6N?�X��B��p9���8|��"��������(�2�Mh�����j>�{��2R9�u�J��Rn�,m1w�0z�U���$Kf�h�r���p���Y��5�$�:�e���:r����7-Y�Hq���2���Y�}�9u�F���r+�Y���h�x,p��p~'���[�����X�I�3��U���NCfO%��\C��q0J�t���|��a���l��~U��6���(JR��R�>Q
��#�a��D�W�#%�+�meM$-��a����bN��N��.�1������+�����eh�y��}�<���w�I��E�	:
��[�M�((�����l�Wr�H��.�<����u ���,�'�gn90Y�&#P��,����u���F��7��Ta���!���U����:��n���\�q�XM4{�+��i�i:�����.�����&�!�d�~�	�F�D;�r�(ec@gZ�0�]�}��-�U������s���2s>��{*'C9Y����y�
G4���v�P����R<�HR5k��$�U.��H9��*2]>!OP5�68� i�6�>��e��<�Ut�6^F�q�����pef�"K��%�|T�T���^5�ps,��x�pu���L-a1a�|����0�5��Br	��������[+�r����-��f������i���wHKP������P)���R]#����^����k)N]���|`��B�\�>)
g����ev���U��t�CP��� �����[:�TV�%��UV��������%��� ��j��j)�SH���go�\���p\��d�8��Y�}b��}1�v
d��(C��L<H"��,>D1?J�s8��s����V��oZ�w��9���(.3'_J1��5�����>L�l���q��G�!hm���RNTpS.m 2����!�fH�H�|�X_`�=���r�H���<�2#W�m���k�u
s�q���VS�4��������������}����:������F��%HF��[U��A[b��2�6�����	��
v-Yl��K�O����}�P!��u���V�e8>�����V)�(��.gN{$������[^����#�@W�����U��y\����[��1"��������
�B`sS���-��-��o�o��j�& �w��Y�����anS��K��"�X�]s#��l����IV��*K�%��wjy9/8�������Z�����$����)4���a$���5n���m;�q�	�B�%�������A����0�����f���
\��?
���Y���hZ"�M� G�>��f�J-b#H�6
��R)e��z��vv}����-�W�*F$Q�h����G��`:~�J�_�����a�p�L��B4"EZ���v�j_�\a��W�=���p2����~h/�s�C����)
���>����>!��j/�	��e�l���)�0�#���x�`��6��P�H����|��f��b;+\�K�^Hh�[�.��4��74���1���X��^�[d[�iX+��S�?��)Y����J:1+�	�����CN+��"b��*�Wq�Y�L���&���D����������I�{G'�`L�k�:��Ji������S��[���2��3��D��	����2��������X���-u�z�?�����1z`��d���+_�S��Z|:3��}tW	I;`1N)�.������$h[������#_F�y\��	���(��u��Y.*D���K�9P��:�M])Gd�����N��
�176n�Zm���-��g�^4�c����a�s�
3�������5�f��e(E�j�Xs!�3��|p���|�E� H��"�Z�h���-HGV��V�H�`�c�����g#)�	�L���#'b(�H����I4sqR��p��'�-���|���t�����z�����Wg������9?�D���zKe�T���B���6�Z�r���3�,�M���A��,�Q4�Der�bz�5�)`����If�{,,�V�A4�a|"���u��������S}��n�>� c5J(
��ro��E	z�����;�A�G7����)\X�B\���.��R���]����c,�����>���M�yY������i��}������	��=���l��/O��?K�S�y��Y���x$��=��"�<���P|j��t%F�iJ�p�����2L`��9?�Xx���*V-:L�Y��5��Hn	�����0��^8l��$m������`�@nJ���q�*C.^S���U�B���	�����V��d��9�����]G	�
�j-�>z8u	?;Y����3A�|�r1�y��J����a���Ho�s�xs8������p���
l��!l����oN���	������*�0o�433��vT��a��M����k���Mb��FOuFb����8���[���L���������Z��w��+��ogk�����������6�f�b
f�ZgS,ffI�
��W��n�5cu?j��HB�Q$x��GR-���)�`IE�p���j$��_
�n?4h�}����l4�;����V���!b��4��kVv���;�B�p��t�\���X�
W"q��/��!/O����dg������!o�F��<��D�)`�[f�{�O*���a�G��NC��s��M&z7L����1�����?�������bK,},
�H���B��B��B�>�p����f�H�����?�?3h�\������#Yc25��>���9_�p��h�o^_���<E9��C���tc�sO�{��.��O/�'<O�����)�>�w _
��������)����AEUR�#�y�[�.^�/���(�5<�gjp�����0>��@7����|?���.�������6f�����y�}�y%��7�Q���2n��3n|���}#]��'?1P�d`��Z!��P���*�/��:W��dA�%�[���%�?���P�d�?e����Cp�:�I��BT�$H
����O<p/�d�3O'�):�����r�����E������'���5�;E���^9�5�n������U��#4��_%*�+��H��������i��S ���T7�H�*�����A�,�C������+�!��'���M�P���PO��)U�O;r	���%n�t��I�����C�	�t)
�U�B:R���mUJ+�����Y�F��Z+�`��L�wr_��Z��c��u�eX�u�|Y�l]o_�][��Wf��u��Y����5��u�~i��H_�\��g
����1�����������r�i�b,����>�����������z�����k��k2��:����u��*n�N�|����b
Sj�3�T��bcX����^�ZT��hk��A�Y�W�;��;����ZF���iE
��Q�h��p��h^�#��z��F���W����48�?���'e8�qPQ�����mxH�eaE���nn���S�n_8)K�Hr�>�@|����DUM�B��o������C�8���T������n@��G�<'�e��zW�J��d7Wa����7d� j�����k;�<!w�OH�/��A2(�[�f����.�W�s��]!��xe���G�-��s�����|vQEmt���U3]�G�{��zf����r~��PS�f-Q����9��jj5�OWG���I*�^^A��.^o?�&E�����p8���x��4L�*z����;���)��L�����.��br�o[*\���.�.�-���4��{��7`l��bWG�U��&C�����Fg��"�W]��Q�P��k�(�����z{�^1NF�XIzu+�=�6����{y�t�`���j3p��Tu��_��_�Z6�8�l�*��+���l��6���0�0
6��tl]���/���4,�����B����^���Rs9�]�����M�&�o�9�?����~8��~A�Q@� �jF�@���?��A�@��`'��|Gx����1�xJ����	���	��lJ���c�p�OHg���T�*O�R�pd[FA|��b��t��Y����<�^V6�m��"u���<��)��T���)1�_wk��$#��]
���9�A�H�>��.������*F�R�n~�J5�������]�����u��pr18�P��5���7��-3������-�e�����q����0M�snB���1[f���T��\U������A�~�T7�<�
*9��W�[io�R���%t����<�9;�e�af��UO��+P�M���I�J��D`1^�V�G�Co���^����M�����U����Y���m/$�,2��r�0I"�]��v��cA8>�^���"n1�������h���U��4Z�LhG��RN�I�-�,��}�.��[ar7���:H�D���nh�v+��Nm�Roxr�\����T*?���v.y
�>Mm�S�4�=H@�+Y�F��{�������99��J�T2�k��f������%��T`zj$����]�!�:|���(��p��I`R��l8�����_��^�_^�l��Kt����.Y��Y�������8��;Q�Y6����N����/��j���vVi2����p~����mE@�^�Ug�F�s�h����������_����~�m��k�������m�KP�����o���2�G���r_���{��	��?�/�����������h7l�vZ;�������~?6w��n����D����jO�A�p�<	�@�g�(��K�}����l���������	�����4�P}��Oj�'��`���S��,{�
���������WA��q}����5`|%�l������` 9"��T8���r�E��H�oP{��1�)�T��7&pD���Wt��]��wU^W�����w��4�b5Y�6p0*EM�e�I�������A�Q��f�p�Wka���p�{�W������A��H��6Z
�
��v���s���	�&�X��@]J$Zlm��r�;}������������a�����Vw�{��� ?,/ ��iB�p�,�t8�w�pqS���$�Y�lu�h�D�Y],Q6 �G���Z�����yo9[������iTvZ���w��Lg�����S�[X��cT����sn��l���]��	T_�'\���g_Y��l��Y������i�(KI]���Kbs�T��`���,�2�_	��QqR*%�"�����JP&����7����5��Y(/}Y
��(��,p6}3�`��L;�u�y6������H��)����
��K���M������S�����'�1�<�%�����4jn��0��|gs�0/�����Jk�z-V�Z-`�����/�[b0��$��3�(B;uB	����������k�����������e��G]�QxN!\p :
��,�\���`	`�r'Ye��)����n�I�ZE����=����d���I�m�����bBi ��M����H�0U��n0)��W�`R�nD,'M�k:�sI="�`������A�SZ"���t���/I3���
���Lt�����?zh!= Y������Z�F##��.�)�;D�b���!��#������}(E�z�(��U�:�.��&[�z��M�����V�o���0n;+��?kG������v�4��`�9��lu��'��Df�[����P�����y����?E�S}�K\�uE��Z��������,L�_�g���&3$���62j�����H7�G��*1	JO��LX�$�e�����i0�Q�����f���Q>��e<�7��P���5.��{>
��_�B���U��?����	��s���M��ev���'*�Sx�x2���)H^������T�W\��F����L��������9�GV�RZ�Gd>[�d-�r��l����=���2��I	��kC��l�,�����^��^�������K��/O~�\�������Q�z�j48���f���#���}�o�o��Q����D4�H���Tv����=�3���F^P!���������B����X��x���x�2p��=��*B��n-��[�uQ���\	I������������d���u-&l��t���[�Z6����O�^�8�PI���-2{�n����%�b�f�{a��r��[�K�>3�������Qd	������V���O&�y�/���t[���q���n��r�-;���{�V����] �����l�S��G�G���N�����EQ�l����fm0l5wZ;�F��h��������W7�
l}������w��j+G�_�����W'c/�i6%��6��=[��`��!,���{�<��mx�����"E����X�.����7�����e������l������,�g/60Ep���C���9��\�Vjc�TM����K3�4��P(�vu�[���R	��&�5 ��e,E��~�`������p�e�6t��r���"E��	=
��M�#F��yr����W�LB��C�J�k�Ut�i�lN�`'��{{
���������+��j3auQ��>���:b��z
�A�4H�|Fj/x��H`�0�
*�n��U��3s����[��Ql\��sts&@��������[x\�*���[l����j���I299�
��W!��o&����Ay����`���+����:�Z]�#���1��&Net�	�<;�T`&��#Ss���W�&E�0G?���R�r�[�m��;�L7������p�a��e�D�1V��*���V�i�7�	����n`������'O��x������e��	�S�@���w��,9�L��7P�d{o�����Ey��mA������D!���u,��T�E;[�i�.�������F���E�������_�����[/�[��|��O�F�����-:�Pv������A=X���=vg��{����c�c��z��7���a7_/���{����=�\��\��a5�+����L�i�<f]VE�y�k�x�x���k����'����r�P���y_5[�G��}�__c�V��*���o�t..��]������
[j��z�Z��o���7�{��N����1��h�/����A��ar���J8���v��c���@���O�n�IQ�V�)�]����{@Xx;U���I�K�[���MQ<t����R3D���:~w���B=�%��f*g��`�6���F��5\��8P�����SK��O��\�I�u�s��r�T��Ot�x�dk�����G�����n�
��KN>��w�1pnF:;��5�(Zn�\@�oI��A/�r���)*�����R���%=���|���������}�b�X�P�T�71�^��E�z�[C
>��W����_F���R yA$vd�r"���<������v�V��rrj����|�X��V>�&��hBn�N�6��k��>M���K�r�i%F���}�f;v(���=���@f����T���?/W��*#2]���k��$�^�~iv3�6�����3�	K�9�,�����T��rd�Q����`�YR��#X;(/>�#�+�k��,{IS��;�#����<��y�A
�|�����q�{r����'��������U�������������XU�����>�T����	2��b�i-*z�
^\8?���#k��~���/����[�
�b����|~
g���P%#H=�Fm�,���RP� �{
��A������xK�����n���e[����4�5�i��� ���N.�l���J��&���p�r\��Ba��B/@���(�V��_�2��\�!N�U8WzJ�td�H�d��|+��������{�����:���^3A,���>l�L�GyXD��y�����
�RR5�{�.2R�jn�p�E���{�^�q��E�+N�����oz�(}�^"GA�m���*PK�'��_��������Tw�;���&������G�l������P��D9��Uw�iQe�	�>S�V��f���i~	��r�7�
���7��Vk������T�V@b����rm���O�#@��6��t��r
��N�$��e�Vi��PG��]�\�Q����@`��
m�]����|S
W������[N�h��J%i�s�;X��
8��U�h	!j��,H�`��){d:�X�/z'�IC"#�J;�3
I�=�T�����;5��]�vd'u�#"Cb$��k$Z]�[�L�h���-�S���0	�E�w�\���O�?�H��(H����T�+:3�(��c
�rC@|���ID���aK�&�Z�K��Z�cu�M\\UK65$6�9�C4���(��$G�d�*�l�)E���o&K��d��V�,�l����f��q����.
������<���9r��8��TMZA-�<������~�E���MC������I��T�n�%�(��o��y(RE�3UL�IT���8�T��=\�F~������5R�m�����t�*(5�4H��2�ro5\e��@{�q���8]��"7�M�7�_U\�21��S�^FmF��Iz�pf�9�MM�-�j��h
R(��ED'tc�eS��4����6	*O,*n����,��e�P����"D:����C$�0��e����\��bE�
��G��O�Z�E-�SWa�7
����$��}������������g�����	|s^��Y�{�6���-�$�m���w��1	R��9�1)�d����Z "�T�Z=�y��DICR��H�@��+^5�5 =��B���.�������i�iwJ;n���E��C��}q��T��R_��~s��^��.��^��{�AI��e�&K�F]u])�E��T��E�#�pz:"�Q��hJR���7�P����o9)W=��X�����P6�	:��k9
 ��A�@�.:�'O�3�}�C�2�>m#�/���7�1��/���4�GOU��i�����CH1<����7��
��F�*�����y��d�y����MO~����4:����P�bm������!]�+�q;oO�e��lk�������PxEB����O)3D�������Cxi]C��U���"��W7�<ij0'I�\.i(�r�s��6�\���Q�,L����`�0�2NH@�o1~���}5�xkS8��Y]OT��9�~�g-�K�)/g�����l�5���
�y�7��6�:�FIe���l�9����S���[���uUle
V~C-{=s:�!]�>36=�Ug�s����C�ii�S���{��FuC�s���O�;�����}��^{������k��@v�>���g�;������S�=���r�yvCr�9�\w�=��s�}-W�x��K(�K
r�_�H�-i�J��� {g1:�4���b�r)���IEj|�#m@��X��\�]?�0=|>�����R� I1#_KS<���D%5�\����b��2���.����O��|u)�5rH���jZ�}e-�X�:y�����f���������zEj��V�+������LJ�E[z��<���<Ytl/H�$8A[+2��X�P����iX2���[]����AC���QH�J���Mz;aP�3���M��-���tm�������8b��
��� �o��
��]��i�RI��
���.'vodHi#��`5X�yp���(��O,������>����@�+�Kn.�� �@(V^611��u]K�m��������m�����������*Z}���f�d:�81d,� a����DQp�Ln\��6p�?W��q���0�$�$���Doh:F�-'a�!���.�{�U� 4Tx+k:i�i@���k����i�����(�����m�Q8EI2��=I�X8������.{����S�F1@�yr������������������y�L_��q�ipf�+��~�{U���F,eiT��I�;����_����S��NV������
6���5#L;d�cvd�/5h
t��-���K���
���j[5q�z�Z�
(�.�i<:�MJi_�Op����>�?�r�)}!�����:���:68�v��E�,�tq;e`�ml�j���uD)�C�9N���dM7�2������YKOc6��M&�[=se~��r}��}u�*�T%|t^��w�l�������;N��
cL>���\S���X�c:�����(_a�4���dl�)��v�IKtRX����9r%)D�|�C��uf�����X��`�p7��nm��O^���[��������� Q(,���M������L(v���7�G��0o	j2������4W�9�����K�8w����R{��!�l��Qd7)K[)��pV����%N��+�p�lv;���2�y��m���-�3���T"�|6kL/��H�H��*?�8���b:2Y����+3�ZOgi+��K��k��T���)f�h��k�[��LX���f>yi
S	��(@&4�����
� �`�r�7zd<���iN�G��[8���-��	'@�	>��3?�)��yY|���UI��������/��8���h@��^�NvL���D4P�wg��Z��?b��h4x~w�LN1I�nB��E%x�c��������_�*������w��F6�l�b=��K�����.�y�i/s��hgN���o�����j�[��!�Ou��z��?s�D4��=���:���h\���n�����!v�yL��_��K�X�	���"�Y5@L����G��o�<;3��]'Q������|:c���������+J�
,����U����y��
�B������������]2
2D��+$���������4���t��F-��l��E�TF����-mH�S�^b�y@�
R�G5�'��*�auV�Cqq��59�0e�F�_�V��'Y]�I�\�j���_���m���_��s_��{w�gx.������4�$�,e-_��y���X�"��R^o��(����	���[�� ���T�����������H��|.?`���8�sz�'K���%��)��'�iBo���r^���]X-3�R�,q�%g���{��)g����s���_+x��j����z�o5���v��+U���W��}�X'va���T[��
m����(�
9�$=�
��k����Y9ch"�9@/�9������)�x��8��"�U����	����
�}��v3$� ll8�!���MuQ���]�t���hJ��Y�K�����]Vr�%-�pL�d����l6��ocfm�f@��bpG��m� ��a�mf_`'�
�-�S�,y��a�+��nh��|�
J9����
"]Ai ���R�9m�����
����`!���
x���	�e�]������I����wg������;�"��n��G��"��> ��Z�f�[?UD�P���e����!��wE�E��������j,���"H>��^�������K`��dyT�Xf��g��������������XyB�Q5t�K����ux����S�9jG��K/ms�����Xs?nXNjj)�������Y:�N�����,�������H�u�)��OU���^��[����&��j�5R�b�J�����;��"����b����~�O�3�r(��4����2R	)���<�\��d�T;H�f�c-���vaJ����?���L����T#:'�,���X�2�$��nlC�b�V�1���I13I�;{���h�~��3�(���'��9J?�^��|�F6X��gD|��*�W�r(�7Z��F���i�p��PDg����/�d� ��k�V���W-��+s�0l�$�K�i���)�����N,�����O`�}1���(!����v�$M��yzDM��9�#���^��g)q����VRjI�T�%��%��\�OM�D9N���������S�M��Iprv��
s���h�U�&�����<����Q[�"�]����4� h�
�����fo3��*����(�3��+��Pj:8��^1�b�w��������S�Q"*������i�Frq9yj=i��I�IH���L>���M'pT�����V��$s��*�I�\�g��l�vIK�:��'���[KM��UL8-�N��9y�i�Z2�\��a+�[�|�%q�
�DJ���Ce�J�is�"���g��}��������HI�?F�u���]<�	��[{��!%8S	q����Z���U���0Mg�x���EYXI|J�S~\�49qzT4`<��fC:�TtP*pIX�/�h���n�F��D,��$�g�c��J�Z�e0���-n#6�����q�3�������G����#K�b�0aa��N����Z�����@�}Z3)�"�U�A�DPS�S��VS}�0�w��I�����xBI����kA������%a�9�v�� ���?J��f2�y##��l�:D�[��-T�=����;#��G	��>��dX-�P��59����y/�^���p�z���DVP���u5�xG;���8�5�s���1��
��n})�Q�zJu�L=��6�Jf��[:"�	�1Ji�@�I���"0CN�����*'��`�1��$f3���hf�Nv����Y�y���X�y~�8b!`��b��g��U;U�������}�s��';�_V��|A�%Ns�e>_|�6Q�r�`�����a(�BDI���J>��*0"��T-��gZ�_*���^��
���*Y���~�b3����_�2�N\b-����v+%���[�+�XU�Y��|���~��akgU����x�]Q��C9��E"���J����\qr����J`��a����TH�T��aF��H���� ���B���<��LX�D��� � ~%�D��)������.�P)������Y�J���8�r����Z	aFc�9�|�iv���`x�z�\+�:%�`�0�t�I���u����\�\���5��$�o�G\n��4����z��Y4G�5��2����-4>���v6]t��F���S��6�w�^v�SK��m����Pr�OW(
�=�|�vn���$�n�I������^\D�Z�������-�����Ui�z"����C��Y�����BJ+��q�S�YTS@);k>���FZ��y�\��
#KZ`�wA�HD��R7�,��+���h��(�%�b�0:�����	a�J%�����U�<�����lN1�D�{'�,��H>Zw��������*5����Z5��bz-�7���%���ertSK����Q�M����j�5�6�#D
�:5�����UF�jJ�@�1*2�t
nX�\�E�R%=2r.CV��bhfv}k�2���!��do�M�!H��[���$�=����'���\�;�=���<�t�l�	�2��
����--VoP(9�.<s�3����Yudgl���6��e�Nh����M>���vJL�f�����|#P���IV��J�E0�BU�F�oK1�sBU�%�$�}B]�/Q�e�{U��xr�*��|���j�,����{�y{�_�v�g~%���O�:��
f]��S*��A$�h8U7����V-c)&���i2�O4Y�&z}���"~�Y
Wmh�#��~�4P�Yv3~�����G�����AH�����C����OoF��M���t����Ly��vU�����b�����;Z�;D�uX���e����{>}���!i���p����/WS����������~��z�z]�J��g��d��� �h>1���u�<��g�l����Up"�+
W�����/��l�iL��1�������&�C[������=tA�'33@,V�����R��J��q���-%/i�����z\:�_��%dJ+��!�v�1��[Y
���;�����C^�������P0�W�x(����5	\S�SXaU�$(��Z����F�U�+<��%Z'�)�Nq5(�6m �
FU�1����a�P*6j��F'����`eE)c��K�GA���q���)g��_��ev��)F<B����z��-<�U���"�/��'ktNj�VdEv�r���>�����iU���6�{�(d+����(��\��oY��>'����j��o��*��-�)��f���k���]S%H@-��(^�,R)O��8�9��~3u�SbS��M9��Q)�M�V�0S�'��\�z����J�G�������?���R�����3��s��������h80(N�s2�j9����(���2����Nm�G'g�����0�����R]������1��"`ul�z~��(% ��}:��FE���C�`�&�J�PS����o��^����8i��J%*���H����I�s���n�)��z�:��R�������Z[���E�(?��U��Fg��U�?|����d����l�iD���_���}={^tk"����_w�#��Oaau�M�v�v�r��t�n9��>0gI�z����^)�9�g�_�O��%T���������~��}�E�^z��;��R����Xf��%��+h��&
y�\�&������[�5��W�1Z�s�
�Yc�&mvHO��+hM��C�m:n$�	������ Gs�L>���%�zb_c,��(�y�XnSa{���~����T?{��$����9�^m�����l`���g?t����D3���E3��6���}4n�y��Nr����JO���������������zOK+��L���*����>oS�9��t�#�z:�;?�g2M�'�	��Z���jeXcT0����v��p�2�����iv�u�V���V�:,��$|��as�}�ScA�$���k_L1�`DJu��P�P)��W����)��C<���u������z%�S'�x)Kp��7'gW�f������U��B��}�w?x@> �@sN/Pq��6�����Ow���DQ3�TN���������,�w[���%��	����AB:�W�q12/�<b�8���EC�
Qch�����Tn�x�
�����E��V���s4,�]���y�;l~��f�Vyt�B��7Wg��<_���GK������Uu�
i_Q�S,��	�I�jO8U�e���U����� ��>I<\(Ve9����	(Y
\V������8oe��w�+?C^"GpS5@�t�0��w������(�ZV�xA#�y��Q��p�����{���XU��,#�Sn�T�|����M��L���	�al�*)���A��
�%*��0����D�i���J�Fb��N�&�T����&�M���tgQ�.�&&��1����5����h9���lGO7��}b@�D{�a�yUt?��!�t��c�����*m�n�D��<��*�8@���2*�
��8h����1�����N:�.��+��Dc�/�ET9��o9�P�j��U%L�K��;���~^�Q�^�db@���A������#z�}$,7��b;+��4e�B����'z�!��Y.�1���XHV!�i*y9����K�R���h+���d{�!S��(�i���8$�� 5N�}���(��D�*	�
�2�`��D�e����9%m:��I'V���&��8��M�������Vem�$^�)���������I��YA�f*���{����{�Q�fU+_H���������J��LR&���f4��#tYFY����N�$��7UX���?"��C��v�L�G��2H�wE���!a�&s�2.�mY��
;`��,��H7����:�8Vh{
#����L��q�H����+��<���0e$���.�
t�[}�.m��8�F����+o�\a��e/7�wl�U`&��;����Kn�zYl��)���X�dX�~��-�	C9�a�)�S`���{���|��7)�[��L���Tq>��x���+&���%���)���S[v��p����j�\4�x0X�I�_D���Tw�$�]��"����n�K*��� Pyzw,o�,��s�|������n���4}r��,iIs�6g97��4C�rd�,��x:�H�
BV��1�f��
���)�
�������N���R]f)���O�lr�C�1z{a��(H���A-��Ci���
}����Lz�3.K�n���m���Bg��G�$��uvEt�>��`���HN��l%�@-.T���:$���X�XEP����%��Z�8���c�!�����(���3Dw<�6@����|D�����F�K�E�l��!v>���
R��a��������$^�
�(.�t��]�JY6������"�e� Dda�?~�������{���z%X
I���
�n���T���R��}��[�q/j��3����*�F��Ok��F�Y#����~?Wd1d�>b�o���~	����a����QX��>2S��I��Y��qO��4��Jmu�3�(}���I����sSfw>��2��f"�x*���\Yp/��,��1Y�
~"�e��Z�j�+���"���sa�*�^	�e�X9LV�$����+�=Xl���`h2���\9����u�^-�W/f%M�9��J4 q��X�����Lj<9�� ;�V�*#_<~��"6�����C�pD������z=�:�4�������J�F���W��2��i��-)����y�����c;J�Y�	q����(c�0�k	M�N���"V	�����A"�E�9lA�;Y�Z���8������{��# ��p~���(�3h�b�~6�/3��@OJeYb.L��V,�=`��e�!T	�!;����k������������k)e�	���*��Dl��&k���vq��/�V�\�����c���h����+���2[��0�T�I�A�,�������U2J|���g'w
S��a��pL��i9Z��Y��/�|��Xf�a��V��k�����+U�e������(L�OX���t�4�w1��V�8�Q��,R���'���r����Z�x���IEBVSNU�����<0@��?VH���Y5���FNxkz�]*�Wy���.v
a5�L���O��J�: ���}���qv�Mm��'R�Y����N)k<-2a����[���HD���}�!��v�TZ1�~��7�Vo��d�Y�3������)�2B_�8<;9�@��>:c�//�����Mw��xqrt�,v����Q��\���i�\��n|	>�P
k�}���4��5b��Uz �P�x���Y�C]8�8���'�>���j���^q;��>s�Y���0r��7������Ok��Z����eq�)7�rb\�J��M{�h�{���
�Cs+j���bPG;�(���`H�<^������4�� kV�$��m2���(�}jo�������;�@��R��<KQDN��*��zY�E�&j�����^!^��8���	��X��)W�����9q��q�c�^@�a��Jxx��~ ��-���kQ�������_����s���ofA��gM�r��PJ.���s�.0�v���H�b}9������U�������qXy���G��'������cX���>���K#=oY�#�9q�����P�z�oy���v�s����~W�|Y��H������W��2�"�����\�j��������_��ie��������������qDk{!_I���!H�@I�!b�*,Q<���=%"�D�U&�/�5��'�,�x����v"�U����O���%���hE"PUv�T��,cJS�J&;�0 �q ��aZS����������HK�l���z��|�$��%
k
����IC\�kr%��a�����w}������m
�p��3w����z��Z
^����hTVm�������U���e������\G�.�����5��&?��(�d8��{�ufO��*��OH��-��
�y���[�q~;
���q<�V�i�����9����z�$27V1P�3�vI��3h a[��N�?�����7�R�	��
L�}�]$������r����w����ay~���b�!O�XP����2~�A�,����d����t��i'<�M�+���o�wB[�3M/�xG�*��j�d����iY���t
�^z���5H���d�,���d1�������z�xt���$GIu����M7�� �4{�FX�W����9��z��j67����������}����wk��`�m��_g���)��;
g�@xy6\� q�.�+������^���@�'%���:J����k��`�l�����U��-����������0l�w@`�Yxi���8hUZ������F���%@�s4��">�|���,��-��	J�pvl�]����|�,��!R���p��er��X/���.��lp[�n�r���H����?d6��F�hwp��U ��z���d7����^��}�����)h���2�W\`�9��
zx�*P�H��h��f��26#
R@�	����eF����ZfD.������8/�����{��t�����R�^ReC�x�~	eQ����>fl��I$>��h���A��	G��7G��p��FP�h��FW$�J����rP0X��g
������z�����ip:����j5����u�f�C2�	��N�������j�<��t��G
�������yo������[������}��x�s� �MB%lW�	P�?��U����Yk�i��f������7!B��d���>[�PN&��~���X�3�l�o����}}P	.^u�t���6�0�"�=m����+�N��Y���_�����8��#���b�����bZ#�#}���9;���>'<���?<z�jR#��p������$!![2����T������p<������a��}Y`��haj"�ZnUbS9I��mf�2�U9�0�.���������8S����p ]��r�������<?;�df���
\��E#�w8�r����G�xi��;��������d�:4���:�q�)�dC�2�mz����U����g�
U$����^Z�[�_�4��h���
�J
����V������D����K.��a�Wp��: �n���)���8^=������r������:�99���&�s
C����P4���BTY1�'R)��g�7pyN�(76�)@2]"-��K���d���������������N�����8}
Z�=Vg�_��������������`P?�	bz���������*6K�2�[%��i���?V��&��+�cR�x_����rpm����R���@��[$�-�*���R4����C�wY���n�U��u����?����B�m��;�5� ���f������"����������[	8��b|D�J	��;g��q:��U���T�G���9o��\W�P=XZu��jz��u������������<S��y���3*d�F�&�tgLIi�7"�Ho��$x�ON��%����J)�=��y���&Xvl�
=r�����]�sG�W�WL�C�,�Y����Nrl�%� ��T.E-�"�*V�������<v$�w��L%F����zzzf~��lW�O����v��!���(~�x�D_������U��[mWkJ3��}9��A�n��:����O����Z� �[��0zG��G@+�6U���(U+�����\�t��$C����dJ��/0�\Bmm�H�/�	�q���q�+�*��/�$��g���ma��O��=Z)-���c4j���h�%`�n{!�����(47�H�xL�����#>�xwr���xl�3�7'O|���>o����Ge��?i7\t�-^2���H�D������Y��}���lE� 7}�*u`���0�x�k���N�
}���&
em���#�R�D�p���d����v�����	t�i�w�i��(���J�P^R��pS��� �lk�d5��(�P�.����6�M�X��zb����I���}�r��)��6H7�;��]I�%��K�t��\�gu��/A�g`���x3�2��p�s�����f`��RE��C�Qb� ���Qo�!Y �n[�F(����
��;�:�Q���Z���X�\���E+����v�5| �f,�VQx�q"��3����(x�y;�7�s���a�_�
�|l;`��X'�%=&16�t =%�6ycI�����
L�i
z�p_����$�^��	�dy��N�#���	��8�]C��v�����Jv�7��w��N�����,SW[/3"m/����4>����D�u�����Mi}��>�fC^�
�,���MU,��gq^�i��_Sc�N/�����2e���k<���OsQ�\�����u�����_��Au%��M�<iRr�����D.@,Y�vF#N��r/g���vV�1M0���G�����
*)���^���v��|��

�MM����O�����CCk�1�r{�=��o�T_S��8��-%�M�*DX7���J�q/\��E��W�x��G��y��f�%SM�<����/����P�u�*^�k��O���g��^M��&�.��k
�J�de��v���vY��C��[��{n_�_�
�mk��O���5	�@�����-fe�S�9����rX�LJo�_�.�V�����f�w�p���������W3��>�O��Q[�i�e������25�I62���^���>Z�!W������Y�%T�8�py��^����~��em����Y���x�	<3���>5*n��)d��E���-@�k{�>�7�N������v���������M�IH.	��}�5<�������(}
v�����%����FP��S�#�[��������T ����d��~���V�������,��:����������]�pj�z�T���:�E����]v[���;Z�����N�_>?�*�:���K��1_�\��o�r�:��h������^M��X��W�ysMI�'�9��������G�x��}���=&���1%M���4��t���$$c/OF�a�<��{d,���>=jM�U���9d��jW��9�	��)|g�SW��(��p=Xg�6H��N@2�^��-+�K>;Q~�w�~����6�<�lR����e4��6������+��Ys��l�V�"�������Z�b��>�ZD�23�b=���"g+���E,�<�e
��Q-9E�f��~1I�2�'�/�C�q�j�A���,Z�L;�l�m+��Pg�nq�s���� ��x8Y=e����t� ���]� �z�s�����M��{q����_�:�z#�� ��������v�Z>���������J@p�j���)�@P���t1/2��Q8�z�����.S:�	YGu\f�����������B�������r����
+^��Q����r��sh6:���?����x�Z��C ���C�X�u�� �K������&��nu���f���dO����L;���/2��N@<��<nC���eA�#Z��;��� ��;CaX����w�`�
r�8���m��(l��d[��[�?N���������|tdm:�|]�F��k��C�d���MH�j���u�^=2�&0 ��w��rP(|���T*�j�5�A�����4_N������Kg/���J<w��)��zH
P��;h�+�7}��j��Z���S*���'�F�U��;�,���*�`8��,G�#���=�E%����~�����n3����aTm6z�(jD���n'�+A]�M��u����*�}�OUa8Hf_]^OF��_�g�dA��#����( �z��p.�}��f�-�
P*�������
�d�b��
D����uxy���qP�U	��/yfUc����|V-����o���[�����h)����J�K����o!9�),&@�p	m���D�\PYFus4��`���S[�2�$�FMFPw���'��3����fa�M�]d����B��n%��W�r9�T���
3�,�6�v*\x��������e`
a%�\#�b9�����z�x~@+.F����e1a.B�Vl,h~�\D@c|)�C�����#-����-�^r8E�c�����������������C���X(��b���VB5��b�&���u_�+6�,�>��b�V9`
t����� ���L2�q��	�a)i���;�/��p��&n���-"�	���Fl+��N"
\@��
��3���y��Mg�L��K���:cJ>�W�������|�������1{W��D�������R:�SGR
hH\�)�� H��
���e������/��2�M���6�\=����L%���Q4_���]�B���.��e�X��jP�{�����������n��������A?��5v��p�Z����qE��NP�W�����G�v�zBV��!F���l0������n���W�T��w�}X �*�Z��K:i����bW%�Z(��_q�xB��W��|D{X�oO��pt�Kh��N���mV"�N?�*���_V���X��e���q2e�����H��"��������2o�'����
Di����u�7y]H@*���i�`�V��S>l:A����0����i��A�.���:��Xn���E�>5O����a�D���.|�����>�m���*P+4���v��L�`���^�V.7�����b�^���r	����l�T�}5Z��$$1_�������.O���3�
���0*A���A%�hW�Oi�O�5����M��Q��������������X!��g��K����c�x%sMZ��Tx|\�B3t��g�0G;L���A�
_�R�5,����i�e7>;����3�\�"�;�9/��oZ�a�����V�*e�%�>;9��)�.���h���=?=�9��}����R���sDc2���o@�a-���EXJ�����;@���t��'��'���Z�sc��y���kS�$x����<��ub
���-��$��_m:���~��(������������c�w�Er�8��0��}��)���d&IE�;.�����$��\7Z��3�k�������pl�J��4��~������k9NEo1�1�����\;�X��}J��v�E��98\\Q���VR5OjUH��X\smWh
������}���������kt���`�E
#���+*�"u�i�������j_�]�|B�B����t�`��Z|�����4�OCg�Ib�f����1����~��tm���4��:]L�I#�dq�&���������	%��1��^��G`���W�v|�����m
��v��-GS���"S�)�����6O`������V"���tb�%r3i���'IH�Q�#���M���DB�9J���-<�I�e:Bw$�%��-�5&rTR`����n]����Z�m����%B���|��$��������ZW��Q<B��N�AG(�:g��W��#i�D��q�D���8��o=��T�����&fA+�	������f����)P����>QzLD��+�G2ed	s�m*oA�������� ��~r#�NZ�ZR����|��J���=Hc�Q���X$�HwYpt���c#��f���q�15���\>���3�&�'�+�M��3SJ�Qj$7���}&�n�K^�����,o��d���'��g��6��ag
���k�r)O����*frw�[;������.�'��~+���j-�����<��*f�A�"�v�t��&�B����K~�ru�"m#�D�����D^�#�U�+<p
������
���J7���Y4�� �T|�l���z�]��m!{�x�+2�/q;��uTd�-��c:wpE��N&����0�>��S����$����{d{�n=o3j��pG���������w���YO<,	
#230Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#117)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Jul 18, 2019 at 4:58 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Jul 16, 2019 at 2:20 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Few comments on the new patch:

1.
Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.

Which mechanism are you talking about here? By any chance is this
related to some old code?

Current code also we have option to prepare multiple records and
insert them at once. I have enhanced the comments to make it more
clear.

2.
+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.

I think this is out-dated information. You seem to forget updating
README after latest changes in API.

Right, fixed.

3.
+ * The cid/xid/reloid/rmid information will be added in the undo record header
+ * in the following cases:
+ * a) The first undo record of the transaction.
+ * b) First undo record of the page.
+ * c) All subsequent record for the transaction which is not the first
+ *   transaction on the page.
+ * Except above cases,  If the rmid/reloid/xid/cid is same in the subsequent
+ * records this information will not be stored in the record, these information
+ * will be retrieved from the first undo record of that page.
+ * If any of the member rmid/reloid/xid/cid has changed, the changed
information
+ * will be stored in the undo record and the remaining information will be
+ * retrieved from the first complete undo record of the page
+ */
+UndoCompressionInfo undo_compression_info[UndoLogCategories];

a. Do we want to compress fork_number also? It is an optional field
and is only include when undo record is for not MAIN_FORKNUM. For
zheap, this means it will never be included, but in future, it could
be included for some other AM or some other use case. So, not sure if
there is any benefit in compressing the same.

Yeah, so as of now I haven't compressed forkno

b. cid/xid/reloid/rmid - I think it is better to write it as rmid,
reloid, xid, cid in the same order as you declare them in
UndoPackStage.

c. Some minor corrections. /Except above/Except for above/; /, If
the/, if the/; /is same/is the same/; /record, these
information/record rather this information/

d. I think there is no need to start the line "If any of the..." from
a new line, it can be continued where the previous line ends. Also,
at the end of that line, add a full stop.

This comments are removed in new patch

4.
/*
+ * Copy the compression global compression info to our context before
+ * starting prepare because this value might get updated multiple time in
+ * case of multi-prepare but the global value should be updated only after
+ * we have successfully inserted the undo record.
+ */

In the above comment, the first 'compression' is not required. /time/times/

This comments are changed now as design is changed

5.
+/*
+ * The below common information will be stored in the first undo
record of the page.
+ * Every subsequent undo record will not store this information, if
required this information
+ * will be retrieved from the first undo record of the page.
+ */
+typedef struct UndoCompressionInfo

The line length in the above comments exceeds the 80-char limit. You
might want to run pgindent to avoid such problems.

Fixed,

6.
+/*
+ * Exclude the common info in undo record flag and also set the compression
+ * info in the context.
+ *

'flag' seems to be a redundant word here?

Obsolete comment as per new changes

7.
+UndoSetCommonInfo(UndoCompressionInfo *compressioninfo,
+   UnpackedUndoRecord *urec, UndoRecPtr urp,
+   Buffer buffer)
+{
+
+ /*
+ * If we have valid compression info and the for the same transaction and
+ * the current undo record is on the same block as the last undo record
+ * then exclude the common information which are same as first complete
+ * record on the page.
+ */
+ if (compressioninfo->valid &&
+ FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+ UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))

Here the comment is just a verbal for of if-check. How about writing
it as: "Exclude the common information from the record which is same
as the first record on the page."

Tried to improved in new code.

8.
UndoSetCommonInfo()
{
..
if (compressioninfo->valid &&
+ FullTransactionIdEquals(compressioninfo->fxid, urec->uur_fxid) &&
+ UndoRecPtrGetBlockNum(urp) == UndoRecPtrGetBlockNum(lasturp))
+ {
+ urec->uur_info &= ~UREC_INFO_XID;
+
+ /* Don't include rmid if it's same. */
+ if (urec->uur_rmid == compressioninfo->rmid)
+ urec->uur_info &= ~UREC_INFO_RMID;
+
+ /* Don't include reloid if it's same. */
+ if (urec->uur_reloid == compressioninfo->reloid)
+ urec->uur_info &= ~UREC_INFO_RELOID;

In all the checks except for transaction id, urec's info is on the
left side. I think all the checks can be consistent.

These are some of the things I noticed while skimming through this
patch. I will do some more detailed review later.

This code is changed now

Please see the latest patch at
/messages/by-id/CAFiTN-uf4Bh0FHwec+JGbiLq+j00V92W162SLd_JVvwW-jwREg@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#231Dilip Kumar
dilipbalaut@gmail.com
In reply to: Amit Kapila (#121)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jul 19, 2019 at 2:28 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 11, 2019 at 9:17 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 11, 2019 at 12:38 AM Robert Haas <robertmhaas@gmail.com> wrote:

I don't like the fact that undoaccess.c has a new global,
undo_compression_info. I haven't read the code thoroughly, but do we
really need that? I think it's never modified (so it could just be
declared const),

Actually, this will get modified otherwise across undo record
insertion how we will know what was the values of the common fields in
the first record of the page. Another option could be that every time
we insert the record, read the value from the first complete undo
record on the page but that will be costly because for every new
insertion we need to read the first undo record of the page.

This information won't be shared across transactions, so can't we keep
it in top transaction's state? It seems to me that will be better
than to maintain it as a global state.

As replied separetly that during recovery we would not have
transaction state so I have decided to read from the first record on
the page please check in the latest patch.

Few more comments on this patch:
1.
PrepareUndoInsert()
{
..
+ if (logswitched)
+ {
..
+ }
+ else
+ {
..
+ resize = true;
..
+ }
+
..
+
+ do
+ {
+ bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
..
+ rbm = RBM_ZERO;
+ cur_blk++;
+ } while (cur_size < size);
+
+ /*
+ * Set/overwrite compression info if required and also exclude the common
+ * fields from the undo record if possible.
+ */
+ if (UndoSetCommonInfo(compression_info, urec, urecptr,
+   context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf))
+ resize = true;
+
+ if (resize)
+ size = UndoRecordExpectedSize(urec);

I see that in some cases where resize is possible are checked before
buffer allocation and some are after. Isn't it better to do all these
checks before buffer allocation? Also, isn't it better to even
compute changed size before buffer allocation as that might sometimes
help in lesser buffer allocations?

Right, fixed.

Can you find a better way to write
:context->prepared_undo_buffers[prepared_undo->undo_buffer_idx[0]].buf)?
It makes the line too long and difficult to understand. Check for
similar instances in the patch and if possible, change them as well.

This code is gone. While replying I realised that I haven't scanned
complete code for such occurance. I will work on that in next
version.

2.
+InsertPreparedUndo(UndoRecordInsertContext *context)
{
..
/*
+ * Try to insert the record into the current page. If it
+ * doesn't succeed then recall the routine with the next page.
+ */
+ InsertUndoData(&ucontext, page, starting_byte);
+ if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+ {
+ MarkBufferDirty(buffer);
+ break;
+ }
+ MarkBufferDirty(buffer);
..
}

Can't we call MarkBufferDirty(buffer) just before 'if' check? That
will avoid calling it twice.

Done

3.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer

/thse/these

Done

4.
+ /*
+ * If we are writing first undo record for the page the we can set the
+ * compression so that subsequent records from the same transaction can
+ * avoid including common information in the undo records.
+ */
+ if (first_complete_undo)

/page the we/page then we

This code is gone

5.
PrepareUndoInsert()
{
..
After
+ * allocation We'll only advance by as many bytes as we turn out to need.
+ */
+ UndoRecordSetInfo(urec);

Change the beginning of comment as: "After allocation, we'll .."

Done

6.
PrepareUndoInsert()
{
..
* TODO:  instead of storing this in the transaction header we can
+ * have separate undo log switch header and store it there.
+ */
+ prevlogurp =
+ MakeUndoRecPtr(UndoRecPtrGetLogNo(prevlog_insert_urp),
+    (UndoRecPtrGetOffset(prevlog_insert_urp) - prevlen));
+

I don't think this TODO is valid anymore because now the patch has a
separate log-switch header.

Yup. Anyway now the log switch design is changed.

7.
/*
+ * If undo log is switched then set the logswitch flag and also reset the
+ * compression info because we can use same compression info for the new
+ * undo log.
+ */
+ if (UndoRecPtrIsValid(prevlog_xact_start))

/can/can't

Right. But now compression code is changed so this comment does not exist.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#232Dilip Kumar
dilipbalaut@gmail.com
In reply to: Rushabh Lathia (#151)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jul 24, 2019 at 11:28 AM Rushabh Lathia
<rushabh.lathia@gmail.com> wrote:

Hi,

I have stated review of
0008-Provide-interfaces-to-store-and-fetch-undo-records.patch, here are few
quick comments.

1) README.undointerface should provide more information like API details or
the sequence in which API should get called.

I have improved the readme where I am describing the more user
specific details based on Robert's suggestions offlist. I think I
need further improvement which can describe the order of api's to be
called. Unfortunately that is not yet included in this patch set.

2) Information about the API's in the undoaccess.c file header block would
good. For reference please look at heapam.c.

Done

3) typo

+ * Later, during insert phase we will write actual records into thse buffers.
+ */

%s/thse/these

Fixed

4) UndoRecordUpdateTransInfo() comments says that this must be called under
the critical section, but seems like undo_xlog_apply_progress() do call it
outside of critical section? Is there exception, then should add comments?
or Am I missing anything?

During recovery, there is an exception but we can add comments for the same.
I think I missed this in the latest patch, I will keep a note of it
and will do this in the next version.

5) In function UndoBlockGetFirstUndoRecord() below code:

/* Calculate the size of the partial record. */
partial_rec_size = UndoRecordHeaderSize(phdr->uur_info) +
phdr->tuple_len + phdr->payload_len -
phdr->record_offset;

can directly use UndoPagePartialRecSize().

This function is part of another patch in undoprocessing patch set

6)

+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+                  RelFileNode rnode,
+                  BlockNumber blk,
+                  ReadBufferMode rbm)
+{
+    int            i;

In the above code variable "i" is mean "block index". It would be good
to give some valuable name to the variable, maybe "blockIndex" ?

Fixed

7)

* We will also keep a previous undo record pointer to the first and last undo
* record of the transaction in the previous log. The last undo record
* location is used find the previous undo record pointer during rollback.

%s/used fine/used to find

Fixed

8)

/*
* Defines the number of times we try to wait for rollback hash table to get
* initialized. After these many attempts it will return error and the user
* can retry the operation.
*/
#define ROLLBACK_HT_INIT_WAIT_TRY 60

%s/error/an error

This is part of different patch in undoprocessing patch set

9)

* we can get the exact size of partial record in this page.
*/

%s/of partial/of the partial"

This comment is removed in the latest code

10)

* urecptr - current transaction's undo record pointer which need to be set in
* the previous transaction's header.

%s/need/needs

Done

11)

/*
* If we are writing first undo record for the page the we can set the
* compression so that subsequent records from the same transaction can
* avoid including common information in the undo records.
*/

%s/the page the/the page then

12)

/*
* If the transaction's undo records are split across the undo logs. So
* we need to update our own transaction header in the previous log.
*/

double space between "to" and "update"

Fixed

13)

* The undo record should be freed by the caller by calling ReleaseUndoRecord.
* This function will old the pin on the buffer where we read the previous undo
* record so that when this function is called repeatedly with the same context

%s/old/hold

Fixed

I will continue further review for the same patch.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#233Dilip Kumar
dilipbalaut@gmail.com
In reply to: Thomas Munro (#187)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 12:21 PM Thomas Munro <thomas.munro@gmail.com> wrote:

Hi Dilip,

commit 2f3c127b9e8bc7d27cf7adebff0a355684dfb94e
Author: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Thu May 2 11:28:13 2019 +0530

Provide interfaces to store and fetch undo records.

+#include "commands/tablecmds.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+#include "miscadmin.h"

"miscadmin.h" comes before "storage...".

Right, fixed.

+/*
+ * Compute the size of the partial record on the undo page.
+ *
+ * Compute the complete record size by uur_info and variable field length
+ * stored in the page header and then subtract the offset of the record so that
+ * we can get the exact size of partial record in this page.
+ */
+static inline Size
+UndoPagePartialRecSize(UndoPageHeader phdr)
+{
+    Size        size;

We decided to use size_t everywhere in new code (except perhaps
functions conforming to function pointer types that historically use
Size in their type).

+    /*
+     * Compute the header size from undo record uur_info, stored in the page
+     * header.
+     */
+    size = UndoRecordHeaderSize(phdr->uur_info);
+
+    /*
+     * Add length of the variable part and undo length. Now, we know the
+     * complete length of the undo record.
+     */
+    size += phdr->tuple_len + phdr->payload_len + sizeof(uint16);
+
+    /*
+     * Subtract the size which is stored in the previous page to get the
+     * partial record size stored in this page.
+     */
+    size -= phdr->record_offset;
+
+    return size;

This is probably a stupid question but why isn't it enough to just
store the offset of the first record that begins on this page, or 0
for none yet? Why do we need to worry about the partial record's
payload etc?

Right, as this patch stand it would be enough to just store the offset
where the first complete record start. But for undo page consistency
checker we need to mask the CID field in the partial record as well.
So we need to know how many bytes of the partial records are already
written in the previous page (phdr->record_offset), what all fields
are there in the partial record (uur_info) and the variable part to
compute the next record offset. Currently, I have improved it by
storing the complete record length instead of payload and tuple length
but this we can further improve by storing the next record offset
directly that will avoid some computation. I haven't worked on undo
consistency patch much in this version so I will analyze this further
in the next version.

+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+                  UnpackedUndoRecord *urec,
+                  Oid dbid)
+{
...
+    /* Fetch compression info for the transaction. */
+    compression_info = GetTopTransactionUndoCompressionInfo(category);

How can this work correctly in recovery? [Edit: it doesn't, as you
just pointed out]

I had started reviewing an older version of your patch (the version
that had made it as far as the undoprocessing branch as of recently),
before I had the bright idea to look for a newer version. I was going
to object to the global variable you had there in the earlier version.
It seems to me that you have to be able to reproduce the exact same
compression in recovery that you produced as "do" time, no? How can
TopTranasctionStateData be the right place for this in recovery?

One data structure that could perhaps hold this would be
UndoLogTableEntry (the per-backend cache, indexed by undo log number,
with pretty fast lookups; used for things like
UndoLogNumberGetCategory()). As long as you never want to have
inter-transaction compression, that should have the right scope to
give recovery per-undo log tracking. If you ever wanted to do
compression between transactions too, maybe UndoLogSlot could work,
but that'd have more complications.

Currently, I have read it from the first record on the page.

+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by undo_apply_size.  If we could not read all the
+ * records till to_urecptr then the caller should consume current set
of records
+ * and call this function again.
+ *
+ * from_urecptr        - Where to start fetching the undo records.
If we can not
+ *                      read all the records because of memory limit then this
+ *                      will be set to the previous undo record
pointer from where
+ *                      we need to start fetching on next call.
Otherwise it will
+ *                      be set to InvalidUndoRecPtr.
+ * to_urecptr        - Last undo record pointer to be fetched.
+ * undo_apply_size    - Memory segment limit to collect undo records.
+ * nrecords            - Number of undo records read.
+ * one_page            - Caller is applying undo only for one block not for
+ *                      complete transaction.  If this is set true then instead
+ *                      of following transaction undo chain using
prevlen we will
+ *                      follow the block prev chain of the block so that we can
+ *                      avoid reading many unnecessary undo records of the
+ *                      transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+                    int undo_apply_size, int *nrecords, bool one_page)

Could you please make it clear in comments and assertions what the
relation between from_urecptr and to_urecptr is and what they mean
(they must be in the same undo log, one must be <= the other, both
point to the *start* of a record, so it's not the same as the total
range of undo)?

I have enhanced the comments for the same

undo_apply_size is not a good parameter name, because the function is
useful for things other than applying records -- like the
undoinspect() extension (or some better version of that), for example.
Maybe max_result_size or something like that?

Changed

+{
...
+        /* Allocate memory for next undo record. */
+        uur = palloc0(sizeof(UnpackedUndoRecord));
...
+
+        size = UnpackedUndoRecordSize(uur);
+        total_size += size;

I see, so the unpacked records are still allocated one at a time. I
guess that's OK for now. From some earlier discussion I had been
expecting an arrangement where the actual records were laid out
contiguously with their subcomponents (things they point to in
palloc()'d memory) nearby.

In earlier version I was allocating one single memory and then packing
the records in that memory. But, their we need to take care of
alignnment of each unpacked undo record so that we can directly access
them so we have changed it this way.

+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+                     UndoLogCategory category)
+{
...
+    char        prevlen[2];
...
+    prev_rec_len = *(uint16 *) (prevlen);

I don't think that's OK, and might crash on a non-Intel system. How
about using a union of uint16 and char[2]?

changed

+    /* Copy undo record transaction header if it is present. */
+    if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+        memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);

I was wondering why you don't use D = S instead of mempcy(&D, &S,
size) wherever you can, until I noticed you use these SizeOfXXX macros
that don't include trailing padding from structs, and that's also how
you allocate objects. Hmm. So if I were to complain about you not
using plain old assignment whenever you can, I'd also have to complain
about that.

Fixed

I think that that technique of defining a SizeOfXXX macro that
excludes trailing bytes makes sense for writing into WAL or undo log
buffers using mempcy(). I'm not sure it makes sense for palloc() and
copying into typed variables like you're doing here and I think I'd
prefer the notational simplicity of using the (very humble) type
system facilities C gives us. (Some memory checker might not like it
you palloc(the shorter size) and then use = if the compiler chooses to
implement it as memcpy sizeof().)

+/*
+ * The below common information will be stored in the first undo record of the
+ * page.  Every subsequent undo record will not store this information, if
+ * required this information will be retrieved from the first undo
record of the
+ * page.
+ */
+typedef struct UndoCompressionInfo

Shouldn't this say "Every subsequent record will not store this
information *if it's the same as the relevant fields in the first
record*"?

+#define UREC_INFO_TRANSACTION                0x001
+#define UREC_INFO_RMID                        0x002
+#define UREC_INFO_RELOID                    0x004
+#define UREC_INFO_XID                        0x008

Should we call this UREC_INFO_FXID, since it refers to a FullTransactionId?

Done

+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into the alignment without padding
+ * bytes, and the undo record itself need not be aligned either, so care
+ * must be taken when reading the header.
+ */

I think you mean "All structures are packed into undo pages without
considering alignment and without trailing padding bytes"? This comes
from the definition of the SizeOfXXX macros IIUC. There might still
be padding between members of some of those structs, no? Like this
one, that has the second member at offset 2 on my system:

Done

+typedef struct UndoRecordHeader
+{
+    uint8        urec_type;        /* record type code */
+    uint16        urec_info;        /* flag bits */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader    \
+    (offsetof(UndoRecordHeader, urec_info) + sizeof(uint16))
+/*
+ * Information for a transaction to which this undo belongs.  This
+ * also stores the dbid and the progress of the undo apply during rollback.
+ */
+typedef struct UndoRecordTransaction
+{
+    /*
+     * Undo block number where we need to start reading the undo for applying
+     * the undo action.   InvalidBlockNumber means undo applying hasn't
+     * started for the transaction and MaxBlockNumber mean undo completely
+     * applied. And, any other block number means we have applied partial undo
+     * so next we can start from this block.
+     */
+    BlockNumber urec_progress;
+    Oid            urec_dbid;        /* database id */
+    UndoRecPtr    urec_next;        /* urec pointer of the next transaction */
+} UndoRecordTransaction;

I propose that we rename this to UndoRecordGroupHeader (or something
like that... maybe "Set", but we also use "set" as a verb in various
relevant function names):

I have changed this

1. We'll also use these for the new "shared" records we recently
invented that don't relate to a transaction. This is really about
defining the unit of discarding; we throw away the whole set of
records at once, which is why it's basically about proividing a space
for "urec_next".

2. Though it also holds rollback progress information, which is a
transaction-specific concept, there can be more than one of these sets
of records for a single transaction anyway. A single transaction can
write undo stuff in more than one undo log (different categories
perm/temp/unlogged/shared and also due to log switching when they are
full).

So really it's just a header for an arbitrary set of records, used to
track when and how to discard them.

If you agree with that idea, perhaps urec_next should become something
like urec_next_group, too. "next" is a bit vague, especially for
something as untyped as UndoRecPtr: someone might think it points to
the next record.

Changed

More soon.

the latest patch at
/messages/by-id/CAFiTN-uf4Bh0FHwec+JGbiLq+j00V92W162SLd_JVvwW-jwREg@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#234Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#188)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Jul 30, 2019 at 1:32 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

Amit, short note: The patches aren't attached in patch order. Obviously
a miniscule thing, but still nicer if that's not the case.

Dilip, this also contains the start of a review for the undo record
interface further down.

Subject: [PATCH 07/14] Provide interfaces to store and fetch undo records.

Add the capability to form undo records and store them in undo logs. We
also provide the capability to fetch the undo records. This layer will use
undo-log-storage to reserve the space for the undo records and buffer
management routines to write and read the undo records.

Undo records are stored in sequential order in the undo log.

Maybe "In each und log undo records are stored in sequential order."?

Done

+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,29 @@
+Undo record interface layer
+---------------------------
+This is the next layer which sits on top of the undo log storage, which will
+provide an interface for prepare, insert, or fetch the undo records.  This
+layer will use undo-log-storage to reserve the space for the undo records
+and buffer management routine to write and read the undo records.

The reference to "undo log storage" kinda seems like a reference into
nothingness...

Changed

+Writing an undo record
+----------------------
+To prepare an undo record, first, it will allocate required space using
+undo log storage module.  Next, it will pin and lock the required buffers and
+return an undo record pointer where it will insert the record.  Finally, it
+calls the Insert routine for final insertion of prepared record.  Additionally,
+there is a mechanism for multi-insert, wherein multiple records are prepared
+and inserted at a time.

I'm not sure whta this is telling me. Who is "it"?

To me the filename ("interface"), and the title of this section,
suggests this provides documentation on how to write code to insert undo
records. But I don't think this does.

I have improved it

+Fetching and undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+Optionally, the caller can provide a callback function with the information of
+the block and offset, which will help in faster retrieval of undo record,
+otherwise, it has to traverse the undo-chain.
+There is also an interface to bulk fetch the undo records.  Where the caller
+can provide a TO and FROM undo record pointer and the memory limit for storing
+the undo records.  This API will return all the undo record between FROM and TO
+undo record pointers if they can fit into provided memory limit otherwise, it
+return whatever can fit into the memory limit.  And, the caller can call it
+repeatedly until it fetches all the records.

There's a lot of terminology in this file that's not been introduced. I
think this needs to be greatly expanded and restructured to allow people
unfamiliar with the code to benefit.

I have improved it, but I think still I need to work on it to
introduce the terminology used.

+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *     entry points for inserting/fetching undo records
+ * NOTES:
+ * Undo record layout:
+ *
+ * Undo records are stored in sequential order in the undo log.  Each undo
+ * record consists of a variable length header, tuple data, and payload
+ * information.

Is that actually true? There's records without tuples, no?

Right, changed this

The first undo record of each transaction contains a
+ * transaction header that points to the next transaction's start
header.

Seems like this needs to reference different persistence levels,
otherwise it seems misleading, given there can be multiple first records
in multiple undo logs?

I have changed it.

+ * This allows us to discard the entire transaction's log at one-shot
rather

s/at/in/

Fixed

+ * than record-by-record. The callers are not aware of transaction header,

s/of/of the/

Fixed

+ * this is entirely maintained and used by undo record layer. See

s/this/it/

Fixed

+ * undorecord.h for detailed information about undo record header.

s/undo record/the undo record/

Fixed

I think at the very least there's explanations missing for:
- what is the locking protocol for multiple buffers
- what are the contexts for insertion
- what phases an undo insertion happens in
- updating previous records in general
- what "packing" actually is

+
+/* Prototypes for static functions. */

Don't think we commonly include that...

Changed, removed all unwanted prototypes

+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+                              UndoRecPtr urp, RelFileNode rnode,
+                              UndoPersistence persistence,
+                              Buffer *prevbuf);
+static int UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+                                                UndoRecPtr xact_urp, int size, int offset);
+static void UndoRecordUpdateTransInfo(UndoRecordInsertContext *context,
+                                               int idx);
+static void UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+                                                                     UndoRecPtr urecptr, UndoRecPtr xact_urp);
+static int UndoGetBufferSlot(UndoRecordInsertContext *context,
+                               RelFileNode rnode, BlockNumber blk,
+                               ReadBufferMode rbm);
+static uint16 UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+                                      UndoPersistence upersistence);
+
+/*
+ * Structure to hold the prepared undo information.
+ */
+struct PreparedUndoSpace
+{
+     UndoRecPtr      urp;                    /* undo record pointer */
+     UnpackedUndoRecord *urec;       /* undo record */
+     uint16          size;                   /* undo record size */
+     int                     undo_buffer_idx[MAX_BUFFER_PER_UNDO];   /* undo_buffer array
+                                                                                                              * index */
+};
+
+/*
+ * This holds undo buffers information required for PreparedUndoSpace during
+ * prepare undo time.  Basically, during prepare time which is called outside
+ * the critical section we will acquire all necessary undo buffers pin and lock.
+ * Later, during insert phase we will write actual records into thse buffers.
+ */
+struct PreparedUndoBuffer
+{
+     UndoLogNumber logno;            /* Undo log number */
+     BlockNumber blk;                        /* block number */
+     Buffer          buf;                    /* buffer allocated for the block */
+     bool            zero;                   /* new block full of zeroes */
+};

Most files define datatypes before function prototypes, because
functions may reference the datatypes.

done

+/*
+ * Prepare to update the transaction header
+ *
+ * It's a helper function for PrepareUpdateNext and
+ * PrepareUpdateUndoActionProgress

This doesn't really explain much. PrepareUpdateUndoActionProgress
doesnt' exist. I assume it's UndoRecordPrepareApplyProgress from 0012?

Enhanced the comments

+ * xact_urp  - undo record pointer to be updated.
+ * size - number of bytes to be updated.
+ * offset - offset in undo record where to start update.
+ */

These comments seem redundant with the parameter names.

fixed

+static int
+UndoRecordPrepareTransInfo(UndoRecordInsertContext *context,
+                                                UndoRecPtr xact_urp, int size, int offset)
+{
+     BlockNumber cur_blk;
+     RelFileNode rnode;
+     int                     starting_byte;
+     int                     bufidx;
+     int                     index = 0;
+     int                     remaining_bytes;
+     XactUndoRecordInfo *xact_info;
+
+     xact_info = &context->xact_urec_info[context->nxact_urec_info];
+
+     UndoRecPtrAssignRelFileNode(rnode, xact_urp);
+     cur_blk = UndoRecPtrGetBlockNum(xact_urp);
+     starting_byte = UndoRecPtrGetPageOffset(xact_urp);
+
+     /* Remaining bytes on the current block. */
+     remaining_bytes = BLCKSZ - starting_byte;
+
+     /*
+      * Is there some byte of the urec_next on the current block, if not then
+      * start from the next block.
+      */

This comment needs rephrasing.

Done

+     /* Loop until we have fetched all the buffers in which we need to write. */
+     while (size > 0)
+     {
+             bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+             xact_info->idx_undo_buffers[index++] = bufidx;
+             size -= (BLCKSZ - starting_byte);
+             starting_byte = UndoLogBlockHeaderSize;
+             cur_blk++;
+     }

So, this locks a very large number of undo buffers at the same time, do
I see that correctly? What guarantees that there are no deadlocks due
to multiple buffers locked at the same time (I guess the order inside
the log)? What guarantees that this is a small enough number that we can
even lock all of them at the same time?

I think we are locking them in the block order and that should avoid
the deadlock. I have explained in the comments.

Why do we need to lock all of them at the same time? That's not clear to
me.

Because this is called outside the critical section so we keep all the
buffers locked what we want to update inside the critical section for
single wal record.

Also, why do we need code to lock an unbounded number here? It seems
hard to imagine we'd ever want to update more than something around 8
bytes? Shouldn't that at the most require two buffers?

Right, it should lock at the most 2 buffers. Now, I have added assert
for that. Basically, it can either lock 1 buffer or 2 buffers so I am
not sure what is the best condition to break the loop. I guess our
target is to write 8 bytes so breaking condition must be the number of
bytes. I agree that we should never go beyond two buffers but for
that, we can add an assert. Do you have another opinion on this?

+/*
+ * Prepare to update the previous transaction's next undo pointer.
+ *
+ * We want to update the next transaction pointer in the previous transaction's
+ * header (first undo record of the transaction).  In prepare phase we will
+ * unpack that record and lock the necessary buffers which we are going to
+ * overwrite and store the unpacked undo record in the context.  Later,
+ * UndoRecordUpdateTransInfo will overwrite the undo record.
+ *
+ * xact_urp - undo record pointer of the previous transaction's header
+ * urecptr - current transaction's undo record pointer which need to be set in
+ *                    the previous transaction's header.
+ */
+static void
+UndoRecordPrepareUpdateNext(UndoRecordInsertContext *context,
+                                                     UndoRecPtr urecptr, UndoRecPtr xact_urp)

That name imo is confusing - it's not clear that it's not actually about
the next record or such.

I agree. I think I will think about what to name it. I am planning
to unify 2 function UndoRecordPrepareUpdateNext and
PrepareUpdateUndoActionProgress then we can directly name it
PrepareUndoRecordUpdate. But for that, I need to get the progress
update code in my patch.

+{
+     UndoLogSlot *slot;
+     int                     index = 0;
+     int                     offset;
+
+     /*
+      * The absence of previous transaction's undo indicate that this backend

*indicates

Done

+     /*
+      * Acquire the discard lock before reading the undo record so that discard
+      * worker doesn't remove the record while we are in process of reading it.
+      */

*the discard worker

Done

+     LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+     /* Check if it is already discarded. */
+     if (UndoLogIsDiscarded(xact_urp))
+     {
+             /* Release lock and return. */
+             LWLockRelease(&slot->discard_update_lock);
+             return;
+     }

Ho, hum. I don't quite remember what we decided in the discussion about
not having to use the discard lock for this purpose.

I think we haven't concluded an alternative solution for this and
planned to keep it as is for now. Please correct me if anyone else
has a different opinion.

+     /* Compute the offset of the uur_next in the undo record. */
+     offset = SizeOfUndoRecordHeader +
+                                     offsetof(UndoRecordTransaction, urec_next);
+
+     index = UndoRecordPrepareTransInfo(context, xact_urp,
+                                                                        sizeof(UndoRecPtr), offset);
+     /*
+      * Set the next pointer in xact_urec_info, this will be overwritten in
+      * actual undo record during update phase.
+      */
+     context->xact_urec_info[index].next = urecptr;

What does "this will be overwritten mean"? It sounds like "context->xact_urec_info[index].next"
would be overwritten, but that can't be true.

+     /* We can now release the discard lock as we have read the undo record. */
+     LWLockRelease(&slot->discard_update_lock);
+}

Hm. Because you expect it to be blocked behind the content lwlocks for
the buffers?

Yes, I added comments.

+/*
+ * Overwrite the first undo record of the previous transaction to update its
+ * next pointer.
+ *
+ * This will insert the already prepared record by UndoRecordPrepareTransInfo.

It doesn't actually appear to insert any records. At least not a record
in the way the rest of the file uses that term?

I think this was old comments. Fixed it.

+ * This must be called under the critical section.

s/under the/in a/

I think I missed in my last patch. Will fix in next version.

Think that should be asserted.

Added the assert.

+     /*
+      * Start writing directly from the write offset calculated during prepare
+      * phase.  And, loop until we write required bytes.
+      */

Why do we do offset calculations multiple times? Seems like all the
offsets, and the split, should be computed in exactly one place.

Sorry, I did not understand this, we are calculating the offset in
the prepare phase. Do you want to point out something else?

+/*
+ * Find the block number in undo buffer array
+ *
+ * If it is present then just return its index otherwise search the buffer and
+ * insert an entry and lock the buffer in exclusive mode.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data
+ * that begins exactly at the beginning of a page, then there cannot be any
+ * useful data after that point.  In that case RBM_ZERO can be passed in as
+ * rbm so that we can skip a useless read of a disk block.  In all other
+ * cases, RBM_NORMAL should be passed in, to read the page in if it doesn't
+ * happen to be already in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+                               RelFileNode rnode,
+                               BlockNumber blk,
+                               ReadBufferMode rbm)
+{
+     int                     i;
+     Buffer          buffer;
+     XLogRedoAction action = BLK_NEEDS_REDO;
+     PreparedUndoBuffer *prepared_buffer;
+     UndoPersistence persistence = context->alloc_context.persistence;
+
+     /* Don't do anything, if we already have a buffer pinned for the block. */

As the code stands, it's locked, not just pinned.

Changed

+     for (i = 0; i < context->nprepared_undo_buffer; i++)
+     {

How large do we expect this to get at most?

In BeginUndoRecordInsert we are computing it

+ /* Compute number of buffers. */
+ nbuffers = (nprepared + MAX_UNDO_UPDATE_INFO) * MAX_BUFFER_PER_UNDO;
+     /*
+      * We did not find the block so allocate the buffer and insert into the
+      * undo buffer array.
+      */
+     if (InRecovery)
+             action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+                                                                                     SMGR_UNDO,
+                                                                                     rnode,
+                                                                                     UndoLogForkNum,
+                                                                                     blk,
+                                                                                     rbm,
+                                                                                     false,
+                                                                                     &buffer);

Why is not locking the buffer correct here? Can't there be concurrent
reads during hot standby?

because XLogReadBufferForRedoBlock is locking it internally. I have
added this in coment in new patch.

+/*
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.

How can a function be called "before all the undo records"?

"before all the undo records which are getting inserted under single
WAL" because it will set the prepare limit and allocate appropriate
memory for that.
So I am not sure what is your point here? why can't we call it before
all the undo record we are inserting?

+ * nprepared - This defines the max number of undo records that can be
+ * prepared before inserting them.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+                                       UndoPersistence persistence,
+                                       int nprepared,
+                                       XLogReaderState *xlog_record)

There definitely needs to be explanation about xlog_record. But also
about memory management etc. Looks like one e.g. can't call this from a
short lived memory context.

I have added coments for this.

+/*
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.

Again, how is deadlocking / max number of buffers handled, and why do
they all need to be locked at the same time?

+     /*
+      * We don't yet know if this record needs a transaction header (ie is the
+      * first undo record for a given transaction in a given undo log), because
+      * you can only find out by allocating.  We'll resolve this circularity by
+      * allocating enough space for a transaction header.  We'll only advance
+      * by as many bytes as we turn out to need.
+      */

Why can we only find this out by allocating? This seems like an API
deficiency of the storage layer to me. The information is in the und log
slot's metadata, no?

I agree with this. I think if Thomas agree we can provide an API in
undo log which can provide us this information before we do the actual
allocation.

+     urec->uur_next = InvalidUndoRecPtr;
+     UndoRecordSetInfo(urec);
+     urec->uur_info |= UREC_INFO_TRANSACTION;
+     urec->uur_info |= UREC_INFO_LOGSWITCH;
+     size = UndoRecordExpectedSize(urec);
+
+     /* Allocate space for the record. */
+     if (InRecovery)
+     {
+             /*
+              * We'll figure out where the space needs to be allocated by
+              * inspecting the xlog_record.
+              */
+             Assert(context->alloc_context.persistence == UNDO_PERMANENT);
+             urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+                                                                                     XidFromFullTransactionId(txid),
+                                                                                     size,
+                                                                                     &need_xact_header,
+                                                                                     &last_xact_start,
+                                                                                     &prevlog_xact_start,
+                                                                                     &prevlogurp);
+     }
+     else
+     {
+             /* Allocate space for writing the undo record. */

That's basically the same comment as before the if.

Removed

+             urecptr = UndoLogAllocate(&context->alloc_context,
+                                                               size,
+                                                               &need_xact_header, &last_xact_start,
+                                                               &prevlog_xact_start, &prevlog_insert_urp);
+
+             /*
+              * If prevlog_xact_start is a valid undo record pointer that means
+              * this transaction's undo records are split across undo logs.
+              */
+             if (UndoRecPtrIsValid(prevlog_xact_start))
+             {
+                     uint16          prevlen;
+
+                     /*
+                      * If undo log is switch during transaction then we must get a

"is switch" is right.

This code is removed now.

+/*
+ * Insert a previously-prepared undo records.

s/a//

Fixed

More tomorrow.

refer the latest patch at
/messages/by-id/CAFiTN-uf4Bh0FHwec+JGbiLq+j00V92W162SLd_JVvwW-jwREg@mail.gmail.com

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#235Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#222)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Aug 9, 2019 at 1:57 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Aug 8, 2019 at 9:31 AM Andres Freund <andres@anarazel.de> wrote:

I know that Robert is working on a patch that revises the undo request
layer somewhat, it's possible that this is best discussed afterwards.

Here's what I have at the moment. This is not by any means a complete
replacement for Amit's undo worker machinery, but it is a significant
redesign (and I believe a significant improvement to) the queue
management stuff from Amit's patch. I wrote this pretty quickly, so
while it passes simple testing, it probably has a number of bugs, and
to actually use it, it would need to be integrated with xact.c; right
now it's just a standalone module that doesn't do anything except let
itself be tested.

Some of the ways it is different from Amit's patches include:

* It uses RBTree rather than binaryheap, so when we look ahead, we
look ahead in the right order.

* There's no limit to the lookahead distance; when looking ahead, it
will search the entirety of all 3 RBTrees for an entry from the right
database.

* It doesn't have a separate hash table keyed by XID. I didn't find
that necessary.

* It's better-isolated, as you can see from the fact that I've
included a test module that tests this code without actually ever
putting an UndoRequestManager in shared memory. I would've liked to
expand this test module, but I don't have time to do that today and
felt it better to get this much sent out.

* It has a lot of comments explaining the design and how it's intended
to integrate with the rest of the system.

Broadly, my vision for how this would get used is:

- Create an UndoRecordManager in shared memory.
- Before a transaction first attaches to a permanent or unlogged undo
log, xact.c would call RegisterUndoRequest(); thereafter, xact.c would
store a pointer to the UndoRecord for the lifetime of the toplevel
transaction.

So, for top-level transactions rollback, we can directly refer from
UndoRequest *, the start and end locations. But, what should we do
for sub-transactions (rollback to savepoint)? One related point is
that we also need information about last_log_start_undo_location to
update the undo apply progress (The basic idea is if the transactions
undo is spanned across multiple logs, we update the progress in each
of the logs.). We can remember that in the transaction state or
undorequest *. Any suggestion?

- Immediately after attaching to a permanent or unlogged undo log,
xact.c would call UndoRequestSetLocation.
- xact.c would track the number of bytes of permanent and unlogged
undo records the transaction generates. If the transaction goes onto
abort, it reports these by calling FinalizeUndoRequest.
- If the transaction commits, it doesn't need that information, but
does need to call UnregisterUndoRequest() as a post-commit step in
CommitTransaction().

IIUC, for each transaction, we have to take a lock first time it
attaches to a log and then the same lock at commit time. It seems the
work under lock is less, but still, can't this cause a contention? It
seems to me this is similar to what we saw in ProcArrayLock where work
under lock was few instructions, but acquiring and releasing the lock
by each backend at commit time was causing a bottleneck.

It might be due to some reason this won't matter in a similar way in
which case we can find that after integrating it with other patches
from undo processing machinery and rebasing zheap branch over it?

How will computation of oldestXidHavingUnappliedUndo will work?

We can probably check the fxid queue and error queue to get that
value. However, I am not sure if that is sufficient because incase we
perform the request in the foreground, it won't be present in queues.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#236Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#221)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 8, 2019 at 7:01 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-07 14:50:17 +0530, Amit Kapila wrote:

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

typedef struct TwoPhaseFileHeader
{
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
uint16          gidlen;                 /* length of the GID - GID follows the header */
XLogRecPtr      origin_lsn;             /* lsn of this record at origin node */
TimestampTz origin_timestamp;   /* time of prepare at origin node */
+
+     /*
+      * We need the locations of the start and end undo record pointers when
+      * rollbacks are to be performed for prepared transactions using undo-based
+      * relations.  We need to store this information in the file as the user
+      * might rollback the prepared transaction after recovery and for that we
+      * need its start and end undo locations.
+      */
+     UndoRecPtr      start_urec_ptr[UndoLogCategories];
+     UndoRecPtr      end_urec_ptr[UndoLogCategories];
} TwoPhaseFileHeader;

Why do we not need that knowledge for undo processing of a non-prepared
transaction?

The non-prepared transaction also needs to be aware of that. It is
stored in TransactionStateData. I am not sure if I understand your
question here.

My concern is that I think it's fairly ugly to store data like this in
the 2pc state file. And it's not an insubstantial amount of additional
data either, compared to the current size, even when no undo is in
use. There's a difference between an unused feature increasing backend
local memory and increasing the size of WAL logged data. Obviously it's
not by a huge amount, but still. It also just feels wrong to me.

We don't need the UndoRecPtr's when recovering from a crash/restart to
process undo. Now we obviously don't want to unnecessarily search for
data that is expensive to gather, which is a good reason for keeping
track of this data. But I do wonder if this is the right approach.

I know that Robert is working on a patch that revises the undo request
layer somewhat, it's possible that this is best discussed afterwards.

Okay, we have started working on integrating with Robert's patch. I
think not only this but many of the other things will also change.
So, I will respond to other comments after integrating with Robert's
patch.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#237Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#206)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 5, 2019 at 11:59 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

(as I was out of context due to dealing with bugs, I've switched to
lookign at the current zheap/undoprocessing branch.

On 2019-07-30 01:02:20 -0700, Andres Freund wrote:

+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

As far as I can tell there's simply no deadlock avoidance scheme in use
here *at all*? I must be missing something.

We are always locking buffer in block order so I am not sure how it
can deadlock? Am I missing something?

+             /* Main loop for writing the undo record. */
+             do
+             {

I'd prefer this to not be a do{} while(true) loop - as written I need to
read to the end to see what the condition is. I don't think we have any
loops like that in the code.

Right, changed

+                     /*
+                      * During recovery, there might be some blocks which are already
+                      * deleted due to some discard command so we can just skip
+                      * inserting into those blocks.
+                      */
+                     if (!BufferIsValid(buffer))
+                     {
+                             Assert(InRecovery);
+
+                             /*
+                              * Skip actual writing just update the context so that we have
+                              * write offset for inserting into next blocks.
+                              */
+                             SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+                             if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+                                     break;
+                     }

How exactly can this happen?

Suppose you insert one record for the transaction which split in
block1 and 2. Now, before this block is actually going to the disk
the transaction committed and become all visible the undo logs are
discarded. It's possible that block 1 is completely discarded but
block 2 is not because it might have undo for the next transaction.
Now, during recovery (FPW is off) if block 1 is missing but block 2 is
their so we need to skip inserting undo for block 1 as it does not
exist.

+                     else
+                     {
+                             page = BufferGetPage(buffer);
+
+                             /*
+                              * Initialize the page whenever we try to write the first
+                              * record in page.  We start writing immediately after the
+                              * block header.
+                              */
+                             if (starting_byte == UndoLogBlockHeaderSize)
+                                     UndoPageInit(page, BLCKSZ, prepared_undo->urec->uur_info,
+                                                              ucontext.already_processed,
+                                                              prepared_undo->urec->uur_tuple.len,
+                                                              prepared_undo->urec->uur_payload.len);
+
+                             /*
+                              * Try to insert the record into the current page. If it
+                              * doesn't succeed then recall the routine with the next page.
+                              */
+                             InsertUndoData(&ucontext, page, starting_byte);
+                             if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+                             {
+                                     MarkBufferDirty(buffer);
+                                     break;

At this point we're five indentation levels deep. I'd extract at least
either the the per prepared undo code or the code performing the writing
across block boundaries into a separate function. Perhaps both.

I have moved it to the separate function.

+/*
+ * Helper function for UndoGetOneRecord
+ *
+ * If any of  rmid/reloid/xid/cid is not available in the undo record, then
+ * it will get the information from the first complete undo record in the
+ * page.
+ */
+static void
+GetCommonUndoRecInfo(UndoPackContext *ucontext, UndoRecPtr urp,
+                                      RelFileNode rnode, UndoLogCategory category, Buffer buffer)
+{
+     /*
+      * If any of the common header field is not available in the current undo
+      * record then we must read it from the first complete record of the page.
+      */

How is it guaranteed that the first record on the page is actually from
the current transaction? Can't there be a situation where that's from
another transaction?

If the first record is not from the same transaction then the record
must have all those fields in it so it should never try to access the
first record. I have updated the comments for the same.

+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin and lock on that buffer in a hope that while traversing the undo chain
+ * the caller might want to read the previous undo record from the same block.
+ */

Wait, so at exit *curbuf is pinned but not locked, if passed in, but is
pinned *and* locked when not? That'd not be a sane API. I don't think
the code works like that atm though.

Comments were wrong, I have fixed.

+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+                              UndoLogCategory category, Buffer *curbuf)
+{
+     Page            page;
+     int                     starting_byte = UndoRecPtrGetPageOffset(urp);
+     BlockNumber cur_blk;
+     UndoPackContext ucontext = {{0}};
+     Buffer          buffer = *curbuf;
+
+     cur_blk = UndoRecPtrGetBlockNum(urp);
+
+     /* Initiate unpacking one undo record. */
+     BeginUnpackUndo(&ucontext);
+
+     while (true)
+     {
+             /* If we already have a buffer then no need to allocate a new one. */
+             if (!BufferIsValid(buffer))
+             {
+                     buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, cur_blk,
+                                                                                        RBM_NORMAL, NULL,
+                                                                                        RelPersistenceForUndoLogCategory(category));
+
+                     /*
+                      * Remember the first buffer where this undo started as next undo
+                      * record what we fetch might fall on the same buffer.
+                      */
+                     if (!BufferIsValid(*curbuf))
+                             *curbuf = buffer;
+             }
+
+             /* Acquire shared lock on the buffer before reading undo from it. */
+             LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+             page = BufferGetPage(buffer);
+
+             UnpackUndoData(&ucontext, page, starting_byte);
+
+             /*
+              * We are done if we have reached to the done stage otherwise move to
+              * next block and continue reading from there.
+              */
+             if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+             {
+                     if (buffer != *curbuf)
+                             UnlockReleaseBuffer(buffer);
+
+                     /*
+                      * Get any of the missing fields from the first record of the
+                      * page.
+                      */
+                     GetCommonUndoRecInfo(&ucontext, urp, rnode, category, *curbuf);
+                     break;
+             }
+
+             /*
+              * The record spans more than a page so we would have copied it (see
+              * UnpackUndoRecord).  In such cases, we can release the buffer.
+              */

Where would it have been copied? Presumably in UnpackUndoData()? Imo the
comment should say so.

I'm a bit confused by the use of "would" in that comment. Either we
have, or not?

This comment is obsolete so removed.

+             if (buffer != *curbuf)
+                     UnlockReleaseBuffer(buffer);

Wait, so we *keep* the buffer locked if it the same as *curbuf? That
can't be right.

At the end we are releasing the lock on the *curbuf. But now I have
changed it so that it is more readable.

+ * Fetch the undo record for given undo record pointer.
+ *
+ * This will internally allocate the memory for the unpacked undo record which
+ * intern will

"intern" should probably be internally? But I'm not sure what the two
"internally"s really add here.

+/*
+ * Release the memory of the undo record allocated by UndoFetchRecord and
+ * UndoBulkFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+     /* Release the memory of payload data if we allocated it. */
+     if (urec->uur_payload.data)
+             pfree(urec->uur_payload.data);
+
+     /* Release memory of tuple data if we allocated it. */
+     if (urec->uur_tuple.data)
+             pfree(urec->uur_tuple.data);
+
+     /* Release memory of the transaction header if we allocated it. */
+     if (urec->uur_txn)
+             pfree(urec->uur_txn);
+
+     /* Release memory of the logswitch header if we allocated it. */
+     if (urec->uur_logswitch)
+             pfree(urec->uur_logswitch);
+
+     /* Release the memory of the undo record. */
+     pfree(urec);
+}

Those comments before each pfree are not useful.

Removed

Also, isn't this both fairly slow and fairly failure prone? The next
record is going to need all that memory again, no? It seems to me that
there should be one record that's allocated once, and then reused over
multiple fetches, increasing the size if necesssary.

I'm very doubtful that all this freeing of individual allocations in the
undo code makes sense. Shouldn't this just be done in short lived memory
contexts, that then get reset as a whole? That's both far less failure
prone, and faster.

+ * one_page                  - Caller is applying undo only for one block not for
+ *                                     complete transaction.  If this is set true then instead
+ *                                     of following transaction undo chain using prevlen we will
+ *                                     follow the block prev chain of the block so that we can
+ *                                     avoid reading many unnecessary undo records of the
+ *                                     transaction.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+                                     int undo_apply_size, int *nrecords, bool one_page)

There's no caller for one_page mode in the series - I assume that's for
later, during page-wise undo? It seems to behave in quite noticably
different ways, is that really OK? Makes the code quite hard to
understand.
Also, it seems quite poorly named to me. It sounds like it's about
fetching a single undo page (which makes no sense, obviously). But what
it does is to switch to an entirely different way of traversing the undo
chains.

one_page was zheap specific so I have removed it. I think in zheap
specific function we can implement it by UndoFetchRecord in a loop.

+     /*
+      * In one_page mode we are fetching undo only for one page instead of
+      * fetching all the undo of the transaction.  Basically, we are fetching
+      * interleaved undo records.  So it does not make sense to do any prefetch
+      * in that case.

What does "interleaved" mean here?

I meant that for one page we are following blockprev chain instead of
complete transaction undo chain so there is no guarantee that the undo
records are together. Basically, the undo records for the different
blocks can be interleaved so I am not sure should we prefetch or not.
I assume that there will often be

other UNDO records interspersed? But that's not guaranteed at all,
right? In fact, for a lot of workloads it seems likely that there will
be many consecutive undo records for a single page? In fact, won't that
be the majority of cases?

Ok, that point makes sense to me but I thought if we always assume
this we will do unwanted prefetch where this is not the case and we
will put unnecessary load on the I/O. Currently, I have moved that
code out of the undo layer so we can take a call while designing zheap
specific function.

Thus it's not obvious to me that there's not often going to be
consecutive pages for this case too. I'd even say that minimizing IO
delay is *MORE* important during page-wise undo, as that happens in the
context of client accesses, and it's not incurring cost on the party
that performed DML, but on some random third party.

I'm doubtful this is a sane interface. There's a lot of duplication
between one_page and not one_page. It presupposes specific ways of
constructing chains that are likely to depend on the AM. to_urecptr is
only used in certain situations. E.g. I strongly suspect that for
zheap's visibility determinations we'd want to concurrently follow all
the necessary chains to determine visibility for all all tuples on the
page, far enough to find the visible tuple - for seqscan's / bitmap heap
scans / everything using page mode scans, that'll be way more efficient
than doing this one-by-one and possibly even repeatedly. But what is
exactly the right thing to do is going to be highly AM specific.

I vaguely suspect what you'd want is an interface where the "bulk fetch"
context basically has a FIFO queue of undo records to fetch, and a
function to actually perform fetching. Whenever a record has been
retrieved, a callback determines whether additional records are needed.
In the case of fetching all the undo for a transaction, you'd just queue
- probably in a more efficient representation - all the necessary
undo. In case of page-wise undo, you'd queue the first record of the
chain you'd want to undo, with a callback for queuing the next
record. For visibility determinations in zheap, you'd queue all the
different necessary chains, with a callback that queues the next
necessary record if still needed for visibility determination.

And then I suspect you'd have a separate callback whenever records have
been fetched, with all the 'unconsumed' records. That then can,
e.g. based on memory consumption, decide to process them or not. For
visibility information you'd probably just want to condense the records
to the minimum necessary (i.e. visibility information for the relevant
tuples, and the visibile tuple when encountered) as soon as available.

I haven't think on this part yet. I will analyze part.

Obviously that's pretty handwavy.

Also, if we are fetching undo records from more than one
+      * log, we don't know the boundaries for prefetching.  Hence, we can't use
+      * prefetching in this case.
+      */

Hm. Why don't we know the boundaries (or cheaply infer them)?

I have added comments for that. Basically, when we get the undo
records from the different log (from and to pointers are in the
different log) we don't know in latest undo log till what point the
undo are from this transaction. We may consider prefetching to the
start of the current log but there is no guarantee that all the blocks
of the current logs are valid and not yet discarded. Ideally, the
better fix would be that the caller always pass the from and to
pointer from the same undo log.

+             /*
+              * If prefetch_pages are half of the prefetch_target then it's time to
+              * prefetch again.
+              */
+             if (prefetch_pages < prefetch_target / 2)
+                     PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+                                                       from_blkno, category);

Hm. Why aren't we prefetching again as soon as possible? Given the
current code there's not really benefit in fetching many adjacent pages
at once. And this way it seems we're somewhat likely to cause fairly
bursty IO?

Hmm right, we can always prefetch as soon as we are behind the
prefetch target. Done that way.

+             /*
+              * In one_page mode it's possible that the undo of the transaction
+              * might have been applied by worker and undo got discarded. Prevent
+              * discard worker from discarding undo data while we are reading it.
+              * See detail comment in UndoFetchRecord.  In normal mode we are
+              * holding transaction undo action lock so it can not be discarded.
+              */

I don't really see a comment explaining this in UndoFetchRecord. Are
you referring to InHotStandby? Because there's no comment about one_page
mode as far as I can tell? The comment is clearly referring to that,
rather than InHotStandby?

I have removed one_page code.

+             if (one_page)
+             {
+                     /* Refer comments in UndoFetchRecord. */

Missing "to".

+                     if (InHotStandby)
+                     {
+                             if (UndoRecPtrIsDiscarded(urecptr))
+                                     break;
+                     }
+                     else
+                     {
+                             LWLockAcquire(&slot->discard_lock, LW_SHARED);
+                             if (slot->logno != logno || urecptr < slot->oldest_data)
+                             {
+                                     /*
+                                      * The undo log slot has been recycled because it was
+                                      * entirely discarded, or the data has been discarded
+                                      * already.
+                                      */
+                                     LWLockRelease(&slot->discard_lock);
+                                     break;
+                             }
+                     }

I find this deeply unsatisfying. It's repeated in a bunch of
places. There's completely different behaviour between the hot-standby
and !hot-standby case. There's UndoRecPtrIsDiscarded for the HS case,
but we do a different test for !HS. There's no explanation as to why
this is even reachable.

I have added comments in UndoFetchRecord.

+                     /* Read the undo record. */
+                     UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+                     /* Release the discard lock after fetching the record. */
+                     if (!InHotStandby)
+                             LWLockRelease(&slot->discard_lock);
+             }
+             else
+                     UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);

And then we do none of this in !one_page mode.

UndoBulkFetchRecord is always called from the aborted transaction so
its undo can never get discarded concurrently so ideally, we don't
need to check for discard. But, during one_page mode, we follow the
when it comes from zheap for the one page it is possible that the undo
for the transaction are applied from the worker for the complete
transaction and its undo logs are discarded. But, I think this is
highly am specific so I have removed one_page mode from here.

+             /*
+              * As soon as the transaction id is changed we can stop fetching the
+              * undo record.  Ideally, to_urecptr should control this but while
+              * reading undo only for a page we don't know what is the end undo
+              * record pointer for the transaction.
+              */
+             if (one_page)
+             {
+                     if (!FullTransactionIdIsValid(fxid))
+                             fxid = uur->uur_fxid;
+                     else if (!FullTransactionIdEquals(fxid, uur->uur_fxid))
+                             break;
+             }
+
+             /* Remember the previous undo record pointer. */
+             prev_urec_ptr = urecptr;
+
+             /*
+              * Calculate the previous undo record pointer of the transaction.  If
+              * we are reading undo only for a page then follow the blkprev chain
+              * of the page.  Otherwise, calculate the previous undo record pointer
+              * using transaction's current undo record pointer and the prevlen. If
+              * undo record has a valid uur_prevurp, this is the case of log switch
+              * during the transaction so we can directly use uur_prevurp as our
+              * previous undo record pointer of the transaction.
+              */
+             if (one_page)
+                     urecptr = uur->uur_prevundo;
+             else if (uur->uur_logswitch)
+                     urecptr = uur->uur_logswitch->urec_prevurp;
+             else if (prev_urec_ptr == to_urecptr ||
+                              uur->uur_info & UREC_INFO_TRANSACTION)
+                     urecptr = InvalidUndoRecPtr;
+             else
+                     urecptr = UndoGetPrevUndoRecptr(prev_urec_ptr, buffer, category);
+

FWIW, this is one of those concerns I was referring to above. What
exactly needs to happen seems highly AM specific.

1. one_page check is gone
2. uur->uur_info & UREC_INFO_TRANSACTION is also related to one_page
so removed this too.
3. else if (uur->uur_logswitch) -> I think this is also related to the
incapability of the caller that it can not identify the log switch but
expect the bulk fetch to detect it and break fetching so that we can
update the
progress in the transaction header of the current log.

I think we can solve these issue by callback as well as you suggested above.

+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */

This should add some note as to when it's expected to be necessary. I
was kind of concerned that this can be necessary, but it's only needed
during log switches, which disarms that concern.

I think this is a normal case because the undo_len store the actual
length of the record. But, if the undo record split across 2 pages
and if we are at the end of the undo record (start of the next record)
then for computing the absolute start offset of the previous undo
record we need the exact distance between these two records and that
will be
current_offset - (the actual length of the previous record + Undo
record header if the previous log is split across 2 pages)

+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+                                      UndoLogCategory category)
+{
+     UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+     BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+     Buffer          buffer = input_buffer;
+     Page            page = NULL;
+     char       *pagedata = NULL;
+     char            prevlen[2];
+     RelFileNode rnode;
+     int                     byte_to_read = sizeof(uint16);

Shouldn't it be byte_to_read? And the sizeof a type that's tied with the
actual undo format? Imagine we'd ever want to change the length format
for undo records - this would be hard to find.

I did not get this comments. Do you mean that we should not rely on
undo format i.e. we should not assume that undo length is stored at
the end of the undo record?

+     char            persistence;
+     uint16          prev_rec_len = 0;
+
+     /* Get relfilenode. */
+     UndoRecPtrAssignRelFileNode(rnode, urp);
+     persistence = RelPersistenceForUndoLogCategory(category);
+
+     if (BufferIsValid(buffer))
+     {
+             page = BufferGetPage(buffer);
+             pagedata = (char *) page;
+     }
+
+     /*
+      * Length if the previous undo record is store at the end of that record
+      * so just fetch last 2 bytes.
+      */
+     while (byte_to_read > 0)
+     {

Why does this need a loop around the number of bytes? Can there ever be
a case where this is split across a record? If so, isn't that a bad idea
anyway?

Yes, as of now, undo record can be splitted at any point even the undo
length can be split acorss 2 pages. I think we can reduce complexity
by making sure undo length doesn't get split acorss pages. But for
handling that while allocating the undo we need to detect this whether
the undo length can get splitted by checking the space in the current
page and the undo record length and based on that we need to allocate
1 extra byte in the undo log. Seems that will add an extra
complexity.

+             /* Read buffer if the current buffer is not valid. */
+             if (!BufferIsValid(buffer))
+             {
+                     buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum,
+                                                                                        cur_blk, RBM_NORMAL, NULL,
+                                                                                        persistence);
+
+                     LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+                     page = BufferGetPage(buffer);
+                     pagedata = (char *) page;
+             }
+
+             page_offset -= 1;
+
+             /*
+              * Read current prevlen byte from current block if page_offset hasn't
+              * reach to undo block header.  Otherwise, go to the previous block
+              * and continue reading from there.
+              */
+             if (page_offset >= UndoLogBlockHeaderSize)
+             {
+                     prevlen[byte_to_read - 1] = pagedata[page_offset];
+                     byte_to_read -= 1;
+             }
+             else
+             {
+                     /*
+                      * Release the current buffer if it is not provide by the caller.
+                      */
+                     if (input_buffer != buffer)
+                             UnlockReleaseBuffer(buffer);
+
+                     /*
+                      * Could not read complete prevlen from the current block so go to
+                      * the previous block and start reading from end of the block.
+                      */
+                     cur_blk -= 1;
+                     page_offset = BLCKSZ;
+
+                     /*
+                      * Reset buffer so that we can read it again for the previous
+                      * block.
+                      */
+                     buffer = InvalidBuffer;
+             }
+     }

I can't help but think that this shouldn't be yet another copy of logic
for how to read undo pages.

I haven't yet thought but I will try to unify this with ReadUndoBytes.
Actually, I didn't do that already because ReadUndoByte needs a start
pointer where we need to read the given number of bytes but here we
have an end pointer. May be by this logic we can compute the start
pointer but that will look equally complex. I will work on this and
try to figure out something.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#238Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#209)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 6, 2019 at 1:26 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-05 11:29:34 -0700, Andres Freund wrote:

Need to do something else for a bit. More later.

+ /*
+  * Compute the header size of the undo record.
+  */
+Size
+UndoRecordHeaderSize(uint16 uur_info)
+{
+     Size            size;
+
+     /* Add fixed header size. */
+     size = SizeOfUndoRecordHeader;
+
+     /* Add size of transaction header if it presets. */
+     if ((uur_info & UREC_INFO_TRANSACTION) != 0)
+             size += SizeOfUndoRecordTransaction;
+
+     /* Add size of rmid if it presets. */
+     if ((uur_info & UREC_INFO_RMID) != 0)
+             size += sizeof(RmgrId);
+
+     /* Add size of reloid if it presets. */
+     if ((uur_info & UREC_INFO_RELOID) != 0)
+             size += sizeof(Oid);
+
+     /* Add size of fxid if it presets. */
+     if ((uur_info & UREC_INFO_XID) != 0)
+             size += sizeof(FullTransactionId);
+
+     /* Add size of cid if it presets. */
+     if ((uur_info & UREC_INFO_CID) != 0)
+             size += sizeof(CommandId);
+
+     /* Add size of forknum if it presets. */
+     if ((uur_info & UREC_INFO_FORK) != 0)
+             size += sizeof(ForkNumber);
+
+     /* Add size of prevundo if it presets. */
+     if ((uur_info & UREC_INFO_PREVUNDO) != 0)
+             size += sizeof(UndoRecPtr);
+
+     /* Add size of the block header if it presets. */
+     if ((uur_info & UREC_INFO_BLOCK) != 0)
+             size += SizeOfUndoRecordBlock;
+
+     /* Add size of the log switch header if it presets. */
+     if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
+             size += SizeOfUndoRecordLogSwitch;
+
+     /* Add size of the payload header if it presets. */
+     if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+             size += SizeOfUndoRecordPayload;

There's numerous blocks with one if for each type, and the body copied
basically the same for each alternative. That doesn't seem like a
reasonable approach to me. Means that many places need to be adjusted
when we invariably add another type, and seems likely to lead to bugs
over time.

I think I have expressed my thought on this in another email
[/messages/by-id/CAFiTN-vDrXuL6tHK1f_V9PAXp2+EFRpPtxCG_DRx08PZXAPkyw@mail.gmail.com%5D

+ /* Add size of the payload header if it presets. */

FWIW, repeating the same comment, with or without minor differences, 10
times is a bad idea. Especially when the comment doesn't add *any* sort
of information.

Ok, fixed

Also, "if it presets" presumably is a typo?

Fixed

+/*
+ * Compute and return the expected size of an undo record.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+     Size            size;
+
+     /* Header size. */
+     size = UndoRecordHeaderSize(uur->uur_info);
+
+     /* Payload data size. */
+     if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+     {
+             size += uur->uur_payload.len;
+             size += uur->uur_tuple.len;
+     }
+
+     /* Add undo record length size. */
+     size += sizeof(uint16);
+
+     return size;
+}
+
+/*
+ * Calculate the size of the undo record stored on the page.
+ */
+static inline Size
+UndoRecordSizeOnPage(char *page_ptr)
+{
+     uint16          uur_info = ((UndoRecordHeader *) page_ptr)->urec_info;
+     Size            size;
+
+     /* Header size. */
+     size = UndoRecordHeaderSize(uur_info);
+
+     /* Payload data size. */
+     if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+     {
+             UndoRecordPayload *payload = (UndoRecordPayload *) (page_ptr + size);
+
+             size += payload->urec_payload_len;
+             size += payload->urec_tuple_len;
+     }
+
+     return size;
+}
+
+/*
+ * Compute size of the Unpacked undo record in memory
+ */
+Size
+UnpackedUndoRecordSize(UnpackedUndoRecord *uur)
+{
+     Size            size;
+
+     size = sizeof(UnpackedUndoRecord);
+
+     /* Add payload size if record contains payload data. */
+     if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+     {
+             size += uur->uur_payload.len;
+             size += uur->uur_tuple.len;
+     }
+
+     return size;
+}

These functions are all basically the same. We shouldn't copy code over
and over like this.

UnpackedUndoRecordSize -> computes the size of the unpacked undo
record so it's different from above two, just payload part is common
so moved payload size to common function.

UndoRecordExpectedSize and UndoRecordSizeOnPage are two different
functions except for the header size computation so I already had the
common function for the header. UndoRecordExpectedSize computes the
expected record size so it can access the payload directly from the
unpack undo record whereas the UndoRecordSizeOnPage needs to calculate
the record size by the record pointer which is already stored on the
page so actually it doesn't have the unpacked undo record instead it
first need to compute the header size and then it needs to reach to
the payload data. Typecast that to payload header and compute the
length. In unpack undo record payload is stored as StringInfoData
whereas on the page it is packed as UndoRecordPayload header. So I
am not sure how to unify them. Anyway, UndoRecordSizeOnPage is
required only for undo page consistency checker patch so I have moved
out of this patch. Later, I am planning to handle the comments of the
undo page consistency checker patch so I will try to work on this
function if I can improve it.

+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+     ucontext->stage = UNDO_PACK_STAGE_HEADER;
+     ucontext->already_processed = 0;
+     ucontext->partial_bytes = 0;
+
+     /* Copy undo record header. */
+     ucontext->urec_hd.urec_type = uur->uur_type;
+     ucontext->urec_hd.urec_info = uur->uur_info;
+
+     /* Copy undo record transaction header if it is present. */
+     if ((uur->uur_info & UREC_INFO_TRANSACTION) != 0)
+             memcpy(&ucontext->urec_txn, uur->uur_txn, SizeOfUndoRecordTransaction);
+
+     /* Copy rmid if present. */
+     if ((uur->uur_info & UREC_INFO_RMID) != 0)
+             ucontext->urec_rmid = uur->uur_rmid;
+
+     /* Copy reloid if present. */
+     if ((uur->uur_info & UREC_INFO_RELOID) != 0)
+             ucontext->urec_reloid = uur->uur_reloid;
+
+     /* Copy fxid if present. */
+     if ((uur->uur_info & UREC_INFO_XID) != 0)
+             ucontext->urec_fxid = uur->uur_fxid;
+
+     /* Copy cid if present. */
+     if ((uur->uur_info & UREC_INFO_CID) != 0)
+             ucontext->urec_cid = uur->uur_cid;
+
+     /* Copy undo record relation header if it is present. */
+     if ((uur->uur_info & UREC_INFO_FORK) != 0)
+             ucontext->urec_fork = uur->uur_fork;
+
+     /* Copy prev undo record pointer if it is present. */
+     if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+             ucontext->urec_prevundo = uur->uur_prevundo;
+
+     /* Copy undo record block header if it is present. */
+     if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+     {
+             ucontext->urec_blk.urec_block = uur->uur_block;
+             ucontext->urec_blk.urec_offset = uur->uur_offset;
+     }
+
+     /* Copy undo record log switch header if it is present. */
+     if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+             memcpy(&ucontext->urec_logswitch, uur->uur_logswitch,
+                        SizeOfUndoRecordLogSwitch);
+
+     /* Copy undo record payload header and data if it is present. */
+     if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+     {
+             ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+             ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+             ucontext->urec_payloaddata = uur->uur_payload.data;
+             ucontext->urec_tupledata = uur->uur_tuple.data;
+     }
+     else
+     {
+             ucontext->urec_payload.urec_payload_len = 0;
+             ucontext->urec_payload.urec_tuple_len = 0;
+     }
+
+     /* Compute undo record expected size and store in the context. */
+     ucontext->undo_len = UndoRecordExpectedSize(uur);
+}

It really can't be right to have all these fields basically twice, in
UnackedUndoRecord, and UndoPackContext. And then copy them one-by-one.
I mean there's really just some random differences (ordering, some field
names) between the structures, but otherwise they're the same?

What on earth do we gain by this? This entire intermediate stage makes
no sense at all to me. We copy data into an UndoRecord, then we copy
into an UndoRecordContext, with essentially a field-by-field copy
logic. Then we have another field-by-field logic that copies the data
into the page.

The idea was that in UnpackedUndoRecord we have all member as a field
by field but in context, we can keep them in headers for example
UndoRecordHeader, UndoRecordGroup, UndoRecordBlock. And, the idea
behind this is that during InsertUndoData instead of calling
InsertUndoByte field by field we call it once for each header because
either we have to write all field of that header or none. But later
we end up having a lot of optional headers and most of them have just
one field in it so it appears that we are copying field by field.

One alternative could be that we palloc a memory in context and then
pack each field in that memory (except the payload and tuple data)
then in one InsertUndoByte call we can insert complete header part and
in we can have 2 more calls to InsertUndoBytes for writing payload and
tuple data. What's your thought on this.

+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+     char       *writeptr = (char *) page + starting_byte;
+     char       *endptr = (char *) page + BLCKSZ;
+
+     switch (ucontext->stage)
+     {
+             case UNDO_PACK_STAGE_HEADER:
+                     /* Insert undo record header. */
+                     if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+                                                              SizeOfUndoRecordHeader, &writeptr, endptr,
+                                                              &ucontext->already_processed,
+                                                              &ucontext->partial_bytes))
+                             return;
+                     ucontext->stage = UNDO_PACK_STAGE_TRANSACTION;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_TRANSACTION:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_TRANSACTION) != 0)
+                     {
+                             /* Insert undo record transaction header. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_txn,
+                                                                      SizeOfUndoRecordTransaction,
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_RMID;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_RMID:
+                     /* Write rmid(if needed and not already done). */
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+                     {
+                             if (!InsertUndoBytes((char *) &(ucontext->urec_rmid), sizeof(RmgrId),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_RELOID;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_RELOID:
+                     /* Write reloid(if needed and not already done). */
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+                     {
+                             if (!InsertUndoBytes((char *) &(ucontext->urec_reloid), sizeof(Oid),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_XID;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_XID:
+                     /* Write xid(if needed and not already done). */
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_XID) != 0)
+                     {
+                             if (!InsertUndoBytes((char *) &(ucontext->urec_fxid), sizeof(FullTransactionId),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_CID;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_CID:
+                     /* Write cid(if needed and not already done). */
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+                     {
+                             if (!InsertUndoBytes((char *) &(ucontext->urec_cid), sizeof(CommandId),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_FORKNUM:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+                     {
+                             /* Insert undo record fork number. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+                                                                      sizeof(ForkNumber),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_PREVUNDO:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+                     {
+                             /* Insert undo record blkprev. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_prevundo,
+                                                                      sizeof(UndoRecPtr),
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_BLOCK:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+                     {
+                             /* Insert undo record block header. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+                                                                      SizeOfUndoRecordBlock,
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_LOGSWITCH:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+                     {
+                             /* Insert undo record transaction header. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+                                                                      SizeOfUndoRecordLogSwitch,
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_PAYLOAD:
+                     if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+                     {
+                             /* Insert undo record payload header. */
+                             if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+                                                                      SizeOfUndoRecordPayload,
+                                                                      &writeptr, endptr,
+                                                                      &ucontext->already_processed,
+                                                                      &ucontext->partial_bytes))
+                                     return;
+                     }
+                     ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_PAYLOAD_DATA:
+                     {
+                             int                     len = ucontext->urec_payload.urec_payload_len;
+
+                             if (len > 0)
+                             {
+                                     /* Insert payload data. */
+                                     if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+                                                                              len, &writeptr, endptr,
+                                                                              &ucontext->already_processed,
+                                                                              &ucontext->partial_bytes))
+                                             return;
+                             }
+                             ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+                     }
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_TUPLE_DATA:
+                     {
+                             int                     len = ucontext->urec_payload.urec_tuple_len;
+
+                             if (len > 0)
+                             {
+                                     /* Insert tuple data. */
+                                     if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+                                                                              len, &writeptr, endptr,
+                                                                              &ucontext->already_processed,
+                                                                              &ucontext->partial_bytes))
+                                             return;
+                             }
+                             ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+                     }
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_UNDO_LENGTH:
+                     /* Insert undo length. */
+                     if (!InsertUndoBytes((char *) &ucontext->undo_len,
+                                                              sizeof(uint16), &writeptr, endptr,
+                                                              &ucontext->already_processed,
+                                                              &ucontext->partial_bytes))
+                             return;
+
+                     ucontext->stage = UNDO_PACK_STAGE_DONE;
+                     /* fall through */
+
+             case UNDO_PACK_STAGE_DONE:
+                     /* Nothing to be done. */
+                     break;
+
+             default:
+                     Assert(0);                      /* Invalid stage */
+     }
+}

I don't understand. The only purpose of this is that we can partially
write a packed-but-not-actually-packed record onto a bunch of pages? And
for that we have an endless chain of copy and pasted code calling
InsertUndoBytes()? Copying data into shared buffers in tiny increments?

If we need to this, what is the whole packed record format good for?
Except for adding a bunch of functions with 10++ ifs and nearly
identical code?

Copying data is expensive. Copying data in tiny increments is more
expensive. Copying data in tiny increments, with a bunch of branches, is
even more expensive. Copying data in tiny increments, with a bunch of
branches, is even more expensive, especially when it's shared
memory. Copying data in tiny increments, with a bunch of branches, is
even more expensive, especially when it's shared memory, especially when
all that shared meory is locked at once.

+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+     char       *readptr = (char *) page + starting_byte;
+     char       *endptr = (char *) page + BLCKSZ;
+
+     switch (ucontext->stage)
+     {
+             case UNDO_PACK_STAGE_HEADER:

You know roughly what I'm thinking.

I have expressed my thought on this in last comment.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#239Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#235)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 13, 2019 at 6:28 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

So, for top-level transactions rollback, we can directly refer from
UndoRequest *, the start and end locations. But, what should we do
for sub-transactions (rollback to savepoint)? One related point is
that we also need information about last_log_start_undo_location to
update the undo apply progress (The basic idea is if the transactions
undo is spanned across multiple logs, we update the progress in each
of the logs.). We can remember that in the transaction state or
undorequest *. Any suggestion?

The UndoRequest is only for top-level rollback. Any state that you
need in order to do subtransaction rollback needs to be maintained
someplace else, probably in the transaction state state, or some
subsidiary data structure. The point here is that the UndoRequest is
going to be stored in shared memory, but there is no reason ever to
store the information about a subtransaction in shared memory, because
that undo always has to be completed by the backend that is
responsible for that transaction. Those things should not get mixed
together.

IIUC, for each transaction, we have to take a lock first time it
attaches to a log and then the same lock at commit time. It seems the
work under lock is less, but still, can't this cause a contention? It
seems to me this is similar to what we saw in ProcArrayLock where work
under lock was few instructions, but acquiring and releasing the lock
by each backend at commit time was causing a bottleneck.

LWLocks are pretty fast these days and the critical section is pretty
short, so I think there's a chance it'll be just fine, but maybe it'll
cause enough cache line bouncing to be problematic. If so, I think
there are several possible ways to redesign the locking to improve
things, but it made sense to me to try the simple approach first.

How will computation of oldestXidHavingUnappliedUndo will work?

We can probably check the fxid queue and error queue to get that
value. However, I am not sure if that is sufficient because incase we
perform the request in the foreground, it won't be present in queues.

Oh, I forgot about that requirement. I think I can fix it so it does
that fairly easily, but it will require a little bit of redesign which
I won't have time to do this week.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#240Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#210)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-06 14:18:42 -0700, Andres Freund wrote:

Here's the last section of my low-leve review. Plan to write a higher
level summary afterwards, now that I have a better picture of the code.

General comments:

- For a feature of this complexity, there's very little architectural
documentation. Some individual parts have a bit, but there's basically
nothing over-arching. That makes it extremely hard for anybody that is
not already involved to understand the design constraints, and even
for people involved it's hard to understand.

I think it's very important for this to have a document that first
explains what the goals, and non-goals, of this feature are. And then
secondly explains the chosen architecture referencing those
constraints. Once that's there it's a lot easier to review this
patchset, to discuss the overall architecture, etc.

- There are too many different email threads and git branches. The same
patches are discussed in different threads, layers exist in slightly
diverging versions in different git trees. Again making it very hard
for anybody not primarily focussing on undo to join the discussion.

I think most of the older git branches should be renamed into
something indicating their historic status. The remaining branches
should be referenced from a wiki page (linked to in each submission of
a new patch version), explaining what they're about. I don't think
it's realistic to have people infer meaning from the current branch
names (undo, proposal-undo-log, undo-log-storage, undo-log-storage-v2,
undo_interface_v1, undoprocessing).

Given the size of the the overall project it's quite possibly not
realistic to manage the whole work in a single git branch. With
separate git branches, as currently done, it's however hard to
understand which version of a what layer is used. I think at the very
least higher layers need to indicate the version of the underlying
layers is being used. I suggest just adding a "v01: " style prefix to
all commit subjects a branch is rebased onto.

It's also currently hard to understand what version of a layer is
being discussed. I think submissions all need to include a version
number (c.f. git format-patch's -v option), and that version ought to
be included in the subject line. Each new major version of a patch
should be started as a reply to the first message of a thread, to
keep the structure of a discussion in a managable shape. New versions
should include explicit explanations about the major changes compared
to the last version.

- The code quality of pretty significant parts of the patchset is not
even close to being good enough. There are areas with a lot of code
duplication. There are very few higher level explanations for
interfaces. There's a lot of "i++; /* increment i to increment it */"
style comments, but not enough higher level comments. There are
significant issues with parts of the code that aren't noted anywhere
in comments, leading to reviewers having to repeatedly re-discover
them (and wasting time on that).

There's different naming styles in related code without a discernible
pattern (e.g. UndoRecordSetInfo being followed by
get_undo_rec_cid_offset). The word-ordering of different layers is
confusing (e.g. BeginUndoRecordInsert vs UndoLogBeginInsert vs
PrepareUndoInsert). Different important externally visible functions
have names that don't allow to determine which is supposed to do what
(PrepareUndoInsert vs BeginUndoRecordInsert).

More specific comments:

- The whole sequencing of undo record insertion in combination with WAL
logging does not appear to be right. It's a bit hard to say, because
there's very little documentation on what the intended default
sequence of operations is.

My understanding is that the currently expected pattern is to:

1) Collect information / perform work needed to perform the action
that needs to be UNDO logged. E.g. perform visibility
determinations, wait for lockers, compute new infomask, etc.

This will likely end with the "content" page(s) (e.g. a table's
page) being exclusively locked.

2) Estimate space needed for all UNDO logging (BeginUndoRecordInsert)

3) Prepare for each undo record, this includes building the
content for each undo record. PrepareUndoInsert(). This acquires,
pins and locks buffers for undo.

4) begin a critical section

5) modify content page, mark buffer dirty

6) write UNDO, using InsertPreparedUndo()

7) associate undo with WAL record (RegisterUndoLogBuffers)

8) write WAL

9) End critical section

But despite reading through the code, including READMEs, I'm doubtful
that's quite the intended pattern. It REALLY can't be right that one
needs to parse many function headers to figure out how the most basic
use of undo could possibly work. There needs to be very clear
documentation about how to write undo records. Functions that sound
like they're involved, need actually useful comments, rather than just
restatements of their function names (cf RegisterUndoLogBuffers,
UndoLogBuffersSetLSN, UndoLogRegister).

- I think there's two fairly fundamental, and related, problems with
the sequence outlined above:

- We can't search for target buffers to store undo data, while holding
the "table" page content locked. That can involve writing out
multiple pages till we find a usable victim buffer. That can take a
pretty long time. While that's happening the content page would
currently be locked. Note how e.g. heapam.c is careful to not hold
*any* content locks while potentially performing IO. I think the
current interface makes that hard.

The easy way to solve would be to require sites performing UNDO
logging to acquire victim pages before even acquiring other content
locks. Perhaps the better approach could be for the undo layer to
hold onto a number of clean buffers, and to keep the last page in an
already written to undo log pinned.

- We can't search for victim buffers for further undo data while
already holding other undo pages content locked. Doing so means that
while we're e.g. doing IO to clean out the new page, old undo data
on the previous page can't be read.

This seems easier to fix. Instead of PrepareUndoInsert() acquiring,
pinning and locking buffers, it'd need to be split into two
operations. One that acquires buffers and pins them, and one that
locks them. I think it's quite possible that the locking operation
could just be delayed until InsertPreparedUndo(). But if we solve
the above problem, most of this might already be solved.

- To me the current split between the packed and unpacked UNDO record
formats makes very little sense, the reasoning behind having them is
poorly if at all documented, results in extremely verbose code, and
isn't extensible.

When preparing to insert an undo record the in-buffer size is computed
with UndoRecordHeaderSize() (needs to know about all optional data)
from within PrepareUndoInsert() (which has a bunch a bunch of
additional knowledge about the record format). Then during insertion
InsertPreparedUndo(), first copies the UnpackedUndoRecord into an
UndoPackContext (again needing ...), and then, via InsertUndoData(),
copies that in small increments into the respective buffers (again
needing knowledge about the complete record format, two copies
even). Beside the code duplication, that also means the memory copies
are very inefficient, because they're all done in tiny increments,
multiple times.

When reading undo it's smilar: UnpackUndoData(), again in small
chunks, reads the buffer data into an UndoPackContext (another full
copy of the unpacked record format). But then FinishUnpackUndo()
*again* copies all that data, into an actual UnpackedUndoRecord
(again, with a copy of the record format, albeit slightly different
looking).

I'm not convinced by Heikki's argument that we shouldn't have
structure within undo records. In my opinion that is a significant
weakness of how WAL was initially designed, and even after Heikki's
work, still is a problem. But this isn't the right design either.

Even if were to stay with the current fixed record format, I think
the current code needs a substantial redesign:

- I think 'packing' during insertion needs to serialize into a char*
allocation during PrepareUndoInsert computing the size in parallel
(or perhaps in InsertPreparedUndo, but probably not). The size of
the record should never be split across record boundaries
(i.e. we'll leave that space unused if we otherwise would need to
split the size). The actual insertion would be a maximally sized
memcpy() (so we'd as many memcpys as the buffer fits in, rather than
one for each sub-type of a record).

That allows to remove most of the duplicated knowledge of the record
format, and makes insertions faster (by doing only large memcpys
while holding exclusive content locks).

- When reading an undo record, the whole stage of UnpackUndoData()
reading data into a the UndoPackContext is omitted, reading directly
into the UnpackedUndoRecord. That removes one further copy of the
record format.

- To avoid having separate copies of the record format logic, I'd
probably encode it into *one* array of metadata. If we had
{offsetoff(UnpackedUndoRecord, member),
membersize(UnpackedUndoRecord, member),
flag}
we could fairly trivially remove most knowledge from the places
currently knowing about the record format.

I have some vague ideas for how to specify the format in a way that is
more extensible, but with more structure than just a blob of data. But
I don't think they're there yet.

- The interface to read undo also doesn't seem right to me. For one
there's various different ways to read records, with associated code
duplication (batch, batch in "one page" mode - but that's being
removed now I think, single record mode).

I think the batch mode is too restrictive. We might not need this
during the first merged version, but I think before long we're going
to want to be able to efficiently traverse all the undo chains we need
to determine the visibility of all tuples on a page. Otherwise we'll
cause a lot of additional synchronous read IO, and will repeatedly
re-fetch information, especially during sequential scans for an older
snapshot. I think I briefly outlined this in an earlier email - my
current though is that the batch interface (which the non-batch
interface should just be a tiny helper around), should basically be a
queue of "to-be-fetched" undo records. When batching reading an entire
transaction, all blocks get put onto that queue. When traversing
multiple chains, the chains are processed in a breadth-first fashion
(by just looking at the queue, and pushing additional work to the
end). That allows to efficiently issue prefetch requests for blocks to
be read in the near future.

I think that batch reading should just copy the underlying data into a
char* buffer. Only the records that currently are being used by
higher layers should get exploded into an unpacked record. That will
reduce memory usage quite noticably (and I suspect it also drastically
reduce the overhead due to a large context with a lot of small
allocations that then get individually freed). That will make the
sorting of undo a bit more CPU inefficient, because individual records
will need to be partially unpacked for comparison, but I think that's
going to be a far smaller loss than the win.

- My reading of the current xact.c integration is that it's not workable
as is. Undo is executed outside of a valid transaction state,
exceptions aren't properly undone, logic would need to be duplicated
to a significant degree, new kind of critical section.

Greetings,

Andres Freund

#241Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#240)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

- I think there's two fairly fundamental, and related, problems with
the sequence outlined above:

- We can't search for target buffers to store undo data, while holding
the "table" page content locked.

The easy way to solve would be to require sites performing UNDO
logging to acquire victim pages before even acquiring other content
locks. Perhaps the better approach could be for the undo layer to
hold onto a number of clean buffers, and to keep the last page in an
already written to undo log pinned.

- We can't search for victim buffers for further undo data while
already holding other undo pages content locked. Doing so means that
while we're e.g. doing IO to clean out the new page, old undo data
on the previous page can't be read.

This seems easier to fix. Instead of PrepareUndoInsert() acquiring,
pinning and locking buffers, it'd need to be split into two
operations. One that acquires buffers and pins them, and one that
locks them. I think it's quite possible that the locking operation
could just be delayed until InsertPreparedUndo(). But if we solve
the above problem, most of this might already be solved.

Basically, that means
- the caller should call PreparedUndoInsert before acquiring table
page content lock right? because the PreparedUndoInsert just compute
the size, allocate the space and pin+lock the buffers and for pinning
the buffers we must compute the size and allocate the space using undo
storage layer.
- So basically, if we delay the lock till InsertPreparedUndo and call
PrepareUndoInsert before acquiring table page content lock this
problem is solved?

Although I haven't yet analyzed the AM specific part that whether it's
always possible to call the PrepareUndoInsert(basically getting all
the undo record ready) before the page content lock. But, I am sure
that won't be much difficult part.

- To me the current split between the packed and unpacked UNDO record
formats makes very little sense, the reasoning behind having them is
poorly if at all documented, results in extremely verbose code, and
isn't extensible.

When preparing to insert an undo record the in-buffer size is computed
with UndoRecordHeaderSize() (needs to know about all optional data)
from within PrepareUndoInsert() (which has a bunch a bunch of
additional knowledge about the record format). Then during insertion
InsertPreparedUndo(), first copies the UnpackedUndoRecord into an
UndoPackContext (again needing ...), and then, via InsertUndoData(),
copies that in small increments into the respective buffers (again
needing knowledge about the complete record format, two copies
even). Beside the code duplication, that also means the memory copies
are very inefficient, because they're all done in tiny increments,
multiple times.

When reading undo it's smilar: UnpackUndoData(), again in small
chunks, reads the buffer data into an UndoPackContext (another full
copy of the unpacked record format). But then FinishUnpackUndo()
*again* copies all that data, into an actual UnpackedUndoRecord
(again, with a copy of the record format, albeit slightly different
looking).

I'm not convinced by Heikki's argument that we shouldn't have
structure within undo records. In my opinion that is a significant
weakness of how WAL was initially designed, and even after Heikki's
work, still is a problem. But this isn't the right design either.

Even if were to stay with the current fixed record format, I think
the current code needs a substantial redesign:

- I think 'packing' during insertion needs to serialize into a char*
allocation during PrepareUndoInsert

ok
computing the size in parallel

(or perhaps in InsertPreparedUndo, but probably not). The size of
the record should never be split across record boundaries
(i.e. we'll leave that space unused if we otherwise would need to
split the size).

I think before UndoRecordAllocate we need to detect this part that
whether the size of the record will start from the last byte of the
page and if so then allocate one extra byte for the undo record. Or
always allocate one extra byte for the undo record for handling this
case. And, in FinalizeUndoAdvance only pass the size how much we have
actually consumed.

The actual insertion would be a maximally sized

memcpy() (so we'd as many memcpys as the buffer fits in, rather than
one for each sub-type of a record).

That allows to remove most of the duplicated knowledge of the record
format, and makes insertions faster (by doing only large memcpys
while holding exclusive content locks).

Right.

- When reading an undo record, the whole stage of UnpackUndoData()
reading data into a the UndoPackContext is omitted, reading directly
into the UnpackedUndoRecord. That removes one further copy of the
record format.

So we will read member by member to UnpackedUndoRecord? because in
context we have at least a few headers packed and we can memcpy one
header at a time like UndoRecordHeader, UndoRecordBlock. But that just
a few of them so if we copy field by field in the UnpackedUndoRecord
then we can get rid of copying in context then copy it back to the
UnpackedUndoRecord. Is this is what in your mind or you want to store
these structures (UndoRecordHeader, UndoRecordBlock) directly into
UnpackedUndoRecord?

- To avoid having separate copies of the record format logic, I'd
probably encode it into *one* array of metadata. If we had
{offsetoff(UnpackedUndoRecord, member),
membersize(UnpackedUndoRecord, member),
flag}
we could fairly trivially remove most knowledge from the places
currently knowing about the record format.

Seems interesting. I will work on this.

I have some vague ideas for how to specify the format in a way that is
more extensible, but with more structure than just a blob of data. But
I don't think they're there yet.

- The interface to read undo also doesn't seem right to me. For one
there's various different ways to read records, with associated code
duplication (batch, batch in "one page" mode - but that's being
removed now I think, single record mode).

I think the batch mode is too restrictive. We might not need this
during the first merged version, but I think before long we're going
to want to be able to efficiently traverse all the undo chains we need
to determine the visibility of all tuples on a page. Otherwise we'll
cause a lot of additional synchronous read IO, and will repeatedly
re-fetch information, especially during sequential scans for an older
snapshot. I think I briefly outlined this in an earlier email - my
current though is that the batch interface (which the non-batch
interface should just be a tiny helper around), should basically be a
queue of "to-be-fetched" undo records. When batching reading an entire
transaction, all blocks get put onto that queue. When traversing
multiple chains, the chains are processed in a breadth-first fashion
(by just looking at the queue, and pushing additional work to the
end). That allows to efficiently issue prefetch requests for blocks to
be read in the near future.

I need to analyze this part.

I think that batch reading should just copy the underlying data into a
char* buffer. Only the records that currently are being used by
higher layers should get exploded into an unpacked record. That will
reduce memory usage quite noticably (and I suspect it also drastically
reduce the overhead due to a large context with a lot of small
allocations that then get individually freed).

Ok, I got your idea. I will analyze it further and work on this if
there is no problem.

That will make the

sorting of undo a bit more CPU inefficient, because individual records
will need to be partially unpacked for comparison, but I think that's
going to be a far smaller loss than the win.

Right.

- My reading of the current xact.c integration is that it's not workable
as is. Undo is executed outside of a valid transaction state,
exceptions aren't properly undone, logic would need to be duplicated
to a significant degree, new kind of critical section.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#242Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#234)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-13 13:53:59 +0530, Dilip Kumar wrote:

On Tue, Jul 30, 2019 at 1:32 PM Andres Freund <andres@anarazel.de> wrote:

+     /* Loop until we have fetched all the buffers in which we need to write. */
+     while (size > 0)
+     {
+             bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+             xact_info->idx_undo_buffers[index++] = bufidx;
+             size -= (BLCKSZ - starting_byte);
+             starting_byte = UndoLogBlockHeaderSize;
+             cur_blk++;
+     }

So, this locks a very large number of undo buffers at the same time, do
I see that correctly? What guarantees that there are no deadlocks due
to multiple buffers locked at the same time (I guess the order inside
the log)? What guarantees that this is a small enough number that we can
even lock all of them at the same time?

I think we are locking them in the block order and that should avoid
the deadlock. I have explained in the comments.

Sorry for harping on this so much: But please, please, *always* document
things like this *immediately*. This is among the most crucial things to
document. There shouldn't need to be a reviewer prodding you to do so
many months after the code has been written. For one you've likely
forgotten details by then, but more importantly dependencies on the
locking scheme will have crept into further places - if it's not well
thought through that can be hrad to undo. And it wastes reviewer /
reader bandwidth.

Why do we need to lock all of them at the same time? That's not clear to
me.

Because this is called outside the critical section so we keep all the
buffers locked what we want to update inside the critical section for
single wal record.

I don't understand this explanation. What does keeping the buffers
locked have to do with the critical section? As explained in a later
email, I think the current approach is not acceptable - but even without
those issues, I don't see why we couldn't just lock the buffers at a
later stage?

+     for (i = 0; i < context->nprepared_undo_buffer; i++)
+     {

How large do we expect this to get at most?

In BeginUndoRecordInsert we are computing it

+ /* Compute number of buffers. */
+ nbuffers = (nprepared + MAX_UNDO_UPDATE_INFO) * MAX_BUFFER_PER_UNDO;

Since nprepared is variable, that doesn't really answer the question.

Greetings,

Andres Freund

#243Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#237)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-13 17:05:27 +0530, Dilip Kumar wrote:

On Mon, Aug 5, 2019 at 11:59 PM Andres Freund <andres@anarazel.de> wrote:

(as I was out of context due to dealing with bugs, I've switched to
lookign at the current zheap/undoprocessing branch.

On 2019-07-30 01:02:20 -0700, Andres Freund wrote:

+/*
+ * Insert a previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

As far as I can tell there's simply no deadlock avoidance scheme in use
here *at all*? I must be missing something.

We are always locking buffer in block order so I am not sure how it
can deadlock? Am I missing something?

Do we really in all circumstances? Note that we update the transinfo
(and also progress) from earlier in the log. But my main point is more
that there's no documented deadlock avoidance scheme. Which imo means
there's none, because nobody will know to maintain it.

+                     /*
+                      * During recovery, there might be some blocks which are already
+                      * deleted due to some discard command so we can just skip
+                      * inserting into those blocks.
+                      */
+                     if (!BufferIsValid(buffer))
+                     {
+                             Assert(InRecovery);
+
+                             /*
+                              * Skip actual writing just update the context so that we have
+                              * write offset for inserting into next blocks.
+                              */
+                             SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+                             if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+                                     break;
+                     }

How exactly can this happen?

Suppose you insert one record for the transaction which split in
block1 and 2. Now, before this block is actually going to the disk
the transaction committed and become all visible the undo logs are
discarded. It's possible that block 1 is completely discarded but
block 2 is not because it might have undo for the next transaction.
Now, during recovery (FPW is off) if block 1 is missing but block 2 is
their so we need to skip inserting undo for block 1 as it does not
exist.

Hm. I'm quite doubtful this is a good idea. How will this not force us
to a emit a lot more expensive durable operations while writing undo?
And doesn't this reduce error detection quite remarkably?

Thomas, Robert?

+                     /* Read the undo record. */
+                     UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+                     /* Release the discard lock after fetching the record. */
+                     if (!InHotStandby)
+                             LWLockRelease(&slot->discard_lock);
+             }
+             else
+                     UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);

And then we do none of this in !one_page mode.

UndoBulkFetchRecord is always called from the aborted transaction so
its undo can never get discarded concurrently so ideally, we don't
need to check for discard.

That's an undocumented assumption. Why would anybody reading the
interface know that?

+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+                                      UndoLogCategory category)
+{
+     UndoLogOffset page_offset = UndoRecPtrGetPageOffset(urp);
+     BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+     Buffer          buffer = input_buffer;
+     Page            page = NULL;
+     char       *pagedata = NULL;
+     char            prevlen[2];
+     RelFileNode rnode;
+     int                     byte_to_read = sizeof(uint16);

Shouldn't it be byte_to_read?

Err, *bytes*_to_read.

And the sizeof a type that's tied with the actual undo format?
Imagine we'd ever want to change the length format for undo records
- this would be hard to find.

Do you mean that we should not rely on undo format i.e. we should not
assume that undo length is stored at the end of the undo record?

I was referencing the use of sizeof(uint16). I think this should either
reference an UndoRecLen typedef or something like it, or use something
roughly like
#define member_size(type, member) (sizeof((type){0}.member))
and then have bytes_to_read be set to something like
member_size(PackedUndoRecord, len)

+     char            persistence;
+     uint16          prev_rec_len = 0;
+
+     /* Get relfilenode. */
+     UndoRecPtrAssignRelFileNode(rnode, urp);
+     persistence = RelPersistenceForUndoLogCategory(category);
+
+     if (BufferIsValid(buffer))
+     {
+             page = BufferGetPage(buffer);
+             pagedata = (char *) page;
+     }
+
+     /*
+      * Length if the previous undo record is store at the end of that record
+      * so just fetch last 2 bytes.
+      */
+     while (byte_to_read > 0)
+     {

Why does this need a loop around the number of bytes? Can there ever be
a case where this is split across a record? If so, isn't that a bad idea
anyway?

Yes, as of now, undo record can be splitted at any point even the undo
length can be split acorss 2 pages. I think we can reduce complexity
by making sure undo length doesn't get split acorss pages.

I think we definitely should do that. I'd probably even include more
than just the size in the header that's not allowed to be split across
pages.

But for handling that while allocating the undo we need to detect this
whether the undo length can get splitted by checking the space in the
current page and the undo record length and based on that we need to
allocate 1 extra byte in the undo log. Seems that will add an extra
complexity.

That seems fairly straightforward?

Greetings,

Andres Freund

#244Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#241)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-14 14:48:07 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

- I think there's two fairly fundamental, and related, problems with
the sequence outlined above:

- We can't search for target buffers to store undo data, while holding
the "table" page content locked.

The easy way to solve would be to require sites performing UNDO
logging to acquire victim pages before even acquiring other content
locks. Perhaps the better approach could be for the undo layer to
hold onto a number of clean buffers, and to keep the last page in an
already written to undo log pinned.

- We can't search for victim buffers for further undo data while
already holding other undo pages content locked. Doing so means that
while we're e.g. doing IO to clean out the new page, old undo data
on the previous page can't be read.

This seems easier to fix. Instead of PrepareUndoInsert() acquiring,
pinning and locking buffers, it'd need to be split into two
operations. One that acquires buffers and pins them, and one that
locks them. I think it's quite possible that the locking operation
could just be delayed until InsertPreparedUndo(). But if we solve
the above problem, most of this might already be solved.

Basically, that means
- the caller should call PreparedUndoInsert before acquiring table
page content lock right? because the PreparedUndoInsert just compute
the size, allocate the space and pin+lock the buffers and for pinning
the buffers we must compute the size and allocate the space using undo
storage layer.

I don't think we can normally pin the undo buffers properly at that
stage. Without knowing the correct contents of the table page - which we
can't know without holding some form of lock preventing modifications -
we can't know how big our undo records are going to be. And we can't
just have buffers that don't exist on disk in shared memory, and we
don't want to allocate undo that we then don't need. So I think what
we'd have to do at that stage, is to "pre-allocate" buffers for the
maximum amount of UNDO needed, but mark the associated bufferdesc as not
yet valid. These buffers would have a pincount > 0, but BM_TAG_VALID
would not be set.

So at the start of a function that will need to insert undo we'd need to
pre-reserve the maximum number of buffers we could potentially
need. That reservation stage would

a) pin the page with the current end of the undo
b) if needed pin the page of older undo that we need to update (e.g. to
update the next pointer)
c) perform clock sweep etc to acquire (find or create) enough clean to
hold the maximum amount of undo needed. These buffers would be marked
as !BM_TAG_VALID | BUF_REFCOUNT_ONE.

I assume that we'd make a) cheap by keeping it pinned for undo logs that
a backend is actively attached to. b) should only be needed once in a
transaction, so it's not too bad. c) we'd probably need to amortize
across multiple undo insertions, by keeping the unused buffers pinned
until the end of the transaction.

I assume that having the infrastructure c) might also make some code
for already in postgres easier. There's obviously some issues around
guaranteeing that the maximum number of such buffers isn't high.

- So basically, if we delay the lock till InsertPreparedUndo and call
PrepareUndoInsert before acquiring table page content lock this
problem is solved?

Although I haven't yet analyzed the AM specific part that whether it's
always possible to call the PrepareUndoInsert(basically getting all
the undo record ready) before the page content lock. But, I am sure
that won't be much difficult part.

I think that is somewhere between not possible, and so expensive in a
lot of cases that we'd not want to do it anyway. You'd at leasthave to
first acquire a content lock on the page, mark the target tuple as
locked, then unlock the page, reserve undo, lock the table page,
actually update it.

- When reading an undo record, the whole stage of UnpackUndoData()
reading data into a the UndoPackContext is omitted, reading directly
into the UnpackedUndoRecord. That removes one further copy of the
record format.

So we will read member by member to UnpackedUndoRecord? because in
context we have at least a few headers packed and we can memcpy one
header at a time like UndoRecordHeader, UndoRecordBlock.

Well, right now you then copy them again later, so not much is gained by
that (although that later copy can happen without the content lock
held). As I think I suggested before, I suspect that the best way would
be to just memcpy() the data from the page(s) into an appropriately
sized buffer with the content lock held, and then perform unpacking
directly into UnpackedUndoRecord. Especially with the bulk API that will
avoid having to do much work with locks held, and reduce memory usage by
only unpacking the record(s) in a batch that are currently being looked
at.

But that just a few of them so if we copy field by field in the
UnpackedUndoRecord then we can get rid of copying in context then copy
it back to the UnpackedUndoRecord. Is this is what in your mind or
you want to store these structures (UndoRecordHeader, UndoRecordBlock)
directly into UnpackedUndoRecord?

I at the moment see no reason not to?

Greetings,

Andres Freund

#245Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#244)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 10:35 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-14 14:48:07 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

- I think there's two fairly fundamental, and related, problems with
the sequence outlined above:

- We can't search for target buffers to store undo data, while holding
the "table" page content locked.

The easy way to solve would be to require sites performing UNDO
logging to acquire victim pages before even acquiring other content
locks. Perhaps the better approach could be for the undo layer to
hold onto a number of clean buffers, and to keep the last page in an
already written to undo log pinned.

- We can't search for victim buffers for further undo data while
already holding other undo pages content locked. Doing so means that
while we're e.g. doing IO to clean out the new page, old undo data
on the previous page can't be read.

This seems easier to fix. Instead of PrepareUndoInsert() acquiring,
pinning and locking buffers, it'd need to be split into two
operations. One that acquires buffers and pins them, and one that
locks them. I think it's quite possible that the locking operation
could just be delayed until InsertPreparedUndo(). But if we solve
the above problem, most of this might already be solved.

Basically, that means
- the caller should call PreparedUndoInsert before acquiring table
page content lock right? because the PreparedUndoInsert just compute
the size, allocate the space and pin+lock the buffers and for pinning
the buffers we must compute the size and allocate the space using undo
storage layer.

I don't think we can normally pin the undo buffers properly at that
stage. Without knowing the correct contents of the table page - which we
can't know without holding some form of lock preventing modifications -
we can't know how big our undo records are going to be. And we can't
just have buffers that don't exist on disk in shared memory, and we
don't want to allocate undo that we then don't need. So I think what
we'd have to do at that stage, is to "pre-allocate" buffers for the
maximum amount of UNDO needed, but mark the associated bufferdesc as not
yet valid. These buffers would have a pincount > 0, but BM_TAG_VALID
would not be set.

So at the start of a function that will need to insert undo we'd need to
pre-reserve the maximum number of buffers we could potentially
need. That reservation stage would

Maybe we can provide an interface where the caller will input the max
prepared undo (maybe BeginUndoRecordInsert) and based on that we can
compute the max number of buffers we could potentially need for this
particular operation. Most of the operation insert/update/delete will
need 1 or 2 undo record so we can avoid pinning very high number of
buffers in most of the cases. Currently, only for the multi-insert
implementation of zheap we might need multiple undo-records (1 undo
per range of record).

a) pin the page with the current end of the undo
b) if needed pin the page of older undo that we need to update (e.g. to
update the next pointer)
c) perform clock sweep etc to acquire (find or create) enough clean to
hold the maximum amount of undo needed. These buffers would be marked
as !BM_TAG_VALID | BUF_REFCOUNT_ONE.

I assume that we'd make a) cheap by keeping it pinned for undo logs that
a backend is actively attached to. b) should only be needed once in a
transaction, so it's not too bad. c) we'd probably need to amortize
across multiple undo insertions, by keeping the unused buffers pinned
until the end of the transaction.

I assume that having the infrastructure c) might also make some code
for already in postgres easier. There's obviously some issues around
guaranteeing that the maximum number of such buffers isn't high.

- So basically, if we delay the lock till InsertPreparedUndo and call
PrepareUndoInsert before acquiring table page content lock this
problem is solved?

Although I haven't yet analyzed the AM specific part that whether it's
always possible to call the PrepareUndoInsert(basically getting all
the undo record ready) before the page content lock. But, I am sure
that won't be much difficult part.

I think that is somewhere between not possible, and so expensive in a
lot of cases that we'd not want to do it anyway. You'd at leasthave to
first acquire a content lock on the page, mark the target tuple as
locked, then unlock the page, reserve undo, lock the table page,
actually update it.

- When reading an undo record, the whole stage of UnpackUndoData()
reading data into a the UndoPackContext is omitted, reading directly
into the UnpackedUndoRecord. That removes one further copy of the
record format.

So we will read member by member to UnpackedUndoRecord? because in
context we have at least a few headers packed and we can memcpy one
header at a time like UndoRecordHeader, UndoRecordBlock.

Well, right now you then copy them again later, so not much is gained by
that (although that later copy can happen without the content lock
held). As I think I suggested before, I suspect that the best way would
be to just memcpy() the data from the page(s) into an appropriately
sized buffer with the content lock held, and then perform unpacking
directly into UnpackedUndoRecord. Especially with the bulk API that will
avoid having to do much work with locks held, and reduce memory usage by
only unpacking the record(s) in a batch that are currently being looked
at.

ok.

But that just a few of them so if we copy field by field in the
UnpackedUndoRecord then we can get rid of copying in context then copy
it back to the UnpackedUndoRecord. Is this is what in your mind or
you want to store these structures (UndoRecordHeader, UndoRecordBlock)
directly into UnpackedUndoRecord?

I at the moment see no reason not to?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#246Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#241)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 2:48 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

I think that batch reading should just copy the underlying data into a
char* buffer. Only the records that currently are being used by
higher layers should get exploded into an unpacked record. That will
reduce memory usage quite noticably (and I suspect it also drastically
reduce the overhead due to a large context with a lot of small
allocations that then get individually freed).

Ok, I got your idea. I will analyze it further and work on this if
there is no problem.

I think there is one problem that currently while unpacking the undo
record if the record is compressed (i.e. some of the fields does not
exist in the record) then we read those fields from the first record
on the page. But, if we just memcpy the undo pages to the buffers and
delay the unpacking whenever it's needed seems that we would need to
know the page boundary and also we need to know the offset of the
first complete record on the page from where we can get that
information (which is currently in undo page header).

As of now even if we leave this issue apart I am not very clear what
benefit you are seeing in the way you are describing compared to the
way I am doing it now?

a) Is it the multiple palloc? If so then we can allocate memory at
once and flatten the undo records in that. Earlier, I was doing that
but we need to align each unpacked undo record so that we can access
them directly and based on Robert's suggestion I have modified it to
multiple palloc.
b) Is it the memory size problem that the unpack undo record will take
more memory compared to the packed record?
c) Do you think that we will not need to unpack all the records? But,
I think eventually, at the higher level we will have to unpack all the
undo records ( I understand that it will be one at a time)

Or am I completely missing something here?

That will make the

sorting of undo a bit more CPU inefficient, because individual records
will need to be partially unpacked for comparison, but I think that's
going to be a far smaller loss than the win.

Right.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#247Thomas Munro
thomas.munro@gmail.com
In reply to: Amit Kapila (#155)
Re: POC: Cleaning up orphaned files using undo logs

Hi Amit,

I've combined three of your messages into one below, and responded
inline. New patch set to follow shortly, with the fixes listed below
(and others from other reviewers).

On Wed, Jul 24, 2019 at 9:15 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

0003-Add-undo-log-manager.patch
1.
allocate_empty_undo_segment()

...

+ /* create two parents up if not exist */
+ parentdir = pstrdup(undo_path);
+ get_parent_directory(parentdir);
+ get_parent_directory(parentdir);
+ /* Can't create parent and it doesn't already exist? */
+ if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)

All of this code is almost same as we have code in
TablespaceCreateDbspace still we have small differences like here you
are using mkdir instead of MakePGDirectory which as far as I can see
use similar permissions for creating directory. Also, it checks
whether the directory exists before trying to create it. Is there a
reason why we need to do a few things differently here if not, they
can both the places use one common function?

Right, MakePGDirectory() arrived in commit da9b580d8990, and should
probably be used everywhere we create directories under pgdata.
Fixed.

Yeah, I think we could just use TablespaceCreateDbspace() for this, if
we are OK with teaching GetDatabasePath() and GetRelationPath() about
how to make special undo paths, OR we are OK with just using
"standard" paths, where undo files just live under database 9 (instead
of the special "undo" directory). I stopped using a "9" directory in
earlier versions because undo moved to a separate namespace when we
agreed to use an extra discriminator in buffer tags and so forth; now
that we're back to using database number 9, the question of whether to
reflect that on the filesystem is back. I have had some trouble
deciding which parts of the system should treat undo logs as some kind
of 'relation' (and the SLRU project will have to tackle the same
questions). I'll think about that some more before making the change.

2.
allocate_empty_undo_segment()
{
..
..
/* Flush the contents of the file to disk before the next checkpoint. */
+ undofile_request_sync(logno, end / UndoLogSegmentSize, tablespace);
..
}

The comment in allocate_empty_undo_segment indicates that the code
wants to flush before checkpoint, but the actual function tries to
register the request with checkpointer. Shouldn't this be similar to
XLogFileInit where we use pg_fsync to flush the contents immediately?

I responded to the general question about when we sync files in an
earlier email. I've updated the comments to make it clearer that it's
handing the work off, not doing it now.

Another thing is that recently in commit 475861b261 (commit by you),
we have introduced a mechanism to not fill the files with zero's for
certain filesystems like ZFS. Do we want similar behavior for undo
files?

Good point. I will create a separate thread to discuss how the
creation of a central file allocation routine (possibly with a GUC),
and see if we can come up with something reusable for this, but
independently committable.

3.
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
+{
+ UndoLogSlot *slot;
+ size_t end;
+
+ slot = find_undo_log_slot(logno, false);
+
+ /* TODO review interlocking */
+
+ Assert(slot != NULL);
+ Assert(slot->meta.end % UndoLogSegmentSize == 0);
+ Assert(new_end % UndoLogSegmentSize == 0);
+ Assert(InRecovery ||
+    CurrentSession->attached_undo_slots[slot->meta.category] == slot);

Can you write some comments explaining the above Asserts? Also, can
you explain what interlocking issues are you worried about here?

I added comments about the assertions. I will come back to the
interlocking in another message, which I've now addressed (alluded to
below as well).

4.
while (end < new_end)
+ {
+ allocate_empty_undo_segment(logno, slot->meta.tablespace, end);
+ end += UndoLogSegmentSize;
+ }
+
+ /* Flush the directory entries before next checkpoint. */
+ undofile_request_sync_dir(slot->meta.tablespace);

I see that at two places after allocating empty undo segment, the
patch performs undofile_request_sync_dir whereas it doesn't perform
the same in UndoLogNewSegment? Is there a reason for the same or is it
missed from one of the places?

You're right. Done.

5.
+static void
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
{
..
/*
+ * We didn't need to acquire the mutex to read 'end' above because only
+ * we write to it.  But we need the mutex to update it, because the
+ * checkpointer might read it concurrently.

Is this assumption correct? It seems patch also modified
slot->meta.end during discard in function UndoLogDiscard. I am
referring below code:

+UndoLogDiscard(UndoRecPtr discard_point, TransactionId xid)
{
..
+ /* Update shmem to show the new discard and end pointers. */
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ slot->meta.discard = discard;
+ slot->meta.end = end;
+ LWLockRelease(&slot->mutex);
..
}

Yeah, the assumption was wrong, and that's what that other TODO note
about interlocking was referring to. I have redesigned this so that
there is a separate per-undo log extend_lock that allows
UndoLogDiscard() (in a background worker or superuser command) and
UndoLogAllocate() to serialise extension of the undo log. Hopefully
foreground processes don't often have to wait (a discard worker will
recycle segments fast enough), but if it ever does have to wait, it's
waiting for another backend to rename() a fully allocated file, which
is hopefully still better than writing a load of zeroes into a new
file.

6.
extend_undo_log()
{
..
..
if (!InRecovery)
+ {
+ xl_undolog_extend xlrec;
+ XLogRecPtr ptr;
+
+ xlrec.logno = logno;
+ xlrec.end = end;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+ ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+ XLogFlush(ptr);
+ }

It is not obvious to me why we need to perform XLogFlush here, can you explain?

It's not needed, and I've removed it. The important thing here is
that we insert the record after creating the files and telling the
checkpointer to flush them; there's no benefit to flushing the WAL
record.

There are three crash recovery possibilities: (1) We recover from a
checkpoint after this record, and the files are already durable, (2)
we recover from a checkpoint before this record, replay this record,
the file(s) may or may not be present but we'll tolerate them if they
are and overwrite, (3) we recover from a checkpoint before this record
was written, and this WAL record is never replayed because it wasn't
flushed, and then there may or may not be some orphaned files but
we'll eventually try to create files with the same names as we extend
the undo log and tolerate their existence.

7.
+attach_undo_log(UndoLogCategory category, Oid tablespace)
{
..
if (candidate->meta.tablespace == tablespace)
+ {
+ logno = *place;
+ slot = candidate;
+ *place = candidate->next_free;
+ break;
+ }

Here, the code is breaking from the loop, so why do we need to set
*place? Am I missing something obvious?

(See further down).

8.
+ /* WAL-log the creation of this new undo log. */
+ {
+ xl_undolog_create xlrec;
+
+ xlrec.logno = logno;
+ xlrec.tablespace = slot->meta.tablespace;
+ xlrec.category = slot->meta.category;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));

Here and in most other places in this patch you are using
sizeof(xlrec) for xlog static data. However, as far as I know in
other places in the code we define the size using offset of the last
parameter of corresponding structure to avoid any inconsistency in WAL
record size across different platforms. Is there a reason to go
differently with this patch? See below one for example:

typedef struct xl_hash_add_ovfl_page
{
uint16 bmsize;
bool bmpage_found;
} xl_hash_add_ovfl_page;

#define SizeOfHashAddOvflPage
\
(offsetof(xl_hash_add_ovfl_page, bmpage_found) + sizeof(bool))

I see. Apparently we don't always do that:

tmunro@dogmatix $ git grep RegisterData | grep sizeof | wc -l
60
tmunro@dogmatix $ git grep RegisterData | grep Size | wc -l
63

I've now done it for all of these structs so that we trim the padding
in some cases, even though in some cases it'll make no difference.

9.
+static void
+undolog_xlog_create(XLogReaderState *record)
+{
+ xl_undolog_create *xlrec = (xl_undolog_create *) XLogRecGetData(record);
+ UndoLogSlot *slot;
+
+ /* Create meta-data space in shared memory. */
+ LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+
+ /* TODO: assert that it doesn't exist already? */
+
+ slot = allocate_undo_log_slot();
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);

Why do we need to acquire locks during recovery?

Mostly because allocate_undo_log_slot() asserts that the lock is held.
We probably don't need this in recovery but it doesn't seem like a
problem, it'll never be contended.

10.
I think UndoLogAllocate can leak allocation of slots. It first
allocates the slot for a new log from the free pool in there is no
existing slot/log, writes a WAL record and then at a later point of
time it actually creates the required physical space in the log via
extend_undo_log which also writes a separate WAL. Now, if there is a
error between these two operations, then we will have a redundant slot
allocated. What if there are repeated errors for similar thing from
multiple backends after which system crashes. Now, after restart, we
will allocate multiple slots for different lognos which don't have any
actual (physical) logs. This might not be a big problem in practice
because the chances of error between two operations are less, but
can't we delay the WAL logging for allocation of a slot for a new log.

I don't think it leaks anything, and the undo log is not redundant,
it's free/available. An undo log is allowed to have no space
allocated (discard == end). In fact that can happen a few different
ways: for example after crash recovery, we unlink all files belonging
to unlogged undo logs by scanning the filesystem, and set discard ==
end, on a segment boundary. That's also the way new undo logs are
born, and I think that's OK. If you crash and recover up to the point
the undo log creation was WAL-logged, you'll now have a log with no
space, and then the first person to try to allocate something in it
will extend it (= create the file, move the end pointer) in the
process of allocating space.

11.
+UndoLogAllocate()
{
..
..
+ /*
+ * Maintain our tracking of the and the previous transaction start
+ * locations.
+ */
+ if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+ {
+ slot->meta.unlogged.last_xact_start =
+ slot->meta.unlogged.this_xact_start;
+ slot->meta.unlogged.this_xact_start = slot->meta.unlogged.insert;
+ }

".. of the and the ..", after first the, something is missing.

Fixed.

12.
UndoLogAllocate()
{
..
..
+ /*
+ * We don't need to acquire log->mutex to read log->meta.insert and
+ * log->meta.end, because this backend is the only one that can
+ * modify them.
+ */
+ if (unlikely(new_insert > slot->meta.end))

I might be confused but slot->meta.end is modified by discard process
also, so how is it safe? If so, may be adding a comment to explain
the same would be good. Also, I think in the comments log should be
replaced with the slot.

Right, now fixed.

I fixed s/log->/slot->/ here and elsewhere in comments.

13.
UndoLogAllocate()
{
..
+ /* This undo log is entirely full.  Get a new one. */
+ if (logxid == GetTopTransactionId())
+ {
+ /*
+ * If the same transaction is split over two undo logs then
+ * store the previous log number in new log.  See detailed
+ * comments in undorecord.c file header.
+ */
..
}

The undorecord.c should be renamed to undoaccess.c

Fixed.

14.
UndoLogAllocate()
{
..
+ if (logxid != GetTopTransactionId())
+ {
+ /*
+ * While we have the lock, check if we have been forcibly detached by
+ * DROP TABLESPACE.  That can only happen between transactions (see
+ * DropUndoLogsInsTablespace()).
+ */

/DropUndoLogsInsTablespace/DropUndoLogsInTablespace

Fixed.

15.
UndoLogSegmentPath()
{
..
/*
+ * Build the path from log number and offset.  The pathname is the
+ * UndoRecPtr of the first byte in the segment in hexadecimal, with a
+ * period inserted between the components.
+ */
+ snprintf(path, MAXPGPATH, "%s/%06X.%010zX", dir, logno,
+ segno * UndoLogSegmentSize);
..
}

a. It is not very clear from the above code why are we multiplying
segno with UndoLogSegmentSize? I see that many of the callers pass
segno as segno/UndoLogSegmentSize. Won't it be better if the caller
take care of passing correct value of segno?

We want "the UndoRecPtr of the first byte in the segment [...] with a
period inserted between the components". Seems clear? So undo log 7,
segno 0 will be 000007.0000000000 and undo log 7, segno 1 will be
000007.0000100000, and UndoRecPtr of its first byte is at
0000070000100000 (so when you're looking at pg_stat_undo_logs or
undoinspect() or any other representation of undo record pointers, you
can easily see which files they are referring to). It's true that we
could pass in the offset of the first byte, instead of the segment
number, but some other callers have a segment number (see undofile.c).

b. In the comment above, instead of offset, shouldn't there be segment number.

No, segno * segment size == offset (the offset part of an UndoRecPtr
is the lower 48 bits; the upper 24 bits are the undo log number).

16. UndoLogGetLastXactStartPoint is not used any where. I think this
was required in previous version of patchset, now, we can remove it.

Done, thanks.

17.
Discussion: /messages/by-id/CAEepm=2EqROYJ_xYz4v5kfr4b0qw_Lq_6Pe8RTEC8rx3upWsSQ@mail.gmail.com

This discussion link seems to be from old discussion/thread, not this one.

Will reference this one.

0019-Add-developer-documentation-for-the-undo-log-storage
18.
+each undo log, a set of meta-data properties is tracked:
+tracked, including:
+
+* the tablespace that holds its segment files
+* the persistence level (permanent, unlogged or temporary)

Here, don't we want to refer to UndoLogCategory rather than
persistence level? "tracked, including:" seems bit confusing.

Fixed here and elsewhere.

0020-Add-user-facing-documentation-for-undo-logs
19.
<row>
+     <entry><structfield>persistence</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Persistence level of data stored in this undo log; one of
+      <literal>permanent</literal>, <literal>unlogged</literal> or
+      <literal>temporary</literal>.</entry>
+    </row>

Don't we want to cover the new (shared) undolog category here?

Done (though I have mixed feelings about this shared category; more on
that soon).

On Thu, Jul 25, 2019 at 12:14 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Jul 24, 2019 at 2:45 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Jul 18, 2019 at 5:10 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

7.
+attach_undo_log(UndoLogCategory category, Oid tablespace)
{
..
if (candidate->meta.tablespace == tablespace)
+ {
+ logno = *place;
+ slot = candidate;
+ *place = candidate->next_free;
+ break;
+ }

Here, the code is breaking from the loop, so why do we need to set
*place? Am I missing something obvious?

I think I know what I was missing. It seems here you are removing an
element from the freelist.

Right.

One point related to detach_current_undo_log.

+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ slot->pid = InvalidPid;
+ slot->meta.unlogged.xid = InvalidTransactionId;
+ if (full)
+ slot->meta.status = UNDO_LOG_STATUS_FULL;
+ LWLockRelease(&slot->mutex);

If I read the comments in structure UndoLogMetaData, it is mentioned
that 'status' is changed by explicit WAL record whereas there is no
WAL record in code to change the status. I see the problem as well if
we don't WAL log this change. Suppose after changing the status of
this log, we allocate a new log and insert some records in that log as
well for the same transaction for which we have inserted records in
the log which we just marked as FULL. Now, here we form the link
between two logs as the same transaction has overflowed into a new
log. Say, we crash after this. Now, after recovery the log won't be
marked as FULL which means there is a chance that it can be used for
some other transaction, if that happens, then our link for a
transaction spanning to different log will break and we won't be able
to access the data in another log. In short, I think it is important
to WAL log this status change unless I am missing something.

I thought it was OK to relax that and was going to just fix the
comment, but the case you describe seems important. It seems we could
either by WAL-logging the status changes as you said, or make sure the
links have enough information to handle that. I'll think about that
some more.

On Thu, Jul 25, 2019 at 10:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:

Some more review of the same patch:
1.
+typedef struct UndoLogSharedData
+{
+ UndoLogNumber free_lists[UndoLogCategories];
+ UndoLogNumber low_logno;

What is the use of low_logno? I don't see anywhere in the code this
being assigned any value. Is it for some future use?

Yeah, fixed, and now used. It reduces the need for 'negative cache
entries' after a backend has been running for a very long time.

2.
+void
+CheckPointUndoLogs(XLogRecPtr checkPointRedo, XLogRecPtr priorCheckPointRedo)
{
..
+ /* Compute header checksum. */
+ INIT_CRC32C(crc);
+ COMP_CRC32C(crc, &UndoLogShared->low_logno, sizeof(UndoLogShared->low_logno));
+ COMP_CRC32C(crc, &UndoLogShared->next_logno,
sizeof(UndoLogShared->next_logno));
+ COMP_CRC32C(crc, &num_logs, sizeof(num_logs));
+ FIN_CRC32C(crc);
+
+ /* Write out the number of active logs + crc. */
+ if ((write(fd, &UndoLogShared->low_logno,
sizeof(UndoLogShared->low_logno)) != sizeof(UndoLogShared->low_logno))
||
+ (write(fd, &UndoLogShared->next_logno,
sizeof(UndoLogShared->next_logno)) !=
sizeof(UndoLogShared->next_logno)) ||

Is it safe to read UndoLogShared without UndoLogLock? All other
places accessing UndoLogShared uses UndoLogLock, so if this usage is
safe, maybe it is better to add a comment.

Fixed for next_logno. And the other one, low_logno is no longer
written to disk (it can be computed).

3.
UndoLogAllocateInRecovery()
{
..
/*
+ * Otherwise we need to do our own transaction tracking
+ * whenever we see a new xid, to match the logic in
+ * UndoLogAllocate().
+ */
+ if (xid != slot->meta.unlogged.xid)
+ {
+ slot->meta.unlogged.xid = xid;
+ if (slot->meta.unlogged.this_xact_start != slot->meta.unlogged.insert)
+ slot->meta.unlogged.last_xact_start =
+ slot->meta.unlogged.this_xact_start;
+ slot->meta.unlogged.this_xact_start =
+ slot->meta.unlogged.insert;

The code doesn't follow the comment. In UndoLogAllocate, both
last_xact_start and this_xact_start are assigned in if block, so the
should be the case here.

True, in "do" I only did the assignment if the values were different,
and in "redo" I did the assignment even if they were the same, which
has the same effect, but is indeed distracting. I've made them the
same.

4.
UndoLogAllocateInRecovery()
{
..
+ /*
+ * Just as in UndoLogAllocate(), the caller may be extending an existing
+ * allocation before committing with UndoLogAdvance().
+ */
+ if (context->try_location != InvalidUndoRecPtr)
+ {
..
}

I am not sure how will this work because unlike UndoLogAllocate, this
function doesn't set try_location initially. It will be set later by
UndoLogAdvance which can easily go wrong because that doesn't include
UndoLogBlockHeaderSize.

Hmm, yeah, I need to look into that some more. The 'advance' function
does include consider header bytes though.

I do admit that this code is very hard to follow. It got that way by
being developed before the 'context' existed. It needs to be
rewritten in a much clearer way; I'm going to do that.

5.
+UndoLogAdvance(UndoLogAllocContext *context, size_t size)
+{
+ context->try_location = UndoLogOffsetPlusUsableBytes(context->try_location,
+ size);
+}

Here, you are using UndoRecPtr whereas UndoLogOffsetPlusUsableBytes
expects offset.

Yeah that is ugly. I created UndoRecPtrPlusUsableBytes().

6.
UndoLogAllocateInRecovery()
{
..
+ /*
+ * At this stage we should have an undo log that can handle this
+ * allocation.  If we don't, something is screwed up.
+ */
+ if (UndoLogOffsetPlusUsableBytes(slot->meta.unlogged.insert, size) >
slot->meta.end)
+ elog(ERROR,
+ "cannot allocate %d bytes in undo log %d",
+ (int) size, slot->logno);
..
}

Similar to point-5, here you are using a pointer instead of offset.

Fixed.

7.
UndoLogAllocateInRecovery()
{
..
+ /* We found a reference to a different (or first) undo log. */
+ slot = find_undo_log_slot(logno, false);
..
+ /* TODO: check locking against undo log slot recycling? */
..
}

I think it is better to have an Assert here that slot can't be NULL.
AFAICS, slot can't be NULL unless there is some bug. I don't
understand this 'TODO' comment.

Yeah. I just removed it. "slot" was already dereferenced above so an
assertion that it's not NULL is too late to have any useful effect.

8.
+ {
+ {"undo_tablespaces", PGC_USERSET,
CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the
tablespace(s) to use for undo logs."),
+ NULL,
+
GUC_LIST_INPUT | GUC_LIST_QUOTE
+ },
+
&undo_tablespaces,
+ "",
+ check_undo_tablespaces,
assign_undo_tablespaces, NULL
+ },

It seems you need to update variable_is_guc_list_quote for this variable.

Huh. Right. Done.

9.
+extend_undo_log(UndoLogNumber logno, UndoLogOffset new_end)
{
..
+ if (!InRecovery)
+ {
+ xl_undolog_extend xlrec;
+ XLogRecPtr ptr;
+
+ xlrec.logno = logno;
+ xlrec.end = end;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+ ptr = XLogInsert(RM_UNDOLOG_ID, XLOG_UNDOLOG_EXTEND);
+ XLogFlush(ptr);
+ }
..
}

Do we need it for temporary/unlogged persistence level? Similarly,
there is a WAL logging in attach_undo_log which I can't understand why
it would be required for temporary/unlogged persistence levels.

You're right, if we crash we don't care about any data in
temporary/unlogged undo logs, since that data belongs to
temporary/unlogged zheap (etc) tables. We destroy all of their files
at startup in ResetUndoLogs(). So therefore we might as well not both
to log the extension stuff.

Done.

10.
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
{
..
+ oid = get_tablespace_oid(name, true);
+ if (oid == InvalidOid)
..
}

Do we need to check permissions to see if the current user is allowed
to create in this tablespace?

Yeah, right. I added a pg_tablespace_aclcheck() check

postgres=> set undo_tablespaces = ts1;
SET
postgres=> create table t ();
ERROR: permission denied for tablespace ts1

11.
+static bool
+choose_undo_tablespace(bool force_detach, Oid *tablespace)
+{
+ char   *rawname;
+ List   *namelist;
+ bool
need_to_unlock;
+ int length;
+ int
i;
+
+ /* We need a modifiable copy of string. */
+ rawname =
pstrdup(undo_tablespaces);

I don't see the usage of rawname outside this function, isn't it
better to free it? I understand that this function won't be called
frequently enough to matter, but still, there is some theoretical
danger if the user continuously changes undo_tablespaces.

Fixed by freeing both rawname and namelist.

12.
+find_undo_log_slot(UndoLogNumber logno, bool locked)
{
..
+ * TODO: We could track the lowest known undo log
number, to reduce
+ * the negative cache entry bloat.
+ */
+ if (result == NULL)
+ {
..
}

Do we have any mechanism to clear this bloat or will it stay till the
end of the session? If it is later, then I think it might be good to
take care of this TODO. I think this is not a blocker, but good to
have kind of stuff.

I did the TODO, so now we can drop negative cache entries below
low_logno from the cache. There are probably more things we could do
here to be more aggressive but that's a start.

13.
+static void
+allocate_empty_undo_segment(UndoLogNumber logno, Oid tablespace,
+ UndoLogOffset end)
{
..
}

What will happen if the transaction creating undolog segment rolls
back? Do we want to have pendingDeletes stuff as we have for normal
relation files? This might also help in clearing the shared memory
state (undo log slots) if any.

No, that's non-transactional. The undo log segment remains created,
just like various other things stay permanently even if the
transaction that created them aborts (relation extension, btree
splits, ...).

--
Thomas Munro
https://enterprisedb.com

#248Thomas Munro
thomas.munro@gmail.com
In reply to: Kuntal Ghosh (#159)
Re: POC: Cleaning up orphaned files using undo logs

Hi Kuntal,

On Thu, Jul 25, 2019 at 5:40 PM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

Here are some review comments on 0003-Add-undo-log-manager.patch. I've
tried to avoid duplicate comments as much as possible.

Thanks! Replies inline. I'll be posting a new patch set shortly with
these and other fixes.

1. In UndoLogAllocate,
+ * time this backend as needed to write to an undo log at all or because
s/as/has

Fixed.

+ * Maintain our tracking of the and the previous transaction start
Do you mean current log's transaction start as well?

Right, fixed.

2. In UndoLogAllocateInRecovery,
we try to find the current log from the first undo buffer. So, after a
log switch, we always have to register at least one buffer from the
current undo log first. If we're updating something in the previous
log, the respective buffer should be registered after that. I think we
should document this in the comments.

I'm not sure I understand. Is this working correctly today?

3. In UndoLogGetOldestRecord(UndoLogNumber logno, bool *full),
it seems the 'full' parameter is not used anywhere. Do we still need this?

+ /* It's been recycled. SO it must have been entirely discarded. */
s/SO/So

Fixed.

4. In CleanUpUndoCheckPointFiles,
we can emit a debug2 message with something similar to : 'removed
unreachable undo metadata files'

Done.

+ if (unlink(path) != 0)
+ elog(ERROR, "could not unlink file \"%s\": %m", path);
according to my observation, whenever we deal with a file operation,
we usually emit a ereport message with errcode_for_file_access().
Should we change it to ereport? There are other file operations as
well including read(), OpenTransientFile() etc.

Done.

5. In CheckPointUndoLogs,
+ /* Capture snapshot while holding each mutex. */
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ serialized[num_logs++] = slot->meta;
+ LWLockRelease(&slot->mutex);
why do we need an exclusive lock to read something from the slot? A
share lock seems to be sufficient.

OK.

pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_SYNC) is called
after pgstat_report_wait_start(WAIT_EVENT_UNDO_CHECKPOINT_WRITE)
without calling pgstat_report_wait_end(). I think you've done the
same to avoid an extra function call. But, it differs from other
places in the PG code. Perhaps, we should follow this approach
everywhere.

Ok, changed.

6. In StartupUndoLogs,
+ if (fd < 0)
+ elog(ERROR, "cannot open undo checkpoint snapshot \"%s\": %m", path);
assuming your agreement upon changing above elog to ereport, the
message should be more user friendly. May be something like 'cannot
open pg_undo file'.

Done.

+ if ((size = read(fd, &slot->meta, sizeof(slot->meta))) != sizeof(slot->meta))
The usage of size of doesn't look like a problem. But, we can save
some extra padding bytes at the end if we use (offsetof + sizeof)
approach similar to other places in PG.

It current ends in a 64 bit value, so there is no padding here.

7. In free_undo_log_slot,
+ /*
+ * When removing an undo log from a slot in shared memory, we acquire
+ * UndoLogLock, log->mutex and log->discard_lock, so that other code can
+ * hold any one of those locks to prevent the slot from being recycled.
+ */
+ LWLockAcquire(UndoLogLock, LW_EXCLUSIVE);
+ LWLockAcquire(&slot->mutex, LW_EXCLUSIVE);
+ Assert(slot->logno != InvalidUndoLogNumber);
+ slot->logno = InvalidUndoLogNumber;
+ memset(&slot->meta, 0, sizeof(slot->meta));
+ LWLockRelease(&slot->mutex);
+ LWLockRelease(UndoLogLock);
you've not taken the discard_lock as mentioned in the comment.

Right, I was half-way between two different ideas about how that
interlocking should work, but I have straightened this out now, and
will write about the overall locking model separately.

8. In find_undo_log_slot,
+ * 1. If the calling code knows that it is attached to this lock or is the
s/lock/slot

Fixed.

BTW I am experimenting with macros that would actually make assertions
about those programming rules.

+ * 2.  All other code should acquire log->mutex before accessing any members,
+ * and after doing so, check that the logno hasn't moved.  If it is not, the
+ * entire undo log must be assumed to be discarded (as if this function
+ * returned NULL) and the caller must behave accordingly.
Perhaps, you meant '..check that the logno remains same. If it is not..'.

Fixed.

+ /*
+ * If we didn't find it, then it must already have been entirely
+ * discarded.  We create a negative cache entry so that we can answer
+ * this question quickly next time.
+ *
+ * TODO: We could track the lowest known undo log number, to reduce
+ * the negative cache entry bloat.
+ */
This is an interesting thought. But, I'm wondering how we are going to
search the discarded logno in the simple hash. I guess that's why it's
in the TODO list.

Done. Each backend tracks its idea of the lowest undo log that
exists. There is a shared low_logno that is recomputed whenever a
slot is freed (ie a log is entirely discarded). Whenever a backend
sees that its own value is too low, it walks forward dropping cache
entries. Perhaps this could be made more proactive later by using
sinval, but I didn't look into that.

9. In attach_undo_log,
+ * For now we have a simple linked list of unattached undo logs for each
+ * persistence level.  We'll grovel though it to find something for the
+ * tablespace you asked for.  If you're not using multiple tablespaces
s/though/through

Fixed.

+ if (slot == NULL)
+ {
+ if (UndoLogShared->next_logno > MaxUndoLogNumber)
+ {
+ /*
+ * You've used up all 16 exabytes of undo log addressing space.
+ * This is a difficult state to reach using only 16 exabytes of
+ * WAL.
+ */
+ elog(ERROR, "undo log address space exhausted");
+ }
looks like a potential unlikely() condition.

Done. Yeah, actually every branch containing an unconditional elog()
at ERROR or higher (or maybe even lower) must surely be considered
unlikely, and it'd be nice to tell the leading compilers about that,
but the last thread about that hasn't made it as far as a useful patch
for some technical reason that didn't seem fatal to the concept, IIRC.
I'd be curious to know what sort effect that sort of rule would have
on the whole tree, in terms of code locality, even if you have to hack
the compiler to find out...

--
Thomas Munro
https://enterprisedb.com

#249Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#246)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-16 09:44:25 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 2:48 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

I think that batch reading should just copy the underlying data into a
char* buffer. Only the records that currently are being used by
higher layers should get exploded into an unpacked record. That will
reduce memory usage quite noticably (and I suspect it also drastically
reduce the overhead due to a large context with a lot of small
allocations that then get individually freed).

Ok, I got your idea. I will analyze it further and work on this if
there is no problem.

I think there is one problem that currently while unpacking the undo
record if the record is compressed (i.e. some of the fields does not
exist in the record) then we read those fields from the first record
on the page. But, if we just memcpy the undo pages to the buffers and
delay the unpacking whenever it's needed seems that we would need to
know the page boundary and also we need to know the offset of the
first complete record on the page from where we can get that
information (which is currently in undo page header).

I don't understand why that's a problem?

As of now even if we leave this issue apart I am not very clear what
benefit you are seeing in the way you are describing compared to the
way I am doing it now?

a) Is it the multiple palloc? If so then we can allocate memory at
once and flatten the undo records in that. Earlier, I was doing that
but we need to align each unpacked undo record so that we can access
them directly and based on Robert's suggestion I have modified it to
multiple palloc.

Part of it.

b) Is it the memory size problem that the unpack undo record will take
more memory compared to the packed record?

Part of it.

c) Do you think that we will not need to unpack all the records? But,
I think eventually, at the higher level we will have to unpack all the
undo records ( I understand that it will be one at a time)

Part of it. There's a *huge* difference between having a few hundred to
thousand unpacked records, each consisting of several independent
allocations, in memory and having one large block containing all
packed records in a batch, and a few allocations for the few unpacked
records that need to exist.

There's also d) we don't need separate tiny memory copies while holding
buffer locks etc.

Greetings,

Andres Freund

#250Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#249)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Aug 16, 2019 at 10:56 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-16 09:44:25 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 2:48 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

I think that batch reading should just copy the underlying data into a
char* buffer. Only the records that currently are being used by
higher layers should get exploded into an unpacked record. That will
reduce memory usage quite noticably (and I suspect it also drastically
reduce the overhead due to a large context with a lot of small
allocations that then get individually freed).

Ok, I got your idea. I will analyze it further and work on this if
there is no problem.

I think there is one problem that currently while unpacking the undo
record if the record is compressed (i.e. some of the fields does not
exist in the record) then we read those fields from the first record
on the page. But, if we just memcpy the undo pages to the buffers and
delay the unpacking whenever it's needed seems that we would need to
know the page boundary and also we need to know the offset of the
first complete record on the page from where we can get that
information (which is currently in undo page header).

I don't understand why that's a problem?

Okay, I was assuming that we will be only copying data part not
complete page including the page header. If we copy the page header
as well we might be able to unpack the compressed record as well.

As of now even if we leave this issue apart I am not very clear what
benefit you are seeing in the way you are describing compared to the
way I am doing it now?

a) Is it the multiple palloc? If so then we can allocate memory at
once and flatten the undo records in that. Earlier, I was doing that
but we need to align each unpacked undo record so that we can access
them directly and based on Robert's suggestion I have modified it to
multiple palloc.

Part of it.

b) Is it the memory size problem that the unpack undo record will take
more memory compared to the packed record?

Part of it.

c) Do you think that we will not need to unpack all the records? But,
I think eventually, at the higher level we will have to unpack all the
undo records ( I understand that it will be one at a time)

Part of it. There's a *huge* difference between having a few hundred to
thousand unpacked records, each consisting of several independent
allocations, in memory and having one large block containing all
packed records in a batch, and a few allocations for the few unpacked
records that need to exist.

There's also d) we don't need separate tiny memory copies while holding
buffer locks etc.

Yeah, that too. Yet another problem could be that how are we going to
process those record? Because for that we need to know all the undo
record pointers between start_urecptr and the end_urecptr right? we
just have the big memory chunk and we have no idea how many undo
records are there and what are their undo record pointers. And
without knowing that information, I am unable to imagine how we are
going to sort them based on block number.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#251Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#243)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 12:39 PM Andres Freund <andres@anarazel.de> wrote:

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused. I'll repeat my previous
arguments on-list:

1. It's absolutely fine to just put a limit on this, because the
higher-level facilities that use this shouldn't be doing a single
WAL-logged operation that touches a zillion buffers. We have been
careful to avoid having WAL-logged operations touch an unbounded
number of buffers in plenty of other places, like the btree code, and
we are going to have to be similarly careful here for multiple
reasons, deadlock avoidance being one. So, saying, "hey, you're going
to lock an unlimited number of buffers" is a straw man. We aren't.
We can't.

2. The write-ahead logging protocol says that you're supposed to lock
all the buffers at once. See src/backend/access/transam/README. If
you want to go patch that file, then this patch can follow whatever
the locking rules in the patched version are. But until then, the
patch should follow *the actual rules* not some other protocol based
on a hand-wavy explanation in an email someplace. Otherwise, you've
got the same sort of undocumented disaster-waiting-to-happen that you
keep complaining about in other parts of this patch. We need fewer of
those, not more!

3. There is no reason to care about all of the buffers being locked at
once, because they are not unlimited in number (see point #1) and
nobody else is looking at them anyway (see the final sentence of what
I quoted above).

I think we are, or ought to be, talking about locking 2 (or maybe in
rare cases 3 or 4) undo buffers in connection with a single WAL
record. If we're talking about more than that, then I think the
higher-level code needs to be changed. If we're talking about that
many, then we don't need to be clever. We can just do the standard
thing that the rest of the system does, and it will be fine just like
it is everywhere else.

Suppose you insert one record for the transaction which split in
block1 and 2. Now, before this block is actually going to the disk
the transaction committed and become all visible the undo logs are
discarded. It's possible that block 1 is completely discarded but
block 2 is not because it might have undo for the next transaction.
Now, during recovery (FPW is off) if block 1 is missing but block 2 is
their so we need to skip inserting undo for block 1 as it does not
exist.

Hm. I'm quite doubtful this is a good idea. How will this not force us
to a emit a lot more expensive durable operations while writing undo?
And doesn't this reduce error detection quite remarkably?

Thomas, Robert?

I think you're going to need to spell out your assumptions in order
for me to be able to comment intelligently. This is another thing
that seems pretty normal to me. Generally, WAL replay might need to
recreate objects whose creation is not separately WAL-logged, and it
might need to skip operations on objects that have been dropped later
in the WAL stream and thus don't exist any more. This seems like an
instance of the latter pattern. There's no reason to try to put valid
data into pages that we know have been discarded, and both inserting
and discarding undo data need to be logged anyway.

As a general point, I think the hope is that undo generated by
short-running transactions that commit and become all-visible quickly
will be cheap. We should be able to dirty shared buffers but then
discard the data without ever writing it out to disk if we've logged a
discard of that data. Obviously, if you've got long-running
transactions that are either generating undo or holding old snapshots,
you're going to have to really flush the data, but we want to avoid
that when we can. And the same is true on the standby: even if we
write the dirty data into shared buffers instead of skipping the write
altogether, we hope to be able to forget about those buffers when we
encounter a discard record before the next checkpoint.

One idea we could consider, if it makes the code sufficiently simpler
and doesn't cost too much performance, is to remove the facility for
skipping over bytes to be written and instead write any bytes that we
don't really want to write to an entirely-fake buffer (e.g. a
backend-private page in a static variable). That seems a little silly
to me; I suspect there's a better way.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#252Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#251)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-17 12:05:21 -0400, Robert Haas wrote:

On Wed, Aug 14, 2019 at 12:39 PM Andres Freund <andres@anarazel.de> wrote:

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused.

There's two reasons here:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

1. It's absolutely fine to just put a limit on this, because the
higher-level facilities that use this shouldn't be doing a single
WAL-logged operation that touches a zillion buffers. We have been
careful to avoid having WAL-logged operations touch an unbounded
number of buffers in plenty of other places, like the btree code, and
we are going to have to be similarly careful here for multiple
reasons, deadlock avoidance being one. So, saying, "hey, you're going
to lock an unlimited number of buffers" is a straw man. We aren't.
We can't.

Well, in the version of code that I was reviewing here, I don't there is
such a limit (there is a limit for buffers per undo record, but no limit
on the number of records inserted together). I think Dilip added a limit
since. And we have the issue of a lot of IO happening while holding
content locks on several pages. So I don't think it's a straw man at
all.

2. The write-ahead logging protocol says that you're supposed to lock
all the buffers at once. See src/backend/access/transam/README. If
you want to go patch that file, then this patch can follow whatever
the locking rules in the patched version are. But until then, the
patch should follow *the actual rules* not some other protocol based
on a hand-wavy explanation in an email someplace. Otherwise, you've
got the same sort of undocumented disaster-waiting-to-happen that you
keep complaining about in other parts of this patch. We need fewer of
those, not more!

But that's not what I'm asking for? I don't even know where you take
from that I don't want this to be documented. I'm mainly asking for a
comment explaining why the current behaviour is what it is. Because I
don't think an *implicit* "normal WAL logging rules" is sufficient
explanation, because all the locking here happens one or two layers away
from the WAL logging site - so it's absolutely *NOT* obvious that that's
the explanation. And I don't think any of the locking sites actually has
comments explaining why the locks are acquired at that time (in fact,
IIRC until the review some even only mentioned pinning, not locking).

Suppose you insert one record for the transaction which split in
block1 and 2. Now, before this block is actually going to the disk
the transaction committed and become all visible the undo logs are
discarded. It's possible that block 1 is completely discarded but
block 2 is not because it might have undo for the next transaction.
Now, during recovery (FPW is off) if block 1 is missing but block 2 is
their so we need to skip inserting undo for block 1 as it does not
exist.

Hm. I'm quite doubtful this is a good idea. How will this not force us
to a emit a lot more expensive durable operations while writing undo?
And doesn't this reduce error detection quite remarkably?

Thomas, Robert?

I think you're going to need to spell out your assumptions in order
for me to be able to comment intelligently. This is another thing
that seems pretty normal to me. Generally, WAL replay might need to
recreate objects whose creation is not separately WAL-logged, and it
might need to skip operations on objects that have been dropped later
in the WAL stream and thus don't exist any more. This seems like an
instance of the latter pattern. There's no reason to try to put valid
data into pages that we know have been discarded, and both inserting
and discarding undo data need to be logged anyway.

Yea, I was "intentionally" vague here. I didn't have a concrete scenario
that I was concerned about, but it somehow didn't quite seem right, and
I didn't encounter an explanation why it's guaranteed to be safe. So
more eyes seemed like a good idea. I'm not at all sure that there is an
actual problem here - I'm mostly trying to understand this code, from
the perspective of somebody reading it for the first time.

I think what primarily makes me concerned is that it's not clear to me
what guarantees that discard is the only reason for the block to
potentially be missing. I contrast to most other similar cases where WAL
replay simply re-creates the objects when trying to replay an action
affecting such an object, here we simply skip over the WAL logged
operation. So if e.g. the entire underlying UNDO file got lost, we
neither re-create it with valid content, nor error out. Which means we
got to be absolutely sure that all undo files are created in a
persistent manner, at their full size. And that there's no way that data
could get lost, without forcing us to perform REDO up to at least the
relevant point again.

While it appears that we always WAL log the undo extension, I am not
convinced the recovery interlock is strong enough. For one
UndoLogDiscard() unlinks segments before WAL logging their removal -
which means if we crash after unlink() and before the
XLogInsert(XLOG_UNDOLOG_DISCARD) we'd theoretically be in trouble (in
practice we might be fine, because there ought to be nobody still
referencing that UNDO - but I don't think that's actually guaranteed as
is). Nor do I see where we're updating minRecoveryLocation when
replaying a XLOG_UNDOLOG_DISCARD, which means that a restart during
recovery could be stopped before the discard has been replayed, leaving
us with wrong UNDO, but allowing write acess. Seems we'd at least need a
few more XLogFlush() calls.

One idea we could consider, if it makes the code sufficiently simpler
and doesn't cost too much performance, is to remove the facility for
skipping over bytes to be written and instead write any bytes that we
don't really want to write to an entirely-fake buffer (e.g. a
backend-private page in a static variable). That seems a little silly
to me; I suspect there's a better way.

I suspect so too.

Greetings,

Andres Freund

#253Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#251)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Aug 17, 2019 at 9:35 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Aug 14, 2019 at 12:39 PM Andres Freund <andres@anarazel.de> wrote:

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused. I'll repeat my previous
arguments on-list:

1. It's absolutely fine to just put a limit on this, because the
higher-level facilities that use this shouldn't be doing a single
WAL-logged operation that touches a zillion buffers. We have been
careful to avoid having WAL-logged operations touch an unbounded
number of buffers in plenty of other places, like the btree code, and
we are going to have to be similarly careful here for multiple
reasons, deadlock avoidance being one. So, saying, "hey, you're going
to lock an unlimited number of buffers" is a straw man. We aren't.
We can't.

Right. So basically, we need to put a limit on how many max undo can
be prepared under single WAL logged operation and that will internally
put the limit on max undo buffers. Suppose we limit max_prepared_
undo to 2 then we need to lock at max 5 undo buffers. We need to
somehow deal with the multi-insert code in the zheap because in that
code for inserting in a single page we write one undo record per range
if all the tuple which we are inserting on a single page are
interleaved. But, maybe we can handle that by just inserting one undo
record which can have multiple ranges.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#254Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#244)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 10:35 PM Andres Freund <andres@anarazel.de> wrote:

- When reading an undo record, the whole stage of UnpackUndoData()
reading data into a the UndoPackContext is omitted, reading directly
into the UnpackedUndoRecord. That removes one further copy of the
record format.

So we will read member by member to UnpackedUndoRecord? because in
context we have at least a few headers packed and we can memcpy one
header at a time like UndoRecordHeader, UndoRecordBlock.

Well, right now you then copy them again later, so not much is gained by
that (although that later copy can happen without the content lock
held). As I think I suggested before, I suspect that the best way would
be to just memcpy() the data from the page(s) into an appropriately
sized buffer with the content lock held, and then perform unpacking
directly into UnpackedUndoRecord. Especially with the bulk API that will
avoid having to do much work with locks held, and reduce memory usage by
only unpacking the record(s) in a batch that are currently being looked
at.

But that just a few of them so if we copy field by field in the
UnpackedUndoRecord then we can get rid of copying in context then copy
it back to the UnpackedUndoRecord. Is this is what in your mind or
you want to store these structures (UndoRecordHeader, UndoRecordBlock)
directly into UnpackedUndoRecord?

I at the moment see no reason not to?

Currently, In UnpackedUndoRecord we store all members directly which
are set by the caller. We store pointers to some header which are
allocated internally by the undo layer and the caller need not worry
about setting them. So now you are suggesting to put other headers
also as structures in UnpackedUndoRecord. I as such don't have much
problem in doing that but I think initially Robert designed
UnpackedUndoRecord structure this way so it will be good if Robert
provides his opinion on this.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#255Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#252)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Aug 17, 2019 at 10:58 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-17 12:05:21 -0400, Robert Haas wrote:

On Wed, Aug 14, 2019 at 12:39 PM Andres Freund <andres@anarazel.de> wrote:

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused.

There's two reasons here:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

One point to remember in this regard is that we do need to modify the
LSN in undo pages after writing WAL, so all the undo pages need to be
locked by that time or we again need to take the lock on them.

1. It's absolutely fine to just put a limit on this, because the
higher-level facilities that use this shouldn't be doing a single
WAL-logged operation that touches a zillion buffers.

Right, by default a WAL log can only cover 4 buffers. If we need to
touch more buffers, then the caller needs to call
XLogEnsureRecordSpace. So, I agree with the point that generally, it
should be few buffers (2 or 3) of undo that need to be touched in a
single operation and if there are more, either callers need to change
or at the very least they need to be careful about the same.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#256Andres Freund
andres@anarazel.de
In reply to: Amit Kapila (#255)
Re: POC: Cleaning up orphaned files using undo logs

On 2019-08-19 17:52:24 +0530, Amit Kapila wrote:

On Sat, Aug 17, 2019 at 10:58 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-17 12:05:21 -0400, Robert Haas wrote:

On Wed, Aug 14, 2019 at 12:39 PM Andres Freund <andres@anarazel.de> wrote:

Again, I think it's not ok to just assume you can lock an essentially
unbounded number of buffers. This seems almost guaranteed to result in
deadlocks. And there's limits on how many lwlocks one can hold etc.

I think for controlling that we need to put a limit on max prepared
undo? I am not sure any other way of limiting the number of buffers
because we must lock all the buffer in which we are going to insert
the undo record under one WAL logged operation.

I heard that a number of times. But I still don't know why that'd
actually be true. Why would it not be sufficient to just lock the buffer
currently being written to, rather than all buffers? It'd require a bit
of care updating the official current "logical end" of a log, but
otherwise ought to not be particularly hard? Only one backend can extend
the log after all, and until the log is externally visibily extended,
nobody can read or write those buffers, no?

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused.

There's two reasons here:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

One point to remember in this regard is that we do need to modify the
LSN in undo pages after writing WAL, so all the undo pages need to be
locked by that time or we again need to take the lock on them.

Well, my main point, which so far has largely been ignored, was that we
may not acquire page locks when we still need to search for victim
buffers later. If we don't need to lock the pages up-front, but only do
so once we're actually copying the records into the undo pages, then we
don't a separate phase to acquire the locks. We can still hold all of
the page locks at the same time, as long as we just acquire them at the
later stage. My secondary point was that *none* of this actually is
documented, even if it's entirely unobvious to the reader that the
relevant code can only run during WAL insertion, due to being pretty far
removed from that.

Greetings,

Andres Freund

#257Amit Kapila
amit.kapila16@gmail.com
In reply to: Andres Freund (#256)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 20, 2019 at 2:46 AM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-19 17:52:24 +0530, Amit Kapila wrote:

On Sat, Aug 17, 2019 at 10:58 PM Andres Freund <andres@anarazel.de> wrote:

Well, I don't understand why you're on about this. We've discussed it
a number of times but I'm still confused.

There's two reasons here:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

One point to remember in this regard is that we do need to modify the
LSN in undo pages after writing WAL, so all the undo pages need to be
locked by that time or we again need to take the lock on them.

Well, my main point, which so far has largely been ignored, was that we
may not acquire page locks when we still need to search for victim
buffers later. If we don't need to lock the pages up-front, but only do
so once we're actually copying the records into the undo pages, then we
don't a separate phase to acquire the locks. We can still hold all of
the page locks at the same time, as long as we just acquire them at the
later stage.

Okay, IIUC, this means that we should have a separate phase where we
call LockUndoBuffers (or something like that) before
InsertPreparedUndo and after PrepareUndoInsert. The LockUndoBuffers
will lock all the buffers pinned during PrepareUndoInsert. We can
probably call LockUndoBuffers before entering the critical section to
avoid any kind of failure in critical section. If so, that sounds
reasonable to me.

My secondary point was that *none* of this actually is
documented, even if it's entirely unobvious to the reader that the
relevant code can only run during WAL insertion, due to being pretty far
removed from that.

I think this can be clearly mentioned in README or someplace else.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#258Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#248)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hello,

Aside from code changes based on review (and I have more to come of
those), the attached experimental patchset (also at
https://github.com/EnterpriseDB/zheap/tree/undo) has a new protocol
that, I hope, allows for better concurrency, reliability and
readability, and removes a bunch of TODO notes about questionable
interlocking. However, I'm not quite done figuring out if the bufmgr
interaction is right and will be manageable on the undoaccess side, so
I'm hoping to get some feedback, not asking for anyone to rebase on
top of it yet.

Previously, there were two LWLocks used to make sure that all
discarding was prevented while anyone was reading or writing data in
any part of an undo log, and (probably more problematically) vice
versa. Here's a new approach that removes that blocking:

1. Anyone is allowed to try to read or write data at any UndoRecPtr
that has been allocated, through the buffer pool (though you'd usually
want to check it with UndoRecPtrIsDiscarded() first, and only rely on
the system I'm describing to deal with races).

2. ReadBuffer() might return InvalidBuffer. This can happen for a
cache miss, if the smgrread implementation wants to indicate that the
buffer has been discarded/truncated and that is expected (md.c won't
ever do that, but undofile.c can).

3. UndoLogDiscard() uses DiscardBuffer() to invalidate any currently
unpinned buffers, and marks as BM_DISCARDED any that happen to be
pinned right now, so they can't be immediately invalidated. Such
buffers are never written back and are eligible for reuse on the next
clock sweep, even if they're written into by a backend that managed to
do that when we were trying to discard.

4. In order to make this work, I needed to track an extra offset
'begin' that says what physical storage exists. So [begin, end) give
you the range of physical undo space (that is, files that exist on
disk) and [discard, insert) give you the range of active data within
it. There are now four offsets per log in shm and in the
pg_stat_undo_logs view.

5. Separating begin from discard allows the WAL logging for
UndoLogDiscard() to do filesystem actions before logging, and other
effects after logging, which have several nice properties if you work
through the various crash scenarios.

This allowed a lot of direct UndoLogSlot access and locking code to be
removed from undodiscard.c and undoaccess.c, because now they can just
proceed as normal, as long as they are prepared to give up whenever
the buffer manager tells them the buffer they're asking for has
evaporated. Once they've pinned a buffer, they don't need to care if
it becomes (or already was) BM_DISCARDED; attempts to dirty it will be
silently ignore and eventually it'll be reclaimed. It also gets rid
of 'oldest_data', which was another scheme tagging along behind the
discard pointer.

So now I'd like to get feedback on the sanity of this scheme. I'm not
saying it doesn't have bugs right now -- I've been trying to figure
out good ways to test it and I'm not quite there yet -- but the
concept. One observation I have is that there were already code paths
in undoaccess.c that can tolerate InvalidBuffer in recovery, due to
the potentially different discard timing for DO vs REDO. I think
that's a point in favour of this scheme, but I can see that it's
inconvenient to have to deal with InvalidBuffer whenever you read.

Some other changes in this patch set:

1. There is a superuser-only procedure pg_force_discard_undo(logno)
that can discard on command. This can be used to get a system
unwedged if rollback actions are failing. For example, if you insert
an elog(ERROR, "boo!") into the smgr_undo() and then roll back a table
creation, you'll see a discard worker repeatedly reporting the error,
and pg_stat_undo_logs will show that the undo log space never gets
freed. This can be fixed with CALL pg_force_discard_undo(<logno>).

2. There is a superuser-only testing-only procedure
pg_force_switch_undo(logno) that can be used to force a transaction is
currently writing to that log number to switch to a new one, as if it
hit the end of the undo log (the 1TB address space within each undo
log). This is good for exercising code that eg rolls back stuff
spread over two undo logs.

3. When I was removing oldest_data from UndoLogSlot, I started
wondering why wait_fxmin was in there, as it was almost the last
reason why discard worker code needed to know about slots. Since we
currently have only a single discard worker, and no facility for
coordinating more than one discard worker, I think its bookkeeping
might as well be backend local. Here I made the stupidest change that
would work: a hash table to hold per-logno wait_fxmin. I'm not
entirely sure what data structure we really want for this -- it's all
a bit brute force right now. Thoughts?

I pulled in the latest code from undoprocessing as of today, and I
might be a bit confused about "Defect and enhancement in multi-log
support" some of which I have squashed into the make undolog patch.
BTW undoprocessing builds with initialized variable warnings in xact.c
on clang today.

--
Thomas Munro
https://enterprisedb.com

Attachments:

undo-20190820.tgzapplication/x-gzip; name=undo-20190820.tgzDownload
�H�[]��i{Y�0:_�W���� ���z[���)m���=�������1dRdbY�U���X�� ��=K���y2�'N������mw������M~��������{�����o�#:;�����n{�+���n��/b�o3�g����2[F�xE;h6�x.���7�Y:��nw���h���t���E�L��"��4X4��o��t4i�{�(
����{�mI�{�^��w��{{�����z�"��������L�v{;;���;����pt����z�A/�v���`w4<<��8��\t�E����]@�*�y)�fa*~����?x����>�!�{��0	�ak�~��������,������@$�����'���;���r���(})�|yts�At����_���"���P*Cb�`���_�A�&�j�f&"d��|� ����7���"�������6��%�ETg��$�$�>��^*�1�"x�$���yS�`�������(L��x:���Ob� �I�h�I||�����-p#���S��T��$I��4hU�Ho6�
Q��"��"y�u��Lw�i �8��D<���`�cX�*��t9o�l� ���@�q��9z%j��8c�� ���^��=v��^�z4�>�1�/i�y��N��] pY�,��;9��f��2L�j�1,"��=D�@�����`��GbG)��Uq�3��k�dA�"��2<[�*��V�+OLp�SvG�����������C�`p1�a���������!L��Lh��x$	C��p|��G�YxQbOzL�U~s�Y�C����;��7L�[�Z-�����K�7'0�_a�����T�����}	�8�{o����������I	3�*�L�In��J4)B8��x�������,�>�1�/�LW�������0�6,3��\/�D?�r��a2�Q����w��G���`adX/w�i7��S�c���~�z>TCh���pco�����D��9,��H<E6T�U��n���j������My��O�(����$N&
�������Q�/���[�K��l6�"Y��e���k��}�}
�����o�OWl�x������b�`��r������^o_le\8I/��`{:�[lO����S�����~I��w���]�1;3v��0M��(L����G�M|��+~o��}��w&����G�aw���L�B����H?��@�F�����2��Z�����d���2�*��[����I���NG�To��<n��A���A���W���(������X������@���
h���?7�UE�g�B�F(\�����V�p<���ko{���mw�|����������V����v�`��!1���*��&}�����6����c���B�.[�*��`��>�k���U!�L�?5�U��:m�M��7��j����]}c��^��F1\Tquk����)pvX0=(5C�ygq��}�l_�Y��v3����x�����a�w�7G�f��\��NS���]�g�w��*�U-�3�-\������/+[���[%���)�n��'������'�����m��J�����Y�"5�����_����b�4���W�������r��-��po�L���S<����������O�b��yx-���j���F�1hy}����o���l,^U�6x����������O�����W��������������v�#�����^N�2]������4�Tco�������i��a��A
K��v1���#Z�f����!T�\�$�f99�_���/@����Sf��A>��2Q\������mxE�Hc����u���5���x<N�	m�i���^��nm��n����%F���	`��q���}�HR�*$aI������
q[��vu����=���Cf�j���OQ�Y9�����~�!���.^<����^��+4rV���c@��?�������2����*���}7c�����j=]�"V�,��`M��5�
���7�0���#X;(/>�#��gcQ����I�G����^�Y��9!����������00�a���o����?���\]��o�W�|����(<������1�w��GD���:��-9M^���sk1j�
��
^\8?W����?��$^����M��r	��|�m�
�{	����������8�==�>���"A�J4
Rj��Na�<��<k����w�D��?
�?]�eS����3
��K�����\�����#|�a�%�I����E�"�0Ho��N���(�V��_0��\�!8�D@�j
���2�!�^�����ROl�"<�0��N��������/b�m�X�����C�dE��%���������P�oCX�����9#d����������8|��V���i�:�����;���1������r:���1��u�n���j��
������A�NT�����f!LH����"�v4H�kD-����#��������QD��=24�����~*��-���i<�!���R�d�Z'���Fp��I�8��8^D�������712�<^i�F�C�\*�����o���3��-�x�&�*����n�{c� ��W��"�I���J8��q�[*���Z���^�l	��0���H����Q
��9���|��`Z��������4�m��[K��U�������js�O(DB��'�������"�'��9!+*�u 	��!E��a����<��):X"�2
b�=1I�#��bBT8���$@~��� `�r���>���41$2�,�A��MR�(!;b����]���	/�%�L/,#b�#"C�f��*���
{<�Ki�fO�4��F���BY"����)x��x#���O�?H���]K�t��2�O<Hp����3���a��6Cv�(���R�J���"N��i8��,�$�ctf�fJ��S�[�����U�$�E@Dc ��o3C�r���$0�I$����d�3�2�B8���d����p�9G���<^6w�O]�8j}��)��C��L�������A����7@�c��$ev���4����B���C�H��o���%��\f��*��(��o��P��2/����Ti���8\F@�������C�y�O��C����
�:HW�����&��I4uC`X�{}�i��6
�c
	��&7������1ZHd�I�0���(�E���;�x��G��3y6��%C6H��\��1�.�\�Pp�����5�+�`M<h7��<���?��B�F3�����<�\��6�-�Fsi0>���*�[�d���'zK�����������ns�I�`�9�k��r��(~����d��������d{�oF�M��sx��g�����4�@iM�'p�=�
�$P��<��
���������m` ���;�Pr@t��-J&��J�!:y����y��3
�]v�3���jg��^P9;�8�={1���������o����\���~��������J���<|wtrz{�\�o���N��48�F�����X��X�����c��J���+�6���j�iw{�X�(�x�H
�|5Y�y3��+�k0N�>6��3;&^'��XF�[�U����I��	�`U���+�u �!,�����|Ll�y�;����h�#�t�N���i�����t\�0<p�qM����a�q�pM��H|�9z#^\��p�J?�t������B����U;]��X�;�VSx��bO�s9O�����*:�W-ym8iZ�������S�
�����N�g|i]C��-���"��W���j0'I6Oj4�J�`� ��W����|�9"
���D�z*�>��������R�&�xkS�4�!�[���K����K�3��2Sn���k�����y:�1Ge��m��QE�d��"*�u�6���+��&F{�U�f��{��63�����^;����^�|�����_��G���kZ���GH�{��FuC�K���O�;����}�7����Y������#��{����������������*=��
)=��r�y/���_�r��/zC���R�\���!}KE������L��s3:�4���b�J)���IEf|�#m@?�Xlz-�X�4�����,Cn6O�(9@E$��|-M)��'���J�J��m�>�7� ]<P��D}��R�"�(!5�MW���W������'���m&��=�a{�M���W�vyc�JR�x��������R�-����Uy�9Gy�H�	0��E�����v5B�5�1l����w�9%'���u���y\Yl���V�ndX7�(�`�o�LO�K�9����3���!�o�N��
{�H�|G��T���w�����
�kR��C��OF���	.'�hd�h#��`5X�ep���Q�z�X�����>.���@����\4�� ��'��lb��$g�u-��Y8LD/Rxg� \�����j	q�n-���V����Y(�a�#�8�����)F@��kI��e2q	dm�J��a3�a�I%��������dC����j��~���4Tx+k:i�i@�f���X'3Z)g�&H�����c[a���A�LJO.����)����&����T�� �<9�h��n����y\~�V�qc�|�
f���83
6ipn�+��~��U��/�FZ���f_��wr=����%qJ�5[N�_/Qn7��*�`��kF�u4�9
�������5�����^��\���h�n���UG�����������������y�{�W�U�9�{N���0$Z��:�`��
����c�4#M�cf��"�P��ww�h�����_��(���������f-=�9��������z���+��\.�w�6R��P��4C/q_�K�w[l�������;N����)Y�+��j��H%����ev�-����/�O�F�����.����"��Z$P����+I!:���3�����5��ZH#��P2vx�����
��>naR���C#������Aa@-uH�:�i|�1&�P���`5c���]$�d:����?��a���cN�)�� �.\�d���^!�F)7H�Q�7)O[q�[�Y%�s��N�1zf�+�8v����*�b9�b��k����)�3���TF
�����L/2vA�H�H���=/4p8W�e<P9����e�vefX�x��B��D$���k����Q`�bf���Zv^���](B��kY���fc*aq�	�(�������H(X�\x��O+a�<�^t7E�%��E��f�h^� 9jO`���`��$���T�,}���UI���O���Q`�Hx���]L}b�0�v
p�#:�^���@��v@PKx�3F�|������$:��$��I��-���A��������*
���k�w9��F6���b�X�K�����AL>�K��?y�s������U��n������M��Uj�~M�h$�L�N�;`_D���>,Dpk,N��s�:�������������fB�2�H'cV
*�z�����S��9��.�s�����o��2O\����I�m+J�,�o�	��LN�K�pX[L�$2Tx}��\���#w�L����
����s!�J/�X��4�T�t��U$�2qcQ'�����"!���6�j��
3�>�*��^&�q
r�,����������I��A6�����=�8Px�:O��T����*��~�F���^>���
�N���7���[oj�?�1�X�Wrff��X�MV?��&�,�=�s�x'��3oy'�<��RO��?�0�B�pv ��������6C�lt���?Y��M,7�x����d	MX/�;��-��u��j����d��Xr6����q��(;�qy������������@����L_�����x�����W��}�X'���YASm�S+T%5�����8�$=�
��k������7��D�s�^��|����B���G��?��`��������������/-�n���$[Nq�����.
��
�����Q M)� KrE�U81�k�J���e��(�,����d��I�������`���N�I����%m3�;im��t���|`�`�~R��n�
��/��P"(a��4�6��nn+��"�������i�ObA�p#�������'`��w���n�'q���?���z�~-����X����>�����Y����0���T!G��)#���n�k��A��0^gb�s�>C�GSt� �b����%����`s��X�8y�;��G���L��a�IO�K3w!�V�~-�USA����
�E��c����F����Q���K��yZ|K@�9��W-'5�FHt����,]g���H?��z8���y\�"%@����{E�
�n���������o�B^-�FjS�S���5t��_����U,�v�/��\���{��H���2P	)�y������2v�t�^�Z�3��'�����9�+�O�l���l1����|��(����%a�8�V��!��U���M�I13I�;{���h�~s�g$JU8	�'�����>�^��|�F>X���D|���Av�+rT�����%�b�1\�/:�Y��d��,Y6�v�5[�h���Z=tW�$5lR������Y���E�E'n��H3Q���{���)%dc�T��N���)�z�Q���FD��HY�B���)q���6hbXI$�6�%��%��\�OMf�r�~��j9����s@��$�y'��7����)jTa��c��:���Fm)�g2�����
�u��S���za]���k&1���������'�����W��X��'n��0�Y�t�3JD%���T]t�?
��\\N����d�$�$d���\>����Dx�k��5�q�s��%c���Ltg���s_����u���<s2�Zf�n�a��hIt�5��+�6�%S�U�A���%��\&������R�l�P�5�2N���~�z�_����_�2�� ��`)���Q�g��0���@w����Z8�gJ#!���n��_�jO�x����W^/�������=���L������X�4���RS�K�������J���h�2J���I�y�=�|�T�%Y#�Az�	gF�$�����_V�g�?rog��,a��!����I��p6�%�">6������R*h>09/�%��*�
#[M�}�0��&���[������#�T��Q�_��~����L;{�vr�"]������Ft�N"WX��-T�������S��	��>�U��z�=�L+kr�����s,{���RI~Ub�z�,��DVP�+�j���v�]9L� ��-�&��W4X~��ea�����TW��d��x�FS�LrK��=U F)K�>I�P@�!'������.��`�1	�$d3���hf�N&v�����(x����:���q�B�
!�����k�vRZ����^��4\� �"';k�8�����"�8����|���>z�z����a(�RDEQ�o(����H�?�j!0���Z�JE��z��k�g�b!�3�s����`:~{�F|���:q��p4��U�VF6�d���7�����.~|mo���akgU�o��0���U��<�R�D�S?��LT���jpe���TV\�+�m����B�MZ���B�����3����&���,�4����A�	k���5H%�_�r���}*�D1�E�o��3V�9g-�C�,l�Sdo{9J��P��dFCisP�4�����a�!� �f�uJ��c>����E����[�h���)?j�kIP����-�i�;��z��Y4G�5��r����#4>���v��H`?3�L�z�����]����h8�t�t�T���
J�`������`V�2��F�r��5�wj'
q�j����C������+�����8d����M!))+��rz��=E�E5������h�1h�5i�������H�0��f�.���`���4��B|@�����"X�)F�#�l	��%J�T�Y��
\%����;��l��H����,Kb8���;��W����Y!U���k%�jE���Nj���F.9������M-�zO�F�h�d�)V�%���h��R��*9��6�W��)M	���0��)�am#pDuJ�����EX	H��\ �ffg��v+G;
�lO�v��q3��q�����g����d�+�o�n�����R�5],�y��,� ��qF���JFq������U/�n�UGv��9�j�Z���&�������>�N�	��L�)�H(a�}��n��'��Sn���s%'T�Y�K��'��������b��[5����J��O\V��P����3oO�o�h��3����'�G]|�.X��)jdP��0�*��ra�TC��c��D�	&�^���M���^f)e1?�,%Wmh�CpB?C�p�\v3~�����G�����A��Y������Ez3�oj_��d'��W���V�hW����.�qJK~���e�C��+T{Y��T|/�O�"Y��u�8�"��
~����F�����G��c���uqp*��U��)4��d��Do��q+�f?;f�����p5�����p���q��&.�V�F/f0��M��5Q��6�U�h��bAq23�b���	+)����t�_N\Q����%-��7�������t	��J*f�v���VV������|����8GyW��`��<�P�c��	�k
r
+�*���W�r�vv6��hY�)�,�:�M�t�[�6��@\���c�-�S=������g�,&�++�2VY��x�4����'�,�����F��"a�#Iw�]�����=<H�QjxV=Y�sR��"+��T2����1�E��V%Oye~��*�lb��7%
<�J�[GVR�"'����j��?�!��[2����|�rg�%vG� U����0�X�2�>l�qs�#'�&v�SBS��M9��Q)��P�V����bc.y=�q
�Ge�#�g`��_�?�t���
�/�/g���$3��s���K���3(N�s2�rr?�aQTke.���Nm�G'g����Sa�{�$A���g��oc�	D��h�z~��(% ��}:��FE�����X���	��a�D�����7�7�pr�q��F��R��}����Ax����,~n���
r��\�U��V�1�}X^k�����s�ZC�5:S���������A��l�F-�-;������t������nM$�5�����0�SX`]m�]{�]�#�9��O���3���%��Qr�7m'x�����?����d����������|���v���*t��;������d&g��e�����M��ZM���I�o?�Lkv���c�^�k*g�����!=y�1w��5����cTw�H���o��� �L>����%�<��fX�1��y�XnSa{���~s����~���I^�s�}����F�f�*��o���v46{���n�|��h����p`�����w��|�V�Z/9�nM���-�a�����k9��y�����5���Tk��j���y=�]���D4a_�43y��c�
��9��N0�WF|���j��v]z��r��U�K\T>S��9������2���.��7S1�
�:�W�P%��WNS=sS���x2��]�m����NC����xY���������^o������F<k��>x��/��5���;T\�������t�����9'�Z6eE�Q�����e�P��Z��:�DcDA�
���a���"����e��� d��1��5��v��~fr���L���V�,�g���U��a9(]���@�a��7����{
�H��}suV���y
z�4
���P��R]�B�WT�K���h2��N�r�!��fprN��Hb�'I�UYF#t��=�%K��Jx]R>���L��N�`�g(K��n������.��F���d# 
��/L�aD9odT�(�F{��������UWE�]���)�M*L���
��M����T��0�q��G���U,�%*��0��D3W�:�031���,�h���&�T����&�M����A�I4�	#n?c0�E�x�,Z�#D� ����E�O@h�h�11oI���@�r�v�!z�Y	�\�6t'���^lv� ���2*�
��8(f�I�1�����N6�N��+����"_H��r�
�r�86���{��a:��(������:��%�����n�^��W�G����Rlg�����R(��0������{��D���7�5�n�G�FYN9&�������r��;:9y���z��*HlZa�<�	85����C����e�`�(]��d���C.lG�.��-��)i��N:�R[Ok�,��o2410��^em�%^,R��YK3C�'��m���f*�s��E����mF��U9�|	 �R'�f�v�*�?3I�Tj:��X���-deu���;U����T��n���#�:94�h'��~�A^����:d���������,�[v����w�n����u$)p���Fdo�l_�$M����+��<�g3�2scXd����>~�m���F5���X���An������;��*0z�Fw���}�-P/��A�7��sp�7?�u�_"�Eb�������)a/�|�F�jKoR���9�����������l��a���]*�#�cy~�)�-�8C3Gv5J.G<,K��@�����`��dW ��2�%���C��
��;�T�=��o�����`�rIr�|g�AMh�>9�wR��d9L���bO��s92�n��x:�H*'��6Bc�7���+��Vg(V(.�.�_���_����Y��<%�R�)�M�r(0FOc/������A���CY���
�:u�_'��Xg\����:'�!27
��c���x2��+������Y�2L#9v���8�t��1�3���+ZcqcAIR����F[h�����r�IU:v�&GA��g��x�m�t1������u'������<U��
;�c�7�
2�T��q��ya�UI�bP\�x��]�Jy6�;�����"�e� Dda������e����m����|�������-����c�L�[HX�����b�:�E��|�8�V�Y%�hr�t�F�n��5��+���KEC�#�-�5�o!rl,tX3�@��X�(>rS}����,���'�~��Z�?e�z�+�(}��I����sSfw��Q��W3e<�y�e��
x��h���,w��hz�V�Z���g������1s�g�$�e�X%LV�$�����f�2L.�b��C���e��I���B�jI
�bV��"��P�$]�)�fh�'���I�'�'�n�PV�*#9_<~��"�>lVq-�\�n�*�c3���z]�<R���E���J�F����"9eX#�<.�[�����y������eb�,��8�?���V�O���x&H�{�L�������A"�E�Zs*��^v�`�f��0���u;��p�@���.�Q�������d�q��3q<)�e	�0�R[����=��C��Cv�����\I����������ke�$x�qU����,[M�L����o$X-KM*/�������'Z�HfV"�T�*�a������Y(�!������4��N� �L5�4R[� �J�n9Z��Y��/�|^�(���%k�0�MToc:0s�T���'�w0nr�0�?a�����TZ����g+�8�Q��,R��PAN������Z�x���I���f�Z2�&S���*cl�Z�8f���:|�%���v��\�	f���)�����RW����Ju@��M�j���J���H�g�>�f8������}�Kn	_R� ��1�/G�1Sia�y�����:�S���R_���$Hmu�NX�9�������\t�3f�����m����w�N�O`1������:�_+Wctk�%w�gE7�>mP
k�}���4��b��Uz&c�t�<G���C����%��?����,o�Z��=����CG_;����_%O���������~��0Z��K:����~���%0������6�����34���H�Y��u�3�Bl����S�����y��d����+�Mn������Mv����{�br���9�2�@�Sk���;�^�i�	��d2j�W�+�ss��rg�X���P/��g9q��s�c�^���
;��<�H����������E�
n����U[���q���4���,����)R�Y���r}���%�!%#��P_��(�d�f���|����V�M��G��'������c��)|�!�~���"=o�fG.w��-��&CyUX}��K�h�.s����U�\��@$����gAx�+M}V������D�|5O�\�U����}����M������n6���ml��^F����e��$H�@I�!��UX�xp7�{FD^��L��k��'OK
�$g@�D��(��NO������M��@U�S��)M%+����Tp����+��"�&�?uwe-�"-�dSg��W���F�8I
��(�5�f�.&M�pQ�����m���o���s�������ok�G^��K�.,�P�����!�91��Fe�&������n����pt�kYThG��t��c����
w�-�� [r8����uf���UXM����{D�	�y���{�q~O}h�qF��5������W����+��,�U�p������H��p�����������J�	��
L��"�H��g��yZ��?���W���eb���<����`+Y���!���n!�I�/�l�n�u;��_�������z'�E>���w�2���M���{������k�t����H��v�mK�x;I��wlOgw���=�v�7�V�����V��������:�q{��Z���7�����':��^�Wm6���������1�����n��-��'�#�����'��j��ys\(�D{����$��U0g��~�S���f�����T`*�(��o���5���/�������Zn�v��`gg�����:8����7���7�		k��V���������_��������H��V������2����:!�!Q�����`�A�s\a2���!"�k�oQ�&�<&��6�����*��(��eN�m$W��8I���F��[��z������~���p��i^�|����7��nW�}���E��m�c�
�H5���bv����C<e
���	��Mb�V~eL-���]S�6��������a�@
=��<���NkH�o�����``����Y��z�q/�j#E,"	������C�������Q ][e~:|��~����������-B�
	J����(G������3.���������Y�����y��},qq����ww{�Vk��x���wzkq�T��V���.����W����<�F�z���z���Y�C������|����t+��_�:��'���8
;�i�����6B�*>�y�wh����F'+d���H�qHp�1�	�9B9��?�E��3E1�o������+��
��L��B��x��s��6���e�5������u���b�6�
��g,e�%�Q�k�����;"����\(�`!M�S��p��LZ�hd� �����fp�������������I�w�O'�o�t#s���9o43�2SO����������������UD9�������rY����dW����4��sX���+g*`�g
� ���@���������?\]�_�^���y$��	\�F'&������`w������T��{t����pE6�Rsy��2m��h��F]J)))n/����x����:���;��?>�����+	[�;H��m$���#bG���
s�p#�r�^��<��!��x����M i��*���dI���b������w0S
�pbH���Ii�����!8�2����L�lp�s]"���F�+�� �/�����m������u����f;{J�,{,��h�n���;�\������g��9�M�{�������a��)M���^����yl��e������O���)���ki<��V).�P����
@ip��@b/[$��v�uCy�v����%���uE
7=+��:��s�����B��Ag��z�q���x�{������E;V)�9�H���� ���C���z�MP����G�������>neX�B���U��m�z�%:T�Z^�QEAJ��:f��bnw�CCY4D<WR�����J����T	O���8��S���lR6"�H��9��x����y3����&x�y��m�M�X�l�A�������6?��^�?8z���6���|����tp~�����6�W���A������7��)|�����t2�=6p�62�����������R����sx����;��!2�r����U �m�9��s2���u��%�
��"l��6��
OT���2g�/�,)�XTt�y��N��<����f�L���t��\Fe�CcZD&�����pZA�������[�D}�4�����7����+�����0.����y�V"s9d�e�F�Z\�{zzrvyqu��T�b�Ui���ej96���hlW!��R���R���I��^��	+"��:fJ^�������YH�P���F>jB�������{����y��>��j�K� �����������ZQ��k�<��:�	)U��`Mh
�+���X�v)�7;�Pn��e�YP<H;���J��?����%�8���R�q��$p+����k��$2�����!y���?�����Mj����<���qN8CR-��|��z'���n�"GU��M��E)����h����%:+�k�o#Y��@&���a�fu�[c����DyS��cCM5����*o�"������3��B���`��?zN���0M.��bFF�u� �O��V�3���Q�(5�r�)��9�{&�N��r�Kske����s���q�Q��{�����[����f$��6fdI�������LC��n�AM���J����Z<,����0�%���Z�x!�J����
�i����Xyz�~��������Q�+F�����^��2�}�%VN�+�����X	)�@u�(��J�����(��E��Ymd�qu�K�x�f�,@�s�+�mm����/�q���w��f=��G�a����������r����t�"Y�G�{~���{�j
rkM���WA�t7e��<��wU��a0����d)�./r�=��5���f	%��U�����H~f�S�pB��G�5��{��>#�q5���f$1��
��2Ce���a������g��{�t��o;;HV;����N���'��F������|*:s�#:�~	
Rl�N�����F��7�rAV��~xi�!V	Q���gF�!#g�)C6r%s��9�zr���� I�@.NW���Z�u�v���*�.��5�B�7N`]����ye}x��L(����6��*#aL�x�A&��}���W[K/*��|s��$���mOI�b�3�+>���pm����&��m��P&D&��}X�=&K������H}��3k��%\�JAPz��1!�h9�Y�}���a��L#y%�������?`��c��3����f�7�J��G���h���b�[U��gx��G�7�����?
W?8�8�<����f������������:���#L�3}(�8F�3�{5{%��,,p�+�\_��y�R�����&�>���&����u3�P�g�jJTM��4�L��u4
)j,C��zN��*�dB���^�&���uU!
VbuN�0a����������w^���GKgl#��Z�"�Ha��c��Zd��7*�o���D�Y�@.��<:�R�U>+�2;����W��^3Pt�.8p�,���_��6$�t����u���
�yS�E~�5��-�4�9�w�����]=s���i��2~D1��4�8E��_�:,����b�=4�H94�/V4(~OG�&�E0�W���E�Fh�H^���Y��k�\iGW3�������'��w��w���~�j����A����.ly{����|�������~s ��D��x�&5v�-�>�&�����C�-w
y��B��;�d���Z~�u�Sl��rS�2�ph{�P��`�W��e'���~������BA��ZN��2�`��
��"��P�J��t��~w����;~����U���G�L��'�o�E1"-p���o�`8���uG�K�P��t�	}����K��;^\��P��!�Qj�)�����{F?�+���4��<��1o��$KI�|��	�f��:)0�x=k��c�z.^���W�!��mu��v��/��gA�"��n{��n6�0��or�&�l��g���c��G��&��{o�~���~:�^�#:;�����n{�+�����������l��q�2[F�xE;h6�x������������n�{c�tv:��A����^����������8c.:���~I��.la��G3 O?y�p��<���D�`�|�1l��c���/^Ct�AV~����{���������N�z��'������G7�D�p����`���
C��#_�3Dg��R5�r�����h>u
c��`*�Dp���p�.���J6��Pv�����z@�(H�*��%�9:d��%8�e:�/�O�(�0�IL�
}{a�<W���>
���h5\��N�@l���:_8�#:]�v��NG4���rCA�p��)B���W�r#�|,�-�;:8�����Z����n�o�����s��^��&�UJ�Ng/����K'������]��������1A��UUHqI8V#��j�x��2���t��12	�����o��O��Acg���
�XM���7����[�
�����s�_�Q�2:gb�J*��il��2zI�pK��yj�%�T���(�*��s�u8��B�'����"2����6_2���
\���^dw�U�IH���&��e�	wH�^S��y�����|Ax�M��J;I����oUh]�?'��z����T���_�cXx�rgT+C��v�%�/0ts�+��U�9a��j��_�_2����6�3���S�������w�r&(��b�)?kg\-�pg�|{:��L��K�����u:��A�1��e��T)X��^�:u��Z��^�+�dQ���N���&������d����d)w�w#7}0.C�_e�a4)�3G+�6�n��gEUH�^����;��#��ppI��j��������dr�8�O5������C�/f���z�rw9i��a��d�U`��h�|5�o(�4���7��4@��ZA�:�R��|�%+��L��v��������O���44E���JMV)�j��_�H����<��s��S5�W��GnL��!����2��j��8�����8�	MM�1z��FF�vLq��2���AXNP��r#�eu�yY�	_�E<L=���)�x��Gh�(�]��2���:�{o�'�#j��|lp
��m)��`Ew�������x�	P��5��k��Z�>&�<.9�~3�����G�o�&�]�>`N2�#��������",�j�-Z�Kn����2!W������\c�	T������J<����\L���q{����$.��K����<@���p�c�7�����#o���%��Ap���Y�7��ksMh� ����N���4G���d+�l,Y+�T��if5���5��+)!�y�9�m6�G)�����r������"���*��p)��<���it�Y�s%$�c�P
X���!�����n&w��u��y[����Foope8H�J�Y�Z��U�'�;�^���I�� A����m�_.�|�y�^�������O�G�l��[*�������M�
m�M�J��=4)+M�x���_���;{��������S����!�wgx��;�n{o��w�c��s��v{��Q{o���;�`���m8
����.�|����V��w�����������KT^��H�OZsDPa!��Z�^"��X�9�1��,@�u��H�>��O�t�e
��0Mg��6��R��Q}�bH���r��7�GW%��i.Y���:9�����h>���I����"t"?u��5������@���~>:�V�
�b*�t�v69����'x����7=q�
�I�
aEZ��q�@�����*kD�<��@L8��o�Gl�"vv]S�
�C�0�5S����=�����;���w��v:�.����"J�M�M9�_�������p$]`��u���%9(P:B��l�H����4���4�����>��x�-�S�kr�������N��].��tzq���+Z���,���w|�v-�����YI�J�6X�������>QpF���~�=NTA���|�m�j����<@#\�4��������K�5lS�V�V*%OX����m9L)�]��'~}=�����L����}�R�+*�y���*��?���k�kLV�E�#w	*p���������X��?1,N��%|�T�������B5A�_B���T�O=[_�.��<
�Q�yO������7U�
B`\���N�5�k/������ �y�-O�j��'I���Q��`��q�0$�]���fP
�x�|Z��pJ�a�����R|%:�V�L�M1���H��L���I�s��e�^.n�]���5E������2������W�h�)���sP%jB1I�����U�� ��{9}���G*��m+Y14�{Uhh���������>t��Z(�m�c���Z8
�]F�]�E�~����^�E��Rs�8�%�}��:�UNX�
���%�V�dH#~��q
�0O(� TU�J\�D�
��9�>�gsm��R����Xe��8�f�����s���T��)��K��U��#
iW����zN�.$2& �����u� g/�#,��(�v����R�z�6gad����Y.���[���P,�s�h,zjy`6u���#�s���+8���������O�]SOR��T!jI���>(O�Qs�ZPKS��2_��K����B�pa���P�����A�{P��,�D��-�0�Y]d.D�]b2flq�9�+���#s�(BjQ-�hL!��d��[T O�[���]���t�D��wI�*�i�����eH��Ct]��&8�x�J���h���������jF�����p��6�����\vs\�����T����d�$S������Y����u\H`���M�er����X�B��%sG�k�:�?j	6D|)o�����E:�t���r�jm0�b;�q���c��	M�K~B����f$F�Fr�3��"y+,VEZ�����;�Co<�z>������mo��QNJ
kM�@��=�;�z�w�E9)���x3c�$�U(�#CK��:trV��kx3[�"eM�m�`�_�7
K:_�'@U��Z{U�������:��������<���tRn����)��~�<��Z��k���d��w������{������������n�������vz�������t�F����7�x��������>�ha�p7�������Rg�����}��{�>[���wI����7�)rV�7�n��5����������*��>G/S���Fq$�J�A�5�GL��#\��������?�E�����z.W1�H
�K�#�����_aA����-wX��#
7BC��f��C�y�*�0����q7:�:���Tv*��H^�I���1����r��F�	J�+������ON.��?��yYf���o�����$��U���Ox����9�=�>>�z��f�Q"���]��<`�W���$�r:��$4D}L*
Zq6��-�u����fw���L�n�I���sL�o2�����B��%X|i�S����tC�5T�~_�����Pt����-��R�aO�=��F	��o7V����'�����L?��\���a(e\\+�J2�{�������[���v���N�pw}��,���[��d����e���\����]�c��+k���EjN�q+��
�8_E�����"�0��H�i;]�U:b?���������0�e��q����Q��m����V����b�q8
�+r��0�
��Nt�-`�W����t��N[l�u���E��dB<�D��s��N��	4������`T����d��}�����_/s�Z���I55GhO�$X�uO��R��������n"/\0dv��6Qi�+ ��������^�D	�F�K#F�F�:�����C��{v���=
M14a�P��N�Xf��K��O�%6	����Z��@�`%�b2����������nmJ���'����CD��-1�8
�2k��fM�	�"�L��yj[p�~�� 

c����`�;!�Y�v�$@Kn�s
2�A"��4��K�C�M�����w���!]���0�c�2������������bDU���C,9�����t��{
����Dr�;s�8�4��G��:yc���864�00f��J���GL�OAA�����Bc�v\���P�������Zx�����y)�R[�J������Rt8*|73/BR�Sp6dI�&���O
�Nr0��*+������X�)��%�&��2��wQ1`�l�v
{���d`�PK�Y��:���T��vU2������}��9"�"��P��:D�>[���+����#�
��V��J���R��kge!�
,
��4���"��H�I��-��\��-4�"�����+�QM�V���M���	������=��r��`�":b^�O/�W�'�7������vi��?��A�C�,�J
(f���yG5I#��}vt�?�i��j��:h�
a(��$�z��s�����`}�d�|���mmv)�JJc�}x��mH��t�S�;n-�����|	E[e����i�T��)������K���D��������%5�+�v�����
|y����`v�F�
�y�5f�@�O���*�6,��;w�;w�'#M�����sF�a<HC�|�!WN��o�7'g��u�fp|{u�\�f�����R�N������T����pP��S^��fm&�����pZ!!\�J�D������Yf�T_�����ot����E
��_���meB��@��|�j�A���\��P��z�E:;m��:;��$9���_��D�Pp?��
&���5L�;�l��xw@�����Z�58!�$fR�\���������|���0�����a~c$��>��?����Z���oU������O�b�����^��_�RE���������f,���e�f'X����O��gu��;T�����n�|�]�(��	
w�b��p�jJ�bj�(U��,���H�e8}'AV���@�A�CO�e�xs�����*fR�����3���q�Ht�K���+��/��^�VzA`h�G����������5��t���%��[�G���djybqA0�����Ks�����D�$�����T��C�a�=Dg!Nh+��xa#x����0j}�@�4����������J7�SK���4fp��#ZR���
w����J�P�"�;��%�o����|�rs��!	+@�����T���.)���
GP�d���M.m����H9���jm
�W�x4=S��25�5�������rn�#����w�����5x�AK����"�x%s�g]����2#5W����p[	��������k�E��^�+��0�#g���hL.f?�! 3�$����P���|k��F$�������������[o�5�SZNmC�!H�����d Z��x��,q
�b��c�v*{�Az<0��p���=1��`p���>H
-�y/$l=9�_�zOrz�U]L�7##�=��p����$��h��
 ON��~��F2�x:L>��a?���,x����!���4��,���L���[�����%�9&���0
PC2z����N9�X;d��d���Y�M�r��$F�%)��l���su��B���Y���S�������9����}�G|FMov]�*n����g�n�Z�%ED��Z8}�����
��D�D$� *�#)(5���k�9=b&s�'��������,��1����`
�7�P>����O����O�)\}fS�q����=ze�;�b�������F��<V��
&��cd/�i������V^H��Z�������<-���_j��2�<1�M/�`
|e���k:�{h(��0��$W�"�k��	��.Al��g�	����f�.n����^������g`l[��q���fm��R���s�9V�
�4��#��j|a��`���=WdB�x|�,��Q%�=�C�Pn�A����;�HR�1��;��i+�����[�F%8�J�k�s�V
V�VlM���_r~YTP�X�#��ng4��-R��e���R����d�3�q��������L�p�����X�+����qK������kc��k"
��}�(�%Q ��������"��2.���	��0���oy�IK}��*��:!�y���4�lM/���S�S*�������R�Qp��h��p�gG�����7��[�
+m������w�{^����[�qw��;����7��[�Z������=R�bU��q��K�'1�H����b?�Y�����6	g���TN��W�#86���T����]�
3��.=�l�$9�Ly��(Gt���H
��.L�=�(`	������w#
G� X��l�������3?IG#�U(R' ���������f-0��j����"�B�5���H�&@r�<A���H@��H���\ 1S�9a���B�O,���Q�a#��C�,��5J�v�������f��$[�g�����B,���%�]�(��m�K!~�S<�8�o������F����\\g�7xa� '�T����eX��lM���%���RF��������
#�7U��y�@�]�t�����K���,^)����=o���dt��wP�I��{�x��P�����������3Z-������p� �V9�����!�61������p��{:?D��Ce�����"����Q8�Z_|��^�,W2�7����*$��z���HZM����f��R��G�^��O�[��?������N��YP���q������_7�#
��v�z���Yq����8\���,Y��h���������n��;h�F��������&���`[b|w;2A�~A�68����\���l�t%L>��w�C4�EDd�Tj/���>���.6�g#�
��`�D������OzY���6�������6��`'h����q�~��*�Dm���:{C�d����9Y���ywt�	�J&��0��`�����8��qt5�/�L0������C���"��1_^\�`�;���R���TT�g<���,I�e�Y�]n$o@/������t-OD��j����I���ii��qO�A��&���VN$�����������s�����������p/�u���Uq�����d[�!��+kwW�@���c��
�*N��L���������$v!��$��"e<ur��G
z��to�F�B�:���D�Sg6���	�������n���I3F�(+E�u����w�;4Y�D���*�"-��b���ix��������]v����p?wF�V���:���?�]�J<�&�u�����g��U8#�*5,�Xu��!�O�_\��6��z�l�>8�t��`5��*fGU�7��
eS��$&+����*]��l�����N�J:q�e�~J>��3��x%��R�F���`��,>�
S<#_���;��}]����0��SQtr�N����I�g�J���o�v�#m2tW�����lMa�����[��k��7�����"��������Z�E������x{X��E��L\�4�;�w��tC���2���m�v������8J1�I�K�,�;�*���o��e������7O&q:�����$�zf�?8j���Yz�lA�'�����J�$��i����^�R��ZYC$��z]m����w�,�_�~$��Q{��
�;��a�u�?����;�p�8+���<�<�H���Z���h������:��)����%N����N=�o��*]r�B���I��{�U|\bsq~�`ot0�v:��n��������+U	�rN�v���<c�'����\�O���s�E�G��.�>����w�*<s������d�t����r��~pq��d�6?��t�{mg�`����{�p���\�=t8y{���q7�wF(����xw������m�������.���������A���d�."���
��.��T���f��)�_�@L�D�=YPs'��� ��`o�L��[�\:�Ryn�:�v�;��Vk'�����o��`������e�k
�h�Hy���L��dFs��D��n�3/��B��aAv�S���5Y"�kmO��������IH�����������<��p2�f�:���.��=-R���T��|�)Uq'��pXU���=�25�g�]��\&9XN������������E+^X$G/3M�X����n,7[n���]�s:Z��=*���3FZ5��n�U�e!�9{���
5|���p����|�<�Y�dw�������oN/�\���S�S�4\�?���������z}���u��s���]v}�1W��z��D[�q?����^+R'�6�8��;_^.�Q���b~?���8�GC���W�+�H��2-(zu�:h��4|no�
9���{���O�q�":z�S��i|G1�js�9lF�
������:e�n'i�Ng��vw:~w��:�_�c�66�i.��	�M[��6zm�����paq�m�����~���W&LL(1T��e�%L-��G������]�9�Z2E�	�[����A��D�I��#�0����������	YX%������U�}�U�	|���0�|���ttRX��bc	�]�.��Q[�;�&� '�����.���I�&L��<���	����c���%��� �4T�j�i����(��������E}W��5��x����/t�Id1���$:�B���N�X��vCF
���2]TT��y���J ��GB�P���J��{6P�G�W.a�`:�$U��PxSYF����!�-
�~����a�e<���[e�#D$+2���4B���������S�7�P�2r������	��l�N��`w8�ia>��A	�'j0da���q��{��:Kc�%�rfEW�D�)?�R
Gq����	&(PS��"��H�p���������*��?��~&�T����<|`P����x7�'����w�����?������f�>j)��}�
z�$D|�q#��)�8\�M��-����������F�;�O�.�foe ��}�27��bk�tK7�&��?�E�,�
���d��|}�!�*rA	nd�����<�����x���3	ydY/���FG�O�[�4���__q�~��G��T���YdYE�0dg0�\����bNI�0�cys
�U�[;��o�t��=�g'�o�����s��
M6���}]S�={���,��d2�
�s��������2_N�it�
�!���u�t�u�Q@"�a�!�C�t��c$<��#;\��<Utj1���NO����`���MZ�f�
��0}4�7�IS)�(1:eg�u��^[��PpL��k�v�����b�����������a���Q��K�l��JV"�5����jaW������%(�����K��n�����%��d�&�S���_s�<����Xd���#]G7��rC��)�w�X��;��:����^�F���\�x����+�01���<�.n��a�����=f�J;#7�&�Q(E�����������V4�_��I� %+A�$oh��Vv]+
G���O��#�o�p)!Q��U,�j��M'�����2`��S���
�3�����ZR.�L���'1�I�i���Ci����/	s�;FQ1:�3g��2
�d�F�����ki�Eq~��J��b��b?���������QI���,�/Wx��S�7K�THi�R�T�@
[�`��0Z�2����B�{k����`?WEwt�B��R�]f-K���LDFLs���W
�p+C*��	y����v� �Sv�|-�u�T��
E"B(/e:h�V�U�����'%������G��#|�q�R0��;�������O j�74C!��~6)�w9j��U��#
�F���*��+�"�_�����]�'�,5��P���+��,�taQ(�!����x��1��u��<)��B����b�G6��-���T(h���L<��M��*P��Y��
����*����*a��<�\�)�������M$��.N��2��0(�.zA��dw��|��1_q��a�����
[+U��4���Q�l�������![�����HZ�/��1�
x�j#N�����=��z0�6���y�V_�����X�����M�T)*�:]q�KS�$E"+D�y������(0�����h�MT	���7�{Ag�j��������p�/��,�iF&����@�>x0����`4�LAy3����@����-�T�������>��!
���+$<5������\�����ck����vp�A.[���FZ?E���+�e�h:�>����
�WR����t"���5o��I_S���R6��Ia���I�+���t�k��V�����;������?���?�����F��Q���}�����n���N����~/�~s��N�����rg�e�������m�
#�1�O��@�s�XCe��V�%�#D�V\_��n�NqR���*iJ52z�W�I��[t�
�g���s$���j�\)����\1�Wj
;�jDT')�����7ZkP+�~�����q�U
��d�6~�s���.��r^������
����H:�0��"�RPizT�#��������f�?ml��
��e��O7iT��`&�������EV�4�'+-k���x�i8��h�\b]�OR��e����.)��K1I�y�r{{'���$�����~0��n���������.��_���H���|��������>����_f����?Jk�.�B\�� 9��%���������|��eF��RNNe&�^q�M�
E����~y�����7�N#�����>EY7�����=<!,�h
����<�
����)����~����5 �VgxWz%�TUg>g�=�6�N�����3����8�l�Sf���t��a���*#�S���O�K������2�3]����a���Z�OzSV��@�y�������0��;�_(�C�M{��_9����R�?:>��w�|�])��\���	�]	�tD������	�G]�J3��|md���b���z���[-V��&����s���4��%�Y1E-��K��
T� 6�����
XN�)w$
M!B���DHn���J0���Z���+��^��Aj�x�	�
E5���V_C���
��X��{A+�ne�m%F�������'
�[6�N������y��$v��y��AMm���}��0|(��ZF7Q���C��r^z���y��4#"��d�/k'�@Z3�so�����m���R��Eu9���nV�n�~�-�T�5y���0��]o����������5qK@��[3}V�b9�( R�x��~�PK1�����O�{;��5nT����jW���K�`�J���j���X
l4��M/�.r��<t xy�A/������c��9��q�����������G��~�sU�??�r�$�rq��������W����R����/�}vx�����-�JK|G��hN��mf.#KO��!s�������7PP=��g��O��z����b����x���i�����xQ�!+��pTK�r����vK��tM��K�L�1��-�q����l�cP���o���p7Hb���@����L�Y�����p�0�������/��^�]���09���?�r&<Ce	�8��s�S���
���$o=&�vC�� ���*�k���%�v��2M�R��#��������%$@TB
2�nQ�K�����X�WI1�1��v�4#�$�o�7~�����6M�S�#oz�cO����������>�#G�g�#;xv=��Y0y	���r;�������C
����E;��'x���G\]V|
P��a��SC���i�0�(�k6>�@��#�1%���l=��
u�`�W��#o��n��X���%�-1��-C����S�\rk�&I+��.�*5�D�����`������b�	O4CN�9���^�WG�{�)��^R*0)��lDX����M��������,�w�i�$6-�U������}�"������wQk^J�)�K�v1�L����U�K�!���rS�0���T�+����.�E�tP "={����8���B�Tr�#��|�>��2�iQ��d����m +��/s��;�L�NO��?'GD�lL�t��j>��~?	��y�WN-3GPX�|)�^�#�;�%r���i��m�&76�7�6s(���3��DA�� �SF���w����y���^J�2����!Du��n";�����'I2���l��c���&��H|CrZJL���D�F��$j_�~<��eU�>����z��������!�>X��"�S�p��Z�G�!��������,��e�c�QB#A�$T����"��W�p��'��nRW���8��M�����L.|�e_�.����R�,��5e�JR��n]�� c_hC��4�W���F��i������s�j����:j�5;�v��i^]��5�0����|"#�u0�y<nT
�$���d��+�o�����zv�(%���:��J��br�Ik�P��
��$�	G�u�:���"C"��-�]f�8'���=U�I����w��Z�g��5WC ��z�7�{3w�����V�T��K8�y��`�%W<����r�xI<c��K�\WZRO�����f��2N`�jo-�k����Rc20q��i�[��\F�}�\YJW�9
��SB�������c�aN�J^��&{��|�������������[��%��jh��6��]�	���.��{er�Ym���}��@W����z��	3�iC���'���Oy�������s8�J��E�����)P����K�����e�=y��h�	�^�-��r�������=|��*��J���:����������f��m��?(���O�:���1i�st�j�so����1�D�<7���������"w���[]������������������~Z�O���*�yc�Z��7��o�6gZ�;��b���W��
R5��@I�����{w��+"������Sw����3|3��(��uR$���Y���q3��@�ugc,�y�zE���h��������r�4����r��p��,���R��~CQxGh8�?2�3��Bf7���]z���/#��6��(����G:�1��y���� �������b�95o'�S �P���R��u�N��/v���'��e��i��L?_�^�7��{�������cq������I�c�US�W��M
�]5���-5T8I8n8I����2;+�*�Z��-�w�m�V�P+�h����1���_������&���(���h\F��	z���~��4IJ=�����5����������������
j�48S��������1���[�������Gt��jp���>��P������=�9�g�>Z�N�QL�9��J���qp��/��=<�;����d��i�i
�t�X��*�*�|�`�~��t��D�[�k:��\�1���[d��K,�T-V}���W��3D�(�7����8��h�m���;E=u����6�b��Y��>R�@�"�;��L��`�����bT�'�oF������:��S#N�P��\� �&r�M�����X�G��T��{.�8�X�-6�jn4�]N�J<���S��^<�u�P�W?����|�G|j��{��`8�=�-������0��epf�T�����Q���A�<p'#$�������^8��'I�e���R�z-����Z6���x�/v3�6{M��+�+adw50�rm�����A�a>)��h2W|�p��� q�>A���j�����yC�Rg��2�*��wh*�wz��G��LM*�@:�����o�������
���=��`/D�Pg����(:��W��~�y�������������g���?�p�p���h��W�����r0�(���V+� 8�{����	�Kn:�`����>���"{P�FU~���a� /
h���$nmQ���������S~K�yn��n(����jB0��G�x{K�4#��A��::9$��
:!'D�
{bg����l����S~�{Zm������o?�}�����l�W���{�S��:8ju�������S������Y�)j����o����ax[��%�bw��I|?�&��@��2�]��-q�K�#��4��=��'#�Z>v�:�����b� P��Qu�{|\�\&�������A�:�br_�.����{h��_]���3#n�����$�����y���cdS?������|��o��������u���W����Szn?������E�h�	��7�o�@]��`�n���=��Nw0��*'�8x����o��[�{���v������c]��{����W=_�v����yS������H%���tSV��Q����m�n��_&���0���O�Jf����O���"�$��-|t�������nU�6)������k��{8B)���h�9*����%] ����
��,Ia��L���5�r'����U}EX�h��3\X#'�2���DQ2����)��z���BlB���4��zS]7�7XD���(����	9<C�@����!}�Mx$���P&�"��*GR(�K��
�>}�(K�����RRv������e��] �a��s��x��Gv���L���t����
cS��a�q�xjgS�C�v�=�<�c�+@�C�����e�^��jn��d�c����etP�L��$%���Dg�b�8����^4����]����\���u�2��l�O4i��,���&������"V��nyW]������;+�jy�e��(��M~�
�����=�r��U���w=�\*�?��<>���U�dW���J���=�^��O1"������E�����>�/��(�K�dJ�*Yo�������n#�-���	���#�Dfz|��9`h5Zp��z�[�5Fz��A�F����o�O|�6������
����{�� ����o���o���u�k��2�����������{��>8���/T�-���o��H��?z����w~�2��[ �*�W����O��D������GV�i8�!L�#���v�Lu�pu����t��e���K��U<`���D?������I�����)��?�����!&2T���y���y�������RGa�>�N@��g���gZ}���x�������
��Xj/�o�j�'Q4�����)M��A$93�
�c�'�O�N��R�b��+G�6����8o�;�4T�0�;�&�4�$"���I����c���?�=�n�D8"N_.���n~K�/(��J]�,�U\��uV���?���e����}K��TF���]���!�J���#��h1��\��66��i�g������~����t��z�25J�
�M�M��-q)�<�p�<h�WM���L��
��p��H�u�n�O��~^��0o��)��G%��V��p*KW@'p������y+Py���RuX
SG_�c�N���o��~���p�bUq�!�	l���-�q3�H��c$��W��p�8�sN�&�>I��b	S#�7�,�����C"DY:�C�+:N������uC�q�r��J~�MA�NS?&�������:��N�1����V�a���O��G�
��� ��C^����-c
���+O��)#����nFYm���H0���
/p���	�Ls�em��e�-K�F��R��V�	�pYt�0�+2�	��QO0-��+z��������/�\��q���7�����Z\����#$�[Ru��c��QtO�N�:6���ne�g��u���(��b�Y`�P��^N�u�K,����X�� bG���q'���4�Q��� ������cR��'�[��3���3���>b����x	M�$Y�|��c����c�e��w�?<�v�K���po�����h����%C_��'�5�|�~T�>�T����@{����?HJ��h��wo������uF5)tL�T!3����~b��Gz��*�9ok��t`�(�DF����?�j�������WC����v3��9&D����b�+�MA��}��K1� !�^|����wl��<h�WhV�W<Z��@*�{����]C�V�Z���knT����D@��_��
C�8�?c@$z��H����~����J-@�Uu�t�*Eo�����l�[���I���w��.�A����������l�����Zt��?E�jM�l�h�T�~�%��o#��((����b/uy�/�)_���|�Y=����.�\�CELM��G{����n��L]��Pc�=
��xsOFJ��(�x�
�E��$��Bd4:�G��2]	�����eY������dg���{@z�,$�c��&����������`���gt5 �C����P�����9��EBf�����n����}8�(NX~
����z}K���'s�!�����];�k�l���0_d:�s�{P�v���)"�����.����KH;MtX��N��d��������O�gS��!��
3h\m{�+'^��VK`����G�����-�B�NB�Pt�w�����7`0=������'�|�,�Y
�|skj4�D���,� �����3t��=B<>Y�G�U��a�Ko����T�w��)������FxB=��ET�.Q.]��'	"%��o�)�A,��j�u��������_�Fb9����Y��*f�PMnRi����a*o�]�{J��)]pw`VV�Ta�(8���d�H����u�5�c��<����@���g��N�X����@�;	F�z�YDg�����=SC��t����U]�V���qc~���apV��&/�8 �CTh�*��Y���x8���&�����9^B�aW��W�j����TNw���y��L�z��~8!ug*�����(��l�Y���F���}k�7p�A1�`Jr���x���������9��0g�~oC����	����"�A�w�h��7��������>B�ovZ��N@O�X��%�=��]�$�?Tvw�����g�������a�����Q���wP>��{{��|0������\=:~����]){�{~��{�C�X	��,��#����s�����5��cd���FH��aO��c�,��K�k�I1���b�3����C�I%;��XK��y���4��h�K��1��g��~���������������b��g���d�?����{5�8�}�~���������_67+����)\�_)����zj��R�X�t\������J�tT���~yX.'��R�I��z�(�hz�;�������f�����
!��x�%��;����C����b��d��W����������b<��J
��O^�w`M�W����Cof�J�Y�-}�r��QP=����U���8���X��c���?�7��?.�No��^x�F)�������?�������]�S�r��j�7�x�s�q�n^]z�����g^����>��<o�6�un����=q	E�XgRF�
�9w�nt��>�1J�~u��y��]r�������T�c%���O���Z���7@��Ux�FH`1��T�����h��-����)�vJ%�*��&jXeJ$�$?V������;�ui�E�+��2���E���������G�������mf����� ��/j^�R���d�p��#	��G�Y��������|�Q1�����8�Xo�������:m$_�[�-Y�pm����m	t;�A�X���z�E`�DD���^q �� �U���&h.���%\ �P���x�e����K��/�E�y��]���"��4������F���4@�3��#��h����n���~��]���i]��v�s�A�Em���+�W���xY�h�*z���Z�:�]����^�kwZ�z����j���s�]��xc-H��G�?z`���Bu���{�
���\��0'���V�E7���6^�Rj3�����qz>�a�e������9������vs��^�>��ZU���������{5E����-�������]���e-?E�?�O[ws�&�)���2��]������W�<����g�~��l���]�m��UDF��( M�.mwj'���f}
�G�����Ui�~�wt��;��;����O��T~3��QB1LDB���C�#�U��:9����m}E���^���._>�T���v��'���MV�e����E��!O�}[����rEl%>������L��.jg��~&d��K�\)XC"��|1^����]��:k3b�U8XCp�	������{�<-�-�U 5�?h^vv������
��$�&�,w��Y�y�u������\6B��w�Q4�^C�dxH	0��
�XHy�_��j��b!�b��	7���w(�YLG���S�R�C2"�z��K(����5��n�-�Z��A��$W�'������p�)y��F\�-F���A�n����Tj����?CN#���4��=����@��[���}	�q]����H�M���?H�uo&`�� 
���
��U��Ng	����6��P�a��9:
L�1U���K���)��\�����
����������1�Kk�p�g���'��B0��"<�_�6�u�jt/�:�����U��8�dF��8�-l��|��
�1����C;$�(�<�T��M��	�o�g'�=�^z������P���>�����x�@(b��p.&�
�����!�b� �C�����+��Y�hF����Xg���m0��v�-�L�|@�K>����w^����C%�_\_������O�v�
�^��3�~t�;�R��<8����k>�������r�hcb�NTpj����Bfkw�������x�nZ����h��t��vA����b��[���$�z���
���-��7�2 �~����.��Y��������Z7X�%��~�U]�.z����������:�>�CTc9'T���w��k�M)`�-�#`��3��5o���X���7W��bqI������:���K_��7�����4��`�qz:�I�T;��}A�S��{�,E
c�l�� ,��S�'���b,HOC�'��r3������PC`[U�}t=����:�4����������]��d1��!P[� �
/MKV�*H�Z��{���^��g8��"�Q�����3��)�,������i[�����]@�T���������8=&��R2�j�cF��N�_*����������K���z(�!]'Kh���x����|X���2�2���Q�T���^�T{��SQ��j�Nk�2����J�c�����p��J^%���r������o����N��T��V2�����yXMr�����7i����UU���!B�{�����Q<��~�6N6Vj��<��kl�z�V�ZW7���?����t&-aS���bek����b$�DW�.$�7�@oj�+��O��M�SzP�c����n~����H��?�m������+��{����U=@���ZMm��.#��}`FO���B2�ML����g�ht_v���1P�AJ��b����������=q��;���=e�R�h1v����,Vf�Hq�q��z,|x�m�����R� ��o�O�f����-`�71A�/O���{
�W�����j��r��}Cn��������s�$FWn��U�;������x���
�.*�"����R*�j`��ZO~�����F!���gQ�AY���������/����
)��	�	�x��`����hH.�����z{-u[��U=����'���{����g���_�����W-���q�h����w�����c�hP����c�_5H��G�|����(}�?r�?;w��Zz��Y��a���*��f8����PO�P������G�z�C�Q�HRi}�\[����i[CX8�A��a�z@�5�]��ux��_}5@��p�.����xKE����\��.�.��������aDJ�|��w{�X����u�a��!7<�����i����p��wB5��f$�������Y�m�;�����zv��`L���b�!�'�&��������OC<C�D-��"��'6����&��N�,��-8��=6p[&X��~�y35���#�vOv[�/;6T�w�B��:#	WqN���:$�-����A�F!��=�)��O���]��N�:�������.�_����ip\4'�����p��q1�1nA�6��
����Cp������6� ��,P��-[,l�B�����n�/�
�G���s�y{�Vv9��^��bj
y�vy9���wl��7qY1���(>���`�s<?�������F	��{�B�r������x�Y�����V�7��eA�;]��.m��f��?{{�e��]V^��v�j���d����P���^���[O���������f�	�������N�6�6�T�����,����Jv1�kr��[�h��p����������b#u.wF��E4������m
�)���[5Y�,�`��]��P���bb�	|�'��:�`(q:�H�����dk��bL�f���G�.U�z��XV��I&� �K�2Z�R�8+�,�o��b��NI9��sa��O����w�r�����;+�Q&��������K�P�Rq����.i'��g���C�Xq	��S��.��';��;��Eo�L����v�o����Ie�����6��d�Z�9"O)�����8��[�CV��������^��T����������0#��UZ����]��E���\m�l:��Q>��(�~�x�_=�c8��~�A/��/~�G�����7O�_7L}��D%�a?=�������
�L�{��d
��V�e<�JPEv��v���b�>
�����7L����6R_~�V5���t:
)�4�6���e�`����uQ�x����)�w��^��o�IY}��S/������
t�����y��
��o�b,(��W��!��:�L'��Y��
>��VC)
?���9���u�Ks"���Z7�X�&oZ���z�l��x}�����x�9��t�	���n1Wdd�&�r��#��$��e�s��y��[�1	%��'�bS����0��J��A�����rF>V_.�I}��3�C�������y�/�{wM��������o�F���B�:"�7/v������!G�Q����������n����������F)@��)k&eR
A"�tr��d5
0��;��N4�J�c����C�J!`z�P�A��;��A�-L�h*o4��'�%qe��6���h�f�M;go�#����`��6��t<OLM�4	G+^����u���U���7/��n�s��8E_��t�^C�h�|���T-��c��{��l�t�!R"�$��5~Z�� 6n�O�lrj�M�[�`�-&{{�O'{I�dm"hO�?l��F�����q\|�a�f*Jb���=@9��V��� 
������~�����l^�9���E���D^O��ze0���w��o�_ �3���m�|5=��1��?SC�����;��dw�x�m����>����
��8����,�{0Aw�/��Rq������*�d��;.����)��2�b��tYL1�^&���bP���l����6*dg�
P�@��P&)���,�(�Y�v+�0���	&HJ$C�
���(X��1��^�~�b�-�L���|N�%�G`��	(��F������l�D�������8�����P�^�q�B�V��6������+�tU��'V��0�BF������7�d��b��D����{x8DE$4�{4#.W�V-��
�@q{��D�.$�
���gs2T�V1kL#��	��m
1��yZ���������aI����X��������e��p�*&��
�p@������
���A�R�c��o�t7�!0������V�{��S�~�)x5q-���SV�L����vWL�I1+t	@4P��F����qwXx����V���7�h�a|����4�;Fd�&��������f|
�V���� �IkAP�R �2��}�����Hoh��#?�wp���3���lk�� ��������9�I�<�s��i��Y��e��X��{�w��W�e]U���K��
+4�]ooX9���}��(��B���\���L��h���*����T�r��a)�0N��������-?VZ���'����(��p7�z�����W�`B;���U��4�6o\�����������q����%���t�!����������*��}���
��ZR{���P���5���lS����S��6�_��e+ L���Iq0��@����`l
�hjM���9�~�����[��������K�F ��c@�	��P���q�7<<><�4Pm���6�~e�6�D��jC:.0l�}��k0��RF537�po�b���a~��7���h��o�������SKJ!1�}7����u���7�P��Y�/�,){�U�
��m����9E������{}Ik ��F�(�Ystl7e�u5{������y���/7a&N'7g2��;i���Pe�w��:�~H�QM�#����|��1$,���\�4{�7����_�.�u�:�I������m���JKOi��}���k������v[�+I��e��v%�
�^K5a/I�V�H���#b�,�3�3�m�?@l��+p��g�])�9	����3�$��_���s�G�/�Jt��_q	��p�
4s�pw��X��[BU���uB��e�~��{�9i�'����W7��$+��K��Y���j�����!�����	_�^u�s+�Im����@�o��&����yy3��4.�7�j�Tk?h�pQ�6�������&(@�����e2F����qb��I;:�+��?u�
�� ����	�����"�jQ��b�:L7�[:&���=�n��l��/�q��`N�b�����$C�_&f?|���������.b������#��RX�.�;Y|�u��K��d���
������<�yVRC�thZ�C�~�b��5�����F�7�h�<"���]
*�}�����nf[���H�1S���P�6����d���v�n��Z���A�r����e�N����v����R�<u����r��GK�CPb��kI��8�����v�r���	��`b(��6!�(Qe�0`�����C

�=
'����U�;"���������(2k�W��{>����+^�|%1b�H��3�t��r����?H�@}r��3\@�~�
'9�Z��Ka���:t��������w�	
'��X+��v�U�����
�D1qn���"���)�:��|ptp|��U��R������`����V�+����g0p���S���]������Sp���m@uf�f���$��(���������6\���("�hU��|=�9�WEP��d��i����	�;T9s�z1�������_���y���?<�A��T����rP]m�e��|f�A{�aq��R�!�KiY(��JR%!���	>uKIy�^�����W�u�G�b�#(����h���<����]0d�I��"�H82�-E���j���h�)�u�	��,p��3�P�%� t����z����fJ��T��������D��Z�~�*�D�V.D^�w�G����:�2J����I}�f��5Q�8R�/bt#�r�V+��8H��|�FS*��4��w� ��.�����������	��K�Q:U�/^�M�=�xD�n��xDXea�}��Px�8�������x9�h��UM/bmKr�{o���V<_�iV���6+'v�7X �����(%N�M�����:��j��������Y��M�#�p�h
��k����������V�iX����������dRm�;�e`���G�#2�������}�KjK7�5����^/�����9��/y��f�>��p�+^�^/��w��!����fQO��+
#��~�y	G��
���h 6�T�p��BE$D\�������
��?]�q����@�0}0��P.�q��<����������/D5��e������d;t���q��t]Q�W�)|�+z�7���P�O
��H��D�>KEB�]�'w�%6�]"�L�$�(�?�R���i���������N/$��1������C;}s}K(�*Q����3j�K"E���u��� ���,�-$�U���@����5Po1O�D�>�*`5�=�*�M�NB=��Cj���yl�9�0$r�R��L��3F��1Z���n�b������Q5&=���F&9�LQL�	��p��f#�w@3R���>x��1�Om������������@� ������O�z8����Y�)p�r�&��Y����z(�	�	��^�������A'��6&0V!�/��vc�:�,Q6 Z�=�]��5|���^�c�
@���pn�V��#�����Q��j�\	�p.F��U�����t����C0ArT�GI�"�B
��9�����j�vs�9�����8���E�e�Z�#��&�7�S��4v��'H�+m��X��������}��;��������h�%���i��F�x�l\q��~3�AH�N��Q�$��
7$g���/R�f:�,u7Q����O-*�P���d^����C.�[�e�����G�9U�>k'��X|�H������:���.`�p�y:�����V�Y(1�m&�I�V�^�TK5}�#��4��W*f�%^DK.6���-��E�0O0��0j�lO,5�#:����3{uS�.��7��F
:��6����0�2�F�����OW�?6Z������q���9l�1\��E��<'��^�bo�~�����5(H��[�{a��4��������e!��\��$f8�A���	�^%���|����)���������7�3gj�Z���V-Oy��Sc��!��A?�[iWf�g,�!N
RG���Y�����>�,RL�Y��[��?x������V�P|��n�)��&����@�:!���XkJ�y�$z��w��	3�Lf7�OUY���Y���9�M�����j��"
��=��I�h��3:�x�(��,������7D�jV����:x'Yw�x���z����O�)�t"���9���������;	����Kw���9�����Vw�������gaz��o�(�,e%E��"����;�e!��"-��+Z�w��<m\v��ut����6eL
��������8����Z.&�,S�~��d����B���h�/I��J��h}S��%���KM����:���6^ ���8y{�O��pVW����T]�{��
����-�k�9�Vb��}��B��#�O�������������P�,�VG���+O}��7���e����D��A��}f���
�+$���d��H~��^��}�{�z9/	�,��e����-�n���;��*�'��^�spv�w����=4r�n7oA��%yVh7�.k��=
}�v^{j����
Ii�
�G:	�SK���
6�$��h^v/k���E�{��������I�����y����|�{��Z��>g��_������[;�`<cW���h]XP�\a"���F���{T'��!�W@�jV���.���D��z���g��@!�o����?�M��X#l��Zk��WXlJ�������
f3���?	2�� �_@u����X��9�~`"�'����B�������W��2*��j����8H�X'@*�s4�����~�Zb2Fs�Y	;��<�hLi��S��:8����R=(ze�����<��/%��.�s}����:����q�����v�������Z�����.��>���N���y�*�f��urK=C����PG5���$���
���s��,z�$Q��8Ql�����k�����z�����P���3K��A���&�<>t)�l�~Lv���B8���]�i-������l+A�`��QM���i�	�S�<�&�)�98�:s.
�U (8�1 �Yb0����O���������
$g�f�
�6���,��{p��,P��U�"0��pQ�Iu���E�
���
T`��f��]<�����(~t����Zr}V����v��dE�G��VGJ����2v+���\VU���
tP��9��[�-�C��nn����D��M�s���Xce��
��`���E��N�L��$n�<��Pfh�d��{���v����b�L; �k��8��������`-2�c}��kr)����N���'G���!���P���nm���4;�>�$���O{�v"�@�9�B!a
I?�l0z~`�6_���VaZC��#%��+J�i���u�D�RR?L3E���	�yT��,���y�_����V��c��y�����t�8H�gV�K}HI���p&�5��3�8��.']VY��9p�a��/��h{�����L��4Fd-����I��K�B���,��d�J�jnSr��Q\.�_V�E��fue�<x,P�4��g�i�P�x8���T���
��q�C��
h5KP- �G$�h�(\t_�+Vf����a#��yTM�wr�Z8�@-����xVt^����'�T�F
�`c�5 �zA��%���_�h����*��n�|xH*=m'�KNQ7U�&���$b�o��K	IU���y�2������B�W7���U�sQk+B�=m�:����vm5~�s���)�6��f�������������N����ly`����xT���h�n�;���;OB�d�]2r�d�eLn�
�hw8A5������:L�0x
��cH�+�
�P�����b*�Z���@
��
&}����B�J,��i_�s�c�>��������g^�=�1P��V1�j�(*��9��x��y�������Z��M�����kZL��-��������#w���N�!Y���h*~��4�:jY�%��Z��-�&�[	jp��
� ��I �l�.��r^�yR�3���3�P�<A��[�q�#Y��rRPj�|E0CrR'�b��D7T�������m�*S�-b�A���l��9S_JIg�[)J�����Mx�|0�?���J�T��+�a�r��_-���p������-�o2D����0&b��Q�I���m�^T����QI �(��?g�0d9I��`7�5b�YR�_�;�0��@�	�����R���k�X�+��Rc�lxBYr�N����T�vj+���K��>A��k/�O�7��~�s�dL>��)@L�i�����`�K�������k: ���(���z=/�U���#<6�=�&_�,X����FS0�����}m]���z�����]��;�)��Ab�x�y�l�A����FH�r���!.\�,�J���@�dHX��MC����� ���P�) �����I�G4:��LD�����E�k�\9]����7^j�%�O-�m��{����<�/� K���[u!i�]�{�k��zG%F�V��od�N��0�	&Pc�~�YA"R�[Z���^�v[4�3��c�(��nh�/Y+1�p&8A��(�z1J6Wl��}���[<�J�|C
���m��_\�6?���<���t��Wt�T7�E��A�ra<�2�q^PkI��2��#��pf�Y����p�z�S?a��
3mR���+f(�z�rM9g^I����������j���e�^��y�|�;�l�%�[@5�x~�m�/�M:4�ky�cY�+�m�>�i���9(�����F�p1����N���
�	�sl0KwH���
�jM��DX=rHux��xj��5�oa&��B{��~.��q8_��`��uS]_`�.���� ����S�>vB���`L��|�CD
�D8?;�������DV�H����I��r�;��Z�$��9pu�����UG��{��t	QR������6�m�~��d���h^�o>|h�� �����V��b�I�����5h������@���$�#�:p\X�f5W���<
����'��	YP�+�t�^��sxN�
?������c���I5��K6��Vu���������z�$�;�5��2 �V�wn���&R�����Q������:���U��ub}����/-������&�RD�	.�������h��.���%,,Vf�y\d�U~/I�,h�Q8bi$��ef��R����_L���1��\���+j�w����7P�r��vQ6>^��4{+�V�EO�-��+Zw��#��� �	��"r�@�2�u���H��"�9����j(����9���kg����m��V����t,�a,��d-�
x�t�*[4�Z���`RDCc���6�5���-�������WQ�!e�P����ajG�w��P{�j��)�P!�3+��\�@In�@%�}���(�%��������	*���:��I\ b6X:����4z��~�����fy9�v�%V�A|����#����U�������"��6V��a �#u����. W�����ip�X�v����i%���{Q�A�E��S����b8;\Wp��/e��a1�L�G��������um��(���q��+�ns���Jt�7����N���#GW��An]P�I301`~(��:����j�������@/!�|�)��e�?s�VA����N�q�)'g�h_�w1��8m7�)��B+���^/��a�m:C	V�-�8�C�bd��|P���� ����Q[b0z�d��������^����a�N
�C0���&l�ym��q;���H��f���1E�na�	c�E�B���b�6���M� #��V�es��5(�!��UR�8Q7�F��/y�|Br2�-	���������]�U����,E�K>mQ���h�P��m�L��(U *�5	��A�6��+C�y�q�ms�
�����N�������e!�LI
��f�?����B�_�@�����q8�[���������K���P��un��=�|�����S�^]����Z��]'�w�����*Y����W�C*��2'=GCg����%L�$6��_�!�Ev3��!1��oE�<��h�9h�������?�w�W�����mx1�����������(X�#vC�p�F�Jj]��4:�t����4���%R~��=�J�s
`���T�e��x���k�!��
3�*�dH1p&���c��0$� ����.W�h8/�?���d��>2�,L��"[��_X�,i
�[�9���ZanB��LXp�h��80~����%���A�D�ja�6�RSL;�d���f����S�A�n�:a�29U��5���4��q]2;���%������v?'��f����L�U�������S3�_R���``[_��������z!�?�����L�`Q^2'&I���(����Ui����|�
z�������l:�'���S>��Xs!%A���5�(�:�*����{ ���-)��w3�o���S�]i���"C�/x�$����'��t1�sj9�Bn�E�a$H������q0��+��v��V6;��D�z�n�5��H4��%�&�W�%@��@��!�)���_k�q��~E�����3V0Cc!:�!���]v�JN���{\���@��f�'�r�,�J�hgd4��|�^gE%h
��\`����m�f
����E��������!f8+>��r�HOY{Mn���p����%�51��[�0��"�S��z��V��i'T��	�����A#���b�C'f��p2Ev��.Dt�kMc`�1���������:H��k�Z��[���\P	���
�����)$!\���Zr���n]����F>�<��	��W�5>��o��?7�]��P�ix%R�a\�,��
o�p���
����=�(��;u���v�IK{�����WT"�����e����D;[i��;M:I
��~#�Y�7����sT2�+�CO���2�#g���(g������l������Z�s��Mw��z$/�N��+�@�&Z�t����O
��7q��U5���AFG��D��xB�l�h�f4S��l�S�O@����cr�t?�a��b�����#"��/��j�>���LeE��������,m�����L�o��q%�~c�J��dR�T�l�%��AI�h|]��Fg�����9������L��-Dg��@�,D8�M ��T ��qy��!a#%�����c��)��!�=2]�����@��k��E�H��&�{@5��
%�.r0��
�%,�xA���p{8;�Y�ab5�����3f7�fg�uc����

�5,Y_���LGT����L���&?Ws!{(�h�+�\pc�y��@5���B����#��Iw�`;m�r��s'�d�B�Rh6���:�c��mY��ztL�
�bb�TT������F��V��01E�O��Me����F��6���%3�-���_�u��[��M���.�-"���$�B�P-e�[9�0�a*&��3p
�=K��H��R��7		|�%Z>a�g��Y�����H���M���J�������tc����n���k��w��Z�{6ug���Jt+r��:�0���M��R"rK��m.�
+3������>bQ���f`�i��f!~�o��0m����C�d��j�/��d}�V�a���&�-�{yDhw��b��2�|��5���.�m�kY:�����]^��[K1�P!~��PR�q-�����&��PDS&�f/u�4�V@pz,���j-5m�����J�F����n�ZT�{��K5&�p����W�D� �U4���F��Dk\�����}qD,s�JX\����c���J�'��l!�q<��e��EI���)6UD�,���K|���R K����:	��5������Len�t@wV�=����
6�vv�$�7�^U���/v4�����?�f���`Z9�
��g�U���?D3�M�W�������7��2!���������Vf�/�2��q��X
��dL��.*c"#Y��D�5���L�����^p+�| Y���oJ\��q�Y�������l�8GFA��� �D��6�^����
�In\�/C�O�U�!�r�#N.��"f.�G���>�]�/0����Dr��(_!d���	�Y4�b����H��k����#������8F���5�����\��(��>i}_c����$P�@���U��LT��)-�^!��2��c%4}K�&�"�l�A|����%��["F�[b6���:����0���������d��}��:ws�����N��p����]�����Y �I����r]:�bZ�R%4�I�H1��o��k�Tw��	\�j��6�pQ��8���
����8����m���lA�L����^�.u��5Y0/	�n
�*��m�I����@��
�?�QQVY����g��h���0��c�������xQ9��"L�a3���[+sW��=S���%)@��������$h��5IFF���6���<0�,!��F$9Z���b�x�L}%wK1�6�r�%/��t���%/#�;G�j��!*w[oQ���s=n�H�������3���WH.=�$O
MX�w��B�2mI,T�,gh���T�@����Y�y���^�REF$��]e4��n<,t�H����%z����5l^�
Q����(*��KR�.���l�3q�+2�KB92y��=��CE@��(/����s��=�F�?������ap������R��_����������4���a����W���-�QM�����^NL��
@����6����Ww�?�{G�i�K{l���:%�^:F�	���_+.D��R_��������Q�x�*T�?��9;@)���-�6�]�J��[�K��4�R0p=�0�����������H���j�*�AHc[HP�
7~�C�'%�C�34P"�!
g��g��������"��%0&9���������:�"z�qu�X�=���D��U��X�I��E*P(�����M{(��p��o������Ag��Yd%p4/c�4(R�iAi������b�)����J��'����H��o�#�/'��������5M;q��st�;�A��4����p��
rN��IP$�#�]������~
H�:b�*(�K������T��g��!+&h�F��~G�(xb�d/�XH�>J�A�O���hR��;2��.�6
h}�5�\Fl����1�N2�c���i��U������B�-�d5H�d(�����>�83��(���!G��6M����z��>�@4�H��J��~���a����<�������
tXB,^g�c|Kk�/�E HzGx���e���by�:�km-���b��1�-M�Y�|c��_d���j��#�8���NlM4�j���a�l��^�Q��!�]T;�L.f ���;/R(��c������G���O����?bS���XBs���O�H6x�e���v
��s�����s%����nnN"�&e�uO�; ^�x@r���/j����$�����D�Y�����lj���4�MJ7������R�������_��_���r�$����Q\�dRy�vI�~�9�xm�m�)^�uK�q5�6@5/R�����\P�I�C2.�����)�z�5s�X����f?%,[�
C����i�����l��!���7���Q/���g�W2�����G~�|���hQ���	o��i`�6�t?����)���F �d����o�yI8m�g��=��EA+������:������nZ�z�y���{~u�����?bP~9�Q�>��s>��i��;�jj�������5���j��6o�������2%pK`�f�*���X9�)����a���3��,u?�)������s���;�z���k���{D�~���u>����0��B�j���H��8U�\�X�0������v,�H����.�Ca1t�]g�hur��\�hy^��<u�\�e�� .k�Z���~��-d�/�m���V#�D��p������s�22�Nd\�s��
������=��S�P���R�> ��Mm'/�%���B��d�������_,��>�����!hA]�w��)����2��"����)bm?�z�vA5V)V��=�����������`��+S���H*��O�T:� $N�������������~��������Dss��W���Ql�W����;+ih:�.��h�;
|;vF:�d���6�E"����Q]�o�oA��&E�3=�l�v�O�I����������8��k`[�?q ����($�NpX}���:��@:�E�f@��#��"}2�#�Q��m9�/�����gu:
t��SI,�h���������AH�[i�|;���#�@D�"����R(Be!4_"l����vUsb�d_��zM�X�����1��& L�~�?!@|�g4#R�w�}��qFe������x��#����h��b��	#��2�@���r�&Rc���W���E���n|@���-g������u�s�sm7�'���gGQF����UR���+�y�U����M�h<��&e�A<����E][����`�iXU/������@��A����s�B�D�i�T��T�0K���xf��cy��+6#Y���Q����Y��?I���������S�6���
���t"?�@���}��V��;�SD���f��V�_�?`���{��Q��gf�B��z����ec�O��#CT��_��0���u���>bXTz�lc�6
��$E�(�DSi�4���>,��I����\��E�c�gH�Hv^����
���x.`6K���Z���be�y�6'n�.�6w?�+�m��!�M�C�U\]��Q4q,
(���	K�&*e��s'�������'�i��K99�����}��F�g��/5@�L6mU������S%/�y����=� 
���G������������m@��.�a|9jfz�M>���`XnZ�����hF�����
� ������5����*�1/�.��1�X�6]�<�$���;��J*dz�Q��h{��}
�,���3k%>�"�	�A�YuRT��=���r�_-V�y�%D�P���>�
yV;�����J��l�!�@S|1�����ot������=Q-�s�(���$Z"u
C��V�O7�6d{�s�\�.����������"����AW��th�Z�FG ���=qsO&��4!k
0�=T�n����L���uu�j��o�*bH����xC���������!��.]�)���������wpt���M]��d��b�-I���I�������ir����5r�<!j����HL�;�f���?a7�`wt���M_�/��n������V�vq��.��������.tcV�����o��B�I�.������)�����;g�h��k���Y�u�W�<4{�M����$`7�4�5�E���R���o���IJ\�����,�U�&%k�����I"���MW���ZuQ��D���P�UQQ]�t��(�F��#e�7��@	-�'�'�'���~��H�0�aQ�lA��P-"��W�V�I���+
�i|�;=��Rqos�`8	RV����V*	2�@�LA��(]oD�8�1"�'���'����"��#'-ZmGE���y������B��Z�\����3H��5&j`b��l���v8�K�����\��p���|_�	x�,/���O���Q����3�)o��]���L������ ����Y_���������5��,��B��v���5����5��%��e��&9f�}�����t��j�w���;wq���w�����|	 L���[U{������c�{R�����b�r������x����"��c�!�9����+T��Pg��h���%H+��!���?8_��^���bUD�'A`���b�4�u��{F�c��S��B�=�T����yPbO�C�>'��t���6>�n�;	�U�sk�;P7����5�$����JT���S0�P���@����<�i5s�i���T���gY�zE���M�V�!e�L���d{�
3��a���bLbpd�zV���H ����)���m7�>�\[ ����4Z�-��P���������jV^��_���������'D����?P�������
�i�����3��$������B�xwL����w!��\�\����:�e�<N+kk&�	X�]K��G�u�S0����%@"gJ`����5��j�a ���X����k�
���@�+ ]����b�p��^������cjJ�$,��l$��H��2c&5�������Q[ A�BRIn�g������Th���m���e����
I���qd���4t��A�|wjM�:�;�q���?u ��>���������D�?�;��������|8o|n��7��VHN������;ez������k��}t�xg��j���������j�B������2�Q�O�Bj�h����\�e�k���/p�Ek2F[��j�3�X�*O]i����?�,(�vY;W�z���/p����3�^:u����o��dq�����7a�����������/2��93<�1Y�o���W0\n.����&}
&�B�4s�N0
��H�5@k M���A���5�<h�d���U�x��������j�"�h�����|?i��Rj�wj���GLR�
��l���'
��w����4jz�3�d�	�����b���>�����b�%��C�4���4g�@�iL�4F�����J">���S6�yd`���
5�6M~���q-t�[9��C��,�_�5�T��'���<�C�V}�<����v��{E_'k������Z�&X�Z7���@�%l`f����|�IC��,��X$`cq1���{Eq\y,������I��G����E�q;�i[�m���
�P����l4� �J���XF��p��@jlS��WFrm��A������L�����N�>}��;W����j~��$A���\P��0�o�'�H�{:=�����3Y��q�����w��'��mU����^SG@�O�c�,P�p8����D��'%��t�=�d17�N�&��V6)���Qg}��@Zh�g|:���:u�����3�	"%������t�.��J��u>Z%+��������jC�@���Z��k{��n�t��iq����smS��&�>�*�,�C��a@����z����`��>��$8��.�!L*��7*���.%DES����_���Q�t/�����n5���l7;
La�Her����_���f���v)��.l�P�{t�������p��_���1	Y�7�@����l���7���|j���
p �p��Q�"_����^"^����������B�i�kq}���&sP@��J3���6X�����UO���q$f
�'HJt�	���{���K*���-'�����[�7�����O�S�i�^����p]'��4��c������'`	E�����%���;�t����=�����/i%�	k�����A��������K�c9�hkh}�=����%k��G)�O�D�n��t�0�u�y�����I��#-�}��|���z��o2��p�^w��H���Q�I
�r%|W��Xv��p�h��27�����o�}Q]>%��������}�
�n�}E�����&�z����"���;)����Q=����Xr�B���>Y@Qm�/.rN(B���V,�PEp�:*T�����P��*r��L>�Z����<��~u��yv�BO��y����F�T��$_�I���H��"JL�6~��.�wb����r�6$���%��`a�oK�~I�&#�-�Zaa�y�@0C����}�����UQ��	��b��%�e�woV��w	�&�X4���N���
/*z�"zgG�����_�R�4D#��,�|�h\tk�z���0�B�:�����<���:��Z7���v�n��$4T��zo����-����Y'�X<���c���(��nw�(+���E��y��h|663K�.7���NH��SX��������M��R�e����s�����R3�|�UM\*y��m5PN��e��M�]��X��?�E4��nU1��(�%��>;�����xk�47���������c���N�9��e�Nd���������JJ��Pc��W���������8�`�N�Z�Zp���
#�Fu����K��b���!��e;[}d���h���S_>����E��\��\:PD��L]o�oL����Ku�z�'�/��=ft[jk���X��}�\�k�N��"�����/M���cL����,- x\�����!���[��n����M��!�pPW�)�������1tl�g�%'���A\ ���8�rY�����n��#�N�7��F��^^���'n��D���d��$�D�_�]-��:����W���l11��:�"X�xR�T>�\����;��
����4�5%�C�\��������,�1M�q(�f�������xE���pg^v��y��=������1�����u:��'��zu)�T�
[�'%e|�k�|f��\N�e�V����i��`����Q���0%;��z�U�@wu�#�=4���Zc�qZ����Wj]��G�����vv���`�@uW����6y��^���F�����@wGq�7��L�a�@�p�8+��+'�u���on3�"�1�����`����`�H�d�m3���HI�z�R,x��D����Dm�w�y���C�������!9�>R2C��yvY��k�B�9���E��?���u
@��h������t *�V@����� ����0Y���`!-��@�lli�5�75����}�������p.m�[�1�do
����>���r-���z>',u�}D�;�����K;yF5d�HO��Y^���Th2<�Z!��l�Y��{�6��E��V��2����hen2���c9J\^���7���n|��i]#LS��{�������*��������VX��I�$��F�^[Uu�h9���\}N9G���W������I��0'�Y��<����~����nx�1K���
����'�����Y�f� �"q����Qd�]7��i����s�|G/w��Uk$5b���N����1�90q'��!6k��\���,������6�[I2M�L�=0�R�q&�m�Zs=�u�!#��e��M��:��@?3q���fM�$�ER����A}D5s���`�r=��%��;�G��������/,?V���n�����7(��/�����kl�L��";��U�fL�021%
���:���|�������s���N)������[�[��%u�����\aL�\�b�	R�`bGF�R����"�`���zd-cd����w�J�3F��r6�-�PNa���9�a�����e����G9���e���8����V�
��z���R��'7<:��\���R�U��q77bs|����c�.��3��33LI=g��:�qP��p}V��)�,���K;����p���W�������nA9��������r��j�K��v��j���������)&���h�U����������R��b�A�<D�Mxf�}�%,�^��/���������&3�8�D�c��ZF�����9����'����W ��C�5����� 9��c�:'�����]�`�6At�y��&N�R2����E.��lW��5�{$��]�18y��z��|Y$�!�y*l�s�T8�4��u+��=)Hx�$�i�-��%����"����[��,�6A�wX�������2��@=%�)
��
�)h4�1�2�c���V��dn�T.P!?6���,����fvH��E���a�FQ��l��rN��+��b{�2r.�:H�T�a�`j�K�xR��,,N\���o����T;V-/_z/����][q2�"f)v'C��r��]�Q��6����&Xk�a�K�_�+2�k|! No��W�z�p���c�l_���@�%}��[@��\���c��������N�����>
��y���A\0d����9k��\r!��2���h���~�������S�#�,���T��`~�.��m��>���ljf�*��3
���G����E|a!��������z|�C �b�X�e=������5�Z
������:����
[$�3�$���w������l���cb@g|8B>�DB�[D,�-gq����
�o��l~d&��8i4����s\����A������+�����,����-��f��Qe-D��c���������_�w:#	M��slsb�p��$W)I%���RT�1����w�{�A�$�������5����k����	�����&��R�o��g��@������%�?fK(�BV\���w3�U�L���t!��d�*����W�OJW�\<�%L]������(�#dBX-��V��-al�F���"�~:��������dz����B=n�\�`��GF����M�V�tE1�����VD��2\��:��[�}P!.3�2�)e15�b�*3�=�h��5���������i�����	�SO��,FB��%��4��>������T����,!�+S�L�>�Awn���j/t>�����������
�{t�l&!�
)�������|���%&�;l�x�%��d�L���������:��8���T�n|]��w�}�<��k��8*E��z�~�-zDp?{�[��~{oP�5��[����V�W/l,�Z�b�6����z����4��J*eSc�E����.@��X���p���*.�-?��v�����/[�W/�Gu�q���i( ����0'���O��~ty~������aX�)l�����I����[�	y
���9A����O/:G'W��G�,� ��D����D���d�@�d�17R#��\�E$_y{������B)5Af�������vw�����Z���w���^�YN����	��9�@fh���O�_��q:Z����p\��=�QI�qI�y!2(B�t/&��&�����{D��F����-����7�@3\��4y�0t�e��+�n+z�c|�s�^���a�R���5&�_�/I�2wU�2���mo��L��F[������[��B"5O����wh��'����D�f^?����P}<�H���"��w��v�_�\�v���BT),�����=���yf�dYR�A��'�z#�'�e��8+����/x.���=�[��%:\�����8QM�5����N������;s)�e9����D���L[���n�������V����E����mo1��OR]�r��V��N�B[�,�O�s
���pO6C����2�rq��$3�}�d��V"0Ln���l���6��,�Vvqk��:�9��&��%s'�c���'����:��QY;���o�s���	�N�@���'GV�N�����\����u-g�%�*�4 `)�v�O�-T�X���	�"��n�`ow[�\� �2f����^�F<��?������AJ.vr]�����"1m:s����rR���W����Y�������-W��jh\��X+��K��8��Gf��D�z�SS�e��c
��+4��V���������[���N��*�
M�B�C�9�0���%�U�%��.���>Zx�X���F�vw��>az?�=Z���0\N��,���,�"[�)�Yc��hNn��)����.�y=�"?����+��U//���3^����p,�Fy:L��U��?>�x���w_2�`���%Lou&8������z�������f����g�{�2����s��]W���������h���R 4�4S���.u}������v7�������E��z�6�y	��&�Q�������>���<���}sx���GbZ�}��0��G���E�AcP����5�[���Fo��0{+l���>����l������&��BDkB���Z
�����1A��@/�2^����Z
*�p����^^i�,�*�_v�8O�~����p�����h��{������H[L�<E;go%N��"'e��zc�F��8K������>t0�����I���d�y5�i�hG�
��21������o���r��%�>�	z����o���&0,/�(K�C������R~��,��pI5+u���u�K�[����y3�/v����V{go�����Z��u�As;l��t���2�!�Ep����h�E��m��R�G�F�FA�Y��������Ui�8F�d��%M
�pt����K?���c������UGM)�xR$)?�#��a�������������������~s���h�h��q�L������,��L#�2�z5��~�o��*p������c
�!��J�UQ'����o�������x��&��u�EF�����!i�?������r�>@Yo������R��p����W�d<[z�����<�:wyx|�9����������w��t~�G�Q0����4b���"�j|U�����;=�^e8�t����?	���c���G�\��NsU��N�OY���c G20�8�	�����h��-%��]�-j>�/��l
�
�;������q��nk�����Z-������h/�[�q����������]':��\I8��S}~jJL�0�)�����/�`64�58]K�B;�nSq�����J����~/�X�)�<�	�������&j�U�^��o��dY?�t���n=}�F�^Z���O9����Zu��W�
��'�Z�6�S����?����,���S��|��5g������������|�.\Ql�"I���'�RD�|g���@)����kN;$����������K�#���Z�23o���
`n�������4���6����h��j7�^���6T���#��vHp�����e}��x��nA��CZcL�	"�T	�G�O���p��p&������!��3��_~j
~q�>������[�������z�
x���*�������B/|��(�Nc�*6��nNB���%�i��
�I`S���u�-��j���#�
�
�~|�\�h��e�Y�97wA����Qc�,s�?����j�U.�n�2����C�M9���B6���?�	��P��o�Jk�����~��#���,����a������]J
��������Q��{�����]�XgQ�0���p0��y�����&>�	]�Hp�����h�����Z�A���=f��^{�������s�����?U�B�+�������0H&<rlB(m��Iy����W���0���"8��;��"%^���Zl����"c6#+1<�{�Kj�<V�X�t�n!3)r�gvy�#�9���>���p
��^o����
b06��,���6� e^��2��U�do�q$����5���
6dSxQ���"�p�s�`B�'�Q���$��A�^���8�_F�mQ`:K��L(�=� �M�
3`ANE��FU.�M�E���R&.�%��{��x?#&)yU�T�^=�0�`2��)g.cQ��������Z9�``�#q)��.�e�a�)	���x%M��O�5��a��y����QV��6T;'���<�s@��!���$�{��ey&8x�.NG7���egg�	S3��r[%w_]F��p���vzp��z���V���������$�gp����[��u7H4�s>�H��B }r{v~~Q�q��g�@C�Z����70���k����W���w.��_U�������V���_����x����!K������u��,��y�yZU���c����}^����������eU�a�z@;�[����k��n��+0���S��E��.Fl��wi�6���/W�?^Ka���������'����:>;2w���N�&�F����,&��1/����}\�\��rG�*_������(~���5�����@��^8���GQ.X���
���I�6S��Gi�&.t����������M�����/fkE���N�5�O�ZM��������N���S�V�S��=�D�M��'�����Z-�5"��`��+�i&� �hv0�l4�����9���&�n$�Q!��=�<(��2��$UH����Pk>
'1&@V0���`N�}D��1n����7����l���6�@�tK�tH_����u������[?�^a��� �P�������#V�����#p%M�wx���-"^�@���#��P�����oON/�/�����
�X�����"��'����nw{�Y���|�F���NQ~���S�>�$N�fG�'9�����{��������^�F����ZpE��=V~�rM��&M���O���
�V�~W
���~
��4Z��
�����]�wp���i�����++�O�R��P�k�����?����Y�������M7��������������������x���$?6H~l�U�x����u���+����6c���<�T�E�Af�d>��AF�}j���	���I�������drxW��Z0ZO��W����a�9��p��Y`n�����O����1��NA�9:><?:9{M��;�p;
����(�E|�+����I�9�AK�R����2��/��WJ?
#,� ���a
Ed��4n��h��v���Qfq�i������AVu��pi��f� 'U/�C����d��F���~�>&�bY��F���|PX~����B�W�������vo+j7��X�%�������wIU�U5���a������B���
&e.
�Q���??�T��4���(��j��),P��j�����5v@8�K�W7
%��k�]y�{��k�����'���eb������FS*$H��ZA��j2�I���EE�!�)��_��)��G�4��n0;������j��l�������?�r2��w���z������>���i����h��H�*�RU��*�ZMU��3�K�Q�
����-�Yo������N=h����;�f}�������	���	@��2���������`�}L`~�/���������k��j7{[�^�[��a��ns�;h��a?��m7�SPJ��I[�^���M �Ulf?��M@uNq*��g�W�&�?oF�Ij�d���Q8��a��+�vp
Z.�4�_o�7�0�^_��w�,m?��!��F{���)�mm���@����j���\����J��"��)<�b�#oP}.�7cx�`C���[�����_����	hT��M������)�h?���&�������ts�yxpMFn5��<��u>�����[�v���]��/���h����pw��5��O�����u�@3�x��nH�0uy|ptz�����6����*G������ h�`;��&yCSc��ell�3@��p��7��BXv��V\��_-��,j���v�����Vs��m�:q|��������768�A�����_{��D�mp*v���^	0P��Y�������a�~����!%�F��'}>4��VJj�E�9������=�0�=G�"�#�Y��h����
��D�.{X"�zz������"�cc&`�
��6�
hE�8���	FV���B�1O���s3"$����
������vD6	�v2�=���c��Qe������&�������.�[��^����#}�����E)oLC�y
`e��x�)�T��Zg�k�tq�}�6�<\����
���crG5�����d�	��Pc��+f+�����g�d��&}�mLu�.fS�������
��
�#;��sK:2Q'e���b`�����%�Ea�		��1���D"��"�Q���p����j#���c��!�������+&����-W��h^�'8���Qe)��t#�����#=��M��aF\g����P-�[���D���Bnv;%@��9I��b�20*(�����SBd
f��O�!.6o�UU�����!j���4�>�q���.s&�����u�F�k�0���8A�tu�Mr���He��s�������}uu�q$"�X{��=��-�/b�B)P��x%h1\��r5�qz8�
	T��p�4p�����	N$�a��g>�{c����9A�	���3'8���e����;�:�	�"P�-n�����:����#}R��i��>�X��W����Y�80a]���A�
O2���oVp�r7F8Z98'�W#���+���5����8����zh��91�$���q\��|j�#d	@Z�
�C��L�xPE�]���'��O��D TR���)^t$@�6�b�`f�4AE��-�����y
�������t�h��AAS�=I�#��@t@�q�R�0�+����%0���a�84�I����-�����m"���x��~!���b~k')V���
2��d+��~&@�!O,������������^�aKOd����Y]9�1B���yf�X�����i[��E'�M?AN}M��k�c�^�k(g�ac-HT!��E#��^$�5�E���������e`�)������c:7�lE	�Xm}|nV�?MY����������x	�)ek���9w?���4N�)�U�������9l�0�+,���&�fL�RX@�8!��HJP� ����JU�)��x�U���l)�M,|QUF<���<+�����R��giz1�g�]&i��`��a�^�}�f�`��L����Usa�\�&XY���9�T \C�p����"\�A��)�5�_�����A�}(�����9��@h���B�|X��l�����0.!���dE-���@G�>GN�p���W�*���q�O�(�L�]��A���u/ 5!%�,� E����x����vq��Q�qk�:����DuT�!>e�M�0UGBN�������[|�H��XT~h	���a�!)�p��n�6�4e���������:���.�&�T��_|����������
6W�5_o�O��L�X�QB��������&>$�F*l"�����n��S���,��iO8� t�����"������5a�\X�C^$�yA6�������+q$%t�%b�$J��k�a+�Q�['!2Ti��+��KduI|3��1��B�"{a2����eGTS���/��!���}�)��0M8Gf�:=�e9�}S�����dSw/�$�y�[��W������E�/IFR����lK���r%e)�.���8H�Jy�1d4V-��GFa��iu�CO�
&�Q+��O��M%j����CD��d <�����5exk�y?��`=��K"UB��Yk���5CT�Zwj<E}\�!n-r�a)>����hYw#� ;T���z��)L��`1��Bv�7��Y$'%r>�d��k��p�����R�!9�4j�=����&��,j��u�K�������:������m��DX P�>$�/��&�,���^�F�V.���q2�b�1�9x��*p	e_���`@�f�v�������HY�*��C##�R�������t	����}���A�Yp
Dha���Dj�gt@>mH��)���:��x��y��xq�z��y�8}���|��������!��w�����w��W'�g��z��	%o���=����.�$��\����I��'������G���1�e�y���i��~0@1/V����8��%n�#Dts�H0��W�)�P�}9�:�E�JbW����o��'�L�#a<or�����F�0'�F']����3���d�2fH5�����`��&f��YH���S���J{���$c�k(B�.Lm)51�8�)�J\))n'��L�27��D
�RG'�P�fK�����r� ��Q������
����\7��Y�����Z��qL0Z�^�J>��J`iy�!���&���.wQj��:�O�phG����{[��l�b8s���W�5�"��0�	��h�	�q���@V���1a��-�K%���go����*+���	�h��;����V#�u��E-��i���*��H�9&,��9l��T<3��k��VX&�9G�#��/�l�6��W���^�[G�8q_���Y3��l>%�
�42��+nb&����yA����`qE�21S�����AT4�Y���_[%���?"2�s�r"��Q1�-�F#����&u��D~cM��=�S��	ww��D9�F'�F\���()�(!�����
�&�=K@#�;�(A����B���)�V� ��tr{k;��x�������mC�'����4���Y�	2����N��)h��*��,�$Z�'������b���d����q\)�i��"���<m����F����F�Y��'�R��@Cg,cy5�P*~��nZ���X�-3W��1����b>�	��G������7����Vo����U�G;c��m�c��OHanm��Yd5�Kq�2�;I
�O&/1]FN�c�%<���+�T�s)��M}���z�Vc!��X��)Fn��b�U�+���9�S����v#�Qaa�0��t�1�?`��b�@D��N!O��h�D
������B����X�����{��t<��}���3:|^��@}��v��G���
���N��v3)��j���*-�fL���2���]e����~{��/�����>2���uS,�T�:����#a
>~��'-W��p�5Tg�S��@����������j��'�4�Oc�:0�����C~��If����,Z;&G����,��CNZ�IhC�Z\C���F��h��C2O�����!��2z,�>|���Cm����H��dT�o�n Q`�r�Gl�����I���zP7��������r�#��E�u�� �Q�!Dc��5DZ��Z&$
|���^�S�M�.��0"�S5�����G}��x��L�C��i�������8^[�%}����D����3�/���TTy"F�4�H��z�����V8
�3Zv1�r��]iT�okjeEl��m|���!�������S�b3�0#���`���fz�tL��C�b|2��J���x��s�"��<��V������	�D��i����������}�0P��YH���pBG)���9�)��`b��3�y�^=��1&�*���tp�Rw�2H������=�L��E3�S��0��L���I�Es&��)�F��d��Q���K�l����6������4UX�"�_�����N�Y��&e�a�$�����	Z�m������P>��=����VC������0D=��lVY��B���'U�P
��'��$�nF{��-��nmm�����v}����������w;��ns{w{��SoF��~7lnm
������H��?2��
u�F#8u������}���F� ���pQ>p7��$NG�D�su�,a+$r"U��a6��IVoq���E1�5��>�i�~4������i�lm��|�f��`XuQ�u8�|���)��%<{�A<���Oj�!�j��=��G�u���fC����c��Z~<va��3���f��w��>����	gH�`ow5��_K�q�D{�^kgg0���6��mXu
����}3���?��Y
.^w����;��__�x���`�'xXw�i�>�X[�uz>����`��J�'6u���?���;��x��N��*�C�^���"�
���<�����6sW�����Oz�$���P��0���O���O�a�J��2�.�����Qk�����a����a���Ra�*#�dn}�g��#{)����9��l���0*��!l�9,�~�C�X�F?�R��F�2�xO&��w�O7g����������'����S��HF�,����$�M�6���&46���1PJP��)8l�����/���o�sL?
7��}��o���N���\��hg�\���S��_�f�_@V�;Q��N+��[7iNb����<9<��~Q}O�{M���I�
�N�����#��p��+�[�[�\�����HN���c����U�K�C����L�����x6��\����J���L��a�d���9|I:m�v�������'��B�����z	-����L<�Lil�����O�	54�1,�;����������k��*p�r��N��i���S,��{J�K���G�0�$8@vt|��.�2N�q���=���Z����O~�r��/�7O���4|��(<�/����.���/��vz�����_�����^O�6<9{���Op�<�>���l���<�u�?��[�vq��m��Yl1�Gv�rP�5�����a%x���g�
�X���Yb�D�/~x��������4���j/�%���Q����� ����7��B�`��}���&�����W�A/��|�������s��p�	{y��m����8��n�n���������MK�E�Y�x
�IE�|6��Aq����+i�9 �G)g;�O�C[�xE�B��((q��M���Pq0��$��;2~�yW����e�2)��	��"}��ZUl���8��\x-������B.�;��O��F\�2R��
����m�M��6MJ��6�x���=��z�g��p��)`<�N@\�����1c�-��,�]BaGA0��da�4!qx���T��9S�B�$uLB��5���P1�|�N�(%���\�J�b8+���x��@�1�Q7�(�����F�`@���8�1��JH��a��X{r�����.q�3��c4���	��h�H�!Y�C26��t�r��J(z�j^���2g��B�m���������
��^��<Y����v� c���B���a�5��I���(���AD��i>I�)L���D��������%�������#<�
���m)S+T���yX�����1����4:z��w&&���e���|`6���w�!�o����?B��(��LJX�i�q:�D���e�9)���-	�'�a�FL��W	&O�	��u�mH.W��e����oa���1\&F�CnS��&%�g��%�+�Dsh�M�#o�7)�Z��DN8{&��d|��Z��P�}Q�B,7#L�Gl�#�����8C����z?�h����TR�����p��B�#��	�(�>K:G~��g�r
����x<��^�s`$�s���g��w'n�3�k�,�
�'�%��������#�����,��p�"�W�Q�6��"o�Y���*������?�_��j�>t�������?��'�#�����J �C&��&0��5c��]��<�$I��I�P�-�����$6��D��
��WU�~6�"Bq��k�Z�v^��\�c�!(�6�����] �C���������0��L��Q�C�Q7��n��YJ�����/�p�Q����Fp�B7^�F`�|�9hx��Yz{}���IOy��
������M��e4�x��I��|3�\�"?��~��o�d8�%�.�����\��s�e~�{t�2#��s�?��m���^�,���^�%����b0�f���[[�������){�m>�nY�v���.)e���E�!� [�\��<��������,;�5�oB�������2�[8���8w��'��G��>���(L����5���Q�>��h4�Zm�"S�}{�UAJ�/������*_Q���k��e�����9���,�����E�����������������!:���������m*&���W�D��^D{��}��n��g��������v���#;b�zz"���5�q`�H��&�\�����o������w���8�g�5�X��g�����n���>\��?��^OC7 �3e1��(�lbD&��Ma'��F����Q�(��h
c��,�^���d 0�^c���(S1i��d]��7	g,
p�
�>��a(�Iz�NG����+0��L�$���������6C��E2�&�DmJ�P��|�����A�8�����U��:����V���5MiY0Q��FLsm(c2�:F�4���Y�!F��<&<�
�08y��Q����ls9`'ES��Tp
���R���W�{��tc����h�`� ��M�r�a��-_�2-���U��H�N��b4��]�
��?����$Z�������Q�`���~��6��.x���������������62�6����m�@il!� 3���?�i�g������69Vr����N\�	�����b����6�vh�*�o��
k����O41cUC�1N58L��%[�~3���]�]d���L�LB�|�i��i`g�T��o\,�0$���\��u6�������,�
�vd8!�n��A�1�6�1?��$�&3��>��}����a�A�&}F�0���Jl	e�	���?e����K���k�v��Zl�����V���P��4�U\]fU�Ms���,g�8�W���������O�	�-�>Y�����f���c&�i��]���.9�W�@��
�x� �2��J����K2�]��NPw�����y���hv(N���m<�������s�4�i�,>g�{s�9#�f��s�4J�w��f��Dt{������q?����7����<���J`���41gu#�$B�Q�K����A�V�0N)�328� �a�����x:��E��F�]��c��6�y`����i>	{�C���x�T�Ch4sjkb*I��>�Y���9�X�+}0��6(��1�@g�6�����`h27������_���������"&��� �`�5d�p�V��C8���p���:����$���f���S���
�g'������y����'&?�L?�0CS@�����{��SX0J�����/t��k����Rx�<�n��FT��9��_S���g����Y��n����������!��]i��w������[���������V���2�{�S���SaO3�
����}� �,	���Jk"WWL��Y `��O�2�r
��J����4��������F0e�J8$�7��! \b2d��M�l<����������V"���:*^S��FQ_�I1V=�"�}��$ RAC��c5�K	B,?�u���)c9:O�r�^6�	s��@��!-�`���+�<��B�'�`1�"��=���0&�mS��a�E�^��x�p^r��������������Q�2� AX0L]���`�g����/�9m��������_���7�O����&�����|��$M��e�}~�C�_��1��2�a4rs�`�j���S��H9�"�@�(�T�ed^94����o�����<>���f�[G������j�z��;���;�e�?l_4������/���L���mx7���8��rv.��� F���D��0�C��a�X~�0W/����t�k;l�����nq��������
���
?�]�U�V����}0�>u�e8|�����/���4�pK�x#�I������G�I���N����x��U4;��F�'����������O���N{����\���8	�Z������Nagk�p�$�w:��O��x}JD��M	!)���!����[411����N�H;8�0�@y��4M����[�m?7�j�(b���TK�c��I�vz}��q�$���Q�����{��fU|3g���n��A�S��y��%g���')u�����f�?h��0,�^�U�|[�B�l[39^�mN/�;�S0��tG������D�X<��9��[8T���~G�{���Z��%��'8��7�V�	��U?0���*��+��=���w}�����=��#�1D�I`�c>dFmp��;zN���4���J<���)���2$�`�����/���������_Al~����~��$J�����G��4Qv<�NW1�������4I�0�Fk����c ��D"6i�����e�������\�{/��r=�OH�x���~���|��q�����`-��Q��������u~�,���H]�n�g�Q�9~]���w����o7��nX/�Yn����Y���?����J��:�>x��&��n
����^d�����+tit|y���V6���	sd�~�
�R�y�)N�]�K�?���� �E\�.�H���/�����F.g��ZaZ�����?���}\[�v�����T`A����A����
�A�}k�Z
�a���U�����o4> �)�\����*���	��{�W��+��C
�l�w�U�`�����T�����}�p��,$QU�{��F������j�������G��Q��w����Uo��{��V���i��pE�A������?2��zU���`~��������~c�$�����o!N�%H�x ��(���4�(hK�����,���G�*�D8o3�_[�
�(UD_	N�+L�st�k&C��S��U���	��A�k�Z�ze���%��X�m~&7��Z����zi���������1�|�@��s5�v��`m�������^Xo��A�/>���Z����ga+��v[��n�klA��������-.?�������%(�\��U2�����^��W3T��1uU,����������hJW�c���k����,x���$�g���&
�p�U��Dgb������<�Z�(���?������I='���e`|�OSCl2%d.S��@<����5X�+|�n���(���~�qEa�K��?��S��:b�His��p����;<��t�Y���+1�$+$�p���1V!��T���
��s�1�����E������,�C_���j~��o���C������b�W_Q����o����y�������c^��*���!�(����G���u^����V�t��p����.1]af�u�C��������cR�������K��[�������LM����
65��F�0Z��
���!#dj�'=�NnZ��vi�Q'�,%Q�[N�����e�������d���5���g�BB�C��dH���J�N;o�|e�Q�,{�T�3��Zf�����y�������JC�)q@�������K����V|"&�i�Asp��o���8���/���?�o�l��I��n�Z�����N��M&���o;g������=����'P�Hq��q��|�i6�Ym��g3�[*�<94�cCmGiq�����{��V{���������e,��
|�������3p�*J��~P0��'o�vN�N�+.c�Sn�&+��rD(zcL����d��Fo�VW��H��E��?_�/O;=�<��u��~����������w�.���J[�O0a.��`����cT��2��n��j��U������f�L�z�U�nc��W__��Q��=qqpvr{�W�_
�EK��=��	�%&3��2M@G��#����H=[M���C6yFY�8u���%�A���8|���+�����{��!g�Z�qw�:#`&��{�v���L�[�������_]�w�������kZ?��`r��:N���C���)�L���
�2�-�����+���2!�L�x/:g��+�3R�H%������r����u4s�@X�5�����q9�r:���T�u*`��R�
�F���L"��f�fk�[���yoU*��w�^_����;3�|�"�!����3��uC�#��-P!�< �[�Q��n�����Z���n#��n��W�4e���Q[��6��������o>��5������eD�
�B\��
�	��S�V�������"]k%�n���i_N��Z0~`�0�&;3�J�P.�,��S�p����c<).�HS������I�^^�Z���iT��dA�-R�b��������/k�o�T��Y�=�1�@U���3��|�b��������������������n3���[{���^����v����k��[��`��P���i\���m�������c���@�Aqj�j�@�������*9t��DDc@��S�����4f�*���vv 6Q���&�������A3�(|k����y/��o9Q�:����b�j�`��������v|D$��Auw����~���6Z��n����
����M���Q��V�x�@��ID��C�O��-�n0
��� �@�g/%`��Uh�~~:�Ez��������E���<�h����������Q���?�v����-k�
������a�V���;��z���h������y�1���s�O��	:q/�4�@�5�A~�@�gX��d<����I����"B���h�I���Jpz�}������H�j4�CC1�)lM�/*?�%��d��NT��ZF�������h�V�m�k5��(l-�J%-/\��wh���h��������OA58p�s/���c�L�*��������I�h��q/���)h��sL��S�D��h�Xc7��?��4|y����u������Q�^�����O�0hb��X���;9��\�L�?7!9��m�\�������',�����"�����U��#�V������@���N�����pp�[��W�#���5��~�������;����V���F;[;���n�G{kkk����I�#�,q���B��B��h4����yo��vV���}dXC��A��4��b��N�W��nG�V=�6)]~qHC�������tZ��tX��Nf�j�*�S�����j2����jD�[�Oh�Y��,x��_�E�)�*��L+���O'�����W����g(�����$�*�|�%���\���0�}��nKY��;��|a�z�~�>�5�2yY�M���0�P�:��8�z�p��%��
{��A���6�A���������!�jg�A��.�b�
�����>x�����l�����`h�-s#����u�5sF��Mz�b�
����
eu���K�\�_nHl��n�@���j�����,'�b$0}����4�=)6s���UIlajqL:-S�$h,�O�>y#)��������=zd����e�~#�tx���}��zspy|DK\E����2�.��E�=^H{.��}���+�g�Q�4������,k�_��<���l�W��� �N'��|�$)��a>� >I��&������G�z\���v��!Y'��=��� �nn7�!��-��G��� ������/4)s���N
������7��?g��Fm�P&���;H�1�E*�y���
��I��E�SlZ,����y�z�����B��=�C��s�?�3�:�������+sa��_%Sa����h�d�fo��D��dtB��=m���}wu��1��p�������F�*�i,0dOs��>��V���z�hc�S��,� |��?���O�yS�F�)�^�5ik�.����������[�q5
����3`����3?��'�lj���c0G]��:`dX��p08�n�`B[����(_�Lt��^�e|����mD��������W���&,�V���hT�� /�cV
'������J}�6���?�`�i���.^B���n�v�;����L|�����/�[��0@��;���	���3��6H�%@�&�Za<������G'�"�nN�H�K.����+��r�������4��A��a�n��D3�?0�p�'�suU���e�lc���m%L4*�(���EL)I>P!v�r�������U�������h��]��>G���PT���<�}�a�J������|
���S�>{Y��nQY�}�@O�d�oQZ����}�(!�Sl[r����0��?��uS�=��4~A%[I��c/�002�-�d2�80��X9�[����Nek����"�X�����w���tV`��O�(�V�y��SHs�'<��Q���F�=x^����
���@+�
�~G��	6���	2��al.
� ����F,��-��	
�����,Vz����e��h	���f$,�)�����}��s�f����7��X�	�g��t]�b!;��9��3��&�����{��a����b��H�a�,�QF���R�������)�.A��H��L�Q�A�.���T���)hL����e�!�T���c�8}_��r3�	!q���)T3��L^a�q,���N/`Q�t�a��T
��h�~`e��)�u)f�2���TuC���Y��������{K�����?x3}�dZ��Oq&�-Vm����������1����|9�)�-7���G�����H���dM�����J��e�K��������7
���wT���	������"Zb�P`!�Wr��2�?�4|*��zb�u�����@���$;s�?���a���'c�=�
?�����	u�*GsJ���~�p��7���x��w��d���:SL=a��e=�"*���pRA����5����j����RS���:���t-V%E�,�E��_�h�h��!F������J��v�h$�d�w#n�I2f�*�7�"C�,'�����)�����KV�F����E*�}�����_�j�T�������HX.�$E8�+,	�������zt%�*�kj�,55�	M}o��������[�46���� �0'��0><�d,dX�I1��quN=�=����8#B�����n;f�����B�x�����=K:���=��%���G�u�VhS���R�|Y��!C!*���X���������`]f���*��34#��vlg4�[;�Km5wX����]�n%�c	�G�R��I�[�ns�_��d���oHa\d(��6��2'��}���.);�\�)=`�Q3��2>�1����B4�P��At����kP�:�_t��:��Ac:�>�=�B��������Q��<��J�������&WgK�G��s�u��?=�������Nh����X��)"tR�S����.	���1������Y�_�����Z_mq��B���So�������?�%��������w�{�hw���������z{k��v[����^�������C�D���?
������F�b���b��-'`o�����
6�[���(�.��C�=I�p�D�0U-�[\�R����(B���H��0���|,��fN�T��ZP�����3�t>bJj����! S2�j��oAO��lB��5�r%X��yK%A�����iL�R�*�42^]2�
�<5�1Z��s+��^nf��������jV@��-D�@	r�4�����R�`S/����V�p$u�T�H��^�.`�z�G�>���9��l;�	�]4������f�k�r�?j�$a+��e���R��G�w��\�t��(�7�� �����t'��+[z}C#��1WK�U��j�����A$q�;e�J�����e�E
}�i%pj��	&��R���7�(�
��.�AWa7�]e�3"����t9zJ��+�P>~W����u�0���Yt"�#L�*���,Dyx�*�>�����)��������s��U5��w��_v_��}8��?�%
������w�������%L���$?V<
������9�n�����K�K9�/�[�����6��S72
HI�M��M�r��z�q������>eq�j��j�����6�Z�2l�{����s�fu
������C������3��"M�6��Q��4vs�M?�MA�d�G�������/�lm-EQ�_\���yG�g��>�G�c����k6�z�{���`�V����vc{P�i,�g[�gc3i�8����u��wW��qS�9tY�a^�X�����:uD?Z�$
�p����+T�e�$��Y����HP��?1������������7��c!�/����z�����x����������R����[D���Y��i^d]F\�U�]�`���$��2�U�Ir*�ZRD%�����$Z�lE�8��������a��7������������HK�i�<Nh���IW7���>6���i��#�b�I�y-L�%�u�N���*��>�A����g�������>��n�(�������5��5�f=6���'��js�8�QL�����5�97����AJ���V��$bG7
��5+$k����d:�{�����Ix?L�����g&&o�
o��b<�Dn�n��|Bw>��#m��Cdv���;3�h���*C���::�"��lV��1�!���T�SV����lPL ��E�p/��Q���qx%�7�[��)�fu�q����bu���X�6�:�2���x2�*1i�
���B qGgO{YG�����Y��Z�D�\���D�lh�S����?5S��v(`e��+��m�a�FCIVR_*�!������1v�)����C����&*)��<��I��?����D���I.O���h��+X��%L��s�`�G�:P�x^)�!o�Pn@CZ6���g�����d<O�z�$A�6�PE�9~����/i&q1��42�.���)�2�X8��
x�M�Ie����\������E����J��.��_�R�h�C%1�6�R����Nx�RZWY|HuF���W����#�>G�wO�>O< (K��n��.K�*r
��}^�Q���\�.�lA�Px.e��tW�[^����Mo"s,��G�Q�\�����	,g(F\8�'I:����Ib2��`7g�O����L��I�xN�>�)�8�T+s���;��>}���j�Q�t#�7�����s��z&����X�Lq�Tb�C��45����cC1Ue��iP��m����mlN�����[��������������v�Q���1G� 3��������!'�wz�+X����cf�M����)m]$l�����v�Zo=h��m���+���4��7C2�r
�1	�����{�vq���c2G����4�>���tk�$=~x���������qpy�������.���(���t�����<�	�'*�d�%'}FZ�f
$D~��w(��Pb��^Q����#��7F;VV����K$��gv��"���l���?���)�����"�75���e*:�m��l�U�g�,
eC=g���W���{������y�:�I1�H0_���iS1����pf����$�)�.]�^'o)�������_��S/R{�g����k=�p���gp� P��������|�	��LiXN�OS�����IC���d�E{����r��v���	��(T���g��l��&���!�f0L5��Td�{o��1�	��B�n�'����!#,x�����h�����d���m�T�~��V�ZK��4����L�e�S��)9��$%�J���(��r���1��u�����C^j�sU[JuH�1��!����,�y�H�qGD"2f��;�c���jJ��~r���
�ZX�'�� B�}2@��������Wz��%&jK��(\���������r��0k&�+H�l�BVP�W/��'5��J&�T�G��D����'�
9��y��H�sw�������(�O��7;�>�[���'���;�m:x���^8�����)<�}�s�`R�����m!h��(g�o������t���a���3���,�`�_>���eb�r���?�\A����d���z��G����5K�����;h��a�m�Z�C��Y���x�m��-x'���l��1��^��@Are�k����@��a�ec{e�s|��vr�/E��=7C� ������<��rS�Qi"����r�I�}'����t"&`]$���
�R�R���\�uN�k��U'��� ���������c���b�e!�EB�z���8.����
I��R�r#U�kQ ���-]aj��q�-�;��$���=�y+4��x����l����K�A����2����+�./����Jg����J����etz�hwZivg�q'q��`{O)�,�,+=��$=}T>��^T�"�M�?���N��uw�]��NI�rE�9b8�M���������k]�8����N��D�P�maNje��������#�����p�S�t-nl�]���@��! ��hz�D+�
��R�]������rA���`�nS����s�s���(�^�F�����s{��������h(��wb�CH	?�Ux(g�Vj�5r["�te��u{��=l�y�����s:����VqW��
Y�lo����!���3m��p
���B@���]��a��g#�`���������g��Y������ab��yl����'%�v�e��lg�~�!w
���-�����
�&�&3s�j%-x�&eEZ"��_��Y��
J�)Z.L�����o
�x��L'3P����T�����]*�	9�tv
���-S�����4�\+�?���"�����$A�7P4�?��q��|&���=����)r��������Jx��G����wy�H��K���'���.���d~��
��Pw����0�S��7�1]P���4�:z/���[4<l*��) ����=X����w���3�_!g)��p�f�\����3`s� z�4i2�#�mdU|��
����P�7�.��14���;>���N;����S��um$O�0�L�>���#p��H�MP��\�?mg|�$BDd\��v�DD��,0piG}gH,H@b��*����2�c�-[��6��<������u(�3�'iQwg��o5;W,��#�.���[$�`�6'X��%�1�iI��.l��r�$��=���zJ�
�L��-�{^��vH���*�:��8��
��L�Z�s@���L�>��-EfC��D{Z��dF���6iv���?��*o4�zk�C�]��-FU��.���*�`�BI�J#��_�r���]��
8%s�����zQE#��&���cx�����AF^<����nh��g`��<#Dh��N������_�X)c-:'�|"���a�v{��� ,-1=��Ac�v���se���� :�q*� g�{	,a��MA�V����K^pD���%0��\���Z��MY�(��g�k�����"��`F���%�]��r����dT�A�:[��Z�\��*�/3���nJ af�S�`+A�1Z[R�y��O_�T�
WTt�1�AfX�Z*zS��A9�,����X�8��/���/k�Q��c�e��N�o�W�g)|�E�O��[!�_u�Q������w��n,������bp��`�@�|G�S�V�����2g�U��W��n��f�x<0���`V�X�}J^�0\���=�$.+) 
"�Qge�9UD`���J4��T������zL��{��"s�pVx���(�:A��#�a+�*��zD�����N������
���,M��^����%s�=�x��bIV ���+�Y|^��}� b*+x�|U��'g~c�)�J�,�����Q}g�c����5�,l�I�/2z��-�����3�+�u�������QP	z�a8��$'`[���2������1��c�*��8>1B��X�����&�
����Z��8]2�1gu����e,/��E�_8�y��O���e��;>Z1��6>
��������9��Yj�D[������O������t��C��m�d`��%��m2<q3'����"��4����� #S����B���6���N��#>vhzv��YQ�p�1u`#�����Q4�M����6��Q2�R��>�C8�:X!nb�����Y~Q}���5���=N��)��8�
��u��E�����fL�~�:���5���,��f���b%����bcis�,����YP��M�~�kO��a��*��z�)��"6������~�!��Y�e[��&+���i���Q�����m�LH����TW�s�W���clN�>�[W��&4,�"�S�g!/��J>5����P�q����qL�c��`�(j\��4`��39	�NSi^��$LSv;��9����-J�
1�?5Do*���������s^h�9����s���.��V��I�@�(�U��I&�d�
��c����$�������b�Nq�Ba|�rX?� %�>������b�AO������������d>��3,"|d�������������$
���� n����G�{�[�oH	�AL�jL�i<B�W'i��%F!d6�=�g3=����:@m�����a@�)�����������	�X�1��9�-�*�b.��db�^���������k��`�y��z��k��M���u���"2^��H��	j����p%��\LH��7����Y��a+o8��
#������1�(��Y7X����3_���j��L�8���4����H����>��L�^�`�+�<�-<h�N17�x�-Nb��YS@?f'.�K��Z����D��H`7�Ux��a�X�qf� b����d�y�D�'A6�T�m�y���b}���L�]�3�lAA�����6#�ai��lR����������d��/�`�Y��;��u�5���]�[�I���&b3vsOq�R������ZQPg��eq|
W$Ye��dv/����:NW��j�����N��8,���:�!������?���i*��+
�sO����LU�T�
|L��s��q;��("�rQ�F���*n��]1cQD��;V����W����+)��
Yh���A�W����f3����[U�Ydp5����P1��Y-�'&����M�]fV2���c�����[�(�� ����@o��bj&nu���r���HB�
]���f}{��K'?�$%Y��
\[����|]T��0<{u�+���Hy�����xm9��L���\km#����
�;�S�z��bp�&���
 ���.��c+e��� �����o�-�t|U���9�������.�ON�U���x�^q���7��)���_�����qe�6�3�?��e�2�=P��Aw �� AK���cGh�� ��jdC0�r����������� /���R�j��b���Ec�F`�����m9���P�4rh4��A�aB����q����m�5��W�n���s���\	W��,G��,n��l��9+����:���U������pxm?x�?�D����i>�>nKfl5�\\��������29N��v����E�Q-��u����d��E����<>����:�����H|�0l�(���x����-l����Y��)n�n-�������{%-��i�P�T�s���H�2K�KB,��#[D7��9�9)���}�p4
���}��?+t�\����$5�7{W�w:�nMZ� =X6+�0�����a%H��+�M��O�0�a����HX�&o�F��� x�j���GFg63�������s�{h�dl���R�cfg��0M����3�x;�v*�#��@����j_�`{
��?��������Q]c,�m}���G���i�J-��C�uF|zT5��39%������D�K?a��
�]��3������"!��j�A�f����"�b�n���2����#Xf�8���s|����%G��fv�v|yy~Y	��n��j���4tb��Y�
5H�C=e8�2�_[���6�|�~�g�$�sOJ	�L��r
P�S+���
O�v�
�o%����%���\�8{��#�B?�t��������w-���� ��[T���;�.�����ZG$�|���i)0��'�c:D�����h�/��_e�	��b��7O �\�|*�w�{�A����d.G�,�]�^��&�X������w� �W'�x�����{���,'��(_�CI�n�w/��q�r�@8������<�j-���	��Q��Q�P�isc(�FvsInnN�P�������hhk��<x���*���]��i<k;��p ~fB����6u(s�B	��<P6s���p���P�)�hD��)�Gv���s��\G������*���|�9��?�r&�
Ex�F����d��?e�+�/����D#�X�����E�Dg*\UOR�}&A�Y�#�J^�t�l�#8q�m�'t�oaR�+�h���#�����{C�E�J�W���=���M�*���)��M��+|��z}�TX44�e�V���7�1��-�������8�Cl�V�q���H��)XIjJ��9N?�����'���^"���IG���g��p�|!,�O�����!��p@C��c�hh�"3~�����fd�@�%�WF���"�z����w=���������7=xq���sx~zz~&���l��?Mh1�'�f$_�����\�L���Q�p�8��j*.f�E0!J��DR��G@u�g#	�`C��h�L���\4�e?4iN�B�_6VM���'X��������I��"V�k��`_s���s�����n��Tl�����>�&��Bf������]�*{�engN����	���/��c�����o����|qsf]b9x�hl5lG`���hG�������0�a�Q����P}��"�,�uK7�[a�b4�?<�������_�s+J��S�����5g��v^j�3��Job�+�E�~�5��F_�A�e:=�~f���r����8�J�8�-C���T|��Sf�39�0o�u�o�����KXp�>j�l�Z����gp�L(�����h�R^������f�5�>��S���K�$7�.����.p ���}\�U��n5��Cgz����P	����#����,���2��@�C�Ag�<y�D����?�x��ovy�Q��-0 =E�D���UYq�?u�28��U��d���H].�_��Q�!������x�F���*Hw\����m�c���2(�_��f��{�� pE	����������D�<�,#G�nLn��r'���x[f�]c�\�1
'��g������N�VWZC����xx�4JIN�?����i�zd��
aNO$����TU)�������N��GM����t���������
�2W��	��g�-5/!rT
TS0��it!(�D�����e�]�c������.��1�d�.���Z�cZW"�4��Z&rRL�%A�e��6��`��P:�c(f��_/N�^�Vf�h�'���wk5�Q�}���I3�o8���J�X�T Z<��N�Ms,�#�@M<�D�~�@��]0�~�7��rkO��iy�)!;]�[�F��!����c��O9=���j:�h��="Oa��4��
�vRB~�dp��.��t�g�)��$��6^6�(��?A�+
���0�:��7

oL�z"�0=9�%
�����2��-w0��V���#�j��W�I$��]������B���������x 
���g�iR��2A��9�'&���W�8����'�e�{L�)�R���6R�x�%.���,YU'K�������������k�{R�t6�����L�X��,xK���-����}s��[V��Se�Y?��'�O0'2������*��z{88�����O�v����R��w���?N"L�~x�P��V?�^�I��h � ��bW��I��U�M~(�A��	��_Q���G6m��I9qI,�aS%���/J�/M���0G�Ir�O[^�!>�_2�3����$�<�|d��
������w�7ED�nQ�wi�w2�V�_K�N�g��=-��n[xWc�F`x����X�h:bH"��!�,����x���b��q$��3V	|����[��^/g���gtg�������a08A�����������p
��!��I�*H����S���O����g�������5z������0N��=����
�Y��k�f���D]�C�������iL���d&C~�)3�B �SA�T��}�H�+��%>b����zb�_��r]��������������G�#�^1Z��'F/�"��R��Z��Ja�������0
��`���}�'�D��q��S<����|��/��fb���������}�m����sgT��sp
]q��6��%����P�{�]�~�����Xi�
���$.1���n�I�
#�3'���t�ln�s���^�U$�#���*���F
-=���u���*%X�4��S�[�\� ���A�����1��n�����w�)C,����:��\v�g�����������*�����C/=���`�b$L����In�r��\���d����^��H�����PCJ�Y�g�<�;3i�0F��S-������A�/��]�K�q�������Wh�u�F���z�UWKa�=��8q��-fA?�j8�p�����h�	�%�l��aO;�������n
��(+�q�0G�)�D�� �.kyfebkB��K����x������.��\���������)�h
_D"���n,Ko$����(��(.�6�@��VR��;F[�(��>mw	����u�����9������&�n��Q����I2`���o�&�3�Q�����p��~�p��z%����}eA�����L�H�NEf�Jl0�*5n��!�N����hk���X`�Ao�(l�������t�]|�i[
!��H�.��:�BZ������mm����}���$F`��J����_����7�GO#�@��Z����I�����U����Z��$�9yf[�U]�U����s��b������R�����|��)d��"����L���T:4JQ�$'b��V#�I�E�L[���J�A%�����DL��	%��
��\w8�7U9 uL����<��������t#K��
�9��o>����
1E����l� 	UH
i]y�m8b��I�j��M�6�S��d���HQ����*#<��I	=��<K��x�����S���Q.�(v���T���a Uqf@%�j��B�lB����:��(��KJ�ap9����8%<�w���[D��&lG�5m�����+����H�6��n��,��t����z.�iCv	�����>�\�K,���W&S��*�V��o�"�\!��T3�L��>TX�D�s����G9�O�J\@%�j��76��`���^���!�����|��xt]�����\�b,�A��81N4OQ7�%\�|�E�������I'"^G�4Y����Y:17u�0���E����26V:qgC0��Uz]�4%6@�)�e
b|zq�e���dG�e��>���A=��8�����e�Bo�%Q|����N��^Y��N����V��]���s����?����eN�j��27��i��h&��l������,*��q�
��V����nu���%������+L�V��\F]e�*����*n�\�U�"�M�����%�D+���5%Fv!R��ry�c�Qf���l�)������V
�UU�UC����A����#VH.�(d�/^e��4 7��J��+W7��1��Zey7o�X�Pe���l��4_�]�RQ�����2jKk+4�~L$e�*�O)���;T������J����cQ�r6q�]���4.*�%�^��6la*F9@���m�03�K�b�� ������fCC��*����a��FT��X����H��]n�2:��O ���M���L��j#E�G,��J[L��QW}��(fx�AH�����`����3����@?e�SR���!#��$`V��S�)*T�0�
�+R�f�3H��=�j6I��Dvf��`��tj��9C�(l`z��i�b=
������.���_���*X!�Y���O��xl�g��B��{3}MU�F`����H�H�y��D�ri������RQ<�&�>f���7@����W��ePc��������^�#Y�`���5b. ���`�G�5>|��^���p2�-��'o	�Ny�x�j�p�1	�2T�����������:����c(�A��7/7�_��z4r��s���v��`p�K��s��E���X�!���<e��_~���T-qTo�k���V���;ul��'Wia���a��%���T6C���c�;^isk��j~�*F��c�C�b�����J
N�+���P��9��,�[%n�����`�����M�S�k�R�(hN��8��������+�Z���Oli=�z[�s�zv,�^�9%[,R-}�����+RKA�@:��m���v��4��nX����bE++������k�����B�=w~/���%]W.�@�
��e�b�������P%)��/D�
n�
g��.2����8�x#��y���m�!�w��Z������Jz*��Rc�
�YF�[L���;��.]p]i��������JD�zy�C*J]���%VX�#R���B��� O�����������0��Yu{�8����
�B�^?&l����OO�.�@1^Pv�8�If-Q���M�g����������tb$���������f�����A_�����bQ����yZ��p������A�������mLh�
�@����.rD
I��{���,}��R;��
�ovF�z�0����������,�?p������j��T�������d�
v]	<��Bp�P�1���L[�r�7���*�9K��NY"���ro��'j]_�����\?����7�oO������"��5moNT!1��D�x3X��h�z�b �s�t+#�@��^����&HhY��_m����e:����F�1�(b�����tX�5S�"��t2��>�%��Q�p�i(���lik�q�L������8@�Il�fc0+�9z�A���9�~��p
P+��%���u1:!�#��'t2R���'�xP�+C:�D�cw��5�����x�l�����*��8�\\��/}%���\M>7r�(VCP[41��S-Z
��a08_��v"�M')�^������lO�f������N2L?�oOsR+����������c����~W�mee=�=���\'BZ���1/���(�fn|����H��s�.
n9�K�t{1X[T.9��t?����XY5���a��W/��4��
"��)����~��'�">�Dw���L��'�u��
|�Muv#������@�U:brG��g�v��[��`F,����]�+~�p��E��A�M�����h�RI{\hX_���(Zm,O���'A������G�qC9!��D~.���i>M�������Q���# ���\�
�P��%��Nr��Vql�C
pk����
�����<b��:�i!u�� L�0� �Il���,��E��
�g���Qah�5T;��;S�T������Q�5�c�bz�O�8���u��oh��`(T���J����I�;������N)i�e��?���x`Dd��p�i�\���
��r/d��h4�H9�v���#!���������*�o��Y�WB������q�*4E�]�K����IA�"�9�c���*��J�'���@�������d�����\7�CV��+\�����>��|h�f��m{E�*�X�l��2h��OY'��Y�C�
�[����K�
����1�7�������!�c^\�D���]G��9WQ8F��q.cpU�|�����I��]J{�p2���R�E>���l�qh���*^.��|6��X{9FU;����*�*q�l/*U��A���^���3�Y��M��9�?/<�s$(?�_	����k2��zU�Y��.��\!���W+���;2���ni�e���������c�y�<�hc���� ��2���a�"���9O��v7��&A�p�����E�^9s���#�T�#��>P�q�5�'|��L��D
F����$r<;��f���E�B�,"����.����g������c<����e��Q��at�oK���I�OG����P��&c�&o�[�DGjB��@4R�\�%�#�	���W��D�0����(�}x
����=P��2�dR�#V�-����V�jy������OU��4���$��(��'����#�].	��}IP���?�)��!���i��U�?�����Q�!�����M��C�������,��5m�_~�O�1\E�`�,v�-P�5�M���L)�kO�j�.YSQ���#����,�Y�i?���FqXJ�h����P��T��F#&@�]Dr��y5j3��7���	c�C�
���A�9���
���a�'0�C`-�`���U�9����B8/Z��S��(��Q��.�gSRp�.�1+��CMTl���/�ZH�T���0�i������B�EI4�K�hE�I�{�C�	0�)
���j�S�+"�Ut����B�<�S��7T|D�y5��-����
���,t�@?<��lq�g���q�����|��E�o����������������q��<{��U����6yR�	��3������a��gu��9C6i%8�Um��/U����x�/�^%X��N
gzu����0t��l8�1���k� ����R��������������
�;���d�#���@���H��Ri4�m��NFb�T�o�wZ�;\���=p��6��]�{�@�����a]���*�X�td�,K!��Y��W��<q[��^e#�a����b�0K��f��.����o���:<kB����'[���f�v����/DAg;������AW��lDyz������U���	����O5����sr*������X��o�]�4�����!��b�k���>�=�!��2�Mh��1;�n�����yq�$$���MX��_xIe
4��Z|��a��7Q��@�P����n�+�b�%�(�>R,#�H���8hV��|k;�k���u�;Gp���i8C��?�s�'�6y�|�������.d����@j�>��$��@�/�P���~�d���7��J6@\YYK>��O����:��x�A�����T����q��#T���\5�!��M2��?��K���pL��:��Bg-����O,�{o>%�.�R��$c�.��d3�e�K�^��jW�a-�l}���FaW����zr���+�b��u%<}=,hOg��)�Y��L�k��D���/���n��%�}%�
k�R
#]���dn���)Q��J\��-B@��l7.Vy���F���]�A��n���$A�uU��b��
�kA:P���^������R�V��+���2u�����em�f���/��G
d&��H�2��Z�M7�tp#-������s�y��,�+7%(��� S������J��iN���@��\��Uy�u��D�)4�&����e������#�\R-��k��K$}���xn*�R�����]�Q�geC��u
d�a,C��HIF��d�~mN��IB\T�biyu�G�����N�#�E��W�S�\\3��kH���,6}��5��P�2�G�����.)[��
7�*����+:Vh�Vs�_)���
�����E�0�78��9#���:����%X0���M@������@`�&mYx��^/Y_���n1���H%#��M;`��_���:�y����
1e&omn>��A��M������������n5����������l0h�������G����L��Z[�[;��\��_�{�z�X����&�����7a[�X��0���������l=~�`}{s�q��QL/&���J^��t�d��A>�h��T�onO;x�]��F}��b���>����S;�Z�k^��Ky(�/"�������[��a�����K��9����j�s�~R� ��/�S�� 2r���F^�Kj
��N�i>�g�id��X:��=��A�����
�:n��&�]�M5��i���,����f�T`5�V�!g��mh`�uI1�����3���t80�;���[J*�A����J����A0$���_��q��k%����^kOub��
�$�%RI��FjB]-on����M���ZEq��X�����M�g4])�$(���\����Z���
��/����b����br�]]�	�Wu����B�������gN��1��y��S���@���a����jw���k[Uv�.���=G���$��s5�vf{����-_8���z����K�O��c>l�V��>���L���@��{��t�D�
a�q�����%���F�
>���\]���?����$����(O[|K,��y=�����E�3-���/D,k`�
��M�&)�~mn�*bk8+�����L���"�2/�I�. v�;h�'�5O2g?�'���"M���J�D��s�"t����HyA��:w'�~�4�r�����y�����~��������R�I[Q/!n9"YCn���sX��N[���p���d�{�TLMr��+"��7J�	��=�G�����A�L�i6��~��z=(�`P�;�r���S(���:AOq��qTtH%���yX����PN��^��.-��<`�e
l��:Ae���z���v��j������{�n#"N�5X��������D ����������
lo�������6����u�����Yc�w6����B.UG�KE�p{X��a�S��1��;�<a��9{zK��c�����C�	l�KN}s�yn:ZQM�����Kt�X���$1��\�ac�!|_� /��������x@��
�84Z=�Nt����ds�Or�c��UX����\���[<<�{F��X 1z�1F�*�����iB�a:~|@���`�
B>���'(r�������w��;��J�����0/����%(���.�5a��y��d]Z����R�	��,=�E#
\\���b=���R�+��r�����D�3�;���`�M���1o�j���������;��
6�Y���� �5m�6�YXu�������w��{U��v�[m��?�>�<{�����^����V(y�t�p�n�������7������[�R���p�@�q;T���i�k�F��_���o�_�_c�:z��a�O�K,w����71z����4��T��.��,��4�%V�l�4���El�w�|�,7�1�j�vM9z�4����l���~��E����o�cA���/�O�_�u�=�mo�o1���+E$�t�	m�6Z��f+�tw~I-o�.K������&o�4����wy�O�{�5{�-��O<)�P���X��)�llZ:h�T�n�[�xC���~e9���k�����y�;Wc����:T���������Y��R[���h��=���m��W�o^��p�}S}D
��'6��mm����	�&�:�7`_��.��|J0�$�U��=��n�Kg�)5�+`Q�����	�N���a����X�%!	���0���%�Lg� �fP�'&�Ri+/�NE�
t\O���`3��|B�cO�R�r���b��v�X�
��H�Y���GX=���������/uv`�L�q�}���$�&��v���������Z�GqS�Y��������-g��.\Q	)]q�}��7!�����7knI�����Q
�]���
~����17Cy~�}���%6�+:~���vT����;nB)(|�������t;�����Y����B����?��~��*C����~v�Rw��jd��`�VF��o����%���b�#,���� ,>���j�u�%�F��2����x���+�e�������uv�M���=p9LK�W�#B���g5��f_��z�7p/���y���������������m�K�z�:�ev�e,�I�S���xI
!���Q� ������[��@�lF4���NX��U6������0*�5��{}6~����& �B��h�la��6��`�X�AJvR���x}�;b�?���|D�KJg�	""�l��43x��|L��c�X\��������'���~�E�j\$��R�	:�.#��m��$@}����O��%Ju-2&C����v��Fac[\���a��|0�����d�1g8+l�*e��e�4.�5�����g3���t������j��T�$P��g��"0�&�y�it4���e�@q���w�����t_���x���I)s�f��`���JP�l�Al�7����������\]u�a��dBc-�-�sH��8,�����;��~��?Y���K7�tZU�VVrH�Hq��84��9`�Z=���2@#�GM�R\[�J&�@n���nUr��'����GDj�M��V3$x���hSm���d-���`}���
|��ec�!gHj���������7��
_��x��W��#/|��������7e�=�,KC�V��8t/�����9�A�-*+����&����,f�|�zY���0��]��l�T3���bv/T{��i�%��p�6�L%�1����Ek"8\<hL��T�21\a���:G�:g�Z�o9��37Z��m?v���Wq���'�@�V�����0 ����fFX��f������^X���o���;z��.v��Y�0W
V-\��q(�W�7�P[�Q�j�f5W���Tf��!W{������+f��]s�QGW=��#.�\W�"G�WK������]���_�e��^��Z�[5W�S����?�b/t���������b)Z��b��}�s�w��Zf�#iUn�yU��#���.�����?���p0���u� ����X��q3:�k���p����k��R�Rd��iL�Z�$�=S�ks��l!R�x��h9�c����xM�������MP���6�`^�S��!���u*nN�U���|Q�����ZUAW����Y5��\B�W�+�Y6��4�Y=��N�GQ.���OOu)������e�7r�7������E�8�i6�[�'B�������<.�����]�s�Y�C��
XJ��S�nZ>�uW�.h��7������r����#T<�>�)�^��O�Arb����U�,�����*��P:�p�����6�
EF?�g�g.��&�T����K����O������o����?�6V@^��T�5���y�
k�v�����e�Uct���#��U��F��% �!�j�n�bVd���u�k�PW+�|9�?F
�Zq8]Do��:Ml��6O?[�=�
��(�R2�����.)8��Xb��X �P����CwjW��rd������lu�!2�'[���xG� f}�5���[�;�[�����#7������l�e�B������������UV�Q�#h
E���EWF	]�	_���������6�,������s������
�I�TB���*��
�0OH��l���A�q��"��*�"��F|J��DF�m]��nW��.��B�#�]�r�~\	hq�(W	Z+Jr7�,����������^�WF��N���xA��W�3���rI?5%�U3LT!	@
i�-�_R���/]_A����1�p�y����v��RR�Kd������4���\�!�T�V� l�<Kt9��&�+���U�0�W������e����D��0�~��hO1��=y�:�S>$���S��	Z��o���b��[��:ii�~����TL^�Di�_%�[�
��|�7
�dr��'�Q��fZV�����e���I>�@���
�R>�\Q��xy`�s�#�5���+R�W�w��h�|&�-�Bq�}��E�V2��J`���J4b����7����9\�_�:�d��gN6)�Mn�j��"��]}c��+��q)���T�����1���n�2��7Hz�F�.Iv�/��d�����nPh��	k�b��?�$���@��S�-���/�DJf,\g�Q�S�UJV��n����������6�������6�{ml��y��p'�����
�vv�Dk�oF��V��[�?n=H���w��wPi  \�6X�����c��y����������g��M6!&/Tiwe������Fx�
�U�mK~m$�}�M����^W�l�y�"�������++o����
������/�`�����u>����IF�=���8�/
�����v�rb<nXc�����bl�aQ���Z�
��V���#M�����p�S<��}��qR�Vb�(c���N_��]e����gpIP�V:n���q+�l���7�,��Ht<�4����W��M��u~zs6����{�TX~����Z<���/�w�S)�5�S������J� �F+��+B������	���R�U�G:OT�>�]������^RL� �Z���s��Wb���x����L�'"��B8���u�%��Q�N�;�/�F�j�o����~xrzD�j�a�T����x�
�K��f��}�b�P�z��p���_�c�����������?�mlt:���7?x�x��q+;r�����;���?���_�Y�o���8�\�0�l)���yu������W����)\��%��U���~��������{�3��k�&-J��kf��f�J�a������y�������2�7�{��^�:���!G�Y�/�U��VVJ?Ao�q��M�M��b7��Bj��E�"X�F��;�h����������G���������+�o=�M��oJA���Z�*^���l���'�%���4��U��.�Q����C~o������;��������e���g��|(,
���wiQ����N>|HrT����s��KJZ���8�,���-�j�/%�G�F�E1&;Tg�E����"IE��,�,������^�8<i����%��m5����������C�!
0�7�D
GS_U$B��0� kn�
�4�G=������!Y6�:oG<D�02�����yf��; 
��9r�_����:2k�[�EFu�gf-�F���/	
p�	���_h9S��]12g����q���cS����N��;qpP(G?1w����xq����/�s='�t��]E/�Qf�l�Qf������yI0~EC��}L'��Pf����_�<��N��2aaEhg��{�F��������G�U��U������W��&c�B&��=���}[����]���L�~���K���\H.sPf(<����0I�����>� ���
z�*��70�F�HW��f;���[��|����^�le�n9#�(�W#sH�c�;���|R�=�W���9��f���d���`��%�q���	E|y+h4�Q���c��6?;����������t��r�8�Dt t+#5��=���O��w�"��S� �|��)��WVX!$rf�X�|� s��vf���]��N�r����[
d����gK��_R�8�����90���b��S���F�o�#��O���y������N9I��(hx�p_	���u[�*��xR����9lT�2������"g�_pH�"F���E������������M��&�cW�O����y#,�n�cEQ3(��#����
'�`�n��Fj����9A[2����D;kb������`��K�7�*h��������"�*����J�5�OJqy��o^�\�DVVd�x0�=�G�Q4�r���V�����g���0+��Blh����X6W��T��>��f�/ ����^��%�������)t�6M
,A<Z�"X�/��7.j�����f=��}�U\#�S3����`�%�9����i3�����I�~��E���E�!�2�'��P��{��H�F�����I��"���aL�@�H�Eoj��i}h����M�60bFN���[a��G�o���z�A��v�T������>C�gO3�A������w�q���������Z�L���F�~���g.��7��'g����1��-�����������L�?�f���vwg�����6�]Gsml�
��v6[�5���A��&^��v�J���vV���b��&�/��E��yZ�&�$MU��	Ft���vV�'�h��
�@���d������	yh��D��m�M���=���r�	�Z�%�F�}��0\�8��3�IF�d�b`��/7�R���\�1��D�/��A{��h������;�y������i��B��(-��P��~�t~,.&���Ed@v�f\4@��T�Z������f�G���gW�y�s�rz�sX.xY9��(����Q�l�C�������Z�0�G�0�.h���v����F,�����{�m��)��Y}����������w��t����w�1~���oj���c|�������?0��9��������'��������z����!�)h����%��e�3�|Z���x��������dFi�����yw]2T`��.s��>4}
���Q�����
[X�~�?��46<�E[��#a~J�%a��7��j_�M:!O��`F�,�Srt@Z��40r���y6�%z+/�<���SY���m���#:0�V{�za=��(D���W�=��%���	@y�>p����z�W��Z�
d�y���*�6��%�Ob^�}�R���o�=�����T�n"�x��x��>���o�+�6��j�;��R�:���xW�P�v��c�)��=D"fc���Vj��JFk4%��U�u�+K'rN�(�1�/�l�VLS�,�b�]����o����:n���������b��yss+����v�:
��N��}����+�����h'���+��W������[H���G�w,�kF@�~�h�_������t�$i����bz?��y?D�b�t��Cz� ���O���e��e
����O����v������w�L�8�4b�H���;qNqx*(e�qW9��F1�@�}3Jq��@��
[�NOo��P��.��pRPrj�u����O��z{��#�i������)�3�W����s��t'd���i]1[�e2�'b��!�04x��3!�xZ�
�1��8�+�j?	��_Jv5��z~t��3�Ug�U��/�rw3��.0��|*!~@")�d�NXr&��8�nA�|:(r�7�,k<;�X5����e^G���xq��3���w���z�eI}9���,D��af8��tz���S�?�R����}�~�s�/9�����>��F],@	L�li4=�Ml);s��-n�5m����$���L=@N0g��-�g�5:+��I"��Q�LzXfhG����Q8�_Yu�H��:n��#Qg�
�;w%�X*e~s+ �5��l����q}�y.5e���z;Mb=NM�.�c�<V��~s���W����?��O��[@�~��~ �>ZV��4�0-��@P�XW����b�qK�M�:�Z����)���u�p[����%�=��3o%�:*�� �]
g*��tY��wz��;0��4���G�~z���	C��Q�*py�,���{�8�W��y8���]F��V�!k
��C�7E�~�U��[�N��p!�%����+/N0����s���^�rYBq�4�C
I�U7�&Q���@�����U\h���+����S�S����oQMV����r���TD��-!K�P�i���~����Ww��>{&��F�r����@JF���<C/_�S`�����  �q��%>���P<�?�a��|^z��}���8�u�������5@t��_E>�}�/K�>�|u�eV��D��nf��AQcJV�^bN���������V���7�0{��'6�sOx�oz��,���Jj/�^�*?����,�x�{]� ��d��0{b��3w�X_��Q1P.�����(�o��,I��xP_ ���Wv2�N/�g.P x�p("-��	����7��w9�R,��Z�EwR�X�c�{���&j���R
NCw�<C�����V�����"]p�
vw�*-(�Cg^;���K�4SW�^P��������,�o�b������t_q(l2U��q��B�a0&jH�SH�Dp���q�q��:��c
[��iM������t,�Sp+x��`Y�sDy��l�t�i9^�c*K(�AL%����]��F'Vw����A�l��tdW�r$��T������=
9�n�����i6�|���=5r�_��L�\m�.����yR7�(��kc�������K��1����y����]���5�s��!(�a��&�,-��q��`��F�g��+�q��X8`������z��C��6A��*�]2���S�1[ |+�
��
8��t�.!w �G��b��Nq�;.��A��K��YO
g`z���lx�)2u�;����G�g���OF��h$�#��w	�# 0�%\�S�8K����C��n�-�xF��e��T��^nG��[��V���F�	��`�����AA� sV���}
�����U�=�g��p�������xAL#��)u�����D!�`��5"�/Th�PR�s�(Y� ��a2�
y�^C0Y��TDw������������5B���E��i�T�Z+n.��*$13�|�\����a��������;��"v��mJ{S�A
$\������QR���)�e�t�?����u���Os�����i��=K45��vw���E�
KA��}h]�.���$�
�A��F�>m��g�|�"|E��������a.3�%p���+�+��o��Ri����`�*c�%�onLz)�.��,=��������������;��?~�nv�����=��M��@����}����x��~�vr��}�ZT�n,*<q�k�A+(��h�����N9J/�d���U`��n���M
[E��G����_}x}�<��vI�����2����c�ff�m�H��p�n�b0��������\�^��AX�L��;����Z���������t����wXv��U
�����2��4E�� -�"]%�C�����E�����1�D�4� .�V,*W�I����2	=��_q�9%��r��T^�`���d��������vac��������BM�{���D@J����W=�#(	�XJ�o*�r�a(
S�c$F]u�0&��f��5m,!���S.t%VR�h��k���~��A��}��U�ZWK����]�o+?�,5�B�N�JZ~�r��a���G���k��k�� ��L
�����F���<}�Ur4�@��Kp�����9������m#D�6�x��}t
�c��t%}'��0r�����DQ\>
��k�(�V����,)��5�Ih����%?4�
����q?d�0Y_���,W!�	������������������
L��|��ywsssg�P�:�l��u2-N6��QCo��M�M���������������o��I����~�������ds{s�����l~��V�3���e8M�9���z�9�)�+�����v������{�wtv{���[=�[���?�>�m=�n�ou��������F��z�ln>��%���<I�.���^�R&�O�o���q1L��
ss>k�;*3����L�f	�_�y�������dm��Og��m�'���{����sw{�'�cu�!�F~&H+��.�;Z���>���u�DK1^H�D�h�����`��XY���g��5E�����D�Fc{�.� N;L���#wN���)u��.87��:1�����l'bS5K�&6��.8�$o!0n�������N�f}�I��W�]��3#eL br��H*��Sj���*9% >��#-�t�\6��4����8
�2��A:1�0�����y��JE���u����l��S���O����Bt�H:Yq�x Tc���������P)W �_ P�Y�c��$��2j2cC&�4�W�*v��g6ox1��l4��6O_�<I� <�<9�sd���	�}���|�a?�~��������Up5�K�25T���n;����,dC/���c�	v1���lw�vw�����*.�}@�I~�`�V�K;�������5��2��4�Z'CJ3y�n��v�F���|P�7\=��l�CG���F>Ck�O���;3����$P��*
�	�;3�k�s�������3F�y`f;N�6�F�:I{�49M���F �Nr#yM�Rp������$K��44N2����~~��q`����$��N����w��5��w�w������{���:y���l����|���7�9���U������G'g���&���������vg��e�`'j$�Ks��~A��d-�|I����k6b��?��F�v��hN{���]ot[�|�����_��Y
hcn�9mpm��Kr-����Q�����)������I7^���(�5PmJk��5�2�8��.}(��sFof�Zp���O���O�R�������q�Ffbp��%-4�o�X/a��������������o_mi30�wwp�	�m�h�b�1�<]4������Xy�
��Tm��hq:���=$��V�������A�%�-�]p�#-�.��x�b�	���������9�'|=��@h��Xx���+��5������/d�c��R�g�^�V�����l|1I�Y]��7�������_�X������_�&����')����c�z�����T�
���������������c%vWH	h30��7�mD0��P��m��)��y�(q�J�Y�����~�)�V$e�-��g�a��Ue�)m=��]Le�2Xn����/�e���c��cQ�yi�S�
Qj\��M��,��~�x#������Dms�bv�9����;�����C�r;�E�@W�Q�v��c����\[5��6i{���jC�N}��nkO0[����������N���H`�5�s�������m�.�5�B��|�SAGz�h3�L���ol��t�����nb���ry��
pF�+�>l������n�ud66��������~e;��6.������B����|btH���������P!�:������ ��c|���u���6	�)�8�g�X���r��p
f�vZ�7������,�t�u.��[{���N�|��Mll<�J�[Y�Qz��pk�V�q�UDm�B��-���v����<��D��'�P9���������hx�w�o��2Dqa;�]�?I�
a�Y�~��iGZ��?M���6})������_��u��Y&��?M�x~�����_}���=xh��J����+���
��������=�lw��{�����pn��������n����Q����?6�>��)�f�����{�����7gG/�������������N`����I�����8;��s�f�t��`Yn�������T1��a���2�b����0K�8�Ss�a���|���^�����S��u�4|�s����'�{g�����A[f�����W3�l��\���7n�M��-�IM���j%���3N�:�+�,����������2t���'��F���\_����!�����v5�rpt��w���������-;�[���{I>���/����VjS1)�6��%]Jy��JR�Xt��W��[fA�_0�
t�U�
|��H�T���0k\_�1�~�p�~��I��n��F���A��{������<�$v�-.�E�����[��-,��rn����xS:��)�R�)(�����H��_�������8��l�S�F�wX�y|�����:;�lc�����{w6���
�Ao�2P����Pd$�+Q�:��+?6��\���FP�7��������X��i��+�-C�������j���;�k���[�����s�k�����������k���]��`����e�]�k
�6�pf�Mt(U<�����}��[�^!C<�i>x�u&j���z:�)����rD/(z�Q���b��U~�G�	��\�����=�l����N��8u9�c�]���du�����}2�����E�9zc�uQ�1��
Yg>��C�]��F�{�MVN2�O���t��,�+��lxU�0�]\�G�!����a������ �F���_��ES�����#:����lF�Z������~��vSc����pV4��O�=?;�{s����C��`���}��C�[?����[�;?���=�����E@�����+�F1�K�[����B�(�jx�f�D��Wzc���������
{#���1N���yJ�_�}M�I�PM�P��w6wz��;���h�}I��C@�ol%���B����Rfk�(�%2J�Y��Y"G_�����E�����^���2?����yq{�^��~�J����D�iI���h}x�b����H�,(����o������e�G�v�.�@����l���D5�D��hC����p#R�/�[�dB(���)Z"^��[]-X�+�D"��7�&��_!�$�������
���z�	��K(����'���><���x^\��r�32�W=E�v���z��U
���Wc����2F1��S:�"0�����.��=�?�F���U�ft�����OBf>$h?�x�"�p�+1f��a��}N���������qc8�t�S.Zg6H�V3��SV\U�'|
��R?�gF��D�6�?�����
���>���h*?B$��g��r�~�%XY�_|������W�������_�~~x���zS`8D����:�n�B��N�F����J�Rf��Ib����c:��[A�X��\��fC���'��6�x�x?awU����b�|A9��{-F��`R1b��J���'����W_���z��
����
C����u1M�w�0�
,o�r4�������!eh>�u%��Aw�A�&�����*u5����q�������aPU*���[���%4�p<y��p=@��"�@�5=[.D���,�{��s��������	�1m�������J�!��W�[F?����Z��
�#/�2`��:�q���e���n���v�w�O�s�	��v������}P��6����$A\d*^�x��W+��e�����~�����V8c�.`�O��?��* ����/#����2���0���}s�.�����.��9��0CfA��-�B�	I���+�gi�ay�	���4��QD��)W��gb����2&>��O�It3�j��U��thv�g�(61?T~�q%�@�[MU1�d����M�3�!O(���!���_�y'��eg����H_X���[�T* 	�/�E���B��,���A%c�h?��SDi��>N�������Wo_�<<�]FR���e$>�`\��4-�G��B_<�������y����us��g<%}e�1^��L�{��<7d�?�S
�X�����������u����j���J'OV0�gi��E���N���>bBD����\k%j���{T&>���	%�	��B�]��oL��B�y��.�q#]w@B'U�� ��P=~L�W�g�k����f����~���)J��[���ZIi�V[�w��U�� ��&�x����t���k�K��� ���v`��X�g+�7�����3v����\��Ir�s��Ub���8;�>H���\I�K)�AQ������V�4�4����d�%����x4V��ZFB������@���y������a���dm��������9�R��h�}���������eb%���2T���[o�{}��W��CW9�
����������P����
���^_�:�nU1������u(����E_M�?��$.JI�q�h�I�y��_�����_)�a��;^B�9T|�GgNP���"��f�p}F�A�)u22��5����$�wB-�cy�SjZ���p��w���+�Wz��}������KE�4we�=
�zw5�m��:��x���:��)1M���vy����P�m���3N����O����W�}q6��s }4}������qW��F288|��������W���{��m������&����++�h<1���4����{�_����Jn���[�?rZ���cZ�~;��-�No1�z�Q�����)hG'��goO�����<N�������c�Bk����M?qm��2�c|`:p����v~��c!�Pj>������Dm�1�V�q��x��[����3������3���B�2�"�K�X���n���[�R����~��dH5��WW=�%���
KY0*��5}�������[����N���F1��k��H��#O2�b��EP 	l�tQdX�9NL}��w�M�J:`�'|I��F3������d0�+��P<�*rae��.1�G���8;����
#�}�J"5;8vx��Pxj���tZ2 )��6/G1fw���p������X\Y�h��<%+�oLc�!E
���"P�j�������������c�FQ��'P� ����CaC���+��P�*#��UP>7�K�.
��A��	a��0��WcC�$�d(�uT)����02�ox@J��	�F�8��OPyt8h��P�m.	v�V�LG}��s�FT�B������I��Z
�������9�
	�
T�V��g� �wT�(������<��V#��D-���l
���4�t��G���yME��|k�RC��.v<<H?{J�����4�=�n}Yl�S�ds DuBWY��)2D(�H��m�n`	UF���~��e��;2DB~~|������U9��q�D�q?=���x�!���Y�r��>8:V���^�Z���gC��!�'�b����`��u����c:�^�����Z�����H:�U��)��8��eJy�~F���d������Ysh

�3R�l�H����,���z���X29X2�GZ�B����:��@=��!��K
��0�>xYP
�f�|���	'^�!�(u����+I�z�����������o���*�����������K�;�,��I�0�HQ�Y��RTP�?9D��iIM-��O�4�bx�U.��)���� D*j�N�Fo�2����C�2U��?��S���}"��^=���:7���"���P�����Tsf[,A��[^A�h�B!��m��>�dc���H<MMG_�}����5����:6bk��l���y�mKu�%�`*��_�;=�;�V��1F���r�Q�+���_\�g��W�a�/2^��6b�V:,�g{g�N�/X�Z|�H�9�d+1����(]F�@��S�kj����2�d�xN5��*Y:��Po�<���G��#'���w��Y���$�w8GTg0"��R���N.���L���>�"	�Y�w��b����)��d����c�W"W�p�\C	�2�������B�R-�� ���)��6*���S�qkF`���C�
�r����������0���k��=�+��|��
������d�<�prA����c��o������2���*�Q�[�m�2�%D����#zm5��%"O��x{B���q
��cw1��K��s�`k���;�bcH�tO&G�=���T�jJ���&��(a�)��l����3W�ms�0�T4����q���,�V�2��.\��`�s?�
B�U:��[A:����m���.A��n8�!N��>2Gl��{�K!n���Yr�����>�z��Y�KR�_�J��� fY����[��:"4�����
�=�l����4
�lkz�>9x��b���p�O�/������E�n��a5��tVU�06]���\�k�g�kWn��2�:H|� 4����:�����u�
�&�M&$�~8:=SB�
���hj�s��]�7o����%��9��{�d�������'��@[� R��n�i)�kX\4o���2<��:�����?o=I��Rn7E�~k��h$��Hdx��G������]�����y5�I'�s�2�,+���A������7��q*EF\���Iv1�j�P�z��S8�t�������c)��:���Ok<8��U��i�G2���K��P=�
�=�P�:�����;7R\�8#z�mg��
�y�m?���sd	����x�Yi�����;�JvC���<I���:��$��v������0�?�\a��^��z�%�.6��3���M���J�D�� ��A����,�������	Z�&jF�m�Mz����
���I������=�C���:���+_�QR�l����i�H�\M��1�H0��cp�l�G��O\U�������c�H��:ZBW�X"��F�)1�G���@������j���m� ��6��f�
�j@L��J��B1-��H������%+�����H�z�YF�#� /2���$����G�������j%�7EQ�.Z�[N���ru��
A_�@(�����G}�o���%�Y���
�����3s����^���V�1`��'�+�C7�I�������0����"��K$�OY)�"jm����j��V��M�[��VB[qs���)J�	M��]��Wk$F=Ua�T���.��d	1�+,i*YUP�]Z���XS�\�b���=U+f�g$���S�'�j��p��p�.�������!�&5����3pd����L��������*l�2�-��2���������R��1���;Wk�m��y��"��
������x�����!����v��!w1t�f:��
#�+,_\t�V
a�	���o�5����F_pR�viH��?7W����u���=�������D�-�`@F
���3*����#�����x��4���x�}�0����G}
�~w�i�|n"1���M~�}}@
h��%�H@T�NN����q�H�k��6�i�Y�������QxFF^]m6�](5��-{4>����H�Y��XG
 ��}�}�%D��X;F9x}hQ�tT�uU[y�7�������N �a��(�.J��C(�</�M��P�����3s�Ng�@�E�,v�.k��b5I/Ra�B�����E�;�b�����x�+�<V���!���g	��J�;P{�� �*]�!�S �T~�������;����8�/P1?���:ymq��ZQB�N{7��B���s.��N{
���9��Z�CR�l|<,rSv(gE���[C�����J���1tl?N!D�b�)�eT*�{�?�[������I����7J�6��lR����Yi�6���@���e���4Q)N��ef��4 
c:�8	��C[J�Y~��?����a�)��M� HHd�PB]�%�QJl�~H�/lD�l��4���F����i,�]8W�*���d�~#H�a^�������3�����'�G��B j����\\^�3r�S�S��:��J���LSQ�������r��t���r�vy���(f�����Uz]$��\��kcQ)��[1�X��(V�c��-jM�.H���2�t>�."9:���E���/Q�1w
@RT�	��dsW��C*��,��Yj3eQ�P/��8J�AM��~�jc�Az���m�{�3f��-H.���^����L�b�p@j�Z��q�gj�2Bq���2�lS�(\w����}��\�
����]A�_�5��C(����%�a�����D�Q��(����7|�2���S�A,)�^FdrGLMw���t���6�,��6�)�LN^r�JPts`i�)d�B���1_\�6P�
u�1����Q��w�m��o���K���j7��f��	���[6���M�]��2��1C��+�N�6��)��Z4�|d�.@�"�#bU��{�IT���&f	�D��3: 8�qA����J>+l���a��D���)2�!Z-9"���#����3�Nq��V���oCJ�(3���<M/�>E8�_�����N%���2��Yk\�J2�/)w4B��DI���;�[��o��zY�`U;���Q����1�����y!l��)�
f��h�m�e�{mb��B��'�&{�y��\��	���}�v����@Zd�|���d
���8(��;�8����d��X00{q1�4��ws:A)X�Q��o�$��q���	�2T�@���1Vj��d��&�OH]8�2���UCi�r�*��T�gH�������	/X��bo�]���*��tQ����m�?&O���94
M�$������.�D����|?�@���kEa��
P��Saz��+O�������D:���J��:�0���S�����D������b��0m��a���G�p,����N��@G'o�U*&^�|Q�	���]��X�*���s���G#�}f�:����1<X����A����4����D���E�q8X�8�*.�:D9�3���<� ���M�!���
�p'�s�?)	�&�I���j���$t���v"�%gr�o�tbB��������f�:uI#�kt'�}������������3���YZ���}6h��U��m�5Kso�@�������nHI��3%����
	�/G�&~���������W7�nSV��`|_-K���iAP'
*v�c�4����������z�7��Wg�$FS�o�7��������+��(��O*�4���a������������?�y��!t��^^$����������6^�Llw�Ky+�}v�I	���pG���U�)0Z9��S�:��P	����0+�^��TB�xM#���f���uc�5���CC/:q�%C�(���R�s �A�Ds��wWM3>��F�_S~��H�������������������f:�&�+��H�������N�~'����� ���1{X�����4S'Ww��i��<��jvN^�X���{4����N���7�zY^� /���Pw������%6R����g���gyo�'�xdv��dE}�C4�x`9��jGt6�`
_$hD,�8���h��k������mMF��E3����i�+������@x�j��,�� �*���v��)�����C�Ze^T��|b�d���r�*	��<��|�M�pk"!(�n�=%�4AH�#�=�dZ��xl������}�c��TV>��:�*��Y7�*�74y��V��i)�G�w�*hDhJE���V�"2���
����e�s��� ��K8``<5L������w��l	(W������>��z�C����^�`���������Uw�(3�	Ih���B�B�M�������v�����.*���m��w����fm
�!�P�4��R0D���c!E�������
1����a���� R��XtG��#����(��,���a?������p��V.�j�o��hVJ�b�XuL&���__26�SW����j����tR��)�me��y"��@���Y�=�)�F�O��z0+�=H��	j�����>QJ+B�|Z0�&,v�XOL �]_�hOc3[�,`�����cP����kp��z�����Xr��E��P�p����gI����cA�������o������X5�����!�E�	R��K~��
����g��������
���yJ�����������0�|�TN@�����S�q�t���Y�����`����~�%��AA6��'/,v3q�.������N�� �8�)���)Y����l�����("�"o�,E}!��R$W��Y�5��*v��_L�?����$)�=����Oxa��3|��so6*�(�=��`�J���Z��nG�BM�s��n�zj�9�My�1�;������������k�����3�������(y������frO:�D��E
r�>g)	�]��E��`b�(q���{qq��")e���[l�iN�AK`G�W�n�'����P������q�H�U0����6��6T�!��u��
��J�Gh)���
��g�7p�{s��0����A����v��������]i1q�	�r��>��A�}Y�S]k�d��}G��$�	�D+O�+Le>�����d�PS�t������,fct�}C��A�su���qn�3�/�.)��6B`}��S8��	T���65��� �
��f>�"Ov^�����3��N��6@@"s��%����������^E�����
a`�!CrW����?�^1�jh'�l��w�A�z����YH���H���Q���o�o�
C�b�I���f���K�T�pz��'�q�j�X=��,��_�Pn�{�":��up9��}���{o�
��o����'�0��5����\�;pE��l��g�I�h��Z�VY=
uJ�Th����Yj\���z.���1KN�|����(����
��I!l���G��)P�tR�j�H*.�\iw�\8<�j���A!�����K%��t���Z,
l��j\;
��0������F�]�Y��[&\�>x>Pe����KTS�~�;E�y�*z�{{����#��1���m�@��;�����"ZDD��:���#^>ri�It���e{u��;aA��������5�~������I� 
�
�I�olczZ��O���A_��������mz�`�9x�"x��=xFsB�8TTz�:�7���D��7n�}wA�*�R���C�c������/d�n�le��)�_��+��i1���0�����Iv�c��,������,& GH����%��ro$��|�����wem��#�+�B4�v=:���W�N"��H���s�qD���:����\Js�]eZs��3����/9�(�m2CmK�Yi�tV@�)SJ��t�o���B�������F�@E�hI9x�gx��,�r�����
0!@J�d�����|8�}o�e������64�d$A��P�WJ���A��{��"�tqx�����x��%���'9��� �=����j�A�%2�q��WQlf������� l��!����|[���(@�����S1��7���^����Qw,B�mD������i��z�8A��~�V[|��kB��V�6�,%��
�����9v��;�������!�����'�109���-�<�����(K�\�,�e��P����,W�F9����nP5�3U^���U-����9�Bd��~�f�X�&��K�Xq�y�_�/_�C�!|i)�^�s�a�@��#���/��!�����5'� �?hf(v�O�������l<5td�Z�*%i�A����P������(���	���ec[P�2��]���^�g�C�X�/&�C0W@f���]��J�(�DI�����E^��|z���&H��T8j�(a���Aw&yQ��@�� #|Y�i��G�E	��B)o��Z�
�&��	��I\�z�8r�9v���J�cv��BaG\���,P?,QR?�sa���X�l����?����dC���@�����NsC���S	v������	�y(9�W�P$Ws}2<s6��s�`&$]H
D\`������1|kt�I����79�`'��n����������%h0�y+n���D���J�d%J����|)��1��Z������]���
�!h�� /�Z)&o<�{+��:��/#��-N��F���F$������c��b^�(���.MT.V^��\��S���U���C\���*A+�c)��>���TnTz�gcx�
����+q�m�`����l�h���eS,��d|��G-���N�p�)��p�Vk��H���x4O�
���Cy�G��p�f�'��<�Bqdp�qm�x���]�(�=�M��O n���&����S�~ffL����1K��=������"�
�p%��T�/Z��"(!Ii
3��0U���Y��e���h�2J|�~���i���em���umY0�]��k��J��1I�Z3B3�.V�\��L�������bRN�$l���6�B�2��m�)>u��o���jgY���q,KR|�C1;��4���,�|W�1$P�VH,Ls�gO��[C�4����X"��������$fs�� O6G�pe�����_x&��fn��Wh�obL�F��|����sC�E�R�6.~���-��?pb%0FP�}��Q���h�ES����Xz�n��c�N��z	15y�O�5�F������LjH���C���	�<���2�n��_z3��d��?��|���u�d�sE.�JL�_�	�%��������4��y��G�
�1]�p��\�1uN��S�V�|�R�z���eU+�,��n�{>9&�/R�
+���O�g5( �
��6�M�iS�����/��(�>����������UY2FK��;u1��B$��*�@�if�4|�_��Z�0���[��Hu?���4��,�
�:�� t�l��G]�Vh�C���
��&��s&��b�
U�@��a@>�5ev�-��m(_��^(RE=YfQ�ydI���Z_Wz��<*s�n6p�����k�Z��H���D��.3#'|�����-���@M���<I�TA

qN��B��L`eZP�H�
����
��`���lk���T�`���{��\��-(#�A��f.9�h��+�)a��S!/C�:"[��^A���)��kO���RR-�YY@s��E��@�l�=���`����o��4��]^���3�k����������V2]t�]x"����-L�����o���]B7B�~�_IX��&f��{����,XY�v��M��[��J�F�����\Y�(�����^a��-�����=�Jx�,���>�&�&84�]#8&O��`T8��Jr*\~-F��6�\D�4���=��� �e��c.����E��i�J?R�I�Vv1�V
P4~L��(+�I�����?�z�5��N��N|��������:S)�F� n(tMGG�1,0���Y7,�H>���2"��PF��I!�j�:(x4Hi��8FD����sED_bl�9
1���jL�����]R��.�����X�8O��bq����`�>qoP$p ���B��y��p�c���E�����/����r6xz�����6�l���;6M�+���<P�D`�p%D��g���ZK���� F�I�K��H��V0l�ano���W��b�DV�mI>G��F��^`��OF���`
���EJ(�H��
��O�o�5"��G�]��Vfy%�v���n�:fg�0L�R�C2��
����Z8�/���4���p:�� 4���\k<��6&{R����)�:���u+�`�9{.�g9��f�v;�!q'9	�p��$B�`����a%C�����>��q���<�#,�i�=]+��l���\"����$�4��x�����Z����DL����E�V�7]���58v��U-�`�21�?*�B�������*�Qo6���J�RZT�Vnw1���5��^l(w���zFv�v�)�_�����a&�u^"UTC��'6+@��j�U�|��fh@��>Wc�����
��Q*�6���V3�k_w�)�NhT8�A�S���ts�lq #*�V��������b�����+��:��!D+?2s1�[�z�44p>���0J��%���I�W1~��8���(��r^�0���/�s��\��C���
��r�X��p�+,�=A�~K�fZ�aF������BFA+i�2�� ���n]�&N,�c���y����9��l��h�*q���Z2"� #�^�� �� �����=>,�)��Sf�d�]qz�
��SbuQ��&R�xo���Z�%=1���y��r�9������y�<YON1��C�CT����3��Sp�k��|�hm�k��
�����(�F��&��`�����_!\4�i�����Wm��e���=
R����X�������y�5���,0�B,��$"/�T��2����\.r\������la� ��W�k�=x��h`����[a�Z�P�e8m\ruq�zK2(�h�LV��"VQK�p(X6RK������L���N�Pd^������T<X*�PD�d��r
�}3��;��G4�ch���&g����G'YW������w�}��#Tt�h[7�C��@�+e�%�N�A��yW[��A�d�6O��nq�mH$�u�h4���������B$�j�������<8�6.f>��t���j��|+�s��ht�3a[E���r1����������U���jK���hlF3�5����������[n%Go���_�=8zqtx����^��v��"�q��4o11S�24�M���id>f�[��la�����^�������^En�MPC��>'���*�?�#��Ujnc�l�_�H9��b���"������"�k�w��8���|B*�S��~�7�#��k,�t2Ta0=)3�t��(,�M��p�����?����j�����v��l`�����7{���?/�~�ly��)�O=�y"����V��fUW�*���o�[�D��~�{J��o��4V������k�+_����R��c�<��*�n
+/&Y��^��|za/�B���B�G>���lOe��n����
f�l;�o-�TmJsJ��n�s6��-��U��n2*7����O8Y��~V�Z���s.�V���'����;4�35����i&)w\�=cx
iC�.0���������yb����@1r��������I��v'1��
�gL���bhz���~��������j]�s����q>��]��K��sI�.�5�����R�������Z���\]�X5�AR\u��7�����	I�#�J���
�'�@C���U ,8�b�uun�%7�����t�T�P�n�����UF���8����*q����-�0�||�,��u��7cL'�-����L�>^��	q6C� ���x&l�
}z�|I�&��9�� 0�Af.<\$���Y�-	_�$�0��,�������������H~�������1Y�h��,��tU�Y�;T����/���SV������q{������O�N��z�w��T�n�,���Jz�:��#�$�6�:GTXZ_�6�&��}��YAo��;:k����C�x���������v��0��t��|�>9x��b��r�wf�t��������?���a�$�����2KDb���y��~>��RC�3fC^��7f�O��m�7�]�#���X��J�s�m���O�U��]���8z|�-=���/��^T��&&]�e^<VD���L��^G������]�B����n���$a�>.&
��d�X��E�b���{[��7�7|��=c�����dc�=������<zZR:���Z2&���~j��VuW�q��{�<�������u�-�F�\3���T9z��b/��K��W]EJ*,~���H�s�U`[8[�����:�Z��"�.��
K�_�$+����p��"m���x�F�BK��=�����������4@�
�Ws[�-�mb- ^X!�`S	�����c�5:t��-%?fy���m��#v7:��,M[���{hx�6��v�qou'*Y��)E��1�,��!�\�Ib������,��)�d&��
0p>'�6��)8T��x���[:4F�|0��������B�7���E�"�!�h��WC���.���P���-�J|��s2��y8K��3�1J����x
l]�pf�5�����.A�����������k#�v�c9K������\i�}���a������&G}Te]%�;�(��E�/J��x�(qt��8�����D~��(�@5�a~Hf��i�o23��i<��f@<���u�
�	{;z���bJP��SQ�o��
�}�[~q~��;����*K�`3��R��@�m�l��������xuW�".h��u�/()��kx�s�e������K��C�P���1�w>�%}�_���������q���eI����v�������*�:����(/
H�/8�w�?�@of'�8���x|��AY��WB� %���y�x��.��[��0�b� ���t,���3��������0�5����(�@ ���h;���@��'EU����$���
�g�rI-UQI��zv[�"*�\�����+�H�8�Ko�0������q�n�f�3�i���.��]��p�uT�8>�5*�l����}r�`xV?�AiG����_����b��-4���"))�s3��������5��,��!����X3>�%~���L}��c@����cX�s����f)�%��f����O�V5�XXvt�qU�d�Gu������n%�4�(���^Y*���xiE�[3�f�nef��^������:��\GS����U~���6��L"[���UH��%eGpTZ�x�(#����cJ��&�J_��|���C�X)|�����\Ln6L���,�
�"xux���y~t|cHXY�����������w����R�y��pFK
�S�<�R-xi�V�\�#�RVZCk���R[����I����2��8��a��J@EA�DeD��$ ��y�
�]V� ��.A�:s:���P�q(�~i���p]�z&$�vyD���M:���
e������w[��W�aeQ��&���o �?��4��q���Q2�9�~oU�@n�� M�F�<e�:��r���=MT��]��tXh)A�#��\�/-s��'#�m��	[��0�q<�(�A���L�>	J-�M��h�V��9���V����"O���p!IC

���;B�5d�,�|l;�y�c�\�L������A%X B�tdw�R�zf�������a��b��,/��u�X���t�
�Mb���i�����QM�������������y��]����xwS���0O|{����B0�@���P�!����;��z�?��,���\��]|�U�����)����*�i�p��6K�`9�f�v�W�d���q}!�&����F�����a"��3����G�m�Ch�)��Br&��w������~RI���.�QxL����{���3[O�s;f����"p��s>/m:J��DA52�
���&�l���I	0�@I-p0z-������P?��V�����Qu�3���Q"�)Qx��8�x�LHk�����R�k j.4�.b}�Tv]�h;�J�h.)-CA�������r����l��UC����'�P�1�P#�)! }����
k�Y�m�g7���\�Svn�t�C;m�"h�q&:,MV�v�<)����s��	�~�3���!������
�YLy����%�
Y�r�te�p��Hu�u��;%�,?���r��D4�>���P �,�����m.��Pb���3D�/n�R4-f���@./%2b�m,]!�
�T-0�F�����7�(��[Sp/�\�G��)S��A�ZJ3A�\�D��v�Sj[�\���ZY���py�@����kx���8t(�#����<���=M�I	a�i��'!e��/n�c��s����	������D6���J��E�������P�sM����������>�� �=P!������t���'@n�	����ZUh<-Td��
�T�F|���T8�e�69�'%��q�Mc��)Z%.d�Q��hU�`��) Uo�F�)��-���������3ma����>���({��K���tx<��
�e�+�"u�����k�'�]0�<������2q�f������T��
��a�:��\,�P����a��;�f���[AM{B�:6���'^��V[�V��X��a���_���Zq�\)����������}F1[�
�~}��<��>�@�N�J2,�����w�r�J��EsUL����DI2�@1��}2K�������P'���:Lx�i��v0��i�GD-..�a��&�M(�y������,����l��
����?��H��D�U�U�b�����+n/�'�B��H%E&��$��Nh{Xm{ej2,H��}?�Z�I`�\��A�	^@�},�S��nY�Y��_!���� ��/�UV���N���[����8�M��4�O�
2�9,NR��QJ�)p$�e��Q��Z{b�+
#D��E�!	P^X���L�����)w��EK%l!
!,i(��Xp�"2�@�2V�P��
�!P�����Y^5��_!�	�t$)$���m�&	��|�����P�,V��E�N���E����c����t���2����;�x�\<����v��4�%��E:��AU+m�]�sq,��0B��SX��'f�s��sz��w.���&���&]��O�����*�j�����f}��I��r��F]��t���=PN��F����YU�-$�8&R�#�����f�*{��pg�������)!��������>����br� ��I6a+������O��<���q4���A��SW�)d�r3)M
Y�(<u�,#�!��a���H&��@F^"��"�0��(�\i�������O��n�#nx�;�����'���)�fY�P&b����(m�J��j�����>A	U�<�=��@��qau�g�`�9�����|s��C��J=>��}p	���S���yj�*�Q����Y{���P�
��4�)o����l��9��r��M~���h�
5������@���K�]�i^���5\���D����^�dY�`1�X
�b���6z�\��j��R��H���g��������SI$j@���I��������AbXI��dL��|�}={��v�T���O�Y��?�A9�����wPN��a��}3������uI��d���$����?pS��6l��R����W/u��'��%
IGI��|�3�x`XD�*'g�,��x��zv��Uo_�R�",��h�s�F1���t��/�h>*���-h�����7��^}+���5	��M���r����*Qt�������1r����� ������?~Dla#�����+7���������R6�|�b���-(���7��9[���3�0�A/��V]��HnV�Q����*���E�_x�_���k:��b0rWH��rq�|�e���Z-
������<b.�����w���&Q�����::R+�4z��L��U���o
h`������������vv��Q�.w����M��Q���5��5�W�P8�%���C�����������	e�����#����!���p��(��W��u�����������l���J��j?7�Z5�?�,�����w���=���:�W��=5����g�X5k�;'�,�(� f�9C�D��U\�y��Oj���(yO	�	�D��)Ec3!���} �*��p�A	9�pq�t�z~3�������YEm�����o�&�
�?���z2Lgm��k�i�����5�A�	O���1��/l
6��|�����{��UTw3��|��A��MfA(P��GP!r�5�	|�8������)d�%���Z�n*��//�5>������}"�du�.��tS�SVB�N<��Pp]'}?��`q\����^���M6���G��Z]I���G������I�s���Z�7����N��sy����u���Y���Y��t~9���X��8�	�A��U�`��������I��ZJ�i��8glB�_�	���\���
�����`&~,���e�������5KK���j��vv�/M�>����'���^fw�lt�>'���F{�8YL��g2HVr�X�U6��j��D2�Z�����Y�
�$5X�0���k�;�L(Vm��Q:.O�rk��5<(���Kg�B�eyR�&�7 +X�}QCM4g$F�A
��%
���.���E�+�K`�{(�c4�0��������>$�����6����|:��7�+x����UwOY��O@��a5g�nO��J3<,����X����^G[x�5A����aJa�����P
���[����7�yQ>�m���sP?��pc�������T��R@�6�H�M&~���>i�=zzss���u}ytl���	�������o����c��0<S��.�F�I��;�l3Pe�C����U�=m8K7Tg9��=�L��@����� �;k{&i\.�r!
(�*`Ry�C�|aJ�u����fF�����5�'_4C}�3�����w�$�������d:��B�z��l�o	`i�,*c��O��Y��P�����y�:�����V��������f`Hv6���L�"����FG�P��D�l�cJJ�\�U�$h�������%�; ��*<�����+��T��D��6�ah���y�^��B���+*oEq���}�$��h�!��[��yX�y�p+�:��z�]��K�
�Ks:	8�R%��[�_��f����_������@�	�h�p:����r��^�����l�����|�s��$G�����^��}Ob��o�bO� ��H�8����x-s���
)l��N��+������p�Y?K����k
\����9��}�)��o3��R&�)�e���\� W��Bo�w�o�N}���������N�'�������M��c�~�
��:����b��9���3Cd��U�3b���;I�hJ=�,�=j����c���	n����6&::>��98/�&#0�	���O�T��
�J6��#7������v�9<�|���������������=[�`8��w��U��3��X��U��U�2�=%�U�	%G������M�l
��T�,
����d����g5#lY�
`vMH���rFXFee;��H��[��t����������z"�+��Gu��.�[yZ��u�]T$B��OYn�<��B��kJrVD�������y{��Z���}eS 3���Fw:�h)-��s��YF���*�B�BC2?~J
���A�B�C-���0�Y��y4bB3[
���gO7
��9���������A�0��z]�P��\_�'�`0!��$�,�W�X^�WiW���z�����eDO�v����IK�+�w�*W	!*��{��_A��8'�=vY`aR�����8�}���p?���u�����,�D��7�kI%��m~��u�J�������!q�1zx�JFA�I+0g��2�i�����aQ=�I���Yaw#TH���E��-=+���1� ���XW��29�x:L�����8�6s��(�e��]&�Qv&�
�� �n��0��
-�����`s�JNi��z�L�{���^����<�"�_�v�����mU"�k���spz]��;�}�jc����d7�`-�V)VSo<�%���Q�C����r�t���O����Wo[g��0gN�A�MQ���
���ySA����@26�$�E�N]�_���Fi�+�X��Wg�ap#��n"vJ1�q>y�A���h���C�@����cw�R!���`�`��C����~��\�K���~5���`nTg����K�y�L�`��%��
sC�<o-
������i�"$&sl.#L�|��R�s���g������r��D,��.R��K����K���H��%�k�������n'�q����K8���W�]�����bFdTJa���qO����C�0L/"D�&nV�u�-��2As�:�Lb�*W4e/�U�4Y��7SN���@:e���B9��y(�V���P��O)�l�f/��y�D�z�1�8�pR6&��&�����V���i���b���*�8��3D=�C��KQE+zx�RMy�8Xx2b�yiD���ee�(��O�>�]|�8Q�+�[�����|:���
\����J=
��,������}�����J	S��+����DZ<��G����a��<#U+"���P9��{L3�� ��+0����2|��p�'�X�D��L���U�����dM�A��X�&���:�s0-�_0��B�����m����@}�}6��;W�^���������f��F-6%�+�����x�l�Zy�
�w�k���H����o��l���K,�o�p\6���&(�H�	�@�|��]Xo�������f��b^el�A�G ��]Y�j i���D�
������CDYX�+�����{��0���U�#��]�;@@J!�q��UC���c��@5�	N�U�s��9�!�l��h�\U��wJ��v�Z�{$���'S�e#��}�����5s��e����E���c��v�����0�u��	Hr�s��V�'���N\���v�������M���st���.���7G���t�v��8a�����������e� �$�'1���i���_G���z4��+��!�]��O��I��Vf�I���sCL��yn������6�!��l�I2�qd������_g�~�~�ve%����6���y�=C���B&	�9�X�����H�U��r���gvh
L�'�#������v~���7����f��-�,]��}�������3-�e�����8��#�<��l�������31�Sz\���O�P&��j�]��:�����t7�|#W7��%���U�{o-�3_ +k�I+Q�P�t��7��W-��9M'3`L�����
����ZO8_0�z���5��
\�F��v�o�x��#�0�p/.{�	�	
Y��h
�%��x�y�P�yA�fx^W��k`�I�-}����Y��&�y�s�r2��`�v�q*��$���Of����6q�H�hh�YL%�����v%.�"��@I�-��"E��q�u��\�U<��� ���2���X!��`$�����?H�Hu���Kz���3j���>�����paQ���E��g�I�����Q{S������]��n�+,�!����J� ���������\��O.�l��>��'��|�5��F�.rJ�h�R
�e��EE���6�,��Xm���-���b��������T+~�,�7M������q���8����l]����o��Fk����d�E?>���s3�Z'���G�|w�:��of��\0��a��h�g��7�Dk�a}�O��TX�'����B
�Y'�fI{3��
jU�z��uo�v�V��|������y=��������p�C
C��j�^t.��>�Kw���h�)��[�[K.��#
��x��������m{<��v4*�?�JM�������MD@	G!��=���5*lA������,Y��%Z��%k��~�lC�Q����Or������VkL|�Q���"�U�:-r���ah�(��A�W������2<���I.d����������P�z������� W�$�������n���Y�v���U�|V�I���������E���H���1�����=����*�00�����0�
a��6�EUv���T��j���OI�������#l�@/�lGu�nCR��
b���m2�=l`L#����S��3��PB��C���9���G+���v^L9c�MPK5$m8�_#�8�G��R�Q�io��A��H~FXQC	��s����,���5WL�������9C�s���i��yY�Th�B�hf���F81�:������.��V�	`.���8��+�}�������Z TN�h[Hd��a���:H3�}@4�5+n����0�T�L�tXo���W�RU�]�`�0�
SsLj���\���x�q���0p�-��36�.+�Q�[]wnhU	:����PRh-��#��+$����P��
`(BG_]c7�#���v1�n=�@4�!|��R��<D��g_�.�wQp�
�<�V
;d=�"�W�`B��"��wR�
�0#z&�*Fh���c����/$��5�O'�(�676y!I���_p�	@�G�����*�A�!��=��<d�����<� ������3i�H#0�$)��4;���:����b�?��^�VZH��V7d����"�F����i��8Z�=�(�,M��"�y�%���3	��`����eh��8�9���"E8�|��`Q�{	�`�w�����`����a��l!���Y��+�qF�	(����'E%|%|c0���
��|]5�;��Ah�{��)1�a;�=���Q�.)��	Z&E�?����[\E�������[�/�$�X��5D����A�63R"\U/��V�ujD��C�-���m���������*��5�aa�,���<�o,�/7Yp+��[��H}�%^<�4K�+hy*F��TL�-�bD��	�9{��Q���-��O.��]l~�f�����!�%!�~=c�H���/p���!$h��oycHNN)��J8��*��.�n#&B�X'wv�'�,Si^��#k����;
���_1�?���R��I��9��������,J:-�b��*��8X���Fovz�,d����`���;)F��������V���0�v�y#��e��C�N"Q�T�^���k��~�2g�����#�R�jX�QT����@H/����j��������E�r�� �K�s�6�Y��r:���9oR�c�3y����uw�'�.2�N!��\T�n�m6��n�%��/%��'�����P?!�2�D�%�\|y�(�I�}�gW>&��h��J��o`�3�5�Q���}��E�K.[�<&/�$�ibc!�&~���	�;��TO�Fjo���M�f!�}#��p@����+��oH">���?�l������������7�;J��0�0���NB3�����)d2k����"2[6%��p8��4�5��4F�/�.�4���*�	�0�,��>���.��~�
���y������W�#�)����4~=�'����e�f����6+��:��f�\�F�zD�W�&!����Ig���7)\8�*�Ck��H��u��x?�e�L"-�I�������<��N#�a@�GI2���5�2=V@)�	�� �d���(�`h!���)���t�!~�t�a�a=F��J8�lz��-rE#{=��5����3��*e`�=]��	�����n�����hN�;��YQGM���N����)��c\Q��\�<G���.���0N�%��_P:��/P�HW��R�p����b�V0d��R((�@�U7_z���������//�H���(
7��S�<�,�j��O�x�1B15������\j�F���:r7JE����WM<2�����U�Q��L�*�b�h�H	��8)m�dc&�_Q�	��>>�mN�����O��2�[��{�2����J����,����_W�e����t@��O���������E������yv�P	��`�fN�8��t�'�M����,898�V!�<�����MO
�P��m!�/�@������r�P�0}���p��)(q��/Rb�|FK_���WF�#�WMA���p ����o�/BPZ/S]+��������l����H�	"�#�T;/��t����[3����](F,��zOa$������$[B�u���K���Z��
8lo#�~]�V0�������TT�.d?8T.��������,�����dS��+����

?Ltv ����4O����K�����T��6�����	�.E��QRx��,���:����u�������5�N����W^����ak�GVsnH� �b��[*�/���~�����W����������x����u�*���!�[2����M*��ZoZ�'��X��tE���ITAKWE�@M|�**�.�S�<:oj7���6@>�Dp�S�<Nv@�+�G�R
���K!���3�&S��V:��~�!�f� �C2`��&Cn����h�z��B7#�I&������`��C�$�%b'(�#km,j�"��"�+g���I����j�� ����M�� 0�`�:<���,���E��|�{���f�f�:q�m����eV	��p{�`�M�/77w�_6v���^������]���X�������Z����6�������e������t���>]����������+��|7�O:�%���9�A�s����J[�-�u�xE`�{�C����c�Ff���Wz[_�V���8�]f�<�j���E%���D�����6��.�w�#dE��(�N��]�
�����T�f�F��4#���\�?��M�
�������K:���>���6,l��b�+�x�w����nn����n�������������,
+���������-X�wWG�7�u��u|��y��E��y��]Cm1�c�5[�Kd���k�L��!����j�]��2��pP�/n."*�j��.�����H��m�uttm����;���z{uq�c�iOll��-6@
�{�LF����������&~t4#�a�G�cr��B(=��5�
�]J' !u����{'A�.>��W�e�2�kv�Aw���?�mn6_��������,\[`���I����WE��D�\�lCs%L%Jer�|��P��7�'!�l�p��{��������x��[������_�DK�"�@�����`���-��������`�F
&�u�����S]��+�[Jh���$� ��������U����*�����F������,�_������T�^6�bB�t�K���/��,fg}d��*Q�]������I���8p|��Y[h�r\)2��Ew�<W)���-�����G�irr���>�zd`����e��/��s����u�������l�)$�8Z�A\�����j�9�6f���_�J)�T���C�y�� �w�8n��(�)k�j����oI�������kh�8Ea��sL�6ZNK�@*���(G�a��S�e����l��%\�{��AW0R�19��M[}�N:W����-���7P������v�r����o��^s��VogkP}���*_��Rx'���R���p�F����8�u�����lAn��Et����G��N������������[����V��U�
�C�Lb��n�3�/���t����R�^U��4����m�6l����\[�0P����`A������~?���?�?^�����\�8��&�x�i��V]����5$��E���*\4�P_y�{������I��s�>k���[����7W�	-�[�4&���4���S�p>��-\I@-L2��8���yY�PU��C��9���1�����5lm��^�|YM*+,���x����|��G��&�KD	n@OIV�of���`DD�4�����|`����8e�W�8\��}���<&����]�ju����\�.~[�DNp�~���!��p�s�����\�6��\�mDS����hn���_�E�aG�#�C����n
��Fl��-�`�f������,�>��Z�����K,X��pV,!t�+�P�����_S|�
��7�L����_�<3=S��j���1�e����u��[��0�&L���T��6V��yNl-���{��dQ���j��L����D����m�^}Q�~���L������3����_v�WVXz���q;?O�kp��`��G��>�>�g��X��l����xE����=���e����{=r���v
��J5�<�+H�@�p4����0v(�����H@����
h5����^�`�^$��|���4���;�`ks3�5�{�`�� ^�2)U��B)'�n���]�~�T�����K��^!�_+�D�����F����uXng��������W������;#��k������x�=��9��;\���	�3����Q$Sr���}L�5��\��
%q�;8�5�?s�6�izgc����}/�|��K����7�v?�nn���mon��w{������a��(�T�	�2��;��@~��>Rg�l��x1@�)MuDo0T!�Wt�F���@v{�P �"�����T:�������ou�k�E�
,���D����#��*��j�@fy�
�]��
;;��,;�M:��z���k��3tfm���9��<�
CHIw������&mu������F� "Vr�_�T���?�<V��H�F����3���H����������b���`�Bhl�Y�Y�Jwg�$�P����k�����5�U���f��#���Gi���z
V�7��8����-I*"
�#<��q�:��;@��p(`��Re����G�������t���0�i5�������E�KE����(��*F���|������\�_/Y��>����������������XQk����#����v�����g�N�1���������v�bx��WU��+�'{���vw��w�67�z����~��}�d��Un�����\�(~^%��9%�*��2��U�R!~];��w�s����N;G����#�Ab��0v0Ij����������]��uMfc�u�����*��xt|c��yt�������V}�L�x����w�k��cY���r���y��x�7���s(��	C��l�������ay�%��\>������:lnn�vw��fo��]��	V��Nu������g�6���L�c`����m�F��D-���,�����"$\%�������ng�����\���������ow����a���2zxN�2���{��}���7{����v��l�EZ���k�^	�]�t�2�=�R����W�Y6� �2��|!�[������.U��H���LX��7�um��7�*��V���"q�6��b�f���%����OK�6SE�U	��no�����`���������Ioo�U��Y#.�+���l�%2?v�oC��X"vmBNIs_��#Q����`�v_��-�.2�%�&����o��_�\�z��]j���,�/��$�����POF3sv����Nc{W��$:��\���Tx�Mx�cTs�;�o��3""	��?��td(;f_��/P��� �u�	��_����
Kb����w������>8��"�L� {�*���!�����@��]��"$��"�D��U��x��(@�OTHP��#�*$�T1�D�"��z}�)� �c�<T�����Y�[�m���VqK�v�c���-t����*0p�V�g�U\���f�{N�4��i��{���v����W���l���������+���G})������@a'������k�%��i2o��S5�Gj��QS�r^�~Zq
~���F@K���Kv��H��"rA@O�t��]l`�*%�/�44�Lu������[F2�{�����+�
�4��`���b�r�mn&��no'�������|~����\�-�	[�-�o�;��f}��8��=t�J4�:��lSp����M�9��%�g��6��Lq��X��0��
�
<��gcW]U6�
au�����D��& Y:�k]�Y�S�w6�!	i$h�A�!�z���'8.7���������{�*��,���'�Z����8�w�������2`g�C�����Mk�z�<<����j6���n$:
Y6����f��#5�B��[S:�����s2���<96��I�4���V���9L>��/�A�_~)�Y�<�W(��C��B'���{�7L#4�W�X�yZ/2H�d����@~6��
��,���9]�B�����&I����+�����/_K~�u�I~15
��W�k���t%Of�jq�mY�~�n���y����;��!M��|<:?������MWH�p'��Z���
�I���2���2����@��T]��|�`�
��H�}����1�H`|�\���kN����Ox��q'��i�vLH=�����+f8i�K���$~����Mh�SD�hH5S#�)�)7��t���i������3P ���<��r�a�^�a�eC���������
��t��|�`C{{�YD�D�� ����by�^@`�*T���sZD���>���x �3��c!��\&����t!x���;�-@���Z#�?xsjC~EG���H�d�
l!#t��g��+L
(���h����M�h��ZP\r��1��Oi�k7���'=���eh����rtzzq|�������?�A���
�-�L���P1��J�/��
��]�Bm/d����w2�jj,	�*#�~���VB�E�,�Z��eN��1�T
�8���Z���~V1�=eqY�C:Td[��P�az[��6�	�jBy<�)�z|<4�Y�w�D$��LGWU$`{��/� �,��G���PA���w�if�	%���'�m���p�Y��8���]n���i@pn�A��r�*�}�?��� �>2�3�Z��"�/�YC}^�!�E��Ra�;�k�C�y�]��f�o��l0?�<I>���\M+cH����d\'nn����y��������9D�:�/|����3
hA)��y_��Wr*1H�K�(�
;���^�y@B���0�32���X4�����9j��K�SYu���VS�T����������+5MKdZ�jt�lD�v�����Xg��W�"�q�����m��zis/�����!��py�a��<},�G#`����,y]���F:9�9�_\]��e
]�s����F��ir]^�c[��V�����4����)T���-���h���+�����#�$|���h���I�ow$m����t��Q���\��5��5�R�Jl�s�nJT��P�������0O�J����O3�������W@�Ks���O��u�~g.�3�N;�IFR����Z������.���N�$D�@o�$����y����g�&�d�����N�;�vD����$��
(������22����!Gm+�I�'�����_ 5���6��4
C�[�6<�X��2�v
��Iu��S�������b��iY�����nw���=����Wm"��Ib����r%�]C��^�O :5�@#\�"��f�l���Ji�&���k*}2vSH��+"�B	m�=F��/�
��RB�G,_#N��#,�Xk�����1~��1����k�B,F)�@�����t%�@�Lq��0��kTy��:�p�2E�i�]����	��o^G[p%��s���O��(YQ�����@v��F3I?=�>�@�
nUd/�m*}��d��6)=��f���xj+F���^��x��b,������R�ZQ�f�y=*k���Lq��]�yd��z�@��1�����mo�$�b��`1�c��u��E��I�0������7�R�U�U��
�JFT
�e��Bg8����^@��F�-x��~�Z����V\��P/�>��P
e�\���������x�a�Xb�A@�2 �d�d�)%��������'b`K��X
��l��\�I����R���ixQ��m�{�������{�[�3�K�s�,+U+�M��Ik��e1X��b�\:������Y��H�q�	`���'���k�	�
��B��)�daJ�/���cS��	a���@�Md�D!�S�����X�,�.�MS�v-�W%_�|������;�.���"]Q/��(|���r"�`�T��D���K��#�����T���^�y _�db�>M�LQ"�/������	DW3��|<������D�����`��<^��F��x�.�lj=K�3�rG��D��
�L���.���p�)�G�+���>�z��!��
����S$IA��o>�i]I�A�A�{��L^�>z��Q<�(�����^��8rA�������.Io�q"�a���`V�����K�K���Ot���m������@�*�dD�
[�g� .x	�.Hb�K�����x�^@���#P�,����4���Z���n��������@�O	��P����U7�{��3j?`?p_���{���=}K`�%�!p��J��������C�V�R�������)g���k����a�>V�Zt�g�K�s�Q.����-~���v;��!@'�N+���W�,�A�� zd����z��q;��".x���"X\>w�B���Q�y"�HZO�/d���P���G��_{�_X�
	Q�^9�2�a�3��h
m���S^�j��Q��x�����L;������$ X�>���|t\]�=�,��v����� @hy�4}���pkL���Um�[���LS�)��^�����Wy����}�����_nn��/��������T�����R�|���8�o��-���WnuL��`b1���|1����!MpD!��$�w��"��.�=Q%}>���V��N���>��"�
W��~��Bh*w�Lt:C0D�����exg��t��������{��xpp����`g�k+��p9��
�]	��������O���A{{��3�&����=%w�<1R� ����w��c��#��+��B�<�#������L����k0����nnv_��{/�����KUmU��u�	��U`������N�a�`�Yzq�v�����;R6q\��J�%f�h�^�K��c������W�Z�;�&�Rp��������o�qs	��'H(�s��|� ;yC"�0�Q�p��&�6�_�C������G��\���������H���V���.�w�_���[7��������9a���?�{08w�Y6��]'�R12yl�d���J�k��az������i�pz/��D��������i��K�P�0>e�8��R	����"���������0�^g�v�����xZ���&�����XUL���p�o0��>b)S$�I�,�c���.�p���F�����f����77���v�����	��o�-A�la7��)]�t��w
���=����Y�	������
mQ��\�2JO�1lxC���M�*��^4�!��k
� 3��Bh�����U,�����?�%\PJ��������nss� 9��&������r.��_�E%�<��D��}�N��NP��Q���	�6��u�K�.4�r_h\B
�|w44G;!\�
8�w��\ sdO�$3�J?`�O�~D�C~�V�yX��x�����
,F����<�����J������H�C��0�kf/�u���
Q�q$��+����i�-5[V��N����D���M���)�����������
�ha���-@iWJL��k57Md���d��~��H�I���g��=���klo�))a���F3���m>[!���-
E������qH�G�tv�������6�+!s��1�f�eprU6X
��6b#�-@�R�����d��q~��)��$t.���j��l9��� �e�h���PR�(5\��5a� J���^�~����li:�qG�y��%j�H}�1c~h��l�3�L�e��;���������+ �u���������C�Xt�����G���
�io��k���F����^��4��l
��X�R	���G�
��uEse�9��Z��#�uw���y6��M��@	�(�@�\o4�1�vQVY�,�fau�����IT�	� �
�o�����E������������>���[#\�7Hs���[���-#
l����|y���[�A*j+m��r
�H�R�-�
�E|:�0���!�ws�H\��p. �b�F������9�b��,X��U�!�"O3Y�63d�9I������~lpo������`EK�.[�i�|]��V��Z��L���'�6����K}�e"j��H��@������X���J`l������#s�����q�*,��yp��8�,a�FT\�a���a$��Y�����,����mY�T���i��L1�UL�w��,�h��58�_r�c3�l���T���>�p*�z����C�J4������qt�q��.�dZr�����++�b��\�j�6Q0����L6��s"��4_����
a���B�5
�*^�|D���b�����8�������EsbV5�sp&#�2��z�Q&����N*���t�����3gb�x��.P9p#���t#�x8���8�����}���������;�l{�~�������.F�MJ+>��,����i����K��V�i�j�cg�����k��|�`%�\�H�6��Q=�����[��l�������~^|���_���7�� ����[W��Gf�#�
���6�;�o����m.��|,��D���w������)�j���rV��g�F�g��]�H�cy�|�pJ�J��|�/(�����\$!}�L��z],�{��AU3 �����0H^HJ���@+��,�
�1Q�+
�����$�C���K�U��>sT��7X�h������/����kq�EV�.�Xp0�F_�]��&��Cq�^�=�w�c���������y�Y���'����� g�o��=�>�{}�|��3��~o/��rK����R	4\�p{H����a#��EB��	{���-�#���6���N�k~�t����W�7�����_m�7!jY��G���d�5lp
�M3�@�3?����
�W��:e
.}av�c�QC����//�����~|m���^����������&���m����0�����&`\S�Tc*?=��:�>����NA��qZ�A�AY6�Nn	�.uOn	����H�o~��q��r�������on���;�~/9�	aQ�����R���!��Bg�1�q'SB��I�mvR��l�~�������JAA
b5>��3�
%
I8��m�@�I�(�P���p\f�vS1����C=���/�[>�b�p�����U_�����u�s��Z����>A�_����XG��q1�,ZK�r�4G>��u�Q�@o����
�,L({��
��4�D����<4�\^����������B�T(��Z��������+�~��.�D_�4�	��^��Y�Q&�����S���m78)�b�OJ�I�v��YP�����w]~��.���7�o\����Wa���,���3r�PyT��m��`3�)8ce����e����2�4��z������xGr:U1��:���dV����������I>���2E��N�]���gG�uq��C���}q^S[ms��4�W+��6w��J��|�����{�W}�+a�*��5);�6Z��ym
�)9D�M���4`J�����d�p1=IQ�;vr��u��u|cX�5�����2~���r�B��U�?���j�2�=���K7�����l>14�f:U�2[��1$a��b#�q���� +���.�/lg�#a�<^c^X&~-��_�����.�&������q�c��1?���}�Y�z����X���Q�F��U|�.��������ns��67��w���$��n���y��={��np��2���-H�mF�Oo�|�I����~�������r�	�T0U���Q�X{��g�4R
��������('f?>�����]+w��;�n�VV���Ozq��(�M�A)Y���p��U�R�5��&.�����!d�fv��G�gyt�:��Z���ug���?�eg{e�.}��=�8Fqe����F���g�H�*�*P'��L1���Adb�n�L����E;��I�FH���F�"4�Ns2wl3��u�� F�%�	��_���`�@'����������R��0��o?�!�\0>dz��W�Y��ws�V#t=�K���&���G��p~W�'�1�\��he�j��l2�������v����Wlbi��>	A�6�K�+�y�����*�'��K���YjJ��9�lJ���Id��b�\��s1��(
.2r��_��`v.����0�':�-�?
QF�D�%A�l�x��{}#�v�w��qo��V(�O���k����r�����xY$1�U���s(��F0�����q����G��L]���9���<>�=k�C���;������qz�{@~4���)T
����%$�n��|���mAV�����A�������U��^)����=�8��q���.���t�Q)�v����M��L�^����L�b�q��?�����@�=��F=i����;	2z������OQ����n��2�wKM���X�$�$v��;�mr<�`����v���`�}�?)������#}����r���2����`�5�A:��M���r���a b�R��9����STy�G�x:�>���x�% �p�����,���'U��=���iO�4��>�Y y|FD�C�(�c]��6,�<��������������z����A��:{J��3e�`[�LY��$�&�-C�8�.Z��X�O���l+������A1�>:�D�`S�e3|�Y/J"�k�wG�>�2���c���)1�,��.���J��U��E�V/�9#"��z~��^���w�%Wtt
����A�J��}�
�6boy����|1'��lr�3D�n*bK9��c��\�9s�[B� �"���A��zd���,�E�
;�0[kL����0�Y������'k5��0T�����D�W�,�g��!���v/2?,oQ��F4���A0��{j>�Z����h
]����T�����s��s�p_U%�{�
yZ��3(������f&K/�n1������aX��&h�dU�/�
G�=�[�u���fv��+�)���}���`8�`6�NVOi�U������/�z�F��$�=�`G>J�Q&,9r���6���W��kf���7��F��n�
�g:�o��h�:��o*r�A5���	
8���sB�E�^��-7���Vd�����������MK^���H��&�T�N��GO���Cu9E��m���������J���B���6Yo��=�����R�L}����X8�e
���[�]�$��#�����S���?���z�����=I��$@�$M�a��olk�;����j������$E7BQC2�x����*X��w����o"o�Y�S,��/%����"��x��k�=���D*����8/�������z6��%������������2�)�xk�<����Xz��d�GSV�?�Th��}}|tu�:yj�z]��,�YX{�t�k���	\%9d�r�r�h'�2��[��g��T������{�jF����Q<�D|��d��g�:x7���P$R	h 
�y0Wb�T��'1��0AX��s���7��Q����eI�Mv�Rv��!���O���Q���	O�����H�x@��	��!
��B�}��eL�f�l����w�����Ni����\���%us�jp�����T���X���Y�1��gs���	�l]����od7�?7��]�V;h�c#5J0���=pF~��gD���)/�'�L���g\��Y+K���t��Q�:�������G�kD��L���Ns7A>63�:%0:D�������x�������)iXD{I�r����4���������T_jR�0y Az�=V�X���kf�k���s	�|���f�����:Dw#'P��?8�^d>���tK��	�|�	�z��^��}++�&Q�//���$��1��{���_�f���3����B�R���s�����J�&��Z5���L����+y�@�� �{ @`����4�Y�;8P7p�f�Q��%KmJRH�9L-����@��>�\�0��~��DF���H����K��I�����������t�x���*&`Dp6��6���C���Y���=�<;�s����w�����%��i��oGQb��C�68�:~o�X�zWQ�y���t�(��X�z���D�E���������t/���;������z�k8t�d��9��C*�D�W�(Fd�tL���'�����
��/�C����B�Y3~}���$�l����(�K/*m��$�c�����;�_,����	*A0�������g�r��~�����~�3sA	.+[%����?��<�;�L�>����O��2����G���X�O�Y��Il�.eHz����>�U[�	�5�k�&
SR�>7����{��i�����e�ma�W��V��D�
��|
fbMq*��v������>c���NH��/�&��D�ha'�^���S�~����-�����bO�&���(�k���VE76�W�/���%�,��
���o6��P��?4�s�>T����Ag�8�qq�@�n���($���/$56CP+:j������[��u�����+���
l������0ek�"��B�=�:��>F�t����R=%u�i�M��EC����o���P���-��]��:�l�R����C���{�+s��<jk�\{���5�'�	��k^�r������g��
���X����a�.����6�lUYyV
f������\�/X�P���M���������\xi�[A�L�JnF�O�-!��������I���"k�vNBW�:���V,�\@���!����o����q�G/G�.�.f���	�6�&`���r�y�Ma��c�O�1l�|���=���u����%�{3'���8Yj�;dX`IXA�5x�dN����������*��B��U��N�B{c�q���}��K5
�����_3�b��������!dlr�����3�N�I^r�,������A/��6{���;���~���W�����M(CX}���U�<��@��rZ��4���|V�����x1jp�Yc���3�����@��5@[�>���%j����n�z��@���%;���V��&�@�e�S�%:��l�"����������Y��3s�\w�'�h��|op���:��C�.�oXx�w��k�j]�^\���#�U^%�aJR��iM��I6[�\=�����������^��Z����1��.'��g�����!��Z���M��Q���:�x�>>:=�~G���Ev`6��ZCn�Q~�����z�h���U��Q��k��%�����kQ�ml��i����A7��?��><����[/_n%�����leE�����O��l"�}��G�|��b�����1�9_#l�j�o&��O��-)K�XB��j�qf�W
m�H�I�\4��C���)��aLFN�wu�,�dP�G�����X�(���D�Ld����DYj����A�7�@�K'��5c�5�h���-v����k��j���G���"x������y/3�G'�g�n��v?�;fzc����V��=N/�Gp����a�%��|�5��7��f��}�s8���_����d*��lO�Y���e�r."Q[7�R����m�g�#��$a���1DC��33v�w��-G6zp-.��?����^��x�������IR�`\���1�=<`��o��x(�Ci�(�c����6�{�X0	3��� �=��	�"�3IP��F!�Y%�4RP�K�����^Y�n
�������	$
���������5��i���yx���$��5�6 ���&J��S4����ql�'����vk��B�t�_Hx�r�l���q��E�A��=,�U+����t���(�!Kf?_V)��&C*��t��f7u��������I���=�%Su��:�����T��3����Ll�If���x�Ib�("�(���
6aC��R<L�����\Ws_�2�X������`I��d<�Cc��������	hlt����%����=���3s�uSE��I	�+Wp��}s���7j�48�A��)7����~w��8sMqG�>x��^t���]6D����Y��k'����������<K�AU4@�[��cF�yC$�}���0������;�h��?����p(y<D�<rJ�t����nCV<P���<���9�c��l�|��cM���|f1,�O��B����y�k�
���9k�8l<�+��<0��Q�6�!5%I�@%C:�*3���M���81������wz}�3(%�(+n��-�,b�� j�dj�a)�#������S���:j����fl��?]a�����g���%�kW�:f�I����vY���Y#z6�f�0c���B[X�`gSr���]_%�K�Hm:�������j�'���Z����`���qK��o������F������C��<��UR���(�F��	��P�,Ri�~$/�M�5�?o~��M1j��� �b��5���)��t�33��7�>s��Zc����OfI8���	������<�Z(^�*Y�,���fm�U������i}
�������V���j�\	|��eak�Y`b��xG�^/Kuq��"��#����� T[�u�6�'��c��l�4�eh~z��^����l(1�U�����@.&�x�$H�s3�&���fi���-}�V�c���l��m�W��{j���Y�n���tcZ��)�9�S�����>*}�����p�F�p3h
�CE��F�G��)�A�����6��A�O���-��2'tS�m��`tO�<���/G�p�	M�����UM��/�h>�(KC�1������,W�5!��BmG���-in<�[�xn��E7+���f��F�������w��d�kz��&�5���(���&cEe$��ah�����xl�5�od�
P)s��3f@u���!r%�I�T~�z����F��+���9	��!El2�@������1�N�C�)Lf��GF
S����"�6<eZ�<�����[1#3�=�V�D�h������Ao:���8
W����y/A���5g��0;|!���O�#���R�T�mV��L&�������Z��J<~��3rVm���yv;Pc��}Yc�*Y����9T��[��j��&.8$P��Q�#{��k�!j$q��Rb��#�s�'���P��U���� �n�=~�>3�:4���oTtJ~���=�
/&/^N�����l��:?:ku��w7?�4��*�W;�

X���!6�Z��`��������m�EMp=���W���Q�J�C���=��K��5Q�<#��:7_x�����G��.:��ci�J�� ��;�p#�d+:;j�3����g\�����:bY�8��~)* �|:PE(l.�z�?L��rR��2P������6;��R�;�}�s�X��g����{�@F�\��b��O�[>7��b7�C^��
�n�N=\�����s�����fjzfN�8o05Z��|����:����=��f8��2�Y�$���*B'�o�V����e�?��}�#�����!�"�"{��e��o2o�������-��%��$��=_0>�Q�=?�	��[��F��L�|��}[��CW�7���<W��$�z\W��xk,1y�zT��w�7���:��qL���AT�^f��������y��}i.������)�1��D+@���J�?|����+PQ}3���G�}�������>���5������E�U����,.��^��l���ke�%�R�@+X������p!1b���S�$��(��O-�L��>��^�|�����mBl�R�QB�qo�v>X���=�����T�9��K�p1P�I4�"%��_[q�\��Q�u�%�D�{Q��0�Al5�Xsu�����Z���f�N���8��<(���ON�!�!e�H�<
�����W��,�����8q!b1L���1?u9
������0�g���A��WP<���g���j�+B��b�X~�
o\�vp�#�3�����^�)���|�Lq�^�*`��FN���$@(I�����Y<8���;�c�D�������)�P������u���o�1��G�W�Mx����+�bmG*��� ���G8`����D���W�q�nU���Qy��4�.T|�F�&�������x@��~�Zs���{����'ph����x���$���V�����6��a]`p%N�M���KJ�4�����0���tG����Z��J"��unf��oo����^�1z&��c�
m�p]
����Q��*���f�	��xj x����W��Z��4�������?��7D�[	tf��������2��yaD"in]$f~sH+CJBr�w
�[��x��d
&J��wA���x����7@�.5�k����,�C�=�.� ^�����A�\R��+��q���_%t�d-��a��� ��0~�2t'��������x�a�$��Z%1��Kq�������2�����(�b��p������Q��kNt�y����+��!|�A0Y����!���b��)a��
�.rm����c���^��c��us�992��������D,���Q�z�w��B��N��p�"Y��U$R���X��B���T�[�U�O��d��
��
�n����F��t�m���O���8[ Es~a7������h2����|7��V4����`�-�E[!�	�Q��B��e�IL(����?��`����\�)(�66����G����b��h��C��������2�@�
"`/F��]���W)!\����(�5�@�?l��DE�a��3�'���$I0������A��&����^��!�%�n�3~�	'*9fH�)N�&�G`RiG�p!Y��d����Y�X&�h8(���"� h�!
d�������93�G��&^0-���`;4��N�3n����1Q�Bv�����;0C��lH�:qO�e*�p���7i��7�`w������x��U�_{���y�}���F&��k�%d,�QJ)J�O �/�����u�KXa�!Vy4:�1�d�����u��Nr\0��`��t
-����-��S
s[Bx? ���yIj��i���heI�r�X������<�p^j4	�5��Y���r��
si@D�y�p�����*��e9{����}���`��Hc<�?��^?�q�c��}�@����{����h�5����5��0zeT$��$Z���fi��eL ��r�E�f�G��Jd.1�x-�
E���GA��Y�u�V���tup�t5�*�4-�IQ�|���X,�+jN1wLNg�����R�����(lR�;Za���jC#�td�8<�qC�'�M���G�S�f���T�e2t@�/��x�H���$�,��=�RX�[8����$9q�6kh	F��x���;�W����rCbg�����Y���s���Vh!U�������-���=�����f�F�)y�G5�������b8�`����������/LR�V�{�:>=�j��m���C�0��p��=��>_�;9=m�]^\�x��
�=��S�L�p{%
�j�&�����P��n����Z%|Z��#�h���_����a�}��Or��M��V�|��v�%�8�I'���ha��'nw�E�.0�<t}������yb����u� ��z_�2�4���+���,�F����VRWx[��(���q��*d�0{)0'��\id,&���b�u���[���`�?r�x"�4b��a�j�gF��kf���Z]/���O<�	����<��|J�(�*�A B<b�	��]p�Q�����.�nM8A�QD��V�dj��A�����+\�������n&�%��`/�$x]XvF������m�����0%�-�i����$���Z
L�	�((���els�B��g�������d�dOH�&Z5�<���D�
[�Y38�^�����$E�/g��ET��6��re}�R�i��X����VV�6-�ux���G�����^����g�Z<�������z��Q���
UU�DpU't7�Z���:J�]Q�8���m7��� u�,���E���f��Z{@���h-�K�6��PoB"��a�DM
b���B�0�
x8���gH�����F3���6O�o�@
�M�5�U;��P��;U��';�����J�V��c�`&��R*`�}0�tAJZ�;��@�������R�����o��85b�0�;I�Xr���������m�����v����x}7JF�����B����b!]S���%�]
�+i0[� ��:I&S{K:)pg���P��%�������+�f&k���h
��A�K�d�d��j�����V�����5'��"hB^��$65��x���c^)[��u3 0������,��i�������'��E7�iH��*([K�JP�K����oK���:�]�+��@��{K��������{�F5]9�>��;�,W:O��,��A�d�s!�,���/�7KghS+1����<6Lu}|?�$�	�����y=�����D	��`����\fi���~7��on<�CZR8/���0�(8�G
e�t�O/�E��JgV9�'������c���j����.P	����
h�%���O�V�b�;�W�����������wmo�F����+:>gb�"i�/����L���������y�4�M���
�������S7��n4I�N&9�xw"�
�@�P��Tr��D��T��H�hR�k��J��n�&����K9��d�v��I�4�~g
��,t+��x�s�2���w�|\��O,G�;-y	SS0CQ��e2����������6,MH�P��s]~���_�m�p�"�����~�.�n$�	f���������-�LT��x�zh����|��u9�����<^��)y�R�":��Q�&�Y���&Fz+O���Az�L���QjqswJ �;GaV:��]�v=����u�sy���u.�$<\����s��3�����$sq?���pE�
����fH@�������X}���ogr6�?J1 Z�����a���j�x;��i<��q�Wc{\#��x�M�+s6�[��w��]�/�z'&{�|h�BY�������kp~���c���������d��BL��ob��*���%�IJq1^798"g�
��5v�z�7��r>*��9n��33�t�N�5n�r���Z���V~~M��#e��,��v�Ql�q��
%���+�|_��c�']�r>_=F��U�(I6Y�7��qQ:��|������:�z���*�%�\�Ml�i�k)��~�t�)}��7*:X������� ����`F�^�/�W7�����7F����������z��H�����A�<�}���#iU0������������,�k[�!�STW;��������{�|���c9q��M�[���^��t�0�
���5�4�s9����cw���9&��p�O�����?������c�p�5���g_��~�������1����98�������"�5������J�tj�`X.WG����]�O�4�,9����u�uL~��u3�-eE��>�����$������cr�H����\���hc����}��-�yx��Mx��MrG2�$����oN�_��}�X���+����������Y�������/����W���W����pF�k.����� �o�#����wF�Z�������:�W�|���d��zL�g�p�`v|Ou�p���z�e�\�.���T�=�I"6�� ��L��-W�Su�AB�t%m��yu��y*c$3��~��(��!n���~(�o�+���/+�CJN�z�`�$��e��
0j\�(eg�"��M0�h��������`����m�^��5��cE9[�v0�Qk=����4F��v,���1Y7�,�T���t���{��&�h�j��mx�p�O>��=25�~uZ�~��]�NN�'������������~������P�+�+�������c�3?"���B��?/�y�����nXLU,r���f5+����m���i������v 9&��nT�������~z�3�Q�����9�
�(>�#0�����ph��j$n����AJ��,�b$M��qd��`3�P!�!���2����D�i:�o�G��Z�(�
ECi�7	��z�J����`@u��������jH>��.G���w ��Ay�������Pv��j���7�p�4��*Hl�vgC9��
��
p��
<���N�P^A�K�kbV2�+����,�$\�{�����x��w���)eYfyw
�2�o��UqK+<���P26��z����"(����5I���p��3}��lUK9�1�Q���j��
\��.��	9�(�� �{��&�?�
�����M�x��=N���-N�|O�g�-�	%Q��iK�$�#"
�\�e
?���dt��v�
�w����h�y�P���\+���IN2�"�D)��v��C��A�_5d�(����4����p�%n�����-cxO%a�4��DT��
W�our��� �?dx�9��]@��w�=������%�v��&
���y_����3N%lk>��K���w]/��1";���N�����g�n�d��'�nHm�8�������������=��r\m���S�v�KA���������U{&I�C9'�/r8�����������gE�
=��V��_8��1|�[WbDK��c�����-8�U4�����R��D��V�[
+����d�=���V�Yi��&�,�i>��~�&�z��ch�h1��?
��r�)���o�j�9��-�6�z�2��q?���.�E��zt5_c�VL�P�%�v�B���i�v�����^���7r�%9���5��5��K�Qv�q�/G�Tj�����7���	|=��[O�k�����]Z	�����;
����\�V+l��N����/9�Zr1���#��U��*�T|�����������#�,��8� �9���(����EE*�|sy~�h��>��_��B�+�=D�-;^��/^�wy���_��"(���3VO>F�
,$��P�(�~:8��/�-����-`��
:��b�T/m����e�++�dsy�FtX~#�=>�["7�PV�Mm�����#�ml��Q��[����ZX�b�*��.��^[���W���X���ua�����CJ��Zt�~��e$�+
���aV���&x���:,ee��~���~�\��+��������s���tj���J�Z�XbY�������b1_�JS`��h����e)��-/��0F����[M�[��w���W�j�Yk���J���/�v�_��o��6S��g���v�l<������� ��P���6��A=lv��x�
Z��_	+�Nw<�Wj�N��mT��)�bW�����RyF���g��3��v>�c��������ia�
��hR��/�_U{�}��������6�U��jm��
 ����u��������;�R}Z���	r��A��S��!r�
�����'v{Df�G�	��!�2s���$� �o����:;��� ���NK�=���']��"�4��y�������:i�&
�Q7�%��+��X��t����a������t��j��t���R�����r-6�2��ohE�T��[%F�Ot���b���w3��8��M��#��&����
�z&��@Z�Q�8T��a������}fC��ca��Rz�[2G�����_�a������]�2���!+�?�M�p���"�=#�Y:+��������������d�#�. �����NGtRn;|��Ce'���-4%U;vW@(����������J�u�O�/'��K�!��:����x�j=��J)�S�O��������."V��B�X������a�����&{��Hm��1%L�������%++mS|���?��3-�����S���UK� (���(�������m����O������?�������Vg�h��n�=�z�������������ag8.����k *���[����z�l>�tr����XI�B�������y\
���<z�1yg����l�f�7�(8�9�G��{�u4��U���$���mt��h>]�G�U��,��s��9�	�=2}��� .��Yn��Y�#Y�_h����PE�q����n�s��I}�e��_�e��:��F8
������Y����j0��G�N����4�������K;�/��z�>�V����7�g�'l�~�D�7���j\@��x?|N����@
e���Q�r�s��lh��w��j���S�m�`"w��fb"�!E��P�t8I���]��K�E�g��k�Y1�j�[��0�������?p�zX��N�M��F�f��(m���Q�����La�|�����%�V_P��JA���%#
���`�H����1R �D�E,:���f�i��h�P��0���k�TW����J��}�35�\��Ko���Pv�2D��������]�VV�3���r0`^��q��
�����l_���_����8�]\����_]�����+t^k�Z���7���FW�����HFX��$���X�Ge]�>`)�H����A�a���?%�(U�'���{�ye��QY�����0�w���r����'qu=v�{��k����w�������v��������hAu�o/���c���`kN�|�
����z�U�V����,�G�Q}����q-�.fC��� �I��W��������W�HN,<��x��O��)Os�����2���HG�
��6������f���]�!K��	gMj�+���$TN �jAu"&�J���c��0�$��T�-+�R�7��AK�)��\x����]�]�<�3�����kY\�������J�eC�u��W�E��r,�5�2���������
#8���)$�{������x=����W�O���5�o�����$�����������-�a���,Q{�P
l��A��US��z������[�#�?���Fm8���0��z��\m�%����z8w��m�?��������������H}{;�We��1[�et�-��Po=�u�C�!���V(,��UL�p��f3�����7:%����X�5��!
�Q<��*<�J6��d��7�9+�/���>&=U1�#%�{u��m7`�B��m�jFpxh����w�0�p����#����?�dUM2��-��5��c�k��vZ�Poj�C�Y��Z���*cTmU��Q�2.�;��?��q���np��*\�H�PiwI������m�w~�D���K���wG�:� ��h�g�mK:�G�$y�0��)��� �P�7hrx���}8Mlq�F���`�#�����X���Ab>{�[MZ���62�Z)��Z�95��.�F��wF��������o�J�DLh�*(���&��R��x~������<�������$��5%\��VS�h���c���R��4�x�g���<D����ZC���1�8\%�@���.b(�8i����t�+o�����jIT���>�q)�R����v�����Q�%EG��G�R�a�@ r�E'��
e�G$��
���X8�5U5��r�'PZ����f$w �p���*�6^���1��P����mIk��^��
/}�����O�a����=�\Bo���K������(	P
,s���� :�.��Xg9O�=�q�>�.�.�}�,�A����jv���Ua�H�����!���kT������HP9[ZV�gj�A��B�9��IOu� ����Hx���K����~��*���\����3�h���y�����-���jI��%��	B�� �5��5������)���d]�6�S~V�z�>�����!9�����YO
1�U����-i�(VJd��(�)U��=J�������w���k����'\q�{�t��4 e�Gt2��1�+]�8.&H�a��L��^�20#������}����=��'���1�"b�5,��T�)��Qi����g$������k���E�)B[UQ%%��P������TI�������t3����&?��g0�Wl�"7C�%�W��#�s���~�����jH@��c�f��Ip0y$3Y�>=�R�T�a��L��V�)�V^�b�z��R�������������\h����7]c8

�?���r<�/N�o4�4�|�z��D�T	��x�}�4�$��2�/
&Nd�L��r<H��)������:���<�(�k_a:��R������������M,I�u��ot?��
��9��,o�\�/��4+�F
x�n�X�!�����;Aq1�)��j����>�b{�����_v���v�A�����}����^e�#�.3�N�Q�!�.9���kU�T|���v�����/<5���xz�v��J{��4�M��������M�6����n+�A��Wp���4u���R����M�>���Rt,H��RJ�B����"�uFt����R��W$QX\\uj.;��vG����Q��A�L�K;�������c|^� ;.�Z/�'��������Qp�?�0���wz���=�.u�'2VR����`������Sb0��Ub@_�+1�/g����sK���%��5�AP��N�j��������A�����i���f�����2��C��rl��&xt��x;:�z�
n�I+�����pT�G�N�\nU�Z���V}Ggp��p�%���]�D�?���)������� '��o
�������(3�"vb��G��N���	��F����������^L%�dZ��������������@�/�7��iNvD��~I��S�v���\�W�@����C�8�^��p�_�QI���W(�r`�9����90=8&�&3�g���@M��{q�{��������F��r����:.���W�8q�8����t������j�a9%�j������$��y*I'��|�F'w&&�Xi����Q����\n��SsT�47��������jQP>��V���.	���u.����)�/5�<@,+W�<Mp*�����Y����9�3�v'�,���?�9/����W����������)������cL���Z-��K�����1K���v�G�En���V��z+���l�[����-���G��u��j�Q�:���a�m�jA�
�Z'����������_D���_�������XFq� ��5�=��z&������Q� C����	����H��^+W`�"�Hr�:�#��
H$�N>r2I*M�/>��N���%���P���z�8��[����j��'��3����Z�5�~�h�D��h���Y���e"�������x�l���x��H�]J�]*��� �h���[/�����Q�m�����E;����@P|{;��^x�6`����y\y�|?�
�Y�E������Cb{M���w����~eG�F�|U6��,�� ���
�Np�$�O��$k;t#e���Ym9���/��C����R��e�-C@�]�O�1|�<U��g�vF3,1��"����qh�Z#�e���?	����Hw�diS��K�?)4G7�Ev�C5s�{x���v��zQ��*h��w5�:�I0�c�����������^FU��Z@V/��)�����j�Z������LFJ��]d��vd����mg���e��3�-�L��n�������r��������7�nr��e������*��*?��.����pM�#O�T��
e����_�����.#���7��R)j���n$�|��e4�o���x�����|D�����(�k�� �� ?�����S�I�|%����4����g5���`x/�V�I�P����������W�`��������4����I�?��
-p��HM?�����x3�eN����G�n���
��nn*��5;7��jbgkXN����z2��/A��f!���H�~�	�	��������������������W���,/xmb���L�l�zj��E������W����P�A��+PI���6\ahn!�Hn
t���E	I;�{���xr98>{s>8�]��ZX���~/���X���/���*R����0���	O�����
n���&�P�ux��h�X-�?M���������M��^��E��}|�r��@f�Ek\e��.d�?{r��V�[h����W������=�	�#�c����#	�&���;�/]���6B8;C����J���l���_kT���/������i�8�*g���2���3pp�#�c���a�>��Q��]�k���Z�O-�1s�w�Z^�^���f'N����5s0������.�#����}	���(��
��:�i���o��o/��������z�RQ�"�|�E4���/����������������y���:z��J�}�)q?�?Nj����15_2�"]��_��p���$p�+��������R?8~�z���T��@�D�|�����1����m�k�f���pv[�V�5u����]N`�G�nF'�F'Wu���B�Mq���L1���O���Y�-��~I�C����|�X}ai����"�_~N�9�,t�3�Y�P�����$Xkt[�!��_.�[�N������rnF�)�o�r���[����b+��T�gX;,���4G���������2���'�U��.�H�v�3���i
e��B�� x�,��g|��W�!����[$~a^j�!C\�i'J�8GB������L���,�{�8�Ot�*�����u8]����>�CiWG������pq9
�����aA^T������p&�2���K&[=-d7�
Rn;����}.����Z�F�g��]E�"���.�{/Q��i��SM �}�Bz�j��Ii����u&!�s�@�2��|g��k:�C��>j\-^d)fzS8k��%��tza1g�|�Y!���t%���xE{��B���\P���t���i���Sxy�����RB
���EN�BQX�]s�3%U��c�$����	{u�@R�����BH��Wz���z6/����H��N��IJ�v�
w	�������(La�I�x����E#����1��5�����E���z��%BDe�(�dE���U#���JqD�������	��0lQK]�"���������F��	��p8���?�f��~��V)��i�����vS���i���{�I���Y���g��i����p.qr��B�SG�Dv6�1��Y��8�Kq�t�������5��$��	0Tn�r7���������W���X��u�����dQ�C���7�-�I�@��r*/c=9<���
mo�`�=b�#���<��-yW�/I5&1��b�U`�7=�Om��6u$�
�Y��k;�
��@�EO��B�A�sKmb4A?�m�J�6�C|(\t��s�d��iE���7��&�}��A��Sd��i4�+�w;
���fFk�S��:�%0����]�!�P3m ��d$�?�1������������c0pc��@1������cK5mR�?����!C0"��|���70['fL�@3���f;���������b^�.������/����3�?I+�����&�3T�i���12��b���� 0�%�DZ3g�{�R����M��J1�_�����l��u��{�c�����>x�����yN�eNOQu�����)E�-���+U�K`����rC�������v�{���;�3�L�#�/;
�}_n���UDs\��vXz�������?���9K������5utP�Q��ts�� C��*J�gN���v�8T6��BRz/Z�y>��H�T���}�f���0d��g��� '�$�n� ��K��<�����%`���x"����Ei����[�C�����MCc���Y��&<����u)M���5��S���.E5��u��M3�]��L��u���X+R-�~�|��TJu�^���5�a��[����uxLE\J0��D(b(#��������	
���>j��#���K�b���R4�W������:V�"���R�Z<���kME�V����(l�ie1���0e����1�-�#tQ($����e����l5��$��$�?�N���WA`�c?fo�����Vz����>����Ev)�'���^�������sD;O�=��SAjx�D� �o
��T9+%a�W���k����zyJ�����u1����qe�+8wWg�0_���g���_�B��l��h���G� M�3�7P������]b�Il��$[eP$�%v.�t|�a�|R���8}7���	dNN�'��aCB5��U�&=��kkC��e����C��N[(1v&�'��U�K�.%Mid~�,����c�X��������h��|F�B�&q9g�w���t�)�c�B�[�T��4�	����`��u�����"�G"�����L��(��`A�T�Ex�
k%���A��m
@�����1S� V�h���-E���aI�OU������q=����$�l���1;��4����n.�N�-k�P�����!F��YF�a��a�����������Y�0�9�v/����#}�{.�1�K��|����BD���5"|;<��O�[�5����W�G)9����-���W�V�:i�.�Y$�0�.��.����MD������=Y8������.'�~��[g$eZ�F�u���w7^��OC�������'?��Sbc
�f�����
��2�E����4�A��#�����i�[�i����`4�2��+�����F&�q������l��3�����J������poYZ�mu�V��B�d��"����-�I�y��d��i%�����8=����������������C�e��������
�;�GY��SK���u�1��b�����ul�T+��EVb�2�D��0\����6���F�V�����2CR�����Y�2??����V��:���h&l[����%Yr0E��6��P�2��ct��L�i���	���"�H�U��a������D=�fE�#��I�B|>~zr�FX�9�}[����f�]w���&;m������f�-�~��:Qnd�`�EG!�
��%)��Y�D1.�����q���p�����`�)~r-�p6�R�9�YL��*!��P?V���p\�_�c�Ja_�`+���!��7Pw�#����
���0'��"qQ�j��r�G��=	n�#!*M�.�LMDj�`I�����Vj�{%qH�=k��~��h��l�C�#�6���f���I�N�e�}>�g=[E\�PM�D�f�������|�f����VKh�CN���=��S�5��?q�*:4���E�8n�X��@�W#�
2�"I� eT*���Vf'<�{O��2�#�������P�48>��_^����*d�I�(76�.�1�7�=��
D�]��>��t�oF��$��
4�|6bi����S����M)��A�8�$������,���Xw�j=�h���6�*�v�N�]�9���+�YG�F���p�)C�����7��/"G�N|������nZa.����~�B��g��h)��TqB�4BY�����LcB[5���-�)�aN��~���{����??��!�V�-�0D�w%��`A .���e)N��� �_x�����6J���[������1��C��������6�,{	_���.��||�	C'�!�'
�"4P0����(�s'U��B*m:
�N�B��/���e�B�����(I���b��j(������ye<R�kR�O����H���������������Y��
]y��<�����K?��
�Wv��4.,��'B;�R��=����n�LA���_��t%7�Afh��V���P6�D����e������dJ��{���{������TW�^�@���<R�����;��,��k�q��c���[]i��N�������^��B$6<s�;��,��=&�(�h�UN�lc��5J��z{�[V��}���e�TV�b.Z�b���XN���g�w�����jk(^����y�B�T���[��*W�|�@i{����T�#qS��aO����fB�*�w/#���}`TQ��	37�����.�������^��ps,��8��:
�VA�������y>Yw��H�kz�����i��^�o����~�j6�����N���9���!-A�z#��$%0R�T����H��Ue��I��k)N]�p���-�C(������[��ncz����&�8*����q�3��u[3���R-1��R����d�f.A���i��X3�eC��B���<{S��}0D������,��0��O-8����� ���52� �H��d���(����Rt �(��mtu?0��&2�rI�Q0�6�
��=���6���0���ZH3M:���4����^�)'*J~^��Dw�gq��2&R%�?�7X{�q�<RD�{�����i�j�szm��q�i,������G�EwMe1��v����������p�Vn��6aZ�d�i��%�/1Ei�K�K��m�M)&�7\��6P�O�����oT�jB�;b�t\����?�V)��s���)��#�����65p�+�V�|��JPz���J�=�K6�\K�5F�7w���e�`^�LnJ[������?^��e�<�GHo�=��i����M
�.�F���c�
x����/x��j��f�]��<R��K�i���r^�*+����j������sG�?������������=y]��m���2[���7xq��h�`h���0��]����5O��8��j��D��/�Y���y5v0�Wji�A�5Q0e�J)[���+����k1Y��:���bD���;����.����0����ih���v$��%�H���������"F6��,8��70��`������n[`���A����9�U��$�}L$=��^�p9*��	�Sa�G"	���X���mT��V��1+?�F�,�����0��k}�2b--V��?u���74�pL�
1���X��Q-�[hZ�iX+uQR�?���3>�_1�*:7<b��BzAgA�N�!���z���W��������]�Ml*"A��wo'����?�s���)3�]l�Os�4���cR�)K�zR1�0q�p�i�B3��,����wb�j�P�I�� ���&}6�+��$3_!Yv��Bc*PZ�OgF�w��*>i�)���`���5x���LyR�}�}���4�kR�#�O6�5s��,����[��DU��|��D�"n��H9"��6�_��6���q����
:���r�E�<�=\~I?;�0J�;��+xP�0�g)[�RDnv�M.�/r���[�1�)z�4��r���j��T��V����2��{�U�
R/$97�E�"L"$��M�=�.s����&�to��21gN#N������"�e|�/y��� ���#�����������_��3?�������o�,�J�{P�Y�F��@��o�P��P������V��k<�7�4����T�[0�+���S� 5�,Xj(���p���L�����E���HJ���kD�<��^-&�����U`�����������7��tL���u��Q���T;�^��S�&W��?������6Xi�9��J=L�9�~��`�u�h�/O������������|����<>J����H�?�?�?��G�l�XT�s��C�Z�M��o�	�d���?y���/Pz���E�����Q�	k��1�;��Y\��v1��\��U�;�.�e�\�����+r!����r�Q*��������fyr	:�+���a�UtGC����.�h���b7aL�|o���h�^�7MD�[��bM�0��\�fnh�R��yX�(f2��\%��7\	�n�Y.w��q�vG�|�p@�`��
aE��wV�r2oL����9�t�uJ���������*��Xg��?~�t�v��L�cx����������Z<��g�����9j����U.W�z��������
%[��~Nw����U69<��q�����K�%oK
��W�����c�j��R|�i$���G�/���!����Xb'���wB����I�\]��F3���f�Q.���>���
%<��6���!��(��Nl�Uu)Q=#qPE�;h����Ru~��E�m0���U���<
,�p>�3��f&}H�rB����?2{\.~zT����>2@&}*���=�a�7�4L�3�
h�(�������/��b����`X��`���`��*�*��U%�0z�*�~���3P$}XrS�!���4�������#Yc2M-����=_���e�N��\'y�*u��1t=&��#%�w��r����@@=���R*I��/<�|�g�{g�_�E�
���������&�#�v�]�wQ�������DTM��g�������(qz\`�
�����/(�j����W`�g������c���w2o?��y�5o�(���9��GY�,�����h&P��O	L���V�#9���B��k��k5V�����-^����5|M�~2F���C^��Cp�$3:�q�7���I�>��
�8�^$/��u���s�I/L*���W���7�_�u��	?!r-�N�GA���Wf-��rt~zz|=���M����GE�1?X��x��B����i��s ��"�T��H�*�����A�,���'*
D>�R����
�7�B��B=��'�y�G���+�EMs��8:�E�d\���a�L�30�(vU�H}�Q��MC(��W�JR�Eg����"��3U������m�}3�k�@���6��e����k�k�����m�k�n+~�L��A�5���x���6�7g
����c�
������
�������[G����������������n������M�u���;�m�
����F�R��X��%��!�@���ns���5�c�ZT��hm�~����a�U.���n��hu*��Z[V���jE
�:�����3��j���������!�u���Z�
Q�%�B�%J�p�]*�����!"^-���QT����������
~a�:�#\�Q����9.�
�3Um������7��{nO!��uP]P
^���v��^����e5�,�R/+(�x���&L7���E9���fP��N�����z�7��T����d��Q��Q�j���j��&��+�4�+���C�;,�I�~>����7�V��������$��r��d����r�=���N����7�s.���\��x����]�P����|�rz	�0)���L����z����^K�_�;�
����k%��)P�x��K9:�$9��Cn�s@�z�����<���J�'`L�'cVj�U��(c�����Fg��]H����	�j�
�W���+~��Me����?l���hXI�nE6�f�<r�O��#�3�jT���9��y(v ��X�r��Y(����03�p���ET�LL����P�ZN�d�������'s�����<�n�.������{����E���m�Ti��A�;������p��Ng�h��G
�DR��j�B�����$��0��f�'p�W�]"������i4���:9�_�)�C��3]������������T\)�,��k,H�N[��c$3������~��)���#���NE�Z'%�tw�6
\I2s�<��\�����-�������W������R����J�Z�f�.���q�Z���V4N��E�tC���V�����>6�`����)�el�$����4���� ��H��b�x�O���L�D���A�~�Mt<3�*1��f��v�Xm����������g��/�������@���w��_�8���\�9-�5�b.��Q���K$P���/T��:��]����O
���&
�����������������"��;?ZI�[���-�8�9��������Q��5�C#���q�V�������O���<�?�A�����I]�n�����N�Zsp	��	�[�wo��d9U*Z��)����T��9O��P�� �DD-fiV�p�0&de$����i/Y����q<'����6�}���S3�������^OD�����'^�������x���������zc{Oc0M\��������3^�������h{��z��r�V�������$)W:���J��,�0D	���u	w�4����+M�f�"��DQ�%�T^`b��1*tx���j��J����F�����f����*��W������x�����k<0��z��oh���
��]<����
�O�R�5k�F8
+�n����aS�Fs�n�Gp�w*�����N�3�*\x��W�<���j�Ju�<�z��;������oeZ�7���aX������3����^5����"��?���Uj����j=�o�y��]��*����O���5�c��(	�������qL��� B	d�;�����
�
 �j�� wRH`�����M�'���8?v����e{�J�t~��55���>��}�B����S�&{]i/��H�!R���xk��f?���n��Y�?�JY�f�S0md���f���a�����_����.J��g���[�1�{�l��+,XX*������L���o��03�E{���=0:�H
��`v���%�5	��[C���s��Z*8��A��6�e8	A{4�4�[�.0sC�����o�(����m]E�����)lt���=�S�e
KL��t@X��{F���2��k��r<�ob�{������7�������u����A�N��;�A�
0���Tg�1�"�1�"���-��S���N�h���;����JJ��R�����K������>�����4JR�b��rPn}���,P!��:����U��xq���	$��)!���T[��������T{���M�%At\/�7�E���6W�S-������d�r9����_
��N����Q���(������!�T�):��F��B	�i|���\� ���$Z��9b�O~	����_{$�&|8��;�Dx��k/=�|���19=q^��qp
���`5G�����z8�|Z���Iu�e48�"�"1<������4�_�	��)���N���TZ�K�^�D}E������nW�)��]��������������V���5�^6���V��F�Sk�����`����M�V��_D���_��K.�}������Z��j�Y�����J�����5��O0��1Fu�C�,�H,�!����u���N���/�$E%]��`��zx���N�G8���:���L�M�a�"L���:�$�����&:_0��>��>%_���I�< �p���0�W�{rK�"}+N�,���A�+A��%�S<�JmIc����5���F>�%��dG�S���Jc�����*G���U�S��m���_��N����
J���(�������:_0X����g�Tc�'.��h-��S��0����"���6���~�U�Y����r @�JE��?��x�0�����7KT�nv�n�n;-X�
O	�.�����2��8'n_�z����I�'�
cM�k�L��&V �m�^z�J�-F�������OuV�l'����!�t�����#��1M���
Oo��$3(�WMOSe��w�p�?��S���R���T�_O���p4�������
������MQ�L�YN'��Lw�=`o��n(��{�VZ�o�R���_(_�P���,������
5�(�'�DV�]��p�������� :�l��U�`�E��C�������"���t�R����`����O��'�����r���n~�3�Th���G�z@6g2�R���ph�;.l��kkGd�
	;S�?�_���%Jrc����h�(=rR�DS$�����j�g�z�703�����8��,��u�p���#WOrc����n�`��>��n�����k�D������V*AP^&C�#����`PqRK%���Zyh{d��,z�?�p�-���
ay��'�� ����fQ�>�o�L��p��������yh%c1[|Em�u+^���k�@�JJ�$sD��\%�5K�EJ%0	11�b'���d���$]���/G��h� 7�4z�����G������m�?�j��y�����<U�4�!�������TX����!��
l��j5�Fg?9D�:�����O������/|B�����Dix�����o���T|%JE+��vM5�&�H2�]�YN1O\����r ��.�^\�a��'XD�*"-��J/����_i����
���q��/,r_z��}�Q����9u�|��F�X%-�n�����r ��������J�CM�|���E��jG|X����$���}Ia�4����N�������\��d�x��ok�I�o��_�Z�_��IAL�08��&H���G��@-��#��aG��\��5�%���o��>i��&�{cg�<@o��@p���n4:Mqo���������F��Q�o��rN�@���G�YJ�`���[�.zv�e��=���i|?w���K��Q��V�
_2[t)��� ��r-s�AjDv������������?UR~��9��T��_��S�|C����E�)�15����H~Id+*_�kz����ac���<��=����N{?�N�����(��%������������HQ�h
9��li�S���'��y�D�`#��h�by,-�9�RI����	=(�+�HH�3��l|��k������U������]��c����W�+���ED������]Z�E�4�W%�YX$��JJ��Q��P��r4zFK�H$<	��0?��#%�����{��#��B����]�)��5v�T�
hZ��z;�*�!�D�\����u���|�*���eJz�*�PT����p>���k
���gM��a59���g�9��N�'0$�`47���<�Po������1@C���{l�5���)|�s!�_k�cj�U)�[_���_�l�����i��(d<�	�����Tp!s��G�A��O���_����b�!�)��Z���h"_p^�X%��8(����� ����?p?`�g��F ����g�xd��dN�`?�n��_`i�>~|3_�:��.]3fZ����TA�z�{0!m��Z�K�F�n��=��#�
9���T����B"T�w��	���yR0Z_UN���3U����0{53f*��"����_�|LOo�\�Z^��H��%U�
N%�	3��4d���p��k9)�	a�P���������c��>*	 ��"]e�u�{��!7��^�M�l�����|����>O��S����R��vuE'�m�JB�V�Q�5���������>��_����*D�1I[-Qj	��
�2����4�7�/���p�������[)������8Y|��,���4�q��"�`�
�+���u����zu>������{3Y��}45P8\A�*Q�f.�������U�h�.\�I!J���a������Qs�Z��zX��a���N�\����8����Zk
o��Z�d��V���o-L�
��up�����8`?�j.�>�����$�<�Nr_��L���z_C����a������jBV����T[���H��%8�I�$j�g�=��_A�J
�r���P�(������xL1p���SL�����"��(��N�`�	V��J
�4����O���z/5,>���T�	���%�^�(eB��mIm���n#�����.�����7�jgw��@�����(��E�5�<���+�cw��d���T�A����[)Z�0��$��/{gWp/���"SL�N�\Z��+�B@�8�v�x�Y���n�����R7eA0��$-/.����4U&�T�C�qq���F�IS#==���Y��f���a/+���u}ur~�����Z�U.8V._�������z�0���K������-OM��N}S�b��'V�C�
�G��f��P��I�
�b#U�U��mv�5����^��R,u�{��S}��aTO�
W��]#�k������VN����owp��{������������wHA9��R�R(��8�oC���Y��[�c�m��FD��d�z� 
�OE-+M
o@������������h Hm��N�Ax�	K���G��������x��
�����X����b�3���P)�s����%�����\�_<�tq�u��	i��K91�n��*� �[!��`��.�����4�E\rZ�B�q�cAjWxT�:%H��+5�b��nC���{K��X��T�]�`�+z��j������")���N�!�6��
������#�"��$�}$	�~7o�TW%~��]��or�-U,���W���t_K�)�bk��t�bfRL�
pf��p�b��,�O��������?����~�44:�V���:�mD�F!��6�;�3K/��D'1b��
%
�4�]���&7"�"L�=������Her`���`�j����|�`���9j�Ea�N������YM���W�JVX:�\��w�����r��X�Gp3yO��&RsO�EQ,�>�9&�����|Q��l}_���%aj����}�*A]]=�f�;l�;����ZM�,��Oa!�3�z��h��e���@�C����:"c�����P����!qB����c�h��Q�7,r ��������Y)����J��A�_��=�<i-��w�&��{=�b��?��Ef"��f�@�2���{(�S��
�F:<�"��7��������S��%D�=���=��[kJ���+���N�'�8�O�a�z����$����1(r�YG�I �tJ3j�����0@�T�7�N�����z~���������8a������|$�����Vr"N�����������#��8��)��-�aJQ��"�w%�qL�8�8�� E�z4��eJV��1�MTaD��������hR�����o�~b7���� �#�^jf���e>6�N_����:Q)����q��%5���	)���e~��@J�[��	������L��� #����0�j����&%�iB	��z<�xF>������j]q��$��]F9�z��ja��*��`�$*9�������$�^LpS�GG�2����/REn3�f�Ws��%m�(W*�5Z�c�e����������IW�Z��z�v,�4�@tg*�@"���b���k���X(���7��v��dA2�'\���w���;~.z�C��l���z���lNY����z�3(��X
��8���S���-�#m �v{�)m�{�%��1*
X�3��(��h�fF�GQLa_D ~�p�4�B�}���JLhf<]��O^��u�9��qZ ��V���t�'� i�"�yAt��mK$)�)D�������Ol~�d�9�9x���������r�����}���H�r��}��E�D��wy��9�Qz�e3���)��}����������1>� k���Q�E�����SkW*�0�i�{�'�����O"!�-����8^Ou�Kht��3�1H�~�~B�
�(�F<~M>j���^�%&�1i�E��C�#V���H�<	w�	�'�PDAr{�[F��H�g6Oy__<�R�gd��cK��K�xg,�
���PMdS&�����tVl��[(����'� 1�g����N�f:������N�8����Ci���3}iJ����T�r7s��������-��N��/��$�>���K�g���,�x�����<�A�2?�v�+�a�����+����B(�Ze�����4��2����q�(D�[i�:��E�P7��Y+�tO�~�\"CV`�"�_�{jO<�����c��Bq�/%#E��0P`��^(��@g�r�(���H�J���[�'�G�Z��8����p@�W�C��:02S���`m�@3G�9;����/�wH���`�Z�0���4A���A���7�J��r�e=�-l������y)�|��K�wT�����6��|:��+�|��|r���J����>�2o����x��{�85�]����w�;Ij��G���{�^�+��mg�`��{�H�wV��g�\����f>���l'�WIYIP`�dMJZm����g<T����2�* ��K��3[��5���g`R���k�T�l��{�t��o�K|�6���q}��c��wn#�-���8P��j����v#���SX$4�0_��cb��pE��xH\�]����a�hk��h%� �a�"d_�Z�lvw^A�-,\���N~��82��&���y��Vh���F����(�+5��!l���&�K����=�PV|6���*�5�X9��u��A�*�������nC������U��rL��$�c*!Ah����R'YS���\�@,I�@�'������[U�����#T�
gG�q5��&2�wga\�/�������%~G�$�3r��$�����Vx�F����:��}e�����������u�{�����U��6�^kUMaz���%��G��2[<�T8�#9io�9�\��T���AgO��SXV&��5��=s�B�4O����(��l�w���2�|\��u��;[���~����#�������%�\�B��.��$�,�8����,�}J�C��[�}-���A.���<��d����Xjd���������3|��X-��z��k���-�C-K���z�$J��Jx���)e��<g��`��x���U^�Vs�uH�'{-���������]�,y/�Y������J��#�|��U,�V}i�'G��Q���lRom�ki����[��z���r���]�[�^�0v�Ma'K7��ay�)�avQM'?�����9�rM���������j�������z�Ff�z�*��Y�Y#��%��o�3����m��}�R��w�AbH�������7_�F��kVmn��7�c3j�~������_�j�r�5t]E���eJO���S��h�x�:I����l������U����LU��w,��RL�.��;s�uY����~�o���v�Xv��Vk�hG�7!��5�E��2;����%qf��z��G�u|�L`"~&�/�q
'�d�"���In�<�r�)"� q{���Y�OiSsD+y�>W���}���.I��"���E
Z��n���6k�W��jTL;�+
�a�:K���B6�B�1���!��!�=��],";�Drm"�&;�4Z-Kn���Tq�����<��*�����aZ/�����N;Zy���po�5R*��biR����R9�;!K��^����� ���N,�7~4S���{�*�{zb�#�rb��9�%]{)��,����/���b2����n�_����k;k�Q����E��m�����=���2TX�E�K�f����w%�����������u`�aI�:~�:�T��2g�e�R|�w�S���-�:.����+*I{4�\����M�q�����9�W1Z*M�zi�����~������a#�KJ
D"�B`�k�gx���Wm�v�G�Mm�yS��L����4�w�L�)PF�G<g2�G�%���-�j[8�MSDlz=W5����{�aX���a�E�O��Y4�,��h��@EsO�s�r�e��x�@A
�2,���Lc�BH���_SX������aq�Y�y���{~G�j��t��F���!#��3o����lveK��\���s,��:fGK5�9�GN�(��)�������Z�bQE�T�;e%��,~��,�q�#��r<-)F���:M"��J��8Q���+���vd
)�����fY��cX������|{��Ez�(I��8�kc8��>_�%�
���]����{�b�i9W�T�fN�b&@���N�O�U���=�B^�&`vMg}�(*8,	�8��,2�&�[��,wh�@rh��G���.���=�'�H(���H8�Cc�)�%z� �iV+�g�9���>���G�su�f����\>T7�_�PUf5������R�h6x�_�	��7�6{ZO��a�qF7�*(�$Ku�?:{v�_���(�g����4%��{��$Qv���3�X�����9��+t�m���B�[�C���0����,��O�,�+�b�Dx/zg�G@vuOT��a�"��F��O�GER�]�B����HT��~�qX���|9�bJ
q�z*?,�������KJ6�l���~&d�y	�\�&8�7������l>�N��`�r����f+|�;���q-���J�t7���v�J�e�Z�M�;�u�1�
�mz �O���H�����`�/�0�W%��y���R1A�I^.s���/�8��&�f�'C��~�uQ�C�(����K��������)wc%���{��.ZR���1�}���q;Lc�m��mI9�|o�Q>J������?�5�|��0������u��E��M��X����B)U������zO�d��9���l�m��fA�4�J'id�Dc.���N�r�����.��",+R�*"�0��<O�%	�k��}�P�"�:~l0�+[���*��U���|
�#��/3����$�b�&xz�U^q�x��y>	8��;����L2���|�|�#>.�p��c#�a ��)/�RP�9UI==N*��Q[�o���[�dV��63Ia%s�b�c
R	�(A�����>Zy)��
${�Skj#7�p����FBe�O4~���)k�;2W�3�J�d�04�T���3����<�U8��@��
i\R�x/��&)lL(�A<�����Xve�JS�59k�;<�c)��p�� G�H��h4|����oi�"�L�">d��.+�"b�^��OcYf�E,��23�Xi��b���#s���q��c�����Y}D�]\2�ElU8]��Y�
Z�tb�����?��kaQ�Q�dO���:g�z���
W��0�iZ�`W���.��~��]N	G_���Cwj??��Kh�O����-������6��Z���o��/�:q\L���r��-,�����������0�1��
��T24���gFq��k�os~�C�:�S�����p&�(��(��P0(��pJ���X��������k�~�;����p��:�?��D6`�Ie�$����SR�FW����~�[�|��D4��^�+��%G%ANt���(C���/zz����(u��e�=F�m�
z�.������4����Y\�����������*Y�~����J����"f�h���z��e(=��S�V&�
�e?�y�a����~|X���1�KL��3�e�X�pg�iA6(LbW�cF���?���0V���*
���<fs���8�7c"I�������
�����+`��V��0��1�:G�'����c���##P��t������LL���pA�^���%3���1c�c�F7����i��G$LDe<I=-O�$�W�$]	����>�*C(��`��O��w��5l�����x�t6�h�C��*��q6���HdV�'��y���kZO����+FG�m9�������de�T��w��:y�\�O�����*y�����w��De�b�2�~8y^������QW���b����M$��Dn=A���@b����l�\^�����K]�[���d����9#^������*��T�E�C�xl���[~~i�������y���:�F(����W~����2o$)i:���c}���)#0h��K�����Bh�b\#�U�@E�,�I�5��U������9�;�/F�)6,3O������.��t���'yjm����p����:���x�*���bZ���|���$��� ��1�Lmj�0�f*��Sr3OsRt��s�;���E~�vp}���)�`�>q�6��5����\)�t��=���@{k�e/�Z�3{'�O�36���/q��>z�y�}��[;�8b��qf{`�t��D��	2�������(�m�V7=tj`�lG��A� �$��s�9.���\a}��J����������Tr=�TUS�����G��V��:��������1*|��H9
��2w@Q�c?@r,�2UQ����%�h�FiO��1QY=���*�����G��@PME���>�-��.�XHM.U�����S�l�#� �4�!��R�0r�b:J�m*���6Ci����������	����J�*�qz����v�X\�ZMg�@-��������$b�����j>M"��S���q�hq.�DRy����
�������'q���*�f�si�3�C-��p���9��@��d��������6X��w*z���e����_F��0�����G=V�Gt�j�r��J��\+#�
���E�Q�����5�F�+��aTt&�+�a�XGW������u3u`��C�E���o~��L�w�����tDL�K���H�K'�(���*�������48���!��ge����UeO����^�����<
'�������������P2���,�+�
��26BN�{���>�=c6�V���lY��]r8���"��+D�L�XA[�I�����������}��x���<�R�1�c�96D|e�+��\�4�r��b� ��Y�+��s�1�V9��k�0P������U�u1��Z�����������E/\��l5Q1�FF_�I�8���Q�*�il�?h�lR1a���k7���*&�P�0�$",i����T��i����t��
���3���H��j��"���n\t�D�,�"�W���`����-�L���(��&Z��
�[��g���`v���>�r�&�?M�
pu���+q��U��e�B&�����P�x��Ki�$tm1�l�eT�AE�,u������7��`�so��_���@y�Hz6"�����|-�^���������d1�8a];���GzB�9�b������1K}u���S�v�����5�2���1�@`����H��s}�$TO$������_�4�""t�i^F��I�&�#����I���[�G�;��#���6H������na`e�����3Fih�c�Ez�-:����.6��c���NE[��q���^m`-�f���[�N������r9�+�q�����bl�s�m�-l���
>6�n��x%���')���2��Gp���W��8��c��3�Z�h�OX�]TU�R�HPf�U�������?
�h�s��"�#W#�z�yc�����Z^�q��&^z�^.qDey��K����|-k/k���������r�^������S�k��M4�'��x���"���p��c;Ns3��F��	��p8����a�n����p����������El:�v�Rz��W4�������2�xb�z��V�I�B�$��h?�
L�7���'��\���s>����v	��������>�qx�p6�jV�-(%�����V��lScv�������l]������:��a��+��>�����Ty����eP;^$jr���F`.2�A���Y����.J7��;��j�A�'K.�rx��y��t�2���7Wd�,������_/����i��e�^b���)�{�u��f���w���0t��[�s������R���\c�xqG@�����BO���5f�3�HgJ�U��������?]����O)�#�������%|f�HyDS�H��Jx'P�
�;�^���p�H�kI���-��K����J�����V��]\��\xo�k����4�������bmV���w��W�%`=�C������]�����K����C��B2�y�h���Q��N��6\����o���b�������J��*�Q�>n6��Zm\��qm��g!eo�l��\�R�uaY�2�J�@��B��� 'IB$k���)��K��b��'�@}s�/T�-�jvQB"<��Fp)Ot��zV���?��!�f��O���A	1K���<*N$�yn�?�/<�`��M�u�������H/j�O��3�O�g��`���rZ�"\$�d)hs1m5�/�����+�g���ea'���q��,�Ac��V�z7��p�8�n���DC�O����sD��X�S��8�/i�$��k�y����FH�s=G#l��c��%Q(��}O�����%1��X�`;���6��t��3�0N�i���&���+'Y#���������OJ��"�����!���'��"��ff8��?����;�$At�����{E��w"�y��d9�v��r��g���N-��J����p�D|�!�2����>���9g�r�:�F�V	�uX.5`':�f���?jP���h����X�������^.�_"RRr��gf�E�f��xk���Y8�3=z�����V)�6"R���7k2�,��9(��D7|'B�$Z��7Tj����1�6i<��B`D�1�)���I�%��V+�N�QW
��A��X��Y�ylV�(~"Q2S�^���|[���d��xV����e������xZ`D�PGUeg�W:�}X���Bd����$����NT�|$��s^*B��������$�����>o����o	i��tz_,���?��������,�I�����$Y��Y���
��p� ��Y%� �\ln�X:%�D��(V|�N�[�T��U[����[�Q9�H�"��
��J�L�$Y*�����r�E$&���_�IA��������k��P���Id]N_u�1Q{�
�+O:��f�%�B�~�5Zt���'!Z�`������_�'F�AH|��vh�R��1�-�QBZ��'������\V���]\�S����c�m���5�
-}q���d��`l@�vi�t	����@�$G���6����(��h'[M����!C�;i�����i���9���&��5'Q1F����������a�Yl����]�k�`��z��)����$@@X�WUr;��wgoO���N��6
7�l$co�"�;�r��M�f;��I�z�I,[�����a��i��$�f��=k1a���X���W���hS�`!7�	��>�
%�����.����r��m���fk3�l�������d�.��������R:�+�i�k�������,���N�:�S����(N� ��#/�l�����]m����������@�^�_���zX��7�H��
�#S�'��k�b���W%����jqg�DQjH�K��@rwy����=��)�M��`L


��
�!�N�v�g]�n?�
-$(<����b��� 47�a@���"��ZL3�G�+d�,7i�7N��X�0���R�M^^kz�Vcf$��xB��W=��������3%y+���F_%c�3���q�a��A���������+TmL�T��_2���j��s�	iT�$���&�1����\�BUS��>A ����P�������0�>q}%{�Vx�uQ��w
����m�('YY�X���%�U+�����n
"S��,���~��Y�T���������j'���h.5���9lA��tr��k`c���GzK~�1+^P��>��n ����8 =��=�?Q�-Y��yrp�n~O�]�_��;KT�#i!�!4z�.�7�=+�E�@��&8�1���h��&��$��P�/|�H%�Ws�!6=�3�	,v������a�z��8��#wN��`��Az�O;�#;$;��mHh`����e��2�����x{|�o	�xd|��,�^��n^~�&�SZj��,����u�YX��Z��>=��{�����?���m�U*:L���p]si�R�^������l�q;L�8�G�
���D�K�'z��.�bR#�����@�O�1"x����5���n���X��R��X�������\'!	\<v�����s�����;�@���r>�=���_��T��(��T�1�1��9L��5��P��M�a�J�;��
i�1��?�����#)@�c� (�p�0�0�����3VT��r.�sb��h���������
��4:h{���!a��b-��3E4�?Sx��
|ZX��U��p���9�J`]��P� �A��:��F<��J����Vr�i3�\�Q
�p��4����`:	���M��NJtmB���������'���Z�j_s�Za�o������Y��d����4�����Z������|~q�q��(q������V-�;�Gz���X	rNt���z Naq��C�=��z�B�F�O� b���2h��
�
F�{;�{8�^'*����-��(�L��r��V��~3����8�O!!��U�.��jU3q���PG^��2���MQp�3��s�7���x�

��&).����\��;??�x�)F,,R1����%>/�M}�:�o�f=��E����MVM2���R}���lZ��T�b����(`����E�����S�7F��g�]^m��m����ANw
���[�4`�#�����=4���UW5��H����s</N��7@��g�J��&H�U���UJf��q��l"}O�d>�����U4��	�Q��%c�/��3�P�
H���B>&�we�����i������o��2���pB�z��iN�i����J�F��^�x���C3Xl��7��������e_�e_�e_�e_�R�F���x
#259Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#244)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 10:35 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-14 14:48:07 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

I don't think we can normally pin the undo buffers properly at that
stage. Without knowing the correct contents of the table page - which we
can't know without holding some form of lock preventing modifications -
we can't know how big our undo records are going to be. And we can't
just have buffers that don't exist on disk in shared memory, and we
don't want to allocate undo that we then don't need. So I think what
we'd have to do at that stage, is to "pre-allocate" buffers for the
maximum amount of UNDO needed, but mark the associated bufferdesc as not
yet valid. These buffers would have a pincount > 0, but BM_TAG_VALID
would not be set.

So at the start of a function that will need to insert undo we'd need to
pre-reserve the maximum number of buffers we could potentially
need. That reservation stage would

a) pin the page with the current end of the undo
b) if needed pin the page of older undo that we need to update (e.g. to
update the next pointer)
c) perform clock sweep etc to acquire (find or create) enough clean to
hold the maximum amount of undo needed. These buffers would be marked
as !BM_TAG_VALID | BUF_REFCOUNT_ONE.

I assume that we'd make a) cheap by keeping it pinned for undo logs that
a backend is actively attached to. b) should only be needed once in a
transaction, so it's not too bad. c) we'd probably need to amortize
across multiple undo insertions, by keeping the unused buffers pinned
until the end of the transaction.

I assume that having the infrastructure c) might also make some code
for already in postgres easier. There's obviously some issues around
guaranteeing that the maximum number of such buffers isn't high.

I have analyzed this further, I think there is a problem if the
record/s will not fit into the current undo log and we will have to
switch the log. Because before knowing the actual record length we
are not sure whether the undo log will switch or not and which undo
log we will get. And, without knowing the logno (rnode) how we are
going to pin the buffers? Am I missing something?

Thomas do you think we can get around this problem?

Apart from this while analyzing the other code I have noticed that in
the current PG code we have few occurrences where try to read buffer
under the buffer lock held.
1.
In gistplacetopage
{
...
for (; ptr; ptr = ptr->next)
{
/* Allocate new page */
ptr->buffer = gistNewBuffer(rel);
GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
ptr->page = BufferGetPage(ptr->buffer);
ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
}
2. During page split we find new buffer while holding the lock on the
current buffer.

That doesn't mean that we can't do better but I am just referring to
the existing code where we already have such issues.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#260Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#252)
Re: POC: Cleaning up orphaned files using undo logs

On Sat, Aug 17, 2019 at 1:28 PM Andres Freund <andres@anarazel.de> wrote:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

So, you seem to be talking about something here which is different
than what I thought we were talking about. One question is whether we
need to lock all of the buffers "ahead of time," and I think the
answer to that question is probably "no." Since nobody else can be
writing to those buffers, and probably also nobody can be reading them
except maybe for some debugging tool, it should be fine if we enter
the critical section and then lock them at the point when we write the
bytes. I mean, there shouldn't be any contention, and I don't see any
other problems.

The other question is whether need to hold all of the buffer locks at
the same time, and that seems a lot more problematic to me. It's hard
to say exactly whether this unsafe, because it depends on exactly what
you think we're doing here, and I don't see that you've really spelled
that out. The normal thing to do is call PageSetLSN() on every page
before releasing the buffer lock, and that means holding all the
buffer locks until after we've called XLogInsert(). Now, you could
argue that we should skip setting the page LSN because the data ahead
of the insertion pointer is effectively invisible anyway, but I bet
that causes problems with checksums, at least, since they rely on the
page LSN being accurate to know whether to emit WAL when a buffer is
written. You could argue that we could do the XLogInsert() first and
only after that lock and dirty the pages one by one, but I think that
might break checkpoint interlocking, since it would then be possible
for the checkpoint scan to pass over a buffer that does not appear to
need writing for the current checkpoint but later gets dirtied and
stamped with an LSN that would have caused it to be written had it
been there at the time the checkpoint scan reached it. I really can't
rule out the possibility that there's some way to make something in
this area work, but I don't know what it is, and I think it's a fairly
risky area to go tinkering.

Well, in the version of code that I was reviewing here, I don't there is
such a limit (there is a limit for buffers per undo record, but no limit
on the number of records inserted together). I think Dilip added a limit
since. And we have the issue of a lot of IO happening while holding
content locks on several pages. So I don't think it's a straw man at
all.

Hmm, what do you mean by "a lot of IO happening while holding content
locks on several pages"? We might XLogInsert() but there shouldn't be
any buffer I/O going on at that point. If there is, I think that
should be redesigned. We should collect buffer pins first, without
locking. Then lock. Then write. Or maybe lock-and-write, but only
after everything's pinned. The fact of calling XLogInsert() while
holding buffer locks is not great, but I don't think it's any worse
here than in any other part of the system, because the undo buffers
aren't going to be suffering concurrent access from any other backend,
and because there shouldn't be more than a few of them.

2. The write-ahead logging protocol says that you're supposed to lock
all the buffers at once. See src/backend/access/transam/README. If
you want to go patch that file, then this patch can follow whatever
the locking rules in the patched version are. But until then, the
patch should follow *the actual rules* not some other protocol based
on a hand-wavy explanation in an email someplace. Otherwise, you've
got the same sort of undocumented disaster-waiting-to-happen that you
keep complaining about in other parts of this patch. We need fewer of
those, not more!

But that's not what I'm asking for? I don't even know where you take
from that I don't want this to be documented. I'm mainly asking for a
comment explaining why the current behaviour is what it is. Because I
don't think an *implicit* "normal WAL logging rules" is sufficient
explanation, because all the locking here happens one or two layers away
from the WAL logging site - so it's absolutely *NOT* obvious that that's
the explanation. And I don't think any of the locking sites actually has
comments explaining why the locks are acquired at that time (in fact,
IIRC until the review some even only mentioned pinning, not locking).

I didn't intend to suggest that you don't want this to be documented.
What I intended to suggest was that you seem to want to deviate from
the documented rules, and it seems to me that we shouldn't do that
unless we change the rules first, and I don't know what you think the
rules should be or why those rules are safe.

I think I basically agree with you about the rest of this: the API
needs to be non-confusing and adequately documented, and it should
avoiding acquiring buffer locks until we have all the relevant pins.

I think what primarily makes me concerned is that it's not clear to me
what guarantees that discard is the only reason for the block to
potentially be missing. I contrast to most other similar cases where WAL
replay simply re-creates the objects when trying to replay an action
affecting such an object, here we simply skip over the WAL logged
operation. So if e.g. the entire underlying UNDO file got lost, we
neither re-create it with valid content, nor error out. Which means we
got to be absolutely sure that all undo files are created in a
persistent manner, at their full size. And that there's no way that data
could get lost, without forcing us to perform REDO up to at least the
relevant point again.

I think the crucial question for me here is the extent to which we're
cross-checking against the discard pointer. If we're like, "oh, this
undo data isn't on disk any more, it must've already been discarded,
let's ignore the write," that doesn't sound particularly great,
because files sometimes go missing. But, if we're like, "oh, we
dirtied this undo buffer but now that undo has been discarded so we
don't need to write the data back to the backing file," that seems
fine. The discard pointer is a fully durable, WAL-logged thing; if
it's somehow wrong, we have got huge problems anyway.

While it appears that we always WAL log the undo extension, I am not
convinced the recovery interlock is strong enough. For one
UndoLogDiscard() unlinks segments before WAL logging their removal -
which means if we crash after unlink() and before the
XLogInsert(XLOG_UNDOLOG_DISCARD) we'd theoretically be in trouble (in
practice we might be fine, because there ought to be nobody still
referencing that UNDO - but I don't think that's actually guaranteed as
is).

Hmm, that sounds a little worrying. I think there are two options
here: unlike what we do with buffers, where we can use buffer locking
etc. to make the insertion of the WAL record effectively simultaneous
with the changes to the data pages, the removal of old undo files has
to happen either before or after XLogInsert(). I think "after" would
be better. If we do it before, then upon starting up, we have to
accept that there might be undo which is not officially discarded
which nevertheless no longer exists on disk; but that might also cause
us to ignore real corruption. If we do it after, then we can just
treat it as a non-critical cleanup that can be performed lazily and at
leisure: at any time, without warning, the system may choose to remove
any or all undo backing files all of whose address space is discarded.
If we fail to remove files, we can just emit a WARNING and maybe retry
later at some convenient point in time, or perhaps even just accept
that we'll leak the file in that case.

Nor do I see where we're updating minRecoveryLocation when
replaying a XLOG_UNDOLOG_DISCARD, which means that a restart during
recovery could be stopped before the discard has been replayed, leaving
us with wrong UNDO, but allowing write acess. Seems we'd at least need a
few more XLogFlush() calls.

That sounds like a problem, but it seems like it might be better to
make sure that minRecoveryLocation gets bumped, rather than adding
XLogFlush() calls.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#261Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#254)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 19, 2019 at 2:04 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Currently, In UnpackedUndoRecord we store all members directly which
are set by the caller. We store pointers to some header which are
allocated internally by the undo layer and the caller need not worry
about setting them. So now you are suggesting to put other headers
also as structures in UnpackedUndoRecord. I as such don't have much
problem in doing that but I think initially Robert designed
UnpackedUndoRecord structure this way so it will be good if Robert
provides his opinion on this.

I don't believe that's what is being suggested. It seems to me that
the thing Andres is complaining about here has roots in the original
sketch that I did for this code. The oldest version I can find is
here:

https://github.com/EnterpriseDB/zheap/commit/7d194824a18f0c5e85c92451beab4bc6f044254c

In this version, and I think still in the current version, there is a
two-stage marshaling strategy. First, the individual fields from the
UnpackedUndoRecord get copied into global variables (yes, that was my
fault, too, at least in part!) which are structures. Then, the
structures get copied into the target buffer. The idea of that design
was to keep the code simple, but it didn't really work out, because
things got a lot more complicated between the time I wrote those 3244
lines of code and the >3000 lines of code that live in this patch
today. One thing that change was that we moved more and more in the
direction of considering individual fields as separate objects to be
separately included or excluded, whereas when I wrote that code I
thought we were going to have groups of related fields that stood or
fell together. That idea turned out to be wrong. (There is the
even-larger question here of whether we ought to take Heikki's
suggestion and make this whole thing a lot more generic, but let's
start by discussing how the design that we have today could be
better-implemented.)

If I understand Andres correctly, he's arguing that we ought to get
rid of the two-stage marshaling strategy. During decoding, he wants
data to go directly from the buffer that contains it to the
UnpackedUndoRecord without ever being stored in the UnpackUndoContext.
During insertion, he wants data to go directly from the
UnpackedUndoRecord to the buffer that contains it. Or, at least, if
there has to be an intermediate format, he wants it to be just a chunk
of raw bytes, rather than a bunch of individual fields like we have in
UndoPackContext currently. I think that's a reasonable goal. I'm not
as concerned about it as he is from a performance point of view, but I
think it would make the code look nicer, and that would be good. If
we save CPU cycles along the way, that is also good.

In broad outline, what this means is:

1. Any field in the UndoPackContext that starts with urec_ goes away.
2. Instead of something like InsertUndoBytes((char *)
&(ucontext->urec_fxid), ...) we'd write InsertUndobytes((char *)
&uur->uur_fxid, ...).
3. Similarly instead of ReadUndoBytes((char *) &ucontext->urec_fxid,
...) we'd write ReadUndoBytes((char *) &uur->uur_fxid, ...).
4. It seems slightly trickier to handle the cases where we've got a
structure instead of individual fields, like urec_hd. But those could
be broken down into field-by-field reads and writes, e.g. in this case
one call for urec_type and a second for urec_info.
5. For uur_group and uur_logswitch, the code would need to allocate
those subsidiary structures before copying into them.

To me, that seems like it ought to be a pretty straightforward change
that just makes things simpler. We'd probably need to pass the
UnpackedUndoRecord to BeginUnpackUndo instead of FinishUnpackUndo, and
keep a pointer to it in the UnpackUndoContext, but that seems fine.
FinishUnpackUndo would end up just about empty, maybe entirely empty.

Is that a reasonable idea?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#262Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#255)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 19, 2019 at 8:22 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

One point to remember in this regard is that we do need to modify the
LSN in undo pages after writing WAL, so all the undo pages need to be
locked by that time or we again need to take the lock on them.

Uh, but a big part of the point of setting the LSN on the pages is to
keep them from being written out before the corresponding WAL is
flushed to disk. If you released and reacquired the lock, the page
could be written out during the window in the middle.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#263Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#256)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Aug 19, 2019 at 5:16 PM Andres Freund <andres@anarazel.de> wrote:

Well, my main point, which so far has largely been ignored, was that we
may not acquire page locks when we still need to search for victim
buffers later. If we don't need to lock the pages up-front, but only do
so once we're actually copying the records into the undo pages, then we
don't a separate phase to acquire the locks. We can still hold all of
the page locks at the same time, as long as we just acquire them at the
later stage.

+1 for that approach. I am in complete agreement.

My secondary point was that *none* of this actually is
documented, even if it's entirely unobvious to the reader that the
relevant code can only run during WAL insertion, due to being pretty far
removed from that.

+1 also for properly documenting stuff.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#264Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#257)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 20, 2019 at 2:42 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Well, my main point, which so far has largely been ignored, was that we
may not acquire page locks when we still need to search for victim
buffers later. If we don't need to lock the pages up-front, but only do
so once we're actually copying the records into the undo pages, then we
don't a separate phase to acquire the locks. We can still hold all of
the page locks at the same time, as long as we just acquire them at the
later stage.

Okay, IIUC, this means that we should have a separate phase where we
call LockUndoBuffers (or something like that) before
InsertPreparedUndo and after PrepareUndoInsert. The LockUndoBuffers
will lock all the buffers pinned during PrepareUndoInsert. We can
probably call LockUndoBuffers before entering the critical section to
avoid any kind of failure in critical section. If so, that sounds
reasonable to me.

I'm kind of scratching my head here, because this is clearly different
than what Andres said in the quoted text to which you were replying.
He clearly implied that we should acquire the buffer locks within the
critical section during InsertPreparedUndo, and you responded by
proposing to do it outside the critical section in a separate step.
Regardless of which way is actually better, when somebody says "hey,
let's do A!" and you respond by saying "sounds good, I'll go implement
B!" that's not really helping us to get toward a solution.

FWIW, although I also thought of doing what you are describing here, I
think Andres's proposal is probably preferable, because it's simpler.
There's not really any reason why we can't take the buffer locks from
within the critical section, and that way callers don't have to deal
with the extra step.

My secondary point was that *none* of this actually is
documented, even if it's entirely unobvious to the reader that the
relevant code can only run during WAL insertion, due to being pretty far
removed from that.

I think this can be clearly mentioned in README or someplace else.

It also needs to be adequately commented in the files and functions involved.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#265Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#260)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-20 09:08:29 -0400, Robert Haas wrote:

On Sat, Aug 17, 2019 at 1:28 PM Andres Freund <andres@anarazel.de> wrote:

The primary one in the context here is that if we do *not* have to lock
the buffers all ahead of time, we can simplify the interface. We
certainly can't lock the buffers over IO (due to buffer reclaim) as
we're doing right now, so we'd need another phase, called by the "user"
during undo insertion. But if we do not need to lock the buffers before
the insertion over all starts, the inserting location doesn't have to
care.

Secondarily, all the reasoning for needing to lock all buffers ahead of
time was imo fairly unconvincing. Following the "recipe" for WAL
insertions is a good idea when writing a new run-of-the-mill WAL
inserting location - but when writing a new fundamental facility, that
already needs to modify how WAL works, then I find that much less
convincing.

So, you seem to be talking about something here which is different
than what I thought we were talking about. One question is whether we
need to lock all of the buffers "ahead of time," and I think the
answer to that question is probably "no." Since nobody else can be
writing to those buffers, and probably also nobody can be reading them
except maybe for some debugging tool, it should be fine if we enter
the critical section and then lock them at the point when we write the
bytes. I mean, there shouldn't be any contention, and I don't see any
other problems.

Right. As long as we are as restrictive about the number of buffers per
undo record, and number of records per WAL insertions, I don't see any
need to go further than that.

Well, in the version of code that I was reviewing here, I don't there is
such a limit (there is a limit for buffers per undo record, but no limit
on the number of records inserted together). I think Dilip added a limit
since. And we have the issue of a lot of IO happening while holding
content locks on several pages. So I don't think it's a straw man at
all.

Hmm, what do you mean by "a lot of IO happening while holding content
locks on several pages"? We might XLogInsert() but there shouldn't be
any buffer I/O going on at that point.

That's my primary complain with how the code is structured right
now. Right now we potentially perform IO while holding exclusive content
locks, often multiple ones even. When acquiring target pages for undo,
currently always already hold the table page exclusive locked, and if
there's more than one buffer for undo, we'll also hold the previous
buffers locked. And acquiring a buffer will often have to write out a
dirty buffer to the OS, and a lot of times that will then also require
the kernel to flush data out. That's imo an absolute no-go for the
general case.

If there is, I think that should be redesigned. We should collect
buffer pins first, without locking. Then lock. Then write. Or maybe
lock-and-write, but only after everything's pinned.

Right. It's easy enough to do that for the locks on undo pages
themselves. The harder part is the content lock on the "table page" - we
don't accurately know how many undo buffers we will need, without
holding the table lock (or preventing modifications in some other
manner).

I tried to outline the problem and potential solutions in more detail
in:
/messages/by-id/20190814065745.2faw3hirvfhbrdwe@alap3.anarazel.de

The fact of calling XLogInsert() while holding buffer locks is not
great, but I don't think it's any worse here than in any other part of
the system, because the undo buffers aren't going to be suffering
concurrent access from any other backend, and because there shouldn't
be more than a few of them.

Yea. That's obviously undesirable, but also fundamentally required at
least in the general case. And it's not at all specific to undo.

[ WAL logging protocol ]

I didn't intend to suggest that you don't want this to be documented.
What I intended to suggest was that you seem to want to deviate from
the documented rules, and it seems to me that we shouldn't do that
unless we change the rules first, and I don't know what you think the
rules should be or why those rules are safe.

IDK. We have at least five different places that at the very least bend
the rules - but with a comment explaining why it's safe in the specific
case. Personally I don't really think the generic guideline needs to
list every potential edge-case.

I think what primarily makes me concerned is that it's not clear to me
what guarantees that discard is the only reason for the block to
potentially be missing. I contrast to most other similar cases where WAL
replay simply re-creates the objects when trying to replay an action
affecting such an object, here we simply skip over the WAL logged
operation. So if e.g. the entire underlying UNDO file got lost, we
neither re-create it with valid content, nor error out. Which means we
got to be absolutely sure that all undo files are created in a
persistent manner, at their full size. And that there's no way that data
could get lost, without forcing us to perform REDO up to at least the
relevant point again.

I think the crucial question for me here is the extent to which we're
cross-checking against the discard pointer. If we're like, "oh, this
undo data isn't on disk any more, it must've already been discarded,
let's ignore the write," that doesn't sound particularly great,
because files sometimes go missing.

Right.

But, if we're like, "oh, we dirtied this undo buffer but now that undo
has been discarded so we don't need to write the data back to the
backing file," that seems fine. The discard pointer is a fully
durable, WAL-logged thing; if it's somehow wrong, we have got huge
problems anyway.

There is some cross-checking against the discard pointer while reading,
but it's not obvious for me that there is in all places. In particularly
for insertions. UndoGetBufferSlot() itself doesn't have a crosscheck
afaict, and I don't see anything in InsertPreparedUndo() either. It's
possible that somehow it's indirectly guaranteed, but if so it'd be far
from obvious enough.

While it appears that we always WAL log the undo extension, I am not
convinced the recovery interlock is strong enough. For one
UndoLogDiscard() unlinks segments before WAL logging their removal -
which means if we crash after unlink() and before the
XLogInsert(XLOG_UNDOLOG_DISCARD) we'd theoretically be in trouble (in
practice we might be fine, because there ought to be nobody still
referencing that UNDO - but I don't think that's actually guaranteed as
is).

Hmm, that sounds a little worrying. I think there are two options
here: unlike what we do with buffers, where we can use buffer locking
etc. to make the insertion of the WAL record effectively simultaneous
with the changes to the data pages, the removal of old undo files has
to happen either before or after XLogInsert(). I think "after" would
be better.

Right.

Nor do I see where we're updating minRecoveryLocation when
replaying a XLOG_UNDOLOG_DISCARD, which means that a restart during
recovery could be stopped before the discard has been replayed, leaving
us with wrong UNDO, but allowing write acess. Seems we'd at least need a
few more XLogFlush() calls.

That sounds like a problem, but it seems like it might be better to
make sure that minRecoveryLocation gets bumped, rather than adding
XLogFlush() calls.

XLogFlush() so far is the way to update minRecoveryLocation:

/*
* During REDO, we are reading not writing WAL. Therefore, instead of
* trying to flush the WAL, we should update minRecoveryPoint instead. We
* test XLogInsertAllowed(), not InRecovery, because we need checkpointer
* to act this way too, and because when it tries to write the
* end-of-recovery checkpoint, it should indeed flush.
*/
if (!XLogInsertAllowed())
{
UpdateMinRecoveryPoint(record, false);
return;
}

I don't think there's currently any other interface available to redo
functions to update minRecoveryLocation. And we already use XLogFlush()
for that purpose in numerous redo routines.

Greetings,

Andres Freund

#266Andres Freund
andres@anarazel.de
In reply to: Thomas Munro (#258)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-20 21:02:18 +1200, Thomas Munro wrote:

Aside from code changes based on review (and I have more to come of
those), the attached experimental patchset (also at
https://github.com/EnterpriseDB/zheap/tree/undo) has a new protocol
that, I hope, allows for better concurrency, reliability and
readability, and removes a bunch of TODO notes about questionable
interlocking. However, I'm not quite done figuring out if the bufmgr
interaction is right and will be manageable on the undoaccess side, so
I'm hoping to get some feedback, not asking for anyone to rebase on
top of it yet.

Previously, there were two LWLocks used to make sure that all
discarding was prevented while anyone was reading or writing data in
any part of an undo log, and (probably more problematically) vice
versa. Here's a new approach that removes that blocking:

1. Anyone is allowed to try to read or write data at any UndoRecPtr
that has been allocated, through the buffer pool (though you'd usually
want to check it with UndoRecPtrIsDiscarded() first, and only rely on
the system I'm describing to deal with races).

2. ReadBuffer() might return InvalidBuffer. This can happen for a
cache miss, if the smgrread implementation wants to indicate that the
buffer has been discarded/truncated and that is expected (md.c won't
ever do that, but undofile.c can).

Hm. This gives me a bit of a stomach ache. It somehow feels like a weird
form of signalling. Can't quite put my finger on why it makes me feel
queasy.

3. UndoLogDiscard() uses DiscardBuffer() to invalidate any currently
unpinned buffers, and marks as BM_DISCARDED any that happen to be
pinned right now, so they can't be immediately invalidated. Such
buffers are never written back and are eligible for reuse on the next
clock sweep, even if they're written into by a backend that managed to
do that when we were trying to discard.

Hm. When is it legitimate for a backend to write into such a buffer? I
guess that's about updating the previous transaction's next pointer? Or
progress info?

5. Separating begin from discard allows the WAL logging for
UndoLogDiscard() to do filesystem actions before logging, and other
effects after logging, which have several nice properties if you work
through the various crash scenarios.

Hm. ISTM we always need to log before doing some filesystem operation
(see also my recent complaint Robert and I are discussing at the bottom
of [1]/messages/by-id/CA+TgmoZc5JVYORsGYs8YnkSxUC=cLQF1Z+fcpH2TTKvqkS7MFg@mail.gmail.com). It's just that we can have a separate stage afterwards?

[1]: /messages/by-id/CA+TgmoZc5JVYORsGYs8YnkSxUC=cLQF1Z+fcpH2TTKvqkS7MFg@mail.gmail.com

So now I'd like to get feedback on the sanity of this scheme. I'm not
saying it doesn't have bugs right now -- I've been trying to figure
out good ways to test it and I'm not quite there yet -- but the
concept. One observation I have is that there were already code paths
in undoaccess.c that can tolerate InvalidBuffer in recovery, due to
the potentially different discard timing for DO vs REDO. I think
that's a point in favour of this scheme, but I can see that it's
inconvenient to have to deal with InvalidBuffer whenever you read.

FWIW, I'm far from convinced that those are currently quite right. See
discussion pointed to above.

I pulled in the latest code from undoprocessing as of today, and I
might be a bit confused about "Defect and enhancement in multi-log
support" some of which I have squashed into the make undolog patch.
BTW undoprocessing builds with initialized variable warnings in xact.c
on clang today.

I've complained about that existance of that commit multiple times
now. So far without any comments.

Greetings,

Andres Freund

#267Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#259)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-20 17:11:38 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 10:35 PM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-14 14:48:07 +0530, Dilip Kumar wrote:

On Wed, Aug 14, 2019 at 12:27 PM Andres Freund <andres@anarazel.de> wrote:

I don't think we can normally pin the undo buffers properly at that
stage. Without knowing the correct contents of the table page - which we
can't know without holding some form of lock preventing modifications -
we can't know how big our undo records are going to be. And we can't
just have buffers that don't exist on disk in shared memory, and we
don't want to allocate undo that we then don't need. So I think what
we'd have to do at that stage, is to "pre-allocate" buffers for the
maximum amount of UNDO needed, but mark the associated bufferdesc as not
yet valid. These buffers would have a pincount > 0, but BM_TAG_VALID
would not be set.

So at the start of a function that will need to insert undo we'd need to
pre-reserve the maximum number of buffers we could potentially
need. That reservation stage would

a) pin the page with the current end of the undo
b) if needed pin the page of older undo that we need to update (e.g. to
update the next pointer)
c) perform clock sweep etc to acquire (find or create) enough clean to
hold the maximum amount of undo needed. These buffers would be marked
as !BM_TAG_VALID | BUF_REFCOUNT_ONE.

I assume that we'd make a) cheap by keeping it pinned for undo logs that
a backend is actively attached to. b) should only be needed once in a
transaction, so it's not too bad. c) we'd probably need to amortize
across multiple undo insertions, by keeping the unused buffers pinned
until the end of the transaction.

I assume that having the infrastructure c) might also make some code
for already in postgres easier. There's obviously some issues around
guaranteeing that the maximum number of such buffers isn't high.

I have analyzed this further, I think there is a problem if the
record/s will not fit into the current undo log and we will have to
switch the log. Because before knowing the actual record length we
are not sure whether the undo log will switch or not and which undo
log we will get. And, without knowing the logno (rnode) how we are
going to pin the buffers? Am I missing something?

That's precisely why I was suggesting (at the start of the quoted block
above) to not associate the buffers with pages at that point. Instead
just have clean, pinned, *unassociated* buffers. Which can be
re-associated without any IO.

Apart from this while analyzing the other code I have noticed that in
the current PG code we have few occurrences where try to read buffer
under the buffer lock held.

1.
In gistplacetopage
{
...
for (; ptr; ptr = ptr->next)
{
/* Allocate new page */
ptr->buffer = gistNewBuffer(rel);
GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
ptr->page = BufferGetPage(ptr->buffer);
ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
}
2. During page split we find new buffer while holding the lock on the
current buffer.

That doesn't mean that we can't do better but I am just referring to
the existing code where we already have such issues.

Those are pretty clearly edge-cases, whereas the undo case at hand is a
very common path. Note again that heapam.c goes to considerably trouble
to never do this for common cases.

Greetings,

Andres Freund

#268Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#266)
Re: POC: Cleaning up orphaned files using undo logs

On 2019-08-20 09:44:23 -0700, Andres Freund wrote:

On 2019-08-20 21:02:18 +1200, Thomas Munro wrote:

Aside from code changes based on review (and I have more to come of
those), the attached experimental patchset (also at
https://github.com/EnterpriseDB/zheap/tree/undo) has a new protocol
that, I hope, allows for better concurrency, reliability and
readability, and removes a bunch of TODO notes about questionable
interlocking. However, I'm not quite done figuring out if the bufmgr
interaction is right and will be manageable on the undoaccess side, so
I'm hoping to get some feedback, not asking for anyone to rebase on
top of it yet.

Previously, there were two LWLocks used to make sure that all
discarding was prevented while anyone was reading or writing data in
any part of an undo log, and (probably more problematically) vice
versa. Here's a new approach that removes that blocking:

Oh, more point I forgot to add: Cool!

#269Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#258)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 20, 2019 at 5:02 AM Thomas Munro <thomas.munro@gmail.com> wrote:

3. UndoLogDiscard() uses DiscardBuffer() to invalidate any currently
unpinned buffers, and marks as BM_DISCARDED any that happen to be
pinned right now, so they can't be immediately invalidated. Such
buffers are never written back and are eligible for reuse on the next
clock sweep, even if they're written into by a backend that managed to
do that when we were trying to discard.

This is definitely more concurrent, but it might be *too much*
concurrency. Suppose that backend #1 is inserting a row and updating
the transaction header for the previous transaction; meanwhile,
backend #2 is discarding the previous transaction. It could happen
that backend #1 locks the transaction header for the previous
transaction and is all set to log the insertion ... but then gets
context-switched out. Now backend #2 swoops in and logs the discard.
Backend #1 now wakes up and finishes logging a change to a page that,
according to the logic of the WAL stream, no longer exists.

It's probably possible to make this work by ignoring WAL references to
discarded pages during replay, but that seems a bit dangerous. At
least, it loses some sanity checking that you might like to have.

It seems to me that you can avoid this if you require that a backend
that wants to set BM_DISCARDED to acquire at least a shared content
lock before doing so. If you do that, then once a backend acquires
content lock(s) on the page(s) containing the transaction header for
the purposes of updating it, it can notice that the BM_DISCARDED flag
is set and choose not to update those pages after all. I think that
would be a smart design choice.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#270Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#239)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 13, 2019 at 8:11 AM Robert Haas <robertmhaas@gmail.com> wrote:

We can probably check the fxid queue and error queue to get that
value. However, I am not sure if that is sufficient because incase we
perform the request in the foreground, it won't be present in queues.

Oh, I forgot about that requirement. I think I can fix it so it does
that fairly easily, but it will require a little bit of redesign which
I won't have time to do this week.

Here's a version with a quick (possibly buggy) prototype of the
oldest-FXID support. It also includes a bunch of comment changes,
pgindent, and a few other tweaks.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

v2-0001-New-undo-request-manager-now-with-UndoRequestMana.patchapplication/octet-stream; name=v2-0001-New-undo-request-manager-now-with-UndoRequestMana.patchDownload
From a189eaa673bab9e4bccef7e1bb7077ef5a3fdf0d Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 8 Aug 2019 16:05:53 -0400
Subject: [PATCH v2] New undo request manager, now with
 UndoRequestManagerOldestFXID.

---
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undorequest.c         | 1114 +++++++++++++++++
 src/backend/lib/rbtree.c                      |   46 +-
 src/include/access/transam.h                  |    1 +
 src/include/access/undorequest.h              |   80 ++
 src/include/lib/rbtree.h                      |   42 +-
 src/test/modules/Makefile                     |    1 +
 .../test_undo_request_manager/Makefile        |   21 +
 .../expected/test_undo_request_manager.out    |   28 +
 .../sql/test_undo_request_manager.sql         |   16 +
 .../test_undo_request_manager--1.0.sql        |    9 +
 .../test_undo_request_manager.c               |  139 ++
 .../test_undo_request_manager.control         |    4 +
 src/tools/pgindent/typedefs.list              |    4 +
 14 files changed, 1479 insertions(+), 28 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/include/access/undorequest.h
 create mode 100644 src/test/modules/test_undo_request_manager/Makefile
 create mode 100644 src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
 create mode 100644 src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.c
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.control

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c6963cf..d036717671 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undolog.o undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000000..d011aaac65
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1114 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *		Undo request manager.
+ *
+ * From the moment a transaction begins until the moment that it commits,
+ * there is a possibility that it might abort, either due to an exception
+ * or because the entire system is restarted (e.g. because of a power
+ * cut). If this happens, all undo generated by that transaction prior
+ * to the abort must be applied.  To ensure this, the calling code must
+ * ensure that an "undo request" is registered for every transaction
+ * that generates undo.
+ *
+ * The undo request should be registered before the transaction writes any
+ * undo records (except for temporary undo records, which the creating backend
+ * will need to process locally). If the transaction goes on to commit, the
+ * undo request can be deleted; if it goes on to abort, it needs to be updated
+ * with the final size of the undo generated by that transaction so that
+ * we can prioritize it appropriately. One of the key tasks of this module
+ * is to decide on the order in which undo requests should been processed;
+ * see GetNextUndoRequest for details.
+ *
+ * We have only a fixed amount of shared memory to store undo requests;
+ * because an undo request has to be created before any undo that might
+ * need to be processed is written, we should never end up in a situation
+ * where there are more existing undo requests that can fit. In extreme
+ * cases, this might cause us to have to refuse to create new requests,
+ * but that should very rare.  If we're starting to run low on space,
+ * FinalizeUndoRequest() will signal callers that undo should be
+ * performed in the foreground; actually hitting the hard limit requires
+ * foreground undo to be interrupted by a crash.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "lib/rbtree.h"
+#include "storage/shmem.h"
+#include "utils/timestamp.h"
+
+/*
+ * An UndoRequest represents the possible need to perform undo actions for
+ * a transaction if it aborts; thus, it should be allocated before writing
+ * undo that might require the system to perform cleanup actions (except
+ * temporary undo, for which the backend is always responsible) and
+ * deallocated when it is clear that no such actions will need to be
+ * performed or when they have all been performed successfully.
+ *
+ * At any given time, an UndoRequest is one of three states: FREE (not
+ * allocated to any transaction; available for reuse), UNLISTED (allocated
+ * to a transaction but not in any RBTree), or LISTED (allocated to a
+ * transaction and in either both requests_by_fxid and requests_by_size or
+ * else in requests_by_retry_time).
+ *
+ * Changes to UndoRequest objects are protected by the UndoRequestManager's
+ * lock, but not all changes require the lock.  The following rules apply:
+ *
+ * fxid must be InvalidFullTransactionId if and only if the UndoRequest is
+ * FREE, and may only be changed while holding the lock.
+ *
+ * next_free_request must be NULL unless the UndoRequest is FREE, and may
+ * only be changed while holding the lock.
+ *
+ * The remaining fields must be accurate if the UndoRequest is LISTED, but
+ * otherwise may or may not contain correct data. They should not be changed
+ * while the request is FREE, may be changed without holding the lock while
+ * the request is UNLISTED, and may only be changed while holding the lock
+ * if the requested is LISTED.
+ *
+ * Callers must be careful never to lose track of an entry that is UNLISTED;
+ * such entries will be permanently leaked. An entry that is FREE can be
+ * reallocated by this module, while one that is LISTED should eventually
+ * get processed and become FREE, but an UNLISTED entry remains the caller's
+ * responsibility until the state is changed.
+ */
+struct UndoRequest
+{
+	FullTransactionId fxid;
+	Oid			dbid;
+	Size		size;
+	UndoRecPtr	start_location_logged;
+	UndoRecPtr	end_location_logged;
+	UndoRecPtr	start_location_unlogged;
+	UndoRecPtr	end_location_unlogged;
+	TimestampTz retry_time;
+	UndoRequest *next_free_request;
+};
+
+/*
+ * An UndoRequestNode just points to an UndoRequest. We use it so that the
+ * same UndoRequest can be placed into more than one RBTree at the same
+ * time.
+ */
+typedef struct UndoRequestNode
+{
+	RBTNode		rbtnode;
+	UndoRequest *req;
+} UndoRequestNode;
+
+/*
+ * Possible sources of UndoRequest objects in need of processing.
+ */
+typedef enum UndoRequestSource
+{
+	UNDO_SOURCE_FXID,
+	UNDO_SOURCE_SIZE,
+	UNDO_SOURCE_RETRY_TIME
+} UndoRequestSource;
+
+/*
+ * An UndoRequestManager manages a collection of UndoRequest and
+ * UndoRequestNode objects. Typically, there would only be one such object
+ * for the whole system, but it's possible to create others for testing
+ * purposes.
+ */
+struct UndoRequestManager
+{
+	LWLock	   *lock;			/* for synchronization */
+	Size		capacity;		/* max # of UndoRequests */
+	Size		utilization;	/* # of non-FREE UndoRequests */
+	Size		soft_size_limit;	/* threshold to not background */
+	UndoRequestSource source;	/* which RBTree to check next? */
+	RBTree		requests_by_fxid;	/* lower FXIDs first */
+	RBTree		requests_by_size;	/* bigger sizes first */
+	RBTree		requests_by_retry_time; /* sooner retry times first */
+	bool		oldest_fxid_valid;	/* true if next field is valid */
+	FullTransactionId oldest_fxid;	/* oldest FXID of any UndoRequest */
+	UndoRequest *all_requests;
+	UndoRequest *first_free_request;
+	UndoRequestNode *first_free_request_node;
+};
+
+/* Static functions. */
+static UndoRequest *FindUndoRequestForDatabase(UndoRequestManager *urm,
+											   Oid dbid);
+static bool BackgroundUndoOK(UndoRequestManager *urm,
+							 UndoRequest *req);
+static RBTNode *UndoRequestNodeAllocate(void *arg);
+static void UndoRequestNodeFree(RBTNode *x, void *arg);
+static void UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata,
+								   void *arg);
+static int	UndoRequestNodeCompareRetryTime(const RBTNode *a,
+											const RBTNode *b,
+											void *arg);
+static int	UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b,
+									   void *arg);
+static int	UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b,
+									   void *arg);
+static void InsertUndoRequest(RBTree *rbt, UndoRequest *req);
+static void RemoveUndoRequest(RBTree *rbt, UndoRequest *req);
+static UndoRequest *FindUndoRequest(UndoRequestManager *urm,
+									FullTransactionId fxid);
+
+/*
+ * Compute the amount of space that will be needed by an undo request manager.
+ *
+ * We need space for the UndoRequestManager itself, for the UndoRequest
+ * objects, and for the UndoRequestNode objects.  We need twice as many
+ * UndoRequestNode objects as we do UndoRequest objects, because unfailed
+ * requests are stored in both requests_by_fxid and requests_by_size; failed
+ * requests are stored only in requests_by_retry_time.
+ */
+Size
+EstimateUndoRequestManagerSize(Size capacity)
+{
+	Size		s = MAXALIGN(sizeof(UndoRequestManager));
+
+	s = add_size(s, MAXALIGN(mul_size(capacity, sizeof(UndoRequest))));
+	s = add_size(s, MAXALIGN(mul_size(capacity,
+									  mul_size(2, sizeof(UndoRequestNode)))));
+
+	return s;
+}
+
+/*
+ * Initialize an undo request manager.
+ *
+ * The caller is responsible for providing an appropriately-sized chunk of
+ * memory; use EstimateUndoRequestManagerSize to find out how much space will
+ * be needed. This means that this infrastructure can potentially be used in
+ * either shared memory or, if desired, in backend-private memory. It will not
+ * work in DSM, though, because it uses pointers.
+ *
+ * The caller must also provide a lock that will be used to protect access
+ * to the data managed by this undo request manager.  This cannot be NULL,
+ * even if the memory is private.
+ */
+void
+InitializeUndoRequestManager(UndoRequestManager *urm, LWLock *lock,
+							 Size capacity, Size soft_limit)
+{
+	UndoRequest *reqs;
+	UndoRequestNode *nodes;
+	int			i;
+
+	/* Basic initialization. */
+	urm->lock = lock;
+	urm->capacity = capacity;
+	urm->utilization = 0;
+	urm->soft_size_limit = soft_limit;
+	urm->source = UNDO_SOURCE_FXID;
+	rbt_initialize(&urm->requests_by_fxid, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareFXID, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_size, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareSize, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_retry_time, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareRetryTime, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	urm->oldest_fxid_valid = true;
+	urm->oldest_fxid = InvalidFullTransactionId;
+
+	/* Find memory for UndoRequest and UndoRequestNode arenas. */
+	reqs = (UndoRequest *)
+		(((char *) urm) + MAXALIGN(sizeof(UndoRequestManager)));
+	urm->all_requests = reqs;
+	nodes = (UndoRequestNode *)
+		(((char *) reqs) + MAXALIGN(capacity * sizeof(UndoRequest)));
+
+	/* Build a free list of UndoRequest objects.  */
+	urm->first_free_request = reqs;
+	for (i = 0; i < capacity - 1; ++i)
+	{
+		UndoRequest *current = &reqs[i];
+		UndoRequest *next = &reqs[i + 1];
+
+		current->next_free_request = next;
+	}
+	reqs[capacity - 1].next_free_request = NULL;
+
+	/*
+	 * Similarly, build a free list of UndoRequestNode objects.  In this case,
+	 * we use the first few bytes of the free object to store a pointer to the
+	 * next free object.
+	 */
+	StaticAssertStmt(sizeof(UndoRequestNode) >= sizeof(UndoRequestNode *),
+					 "UndoRequestNode is too small");
+	urm->first_free_request_node = nodes;
+	for (i = 0; i < 2 * capacity - 1; ++i)
+	{
+		UndoRequestNode *current = &nodes[i];
+		UndoRequestNode *next = &nodes[i + 1];
+
+		*(UndoRequestNode **) current = next;
+	}
+	*(UndoRequestNode **) &nodes[2 * capacity - 1] = NULL;
+}
+
+/*
+ * Register a new undo request. If unable, returns NULL.
+ *
+ * This function should be called before a transaction first writes any undo;
+ * at end of transaction, the caller call either UnregisterUndoRequest (on
+ * commit) or FinalizeUndoRequest (on abort).
+ *
+ * The returned request is UNLISTED (as defined above).
+ */
+UndoRequest *
+RegisterUndoRequest(UndoRequestManager *urm, FullTransactionId fxid, Oid dbid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	req = urm->first_free_request;
+	if (req != NULL)
+	{
+		/* Pop free list. */
+		urm->first_free_request = req->next_free_request;
+		req->next_free_request = NULL;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = 0;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->end_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->end_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+
+		/* Save this fxid as the oldest one, if necessary. */
+		if (urm->oldest_fxid_valid &&
+			(!FullTransactionIdIsValid(urm->oldest_fxid)
+			 || FullTransactionIdPrecedes(fxid, urm->oldest_fxid)))
+			urm->oldest_fxid = fxid;
+	}
+
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Set the start location for either logged or unlogged undo.
+ *
+ * We don't need a lock here, because this request must be UNLISTED (as
+ * defined above).
+ */
+void
+UndoRequestSetStartLocation(UndoRequestManager *urm, UndoRequest *req,
+							bool is_logged, UndoRecPtr start_location)
+{
+	Assert(UndoRecPtrIsValid(start_location));
+	if (is_logged)
+		req->start_location_logged = start_location;
+	else
+		req->start_location_unlogged = start_location;
+}
+
+/*
+ * Finalize details for an undo request.
+ *
+ * Since an UndoRequest should be registered before beginning to write undo,
+ * the undo size won't be known at that point; this function should be getting
+ * called at prepare time for a prepared transaction, or at abort time
+ * otherwise, by which point the size should be known.
+ *
+ * Caller should report the total size of generated undo in bytes, counting
+ * only logged and unlogged undo that will be processed by background workers.
+ * Any undo bytes that aren't part of the logged or unlogged undo records
+ * that may need cleanup actions performed should not be included in size;
+ * for example, temporary undo doesn't count, as the caller must deal with
+ * that outside of this mechanism.
+ *
+ * Caller must also pass the end location for logged and unlogged undo;
+ * each should be if InvalidUndoRecPtr if and only if the corresponding
+ * start location was never set.
+ *
+ * We don't need a lock here, because this request must be UNLISTED (as
+ * defined above).
+ */
+void
+FinalizeUndoRequest(UndoRequestManager *urm, UndoRequest *req, Size size,
+					UndoRecPtr end_location_logged,
+					UndoRecPtr end_location_unlogged)
+{
+	Assert(size != 0);
+	Assert(UndoRecPtrIsValid(end_location_logged) ||
+		   UndoRecPtrIsValid(end_location_unlogged));
+	Assert(UndoRecPtrIsValid(end_location_logged) ==
+		   UndoRecPtrIsValid(req->start_location_logged));
+	Assert(UndoRecPtrIsValid(end_location_unlogged) ==
+		   UndoRecPtrIsValid(req->start_location_unlogged));
+	req->size = size;
+	req->end_location_logged = end_location_logged;
+	req->end_location_unlogged = end_location_unlogged;
+}
+
+/*
+ * Release a previously-allocated undo request.
+ *
+ * On entry, the undo request should be either LISTED or UNLISTED; on exit,
+ * it will be FREE (as these terms are defined above).
+ *
+ * This should be used at transaction commit, if an UndoRequest was
+ * registered, or when undo for an aborted transaction has been succesfully
+ * processed.
+ *
+ * Because this function may be called as a post-commit step, it must never
+ * throw an ERROR.
+ */
+void
+UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * Remove the UndoRequest from any RBTree that contains it.  If the retry
+	 * time is not DT_NOBEGIN, then the request has been finalized and undo
+	 * has subsequently failed.  If the size is 0, the request has not been
+	 * finalized yet, so it's not in any RBTree.
+	 */
+	if (req->retry_time != DT_NOBEGIN)
+		RemoveUndoRequest(&urm->requests_by_retry_time, req);
+	else if (req->size != 0)
+	{
+		RemoveUndoRequest(&urm->requests_by_fxid, req);
+		RemoveUndoRequest(&urm->requests_by_size, req);
+	}
+
+	/* Plan to recompute oldest_fxid, if necessary. */
+	if (FullTransactionIdEquals(req->fxid, urm->oldest_fxid))
+		urm->oldest_fxid_valid = false;
+
+	/* Push onto freelist. */
+	req->next_free_request = urm->first_free_request;
+	urm->first_free_request = req;
+
+	/* Decrease utilization. */
+	--urm->utilization;
+
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Try to hand an undo request off for background processing.
+ *
+ * If this function returns true, the UndoRequest can be left for background
+ * processing; the caller need not do anything more. If this function returns
+ * false, the caller should try to process it in the foreground, and must
+ * call either UnregisterUndoRequest on success or RescheduleUndoRequest
+ * on failure.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ *
+ * On entry, the UndoRequest should be UNLISTED; on exit, it is LISTED
+ * if this function returns true, and remains UNLISTED if this function
+ * returns false (see above for definitions).
+ */
+bool
+PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req)
+{
+	bool		background;
+
+	/*
+	 * If we failed after allocating an UndoRequest but before setting any
+	 * start locations, there's no work to be done.  In that case, we can just
+	 * unregister the request.
+	 */
+	if (!UndoRecPtrIsValid(req->start_location_logged) &&
+		!UndoRecPtrIsValid(req->start_location_unlogged))
+	{
+		UnregisterUndoRequest(urm, req);
+		return true;
+	}
+
+	/*
+	 * We need to check shared state in order to determine whether or not to
+	 * perform this undo in the background, and if we are going to perform it
+	 * in the background, also to add it to requests_by_fxid and
+	 * requests_by_size.
+	 */
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	background = BackgroundUndoOK(urm, req);
+	if (background)
+	{
+		/*
+		 * We're going to handle this in the background, so add it to
+		 * requests_by_fxid and requests_by_size, so that GetNextUndoRequest
+		 * can find it.
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	LWLockRelease(urm->lock);
+
+	return background;
+}
+
+/*
+ * Get an undo request that needs background processing.
+ *
+ * Unless dbid is InvalidOid, any request returned must be from the indicated
+ * database.  If minimum_runtime_reached is true, the caller only wants to
+ * process another request if the next request happens to be from the correct
+ * database. If it's false, the caller wants to avoiding exiting too quickly,
+ * and would like to process a request from the database if there's one
+ * available.
+ *
+ * If no suitable request is found, *fxid gets InvalidFullTransactionId;
+ * otherwise, *fxid gets the FullTransactionId of the transaction and
+ * the parameters which follow get the start and end locations of logged
+ * and unlogged undo for that transaction.  It's possible that the transaction
+ * wrote only logged undo or only unlogged undo, in which case the other
+ * pair fields will have a value of InvalidUndoRecPtr, but it should never
+ * happen that all of the fields get InvalidUndoRecPtr, because that would
+ * mean we queued up an UndoRequest to do nothing.
+ *
+ * This function, as a side effect, makes the returned UndoRequest UNLISTED,
+ * as defined above, so that no other backend will attempt to process it
+ * simultaneously. The caller must be certain to call either
+ * UnregisterUndoRequest (if successful) or RescheduleUndoRequest (on
+ * failure) to avoid leaking the UndoRequest.
+ */
+UndoRequest *
+GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+				   bool minimum_runtime_reached,
+				   FullTransactionId *fxid,
+				   UndoRecPtr * start_location_logged,
+				   UndoRecPtr * end_location_logged,
+				   UndoRecPtr * start_location_unlogged,
+				   UndoRecPtr * end_location_unlogged)
+{
+	UndoRequest *req = NULL;
+	int			nloops;
+	bool		saw_db_mismatch = false;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/* Some might have no work, so loop until all are checked. */
+	for (nloops = 0; nloops < 3; ++nloops)
+	{
+		RBTree	   *rbt;
+		UndoRequestSource source = urm->source;
+		UndoRequestNode *node;
+
+		/*
+		 * We rotate between the three possible sources of UndoRequest
+		 * objects.
+		 *
+		 * The idea here is that processing the requests with the oldest
+		 * transaction IDs is important because it helps us discard undo log
+		 * data sooner and because it allows XID horizons to advance. On the
+		 * other hand, handling transactions that generated a very large
+		 * amount of undo is also a priority, because undo will probably take
+		 * a long to finish and thus should be started as early as possible
+		 * and also because it likely touched a large number of pages which
+		 * will be slow to access until the undo is processed.
+		 *
+		 * However, we also need to make sure to periodically retry undo for
+		 * transactions that previously failed. We hope that this will be very
+		 * rare, but if it does happen we can neither affort to retry those
+		 * transactions over and over in preference to all others, nor on the
+		 * other hand to just ignore them forever.
+		 *
+		 * We could try to come up with some scoring system that assigns
+		 * relative levels of importance to FullTransactionId age, undo size,
+		 * and retry time, but it seems difficult to come up with a weighting
+		 * system that can ensure that nothing gets starved. By rotating among
+		 * the sources evenly, we know that as long as we continue to process
+		 * undo requests on some sort of regular basis, each source will get
+		 * some amount of attention.
+		 */
+		switch (source)
+		{
+			case UNDO_SOURCE_FXID:
+				rbt = &urm->requests_by_fxid;
+				urm->source = UNDO_SOURCE_SIZE;
+				break;
+			case UNDO_SOURCE_SIZE:
+				rbt = &urm->requests_by_size;
+				urm->source = UNDO_SOURCE_RETRY_TIME;
+				break;
+			case UNDO_SOURCE_RETRY_TIME:
+				rbt = &urm->requests_by_retry_time;
+				urm->source = UNDO_SOURCE_FXID;
+				break;
+		}
+
+		/* Get highest-priority item. */
+		node = (UndoRequestNode *) rbt_leftmost(rbt);
+		if (node == NULL)
+			continue;
+
+		/*
+		 * We can only take an item from the retry time RBTree if the retry
+		 * time is in the past.
+		 */
+		if (source == UNDO_SOURCE_RETRY_TIME &&
+			node->req->retry_time > GetCurrentTimestamp())
+			continue;
+
+		/*
+		 * If a database OID was specified, it must match. If it does not, we
+		 * go ahead and try any remaining RBTree.  Note that this needs to be
+		 * after the other tests so that we get the right value for the
+		 * saw_db_mismatch flag.
+		 */
+		if (OidIsValid(dbid) && node->req->dbid != dbid)
+		{
+			saw_db_mismatch = true;
+			continue;
+		}
+
+		/* Looks like we have a winner. */
+		req = node->req;
+		break;
+	}
+
+	/*
+	 * Determine whether we should do a more exhaustive search.
+	 *
+	 * If we found a node, we don't need look any harder.  If we didn't see a
+	 * database mismatch, then looking harder can't help: there's nothing to
+	 * do at all, never mind for which database.  If the caller set
+	 * minimum_runtime_reached, then they don't want us to look harder.
+	 */
+	if (req == NULL && saw_db_mismatch && !minimum_runtime_reached)
+		req = FindUndoRequestForDatabase(urm, dbid);
+
+	/*
+	 * If we found a suitable request, remove it from any RBTree that contains
+	 * it.
+	 */
+	if (req != NULL)
+	{
+		if (req->retry_time != DT_NOBEGIN)
+			RemoveUndoRequest(&urm->requests_by_retry_time, req);
+		else
+		{
+			RemoveUndoRequest(&urm->requests_by_fxid, req);
+			RemoveUndoRequest(&urm->requests_by_size, req);
+		}
+	}
+
+	LWLockRelease(urm->lock);
+
+	/*
+	 * Set output parameters.  Any request we found is now UNLISTED, so it's
+	 * safe to do this without the lock.
+	 */
+	if (req == NULL)
+		*fxid = InvalidFullTransactionId;
+	else
+	{
+		*fxid = req->fxid;
+		*start_location_logged = req->start_location_logged;
+		*end_location_logged = req->end_location_logged;
+		*start_location_unlogged = req->start_location_unlogged;
+		*end_location_unlogged = req->end_location_unlogged;
+	}
+
+	/* All done. */
+	return req;
+}
+
+/*
+ * Reschedule an undo request after undo failure.
+ *
+ * This function should be called when undo processing fails, either in the
+ * foreground or in the background.  The foreground case occurs when
+ * FinalizeUndoRequest returns false and undo then also fails; the background
+ * case occurs when GetNextUndoRequest returns an UndoRequest and undo then
+ * fails. Note that this function isn't used after a shutdown or crash: see
+ * comments in RecreateUndoRequest for how we handle that case.
+ *
+ * In either of the cases where this function is reached, the UndoRequest
+ * should be UNLISTED; on return, it will be LISTED (both as defined above).
+ * If it's a foreground undo failure, it's never been LISTED; if it's a
+ * background undo failure, it was made UNLISTED by GetNextUndoRequest. So,
+ * we don't have to remove the request from anywhere, not even conditionally;
+ * we just need to add it to the set of failed requests.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ */
+void
+RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * This algorithm for determining the next retry time is fairly
+	 * unsophisticated: the first retry happens after 10 seconds, and each
+	 * subsequent retry after 30 seconds. We could do something more
+	 * complicated here, but we'd need to do more bookkeeping and it's unclear
+	 * what we'd gain.
+	 */
+	if (req->retry_time == DT_NOBEGIN)
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 10 * 1000);
+	else
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 30 * 1000);
+
+	InsertUndoRequest(&urm->requests_by_retry_time, req);
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Recreate UndoRequest state after a shutdown.
+ *
+ * This function is expected to be called after a shutdown, whether a clean
+ * shutdown or a crash, both for aborted transactions with unprocessed undo
+ * and also for prepared transactions.  All calls to this function must be
+ * completed, and SuspendPreparedUndoRequest must be called for every prepared
+ * transaction, before the first call to GetNextUndoRequest occurs.
+ *
+ * This function be called up two twice per FullTransactionId, once with
+ * is_logged true and once with is_logged false, because the transaction may
+ * have both logged and unlogged undo in different places. start_location is
+ * the beginning of the type of undo indicated by the is_logged parameter, and
+ * size is the amount of such undo in bytes.  If this function is called twice,
+ * the result will be a single UndoRequest containing both start locations and
+ * a size which is the sum of the two sizes passed to the separate calls.
+ *
+ * If this function is unable to allocate a new UndoRequest when required,
+ * it will return false.  If that happens, it's not safe to continue using
+ * this UndoRequestManager and a system-wide shutdown to raise the limit on
+ * the number of outstanding requests is indicated.
+ */
+bool
+RecreateUndoRequest(UndoRequestManager *urm, FullTransactionId fxid,
+					Oid dbid, bool is_logged, UndoRecPtr start_location,
+					UndoRecPtr end_location, Size size)
+{
+	UndoRequest *req;
+
+	Assert(UndoRecPtrIsValid(start_location));
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	if (req)
+	{
+		/* Already called for opposite value of is_logged. */
+		if (is_logged)
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_logged));
+			Assert(!UndoRecPtrIsValid(req->end_location_logged));
+			req->start_location_logged = start_location;
+			req->end_location_logged = end_location;
+		}
+		else
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_unlogged));
+			Assert(!UndoRecPtrIsValid(req->end_location_unlogged));
+			req->start_location_unlogged = start_location;
+			req->end_location_unlogged = end_location;
+		}
+		Assert(req->dbid == dbid);
+
+		/* Adjusting size may change position in RBTree. */
+		RemoveUndoRequest(&urm->requests_by_size, req);
+		req->size += size;
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	else
+	{
+		/* First call for this FullTransactionId. */
+		req = urm->first_free_request;
+		if (req == NULL)
+		{
+			LWLockRelease(urm->lock);
+			return false;
+		}
+
+		/* We got an item; pop it from the free list. */
+		urm->first_free_request = req->next_free_request;
+		req->next_free_request = NULL;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = size;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+		if (is_logged)
+			req->start_location_logged = start_location;
+		else
+			req->start_location_unlogged = start_location;
+
+		/*
+		 * List this request so that undo workers will see it.  Note that we
+		 * assume that these are new aborts, but it's possible that there are
+		 * actually a whole series of previous undo failures before the
+		 * shutdown or crash. If we had the information about whether this
+		 * request had failed previously, we could set req->retry_time and
+		 * insert it into requests_by_retry_time rather than requests_by_fxid
+		 * and requests_by_size, but it doesn't seem important to retain
+		 * information about undo failure across crashes or shutdowns, because
+		 * we're just trying to guarantee that we don't busy-loop or starve
+		 * other requests. (FindUndoRequest would get confused, too.)
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+
+	LWLockRelease(urm->lock);
+	return true;
+}
+
+/*
+ * Adjust UndoRequestManager state for prepared transactions.
+ *
+ * After a restart, once all calls to RecreateUndoRequest have been completed
+ * and before the first call to GetNextUndoRequest, this function should
+ * be called for each prepared transaction. That's necessary to avoid
+ * prematurely executed undo actions for transactions that haven't aborted
+ * yet and might go on to commit. The UndoRequest for the indicated fxid is
+ * made UNLISTED (as defined above) so that GetNextUndoRequest does not find
+ * them.
+ *
+ * The caller should retain a pointer to the returned UndoRequest and, when
+ * the prepared transaction is eventually committed or rolled back, should
+ * invoke UnregisterUndoRequest on commit or FinalizeUndoRequest on abort.
+ */
+UndoRequest *
+SuspendPreparedUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	Assert(req != NULL);
+	Assert(req->size != 0);
+	RemoveUndoRequest(&urm->requests_by_fxid, req);
+	RemoveUndoRequest(&urm->requests_by_size, req);
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Get oldest registered FXID, whether LISTED or UNLISTED (as defined above).
+ *
+ * We cache the result of this computation so as to avoid repeating it too
+ * often.
+ */
+FullTransactionId
+UndoRequestManagerOldestFXID(UndoRequestManager *urm)
+{
+	FullTransactionId result = InvalidFullTransactionId;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	if (urm->oldest_fxid_valid)
+		result = urm->oldest_fxid;
+	else
+	{
+		int			i;
+
+		for (i = 0; i < urm->capacity; ++i)
+		{
+			UndoRequest *req = &urm->all_requests[i];
+
+			if (FullTransactionIdIsValid(req->fxid) &&
+				(!FullTransactionIdIsValid(result) ||
+				 FullTransactionIdPrecedes(req->fxid, result)))
+				result = req->fxid;
+		}
+
+		urm->oldest_fxid = result;
+		urm->oldest_fxid_valid = true;
+	}
+
+	LWLockRelease(urm->lock);
+
+	return result;
+}
+
+/*
+ * Perform a left-to-right search of all three RBTrees, looking for a request
+ * for a given database. The searches are interleaved so that we latch
+ * onto the highest-priority request in any RBTree.
+ *
+ * It's possible that we should have some kind of limit on this search, so
+ * that it doesn't do an exhaustive search of every RBTree. However, it's not
+ * exactly clear how that would affect the behavior, or how to pick a
+ * reasonable limit.
+ */
+static UndoRequest *
+FindUndoRequestForDatabase(UndoRequestManager *urm, Oid dbid)
+{
+	RBTreeIterator iter[3];
+	int			doneflags = 0;
+	int			i = 0;
+
+	rbt_begin_iterate(&urm->requests_by_fxid, LeftRightWalk, &iter[0]);
+	rbt_begin_iterate(&urm->requests_by_size, LeftRightWalk, &iter[1]);
+	rbt_begin_iterate(&urm->requests_by_retry_time, LeftRightWalk, &iter[2]);
+
+	while (1)
+	{
+		UndoRequestNode *node;
+
+		if ((doneflags & (1 << i)) == 0)
+		{
+			node = (UndoRequestNode *) rbt_iterate(&iter[i]);
+			if (node == NULL)
+			{
+				doneflags |= 1 << i;
+				if (doneflags == 7) /* all bits set */
+					break;
+			}
+			else if (node->req->dbid == dbid)
+				return node->req;
+		}
+		i = (i + 1) % 3;
+	}
+
+	return NULL;
+}
+
+/*
+ * Is it OK to handle this UndoRequest in the background?
+ */
+static bool
+BackgroundUndoOK(UndoRequestManager *urm, UndoRequest *req)
+{
+	/*
+	 * If we've passed the soft size limit, it's not OK to background it.
+	 */
+	if (urm->utilization > urm->soft_size_limit)
+		return false;
+
+	/*
+	 * Otherwise, allow it.
+	 *
+	 * TODO: We probably want to introduce some additional rules here based on
+	 * the size of the request.
+	 */
+	return true;
+}
+
+/*
+ * RBTree callback to allocate an UndoRequestNode.
+ *
+ * Everything is preallocated, so we're just popping the freelist.
+ */
+static RBTNode *
+UndoRequestNodeAllocate(void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = urm->first_free_request_node;
+
+	/*
+	 * Any LISTED UndoRequest should either be in both requests_by_fxid and
+	 * requests_by_size, or it should be in requests_by_retry_time, or it
+	 * should be in neither RBTree; consequently, it should be impossible to
+	 * use more than 2 UndoRequestNode objects per UndoRequest. Since we
+	 * preallocate that number, we should never run out. In case there's a bug
+	 * in the logic, let's insert a runtime check here even when Asserts are
+	 * disabled.
+	 */
+	if (node == NULL)
+		elog(ERROR, "no free UndoRequestNode");
+
+	/* Pop freelist. */
+	urm->first_free_request_node = *(UndoRequestNode **) node;
+
+	return &node->rbtnode;
+}
+
+/*
+ * RBTree callback to free an UndoRequestNode.
+ *
+ * Just put it back on the freelist.
+ */
+static void
+UndoRequestNodeFree(RBTNode *x, void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = (UndoRequestNode *) x;
+
+	*(UndoRequestNode **) node = urm->first_free_request_node;
+	urm->first_free_request_node = node;
+}
+
+/*
+ * RBTree callback to combine an UndoRequestNode with another one.
+ *
+ * The key for every RBTree includes the FXID, which is unique, so it should
+ * never happen that we need to merge requests.
+ */
+static void
+UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata, void *arg)
+{
+	elog(ERROR, "undo requests should never need to be combined");
+}
+
+/*
+ * RBTree comparator for requests_by_retry_time. Older retry
+ * times first; in the case of a tie, smaller FXIDs first.  This avoids ties,
+ * which is important since we don't want to merge requests, and also favors
+ * retiring older transactions first, which is generally desirable.
+ */
+static int
+UndoRequestNodeCompareRetryTime(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	TimestampTz retry_time_a = aa->req->retry_time;
+	TimestampTz retry_time_b = bb->req->retry_time;
+
+	if (retry_time_a != retry_time_b)
+		return retry_time_a < retry_time_b ? -1 : 1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. Lower FXIDs first. No tiebreak,
+ * because FXIDs should be unique.
+ */
+static int
+UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. As in we do for the retry
+ * time RBTree, break ties in favor of lower FXIDs.
+ */
+static int
+UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	Size		size_a = aa->req->size;
+	Size		size_b = bb->req->size;
+
+	if (size_a != size_b)
+		return size_a < size_b ? 1 : -1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Insert an UndoRequest into one RBTree.
+ *
+ * The actual RBTree element is an UndoRequestNode, which just points to
+ * the actual UndoRequest.
+ */
+static void
+InsertUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode dummy;
+	bool		isNew;
+
+	/*
+	 * The rbt_insert interface is a bit strange: we have to pass something
+	 * that looks like an RBTNode, but the RBTNode itself doesn't need to be
+	 * initialized - only the "extra" data that follows the end of the
+	 * structure needs to be correct.
+	 */
+	dummy.req = req;
+	rbt_insert(rbt, &dummy.rbtnode, &isNew);
+	Assert(isNew);
+}
+
+/*
+ * Remove an UndoRequest from one RBTree.
+ *
+ * This is just the reverse of InsertUndoRequest, with the same interface
+ * quirk.
+ */
+static void
+RemoveUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode dummy;
+	RBTNode    *node;
+
+	dummy.req = req;
+	node = rbt_find(rbt, &dummy.rbtnode);
+	rbt_delete(rbt, node);
+}
+
+/*
+ * Find an UndoRequest by FXID.
+ *
+ * If we needed to do this frequently, it might be worth maintaining a hash
+ * table mapping FXID -> UndoRequest, but since we only need it after a system
+ * restart, RBTree's O(lg n) performance seems good enough.
+ *
+ * Note that this can only find an UndoRequest that has not failed and is not
+ * yet being processed, because a failed UndoRequest would be in
+ * requests_by_retry_time, not requests_by_fxid, and an in-progress
+ * UndoRequest wouldn't be in either data structure. That restriction, too,
+ * is OK for current uses.
+ */
+static UndoRequest *
+FindUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest dummy_request;
+	UndoRequestNode dummy_node;
+	RBTNode    *node;
+
+	/*
+	 * Here we need both a dummy UndoRequest and a dummy UndoRequestNode; only
+	 * the comparator will look at the dummy UndoRequestNode, and it will only
+	 * look at UndoRequest, and specifically its FXID.
+	 */
+	dummy_request.fxid = fxid;
+	dummy_node.req = &dummy_request;
+	node = rbt_find(&urm->requests_by_fxid, &dummy_node.rbtnode);
+	if (node == NULL)
+		return NULL;
+	return ((UndoRequestNode *) node)->req;
+}
diff --git a/src/backend/lib/rbtree.c b/src/backend/lib/rbtree.c
index 33181e9211..bda870eab7 100644
--- a/src/backend/lib/rbtree.c
+++ b/src/backend/lib/rbtree.c
@@ -35,25 +35,6 @@
 #define RBTBLACK	(0)
 #define RBTRED		(1)
 
-/*
- * RBTree control structure
- */
-struct RBTree
-{
-	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
-
-	/* Remaining fields are constant after rbt_create */
-
-	Size		node_size;		/* actual size of tree nodes */
-	/* The caller-supplied manipulation functions */
-	rbt_comparator comparator;
-	rbt_combiner combiner;
-	rbt_allocfunc allocfunc;
-	rbt_freefunc freefunc;
-	/* Passthrough arg passed to all manipulation functions */
-	void	   *arg;
-};
-
 /*
  * all leafs are sentinels, use customized NIL name to prevent
  * collision with system-wide constant NIL which is actually NULL
@@ -122,6 +103,33 @@ rbt_create(Size node_size,
 	return tree;
 }
 
+/*
+ * rbt_initialize: initalize an empty RBTree
+ *
+ * This is just like rbt_create, except that the caller is responsible for
+ * allocating the memory.
+ */
+void
+rbt_initialize(RBTree *rbt,
+			   Size node_size,
+			   rbt_comparator comparator,
+			   rbt_combiner combiner,
+			   rbt_allocfunc allocfunc,
+			   rbt_freefunc freefunc,
+			   void *arg)
+{
+	Assert(node_size > sizeof(RBTNode));
+
+	rbt->root = RBTNIL;
+	rbt->node_size = node_size;
+	rbt->comparator = comparator;
+	rbt->combiner = combiner;
+	rbt->allocfunc = allocfunc;
+	rbt->freefunc = freefunc;
+
+	rbt->arg = arg;
+}
+
 /* Copy the additional data fields from one RBTNode to another */
 static inline void
 rbt_copy_data(RBTree *rbt, RBTNode *dest, const RBTNode *src)
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..cc00509699 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000000..7be7308e15
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *		Undo request manager.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOREQUEST_H
+#define UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "datatype/timestamp.h"
+
+struct UndoRequest;
+struct UndoRequestManager;
+typedef struct UndoRequest UndoRequest;
+typedef struct UndoRequestManager UndoRequestManager;
+
+/* Initialization functions. */
+extern Size EstimateUndoRequestManagerSize(Size capacity);
+extern void InitializeUndoRequestManager(UndoRequestManager *urm,
+										 LWLock *lock, Size capacity,
+										 Size soft_limit);
+
+/* Call this before inserting undo records. */
+extern UndoRequest *RegisterUndoRequest(UndoRequestManager *urm,
+										FullTransactionId fxid,
+										Oid dbid);
+
+/* Remember where our undo starts and ends. */
+extern void UndoRequestSetStartLocation(UndoRequestManager *urm,
+										UndoRequest *req,
+										bool is_logged,
+										UndoRecPtr start_location);
+
+/* Remember undo size and end locations. */
+extern void FinalizeUndoRequest(UndoRequestManager *urm,
+								UndoRequest *req,
+								Size size,
+								UndoRecPtr end_location_logged,
+								UndoRecPtr end_location_unlogged);
+
+/* Forget about an UndoRequest we don't need any more. */
+extern void UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Attempt to dispatch UndoRequest for background processing. */
+extern bool PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req);
+
+/* Get work for background undo process. */
+extern UndoRequest *GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+									   bool minimum_runtime_reached,
+									   FullTransactionId *fxid,
+									   UndoRecPtr *start_location_logged,
+									   UndoRecPtr *end_location_logged,
+									   UndoRecPtr *start_location_unlogged,
+									   UndoRecPtr *end_location_unlogged);
+
+/* Reschedule failed undo attempt. */
+extern void RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Restore state after crash. */
+extern bool RecreateUndoRequest(UndoRequestManager *urm,
+								FullTransactionId fxid, Oid dbid,
+								bool is_logged,
+								UndoRecPtr start_location,
+								UndoRecPtr end_location,
+								Size size);
+extern UndoRequest *SuspendPreparedUndoRequest(UndoRequestManager *urm,
+											   FullTransactionId fxid);
+
+/* Get oldest registered FXID. */
+FullTransactionId UndoRequestManagerOldestFXID(UndoRequestManager *urm);
+
+#endif
diff --git a/src/include/lib/rbtree.h b/src/include/lib/rbtree.h
index 6d79a24015..ff6f99a932 100644
--- a/src/include/lib/rbtree.h
+++ b/src/include/lib/rbtree.h
@@ -28,8 +28,33 @@ typedef struct RBTNode
 	struct RBTNode *parent;		/* parent, or NULL (not RBTNIL!) if none */
 } RBTNode;
 
-/* Opaque struct representing a whole tree */
-typedef struct RBTree RBTree;
+/* Support functions to be provided by caller */
+typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
+typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
+typedef RBTNode *(*rbt_allocfunc) (void *arg);
+typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
+
+/*
+ * RBTree control structure
+ *
+ * This is declared here to make it possible to preallocate an object of
+ * the correct size, but callers should not access the members diretly.
+ */
+typedef struct RBTree
+{
+	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
+
+	/* Remaining fields are constant after rbt_create */
+
+	Size		node_size;		/* actual size of tree nodes */
+	/* The caller-supplied manipulation functions */
+	rbt_comparator comparator;
+	rbt_combiner combiner;
+	rbt_allocfunc allocfunc;
+	rbt_freefunc freefunc;
+	/* Passthrough arg passed to all manipulation functions */
+	void	   *arg;
+} RBTree;
 
 /* Available tree iteration orderings */
 typedef enum RBTOrderControl
@@ -53,18 +78,19 @@ struct RBTreeIterator
 	bool		is_over;
 };
 
-/* Support functions to be provided by caller */
-typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
-typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
-typedef RBTNode *(*rbt_allocfunc) (void *arg);
-typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
-
 extern RBTree *rbt_create(Size node_size,
 						  rbt_comparator comparator,
 						  rbt_combiner combiner,
 						  rbt_allocfunc allocfunc,
 						  rbt_freefunc freefunc,
 						  void *arg);
+extern void rbt_initialize(RBTree *rbt,
+						   Size node_size,
+						   rbt_comparator comparator,
+						   rbt_combiner combiner,
+						   rbt_allocfunc allocfunc,
+						   rbt_freefunc freefunc,
+						   void *arg);
 
 extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
 extern RBTNode *rbt_leftmost(RBTree *rbt);
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 60d6d7be1b..f32afffab1 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		  test_rbtree \
 		  test_rls_hooks \
 		  test_shm_mq \
+		  test_undo_request_manager \
 		  unsafe_tests \
 		  worker_spi
 
diff --git a/src/test/modules/test_undo_request_manager/Makefile b/src/test/modules/test_undo_request_manager/Makefile
new file mode 100644
index 0000000000..5bc4695004
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo_request_manager/Makefile
+
+MODULE_big = test_undo_request_manager
+OBJS = test_undo_request_manager.o $(WIN32RES)
+PGFILEDESC = "test_undo_request_manager - test undo request manager code"
+
+EXTENSION = test_undo_request_manager
+DATA = test_undo_request_manager--1.0.sql
+
+REGRESS = test_undo_request_manager
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_request_manager
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
new file mode 100644
index 0000000000..c79611b3b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
@@ -0,0 +1,28 @@
+CREATE EXTENSION test_undo_request_manager;
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+ERROR:  unable to register undo request #2
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+ urm_simple_test 
+-----------------
+ {1001,1002}
+(1 row)
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+                urm_simple_test                 
+------------------------------------------------
+ {1001,1006,1002,1007,1003,1008,1004,1009,1005}
+(1 row)
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
+                   urm_simple_test                   
+-----------------------------------------------------
+ {1001,1010,1002,1009,1003,1008,1004,1007,1005,1006}
+(1 row)
+
diff --git a/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
new file mode 100644
index 0000000000..6611e040b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
@@ -0,0 +1,16 @@
+CREATE EXTENSION test_undo_request_manager;
+
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
new file mode 100644
index 0000000000..30ff471c23
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
@@ -0,0 +1,9 @@
+/* src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_undo_request_manager" to load this file. \quit
+
+CREATE FUNCTION urm_simple_test(capacity pg_catalog.int4,
+								requests pg_catalog.int8[])
+    RETURNS pg_catalog.int8[] STRICT
+	AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.c b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
new file mode 100644
index 0000000000..8b994283c8
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
@@ -0,0 +1,139 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_undo_request_manager.c
+ *		Test undo request manager.
+ *
+ * Copyright (c) 2013-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/test_undo_request_manager/undo_request_manager.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "catalog/pg_type_d.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "utils/array.h"
+
+PG_MODULE_MAGIC;
+PG_FUNCTION_INFO_V1(urm_simple_test);
+
+/*
+ * SQL-callable test function.  We create an UndoRequestManager in
+ * backend-private memory here and exercise it a bit to see if it breaks.
+ *
+ * The first argument is the capacity of the UndoRequestManager as an integer.
+ *
+ * The second argument is 1-dimensional bigint array, where each subarray
+ * contains a hypothetical undo size.
+ *
+ * This function registers and inserts all the requests (failing if space is
+ * exhausted) with fake, sequentially assigned transaction IDs, and then
+ * fetches them back one by one. The return value is an array of fake
+ * transaction IDs in the order they were returned.
+ *
+ * This test doesn't simulate undo failure, multi-database operation, or
+ * prepared transactions.
+ */
+Datum
+urm_simple_test(PG_FUNCTION_ARGS)
+{
+	int64	capacity = PG_GETARG_INT32(0);
+	ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	  *darray;
+	int			nentries;
+	Datum	  *dresult;
+	ArrayType *result;
+	UndoRequestManager *urm;
+	const UndoRecPtr SomeValidUndoRecPtr = InvalidUndoRecPtr + 1;
+	int			i;
+	FullTransactionId fake_fxid = FullTransactionIdFromEpochAndXid(0, 1000);
+
+	/* Require positive capacity. */
+	if (capacity <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("undo request manager capacity must be a positive integer")));
+
+	/* Sanity-check and deconstruct array. */
+	if (ARR_NDIM(array) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("array must have exactly 1 dimension")));
+	if (array_contains_nulls(array))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("cannot work with arrays containing NULLs")));
+	deconstruct_array(array, INT8OID, 8, FLOAT8PASSBYVAL, 'd',
+					  &darray, NULL, &nentries);
+
+	/*
+	 * Initialize UndoRequestManager. We have to supply an LWLock; rather than
+	 * creating a new one somewhere, just use our own backendLock. These locks
+	 * aren't that heavily trafficked and we won't have any reason to take it
+	 * for any other purpose while the UndoRequstManager holds it, so this
+	 * should be safe enough.
+	 *
+	 * We make the soft limit equal to the full capacity here for testing
+	 * purposes, which means that we should always succeed in dispatching to
+	 * the background.
+	 */
+	urm = palloc(EstimateUndoRequestManagerSize(capacity));
+	InitializeUndoRequestManager(urm, &MyProc->backendLock,
+								 capacity, capacity);
+
+	/* Insert entries as provided by caller. */
+	for (i = 0; i < nentries; ++i)
+	{
+		int64	size = DatumGetInt64(darray[i]);
+		UndoRequest *req;
+
+		FullTransactionIdAdvance(&fake_fxid);
+
+		req = RegisterUndoRequest(urm, fake_fxid, MyDatabaseId);
+		if (req == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to register undo request #%d", i + 1)));
+		UndoRequestSetStartLocation(urm, req, true, SomeValidUndoRecPtr);
+		FinalizeUndoRequest(urm, req, size,
+							SomeValidUndoRecPtr,
+							InvalidUndoRecPtr);
+		if (!PerformUndoInBackground(urm, req))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to background undo request #%d", i + 1)));
+	}
+
+	/* Now get the entries back. */
+	dresult = palloc(nentries * sizeof(Datum));
+	for (i = 0; true; ++i)
+	{
+		UndoRequest *req;
+		UndoRecPtr	p[4];
+
+		/* Get some work. */
+		req = GetNextUndoRequest(urm, MyDatabaseId, true,
+								 &fake_fxid, &p[0], &p[1], &p[2], &p[3]);
+		if (req == NULL)
+			break;
+		if (i >= nentries)
+			elog(ERROR, "found more undo requests than were inserted");
+
+		/* Save the fake FXID. */
+		dresult[i] =
+			Int64GetDatum((int64) U64FromFullTransactionId(fake_fxid));
+
+		/* Report that we successfully processed the imaginary undo. */
+		UnregisterUndoRequest(urm, req);
+	}
+
+	/* Put result into array form. */
+	result = construct_array(dresult, i, INT8OID, 8, FLOAT8PASSBYVAL, 'd');
+	PG_RETURN_ARRAYTYPE_P(result);
+}
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.control b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
new file mode 100644
index 0000000000..0a340e9843
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
@@ -0,0 +1,4 @@
+comment = 'Test code for undo request manager'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_request_manager'
+relocatable = true
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 432d2d812e..4c78ba61a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2536,6 +2536,10 @@ ULONG
 ULONG_PTR
 UV
 UVersionInfo
+UndoRequest
+UndoRequestManager
+UndoRequestNode
+UndoRequestSource
 Unique
 UniquePath
 UniquePathMethod
-- 
2.17.2 (Apple Git-113)

#271Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#261)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 20, 2019 at 7:57 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Aug 19, 2019 at 2:04 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Currently, In UnpackedUndoRecord we store all members directly which
are set by the caller. We store pointers to some header which are
allocated internally by the undo layer and the caller need not worry
about setting them. So now you are suggesting to put other headers
also as structures in UnpackedUndoRecord. I as such don't have much
problem in doing that but I think initially Robert designed
UnpackedUndoRecord structure this way so it will be good if Robert
provides his opinion on this.

I don't believe that's what is being suggested. It seems to me that
the thing Andres is complaining about here has roots in the original
sketch that I did for this code. The oldest version I can find is
here:

https://github.com/EnterpriseDB/zheap/commit/7d194824a18f0c5e85c92451beab4bc6f044254c

In this version, and I think still in the current version, there is a
two-stage marshaling strategy. First, the individual fields from the
UnpackedUndoRecord get copied into global variables (yes, that was my
fault, too, at least in part!) which are structures. Then, the
structures get copied into the target buffer. The idea of that design
was to keep the code simple, but it didn't really work out, because
things got a lot more complicated between the time I wrote those 3244
lines of code and the >3000 lines of code that live in this patch
today. One thing that change was that we moved more and more in the
direction of considering individual fields as separate objects to be
separately included or excluded, whereas when I wrote that code I
thought we were going to have groups of related fields that stood or
fell together. That idea turned out to be wrong. (There is the
even-larger question here of whether we ought to take Heikki's
suggestion and make this whole thing a lot more generic, but let's
start by discussing how the design that we have today could be
better-implemented.)

If I understand Andres correctly, he's arguing that we ought to get
rid of the two-stage marshaling strategy. During decoding, he wants
data to go directly from the buffer that contains it to the
UnpackedUndoRecord without ever being stored in the UnpackUndoContext.
During insertion, he wants data to go directly from the
UnpackedUndoRecord to the buffer that contains it. Or, at least, if
there has to be an intermediate format, he wants it to be just a chunk
of raw bytes, rather than a bunch of individual fields like we have in
UndoPackContext currently. I think that's a reasonable goal. I'm not
as concerned about it as he is from a performance point of view, but I
think it would make the code look nicer, and that would be good. If
we save CPU cycles along the way, that is also good.

In broad outline, what this means is:

1. Any field in the UndoPackContext that starts with urec_ goes away.
2. Instead of something like InsertUndoBytes((char *)
&(ucontext->urec_fxid), ...) we'd write InsertUndobytes((char *)
&uur->uur_fxid, ...).
3. Similarly instead of ReadUndoBytes((char *) &ucontext->urec_fxid,
...) we'd write ReadUndoBytes((char *) &uur->uur_fxid, ...).
4. It seems slightly trickier to handle the cases where we've got a
structure instead of individual fields, like urec_hd. But those could
be broken down into field-by-field reads and writes, e.g. in this case
one call for urec_type and a second for urec_info.
5. For uur_group and uur_logswitch, the code would need to allocate
those subsidiary structures before copying into them.

To me, that seems like it ought to be a pretty straightforward change
that just makes things simpler. We'd probably need to pass the
UnpackedUndoRecord to BeginUnpackUndo instead of FinishUnpackUndo, and
keep a pointer to it in the UnpackUndoContext, but that seems fine.
FinishUnpackUndo would end up just about empty, maybe entirely empty.

Is that a reasonable idea?

I have already attempted that part and I feel it is not making code
any simpler than what we have today. For packing, it's fine because I
can process all the member once and directly pack it into one memory
chunk and I can insert that to the buffer by one call of
InsertUndoBytes and that will make the code simpler.

But, while unpacking if I directly unpack to the UnpackUndoRecord then
there are few complexities. I am not saying those are difficult to
implement but code may not look better.

a) First, we need to add extra stages for unpacking as we need to do
field by field.

b) Some of the members like uur_payload and uur_tuple are not the same
type in the UnpackUndoRecord compared to how it is stored in the page.
In UnpackUndoRecord those are StringInfoData whereas on the page we
store it as UndoRecordPayload header followed by the actual data. I
am not saying we can not unpack this directly we can do it like,
first read the payload length from the page in uur_payload.len then
read tuple length in uur_tuple.len then read both the data. And, for
that, we will have to add extra stages.

c) Currently, in UnpackUndoContext the members are stored in the same
order in which we are storing them to the page whereas in
UnpackUndoRecord they are stored in the order such that they are more
convenient for them to understand, like all the fields which are set
by the caller are separate from the fields which are allocated
internally by the undo layer (transaction header and the log switch
header). Now, for directly unpacking to the UnpackUndoRecord, we need
to read them out of order which will make code more unreadable.

Another option could be that we unpack some part directly into the
UnapackUndoRecord (individual fields) and other parts to
UnpackUndoContext (structures, payload) and in Finalise only copy
those parts from UnpackUndoContext to UnapackUndoRecord. The code
might look bit confusing though.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#272Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#264)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Aug 20, 2019 at 8:10 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Aug 20, 2019 at 2:42 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

Well, my main point, which so far has largely been ignored, was that we
may not acquire page locks when we still need to search for victim
buffers later. If we don't need to lock the pages up-front, but only do
so once we're actually copying the records into the undo pages, then we
don't a separate phase to acquire the locks. We can still hold all of
the page locks at the same time, as long as we just acquire them at the
later stage.

Okay, IIUC, this means that we should have a separate phase where we
call LockUndoBuffers (or something like that) before
InsertPreparedUndo and after PrepareUndoInsert. The LockUndoBuffers
will lock all the buffers pinned during PrepareUndoInsert. We can
probably call LockUndoBuffers before entering the critical section to
avoid any kind of failure in critical section. If so, that sounds
reasonable to me.

I'm kind of scratching my head here, because this is clearly different
than what Andres said in the quoted text to which you were replying.
He clearly implied that we should acquire the buffer locks within the
critical section during InsertPreparedUndo, and you responded by
proposing to do it outside the critical section in a separate step.
Regardless of which way is actually better, when somebody says "hey,
let's do A!" and you respond by saying "sounds good, I'll go implement
B!" that's not really helping us to get toward a solution.

I got confused by the statement "We can still hold all of the page
locks at the same time, as long as we just acquire them at the later
stage."

FWIW, although I also thought of doing what you are describing here, I
think Andres's proposal is probably preferable, because it's simpler.
There's not really any reason why we can't take the buffer locks from
within the critical section, and that way callers don't have to deal
with the extra step.

IIRC, the reason this was done before starting critical section was
because of coding convention mentioned in src/access/transam/README
(Section: Write-Ahead Log Coding). It says first pin and exclusive
lock the shared buffers and then start critical section. It might be
that we can bypass that convention here, but I guess it is mainly to
avoid any error in the critical section. I have checked the
LWLockAcquire path and there doesn't seem to be any reason that it
will throw error except when the caller has acquired many locks at the
same time which is not the case here.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#273Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#271)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 21, 2019 at 3:55 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I have already attempted that part and I feel it is not making code
any simpler than what we have today. For packing, it's fine because I
can process all the member once and directly pack it into one memory
chunk and I can insert that to the buffer by one call of
InsertUndoBytes and that will make the code simpler.

OK...

But, while unpacking if I directly unpack to the UnpackUndoRecord then
there are few complexities. I am not saying those are difficult to
implement but code may not look better.

a) First, we need to add extra stages for unpacking as we need to do
field by field.

b) Some of the members like uur_payload and uur_tuple are not the same
type in the UnpackUndoRecord compared to how it is stored in the page.
In UnpackUndoRecord those are StringInfoData whereas on the page we
store it as UndoRecordPayload header followed by the actual data. I
am not saying we can not unpack this directly we can do it like,
first read the payload length from the page in uur_payload.len then
read tuple length in uur_tuple.len then read both the data. And, for
that, we will have to add extra stages.

I don't think that this is true; or at least, I think it's avoidable.
The idea of an unpacking stage is that you refuse to advance to the
next stage until you've got a certain number of bytes of data; and
then you unpack everything that pertains to that stage. If you have 2
4-byte fields that you want to unpack together, you can just wait
until you've 8 bytes of data and then unpack both. You don't really
need 2 separate stages. (Similarly, your concern about fields being
in a different order seems like it should be resolved by agreeing on
one ordering and having everything use it; I don't know why there
should be one order that is better in memory and another order that is
better on disk.)

The bigger issue here is that we don't seem to be making very much
progress toward improving the overall design. Several significant
improvements have been suggested:

1. Thomas suggested quite some time ago that we should make sure that
the transaction header is the first optional header. If we do that,
then I'm not clear on why we even need this incremental unpacking
stuff any more. The only reason for all of this machinery was so that
we could find the transaction header at an unknown offset inside a
complex record format; there is, if I understand correctly, no other
case in which we want to incrementally decode a record. But if the
transaction header is at a fixed offset, then there seems to be no
need to even have incremental decoding at all. Or it can be very
simple, with three stages: (1) we don't yet have enough bytes to
figure out how big the record is; (2) we have enough bytes to figure
out how big the record is and we have figured that out but we don't
yet have all of those bytes; and (3) we have the whole record, we can
decode the whole thing and we're done.

2. Based on a discussion with Thomas, I suggested the GHOB stuff,
which gets rid of the idea of transaction headers inside individual
records altogether; instead, there is one undo blob per transaction
(or maybe several if we overflow to another undo log) which begins
with a sentinel byte that identifies it as owned by a transaction, and
then the transaction header immediately follows that without being
part of any record, and the records follow that data. As with the
previous idea, this gets rid of the need for incremental decoding
because it gets rid of the need to find the transaction header inside
of a bigger record. As Thomas put it to me off-list, it puts the
records inside of a larger chunk of space owned by the transaction
instead of putting the transaction header inside of some particular
record; that seems more correct than the way we have it now.

3. Heikki suggested that the format should be much more generic and
that more should be left up to the AM. While neither Andres nor I are
convinced that we should go as far in that direction as Heikki is
proposing, the idea clearly has some merit, and we should probably be
moving that way to some degree. For instance, the idea that we should
store a block number and TID is a bit sketchy when you consider that a
multi-insert operation really wants to store a TID list. The zheap
tree has a ridiculous kludge to work around that problem; clearly we
need something better. We've also mentioned that, in the future, we
might want to support TIDs that are not 6 bytes, and that even just
looking at what's currently under development, zedstore wants to treat
TIDs as 48-bit opaque quantities, not a 4-byte block number and a
2-byte item pointer offset. So, there is clearly a need to go through
the whole way we're doing this and rethink which parts are generic and
which parts are AM-specific.

4. A related problem, which has been mentioned or at least alluded to
by both Heikki and by me, is that we need a better way of handling the
AM-specific data. Right now, the zheap code packs fixed-size things
into the payload data and then finds them by knowing the offset where
any particular data is within that field, but that's an unmaintainable
mess. The zheap code could be improved by at least defining those
offsets as constants someplace and adding some comments explaining the
payload formats of various undo records, but even if we do that, it's
not going to generalize very well to anything more complicated than a
few fixed-size bits of data. I suggested using the pqformat stuff to
try to structure that -- a suggestion to which Heikki has
unfortunately not responded, because I'd really like to get his
thoughts on it -- but whether we do that particular thing or not, I
think we need to do something. In addition to wanting a better way of
handling packing and unpacking for payload data, there's also a desire
to have it participate in record compression, for which we don't seem
to have any kind of plan.

5. Andres suggested multiple ideas for cleaning up and improving this
code in /messages/by-id/20190814065745.2faw3hirvfhbrdwe@alap3.anarazel.de
- which include the idea currently under discussion, several of the
same ideas that I mentioned above, and a number of other things, such
as making packing serialize to a char * rather than some ad-hoc
intermediate format and having a metadata array over which we can loop
rather than having multiple places where there's a separate bit of
code for every field type. I don't think those suggestions are
entirely unproblematic; for instance, the metadata array would would
probably work a lot better if we moved the transaction and log-switch
headers outside of individual records as suggested in (2) above.
Otherwise, the metadata would have to include not only data-structure
offsets but some kind of a flag indicating which of several data
structures ought to contain the relevant information, which would make
the whole thing a lot messier. And depending on what we do about (4),
this might become moot or the details might change quite a bit,
because if we no longer have a long list of "generic" fields, then we
also won't have a bunch of places in the code that deal with that long
list of generic fields, which means the metadata array might not be
necessary, or might be simpler or smaller or designed differently.
All of which is to make the point that responding to Andres's feedback
will require a bunch of decisions about which parts of the feedback to
take (because some of them are mutually exclusive, as he acknowledges
himself) and what to do about them (because some of them are vague);
yet, taken together, they seem to amount to the need for significant
design changes, as do (1)-(4).

Now, just to be clear, the code we're talking about here is mostly
based on an original design by me, and whatever defects were present
in that original design are nobody's fault but mine. And that list of
defects includes pretty much everything in the above list. But, what
we need to figure out at this point is how we're going to get those
things fixed, and it seems to me that we're going to need a pretty
substantial redesign, but this discussion is kind of down in the
weeds. I mean, what are we gaining by arguing about how many stages
we need for incremental unpacking if the real thing we need to is get
rid of that concept altogether?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#274Robert Haas
robertmhaas@gmail.com
In reply to: Amit Kapila (#272)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 21, 2019 at 6:38 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

FWIW, although I also thought of doing what you are describing here, I
think Andres's proposal is probably preferable, because it's simpler.
There's not really any reason why we can't take the buffer locks from
within the critical section, and that way callers don't have to deal
with the extra step.

IIRC, the reason this was done before starting critical section was
because of coding convention mentioned in src/access/transam/README
(Section: Write-Ahead Log Coding). It says first pin and exclusive
lock the shared buffers and then start critical section. It might be
that we can bypass that convention here, but I guess it is mainly to
avoid any error in the critical section. I have checked the
LWLockAcquire path and there doesn't seem to be any reason that it
will throw error except when the caller has acquired many locks at the
same time which is not the case here.

Yeah, I think it's fine to deviate from that convention in this
respect. We treat LWLockAcquire() as a no-fail operation in many
places; in my opinion, that elog(ERROR) that we have for too many
LWLocks should be changed to elog(PANIC) precisely because we do treat
LWLockAcquire() as no-fail in lots of places in the code, but I think
I suggested that once and got slapped down, and I haven't had the
energy to fight about it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#275Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#274)
Re: POC: Cleaning up orphaned files using undo logs

On August 21, 2019 8:36:34 AM PDT, Robert Haas <robertmhaas@gmail.com> wrote:

We treat LWLockAcquire() as a no-fail operation in many
places; in my opinion, that elog(ERROR) that we have for too many
LWLocks should be changed to elog(PANIC) precisely because we do treat
LWLockAcquire() as no-fail in lots of places in the code, but I think
I suggested that once and got slapped down, and I haven't had the
energy to fight about it.

Fwiw, that proposal has my vote.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

#276Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#240)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 14, 2019 at 2:57 AM Andres Freund <andres@anarazel.de> wrote:

- My reading of the current xact.c integration is that it's not workable
as is. Undo is executed outside of a valid transaction state,
exceptions aren't properly undone, logic would need to be duplicated
to a significant degree, new kind of critical section.

Regarding this particular point:

ReleaseResourcesAndProcessUndo() is only supposed to be called after
AbortTransaction(), and the additional steps it performs --
AtCleanup_Portals() and AtEOXact_Snapshot() or alternatively
AtSubCleanup_Portals -- are taken from Cleanup(Sub)Transaction.
That's not crazy; the other steps in Cleanup(Sub)Transaction() look
like stuff that's intended to be performed when we're totally done
with this TransactionState stack entry, whereas these things are
slightly higher-level cleanups that might even block undo (e.g.
undropped portal prevents orphaned file cleanup). Granted, there are
no comments explaining why those particular cleanup steps are
performed here, and it's possible some other approach is better, but I
think perhaps it's not quite as flagrantly broken as you think.

I am also not convinced that semi-critical sections are a bad idea,
although the if (SemiCritSectionCount > 0) test at the start of
ReleaseResourcesAndProcessUndo() looks wrong. To roll back a
subtransaction, we must perform undo in the foreground, and if that
fails, the toplevel transaction can't be allowed to commit, full stop.
Since we expect this to be a (very) rare scenario, I don't know why
escalating to FATAL is a catastrophe. The only other option is to do
something along the lines of SxactIsDoomed(), where we force all
subsequent commits (and sub-commits?) within the toplevel xact to
fail. You can argue that the latter is a better user experience, and
for SSI I certainly agree, but this case isn't quite the same: there's
a good chance we're dealing with a corrupted page or system
administrator intervention to try to kill off a long-running undo
task, and continuing in such cases seems a lot more dubious than after
a serializability failure, where retrying is the expected recovery
mode. The other case is where toplevel undo for a temporary table
fails. It is unclear to me what, other than FATAL, could suffice
there. I guess you could just let the session continue and leave the
transaction undone, leaving whatever MVCC machinery the table AM may
have look through it, but that sounds inferior to me. Rip the bandaid
off.

Some general complaints from my side about the xact.c changes:

1. The code structure doesn't seem quite right. For example:

1a. ProcessUndoRequestForEachLogCat has a try/catch block, but it
seems to me that the job of a try/catch block is to provide structured
error-handling for code for resources for which there's no direct
handling in xact.c or resowner.c. Here, we're inside of xact.c, so
why are we adding a try/catch block
1b. ReleaseResourcesAndProcessUndo does part of the work of cleaning
up a failed transaction but not all of it, the rest being done by
AbortTransaction, which is called before entering it, plus it also
kicks off the actual undo work. I would expect a cleaner division of
responsibility.
1c. Having an undo request per UndoLogCategory rather than one per
transaction doesn't seem right to me; hopefully that will get cleaned
up when the undorequest.c stuff I sent before is integrated.
1d. The code at the end of FinishPreparedTransaction() seems to expect
that the called code will catch any error, but that clearing the error
state might need to happen here, and also that we should fire up a new
transaction; I suspect, but am not entirely sure, that that is not the
way it should work. The code added earlier in that function also
looks suspicious, because it's filling up what is basically a
high-level control function with a bunch of low-level undo-specific
details. In both places, the undo-specific concerns probably need to
be better-isolated.

2. Signaling is done using some odd-looking mechanisms. For instance:

2a. The SemiCritSectionCount > 0 test at the top of
ReleaseResourcesAndProcessUndo that I complained about earlier looks
like a guard against reentrancy, but that must be the wrong way to get
there; it makes it impossible to reuse what is ostensibly a
general-purpose facility for any non-undo related purpose without
maybe breaking something.
2b. ResetUndoActionsInfo() is called from a bunch of place, but only 2
of those places have a comment explaining why, and the function
comment is pretty unilluminating. This looks like some kind of
signaling machinery, but it's not very clear to me what it's actually
trying to do.
2c. ResourceOwnerReleaseInternal() is directly calling
NeedToPerformUndoActions(), which feels like a layering violation.
2d. I'm not really sure that TRANS_UNDO is serving any useful purpose;
I think we need TBLOCK_UNDO and TBLOCK_SUBUNDO, but I'm not really
sure TRANS_UNDO is doing anything useful; the change to
SubTransactionIsActive() looks wrong to me, and the other changes I
think would mostly go away if we just used TRANS_INPROGRESS.
2e. I'm skeptical that the err_out_to_client() stuff is the right way
to suppress undo failure messages from being sent to the client. That
needs to be done, but this doesn't seem like the right way. This is
related to my complaint above about using a try/catch block inside
xact.c.

3. I noticed a few other mistakes when reading through this again
which I include here for the sake of completeness:

3a. memset(..., InvalidUndoRecPtr, ...) will only happen to work if
every byte of InvalidUndoRecPtr happens to have the same value. That
happens to be true, because it's defined 8 bytes of zeroes, but it's
not OK to code it like this.
3b. "undoRequestResgistered" is a typo.
3c. GetEpochForXid definitely shouldn't exist any more... as has been
reported in the past.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#277Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#273)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 21, 2019 at 9:04 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Aug 21, 2019 at 3:55 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

I have already attempted that part and I feel it is not making code
any simpler than what we have today. For packing, it's fine because I
can process all the member once and directly pack it into one memory
chunk and I can insert that to the buffer by one call of
InsertUndoBytes and that will make the code simpler.

OK...

The bigger issue here is that we don't seem to be making very much
progress toward improving the overall design. Several significant
improvements have been suggested:

1. Thomas suggested quite some time ago that we should make sure that
the transaction header is the first optional header.

I think this is already done at least 2-3 version before. So now we
are updating the transaction header directly by writing at that offset
and we don't need staging for this.

If we do that,
then I'm not clear on why we even need this incremental unpacking
stuff any more. The only reason for all of this machinery was so that
we could find the transaction header at an unknown offset inside a
complex record format; there is, if I understand correctly, no other
case in which we want to incrementally decode a record.
But if the
transaction header is at a fixed offset, then there seems to be no
need to even have incremental decoding at all. Or it can be very
simple, with three stages: (1) we don't yet have enough bytes to
figure out how big the record is; (2) we have enough bytes to figure
out how big the record is and we have figured that out but we don't
yet have all of those bytes; and (3) we have the whole record, we can
decode the whole thing and we're done.

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset. But, maybe we can still follow this idea which will make
unpacking far simpler. I have a few ideas
a) Store payload header right after the transaction header so that we
can easily know the complete record size.
b) Once we decode the first header by uur_info we can compute an exact
offset of the payload header and from there we can know the record
size.

2. Based on a discussion with Thomas, I suggested the GHOB stuff,
which gets rid of the idea of transaction headers inside individual
records altogether; instead, there is one undo blob per transaction
(or maybe several if we overflow to another undo log) which begins
with a sentinel byte that identifies it as owned by a transaction, and
then the transaction header immediately follows that without being
part of any record, and the records follow that data. As with the
previous idea, this gets rid of the need for incremental decoding
because it gets rid of the need to find the transaction header inside
of a bigger record. As Thomas put it to me off-list, it puts the
records inside of a larger chunk of space owned by the transaction
instead of putting the transaction header inside of some particular
record; that seems more correct than the way we have it now.

3. Heikki suggested that the format should be much more generic and
that more should be left up to the AM. While neither Andres nor I are
convinced that we should go as far in that direction as Heikki is
proposing, the idea clearly has some merit, and we should probably be
moving that way to some degree. For instance, the idea that we should
store a block number and TID is a bit sketchy when you consider that a
multi-insert operation really wants to store a TID list. The zheap
tree has a ridiculous kludge to work around that problem; clearly we
need something better. We've also mentioned that, in the future, we
might want to support TIDs that are not 6 bytes, and that even just
looking at what's currently under development, zedstore wants to treat
TIDs as 48-bit opaque quantities, not a 4-byte block number and a
2-byte item pointer offset. So, there is clearly a need to go through
the whole way we're doing this and rethink which parts are generic and
which parts are AM-specific.

4. A related problem, which has been mentioned or at least alluded to
by both Heikki and by me, is that we need a better way of handling the
AM-specific data. Right now, the zheap code packs fixed-size things
into the payload data and then finds them by knowing the offset where
any particular data is within that field, but that's an unmaintainable
mess. The zheap code could be improved by at least defining those
offsets as constants someplace and adding some comments explaining the
payload formats of various undo records, but even if we do that, it's
not going to generalize very well to anything more complicated than a
few fixed-size bits of data. I suggested using the pqformat stuff to
try to structure that -- a suggestion to which Heikki has
unfortunately not responded, because I'd really like to get his
thoughts on it -- but whether we do that particular thing or not, I
think we need to do something. In addition to wanting a better way of
handling packing and unpacking for payload data, there's also a desire
to have it participate in record compression, for which we don't seem
to have any kind of plan.

5. Andres suggested multiple ideas for cleaning up and improving this
code in /messages/by-id/20190814065745.2faw3hirvfhbrdwe@alap3.anarazel.de
- which include the idea currently under discussion, several of the
same ideas that I mentioned above, and a number of other things, such
as making packing serialize to a char * rather than some ad-hoc
intermediate format

I have implemented this patch. I will post this along with other changes.

and having a metadata array over which we can loop

rather than having multiple places where there's a separate bit of
code for every field type. I don't think those suggestions are
entirely unproblematic; for instance, the metadata array would would
probably work a lot better if we moved the transaction and log-switch
headers outside of individual records as suggested in (2) above.
Otherwise, the metadata would have to include not only data-structure
offsets but some kind of a flag indicating which of several data
structures ought to contain the relevant information, which would make
the whole thing a lot messier. And depending on what we do about (4),
this might become moot or the details might change quite a bit,
because if we no longer have a long list of "generic" fields, then we
also won't have a bunch of places in the code that deal with that long
list of generic fields, which means the metadata array might not be
necessary, or might be simpler or smaller or designed differently.
All of which is to make the point that responding to Andres's feedback
will require a bunch of decisions about which parts of the feedback to
take (because some of them are mutually exclusive, as he acknowledges
himself) and what to do about them (because some of them are vague);
yet, taken together, they seem to amount to the need for significant
design changes, as do (1)-(4).

Now, just to be clear, the code we're talking about here is mostly
based on an original design by me, and whatever defects were present
in that original design are nobody's fault but mine. And that list of
defects includes pretty much everything in the above list. But, what
we need to figure out at this point is how we're going to get those
things fixed, and it seems to me that we're going to need a pretty
substantial redesign, but this discussion is kind of down in the
weeds. I mean, what are we gaining by arguing about how many stages
we need for incremental unpacking if the real thing we need to is get
rid of that concept altogether?

Actually, In my local changes, I have already got rid of the multiple
stages because I am packing all fields in one char * as suggested in
the first part of the 4). I had a problem while unpacking because we
don't know the complete size of the record beforehand especially
because of the payload data. I have suggested a couple of points
above as part of 1) for handling the payload size.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#278Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#277)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-22 09:51:22 +0530, Dilip Kumar wrote:

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset.

Wait, but that's just purely self inflicted damage, no? The initial
length just needs to include the payload. And all this is not an issue
anymore?

Greetings,

Andres Freund

#279Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#278)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 9:58 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 09:51:22 +0530, Dilip Kumar wrote:

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset.

Wait, but that's just purely self inflicted damage, no? The initial
length just needs to include the payload. And all this is not an issue
anymore?

Actually, we store the undo length only at the end of the record and
that is for traversing the transaction's undo record chain during bulk
fetch. Ac such in the beginning of the record we don't have the undo
length. We do have uur_info but that just tell us which all optional
header are included in the record.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#280Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#279)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On 2019-08-22 10:19:04 +0530, Dilip Kumar wrote:

On Thu, Aug 22, 2019 at 9:58 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 09:51:22 +0530, Dilip Kumar wrote:

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset.

Wait, but that's just purely self inflicted damage, no? The initial
length just needs to include the payload. And all this is not an issue
anymore?

Actually, we store the undo length only at the end of the record and
that is for traversing the transaction's undo record chain during bulk
fetch. Ac such in the beginning of the record we don't have the undo
length. We do have uur_info but that just tell us which all optional
header are included in the record.

But why? It makes a *lot* more sense to have it in the beginning. I
don't think bulk-fetch really requires it to be in the end - we can
still process records forward on a page-by-page basis.

Greetings,

Andres Freund

#281Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#280)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 10:24 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 10:19:04 +0530, Dilip Kumar wrote:

On Thu, Aug 22, 2019 at 9:58 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 09:51:22 +0530, Dilip Kumar wrote:

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset.

Wait, but that's just purely self inflicted damage, no? The initial
length just needs to include the payload. And all this is not an issue
anymore?

Actually, we store the undo length only at the end of the record and
that is for traversing the transaction's undo record chain during bulk
fetch. Ac such in the beginning of the record we don't have the undo
length. We do have uur_info but that just tell us which all optional
header are included in the record.

But why? It makes a *lot* more sense to have it in the beginning. I
don't think bulk-fetch really requires it to be in the end - we can
still process records forward on a page-by-page basis.

Yeah, we can handle the bulk fetch as you suggested and it will make
it a lot easier. But, currently while registering the undo request
(especially during the first pass) we need to compute the from_urecptr
and the to_urecptr. And, for computing the from_urecptr, we have the
end location of the transaction because we have the uur_next in the
transaction header and that will tell us the end of our transaction
but we still don't know the undo record pointer of the last record of
the transaction. As of know, we read previous 2 bytes from the end of
the transaction to know the length of the last record and from there
we can compute the undo record pointer of the last record and that is
our from_urecptr.

Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#282Amit Kapila
amit.kapila16@gmail.com
In reply to: Dilip Kumar (#281)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 11:04 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Aug 22, 2019 at 10:24 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 10:19:04 +0530, Dilip Kumar wrote:

On Thu, Aug 22, 2019 at 9:58 AM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2019-08-22 09:51:22 +0530, Dilip Kumar wrote:

We can not know the complete size of the record even by reading the
header because we have a payload that is variable part and payload
length are stored in the payload header which again can be at random
offset.

Wait, but that's just purely self inflicted damage, no? The initial
length just needs to include the payload. And all this is not an issue
anymore?

Actually, we store the undo length only at the end of the record and
that is for traversing the transaction's undo record chain during bulk
fetch. Ac such in the beginning of the record we don't have the undo
length. We do have uur_info but that just tell us which all optional
header are included in the record.

But why? It makes a *lot* more sense to have it in the beginning. I
don't think bulk-fetch really requires it to be in the end - we can
still process records forward on a page-by-page basis.

Yeah, we can handle the bulk fetch as you suggested and it will make
it a lot easier. But, currently while registering the undo request
(especially during the first pass) we need to compute the from_urecptr
and the to_urecptr. And, for computing the from_urecptr, we have the
end location of the transaction because we have the uur_next in the
transaction header and that will tell us the end of our transaction
but we still don't know the undo record pointer of the last record of
the transaction. As of know, we read previous 2 bytes from the end of
the transaction to know the length of the last record and from there
we can compute the undo record pointer of the last record and that is
our from_urecptr.

How about if we store the location of the last record of the
transaction instead of the location of the next transaction in the
transaction header? I think if we do that then discard worker might
need to do some additional work in some cases as it needs to tell the
location up to which discard is possible, however, many other cases
might get simplified. With this also, when the log is switched while
writing records for the same transaction, the transaction header in
the first log will store the start location of the same transaction's
records in the next log.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#283Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#280)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 12:54 AM Andres Freund <andres@anarazel.de> wrote:

But why? It makes a *lot* more sense to have it in the beginning. I
don't think bulk-fetch really requires it to be in the end - we can
still process records forward on a page-by-page basis.

There are two separate needs here: to be able to go forward, and to be
able to go backward. We have the length at the end of each record not
because we're stupid, but so that we can back up. If we have another
way of backing up, then the thing to do is not to move that to
beginning of the record but to remove it entirely as unnecessary
wastage. We can also think about how to improve forward traversal.

Considering each problem separately:

For forward traversal, we could simplify things somewhat by having
only 3 decoding stages instead of N decoding stages. We really only
need (1) a stage for accumulating bytes until we have uur_info, and
then (2) a stage for accumulating bytes until we know the payload and
tuple lengths, and then (3) a stage for accumulating bytes until we
have the whole record. We have a lot more stages than that right now
but I don't think we really need them for anything. Originally we had
them so that we could do incremental decoding to find the transaction
header in the record, but now that the transaction header is at a
fixed offset, I think the multiplicity of stages is just baggage.

We could simplify things more by deciding that the first two bytes of
the record are going to contain the record size. That would increase
the size of the record by 2 bytes, but we could (mostly) claw those
bytes back by not storing the size of both uur_payload and uur_tuple.
The size of the other one would be computed by subtraction: take the
total record size, subtract the size of whichever of those two things
we store, subtract the mandatory and optional headers that are
present, and the rest must be the other value. That would still add 2
bytes for records that contain neither a payload nor a tuple, but that
would probably be OK given that (a) a lot of records wouldn't be
affected, (b) the code would get simpler, and (c) something like this
seems necessary anyway given that we want to make the record format
more generic. With this approach instead of 3 stages we only need 2:
(1) accumulating bytes until we have the 2-byte length word, and (2)
accumulating bytes until we have the whole record.

For backward traversal, as I see it, there are basically two options.
One is to do what we're doing right now, and store the record length
at the end of the record. (That might mean that a record both begins
and ends with its own length, which is not a crazy design.) The other
is to do what I think you are proposing here: locate the beginning of
the first record on the page, presumably based on some information
stored in the page header, and then work forward through the page to
figure out where all the records start. Then process them in reverse
order. That saves 2 bytes per record. It's a little more expensive in
terms of CPU cycles, especially if you only need some of the records
on the page but not all of them, but that's probably not too bad.

I think I'm basically agreeing with what you are proposing but I think
it's important to spell out the underlying concerns, because otherwise
I'm afraid we might think we have a meeting of the minds when we don't
really.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#284Robert Haas
robertmhaas@gmail.com
In reply to: Dilip Kumar (#281)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 1:34 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Yeah, we can handle the bulk fetch as you suggested and it will make
it a lot easier. But, currently while registering the undo request
(especially during the first pass) we need to compute the from_urecptr
and the to_urecptr. And, for computing the from_urecptr, we have the
end location of the transaction because we have the uur_next in the
transaction header and that will tell us the end of our transaction
but we still don't know the undo record pointer of the last record of
the transaction. As of know, we read previous 2 bytes from the end of
the transaction to know the length of the last record and from there
we can compute the undo record pointer of the last record and that is
our from_urecptr.=

I don't understand this. If we're registering an undo request at "do"
time, we don't need to compute the starting location; we can just
remember the UndoRecPtr of the first record we inserted. If we're
reregistering an undo request after a restart, we can (and, I think,
should) work forward from the discard location rather than backward
from the insert location.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#285Dilip Kumar
dilipbalaut@gmail.com
In reply to: Robert Haas (#284)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 7:34 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Aug 22, 2019 at 1:34 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Yeah, we can handle the bulk fetch as you suggested and it will make
it a lot easier. But, currently while registering the undo request
(especially during the first pass) we need to compute the from_urecptr
and the to_urecptr. And, for computing the from_urecptr, we have the
end location of the transaction because we have the uur_next in the
transaction header and that will tell us the end of our transaction
but we still don't know the undo record pointer of the last record of
the transaction. As of know, we read previous 2 bytes from the end of
the transaction to know the length of the last record and from there
we can compute the undo record pointer of the last record and that is
our from_urecptr.=

I don't understand this. If we're registering an undo request at "do"
time, we don't need to compute the starting location; we can just
remember the UndoRecPtr of the first record we inserted. If we're
reregistering an undo request after a restart, we can (and, I think,
should) work forward from the discard location rather than backward
from the insert location.

Right, we work froward from the discard location. So after the
discard location, while traversing the undo log when we encounter an
aborted transaction we need to register its rollback request. And,
for doing that we need 1) start location of the first undo record . 2)
start location of the last undo record (last undo record pointer).

We already have 1). But we have to compute 2). For doing that if we
unpack the first undo record we will know the start of the next
transaction. From there if we read the last two bytes then that will
have the length of the last undo record of our transaction. So we can
compute 2) with below formula

start of the last undo record = start of the next transaction - length
of our transaction's last record.

Am I making sense here?

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#286Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#285)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 9:21 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Aug 22, 2019 at 7:34 PM Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Aug 22, 2019 at 1:34 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

Yeah, we can handle the bulk fetch as you suggested and it will make
it a lot easier. But, currently while registering the undo request
(especially during the first pass) we need to compute the from_urecptr
and the to_urecptr. And, for computing the from_urecptr, we have the
end location of the transaction because we have the uur_next in the
transaction header and that will tell us the end of our transaction
but we still don't know the undo record pointer of the last record of
the transaction. As of know, we read previous 2 bytes from the end of
the transaction to know the length of the last record and from there
we can compute the undo record pointer of the last record and that is
our from_urecptr.=

I don't understand this. If we're registering an undo request at "do"
time, we don't need to compute the starting location; we can just
remember the UndoRecPtr of the first record we inserted. If we're
reregistering an undo request after a restart, we can (and, I think,
should) work forward from the discard location rather than backward
from the insert location.

Right, we work froward from the discard location. So after the
discard location, while traversing the undo log when we encounter an
aborted transaction we need to register its rollback request. And,
for doing that we need 1) start location of the first undo record . 2)
start location of the last undo record (last undo record pointer).

We already have 1). But we have to compute 2). For doing that if we
unpack the first undo record we will know the start of the next
transaction. From there if we read the last two bytes then that will
have the length of the last undo record of our transaction. So we can
compute 2) with below formula

start of the last undo record = start of the next transaction - length
of our transaction's last record.

Maybe I am saying that because I am just thinking how the requests are
registered as per the current code. But, those requests will
ultimately be used for collecting the record by the bulk fetch. So if
we are planning to change the bulk fetch to read forward then maybe we
don't need the valid last undo record pointer because that we will
anyway get while processing forward. So just knowing the end of the
transaction is sufficient for us to know where to stop. I am not sure
if this solution has any problem. Probably I should think again in
the morning when my mind is well-rested.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#287Andres Freund
andres@anarazel.de
In reply to: Dilip Kumar (#286)
Re: POC: Cleaning up orphaned files using undo logs

Hi

On August 22, 2019 9:14:10 AM PDT, Dilip Kumar <dilipbalaut@gmail.com> wrote:

But, those requests will
ultimately be used for collecting the record by the bulk fetch. So if
we are planning to change the bulk fetch to read forward then maybe we
don't need the valid last undo record pointer because that we will
anyway get while processing forward. So just knowing the end of the
transaction is sufficient for us to know where to stop. I am not sure
if this solution has any problem. Probably I should think again in
the morning when my mind is well-rested.

I don't think we can easily do so for bulk apply without incurring significant overhead. It's pretty cheap to read in forward order and then process backwards on a page level - but for an entire transactions undo the story is different. We can't necessarily keep all of it in memory, so we'd have to read the undo twice to find the end. Right?

Andres

Andres

Andres
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

#288Dilip Kumar
dilipbalaut@gmail.com
In reply to: Andres Freund (#287)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Aug 22, 2019 at 9:55 PM Andres Freund <andres@anarazel.de> wrote:

Hi

On August 22, 2019 9:14:10 AM PDT, Dilip Kumar <dilipbalaut@gmail.com> wrote:

But, those requests will
ultimately be used for collecting the record by the bulk fetch. So if
we are planning to change the bulk fetch to read forward then maybe we
don't need the valid last undo record pointer because that we will
anyway get while processing forward. So just knowing the end of the
transaction is sufficient for us to know where to stop. I am not sure
if this solution has any problem. Probably I should think again in
the morning when my mind is well-rested.

I don't think we can easily do so for bulk apply without incurring significant overhead. It's pretty cheap to read in forward order and then process backwards on a page level - but for an entire transactions undo the story is different. We can't necessarily keep all of it in memory, so we'd have to read the undo twice to find the end. Right?

I was not talking about the entire transaction, I was also telling
about the page level as you suggested. I was just saying that we may
not need the start position of the last undo record of the transaction
for registering the rollback request (which we currently do).
However, we need to know the end of the transaction to know the last
page from which we need to start reading forward.

Let me explain with an example

Transaction1
first, undo start at 10
first, undo end at 100
second, undo start at 101
second, undo end at 200
......
last, undo start at 1000
last, undo end at 1100

Transaction2
first, undo start at 1101
first, undo end at 1200
second, undo start at 1201
second, undo end at 1300

Suppose we want to register the request for Transaction1. Then
currently we need to know the start undo record pointer (10 as per
above example) and the last undo record pointer (1000). But, we only
know the start undo record pointer(10) and the start of the next
transaction(1101). So for calculating the start of the last record,
we use 1101 - 101 (length of the last record store 2 bytes before
1101).

So, now I am saying that maybe we don't need to compute the start of
last undo record (1000) because it's enough to know the end of the
last undo record(1100). Because on whichever page the last undo
record ends, we can start from that page and read forward on that
page.

* All numbers I used in the above example can be considered as undo
record pointers.

--
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com

#289Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#266)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 21, 2019 at 4:44 AM Andres Freund <andres@anarazel.de> wrote:

On 2019-08-20 21:02:18 +1200, Thomas Munro wrote:

1. Anyone is allowed to try to read or write data at any UndoRecPtr
that has been allocated, through the buffer pool (though you'd usually
want to check it with UndoRecPtrIsDiscarded() first, and only rely on
the system I'm describing to deal with races).

2. ReadBuffer() might return InvalidBuffer. This can happen for a
cache miss, if the smgrread implementation wants to indicate that the
buffer has been discarded/truncated and that is expected (md.c won't
ever do that, but undofile.c can).

Hm. This gives me a bit of a stomach ache. It somehow feels like a weird
form of signalling. Can't quite put my finger on why it makes me feel
queasy.

Well, if we're going to allow concurrent access and discarding, there
has to be some part of the system where you can discover that the
thing you wanted is gone. What's wrong with here?

Stepping back a bit... why do we have a new concept here? The reason
we don't have anything like this for relations currently is that we
don't have live references to blocks that are expected to be
concurrently truncated, due to heavyweight locking. But the whole
purpose of the undo system is to allow cheap truncation/discarding of
data that you *do* have live references to, and furthermore the
discarding is expected to be frequent. The present experiment is
about trying to do so without throwing our hands up and using a
pessimistic lock.

At one point Robert and I discussed some kind of scheme where you'd
register your interest in a range of the log before you begin reading
(some kind of range locks or hazard pointers), so that you would block
discarding in that range, but the system would still allow someone to
read in the middle of the log while the discard worker concurrently
discards non-overlapping data at the end. But I kept returning to the
idea that the buffer pool already has block-level range locking of
various kinds. You can register your interest in a range by pinning
the buffers. That's when you'll find out if the range is already
gone. We could add an extra layer of range locking around that, but
it wouldn't be any better, it'd just thrash your bus a bit more, and
require more complexity in the discard worker (it has to defer
discarding a bit, and either block or go away and come back later).

3. UndoLogDiscard() uses DiscardBuffer() to invalidate any currently
unpinned buffers, and marks as BM_DISCARDED any that happen to be
pinned right now, so they can't be immediately invalidated. Such
buffers are never written back and are eligible for reuse on the next
clock sweep, even if they're written into by a backend that managed to
do that when we were trying to discard.

Hm. When is it legitimate for a backend to write into such a buffer? I
guess that's about updating the previous transaction's next pointer? Or
progress info?

Yes, previous transaction header's next pointer, and progress counter
during rollback. We're mostly interested in the next pointer here,
because the progress counter update would normally not be updated at a
time when the page might be concurrently discarded. The exception to
that is a superuser running CALL pg_force_discard_undo() (a
data-eating operation designed to escape a system that can't
successfully roll back and gets stuck, blowing away
not-yet-rolled-back undo records).

Here are some other ideas about how to avoid conflicts between
discarding and transaction header update:

1. Lossy self-update-only: You could say that transactions are only
allowed to write to their own transaction header, and then have them
periodically update their own length in their own transaction header,
and then teach the discard worker that the length information is only
a starting point for a linear search for the next transaction based on
page header information. That removes complexity for writers, but
adds complexity and IO and CPU to the discard worker. Bleugh.

2. Strict self-update-only: We could update it as part of
transaction cleanup. That is, when you commit or abort, probably at
some time when your transaction is still advertised as running, you go
and update your own transaction header with your the size. If you
never reach that stage, I think you can fix it during crash recovery,
during the startup scan that feeds the rollback request queues. That
is, if you encounter a transaction header with length == 0, it must be
the final one and its length is therefore known and can be updated,
before you allow new transactions to begin. There are some
complications like backends that exit without crashing, which I
haven't thought about. As Amit just pointed out to me, that means
that the update is not folded into the same buffer access as the next
transaction, but perhaps you can mitigate that by not updating your
header if the next header will be on the same page -- the next
transaction can do it safely then (this page with the insert pointer
on it can't be discarded). As Dilip just pointed out to me, it means
that you always do an update that you might not never need to do if
the transaction is discarded, to which I have no answer. Bleugh.

3. Perhaps there is a useful middle ground between ideas 1 and 2: if
it's 0, the discard worker will perform a scan of page headers to
compute the correct value, but if it's non-zero it will consider it to
be correct and trust that value. The extra work would only happen
after crash recovery or things like elog(FATAL, ...).

4. You could keep one extra transaction around all the time. That
is, because we know we only ever want to stamp the transaction header
of the previous transaction, don't let a transaction that hasn't been
stamped with a length yet be discarded. But now we waste more space!

5. You could figure out a scheme to advertise the block number of the
start of the previous transaction. You could have an LWLock that you
have to take to stamp the transaction header of the previous
transaction, and UndoLogDiscard() only has to take the lock if it
wants to discard a range that overlaps with that block. This avoids
contention for some workloads, but not others, so it seems like a half
measure, and again you still have to deal with InvalidBuffer when
reading. It's basically range locking; the buffer pool is already a
kind of range locking scheme!

These schemes are all about avoiding conflicts between discarding and
writing, but you'd still have to tolerate InvalidBuffer for reads (ie
reading zheap records) with this scheme, so I suppose you might as
well just treat updates the same and not worry about any of the above.

5. Separating begin from discard allows the WAL logging for
UndoLogDiscard() to do filesystem actions before logging, and other
effects after logging, which have several nice properties if you work
through the various crash scenarios.

Hm. ISTM we always need to log before doing some filesystem operation
(see also my recent complaint Robert and I are discussing at the bottom
of [1]). It's just that we can have a separate stage afterwards?

[1] /messages/by-id/CA+TgmoZc5JVYORsGYs8YnkSxUC=cLQF1Z+fcpH2TTKvqkS7MFg@mail.gmail.com

I talked about this a bit with Robert and he pointed out that it's
probably not actually necessary to WAL-log these operations at all,
now that 'begin' and 'end' (= physical storage range) have been
separated from 'discard' and 'insert' (active undo data range).
Instead you could do it like this:

1. Maintain begin and end pointers in shared memory only, no WAL, no
checkpoint.
2. Compute their initial values by scanning the filesystem at startup time.
3. Treat (logical) discard and insert pointers as today; WAL before
shm, checkpoint.
4. begin must be <= discard, and end must be >= insert, or else PANIC.

I'm looking into that.

So now I'd like to get feedback on the sanity of this scheme. I'm not
saying it doesn't have bugs right now -- I've been trying to figure
out good ways to test it and I'm not quite there yet -- but the
concept. One observation I have is that there were already code paths
in undoaccess.c that can tolerate InvalidBuffer in recovery, due to
the potentially different discard timing for DO vs REDO. I think
that's a point in favour of this scheme, but I can see that it's
inconvenient to have to deal with InvalidBuffer whenever you read.

FWIW, I'm far from convinced that those are currently quite right. See
discussion pointed to above.

Yeah. It seems highly desirable to make it so that all decisions
about whether a write to an undo block is required or should be
skipped are made on the primary, so that WAL reply just does what it's
told. I am working on that.

--
Thomas Munro
https://enterprisedb.com

#290Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#289)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Aug 23, 2019 at 2:04 AM Thomas Munro <thomas.munro@gmail.com> wrote:

2. Strict self-update-only: We could update it as part of
transaction cleanup. That is, when you commit or abort, probably at
some time when your transaction is still advertised as running, you go
and update your own transaction header with your the size. If you
never reach that stage, I think you can fix it during crash recovery,
during the startup scan that feeds the rollback request queues. That
is, if you encounter a transaction header with length == 0, it must be
the final one and its length is therefore known and can be updated,
before you allow new transactions to begin. There are some
complications like backends that exit without crashing, which I
haven't thought about. As Amit just pointed out to me, that means
that the update is not folded into the same buffer access as the next
transaction, but perhaps you can mitigate that by not updating your
header if the next header will be on the same page -- the next
transaction can do it safely then (this page with the insert pointer
on it can't be discarded). As Dilip just pointed out to me, it means
that you always do an update that you might not never need to do if
the transaction is discarded, to which I have no answer. Bleugh.

Andres and I have spent a lot of time on the phone over the last
couple of days and I think we both kind of like this option. I don't
think that the costs are likely to be very significant: you're talking
about pinning, locking, dirtying, unlocking, and unpinning one buffer
at commit time, or maybe two if your transaction touched both logged
and unlogged tables. If the transaction is short enough for that
overhead to matter, that buffer is probably already in shared_buffers,
is probably already dirty, and is probably already in your CPU's
cache. So I think the overhead will turn out to be low.

Moreover, I doubt that we want to separately discard every transaction
anyway. If you have very light-weight transactions, you don't want to
add an extra WAL record per transaction anyway. Increasing the number
of separate WAL records per transaction from say 5 to 6 would be a
significant additional cost. You probably want to perform a discard,
say, every 5 seconds or sooner if you can discard at least 64kB of
undo, or something of that sort. So we're not going to save the
overhead of updating the previous transaction header often enough to
make much difference unless we're discarding so aggressively that we
incur a much larger overhead elsewhere. I think.

I am a little concerned about the backends that exit without crashing.
Andres seems to want to treat that case as a bug to be fixed, but I
doubt whether that's going to be practical. We're really only
talking about extreme corner cases here, because
before_shmem_exit(ShutdownPostgres, 0) means we'll
AbortOutOfAnyTransaction() which should RecordTransactionAbort(). Only
if we fail in the AbortTransaction() prior to reaching
RecordTransactionAbort() will we manage to reach the later cleanup
stages without having written an abort record. I haven't scrutinized
that code lately to see exactly how things can go wrong there, but
there shouldn't be a whole lot. However, there's probably a few
things, like maybe a really poorly-timed malloc() failure.

A zero-order solution would be to install a deadman switch. At
on_shmem_exit time, you must detach from any undo log to which you are
connected, so that somebody else can attach to it later. We can stick
in a cross-check there that you haven't written any undo bytes to that
log and PANIC if you have. Then the system must be water-tight.
Perhaps it's possible to do better: if we could identify the cases in
which such logic gets reached, we could try to guarantee that WAL is
written and the undo log safely detached before we get there. But at
the various least we can promote ERROR/FATAL to PANIC in the relevant
case.

A better solution would be to detect the problem and make sure we
recover from it before reusing the undo log. Suppose each undo log
has three states: (1) nobody's attached, (2) somebody's attached, and
(3) nobody's attached but the last record might need a fixup. When we
start up, all undo logs are in state 3, and the discard worker runs
around and puts them into state 1. Subsequently, they alternate
between states 1 and 2 for as long as the system remains up. But if
as an exceptional case we reach on_shmem_exit without having detached
the undo log, because of cascading failures, then we put the undo log
in state 3. The discard worker already knows how to move undo logs
from state 3 to state 1, and it can do the same thing here. Until it
does nobody else can reuse that undo log.

I might be missing something, but I think that would nail this down
pretty tightly.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#291Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#276)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Aug 21, 2019 at 4:34 PM Robert Haas <robertmhaas@gmail.com> wrote:

ReleaseResourcesAndProcessUndo() is only supposed to be called after
AbortTransaction(), and the additional steps it performs --
AtCleanup_Portals() and AtEOXact_Snapshot() or alternatively
AtSubCleanup_Portals -- are taken from Cleanup(Sub)Transaction.
That's not crazy; the other steps in Cleanup(Sub)Transaction() look
like stuff that's intended to be performed when we're totally done
with this TransactionState stack entry, whereas these things are
slightly higher-level cleanups that might even block undo (e.g.
undropped portal prevents orphaned file cleanup). Granted, there are
no comments explaining why those particular cleanup steps are
performed here, and it's possible some other approach is better, but I
think perhaps it's not quite as flagrantly broken as you think.

Andres smacked me with the clue-bat off-list and now I understand why
this is broken: there's no guarantee that running the various
AtEOXact/AtCleanup functions actually puts the transaction back into a
good state. They *might* return the system to the state that it was
in immediately following StartTransaction(), but they also might not.
Moreover, ProcessUndoRequestForEachLogCat uses PG_TRY/PG_CATCH and
then discards the error without performing *any cleanup at all* but
then goes on and tries to do undo for other undo log categories
anyway. That is totally unsafe.

I think that there should only be one chance to perform undo actions,
and as I said or at least alluded to before, if that throws an error,
it shouldn't be caught by a TRY/CATCH block but should be handled by
the state machine in xact.c. If we're not going to make the state
machine handle these conditions, the addition of
TRANS_UNDO/TBLOCK_UNDO/TBLOCK_SUBUNDO is really missing the boat. I'm
still not quite sure of the exact sequence of steps: we clearly need
AtCleanup_Portals() and a bunch of the other stuff that happens during
CleanupTransaction(), ideally including the freeing of memory, to
happen before we try undo. But I don't have a great feeling for how to
make that happen, and it seems more desirable for undo to begin as
soon as the transaction fails rather than waiting until
Cleanup(Sub)Transaction() time. I think some more research is needed
here.

I am also not convinced that semi-critical sections are a bad idea,

Regarding this, after further thought and discussion with Andres,
there are two cases here that call for somewhat different handling:
temporary undo, and subtransaction abort.

In the case of a subtransaction abort, we can't proceed with the
toplevel transaction unless we succeed in applying the
subtransaction's undo, but that does not require killing off the
backend. It might be a better idea to just fail the containing
subtransaction with the error that occurred during undo apply; if
there are multiple levels of subtransactions present then we might
fail in the same way several times, but eventually we'll fail at the
top level, forcibly kick the undo into the background, and the session
can continue. The background workers will, hopefully, eventually
recover the situation. Even if they can't, because, say, the failure
is due to a bug or whatever, killing off the session doesn't really
help.

In the case of temporary undo, killing the session is a much more
appealing approach. If we don't, how will that undo ever get
processed? We could retry at some point (like every time we return to
the toplevel command prompt?) or just ignore the fact that we didn't
manage to perform those undo actions and leave that undo there like an
albatross, accumulating more and more undo behind it until the session
exits or the disk fills up. The latter strikes me as a terrible user
experience, especially because for wire protocol reasons we'd have to
swallow the errors or at best convert them to warnings, but YMMV.

Anyway, probably these cases should not be handled exactly the same
way, but exactly what to do probably depends on the previous question:
how exactly does the integration into xact.c's state machine work,
anyway?

Meanwhile, I've been working up a prototype of how the undorequest.c
stuff I sent previously could be integrated with xact.c. In working
on that, I've realized that there seem to be two different tasks. One
is tracking the information that we'll need to have available to
perform undo actions. The other is the actual transaction state
manipulation: when and how do we abort transactions, cleanup
transactions, start new transactions specifically for undo? How are
transactions performing undo specially marked, if at all? The
attached patch includes a new module, undostate.c/h, which tries to
handle the first of those things; this is just a prototype, and is
missing some pieces marked with XXX, but I think it's probably the
right general direction. It will still need to be plugged into a
framework for launching undo apply background workers (which might
require some API adjustments) and it needs xact.c to handle the core
transactional stuff. But hopefully it will help to illustrate how the
undorequest.c stuff that I sent before can actually be put to use.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

v3-0001-New-undo-request-manager-now-with-some-xact.c-int.patchapplication/octet-stream; name=v3-0001-New-undo-request-manager-now-with-some-xact.c-int.patchDownload
From c2df9aa0a159d10b4522ae0c7e915ecee019759c Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 8 Aug 2019 16:05:53 -0400
Subject: [PATCH v3] New undo request manager, now with some xact.c
 integration.

---
 src/backend/access/transam/xact.c             |    9 +
 src/backend/access/undo/Makefile              |    2 +-
 src/backend/access/undo/undorequest.c         | 1101 +++++++++++++++++
 src/backend/access/undo/undostate.c           |  526 ++++++++
 src/backend/lib/rbtree.c                      |   46 +-
 src/backend/storage/ipc/ipci.c                |    3 +
 src/backend/storage/lmgr/lwlocknames.txt      |    1 +
 src/include/access/transam.h                  |    1 +
 src/include/access/undorequest.h              |   76 ++
 src/include/access/undostate.h                |   37 +
 src/include/lib/rbtree.h                      |   42 +-
 src/test/modules/Makefile                     |    1 +
 .../test_undo_request_manager/.gitignore      |    4 +
 .../test_undo_request_manager/Makefile        |   21 +
 .../expected/test_undo_request_manager.out    |   28 +
 .../sql/test_undo_request_manager.sql         |   16 +
 .../test_undo_request_manager--1.0.sql        |    9 +
 .../test_undo_request_manager.c               |  139 +++
 .../test_undo_request_manager.control         |    4 +
 src/tools/pgindent/typedefs.list              |    5 +
 20 files changed, 2043 insertions(+), 28 deletions(-)
 create mode 100644 src/backend/access/undo/undorequest.c
 create mode 100644 src/backend/access/undo/undostate.c
 create mode 100644 src/include/access/undorequest.h
 create mode 100644 src/include/access/undostate.h
 create mode 100644 src/test/modules/test_undo_request_manager/.gitignore
 create mode 100644 src/test/modules/test_undo_request_manager/Makefile
 create mode 100644 src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
 create mode 100644 src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.c
 create mode 100644 src/test/modules/test_undo_request_manager/test_undo_request_manager.control

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index d7930c077d..6eacc9e242 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undostate.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -2243,6 +2244,7 @@ CommitTransaction(void)
 	AtEOXact_PgStat(true, is_parallel_worker);
 	AtEOXact_Snapshot(true, false);
 	AtEOXact_ApplyLauncher(true);
+	AtCommit_UndoState();
 	pgstat_report_xact_timestamp(0);
 
 	CurrentResourceOwner = NULL;
@@ -2566,6 +2568,7 @@ AbortTransaction(void)
 	TransactionState s = CurrentTransactionState;
 	TransactionId latestXid;
 	bool		is_parallel_worker;
+	bool		perform_foreground_undo;
 
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
@@ -2729,6 +2732,8 @@ AbortTransaction(void)
 		AtEOXact_HashTables(false);
 		AtEOXact_PgStat(false, is_parallel_worker);
 		AtEOXact_ApplyLauncher(false);
+		AtAbort_UndoState(&perform_foreground_undo);
+		/* XXX need to do something with perform_foreground_undo */
 		pgstat_report_xact_timestamp(0);
 	}
 
@@ -4825,6 +4830,7 @@ CommitSubTransaction(void)
 	AtEOSubXact_PgStat(true, s->nestingLevel);
 	AtSubCommit_Snapshot(s->nestingLevel);
 	AtEOSubXact_ApplyLauncher(true, s->nestingLevel);
+	AtSubCommit_UndoState(s->nestingLevel);
 
 	/*
 	 * We need to restore the upper transaction's read-only state, in case the
@@ -4852,6 +4858,7 @@ static void
 AbortSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	bool	perform_foreground_undo;
 
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
@@ -4979,6 +4986,8 @@ AbortSubTransaction(void)
 		AtEOSubXact_PgStat(false, s->nestingLevel);
 		AtSubAbort_Snapshot(s->nestingLevel);
 		AtEOSubXact_ApplyLauncher(false, s->nestingLevel);
+		AtSubAbort_UndoState(s->nestingLevel, &perform_foreground_undo);
+		/* XXX need to do something with perform_foreground_undo */
 	}
 
 	/*
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c6963cf..845a4802b4 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undolog.o undorequest.o undostate.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
new file mode 100644
index 0000000000..37660cdb5f
--- /dev/null
+++ b/src/backend/access/undo/undorequest.c
@@ -0,0 +1,1101 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.c
+ *		Undo request manager.
+ *
+ * From the moment a transaction begins until the moment that it commits,
+ * there is a possibility that it might abort, either due to an exception
+ * or because the entire system is restarted (e.g. because of a power
+ * cut). If this happens, all undo generated by that transaction prior
+ * to the abort must be applied.  To ensure this, the calling code must
+ * ensure that an "undo request" is registered for every transaction
+ * that generates undo.
+ *
+ * The undo request should be registered before the transaction writes any
+ * undo records (except for temporary undo records, which the creating backend
+ * will need to process locally). If the transaction goes on to commit, the
+ * undo request can be deleted; if it goes on to abort, it needs to be updated
+ * with the final size of the undo generated by that transaction so that
+ * we can prioritize it appropriately. One of the key tasks of this module
+ * is to decide on the order in which undo requests should been processed;
+ * see GetNextUndoRequest for details.
+ *
+ * We have only a fixed amount of shared memory to store undo requests;
+ * because an undo request has to be created before any undo that might
+ * need to be processed is written, we should never end up in a situation
+ * where there are more existing undo requests that can fit. In extreme
+ * cases, this might cause us to have to refuse to create new requests,
+ * but that should very rare.  If we're starting to run low on space,
+ * FinalizeUndoRequest() will signal callers that undo should be
+ * performed in the foreground; actually hitting the hard limit requires
+ * foreground undo to be interrupted by a crash.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorequest.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "lib/rbtree.h"
+#include "storage/shmem.h"
+#include "utils/timestamp.h"
+
+/*
+ * An UndoRequest represents the possible need to perform undo actions for
+ * a transaction if it aborts; thus, it should be allocated before writing
+ * undo that might require the system to perform cleanup actions (except
+ * temporary undo, for which the backend is always responsible) and
+ * deallocated when it is clear that no such actions will need to be
+ * performed or when they have all been performed successfully.
+ *
+ * At any given time, an UndoRequest is one of three states: FREE (not
+ * allocated to any transaction; available for reuse), UNLISTED (allocated
+ * to a transaction but not in any RBTree), or LISTED (allocated to a
+ * transaction and in either both requests_by_fxid and requests_by_size or
+ * else in requests_by_retry_time).
+ *
+ * Changes to UndoRequest objects are protected by the UndoRequestManager's
+ * lock, but not all changes require the lock.  The following rules apply:
+ *
+ * fxid must be InvalidFullTransactionId if and only if the UndoRequest is
+ * FREE, and may only be changed while holding the lock.
+ *
+ * next_free_request must be NULL unless the UndoRequest is FREE, and may
+ * only be changed while holding the lock.
+ *
+ * The remaining fields must be accurate if the UndoRequest is LISTED, but
+ * otherwise may or may not contain correct data. They should not be changed
+ * while the request is FREE, may be changed without holding the lock while
+ * the request is UNLISTED, and may only be changed while holding the lock
+ * if the requested is LISTED.
+ *
+ * Callers must be careful never to lose track of an entry that is UNLISTED;
+ * such entries will be permanently leaked. An entry that is FREE can be
+ * reallocated by this module, while one that is LISTED should eventually
+ * get processed and become FREE, but an UNLISTED entry remains the caller's
+ * responsibility until the state is changed.
+ */
+struct UndoRequest
+{
+	FullTransactionId fxid;
+	Oid			dbid;
+	Size		size;
+	UndoRecPtr	start_location_logged;
+	UndoRecPtr	end_location_logged;
+	UndoRecPtr	start_location_unlogged;
+	UndoRecPtr	end_location_unlogged;
+	TimestampTz retry_time;
+	UndoRequest *next_free_request;
+};
+
+/*
+ * An UndoRequestNode just points to an UndoRequest. We use it so that the
+ * same UndoRequest can be placed into more than one RBTree at the same
+ * time.
+ */
+typedef struct UndoRequestNode
+{
+	RBTNode		rbtnode;
+	UndoRequest *req;
+} UndoRequestNode;
+
+/*
+ * Possible sources of UndoRequest objects in need of processing.
+ */
+typedef enum UndoRequestSource
+{
+	UNDO_SOURCE_FXID,
+	UNDO_SOURCE_SIZE,
+	UNDO_SOURCE_RETRY_TIME
+} UndoRequestSource;
+
+/*
+ * An UndoRequestManager manages a collection of UndoRequest and
+ * UndoRequestNode objects. Typically, there would only be one such object
+ * for the whole system, but it's possible to create others for testing
+ * purposes.
+ */
+struct UndoRequestManager
+{
+	LWLock	   *lock;			/* for synchronization */
+	Size		capacity;		/* max # of UndoRequests */
+	Size		utilization;	/* # of non-FREE UndoRequests */
+	Size		soft_size_limit;	/* threshold to not background */
+	UndoRequestSource source;	/* which RBTree to check next? */
+	RBTree		requests_by_fxid;	/* lower FXIDs first */
+	RBTree		requests_by_size;	/* bigger sizes first */
+	RBTree		requests_by_retry_time; /* sooner retry times first */
+	bool		oldest_fxid_valid;	/* true if next field is valid */
+	FullTransactionId oldest_fxid;	/* oldest FXID of any UndoRequest */
+	UndoRequest *all_requests;
+	UndoRequest *first_free_request;
+	UndoRequestNode *first_free_request_node;
+};
+
+/* Static functions. */
+static UndoRequest *FindUndoRequestForDatabase(UndoRequestManager *urm,
+											   Oid dbid);
+static bool BackgroundUndoOK(UndoRequestManager *urm,
+							 UndoRequest *req);
+static RBTNode *UndoRequestNodeAllocate(void *arg);
+static void UndoRequestNodeFree(RBTNode *x, void *arg);
+static void UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata,
+								   void *arg);
+static int	UndoRequestNodeCompareRetryTime(const RBTNode *a,
+											const RBTNode *b,
+											void *arg);
+static int	UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b,
+									   void *arg);
+static int	UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b,
+									   void *arg);
+static void InsertUndoRequest(RBTree *rbt, UndoRequest *req);
+static void RemoveUndoRequest(RBTree *rbt, UndoRequest *req);
+static UndoRequest *FindUndoRequest(UndoRequestManager *urm,
+									FullTransactionId fxid);
+
+/*
+ * Compute the amount of space that will be needed by an undo request manager.
+ *
+ * We need space for the UndoRequestManager itself, for the UndoRequest
+ * objects, and for the UndoRequestNode objects.  We need twice as many
+ * UndoRequestNode objects as we do UndoRequest objects, because unfailed
+ * requests are stored in both requests_by_fxid and requests_by_size; failed
+ * requests are stored only in requests_by_retry_time.
+ */
+Size
+EstimateUndoRequestManagerSize(Size capacity)
+{
+	Size		s = MAXALIGN(sizeof(UndoRequestManager));
+
+	s = add_size(s, MAXALIGN(mul_size(capacity, sizeof(UndoRequest))));
+	s = add_size(s, MAXALIGN(mul_size(capacity,
+									  mul_size(2, sizeof(UndoRequestNode)))));
+
+	return s;
+}
+
+/*
+ * Initialize an undo request manager.
+ *
+ * The caller is responsible for providing an appropriately-sized chunk of
+ * memory; use EstimateUndoRequestManagerSize to find out how much space will
+ * be needed. This means that this infrastructure can potentially be used in
+ * either shared memory or, if desired, in backend-private memory. It will not
+ * work in DSM, though, because it uses pointers.
+ *
+ * The caller must also provide a lock that will be used to protect access
+ * to the data managed by this undo request manager.  This cannot be NULL,
+ * even if the memory is private.
+ */
+void
+InitializeUndoRequestManager(UndoRequestManager *urm, LWLock *lock,
+							 Size capacity, Size soft_limit)
+{
+	UndoRequest *reqs;
+	UndoRequestNode *nodes;
+	int			i;
+
+	/* Basic initialization. */
+	urm->lock = lock;
+	urm->capacity = capacity;
+	urm->utilization = 0;
+	urm->soft_size_limit = soft_limit;
+	urm->source = UNDO_SOURCE_FXID;
+	rbt_initialize(&urm->requests_by_fxid, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareFXID, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_size, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareSize, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	rbt_initialize(&urm->requests_by_retry_time, sizeof(UndoRequestNode),
+				   UndoRequestNodeCompareRetryTime, UndoRequestNodeCombine,
+				   UndoRequestNodeAllocate, UndoRequestNodeFree, urm);
+	urm->oldest_fxid_valid = true;
+	urm->oldest_fxid = InvalidFullTransactionId;
+
+	/* Find memory for UndoRequest and UndoRequestNode arenas. */
+	reqs = (UndoRequest *)
+		(((char *) urm) + MAXALIGN(sizeof(UndoRequestManager)));
+	urm->all_requests = reqs;
+	nodes = (UndoRequestNode *)
+		(((char *) reqs) + MAXALIGN(capacity * sizeof(UndoRequest)));
+
+	/* Build a free list of UndoRequest objects.  */
+	urm->first_free_request = reqs;
+	for (i = 0; i < capacity - 1; ++i)
+	{
+		UndoRequest *current = &reqs[i];
+		UndoRequest *next = &reqs[i + 1];
+
+		current->next_free_request = next;
+	}
+	reqs[capacity - 1].next_free_request = NULL;
+
+	/*
+	 * Similarly, build a free list of UndoRequestNode objects.  In this case,
+	 * we use the first few bytes of the free object to store a pointer to the
+	 * next free object.
+	 */
+	StaticAssertStmt(sizeof(UndoRequestNode) >= sizeof(UndoRequestNode *),
+					 "UndoRequestNode is too small");
+	urm->first_free_request_node = nodes;
+	for (i = 0; i < 2 * capacity - 1; ++i)
+	{
+		UndoRequestNode *current = &nodes[i];
+		UndoRequestNode *next = &nodes[i + 1];
+
+		*(UndoRequestNode **) current = next;
+	}
+	*(UndoRequestNode **) &nodes[2 * capacity - 1] = NULL;
+}
+
+/*
+ * Register a new undo request. If unable, returns NULL.
+ *
+ * This function should be called before a transaction first writes any undo;
+ * at end of transaction, the caller call either UnregisterUndoRequest (on
+ * commit) or FinalizeUndoRequest (on abort).
+ *
+ * The returned request is UNLISTED (as defined above).
+ */
+UndoRequest *
+RegisterUndoRequest(UndoRequestManager *urm, FullTransactionId fxid, Oid dbid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	req = urm->first_free_request;
+	if (req != NULL)
+	{
+		/* Pop free list. */
+		urm->first_free_request = req->next_free_request;
+		req->next_free_request = NULL;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = 0;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->end_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->end_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+
+		/* Save this fxid as the oldest one, if necessary. */
+		if (urm->oldest_fxid_valid &&
+			(!FullTransactionIdIsValid(urm->oldest_fxid)
+			 || FullTransactionIdPrecedes(fxid, urm->oldest_fxid)))
+			urm->oldest_fxid = fxid;
+	}
+
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Finalize details for an undo request.
+ *
+ * Since an UndoRequest should be registered before beginning to write undo,
+ * the undo size won't be known at that point; this function should be getting
+ * called at prepare time for a prepared transaction, or at abort time
+ * otherwise, by which point the size should be known.
+ *
+ * Caller should report the total size of generated undo in bytes, counting
+ * only logged and unlogged undo that will be processed by background workers.
+ * Any undo bytes that aren't part of the logged or unlogged undo records
+ * that may need cleanup actions performed should not be included in size;
+ * for example, temporary undo doesn't count, as the caller must deal with
+ * that outside of this mechanism.
+ *
+ * Caller must also pass the end location for logged and unlogged undo;
+ * each should be if InvalidUndoRecPtr if and only if the corresponding
+ * start location was never set.
+ *
+ * We don't need a lock here, because this request must be UNLISTED (as
+ * defined above).
+ */
+void
+FinalizeUndoRequest(UndoRequestManager *urm, UndoRequest *req, Size size,
+					UndoRecPtr start_location_logged,
+					UndoRecPtr start_location_unlogged,
+					UndoRecPtr end_location_logged,
+					UndoRecPtr end_location_unlogged)
+{
+	Assert(size != 0);
+	Assert(UndoRecPtrIsValid(end_location_logged) ||
+		   UndoRecPtrIsValid(end_location_unlogged));
+	Assert(UndoRecPtrIsValid(end_location_logged) ==
+		   UndoRecPtrIsValid(req->start_location_logged));
+	Assert(UndoRecPtrIsValid(end_location_unlogged) ==
+		   UndoRecPtrIsValid(req->start_location_unlogged));
+	req->size = size;
+	req->start_location_logged = start_location_logged;
+	req->start_location_unlogged = start_location_unlogged;
+	req->end_location_logged = end_location_logged;
+	req->end_location_unlogged = end_location_unlogged;
+}
+
+/*
+ * Release a previously-allocated undo request.
+ *
+ * On entry, the undo request should be either LISTED or UNLISTED; on exit,
+ * it will be FREE (as these terms are defined above).
+ *
+ * This should be used at transaction commit, if an UndoRequest was
+ * registered, or when undo for an aborted transaction has been succesfully
+ * processed.
+ *
+ * Because this function may be called as a post-commit step, it must never
+ * throw an ERROR.
+ */
+void
+UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * Remove the UndoRequest from any RBTree that contains it.  If the retry
+	 * time is not DT_NOBEGIN, then the request has been finalized and undo
+	 * has subsequently failed.  If the size is 0, the request has not been
+	 * finalized yet, so it's not in any RBTree.
+	 */
+	if (req->retry_time != DT_NOBEGIN)
+		RemoveUndoRequest(&urm->requests_by_retry_time, req);
+	else if (req->size != 0)
+	{
+		RemoveUndoRequest(&urm->requests_by_fxid, req);
+		RemoveUndoRequest(&urm->requests_by_size, req);
+	}
+
+	/* Plan to recompute oldest_fxid, if necessary. */
+	if (FullTransactionIdEquals(req->fxid, urm->oldest_fxid))
+		urm->oldest_fxid_valid = false;
+
+	/* Push onto freelist. */
+	req->next_free_request = urm->first_free_request;
+	urm->first_free_request = req;
+
+	/* Decrease utilization. */
+	--urm->utilization;
+
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Try to hand an undo request off for background processing.
+ *
+ * If this function returns true, the UndoRequest can be left for background
+ * processing; the caller need not do anything more. If this function returns
+ * false, the caller should try to process it in the foreground, and must
+ * call either UnregisterUndoRequest on success or RescheduleUndoRequest
+ * on failure.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ *
+ * On entry, the UndoRequest should be UNLISTED; on exit, it is LISTED
+ * if this function returns true, and remains UNLISTED if this function
+ * returns false (see above for definitions).
+ */
+bool
+PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req)
+{
+	bool		background;
+
+	/*
+	 * If we failed after allocating an UndoRequest but before setting any
+	 * start locations, there's no work to be done.  In that case, we can just
+	 * unregister the request.
+	 */
+	if (!UndoRecPtrIsValid(req->start_location_logged) &&
+		!UndoRecPtrIsValid(req->start_location_unlogged))
+	{
+		UnregisterUndoRequest(urm, req);
+		return true;
+	}
+
+	/*
+	 * We need to check shared state in order to determine whether or not to
+	 * perform this undo in the background, and if we are going to perform it
+	 * in the background, also to add it to requests_by_fxid and
+	 * requests_by_size.
+	 */
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	background = BackgroundUndoOK(urm, req);
+	if (background)
+	{
+		/*
+		 * We're going to handle this in the background, so add it to
+		 * requests_by_fxid and requests_by_size, so that GetNextUndoRequest
+		 * can find it.
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	LWLockRelease(urm->lock);
+
+	return background;
+}
+
+/*
+ * Get an undo request that needs background processing.
+ *
+ * Unless dbid is InvalidOid, any request returned must be from the indicated
+ * database.  If minimum_runtime_reached is true, the caller only wants to
+ * process another request if the next request happens to be from the correct
+ * database. If it's false, the caller wants to avoiding exiting too quickly,
+ * and would like to process a request from the database if there's one
+ * available.
+ *
+ * If no suitable request is found, *fxid gets InvalidFullTransactionId;
+ * otherwise, *fxid gets the FullTransactionId of the transaction and
+ * the parameters which follow get the start and end locations of logged
+ * and unlogged undo for that transaction.  It's possible that the transaction
+ * wrote only logged undo or only unlogged undo, in which case the other
+ * pair fields will have a value of InvalidUndoRecPtr, but it should never
+ * happen that all of the fields get InvalidUndoRecPtr, because that would
+ * mean we queued up an UndoRequest to do nothing.
+ *
+ * This function, as a side effect, makes the returned UndoRequest UNLISTED,
+ * as defined above, so that no other backend will attempt to process it
+ * simultaneously. The caller must be certain to call either
+ * UnregisterUndoRequest (if successful) or RescheduleUndoRequest (on
+ * failure) to avoid leaking the UndoRequest.
+ */
+UndoRequest *
+GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+				   bool minimum_runtime_reached,
+				   Oid *out_dbid,
+				   UndoRecPtr *start_location_logged,
+				   UndoRecPtr *end_location_logged,
+				   UndoRecPtr *start_location_unlogged,
+				   UndoRecPtr *end_location_unlogged)
+{
+	UndoRequest *req = NULL;
+	int			nloops;
+	bool		saw_db_mismatch = false;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/* Some might have no work, so loop until all are checked. */
+	for (nloops = 0; nloops < 3; ++nloops)
+	{
+		RBTree	   *rbt;
+		UndoRequestSource source = urm->source;
+		UndoRequestNode *node;
+
+		/*
+		 * We rotate between the three possible sources of UndoRequest
+		 * objects.
+		 *
+		 * The idea here is that processing the requests with the oldest
+		 * transaction IDs is important because it helps us discard undo log
+		 * data sooner and because it allows XID horizons to advance. On the
+		 * other hand, handling transactions that generated a very large
+		 * amount of undo is also a priority, because undo will probably take
+		 * a long to finish and thus should be started as early as possible
+		 * and also because it likely touched a large number of pages which
+		 * will be slow to access until the undo is processed.
+		 *
+		 * However, we also need to make sure to periodically retry undo for
+		 * transactions that previously failed. We hope that this will be very
+		 * rare, but if it does happen we can neither affort to retry those
+		 * transactions over and over in preference to all others, nor on the
+		 * other hand to just ignore them forever.
+		 *
+		 * We could try to come up with some scoring system that assigns
+		 * relative levels of importance to FullTransactionId age, undo size,
+		 * and retry time, but it seems difficult to come up with a weighting
+		 * system that can ensure that nothing gets starved. By rotating among
+		 * the sources evenly, we know that as long as we continue to process
+		 * undo requests on some sort of regular basis, each source will get
+		 * some amount of attention.
+		 */
+		switch (source)
+		{
+			case UNDO_SOURCE_FXID:
+				rbt = &urm->requests_by_fxid;
+				urm->source = UNDO_SOURCE_SIZE;
+				break;
+			case UNDO_SOURCE_SIZE:
+				rbt = &urm->requests_by_size;
+				urm->source = UNDO_SOURCE_RETRY_TIME;
+				break;
+			case UNDO_SOURCE_RETRY_TIME:
+				rbt = &urm->requests_by_retry_time;
+				urm->source = UNDO_SOURCE_FXID;
+				break;
+		}
+
+		/* Get highest-priority item. */
+		node = (UndoRequestNode *) rbt_leftmost(rbt);
+		if (node == NULL)
+			continue;
+
+		/*
+		 * We can only take an item from the retry time RBTree if the retry
+		 * time is in the past.
+		 */
+		if (source == UNDO_SOURCE_RETRY_TIME &&
+			node->req->retry_time > GetCurrentTimestamp())
+			continue;
+
+		/*
+		 * If a database OID was specified, it must match. If it does not, we
+		 * go ahead and try any remaining RBTree.  Note that this needs to be
+		 * after the other tests so that we get the right value for the
+		 * saw_db_mismatch flag.
+		 */
+		if (OidIsValid(dbid) && node->req->dbid != dbid)
+		{
+			saw_db_mismatch = true;
+			continue;
+		}
+
+		/* Looks like we have a winner. */
+		req = node->req;
+		break;
+	}
+
+	/*
+	 * Determine whether we should do a more exhaustive search.
+	 *
+	 * If we found a node, we don't need look any harder.  If we didn't see a
+	 * database mismatch, then looking harder can't help: there's nothing to
+	 * do at all, never mind for which database.  If the caller set
+	 * minimum_runtime_reached, then they don't want us to look harder.
+	 */
+	if (req == NULL && saw_db_mismatch && !minimum_runtime_reached)
+		req = FindUndoRequestForDatabase(urm, dbid);
+
+	/*
+	 * If we found a suitable request, remove it from any RBTree that contains
+	 * it.
+	 */
+	if (req != NULL)
+	{
+		if (req->retry_time != DT_NOBEGIN)
+			RemoveUndoRequest(&urm->requests_by_retry_time, req);
+		else
+		{
+			RemoveUndoRequest(&urm->requests_by_fxid, req);
+			RemoveUndoRequest(&urm->requests_by_size, req);
+		}
+	}
+
+	LWLockRelease(urm->lock);
+
+	/*
+	 * Set output parameters.  Any request we found is now UNLISTED, so it's
+	 * safe to do this without the lock.
+	 */
+	if (req == NULL)
+		*out_dbid = InvalidOid;
+	else
+	{
+		*out_dbid = req->dbid;
+		*start_location_logged = req->start_location_logged;
+		*end_location_logged = req->end_location_logged;
+		*start_location_unlogged = req->start_location_unlogged;
+		*end_location_unlogged = req->end_location_unlogged;
+	}
+
+	/* All done. */
+	return req;
+}
+
+/*
+ * Reschedule an undo request after undo failure.
+ *
+ * This function should be called when undo processing fails, either in the
+ * foreground or in the background.  The foreground case occurs when
+ * FinalizeUndoRequest returns false and undo then also fails; the background
+ * case occurs when GetNextUndoRequest returns an UndoRequest and undo then
+ * fails. Note that this function isn't used after a shutdown or crash: see
+ * comments in RecreateUndoRequest for how we handle that case.
+ *
+ * In either of the cases where this function is reached, the UndoRequest
+ * should be UNLISTED; on return, it will be LISTED (both as defined above).
+ * If it's a foreground undo failure, it's never been LISTED; if it's a
+ * background undo failure, it was made UNLISTED by GetNextUndoRequest. So,
+ * we don't have to remove the request from anywhere, not even conditionally;
+ * we just need to add it to the set of failed requests.
+ *
+ * Because this function may be called as during transaction abort, it must
+ * never throw an ERROR. Technically, InsertUndoRequest might reach
+ * UndoRequestNodeAllocate which could ERROR if the freelist is empty, but
+ * if that happens there's a bug someplace.
+ */
+void
+RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req)
+{
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	/*
+	 * This algorithm for determining the next retry time is fairly
+	 * unsophisticated: the first retry happens after 10 seconds, and each
+	 * subsequent retry after 30 seconds. We could do something more
+	 * complicated here, but we'd need to do more bookkeeping and it's unclear
+	 * what we'd gain.
+	 */
+	if (req->retry_time == DT_NOBEGIN)
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 10 * 1000);
+	else
+		req->retry_time =
+			TimestampTzPlusMilliseconds(GetCurrentTimestamp(), 30 * 1000);
+
+	InsertUndoRequest(&urm->requests_by_retry_time, req);
+	LWLockRelease(urm->lock);
+}
+
+/*
+ * Recreate UndoRequest state after a shutdown.
+ *
+ * This function is expected to be called after a shutdown, whether a clean
+ * shutdown or a crash, both for aborted transactions with unprocessed undo
+ * and also for prepared transactions.  All calls to this function must be
+ * completed, and SuspendPreparedUndoRequest must be called for every prepared
+ * transaction, before the first call to GetNextUndoRequest occurs.
+ *
+ * This function be called up two twice per FullTransactionId, once with
+ * is_logged true and once with is_logged false, because the transaction may
+ * have both logged and unlogged undo in different places. start_location is
+ * the beginning of the type of undo indicated by the is_logged parameter, and
+ * size is the amount of such undo in bytes.  If this function is called twice,
+ * the result will be a single UndoRequest containing both start locations and
+ * a size which is the sum of the two sizes passed to the separate calls.
+ *
+ * If this function is unable to allocate a new UndoRequest when required,
+ * it will return false.  If that happens, it's not safe to continue using
+ * this UndoRequestManager and a system-wide shutdown to raise the limit on
+ * the number of outstanding requests is indicated.
+ */
+bool
+RecreateUndoRequest(UndoRequestManager *urm, FullTransactionId fxid,
+					Oid dbid, bool is_logged, UndoRecPtr start_location,
+					UndoRecPtr end_location, Size size)
+{
+	UndoRequest *req;
+
+	Assert(UndoRecPtrIsValid(start_location));
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	if (req)
+	{
+		/* Already called for opposite value of is_logged. */
+		if (is_logged)
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_logged));
+			Assert(!UndoRecPtrIsValid(req->end_location_logged));
+			req->start_location_logged = start_location;
+			req->end_location_logged = end_location;
+		}
+		else
+		{
+			Assert(!UndoRecPtrIsValid(req->start_location_unlogged));
+			Assert(!UndoRecPtrIsValid(req->end_location_unlogged));
+			req->start_location_unlogged = start_location;
+			req->end_location_unlogged = end_location;
+		}
+		Assert(req->dbid == dbid);
+
+		/* Adjusting size may change position in RBTree. */
+		RemoveUndoRequest(&urm->requests_by_size, req);
+		req->size += size;
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+	else
+	{
+		/* First call for this FullTransactionId. */
+		req = urm->first_free_request;
+		if (req == NULL)
+		{
+			LWLockRelease(urm->lock);
+			return false;
+		}
+
+		/* We got an item; pop it from the free list. */
+		urm->first_free_request = req->next_free_request;
+		req->next_free_request = NULL;
+
+		/* Increase utilization. */
+		++urm->utilization;
+
+		/* Initialize request object. */
+		req->fxid = fxid;
+		req->dbid = dbid;
+		req->size = size;
+		req->start_location_logged = InvalidUndoRecPtr;
+		req->start_location_unlogged = InvalidUndoRecPtr;
+		req->retry_time = DT_NOBEGIN;
+		if (is_logged)
+			req->start_location_logged = start_location;
+		else
+			req->start_location_unlogged = start_location;
+
+		/*
+		 * List this request so that undo workers will see it.  Note that we
+		 * assume that these are new aborts, but it's possible that there are
+		 * actually a whole series of previous undo failures before the
+		 * shutdown or crash. If we had the information about whether this
+		 * request had failed previously, we could set req->retry_time and
+		 * insert it into requests_by_retry_time rather than requests_by_fxid
+		 * and requests_by_size, but it doesn't seem important to retain
+		 * information about undo failure across crashes or shutdowns, because
+		 * we're just trying to guarantee that we don't busy-loop or starve
+		 * other requests. (FindUndoRequest would get confused, too.)
+		 */
+		InsertUndoRequest(&urm->requests_by_fxid, req);
+		InsertUndoRequest(&urm->requests_by_size, req);
+	}
+
+	LWLockRelease(urm->lock);
+	return true;
+}
+
+/*
+ * Adjust UndoRequestManager state for prepared transactions.
+ *
+ * After a restart, once all calls to RecreateUndoRequest have been completed
+ * and before the first call to GetNextUndoRequest, this function should
+ * be called for each prepared transaction. That's necessary to avoid
+ * prematurely executed undo actions for transactions that haven't aborted
+ * yet and might go on to commit. The UndoRequest for the indicated fxid is
+ * made UNLISTED (as defined above) so that GetNextUndoRequest does not find
+ * them.
+ *
+ * The caller should retain a pointer to the returned UndoRequest and, when
+ * the prepared transaction is eventually committed or rolled back, should
+ * invoke UnregisterUndoRequest on commit or FinalizeUndoRequest on abort.
+ */
+UndoRequest *
+SuspendPreparedUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest *req;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+	req = FindUndoRequest(urm, fxid);
+	Assert(req != NULL);
+	Assert(req->size != 0);
+	RemoveUndoRequest(&urm->requests_by_fxid, req);
+	RemoveUndoRequest(&urm->requests_by_size, req);
+	LWLockRelease(urm->lock);
+
+	return req;
+}
+
+/*
+ * Get oldest registered FXID, whether LISTED or UNLISTED (as defined above).
+ *
+ * We cache the result of this computation so as to avoid repeating it too
+ * often.
+ */
+FullTransactionId
+UndoRequestManagerOldestFXID(UndoRequestManager *urm)
+{
+	FullTransactionId result = InvalidFullTransactionId;
+
+	LWLockAcquire(urm->lock, LW_EXCLUSIVE);
+
+	if (urm->oldest_fxid_valid)
+		result = urm->oldest_fxid;
+	else
+	{
+		int			i;
+
+		for (i = 0; i < urm->capacity; ++i)
+		{
+			UndoRequest *req = &urm->all_requests[i];
+
+			if (FullTransactionIdIsValid(req->fxid) &&
+				(!FullTransactionIdIsValid(result) ||
+				 FullTransactionIdPrecedes(req->fxid, result)))
+				result = req->fxid;
+		}
+
+		urm->oldest_fxid = result;
+		urm->oldest_fxid_valid = true;
+	}
+
+	LWLockRelease(urm->lock);
+
+	return result;
+}
+
+/*
+ * Perform a left-to-right search of all three RBTrees, looking for a request
+ * for a given database. The searches are interleaved so that we latch
+ * onto the highest-priority request in any RBTree.
+ *
+ * It's possible that we should have some kind of limit on this search, so
+ * that it doesn't do an exhaustive search of every RBTree. However, it's not
+ * exactly clear how that would affect the behavior, or how to pick a
+ * reasonable limit.
+ */
+static UndoRequest *
+FindUndoRequestForDatabase(UndoRequestManager *urm, Oid dbid)
+{
+	RBTreeIterator iter[3];
+	int			doneflags = 0;
+	int			i = 0;
+
+	rbt_begin_iterate(&urm->requests_by_fxid, LeftRightWalk, &iter[0]);
+	rbt_begin_iterate(&urm->requests_by_size, LeftRightWalk, &iter[1]);
+	rbt_begin_iterate(&urm->requests_by_retry_time, LeftRightWalk, &iter[2]);
+
+	while (1)
+	{
+		UndoRequestNode *node;
+
+		if ((doneflags & (1 << i)) == 0)
+		{
+			node = (UndoRequestNode *) rbt_iterate(&iter[i]);
+			if (node == NULL)
+			{
+				doneflags |= 1 << i;
+				if (doneflags == 7) /* all bits set */
+					break;
+			}
+			else if (node->req->dbid == dbid)
+				return node->req;
+		}
+		i = (i + 1) % 3;
+	}
+
+	return NULL;
+}
+
+/*
+ * Is it OK to handle this UndoRequest in the background?
+ */
+static bool
+BackgroundUndoOK(UndoRequestManager *urm, UndoRequest *req)
+{
+	/*
+	 * If we've passed the soft size limit, it's not OK to background it.
+	 */
+	if (urm->utilization > urm->soft_size_limit)
+		return false;
+
+	/*
+	 * Otherwise, allow it.
+	 *
+	 * TODO: We probably want to introduce some additional rules here based on
+	 * the size of the request.
+	 */
+	return true;
+}
+
+/*
+ * RBTree callback to allocate an UndoRequestNode.
+ *
+ * Everything is preallocated, so we're just popping the freelist.
+ */
+static RBTNode *
+UndoRequestNodeAllocate(void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = urm->first_free_request_node;
+
+	/*
+	 * Any LISTED UndoRequest should either be in both requests_by_fxid and
+	 * requests_by_size, or it should be in requests_by_retry_time, or it
+	 * should be in neither RBTree; consequently, it should be impossible to
+	 * use more than 2 UndoRequestNode objects per UndoRequest. Since we
+	 * preallocate that number, we should never run out. In case there's a bug
+	 * in the logic, let's insert a runtime check here even when Asserts are
+	 * disabled.
+	 */
+	if (node == NULL)
+		elog(ERROR, "no free UndoRequestNode");
+
+	/* Pop freelist. */
+	urm->first_free_request_node = *(UndoRequestNode **) node;
+
+	return &node->rbtnode;
+}
+
+/*
+ * RBTree callback to free an UndoRequestNode.
+ *
+ * Just put it back on the freelist.
+ */
+static void
+UndoRequestNodeFree(RBTNode *x, void *arg)
+{
+	UndoRequestManager *urm = arg;
+	UndoRequestNode *node = (UndoRequestNode *) x;
+
+	*(UndoRequestNode **) node = urm->first_free_request_node;
+	urm->first_free_request_node = node;
+}
+
+/*
+ * RBTree callback to combine an UndoRequestNode with another one.
+ *
+ * The key for every RBTree includes the FXID, which is unique, so it should
+ * never happen that we need to merge requests.
+ */
+static void
+UndoRequestNodeCombine(RBTNode *existing, const RBTNode *newdata, void *arg)
+{
+	elog(ERROR, "undo requests should never need to be combined");
+}
+
+/*
+ * RBTree comparator for requests_by_retry_time. Older retry
+ * times first; in the case of a tie, smaller FXIDs first.  This avoids ties,
+ * which is important since we don't want to merge requests, and also favors
+ * retiring older transactions first, which is generally desirable.
+ */
+static int
+UndoRequestNodeCompareRetryTime(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	TimestampTz retry_time_a = aa->req->retry_time;
+	TimestampTz retry_time_b = bb->req->retry_time;
+
+	if (retry_time_a != retry_time_b)
+		return retry_time_a < retry_time_b ? -1 : 1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. Lower FXIDs first. No tiebreak,
+ * because FXIDs should be unique.
+ */
+static int
+UndoRequestNodeCompareFXID(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * RBTree comparator for requests_by_size. As in we do for the retry
+ * time RBTree, break ties in favor of lower FXIDs.
+ */
+static int
+UndoRequestNodeCompareSize(const RBTNode *a, const RBTNode *b, void *arg)
+{
+	const UndoRequestNode *aa = (UndoRequestNode *) a;
+	const UndoRequestNode *bb = (UndoRequestNode *) b;
+	FullTransactionId fxid_a = aa->req->fxid;
+	FullTransactionId fxid_b = bb->req->fxid;
+	Size		size_a = aa->req->size;
+	Size		size_b = bb->req->size;
+
+	if (size_a != size_b)
+		return size_a < size_b ? 1 : -1;
+
+	if (FullTransactionIdPrecedes(fxid_a, fxid_b))
+		return -1;
+	else if (FullTransactionIdPrecedes(fxid_b, fxid_a))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Insert an UndoRequest into one RBTree.
+ *
+ * The actual RBTree element is an UndoRequestNode, which just points to
+ * the actual UndoRequest.
+ */
+static void
+InsertUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode dummy;
+	bool		isNew;
+
+	/*
+	 * The rbt_insert interface is a bit strange: we have to pass something
+	 * that looks like an RBTNode, but the RBTNode itself doesn't need to be
+	 * initialized - only the "extra" data that follows the end of the
+	 * structure needs to be correct.
+	 */
+	dummy.req = req;
+	rbt_insert(rbt, &dummy.rbtnode, &isNew);
+	Assert(isNew);
+}
+
+/*
+ * Remove an UndoRequest from one RBTree.
+ *
+ * This is just the reverse of InsertUndoRequest, with the same interface
+ * quirk.
+ */
+static void
+RemoveUndoRequest(RBTree *rbt, UndoRequest *req)
+{
+	UndoRequestNode dummy;
+	RBTNode    *node;
+
+	dummy.req = req;
+	node = rbt_find(rbt, &dummy.rbtnode);
+	rbt_delete(rbt, node);
+}
+
+/*
+ * Find an UndoRequest by FXID.
+ *
+ * If we needed to do this frequently, it might be worth maintaining a hash
+ * table mapping FXID -> UndoRequest, but since we only need it after a system
+ * restart, RBTree's O(lg n) performance seems good enough.
+ *
+ * Note that this can only find an UndoRequest that has not failed and is not
+ * yet being processed, because a failed UndoRequest would be in
+ * requests_by_retry_time, not requests_by_fxid, and an in-progress
+ * UndoRequest wouldn't be in either data structure. That restriction, too,
+ * is OK for current uses.
+ */
+static UndoRequest *
+FindUndoRequest(UndoRequestManager *urm, FullTransactionId fxid)
+{
+	UndoRequest dummy_request;
+	UndoRequestNode dummy_node;
+	RBTNode    *node;
+
+	/*
+	 * Here we need both a dummy UndoRequest and a dummy UndoRequestNode; only
+	 * the comparator will look at the dummy UndoRequestNode, and it will only
+	 * look at UndoRequest, and specifically its FXID.
+	 */
+	dummy_request.fxid = fxid;
+	dummy_node.req = &dummy_request;
+	node = rbt_find(&urm->requests_by_fxid, &dummy_node.rbtnode);
+	if (node == NULL)
+		return NULL;
+	return ((UndoRequestNode *) node)->req;
+}
diff --git a/src/backend/access/undo/undostate.c b/src/backend/access/undo/undostate.c
new file mode 100644
index 0000000000..9b44677af8
--- /dev/null
+++ b/src/backend/access/undo/undostate.c
@@ -0,0 +1,526 @@
+/*-------------------------------------------------------------------------
+ *
+ * undostate.c
+ *		Undo system state management and transaction integration.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undostate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "access/undostate.h"
+#include "access/xact.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+/*
+ * The capacity of the UndoRequestManager represents the maximum number of
+ * in-progress or aborted transactions that have written undo which still needs
+ * to be tracked.  Once an aborted transaction's undo actions have been
+ * executed, it no longer counts against this limit.
+ *
+ * We could make the multiplier or the absolute value user-settable, but for
+ * now we just hard-code the capacity as a fixed multiple of MaxBackends.
+ * Hopefully, we'll never get very close to this limit, because if we do,
+ * it means that the system is aborting transactions faster than the undo
+ * machinery can perform the undo actions.
+ */
+#define UNDO_CAPACITY_PER_BACKEND		10
+
+/*
+ * If the UndoRequestManager is almost full, then we start refusing all
+ * requests to perform undo in the background. Instead, the aborting
+ * transactions will need to execute their own undo actions.  The point is
+ * to avoid hitting the hard limit, at which stage we would have to start
+ * refusing undo-writing transactions completely. This constant represents
+ * the percentage of UndoRequestManager space that may be consumed before we
+ * hit the soft limit.
+ *
+ * Note that this should be set so that the remaining capacity when the limit
+ * is hit is at least MaxBackends; if this is done, it shouldn't be possible
+ * to hit the hard limit unless the system crashes at least once while the
+ * number of tracked transactions is already above the soft limit.  We set it
+ * a bit lower than that here so as to make it unlikely that we'll hit the
+ * hard limit even if there are multiple crashes.
+ */
+#define UNDO_SOFT_LIMIT_MULTIPLIER		0.85
+
+/* Per-subtransaction backend-private undo state. */
+typedef struct UndoSubTransaction
+{
+	SubTransactionId	nestingLevel;
+	UndoRecPtr	start_location[UndoLogCategories];
+	struct UndoSubTransaction *next;
+} UndoSubTransaction;
+
+/* Backend-private undo state (but with pointers into shared memory). */
+typedef struct UndoStateData
+{
+	UndoRequestManager *manager;
+	UndoRequest *my_request;
+	bool	is_undo;
+	bool	is_background_undo;
+	bool	has_undo;
+	UndoSubTransaction *subxact;
+	UndoRecPtr last_location[UndoLogCategories];
+	Size last_size[UndoLogCategories];
+	Size total_size[UndoLogCategories];
+} UndoStateData;
+
+UndoStateData UndoState;
+UndoSubTransaction UndoTopState;
+
+static void ResetUndoState(void);
+static UndoRecPtr GetUndoRecordEndPtr(UndoRecPtr start_location, Size size);
+
+/*
+ * How much shared memory do we need for undo state management?
+ */
+Size
+UndoStateShmemSize(void)
+{
+	Size	capacity = mul_size(UNDO_CAPACITY_PER_BACKEND, MaxBackends);
+
+	return EstimateUndoRequestManagerSize(capacity);
+}
+
+/*
+ * Initialize UndoRequestManager if required.
+ *
+ * Otherwise, just stash a pointer to it.
+ */
+void
+UndoStateShmemInit(void)
+{
+	Size	capacity = UNDO_CAPACITY_PER_BACKEND * MaxBackends;
+	Size	soft_limit = capacity * UNDO_SOFT_LIMIT_MULTIPLIER;
+	Size	size = EstimateUndoRequestManagerSize(capacity);
+	bool	found;
+
+	UndoState.manager = (UndoRequestManager *)
+		ShmemInitStruct("undo request manager", size, &found);
+	if (!found)
+		InitializeUndoRequestManager(UndoState.manager, UndoRequestLock,
+									 capacity, soft_limit);
+	Assert(UndoState.my_request == NULL);
+	ResetUndoState();
+}
+
+/*
+ * Accumulate information about one undo record insertion within the current
+ * transaction.
+ *
+ * This must be called for before every undo record insertion. We will need these
+ * details to decide what to do if the transaction aborts. It's important that this
+ * is called before the undo is actually inserted, because if we need to register
+ * an UndoRequest and fail to do so, the failure needs to occur while we still have
+ * no undo that will potentially require background processing.
+ */
+void
+UndoStateAccumulateRecord(UndoLogCategory category, UndoRecPtr start_location,
+						  Size size)
+{
+	int		nestingLevel = GetCurrentTransactionNestLevel();
+	UndoRecPtr *sub_start_location;
+
+	/* Remember that we've done something undo-related. */
+	UndoState.has_undo = true;
+
+	/* We should be connected to a database. */
+	Assert(OidIsValid(MyDatabaseId));
+
+	/* Register new UndoRequest if required for this persistence level. */
+	if (UndoState.my_request == NULL &&
+		(category == UNDO_PERMANENT || category == UNDO_UNLOGGED))
+		UndoState.my_request = RegisterUndoRequest(UndoState.manager,
+												   GetTopFullTransactionId(),
+												   MyDatabaseId);
+
+	/*
+	 * If we've entered a subtransaction, spin up a new UndoSubTransaction so that
+	 * we can track the start locations for the subtransaction separately from any
+	 * parent (sub)transactions.
+	 */
+	if (nestingLevel > UndoState.subxact->nestingLevel)
+	{
+		UndoSubTransaction *subxact;
+		int i;
+
+		subxact = MemoryContextAlloc(TopMemoryContext, sizeof(UndoSubTransaction));
+		subxact->nestingLevel = nestingLevel;
+		subxact->next = UndoState.subxact;
+
+		for (i = 0; i < UndoLogCategories; ++i)
+			subxact->start_location[i] = InvalidUndoRecPtr;
+	}
+
+	/*
+	 * If this is the first undo for this persistence level in this subtransaction,
+	 * record the start location.
+	 */
+	sub_start_location = &UndoState.subxact->start_location[category];
+	if (!UndoRecPtrIsValid(*sub_start_location))
+		*sub_start_location = start_location;
+
+	/*
+	 * Remember this as the last start location and record size for the persistence
+	 * level.
+	 */
+	UndoState.last_location[category] = start_location;
+	UndoState.last_size[category] = size;
+
+	/* Add to total size for persistence level. */
+	UndoState.total_size[category] += size;
+}
+
+/*
+ * Attempt to obtain an UndoRequest for background processing.
+ *
+ * If there is no work to be done right now, this function will return InvalidOid.
+ * Otherwise, the return value is the OID of the database to which the caller must
+ * be connected to perform the necessary undo work.
+ *
+ * When this function returns a database OID, any subsequent transaction abort will
+ * reschedule the UndoRequest for later reprocessing, and it's no longer this
+ * backend's responsibility. However, a transaction commit does not automatically
+ * unregister the request as successfully completed; to do that, call
+ * FinishBackgroundUndo.
+ *
+ * The minimum_runtime_reached parameter is passed to GetNextUndoRequest, q.v.
+ */
+Oid
+InitializeBackgroundUndoState(bool minimum_runtime_reached)
+{
+	Oid			dbid;
+	UndoRecPtr	start_location_logged;
+	UndoRecPtr	start_location_unlogged;
+	UndoRecPtr	end_location_logged;
+	UndoRecPtr	end_location_unlogged;
+
+	Assert(!UndoState.has_undo && !UndoState.is_undo);
+	Assert(UndoState.my_request == NULL);
+
+	UndoState.my_request =
+		GetNextUndoRequest(UndoState.manager, MyDatabaseId, minimum_runtime_reached,
+						   &dbid, &start_location_logged, &start_location_unlogged,
+						   &end_location_logged, &end_location_unlogged);
+	if (UndoState.my_request == NULL)
+		return InvalidOid;
+
+	UndoState.has_undo = true;
+	UndoState.is_undo = true;
+	UndoState.is_background_undo = true;
+	UndoState.subxact->start_location[UNDO_PERMANENT] = start_location_logged;
+	UndoState.subxact->start_location[UNDO_UNLOGGED] = start_location_unlogged;
+
+	/*
+	 * The "last location" and "last size" data we set up here isn't really accurate;
+	 * our goal is just to get the correct end location through to the code that
+	 * actually processes undo.
+	 */
+	UndoState.last_location[UNDO_PERMANENT] = end_location_logged;
+	UndoState.last_location[UNDO_UNLOGGED] = end_location_unlogged;
+	UndoState.last_size[UNDO_PERMANENT] = 0;
+	UndoState.last_size[UNDO_UNLOGGED] = 0;
+
+	Assert(OidIsValid(dbid));
+	return dbid;
+}
+
+/*
+ * If background undo processing succeeds, call this function.
+ *
+ * It will unregister the undo request.
+ */
+void
+FinishBackgroundUndo(void)
+{
+	Assert(UndoState.is_background_undo);
+	Assert(UndoState.my_request != NULL);
+
+	UnregisterUndoRequest(UndoState.manager, UndoState.my_request);
+	ResetUndoState();
+}
+
+/*
+ * Perform undo actions.
+ *
+ * This function might be called either to process undo actions in the background
+ * or to perform foreground undo.  Caller must ensure that we have a valid
+ * transaction context so that it's safe for us to do things that might fail.
+ *
+ * Our job is to apply all undo for transaction nesting levels greater than or
+ * equal to the level supplied as an argument.
+ */
+void
+PerformUndoActions(int nestingLevel)
+{
+	/*
+	 * XXX. NOT IMPLEMENTED.
+	 *
+	 * Invoke facilities to actually apply undo actions from here, passing the
+	 * relevant information from the UndoState so that they know what to do.
+	 *
+	 * In the case of subtransaction undo, this also needs to tear down the
+	 * relevant UndoSubTransaction (or else we need a separate entrypoint for
+	 * that). For a top-level transaction, AtCommit_UndoState() or
+	 * FinishBackgroundUndo() will take care of it.
+	 */
+}
+
+/*
+ * Post-commit cleanup of the undo state.
+ *
+ * NB: This code MUST NOT FAIL, since it is run as a post-commit cleanup step.
+ * Don't put anything complicated in this function!
+ */
+void
+AtCommit_UndoState(void)
+{
+	/*
+	 * For background undo processing, the fact that the transaction is committing
+	 * doesn't necessarily mean we're done.  For example, we might have just been
+	 * connecting to the database or something of that sort. Client code must call
+	 * FinishBackgroundUndo() to report successful completion. So, do nothing in
+	 * that case.
+	 */
+	if (UndoState.is_background_undo)
+		return;
+
+	/* Shouldn't commit after beginning foreground undo. */
+	Assert(!UndoState.is_undo);
+
+	/* Also exit quickly if we never did anything undo-related. */
+	if (!UndoState.has_undo)
+		return;
+
+	/*
+	 * Since our (foreground) transaction committed, we know that no undo actions
+	 * for any undo we wrote will need to be performed, and can therefore unregister
+	 * our UndoRequest, if any.
+	 */
+	if (UndoState.my_request != NULL)
+	{
+		UnregisterUndoRequest(UndoState.manager, UndoState.my_request);
+		UndoState.my_request = NULL;
+	}
+
+	/* Reset state for next transaction. */
+	ResetUndoState();
+}
+
+/*
+ * Post-abort cleanup of the undo state.
+ *
+ * Our main goals here are to (1) tell the caller whether foreground undo is
+ * required and (2) avoid losing track of any UndoRequest that we own.
+ */
+void
+AtAbort_UndoState(bool *perform_foreground_undo)
+{
+	bool	has_temporary_undo = false;
+	Size	request_size;
+	UndoRecPtr end_location_logged;
+	UndoRecPtr end_location_unlogged;
+
+	*perform_foreground_undo = false;
+
+	/* Exit quickly if this transaction generated no undo. */
+	if (!UndoState.has_undo)
+		return;
+
+	/* This is a toplevel abort, so collapse all subtransaction state. */
+	while (UndoState.subxact->next != NULL)
+	{
+		UndoSubTransaction *cursubxact = UndoState.subxact;
+		UndoSubTransaction *nextsubxact = cursubxact->next;
+		int	i;
+
+		for (i = 0; i < UndoLogCategories; ++i)
+			if (!UndoRecPtrIsValid(nextsubxact->start_location[i]))
+				nextsubxact->start_location[i] = cursubxact->start_location[i];
+		pfree(cursubxact);
+		UndoState.subxact = nextsubxact;
+	}
+
+	/* Figure out whether there any temporary undo remaining to be processed. */
+	has_temporary_undo =
+		UndoRecPtrIsValid(UndoState.subxact->start_location[UNDO_TEMP]);
+
+	if (UndoState.is_undo)
+	{
+		/*
+		 * Regrettably, we seem to have failed when attempting to perform undo
+		 * actions. First, try to reschedule any undo request for later background
+		 * processing, so that we don't lose track of it.
+		 */
+		if (UndoState.my_request != NULL)
+			RescheduleUndoRequest(UndoState.manager, UndoState.my_request);
+
+		/*
+		 * XXX. If we have any temporary undo, we're in big trouble, because there's
+		 * no way for background workers to process it, and apparently we're also
+		 * unable to process it.  Should we throw FATAL?  Just leave the undo
+		 * unapplied and somehow retry at a later point in the session?
+		 */
+		if (has_temporary_undo)
+			/* experience_intense_sadness */;
+
+		return;
+	}
+
+	/*
+	 * If we have no UndoRequest, then the caller must perform foreground undo if
+	 * we have any temporary undo.
+	 */
+	if (UndoState.my_request == NULL)
+	{
+		if (has_temporary_undo)
+			*perform_foreground_undo = true;
+		else
+			ResetUndoState();
+		return;
+	}
+
+
+	/*
+	 * Update UndoRequest details.
+	 *
+	 * NB: Background processing facilities don't care about our temporary undo.
+	 */
+	request_size = UndoState.total_size[UNDO_PERMANENT] +
+		UndoState.total_size[UNDO_UNLOGGED];
+	end_location_logged =
+		GetUndoRecordEndPtr(UndoState.last_location[UNDO_PERMANENT],
+							UndoState.last_size[UNDO_PERMANENT]);
+	end_location_unlogged =
+		GetUndoRecordEndPtr(UndoState.last_location[UNDO_UNLOGGED],
+							UndoState.last_size[UNDO_UNLOGGED]);
+	FinalizeUndoRequest(UndoState.manager, UndoState.my_request, request_size,
+						UndoState.subxact->start_location[UNDO_PERMANENT],
+						UndoState.subxact->start_location[UNDO_UNLOGGED],
+						end_location_logged,
+						end_location_unlogged);
+
+	/*
+	 * We have generated undo for permanent and/or unlogged tables.  Is it OK for
+	 * that work to get handled in the background?
+	 */
+	if (PerformUndoInBackground(UndoState.manager, UndoState.my_request))
+	{
+		if (!has_temporary_undo)
+		{
+			/* No temporary undo, and everything else in the background. */
+			ResetUndoState();
+			return;
+		}
+
+		/*
+		 * Permanent and unloged undo in the background, but temporary undo is
+		 * still our problem.
+		 */
+		UndoState.my_request = NULL;
+		UndoState.subxact->start_location[UNDO_PERMANENT] = InvalidUndoRecPtr;
+		UndoState.subxact->start_location[UNDO_UNLOGGED] = InvalidUndoRecPtr;
+		UndoState.last_location[UNDO_PERMANENT] = InvalidUndoRecPtr;
+		UndoState.last_location[UNDO_UNLOGGED] = InvalidUndoRecPtr;
+		UndoState.last_size[UNDO_PERMANENT] = 0;
+		UndoState.last_size[UNDO_UNLOGGED] = 0;
+		UndoState.total_size[UNDO_PERMANENT] = 0;
+		UndoState.total_size[UNDO_UNLOGGED] = 0;
+	}
+
+	/* Caller needs to initiate foreground undo. */
+	*perform_foreground_undo = true;
+}
+
+/*
+ * Post-subtransaction commit cleanup of the undo state.
+ *
+ * Like AtCommit_UndoState, this must not fail.
+ */
+void
+AtSubCommit_UndoState(int level)
+{
+	UndoSubTransaction *cursubxact = UndoState.subxact;
+	UndoSubTransaction *nextsubxact = cursubxact->next;
+	int		i;
+
+	/* Exit quickly if the transaction or this subtransaction has no undo. */
+	if (!UndoState.has_undo || cursubxact->nestingLevel < level)
+		return;
+
+	/* If this fails, some other subtransaction failed to clean up properly. */
+	Assert(cursubxact->nestingLevel == level);
+
+	/* If this fails, things are really messed up. */
+	Assert(nextsubxact->nestingLevel < cursubxact->nestingLevel);
+
+	/*
+	 * If we have undo but our parent subtransaction doesn't, we can just adjust
+	 * the nesting level of the current UndoSubTransaction.
+	 */
+	if (nextsubxact->nestingLevel < cursubxact->nestingLevel - 1)
+	{
+		cursubxact->nestingLevel--;
+		return;
+	}
+
+	/* Merge our data with parent. */
+	for (i = 0; i < UndoLogCategories; ++i)
+		if (!UndoRecPtrIsValid(nextsubxact->start_location[i]))
+			nextsubxact->start_location[i] = cursubxact->start_location[i];
+	pfree(cursubxact);
+	UndoState.subxact = nextsubxact;
+}
+
+void
+AtSubAbort_UndoState(int level, bool *perform_foreground_undo)
+{
+	/* XXX DO STUFF */
+}
+
+/*
+ * Reset backend-local undo state.
+ */
+static void
+ResetUndoState(void)
+{
+	int		i;
+
+	UndoState.my_request = NULL;
+	UndoState.is_undo = false;
+	UndoState.is_background_undo = false;
+	UndoState.has_undo = false;
+	UndoState.subxact = &UndoTopState;
+	UndoTopState.nestingLevel = 1;
+	UndoTopState.next = NULL;
+
+	for (i = 0; i < UndoLogCategories; ++i)
+	{
+		UndoTopState.start_location[i] = InvalidUndoRecPtr;
+		UndoState.last_location[i] = InvalidUndoRecPtr;
+		UndoState.last_size[i] = 0;
+		UndoState.total_size[i] = 0;
+	}
+}
+
+/*
+ * Add the size of an undo record to the location where it starts to find the end
+ * location.
+ */
+static UndoRecPtr
+GetUndoRecordEndPtr(UndoRecPtr start_location, Size size)
+{
+	UndoLogNumber	logno = UndoRecPtrGetLogNo(start_location);
+	UndoLogOffset	offset = UndoRecPtrGetOffset(start_location);
+
+	offset = UndoLogOffsetPlusUsableBytes(offset, size);
+	return MakeUndoRecPtr(logno, offset);
+}
diff --git a/src/backend/lib/rbtree.c b/src/backend/lib/rbtree.c
index 33181e9211..bda870eab7 100644
--- a/src/backend/lib/rbtree.c
+++ b/src/backend/lib/rbtree.c
@@ -35,25 +35,6 @@
 #define RBTBLACK	(0)
 #define RBTRED		(1)
 
-/*
- * RBTree control structure
- */
-struct RBTree
-{
-	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
-
-	/* Remaining fields are constant after rbt_create */
-
-	Size		node_size;		/* actual size of tree nodes */
-	/* The caller-supplied manipulation functions */
-	rbt_comparator comparator;
-	rbt_combiner combiner;
-	rbt_allocfunc allocfunc;
-	rbt_freefunc freefunc;
-	/* Passthrough arg passed to all manipulation functions */
-	void	   *arg;
-};
-
 /*
  * all leafs are sentinels, use customized NIL name to prevent
  * collision with system-wide constant NIL which is actually NULL
@@ -122,6 +103,33 @@ rbt_create(Size node_size,
 	return tree;
 }
 
+/*
+ * rbt_initialize: initalize an empty RBTree
+ *
+ * This is just like rbt_create, except that the caller is responsible for
+ * allocating the memory.
+ */
+void
+rbt_initialize(RBTree *rbt,
+			   Size node_size,
+			   rbt_comparator comparator,
+			   rbt_combiner combiner,
+			   rbt_allocfunc allocfunc,
+			   rbt_freefunc freefunc,
+			   void *arg)
+{
+	Assert(node_size > sizeof(RBTNode));
+
+	rbt->root = RBTNIL;
+	rbt->node_size = node_size;
+	rbt->comparator = comparator;
+	rbt->combiner = combiner;
+	rbt->allocfunc = allocfunc;
+	rbt->freefunc = freefunc;
+
+	rbt->arg = arg;
+}
+
 /* Copy the additional data fields from one RBTNode to another */
 static inline void
 rbt_copy_data(RBTree *rbt, RBTNode *dest, const RBTNode *src)
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c324925c..0754269982 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,7 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undostate.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +150,7 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, UndoStateShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -266,6 +268,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	BTreeShmemInit();
 	SyncScanShmemInit();
 	AsyncShmemInit();
+	UndoStateShmemInit();
 
 #ifdef EXEC_BACKEND
 
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 4b42a1cf0b..aee8d3eba0 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -50,3 +50,4 @@ OldSnapshotTimeMapLock				42
 LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
+UndoRequestLock						46
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..cc00509699 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
new file mode 100644
index 0000000000..4ab28f6772
--- /dev/null
+++ b/src/include/access/undorequest.h
@@ -0,0 +1,76 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorequest.h
+ *		Undo request manager.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorequest.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOREQUEST_H
+#define UNDOREQUEST_H
+
+#include "access/transam.h"
+#include "access/undolog.h"
+#include "datatype/timestamp.h"
+
+struct UndoRequest;
+struct UndoRequestManager;
+typedef struct UndoRequest UndoRequest;
+typedef struct UndoRequestManager UndoRequestManager;
+
+/* Initialization functions. */
+extern Size EstimateUndoRequestManagerSize(Size capacity);
+extern void InitializeUndoRequestManager(UndoRequestManager *urm,
+										 LWLock *lock, Size capacity,
+										 Size soft_limit);
+
+/* Call this before inserting undo records. */
+extern UndoRequest *RegisterUndoRequest(UndoRequestManager *urm,
+										FullTransactionId fxid,
+										Oid dbid);
+
+/* Remember undo size and end locations. */
+extern void FinalizeUndoRequest(UndoRequestManager *urm,
+								UndoRequest *req,
+								Size size,
+								UndoRecPtr start_location_logged,
+								UndoRecPtr start_location_unlogged,
+								UndoRecPtr end_location_logged,
+								UndoRecPtr end_location_unlogged);
+
+/* Forget about an UndoRequest we don't need any more. */
+extern void UnregisterUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Attempt to dispatch UndoRequest for background processing. */
+extern bool PerformUndoInBackground(UndoRequestManager *urm, UndoRequest *req);
+
+/* Get work for background undo process. */
+extern UndoRequest *GetNextUndoRequest(UndoRequestManager *urm, Oid dbid,
+									   bool minimum_runtime_reached,
+									   Oid *out_dbid,
+									   UndoRecPtr *start_location_logged,
+									   UndoRecPtr *end_location_logged,
+									   UndoRecPtr *start_location_unlogged,
+									   UndoRecPtr *end_location_unlogged);
+
+/* Reschedule failed undo attempt. */
+extern void RescheduleUndoRequest(UndoRequestManager *urm, UndoRequest *req);
+
+/* Restore state after crash. */
+extern bool RecreateUndoRequest(UndoRequestManager *urm,
+								FullTransactionId fxid, Oid dbid,
+								bool is_logged,
+								UndoRecPtr start_location,
+								UndoRecPtr end_location,
+								Size size);
+extern UndoRequest *SuspendPreparedUndoRequest(UndoRequestManager *urm,
+											   FullTransactionId fxid);
+
+/* Get oldest registered FXID. */
+extern FullTransactionId UndoRequestManagerOldestFXID(UndoRequestManager *urm);
+
+#endif
diff --git a/src/include/access/undostate.h b/src/include/access/undostate.h
new file mode 100644
index 0000000000..44118c24dc
--- /dev/null
+++ b/src/include/access/undostate.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * undostate.h
+ *		Undo system state management and transaction integration.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undostate.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOSTATE_H
+#define UNDOSTATE_H
+
+#include "access/undolog.h"
+
+extern Size UndoStateShmemSize(void);
+extern void UndoStateShmemInit(void);
+
+extern void UndoStateAccumulateRecord(UndoLogCategory category,
+									  UndoRecPtr start_location,
+									  Size size);
+
+extern Oid InitializeBackgroundUndoState(bool minimum_runtime_reached);
+extern void FinishBackgroundUndo(void);
+
+extern void PerformUndoActions(int nestingLevel);
+
+extern void AtCommit_UndoState(void);
+extern void AtAbort_UndoState(bool *perform_foreground_undo);
+extern void AtSubCommit_UndoState(int level);
+extern void AtSubAbort_UndoState(int level, bool *perform_foreground_undo);
+
+/* XXX what about prepare? */
+
+#endif
diff --git a/src/include/lib/rbtree.h b/src/include/lib/rbtree.h
index 6d79a24015..ff6f99a932 100644
--- a/src/include/lib/rbtree.h
+++ b/src/include/lib/rbtree.h
@@ -28,8 +28,33 @@ typedef struct RBTNode
 	struct RBTNode *parent;		/* parent, or NULL (not RBTNIL!) if none */
 } RBTNode;
 
-/* Opaque struct representing a whole tree */
-typedef struct RBTree RBTree;
+/* Support functions to be provided by caller */
+typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
+typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
+typedef RBTNode *(*rbt_allocfunc) (void *arg);
+typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
+
+/*
+ * RBTree control structure
+ *
+ * This is declared here to make it possible to preallocate an object of
+ * the correct size, but callers should not access the members diretly.
+ */
+typedef struct RBTree
+{
+	RBTNode    *root;			/* root node, or RBTNIL if tree is empty */
+
+	/* Remaining fields are constant after rbt_create */
+
+	Size		node_size;		/* actual size of tree nodes */
+	/* The caller-supplied manipulation functions */
+	rbt_comparator comparator;
+	rbt_combiner combiner;
+	rbt_allocfunc allocfunc;
+	rbt_freefunc freefunc;
+	/* Passthrough arg passed to all manipulation functions */
+	void	   *arg;
+} RBTree;
 
 /* Available tree iteration orderings */
 typedef enum RBTOrderControl
@@ -53,18 +78,19 @@ struct RBTreeIterator
 	bool		is_over;
 };
 
-/* Support functions to be provided by caller */
-typedef int (*rbt_comparator) (const RBTNode *a, const RBTNode *b, void *arg);
-typedef void (*rbt_combiner) (RBTNode *existing, const RBTNode *newdata, void *arg);
-typedef RBTNode *(*rbt_allocfunc) (void *arg);
-typedef void (*rbt_freefunc) (RBTNode *x, void *arg);
-
 extern RBTree *rbt_create(Size node_size,
 						  rbt_comparator comparator,
 						  rbt_combiner combiner,
 						  rbt_allocfunc allocfunc,
 						  rbt_freefunc freefunc,
 						  void *arg);
+extern void rbt_initialize(RBTree *rbt,
+						   Size node_size,
+						   rbt_comparator comparator,
+						   rbt_combiner combiner,
+						   rbt_allocfunc allocfunc,
+						   rbt_freefunc freefunc,
+						   void *arg);
 
 extern RBTNode *rbt_find(RBTree *rbt, const RBTNode *data);
 extern RBTNode *rbt_leftmost(RBTree *rbt);
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 60d6d7be1b..f32afffab1 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
 		  test_rbtree \
 		  test_rls_hooks \
 		  test_shm_mq \
+		  test_undo_request_manager \
 		  unsafe_tests \
 		  worker_spi
 
diff --git a/src/test/modules/test_undo_request_manager/.gitignore b/src/test/modules/test_undo_request_manager/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_undo_request_manager/Makefile b/src/test/modules/test_undo_request_manager/Makefile
new file mode 100644
index 0000000000..5bc4695004
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_undo_request_manager/Makefile
+
+MODULE_big = test_undo_request_manager
+OBJS = test_undo_request_manager.o $(WIN32RES)
+PGFILEDESC = "test_undo_request_manager - test undo request manager code"
+
+EXTENSION = test_undo_request_manager
+DATA = test_undo_request_manager--1.0.sql
+
+REGRESS = test_undo_request_manager
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_undo_request_manager
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
new file mode 100644
index 0000000000..c79611b3b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/expected/test_undo_request_manager.out
@@ -0,0 +1,28 @@
+CREATE EXTENSION test_undo_request_manager;
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+ERROR:  unable to register undo request #2
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+ urm_simple_test 
+-----------------
+ {1001,1002}
+(1 row)
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+                urm_simple_test                 
+------------------------------------------------
+ {1001,1006,1002,1007,1003,1008,1004,1009,1005}
+(1 row)
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
+                   urm_simple_test                   
+-----------------------------------------------------
+ {1001,1010,1002,1009,1003,1008,1004,1007,1005,1006}
+(1 row)
+
diff --git a/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
new file mode 100644
index 0000000000..6611e040b6
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/sql/test_undo_request_manager.sql
@@ -0,0 +1,16 @@
+CREATE EXTENSION test_undo_request_manager;
+
+-- not enough space
+select urm_simple_test(1, '{10000,20000}');
+
+-- simple case
+select urm_simple_test(2, '{10000,20000}');
+
+-- should alternate between early and large requests in order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,1000000,1000000,1000000}');
+
+-- should alternate between early and large requests, but the large requests
+-- should be processed in reverse order
+select urm_simple_test(10,
+'{10000,20000,30000,40000,50000,1000000,2000000,3000000,4000000,50000000}');
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
new file mode 100644
index 0000000000..30ff471c23
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql
@@ -0,0 +1,9 @@
+/* src/test/modules/test_undo_request_manager/test_undo_request_manager--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_undo_request_manager" to load this file. \quit
+
+CREATE FUNCTION urm_simple_test(capacity pg_catalog.int4,
+								requests pg_catalog.int8[])
+    RETURNS pg_catalog.int8[] STRICT
+	AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.c b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
new file mode 100644
index 0000000000..8b994283c8
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.c
@@ -0,0 +1,139 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_undo_request_manager.c
+ *		Test undo request manager.
+ *
+ * Copyright (c) 2013-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/test_undo_request_manager/undo_request_manager.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undorequest.h"
+#include "catalog/pg_type_d.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "utils/array.h"
+
+PG_MODULE_MAGIC;
+PG_FUNCTION_INFO_V1(urm_simple_test);
+
+/*
+ * SQL-callable test function.  We create an UndoRequestManager in
+ * backend-private memory here and exercise it a bit to see if it breaks.
+ *
+ * The first argument is the capacity of the UndoRequestManager as an integer.
+ *
+ * The second argument is 1-dimensional bigint array, where each subarray
+ * contains a hypothetical undo size.
+ *
+ * This function registers and inserts all the requests (failing if space is
+ * exhausted) with fake, sequentially assigned transaction IDs, and then
+ * fetches them back one by one. The return value is an array of fake
+ * transaction IDs in the order they were returned.
+ *
+ * This test doesn't simulate undo failure, multi-database operation, or
+ * prepared transactions.
+ */
+Datum
+urm_simple_test(PG_FUNCTION_ARGS)
+{
+	int64	capacity = PG_GETARG_INT32(0);
+	ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
+	Datum	  *darray;
+	int			nentries;
+	Datum	  *dresult;
+	ArrayType *result;
+	UndoRequestManager *urm;
+	const UndoRecPtr SomeValidUndoRecPtr = InvalidUndoRecPtr + 1;
+	int			i;
+	FullTransactionId fake_fxid = FullTransactionIdFromEpochAndXid(0, 1000);
+
+	/* Require positive capacity. */
+	if (capacity <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("undo request manager capacity must be a positive integer")));
+
+	/* Sanity-check and deconstruct array. */
+	if (ARR_NDIM(array) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("array must have exactly 1 dimension")));
+	if (array_contains_nulls(array))
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("cannot work with arrays containing NULLs")));
+	deconstruct_array(array, INT8OID, 8, FLOAT8PASSBYVAL, 'd',
+					  &darray, NULL, &nentries);
+
+	/*
+	 * Initialize UndoRequestManager. We have to supply an LWLock; rather than
+	 * creating a new one somewhere, just use our own backendLock. These locks
+	 * aren't that heavily trafficked and we won't have any reason to take it
+	 * for any other purpose while the UndoRequstManager holds it, so this
+	 * should be safe enough.
+	 *
+	 * We make the soft limit equal to the full capacity here for testing
+	 * purposes, which means that we should always succeed in dispatching to
+	 * the background.
+	 */
+	urm = palloc(EstimateUndoRequestManagerSize(capacity));
+	InitializeUndoRequestManager(urm, &MyProc->backendLock,
+								 capacity, capacity);
+
+	/* Insert entries as provided by caller. */
+	for (i = 0; i < nentries; ++i)
+	{
+		int64	size = DatumGetInt64(darray[i]);
+		UndoRequest *req;
+
+		FullTransactionIdAdvance(&fake_fxid);
+
+		req = RegisterUndoRequest(urm, fake_fxid, MyDatabaseId);
+		if (req == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to register undo request #%d", i + 1)));
+		UndoRequestSetStartLocation(urm, req, true, SomeValidUndoRecPtr);
+		FinalizeUndoRequest(urm, req, size,
+							SomeValidUndoRecPtr,
+							InvalidUndoRecPtr);
+		if (!PerformUndoInBackground(urm, req))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unable to background undo request #%d", i + 1)));
+	}
+
+	/* Now get the entries back. */
+	dresult = palloc(nentries * sizeof(Datum));
+	for (i = 0; true; ++i)
+	{
+		UndoRequest *req;
+		UndoRecPtr	p[4];
+
+		/* Get some work. */
+		req = GetNextUndoRequest(urm, MyDatabaseId, true,
+								 &fake_fxid, &p[0], &p[1], &p[2], &p[3]);
+		if (req == NULL)
+			break;
+		if (i >= nentries)
+			elog(ERROR, "found more undo requests than were inserted");
+
+		/* Save the fake FXID. */
+		dresult[i] =
+			Int64GetDatum((int64) U64FromFullTransactionId(fake_fxid));
+
+		/* Report that we successfully processed the imaginary undo. */
+		UnregisterUndoRequest(urm, req);
+	}
+
+	/* Put result into array form. */
+	result = construct_array(dresult, i, INT8OID, 8, FLOAT8PASSBYVAL, 'd');
+	PG_RETURN_ARRAYTYPE_P(result);
+}
diff --git a/src/test/modules/test_undo_request_manager/test_undo_request_manager.control b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
new file mode 100644
index 0000000000..0a340e9843
--- /dev/null
+++ b/src/test/modules/test_undo_request_manager/test_undo_request_manager.control
@@ -0,0 +1,4 @@
+comment = 'Test code for undo request manager'
+default_version = '1.0'
+module_pathname = '$libdir/test_undo_request_manager'
+relocatable = true
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 432d2d812e..e0049245a9 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2536,6 +2536,11 @@ ULONG
 ULONG_PTR
 UV
 UVersionInfo
+UndoRecPtr
+UndoRequest
+UndoRequestManager
+UndoRequestNode
+UndoRequestSource
 Unique
 UniquePath
 UniquePathMethod
-- 
2.17.2 (Apple Git-113)

#292Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Robert Haas (#291)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

I was doing some testing for the scenario where the undo written by a
transaction overflows to multiple undo logs. For that I've modified
the following macro:
#define UndoLogMaxSize (1024 * 1024) /* 1MB undo log size */
(I should have used the provided pg_force_switch_undo though..)

I'm getting the following assert failure while performing the recovery
with the same.
"TRAP: FailedAssertion("slot->meta.status == UNDO_LOG_STATUS_FULL",
File: "undolog.c", Line: 997)"

I found that we don't emit an WAL record when we update the
slot->meta.status as UNDO_LOG_STATUS_FULL. If we don't that, after
crash recovery, some new transaction may use that undo log which is
wrong, IMHO. Am I missing something?

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

#293Thomas Munro
thomas.munro@gmail.com
In reply to: Kuntal Ghosh (#292)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Aug 30, 2019 at 8:27 PM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

I'm getting the following assert failure while performing the recovery
with the same.
"TRAP: FailedAssertion("slot->meta.status == UNDO_LOG_STATUS_FULL",
File: "undolog.c", Line: 997)"

I found that we don't emit an WAL record when we update the
slot->meta.status as UNDO_LOG_STATUS_FULL. If we don't that, after
crash recovery, some new transaction may use that undo log which is
wrong, IMHO. Am I missing something?

Thanks, right, that status logging is wrong, will fix in next version.

--
Thomas Munro
https://enterprisedb.com

#294vignesh C
vignesh21@gmail.com
In reply to: Thomas Munro (#293)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Hi Thomas,

While testing one of the recovery scenarios I found one issue:
FailedAssertion("!(logno == context->recovery_logno)

The details of the same is mentioned below:
The context's try_location was not updated in
UndoLogAllocateInRecovery, in PrepareUndoInsert the try_location was
updated with the undo record size.
In the subsequent UndoLogAllocateInRecovery as the value for
try_location was not initialized but only updated with the size the
logno will always not match if the recovery_logno is non zero and the
assert fails.

Fixed by setting the try_location in UndoLogAllocateInRecovery,
similar to try_location setting in UndoLogAllocate.
Patch for the same is attached.

Please have a look and add the changes in one of the upcoming version.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Show quoted text

On Mon, Sep 2, 2019 at 9:53 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Fri, Aug 30, 2019 at 8:27 PM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

I'm getting the following assert failure while performing the recovery
with the same.
"TRAP: FailedAssertion("slot->meta.status == UNDO_LOG_STATUS_FULL",
File: "undolog.c", Line: 997)"

I found that we don't emit an WAL record when we update the
slot->meta.status as UNDO_LOG_STATUS_FULL. If we don't that, after
crash recovery, some new transaction may use that undo log which is
wrong, IMHO. Am I missing something?

Thanks, right, that status logging is wrong, will fix in next version.

--
Thomas Munro
https://enterprisedb.com

Attachments:

0001-FailedAssertion-logno-context-recovery_logno-fix.patchapplication/x-patch; name=0001-FailedAssertion-logno-context-recovery_logno-fix.patchDownload
From e69a9eea71562434cc0512871a9b6d7424ce93ec Mon Sep 17 00:00:00 2001
From: Vigneshwaran c <vignesh21@gmail.com>
Date: Fri, 30 Aug 2019 11:45:03 +0530
Subject: [PATCH] FailedAssertion("!(logno == context->recovery_logno) fix

    try_location was not updated in UndoLogAllocateInRecovery, in
    PrepareUndoInsert the try_location was updated with the undo record size.
    In the subsequent UndoLogAllocateInRecovery as the value for try_location
    was not initialized but only updated with the size the logno will always
    not match if the recovery_logno is non zero and the assert fails. Fixed
    by setting the try_location in UndoLogAllocateInRecovery, similar to
    try_location setting in UndoLogAllocate.

    Patch by Vigneshwaran C, reviewed by Dilip Kumar.
---
 src/backend/access/undo/undolog.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/backend/access/undo/undolog.c b/src/backend/access/undo/undolog.c
index 073221c..9acd570 100644
--- a/src/backend/access/undo/undolog.c
+++ b/src/backend/access/undo/undolog.c
@@ -960,6 +960,14 @@ UndoLogAllocateInRecovery(UndoLogAllocContext *context,
 			*need_xact_header =
 				context->try_location == InvalidUndoRecPtr &&
 				slot->meta.unlogged.insert == slot->meta.unlogged.this_xact_start;
+
+			/*
+			 * If no try_location was passed in, or if we switched logs, then we'll
+			 * return the current insertion point.
+			 */
+			if (context->try_location == InvalidUndoRecPtr)
+				context->try_location = MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert);
+
 			*last_xact_start = slot->meta.unlogged.last_xact_start;
 			context->recovery_logno = slot->logno;
 
-- 
1.8.3.1

#295Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: vignesh C (#294)
Re: POC: Cleaning up orphaned files using undo logs

On 2019-Sep-06, vignesh C wrote:

Hi Thomas,

While testing one of the recovery scenarios I found one issue:
FailedAssertion("!(logno == context->recovery_logno)

I marked this patch Waiting on Author.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#296Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Thomas Munro (#293)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

While testing zheap over undo apis, we've found the following
issues/scenarios that might need some fixes/discussions:

1. In UndoLogAllocateInRecovery, when we find the current log number
from the list of registered blocks, we don't check whether the
block->in_use flag is true or not. In XLogResetInsertion, we just
reset in_use flag without reseting the blocks[]->rnode information.
So, if we don't check the in_use flag, it's possible that we'll
consult some block information from the previous WAL record. IMHO,
just adding an in_use check in UndoLogAllocateInRecovery will solve
the problem.

2. A transaction, inserts one undo record and generated a WAL record
for the same, say at WAL location 0/2000A000. Next, the undo record
gets discarded and WAL is generated to update the meta.discard pointer
at location 0/2000B000 At the same time, an ongoing checkpoint with
checkpoint.redo at 0/20000000 flushes the latest meta.discard pointer.
Now, the system crashes.
Now, the recovery starts from the location 0/20000000. When the
recovery of 0/2000A000 happens, it sees the undo record that it's
about to insert, is already discarded as per meta.discard (flushed by
checkpoint). In this case, should we just skip inserting the undo
record?

3. Currently, we create a backup image of the unlogged part of the
undo log's metadata only when some backend allocates some space from
the undo log (in UndoLogAllocate). This helps us restore the unlogged
meta part after a checkpoint.
When we perform an undo action, we also update the undo action
progress and emit an WAL record. The same operation can performed by
the undo worker which doesn't allocate any space from the undo log.
So, if an undo worker emits an WAL record to update undo action
progress after a checkpoint, it'll not be able to WAL log the backup
image of the meta unlogged part. IMHO, this breaks the recovery logic
of unlogged part of undo meta.

Thoughts?

On Mon, Sep 2, 2019 at 9:47 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Fri, Aug 30, 2019 at 8:27 PM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

I'm getting the following assert failure while performing the recovery
with the same.
"TRAP: FailedAssertion("slot->meta.status == UNDO_LOG_STATUS_FULL",
File: "undolog.c", Line: 997)"

I found that we don't emit an WAL record when we update the
slot->meta.status as UNDO_LOG_STATUS_FULL. If we don't that, after
crash recovery, some new transaction may use that undo log which is
wrong, IMHO. Am I missing something?

Thanks, right, that status logging is wrong, will fix in next version.

--
Thomas Munro
https://enterprisedb.com

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

#297Thomas Munro
thomas.munro@gmail.com
In reply to: Kuntal Ghosh (#296)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 16, 2019 at 5:27 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

While testing zheap over undo apis, we've found the following
issues/scenarios that might need some fixes/discussions:

Thanks!

1. In UndoLogAllocateInRecovery, when we find the current log number
from the list of registered blocks, we don't check whether the
block->in_use flag is true or not. In XLogResetInsertion, we just
reset in_use flag without reseting the blocks[]->rnode information.
So, if we don't check the in_use flag, it's possible that we'll
consult some block information from the previous WAL record. IMHO,
just adding an in_use check in UndoLogAllocateInRecovery will solve
the problem.

Agreed. I added a line to break out of that loop if !block->in_use.

BTW I am planning to simplify that code considerably, based on a plan
to introduce a new rule: there can be only one undo record and
therefore only one undo allocation per WAL record.

2. A transaction, inserts one undo record and generated a WAL record
for the same, say at WAL location 0/2000A000. Next, the undo record
gets discarded and WAL is generated to update the meta.discard pointer
at location 0/2000B000 At the same time, an ongoing checkpoint with
checkpoint.redo at 0/20000000 flushes the latest meta.discard pointer.
Now, the system crashes.
Now, the recovery starts from the location 0/20000000. When the
recovery of 0/2000A000 happens, it sees the undo record that it's
about to insert, is already discarded as per meta.discard (flushed by
checkpoint). In this case, should we just skip inserting the undo
record?

I see two options:

1. We make it so that if you're allocating in recovery and discard >
insert, we'll just set discard = insert so you can proceed. The code
in undofile_get_segment_file() already copes with missing files during
recovery.

2. We skip the insert as you said.

I think option 1 is probably best, otherwise you have to cope with
failure to insert by skipping, as you said.

3. Currently, we create a backup image of the unlogged part of the
undo log's metadata only when some backend allocates some space from
the undo log (in UndoLogAllocate). This helps us restore the unlogged
meta part after a checkpoint.
When we perform an undo action, we also update the undo action
progress and emit an WAL record. The same operation can performed by
the undo worker which doesn't allocate any space from the undo log.
So, if an undo worker emits an WAL record to update undo action
progress after a checkpoint, it'll not be able to WAL log the backup
image of the meta unlogged part. IMHO, this breaks the recovery logic
of unlogged part of undo meta.

I thought that was OK because those undo data updates don't depend on
the insert pointer. But I see what you mean: the next modification of
the page that DOES depend on the insert pointer might not log the
meta-data if it's not the first WAL record to touch it after a
checkpoint. Rats. I'll have to think about that some more.

--
Thomas Munro
https://enterprisedb.com

#298Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Thomas Munro (#297)
Re: POC: Cleaning up orphaned files using undo logs

Hello Thomas,

On Mon, Sep 16, 2019 at 11:23 AM Thomas Munro <thomas.munro@gmail.com> wrote:

1. In UndoLogAllocateInRecovery, when we find the current log number
from the list of registered blocks, we don't check whether the
block->in_use flag is true or not. In XLogResetInsertion, we just
reset in_use flag without reseting the blocks[]->rnode information.
So, if we don't check the in_use flag, it's possible that we'll
consult some block information from the previous WAL record. IMHO,
just adding an in_use check in UndoLogAllocateInRecovery will solve
the problem.

Agreed. I added a line to break out of that loop if !block->in_use.

I think we should skip the block if !block->in_use. Because, the undo
buffer can be registered in a subsequent block as well. For different
operations, we can use different block_id to register the undo buffer
in the redo record.

BTW I am planning to simplify that code considerably, based on a plan
to introduce a new rule: there can be only one undo record and
therefore only one undo allocation per WAL record.

Okay. In that case, we need to rethink the cases for multi-inserts and
non-inlace updates both of which currently inserts multiple undo
record corresponding to a single WAL record. For multi-inserts, it can
be solved easily by moving all the offset information in the payload.
But, for non-inlace updates, we insert one undo record for the update
and one for the insert. Wondering whether we've to insert two WAL
records - one for update and one for the new insert.

2. A transaction, inserts one undo record and generated a WAL record
for the same, say at WAL location 0/2000A000. Next, the undo record
gets discarded and WAL is generated to update the meta.discard pointer
at location 0/2000B000 At the same time, an ongoing checkpoint with
checkpoint.redo at 0/20000000 flushes the latest meta.discard pointer.
Now, the system crashes.
Now, the recovery starts from the location 0/20000000. When the
recovery of 0/2000A000 happens, it sees the undo record that it's
about to insert, is already discarded as per meta.discard (flushed by
checkpoint). In this case, should we just skip inserting the undo
record?

I see two options:

1. We make it so that if you're allocating in recovery and discard >
insert, we'll just set discard = insert so you can proceed. The code
in undofile_get_segment_file() already copes with missing files during
recovery.

Interesting. This should work.

3. Currently, we create a backup image of the unlogged part of the
undo log's metadata only when some backend allocates some space from
the undo log (in UndoLogAllocate). This helps us restore the unlogged
meta part after a checkpoint.
When we perform an undo action, we also update the undo action
progress and emit an WAL record. The same operation can performed by
the undo worker which doesn't allocate any space from the undo log.
So, if an undo worker emits an WAL record to update undo action
progress after a checkpoint, it'll not be able to WAL log the backup
image of the meta unlogged part. IMHO, this breaks the recovery logic
of unlogged part of undo meta.

I thought that was OK because those undo data updates don't depend on
the insert pointer. But I see what you mean: the next modification of
the page that DOES depend on the insert pointer might not log the
meta-data if it's not the first WAL record to touch it after a
checkpoint. Rats. I'll have to think about that some more.

Cool.

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

#299Robert Haas
robertmhaas@gmail.com
In reply to: Kuntal Ghosh (#298)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 16, 2019 at 11:09 AM Kuntal Ghosh
<kuntalghosh.2007@gmail.com> wrote:

Okay. In that case, we need to rethink the cases for multi-inserts and
non-inlace updates both of which currently inserts multiple undo
record corresponding to a single WAL record. For multi-inserts, it can
be solved easily by moving all the offset information in the payload.
But, for non-inlace updates, we insert one undo record for the update
and one for the insert. Wondering whether we've to insert two WAL
records - one for update and one for the new insert.

No, I think the solution is to put the information about both halves
of the non-in-place update in the same undo record. I think the only
reason why that's tricky is because we've got two block numbers and
two offsets, and the only reason that's a problem is because
UnpackedUndoRecord only has one field for each of those things, and
that goes right back to Heikki's comments about the format not being
flexible enough. If you see some other problem, it would be
interesting to know what it is.

One thing I've been thinking about is: suppose that you're following
the undo chain for a tuple and you come to a non-in-place update
record. Can you get confused? I don't think so, because you can
compare the TID for which you're following the chain to the new TID
and the old TID in the record and it should match one or the other but
not both. But I don't think you even really need to do that much: if
you started with a deleted item, the first thing in the undo chain has
to be a delete or non-in-place update that got rid of it. And if you
started with a non-deleted item, then the beginning of the undo chain,
if it hasn't been discarded yet, will be the insert or non-in-place
update that created it. There's nowhere else that you can hit a
non-in-place update, and no room (that I can see) for any ambiguity.

It seems to me that zheap went wrong in ending up with separate undo
types for in-place and non-in-place updates. Why not just have ONE
kind of undo record that describes an update, and allow that update to
have either one TID or two TIDs depending on which kind of update it
is? There may be a reason, but I don't know what it is, unless it's
just that the UnpackedUndoRecord idea that I invented wasn't flexible
enough and nobody thought of generalizing it. Curious to hear your
thoughts on this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#300Thomas Munro
thomas.munro@gmail.com
In reply to: Kuntal Ghosh (#298)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Sep 17, 2019 at 3:09 AM Kuntal Ghosh <kuntalghosh.2007@gmail.com> wrote:

On Mon, Sep 16, 2019 at 11:23 AM Thomas Munro <thomas.munro@gmail.com> wrote:

Agreed. I added a line to break out of that loop if !block->in_use.

I think we should skip the block if !block->in_use. Because, the undo
buffer can be registered in a subsequent block as well. For different
operations, we can use different block_id to register the undo buffer
in the redo record.

Oops, right. So it should just be added to the if condition. Will do.

--
Thomas Munro
https://enterprisedb.com

#301Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#299)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 16, 2019 at 10:37 PM Robert Haas <robertmhaas@gmail.com> wrote:

It seems to me that zheap went wrong in ending up with separate undo
types for in-place and non-in-place updates. Why not just have ONE
kind of undo record that describes an update, and allow that update to
have either one TID or two TIDs depending on which kind of update it
is? There may be a reason, but I don't know what it is, unless it's
just that the UnpackedUndoRecord idea that I invented wasn't flexible
enough and nobody thought of generalizing it. Curious to hear your
thoughts on this.

I think not only TID's, but we also need to two uur_prevundo (previous
undo of the block) pointers. This is required both when we have to
perform page-wise undo and chain traversal during visibility checks.
So, we can keep a combination of TID and prevundo.

The other thing is that during rollback when we collect the undo for
each page, applying the action for this undo need some thoughts. For
example, we can't apply the undo to rollback both Insert and
non-inplace-update as both are on different pages. The reason is that
the page where non-inplace-update has happened might have more undos
that need to be applied before this. We can somehow make this undo
available to apply while collecting undo for both the heap pages. I
think there is also a need to
identify which TID is for Insert and which is for non-inplace-update
part of the operation because we won't know that while applying undo
unless we check the state of a tuple on the page.

So, with this idea, we will make one undo record part of multiple
chains which might need some consideration at different places like
above.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#302Michael Paquier
michael@paquier.xyz
In reply to: Thomas Munro (#300)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Sep 17, 2019 at 10:03:20AM +1200, Thomas Munro wrote:

Oops, right. So it should just be added to the if condition. Will do.

It's been a couple of months and the discussion has stale. It seems
also that the patch was waiting for an update. So I am marking it as
RwF for now. Please feel free to update it if you feel that's not
adapted.
--
Michael

#303Thomas Munro
thomas.munro@gmail.com
In reply to: Michael Paquier (#302)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Nov 28, 2019 at 3:45 PM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Sep 17, 2019 at 10:03:20AM +1200, Thomas Munro wrote:

Oops, right. So it should just be added to the if condition. Will do.

It's been a couple of months and the discussion has stale. It seems
also that the patch was waiting for an update. So I am marking it as
RwF for now. Please feel free to update it if you feel that's not
adapted.

Thanks. We decided to redesign a couple of aspects of the undo
storage and record layers that this patch was intended to demonstrate,
and work on that is underway. More on that soon.

#304Antonin Houska
ah@cybertec.at
In reply to: Thomas Munro (#303)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Thomas Munro <thomas.munro@gmail.com> wrote:

On Thu, Nov 28, 2019 at 3:45 PM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Sep 17, 2019 at 10:03:20AM +1200, Thomas Munro wrote:

Oops, right. So it should just be added to the if condition. Will do.

It's been a couple of months and the discussion has stale. It seems
also that the patch was waiting for an update. So I am marking it as
RwF for now. Please feel free to update it if you feel that's not
adapted.

Thanks. We decided to redesign a couple of aspects of the undo
storage and record layers that this patch was intended to demonstrate,
and work on that is underway. More on that soon.

As my boss expressed in his recent blog post, we'd like to contribute to the
zheap development, and a couple of developers from other companies are
interested in this as well. Amit Kapila suggested that the "cleanup of
orphaned files" feature is a good start point in getting the code into PG
core, so I've spent some time on it and tried to rebase the patch set.

In fact what I did is not mere rebasing against the current master branch -
I've also (besides various bug fixes) done some design changes.

Incorporated the new Undo Record Set (URS) infrastructure
---------------------------------------------------------

This is also pointed out in [0]/messages/by-id/CA+TgmoZwkqXs3hpT_nd17fyMnZDkg8yU=5kG+HQw+80rumiwUA@mail.gmail.com.

I started from [1]https://github.com/EnterpriseDB/zheap/tree/undo-record-set and tried to implement some missing parts (e.g. proper
closing of the URSs after crash), introduced UNDO_DEBUG preprocessor macro
which makes the undo log segments very small and fixed some bugs that the
small segments exposed.

The most significant change I've done was removal of the undo requests from
checkpoint. I could not find any particular bug / race conditions related to
including the requests into the checkpoint, but I concluded that it's easier
to think about consistency and checkpoint timings if we scan the undo log on
restart (after recovery has finished) and create the requests from scratch.

[2]: https://github.com/cybertec-postgresql/postgres/tree/undo-record-set-ah

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

Since the concept of undo requests is closely related to the undo worker, I
removed undorequest.c too. The new (much simpler) undo worker gets the
information on incomplete / aborted transactions from the undo log as
mentioned above.

SMGR enhancement
----------------

I used the 0001 patch from [3]/messages/by-id/CA+hUKGJfznxutTwpMLKPMjU_k9GhERoogyxx2Sf105LOA2La2A@mail.gmail.com rather than [4]/messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com, although it's more invasive
because I noticed somewhere in the discussion that there should be no reserved
database OID for the undo log. (InvalidOid cannot be used because it's already
in use for shared catalogs.)

Components added
----------------

pg_undo_dump utility and test framework for undoread.c. BTW, undoread.c seems
to need some refactoring.

Following are a few areas which are not implemented yet because more
discussion is needed there:

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed. If the
URS only consists of one chunk, the discard pointer can simply be advanced
to the end of the chunk. But if there are multiple chunks, the discard
worker might need to scan quite some amount of the undo log because (IIUC)
chunks of different URSs can be interleaved (if there's not enough space
for a record in the log 1, log 2 is used, but before we get to discarding,
another transaction could have added its chunk to the log 1) and because
the chunks only contain links backwards, not forward. If we added the
forward link to the chunk header, it would make chunk closing more
complex.

How about storing the type header (which includes XID) in each chunk
instead of only the first chunk of the URS? Thus we'd be able to check for
each chunk separately whether it can be discarded.

* if the URS belongs to an aborted transaction or a transaction that could
not finish due to server crash, the transaction status alone does not
justify discarding: we also need to be sure that the underlying undo
records have been applied. So if we want to do without the
oldestXidHavingUndo variable, some sort of undo progress tracking is
needed, see below.

Do not execute the same undo record multiple times
--------------------------------------------------

Although I've noticed in the zheap code that it checks whether particular undo
action was already undone, I think this functionality fits better in the URS
layer. Also note in [1]https://github.com/EnterpriseDB/zheap/tree/undo-record-set (i.e. the undo layer, no zheap) that the header
comment of AtSubAbort_XactUndo() refers to this problem.

I've tried to implement such a thing (not included in this patch) by adding
last_rec_applied field to UndoRecordSetChunkHeader. When the UNDO stage of the
transaction starts, this field is set to the last undo record of given chunk,
and once that record is applied, the pointer moves to the previous record in
terms of undo pointer (i.e. the next record to be applied - the records are
applied in reverse order) and so on. For recovery purposes, the pointer is
maintained in a similar way as the ud_insertion_point field of
UndoPageHeaderData. However, although I haven't tested performance yet, I
wonder if it's o.k. to lock the buffer containing the chunk header exclusively
for each undo record execution. I wonder if there's a better place to store
the progress information, maybe at page level?

I can spend more time on this project, but need a hint which part I should
focus on. Other hackers might have the same problem. Thanks for any
suggestions.

[0]: /messages/by-id/CA+TgmoZwkqXs3hpT_nd17fyMnZDkg8yU=5kG+HQw+80rumiwUA@mail.gmail.com
[1]: https://github.com/EnterpriseDB/zheap/tree/undo-record-set
[2]: https://github.com/cybertec-postgresql/postgres/tree/undo-record-set-ah
[3]: /messages/by-id/CA+hUKGJfznxutTwpMLKPMjU_k9GhERoogyxx2Sf105LOA2La2A@mail.gmail.com
[4]: /messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20201112.tgzapplication/x-gzipDownload
�d�_�}kw�F��~�~E�w��L�"��{��d[�V���frx@��&J�$�����h<I��������$�������z�<��o��-�0�����k�Z�^O��1���2�]�T����:���n����i����>�I�[���P��/kG�#��'��J�?�������������!|����oz����l�7�msiF���V���j�c<���^_���5D��6������:s�o��_���t���Bz�p���eYF�l�]�3�u��Ag`fm�?���%�|O\;KaD����mX�]l��{��������O�����w�q��c5�����9/����!��8��b�0����V[�[F��{����cE/�O����@�gh)-E����=�)������*t�t5�2MK��@x����zv(�����0L�����)A{��	O���8s��
,�����W�nW��da�	EtgF����;{��o}
wq4��#f�\��f��B��a7����a{���5�6�	����{�����I�&/�t4Dd~rD�/���&��Q�pi��]�'������
	"�5D�B�X4��������9���|C[�a�N����p&��LX�|� �����)
�2-��|�/`B���������e��S�������0�ha7���p�X���DA�P����*�%���+0[E���u�����+J��94�<�����s�oT�d�DW�� ��3g+/�w���Z�8���.���������&(�
������,�04o�}�>�:���~{�����qt�Z,���u������������^����?�����
;�����;o��_�_�������(p��C���^������B����������5_�K���f��������n���6�z�����T���o���
�
���a�6��?_;�����%H�*�#������
�����]����'>�C���X	����������������M-P<��j�P����j��jaD�+wng{[_-�T�����3�;�S49������E=����1���\��G�Z�u�'w@%�N��Z�8�9�]��r��4
G~��X�]R-�3[�j
�jE�6,]�pI��YR��I�m�>
L/4�]9R�^f��������8��p�#���������E�Hv��k����r���v���~8
��p�Z;, y$
�����;@8��z`;t���n��C��E�2L�4�`����=��.}����W�����9|O�aR�z3�zbz�o�n�x�j�"E�*�!�q���Q����,d�����i�X����N�	�$S������R�Z������U����lG!�����Ui�U��*_!Z/���)[K���4|��^�������5q%r�e	^�^XR+��M��������Y`��`�u��h�z�W9�Z[���3w���p<�S/`{��&){#&(�������j���%_y=7W�����k������� P�r���W�����owfv��v��fw:v��5m�9#K\��.�BU���b���l��s����2z�]I�P�X�;���Ntx����Z�x$�Nd"-�����lrv|3��?��=���b��D$"N�F���	�	>k�����������?�7���;;B�I�bO$�E�����w`;M@������.����I��a�2}��S�Q���5���� w
�����/���LH��+!q�mN�V�7��gZ��YFkT�3e-�Q��bP���!�\���k�Z�&W(|�8��o���O.��d������f�{���������_v��:|�)��%�4�nM���;�j�-*������������C����`���~����[p�e\H�9�������	�T��t�+�&j2%5,�/Qm7���`0��������o5�WcWe��q��U�������P~��`��'T^�
��[�������U`����7D`���3��wM1>�9�JPy���������2�����������B|�������i�z
������p�����7����G����x~x�o�7Xm��7�^���O�oD~0��O������k�wn�������sW�����n�*�s�����qz����M�l���������f�������FQ��(��
��c���V�'�<?}O^#b'�Bh�"�2��������N������G8]���_�OO�O�����+}�o�w�L�����\Tb3���U��������_��;���B���8+�n�kq	�G������E�2��8d�����DZ�T�G����!?����|�jkh�M�Hi�<6`Il7��3�}X��]-s�:����<8L&R*CE{���^4^�k'�<����Me�_:xF�#��&&�K�9!��),��	$�I�+`v-X����/�;�}�	���������)1V�n��UyI��V��2��	T��M�7M�O�Bq�[������� ���7�~^��k����\�]��/������\����`m��7����ov��X�qy�;��������'3�oP�C=
a*�C^�	+k�7��*/W�����hfMa[���N�o=a�v�X���`��9��5��x�� +)v���W�������_�?OP������O�(�����R������uEU1�,���������l�w����{����_�b���p���#B@+�������&(����t�;;�D����hT1�0�����v��x��������(-����_�����sdh��Iv��i�������K��{���Q���<�����l��8��s��{�h�gj�����6H�7�����I�������gH���//Z�j2�vjT�����*�04���-'�����-|�����Q���z��_kd���g�zl��D9�l7<����l=�AQ��`���d���0U7�����/6K�������5���ZNH)�$Q�~]q��a�mG��L�5��2�����Vq��P��3����o�Uyv�2&GF���:1�^�L���3s�#
O�I���Z�^�]�>����$GS�?�?7c��������p����%m�Z�"�k��`��m39��t1&]�����\���5���z��*���QQe{�u�{ium`���~�=�r5P����Y2���������!�����V!p�(*��#��Q�x%� 0Q`���u�VXW�w�X�m�����Qj��g:��
�m�~2�������Gf�Z�*�T��^����~i]��=Tkf�x^�cio:�E����m2������l������6���T2�N���5+O���6���P2�n�xz������?�D�&�c9=��������h���yic� N�����d�~4��%���G4�b"����+�m:C9�N�tP��4=������;91����G�L�,SL�����Y���h�)�����@I�:��M������3�Z��o���m9�1�����u-�c])�Ml!S
�;��o�&�|�������]�4���n����|G%E������1������<{�-6�
�-��td�,nH|����D�
 ������Wx#�������� �X5���:�0=���QS�����]�M����H
�14�]e^H0��h_Lq�:H�c�V>������M�H�����k��A��q��f��}s���3��MZc���$qs����m*��I��/���t�v��S���,����~��a����9�2P�<�P��h��-&1fMVF�{'yG�uc���
T��<=U�Gfp��>	��bAu�3���F�GfW�C!,U�������F"�$rdl��p����Hd���n�����Qw�mwF��t-2��*��t1D�Q���X��F4��W�����
�,�Hy�.C��;u�Y��K�"po������7�C�l����/��m�$r562���=������fO����7������B'j�1#?�p�N<8�\���K�:B]i��t���������P���w�q��%�c}J8z�G9���MA�9��<�-�7��GeqXe]�>���������3�O�5�����;�a���?��?������8'H��z�m|��� X���j�33GVg:h6��5���=���3�U v�$�v�Gf?�1d��	
m��d����
1,����`/e
F���PE��fv6>9O��������M���bn"m\E�BY��;��0>���G����X�x�����ShB&�6Oo�U_��Dk$4��eJ������h��B9c�q�G��msj����(������j�!;E������B�+:�*T _ZHb�8Pq���t�T������<��
K���i��N�x�{��kE?����g.�;�Qd�����N�����K���!������5�s7Z�u��V&��xA�F�h����jq-�
jf����O<�2[f��J�o$/�b)T�"�:�<N���<`��X�f������ �e��Vh��a~87h�bi�u��brs��,5���c
�:"���*���R��t�~�?��@F:N�o��m�XKFr����\I���{l>(���h&�����
�AN��\�3�k���-��O�0Ce��S��N =1m2\���m�@�������C��R�<}Bob�'�S�T+����|J��f�L{��|�]x���������#6��{�F(��/��n����G;�3�1��t�2�3�{��(7���s)p�C�I��|��;�LaD��aTuy6��`�@cy�����G
n 4L���|-�P����}�xh������1___�\O.�O��]�����dJZ�2pC�T���w�����
>�;}�:!��$,,��S^!�/��NF�+��I�=��5������3�����Z-&���]�(v�k2�b�3_:V�\H��f��u�Yw�m6���hjuf���'�ll��/���&e~����z#�K��@$k�I�B�A�lC6D�������8E�$�Y��!�iAJ�L�~��U�;���
B$��Z!�+�� ���5$�CF{c�����M�+cf���37#�cj��������'�r�
6�����U�����������uV9��X �A�J�.�WJ�������|�7'���7Hc�H��#�^$6m���h�4�#�v�����G�TS���*�K>�}�O�t��)�<�WE�
eA.o�c�����M�4��D��i^"��t?8�`�)�D�|�^�����)N0.O$`��=}$���p�p8m���|2��F����)���
��7�ip3����L�}cz�)8����lt���J���������h�~����)��z�okT2[M:<���9�Z������x��;[I&ju��g
X��\�`:��7*�uG���O��x!�^G��_�G����S��H��Z6�;��<jC�U�/$�5�m�?�I
?��0zg�L��]���\	�/�������%?SP^�������L���6��_p��w8n�A��;GgF���G�9|�4��h���s��O*p�R�'��3���)?S�\�Ei��! Y�x��
`3�P���4�W�s�B_�����*���B�������@�[���l�R~;x!>\��YH��K��8+��������0��A���a��.�L��m�;|W��� ����C�95��c/�-�����Q��������a�'�Mv��\�zO���f�|U��6�� ?sp�P������m!�hA��	IQeBc��90:�Y�9`0�^�\�W��&��V����-��D(�L�&��|$��^-`[#��K/�~0���K�\��q;~s�x��a����qU�f%!SN�$_!�!RYS�Xz�������� �h+�n����9�����g)d4�N���p��M�W����|7��8���|��������=6@�/���PZ�O�^%��UW�P�������?)������T��"v���	�_���[x?������|`�I>+�����
m�{�~?�^��qE��V��z���~��� N�W�d��q���@Vw�n��?j6G������{�
�����lA���]�������l��:>�#m�)U��*LSz��h�$h��e�R���i�N������3gv�?�����Z�}dI� ]>Q��5��v-7���	y� i����Ur)�� ��4z'�<��p(@5�?Hz&�.O3R����hf������.2��6���,������*cf���hRe(�-%���v�?��Psl�#cf��h��P"W�L�����Df���!Z��H��������]Cl��;B�%{����S����������7����2�*����h��X�����Q��o;����F��{99�8?��%�>���4��Wp�C��n~�J��j��:Q��p���):����f{:j32�NG��p�_K'
�+�e�%�K���S��g{�����%��}A��r��?(gR<xQ���S���P��W|?���V��k��fm��U��q����6��V�����peF�2���2H���2I����^�2[P�*�,[JY��Fv�2�0��V���F�Y� �����������%�.�l�������h|�yNk�2D@�����Q�lX\W�3o��
��B���L\X*�Z����)�����m�U�)�����(��v�S���i�3w>GM2�A�^���-���u���>o��#�,�a=�) ���y��E+4���.T��m��=��b�k�������,�ybz,�����4����L>X^(�8�fC��1�f����?�U�t�O�����	���_Uge���u�[,D��?���~m����d�	�\��@�i��[q���X��L�(�&��dK�+x�(X�N���Y/ed���8�2�x��wf�A�@_j�������]{�e�*=�2���w
�@���2?uWg��Y�U�^��ZH�\/"kJ�uD#������]Q��x��8��RfA��O��_��U#���MV����3{�o�~N)9�=	�6]���4��,�7�������T
.���q��I��M;��n���N`'n9��Lq��o|�t�������21j���0�k�,��K&vSB�;`�*��5d"��������8��VA��QeT��a�F��O���f�(���A�I���^�c���5	�x!Mee5���	eWo�&�Wg�S���^�O�)����'{��@p�U	��A�E^��"H"�7�k�F�?���Jbv������`��&�|-��4�V(�������uJ���6�KX�05��W�F���_��z����c:/��/PD��������:��PE%�����t������N�hw���l��b�"��4�����6�_�{���
�+�uC�w�>mLx�'��h�>������P�:~�����Ay����aN��RFL'	������j�5�u���]5J[(����??�Is�@��I��Mi,�l���p@a�QQ8u�9E���B&��&��5t��2�z��[^w�?�f~��[��[�������$�<�
%�Ru�|L��W�bO�
�5.�x������������%����Q_�i��Rw@1e0����{���L�,���U����<��?��
������nNC�����92:I����=��-����h�@@�K��`���v)d}�0����|RR�w�V\�	S^��U�t���KW
]eSA��xEWs�NS<������G,��9�����2N6����O�����IYWIXf|����g�������Ss���]�DV��`0

��f0�f����L�)�On���E(�Hj���#�����x-��=)��7/�3#����&8����>l��b�����������"@v(+<y�C��]�����`�K�K�\;/K`�}y8A�:����\��X�\��Db����<1h��
>���i+���������iq��q,�����g^w�i9���>����������M��g����S�,��i���{��E!990���YX��Zhfd��5��7����z�IM�,.��3�yf��JX�:L�d�|�z!
G]
G=R���k8�b�����i�
4���H������|?����c�v��$�
�n�hw!�%����ycso������
/�B���������Qr��vb��2�����h|u��*[J�����:���0Ti���<����u��n�����"[s���r�e�����lB�k}]Y�Q�'�7Z����\y@��|�*�nD?,���C�[	�w�uM�B�!�'��1?\P`]��IBs���5�g�y��3s�qq4	r��)�S��k9��N�@�;����������� ��1^^~%�����zH�I��L�2��!�y���SOvP�.��
�1�'w�D9�V_h���+F��A�����nj;;w�s�&�~�������{r�L���F��������fJ@������o��h*�f����Eu���������d���oD�VJ��
�]��B
��'���\WQ�&i��y���������������u�7��]����g���V�F��]�]��{����k~qHY������'�@:��A�5L�gY�*v'C{�ll*�o�W$��j�����k��pF������U�8*L�����n5����7�f��>�ia�����k��-5����������y[�I<q�o|��,����$�X�,L�(��"���9�n�J4g����DlN���R`��N�05:�(`G���'�4F����
%H�JM�����=Z�(�����'��G�W�w'��G5�!2����VH�Q����0�I����W��T"9Cu�Yu:����M���&���F����T��Q���5�2�I
!A���8�VN$��w��n���/_�$�!�v�QH�k��w��VVQPT���-}��%�uw?�X�_��K�p��/�/J1�m��u�x����Y���)|j�w=?b����y!$���[���hM�)I��K����/d���Q,9%�y���V��5Kc
d�x��K2��d�Fj
�����%�	K�������e��i������X��:�'���>:?��{��:�p�JR��R��k�L.�y��
���
,�������\,�����V��*r��&g�,(���32zS�D?����g�!��ns�7:�UaRwFd���� �jAF�����2}���bd��*?y������-|�{�lh	�5a�f��\�I�������x��b�=y)������0�~��q���S$��h?�(�V�Of��~[7��8Z���	_/���b�
��d�3�T��)�^<�b����&3��Px���BX�?��_]L@�����?^N��/����$���Q���m�*`�L�4#��x)G_���1H���������8�%�W�H��m�^�FI��l(����QuP�R_/d�C������	��P�i�M���0r"w��Y��z��m�������N��������_I�_�~�N6�9"��#����-Ckd�o"���;����w���Cv(s������%�l����cEqN�E����s0*���������(��n5��A����(��2 ��u���_�I\�o�5��J4&��y������A�����|���Zq]��	0"�"��VN�L�f#����o��3�w�[2l���l�D4&��NWDz|��E1TTnJb��Dz>�?�,YP������'����Y���"��Z_
�1]D��U�L�q����,��L�)�U���i�*�d���~.';����P��p��
��f�k�-k`��A	���%�|V1p����Y�\#��^s�""�	P��H[ni"e<��1a���[�b�"ML4[-���N�q=�S�H�@����8YA-�@U�h���U'�B��A��7��KR��a�/$Rq��x-��tE8CyL+17��WC����+%9���Ib������j�D���!&�b�G�%,���r��&�������d����v0��Kn}���z��KA�.5�����[1����s����!��Q9D&GI������*�9l��"�b������2����]�
��`	9g\�$-�D����2��6��c}n!s����H;����QH��t|!M�������d�A�G��p�3T97|����D�2y�GDM�������P�!�#cVT3['���bG�t�PV���F�84Af~���5_5S�7W��7���#�?j��-[x�/Fa�J����Y�����6��`Kap@�;�?��6����#5���;�n1\HJ��c�����%{�v^kXTDE�X3s6������Z���hW��6Up��k����i$��S��O�������K���Y�]�j���e�lR��������8vg4��M��������ae����,��L?u���_���r
zD�Q>G��}�o��qiz�},����l
;��L�U T.��<O���+�<�	��������CQuAI��	i�
�x����'G������3�>|��}�������Cv�����0~-sD��a<j��8O���~�G�����A�8�D|�4�Q��G�����0��,�������p�7ppN 
�79x35T�Xcj��-�����iZ�������I����Z�X��m�>���T.\����U@�2#T�����Lk���3'!Q��W\����������]R�q��!3�2(�da.�h��Z���&��A�5%�N��L8���?Ce7]���Jg��h�q�x.��>���K�.��K������#�<�2Yl)��yl���n�N����pmrK��d�v����SFL�@^����r�x�W�����#~�s����2�[�a��)�����Q�����������$��E�H�M�D+���c{S�
Nk0Xa7����q��h/Y)�a��[�=�d��xQ��c
�Bc]���=�)�����b
0f�J$F&f=Um*Mi''���&�-�!/B��(��"c��D.��.�O���y�AS�z?6)+�1Ht����w�x���-�^�����_Q��wy[_��k�3�X9��;X��5�����G��@_���w�j���M/F++nvK*�����"%��Z��$�eC!95T��wa���������1����Vu���r��o����������������B��s�������_
/*�k���E��N�g�����]^���L��������#M�%����L{b�?��G�OBX�z�g��cI�z�`�`��Y,��U�I�U�d�3���u��rY��si��+.��
��mf����_b�dnTS�{�&��~��-+�OJ�O6tP���#�5'1C��0�-�)���L�!��[F���n����G$]�R,M��cS+R��)6%�W���^-�&���*ws��IY�q����G�����C�p����x����K�u$�(�����=��}q�g���V@��!I���X�����F����9�^�R������M4����=P!P)��[�k({�
9�=5�w��Gr�^�����K��t����Wt10E�x��^
���m���j|x<����yu|�������;�x�=
�n������4X�8�EX���{�A1����7Iv�"�	Q�S,��/��J�$O���$��~a�A����^`��W%�������Ok���I��+g���"����� ;�}�&��J��).o������n+�97FS��%~�T�!��s��no 5�'a�r�]+e�@9%��_E��-��79��@":<s=s2]�P�QG|r�������������=WM��k��`�N��(/u�)<�3O����EO�)�\������?���=�v:�~���v����?���S��ZT+K�I���z�����2&W�d��f�RQ��G����u���8����:;%���9w���Rb��{���6�p����<��
K	�X���@jB���Z��`u�!_Pr�4���([��RH��+���S"���q���mHE��^	�hB�v�q��xTv*f�L.��s@��F!�J5��c�WV��������I7
Y��Jm�"u����2�VJ��*�l��F�?mY�f��;��5�7��m������j-F�V6���?���5�(R����o��h��e��p1�:{p�w�!~����]J�C���E��"o(+0�������r)�bc���������\ekY3��f�X�����n�@���C�vO�?��(�a��vZ[�p�,2��()�����L���$v��
f{�>�-n��qi�'������t
xKg�Kb�E9���Y�+��2&*K7e�Z��G%A�Jbm�#��s��W�.p�`*���{�i��t�;:~����q�g�9� @��,F�����������S: ����/����"�$v)���U�]�k��a����Q�3�?�
�C�v��+)#���v���-��D<��w��9��h.�/i#��Q(��lt
T�Nd����{���:-!�P�^q��~�����_`P����\)c*���lM;B�R'����q���G��-��ZNEQ�!�����c�����ba�Y+j��4�r2.	l�b!������H3������u��+���Z����a�`:�G�,2����~���d��"GE�
�U��Z�8������5h6aw
G����p�.ji��
����O�
�V��?x��bGfD^��lg�����'�3���_7G+��}��r����!p��U_���x����L���n=Y��7Y�I!��E�4>E
l�	Z���?$���`��Q�RD0U�QtX0��UH�
�w����W�^w�+I�0������8�t����V��
2s_�Sx^:�#Q�2w��n|:K�],��� ��������1���P���l�?��0�K?��1Q�w��������f:��"e������`6�6�-sjw��e��\i;��+-C�6���F�b�|��4�"R��*��b����)�uU+�� �>I���7������K��Pq�e���;l�����Q�'��Ih���'C>=�P�	�-��&abb[�W�IH�P8�#�0 tW����^-���,��P���3Ym�o�5�r�]�(>UEK9<-,�:a
 �����>rC�� B����nI���3�+���x-�Q�m� y-���t��S�JW�V�%?�j'x6�;��h�t��S��y�����|8j�64��p�:�H.��������W-�Hu��_[�x���(N,"���IZ�zyE����4��R��$����X��*�
�\8�Or�?�z�����1�<�����K�a���n�0c����~�c����Z��C���-����b��Q(�AOA��}�V��u=k���IK�s'�����N����O1a���r��=�w�lUK�[U���>�%�����WY^<�o����g��V��7�E���K����Lq��*���l|�h=���s�N�i_S1z�����zt-�5��p8��Y3�����<Ip�u7������TX�����������n���pict��A^	��;��Q���T�����
,-4k�a�qO�8�V�g�Y�����n���1�<Y��Z1R�Q.=�D�>�po)�X����}���G���+���fB	'�Y�{��#�n��
�?�-]vX)���N\m�5g���q�Q.Z:�bm�y�3D���8CT	y���}������A�;3g��F�FgH�R���Xjx��`@��`g��;!���Hk��
H�1�9;���f@�~0�(������=G�N���4��^��q�/c63^:-^|��9�S�Z�0	�Z����>������(Q��j>��P������&���ni��,m���4P��a�F�a�i$��q��h�"Mnc:�EZw����l�U�Sd�Zx�U��8h�����7���Y�5���3���n�
�����_�pg��K�5S�2FQn��=�_���#���D���J<��ZL�;E���O���+�*�1f\fk�>���.�Y�����M�&�z���h$�����UC���2����\a�t�At�E�cVY.�N
�Ic#x�'J����r�����ZRJ�M;�����h5�����i9��k-�m�J��>�Ku�h�h�
v�8�#�%������T�-�b�9�rz���!�6p������<3�3����Xy�q�1�7��I�b�F��'8���g&E�>���R���w�DA����l
��Vj���A��>����f4\}���h7��:2�����_��
��������2�62osWv���� I�K�R�J3���O��u��5��d��`:H��l3��DE.�qI�;�c����n�U}���X�7�����W�::Ba��+�����5�S�����AO���2�:�tI��.����H��"�?Q��e�S��+Y=jM���:�DW�}���������:��)>Ke��!l�*K��A3Ep��-�e�#_2L����[�K{�A�����xe6�A�6��x������8>�IOK&=N�_������%��lPq��*��7����P�"��_�J;)�Z���lf�!��UD+t��E��.ltt�H~�Nq�y�69���*?��b ��1�LX9�v;V��77;����s����#�=��Ku����~_
c�e����!��U�14
�Cc_�.�E4YcMr��p�Nx�'xAc�J7�WY&u'�,���:'��Ez%��n�)��*�6��c�gigj���R
�v������_�V^���z�W"����b�'V���$sC�wg��U���xKO��"�K�D�~mcOQ���������A��I�H@���������j�Jh�����B -WP����n��*o"T�UT;�i��=4��v�e4���;�A���f��(v����9)�����n�����W	@(�8�%���e�ue��E���-��W]�� ��H�[��T�3�e�7���n��K�b�=�&���Nj�	e=7�n��7���u�l]���-�.���`��)8��yl������/�*���94���`�FJ�A|$����F����2��Y�����5r��lX�������[dUz�0nm��c�W��He?}������`g, */�������R�aO���W�%���I*j���=(��=,-���(��7�K�-����r�d��f����
�(�2���x5=��3z�D���{�y� O�0��G=��|G
b���B��K
&J���
J�Z,����j�5���y-���C�'�^�(�aQ��������D+&�}���J�F'v~27AK�����n����������<��o��-D��V�����~�Z.A��z�o��e{[�'6���5�X��mS���g������v���zx����n������=|�����P�5�Y��'#����0����`E���5�[]c44��l��eM�n}���g@����0��zI��6,�.6�R����\O|�W�'S|k�����R�XM3z�T�y)n�V
a���/����lu_������{����cE/�O���������-�O4AK�6������2K�t>��z�s+���C��O���Y���=k@W����)���2��
r�����{{QL ��8���W�#=�H�������'��A��F��I����H- ���<ph6���wf�;u/i\%��L8B]8��F(���{����*2���$���KdDd��y3��Oq�
���;[
�I��b�cM�5
�)�&����������lrtr}8�::>"S�f��%�[v����GP�T��8+��x� �3g+/������U�r�BYt?��+�:�E��s��j�����u��%t�?p�t����VaWXw"+�$\Z������II0|����]:�0��^aD,KH;�������u�Z���HP�az������5���l6[���h[���'�d�?��Y��dD����.���_#�Or�K�S/y38>�<��>��9>��A�Wg�����t~,!��#�J��J;k�`��pO����lV�5E���{Jf,���7#������(t���A�9{=��G�Z���g��G��)�:��z���gO���e�Z��U������s��B��D�M��6��1��m���<���im����;R"�e���X�+gv�������s���E^�[�
�aX���g��d��;Z��7����b$�M���� |�	^�{�XMV�z'1��v���Xy+�2�i5�_���^���	yX`�g3��x�7L�B�7T�������B���teH�-/_hT4�F$�G4Y ��0�H>ZE���
S��&������C��p���a#�V� !��HPo	<J�����r��s�
2���ug������T"Ix7�&�D�IsOl���	��G@�:��0�	��	 6|��c��Vs��A��H��R�M'�B+@�B�v���~rpA�9H��C[���	��I���)������Tl���,�B2<��*�D�a�C��I��%IN��s��e����1����� J�A��O��\�����wI\����Gqh�p��AN��P��@�X�&7�B��<�0%=+,,>\4�]=�j���zZ����1w?9�������������P�u�d�K��5q���;�R�c���JI���8<�-���	����2�Y�U�� T�����;��wLQEQ�)�������d���f�q����L�,�d]���i3�	�<��A�I�9'��Z�!�������� �_rG��Bt��u*�gcmju�FR�8�����3�XA��}h�(^�f��5�n��23xJ����n��.���#����1Y�U��d50����!�I��p�kZ2����
����K)����Sy�8~��$I��e��w$IR�R�ys����:qd����AD�=�l������1e�����R��#s_|�L���f�6!HU�����������@0��������w��������(=j
9d�a~����7�����$r@o�#����������xr}|39�xu�\�b�kO9�b�xJJ��c��M[�[�����L�~�=�"{��PQQ�&�C��i�o#	��M����������5�����1Or!%�����J���-]/e��L[Y�Ef����Z_�@*&�Ll����8�_#�����R�*���A���a9V������?5�!l��+���n��_����
�>9���	��_c����\���C���b�u?��|��?>�������%9��3Z��*�b12s�#)��=�A��WNO!Q�*���3��dm8bcJ���^��5^1h�xA��H0�o=XZR���`)���r��G�;owg.�X%_��5��j���|�����d�zia~b��Q��t�P{P5�B���;���k����6�T����1��k���)�71��B�W�� ��s�F�f3���|\�^#��]B����L�E!���/jdVf�B��T����7l�qj�{h?�S��m�4
����fl��@� Ups������*[���0�4'oK�N���2�1!+��4�rX���vX���N�^'-�'d������L�����������
��p�us~�1���Qa`�u���z�f��)]���|.%s�����%�8������
%#	�X5����N6�����c�D�Z���8�ar�a|u|��r�5^�IN�m5��TE�[�����g����1Q
c�I
����#��d��D�'G�+[D���5	9&�O�����:�%��?�#���oAw��9��������~H� �%_��d��-�k��&��]2������?8��;������0���B2(c�1�#:���.A�����<���d�f�d�Am��T�&�������$K^/Y��{�4�:v�������3_����8�oE~Q�	� �7����{�����+R�{+2b1C|��jS�f]���Y$y)�1��+&7��VX$
�+�L^`Y�R��C�b����P<:��
N�{b���\�U�QB���%(C���M���!U!���TH�3���F�"#>����OE���f�L�����k��V���<e���yv�R�t�������u~CS^�9�.+r}B3���E�����tc�ARs�3���l�e��CJ2b�X���Q����?�'�t��8��
��$���U*\�$��j��2r0F�M3��L����-r
�8�q�q�����$����8���b���k���V�y
H)�
<�cxH��g�%Q	���"�N��09^,�(������V`���~
�i���!���@� �����A�������?�����uR�,oybCt�b��L���	��ul,���d&dLZ�q�.cq=�������@��C�.��sFP��v�_	P�<�8?V�
v����R(UI)���!	R���1]-���_"��4��w��(?�:���33W���^&�N�O�T�f�sS���hKh������b�\z=��{���$OO���-������=�a��+����=�$�N'W����j���v%V�+��]�s3 [Qi?kJ���h��g�����P�����Fy^Z��7�d�8�QS9��:��n��r��u���^3P���j>T���B������o�������QCr����H�@���"����\��	:��am��"�iu^��%af�I<`��Zh�^���8*�O#��H_���n�)�������	
�x��������b�O��e�,��Bh�7�&:��Bo���I�\<	�i��R�i������&�a|��������m��?�A���������k���7RZ��j�D��4�0jx�0g�jk�"��f�H���cxBt��,e�����M��>�����p���WO-����-���sLg�<[������`a'97��������j�!��$��a��lvt���M��3���q��A�l��#G����P�b��oz�=����!O�k����'������0�k�~�����������(�M|o�'�����6�qI�a��d���l4��t8���~AD���q��6������|�y���E���'w�s��%�Kj�>Y�d�{bclN�������w���y������,*���
�"5P��O�l��
V���9�6}vT��(_���\,0��m�(�=�<]1&��6oi�����asn���8�Xd�n3��!7$�l�
������J;P�(�������V��$c���=x��_�Q���K�Z����IUB�rO���B�5c�UZ+'�_3�Z>���w����F���J�G8{]�&��b�eP�� f��������i��(S2��v������iU��aq|)]i��f|�~+��3���OhU������{����������Lu������2P�
IlD>8S��W�&����������F�f������e��~�������(�E���
�����r������icgG�~����)#s�[
������k������}��H���	�TZ3����T��������^�I����O��WuF7(f���N�uZ#�T���nN�1p�������M��}�D�r��Xs����y�|��6�d_�K���d��i�M�
���/U����<�w���y+=)2S�K�����l�3���b6�'W��� �TA��-*T_���U]V�v3�H
������?��D���I��n�0x\��k�\�w~4A���%B��!t����X_��OSo#�(X��u�����J�$�����O��^�w���Z�S��EAi7h@���#yLU�J���lc:��f�p���y6Q~�k:)\�� ������	>OzRq�������\�L���y�zz�_I���d�P�������+FAK"�aj��9�V�6xJ4�0
�8�!����O�s�������(��[��i�������(����P7���\�����s�v_�X�����W���t�v+�������{���O�v`�L������a���H������z�0��A���(�S'���h��v�������l�q\ ��v��w�����+T��v�C:������|s�<�`���(\��$#��y>�|B��AS�u������Q��c�q�gG?O��5�^R6����&S1u����#(U����m�:���Pz���|4�LA�8 ��@x!i�4U����������hT�{������AO�`7p<s�n�=m6��
�k
�-��������gNd����r&������5������GV�JS�]�'da����! ��l�
b���&��K�����r�b�HO9�3Z[�n�bs����p�(���F��r�r�LF,b�B�q�����`�x�JC�4@��&���F�����(��! wu@>�����������L�f�������z>�p"�W�K	��i�s7r�	�Nh ��~�n-��@fD;!x&�?��&	��d4��PZI�����Mi�����.0��$���:��U����G��DqtBt�N�0@X���4(wJ��!+��`�K��ja���9sMqmN�;�=��+	��`��w�����\�V X�	�����������VmE�)��h�<8X�a���������\��s�>������c������>���������_'�Kgxus|8>wV�����c��0�y�����Kc������������h\�z 3�S�7@��8ae�B�%�G&���M���&E1����c��gqo����z����b���������\�!�u[�.?��h�����6����j���z�D��Eb����a*�B$��/���N[�j�N[���e+@�m�G�0}���f��t��TW��u��������*����,(������3���Q���u�`���g�����b}n����=EC�������� M��_�)����4�* q|Z�wL����4(}�A�ns��vLi��-��Y\��r�N���k��
]<�������)�M/0�����CR�����/��[L�+�Y&�y2Y�R�]��P������E��V�0�h�2 ����+�N��,k��>G@�lj���eU��tp���R�3���p��03�����+���Y�������P����K�:����-��9�]������j��p��-V�Z;��z44{*?
:J�BR
�]���"L��n����\�^-��'}�f���C��v��=�A?3��yp���A��Y}v��V�����u����NU#�����NY5���w/Z�^I5��S�%"�_�j��Bt��xA���je�,dKT�N��1\���yr����E��SU[�r����z��"���%�Y�<�]'�Wc�sa}S�+�G7s���a��zJ��wID��?Z��qv[�0�Z5�����VU��Z8���H���"�nw�����8�VAF{PQ�X���
z�����mT%���i��VT��[�~m���D�r�,E���-�I�lr8���,��)�&��"b�����dr������M ��z�(WG����`�W�������K���I�Z���|�{<��r�"��V�_^9��z���c����Q��E�H���(q5�[�r����_�[�: fP
UT1���v�!�G#���`7z�tv��DY|���h��)��V�Pk�Uf��B����X3�u5�K<�Z��xz]����%�oU1QI��V"N��+�KK���f�c�t�nYA���b>c��blW#�.�T�	�R,��u>am�<�_��"f�@��MZ�
�-�e��fs:����iL+\����&��K�

Z�4����������N�����'"���^d�W�5o����������G'W�hM�L�7R`J�[���Nw��<Yo�#�� �%�,a�������sB��9\J�{�w�1���'�k$AY|Z���[�B8���d"��=h��a��lO�F�;u���xk�+]�|Q��g6��x���:���M���"������h:�V������\7��3~[����6�zZIA	Rs`���1�:���kv�^k:27���z�j�9����iQ��8Wq�gZ!	�,��<��Lj����#;�i��P1����{_0!$���A��i��W�'��Q��T�)��lv����-��|`;��
���1�����h0�)Z��q��t�urT�	;��V�q|)^����.}f����|(�X���k��o��QJ�%�s����=Hq��;sIv�������h�}K>��}��{���(	�����z�����1���oN���oN.��T7_��W]�=�7�����x�Y��Y��xC�U��b�E�-���"2�S �23�{'���5YTE��/$�����T9�\��O1��
���0��{M��'��.����x|s�`�<���H6���9��V�n����Rl�L�FS�=_�q��!���7��&�"{���I���I����/��x&��z�� |1���Ld}&��d�
gvs�����*�`�����L.���L+��pn������W�jr�q��:��E��6�32O�c��,�����S K���f�"9�,��@�Q���La����3�0�����������z����
�� M�f+�BP���6�T%(,�|m���1�O{�f�7���F�g��2m�MM$�	b&�����Q��j���}V������~����?_����>��&��t�A�Q�B`��������v����?}�hSh���Yl�v0����s��M�@V��.��-�������U-|\��w����7��k�}����eC�G6f��3-�"���J��4D\M�zBUXS��H���d
���/�{$D<eL��p�q&P1h����������0~���KZ$�V�6���QY��I���:uaW��]U���=����[Uko�v��RF��>l��8]�x��mU����a���4@EE���������y&�[�w��%��<��8r��V��#���n�������<N�cr��Q�!�>�#>�
�}y(8k�S������c8=���OwZ�{�w�n��E?e�"�*���j�����X�D�6�g�^�<�W2e��")���A�w�%W�����F�b�{��1��<���&7E���*�������Q�������7�x�(E�=����_Z��d�;�>�l'����M���UN��rC���~_�Mxr�%N�����w8��M���i������VJf��,������0z�c���5'��v|uuxq��x5>������L�<��*R��/���3����'5��`��b�0�[8�0"�������xrl���q�0��Ei<	����1��5���fH������������||9�:L0�!�-��eC��-+��cS��E��������\,�� ����2��0�[2����J��(�q�C9
��X-@jH"q�4%n|��>����#����!qH��=���	-P�'?�O��z��`��7 �_b����������P�����e����c��84�

�p��AN�u�h���h��KJ��3%�nb��Wj�� �~�)���s>3��$�z['�df��(����6��
�T@oU���K��70,
8���nn��5B�������s.�N����c�����*"���CO
�'tf��{ �=9j��9�c���RL���s�>���DF:l%\]-9�����sq�9�����jq��#�Xj+
�<��
C����Z1b�q�����h�s?p������t�����
%��Q����Up�8�F�C�����e�wpz�wum
ik�,z��nxHN!{��P���LH��pl�F�y\\��8��i�����2��*�E�G��dD�7����NfI�?����dR��a��0�b|z��q���^FA,?���qX��
q�q�+3.�\���I���
U�I[��T"DD�����������#������#O�cW�H�'@�9���0��>�Cv���(�� ��S����4�B��)���oCiJ�H�C�gA��L����8���=�;����nS^�z�B����h�#f��~�+�,$��c\}���dO&�oM
Ukt��)bN)��rK(��W���H8��g`�B8�O�n����X@�05���C�����3�]r�^�I�o���C��	�h�H�[�0�A���}rK0���p�cr�O������0����E��wA�-�8�.�"N��1�$g��m�`251��J����fv?jSv#��L��$��2�'Q�8wp�G��x��jz����qls���q���J�@�K*��g��s;����7�-� �������;��S�*��{w:~=���������k`<O?^�|La�6���SU���e�eS�B���c6���i�f�{���,�Z++�r���B�����p5�Z%���R�"r�������\�����mt�f�����D����B��st�n����B�Q���$����f|�L�k�=U�:Y����@?�FN~�20�^������C�a����_y�C���/>���]^��|�&	uI����dD��J�SUU"y�0�1�:����KV^��~�J1�/R1�R���_+D�5j�a���D�%E���1Z*���n��"��Q�R�p
�/K����S
�z5�����6��G\?��c$MF�
���B��\\p_/H�+.W_W� B�N��`���P����\�wk���X��d���;	���i�E�
�j��^2ca�cn��tz�c��nrF�\�������r������r.<'P�ic(�P�3���l'�6\�}\�Y�$&C�$X��{�_���Z�I�
��������,.���XK~��"��dU��V��g���V�9E o*)p4>�t$��w�9 �E�z���FHr�*j��	48�>]h�!>p�P�������~�m��I��7'�U_�V(�+��G��m����������3ot�WeL|�j������,��Y���C=6/�t)�P� ��^����'B���xa��s�S&D�c2,�h�������T�'��+��,�n�B{��k��Q9
���%�I���A����?
M���w�t�������e2#3�"Q�t���Pc�`�3_��J��h(�����~��wvPsGw���Sz��Ua������L�8e��D��d@��:;PEJ�5H�����R�Q���d,���J�Ga�-�����#'�eY]W����c�[J�J�
�,n(�5���w8���`g�t��&�!y���\M�x�����,(q�ps��e&�.�8��Wv��aR_����fB����9E����TMkF<��)�]�^ )z ����N
��q&�9�d���i��R�N	�S1&.�/f=��.�U7�L�c{D��a����������KK/�\��!AW����	�4�c�.V�q
�<
�����DP�"0��&G���x�.�d�y���x_q�7t��"��s��Z~6f�����j*���(��)|�:���-/���Q�����K �\���4�����y��Dy����ct�X�5f(f�U��I����uc(�5��
�B��N�M�K2P4=J~M���D�L4�W�������yR�����GWrV�	�{��}�pj-,����.!_X����#G,�b�R���H
��3��0J|��TIY�������J�H�mB=���j�JT�n�"_��cL9eFof��I!�Dwy�#��~�G}ns����y����A��b���xV��8>Y(���O!��7^I����F2;8��UW[A�gl�����[���D�3% Q$�)�\R��mH�j0R��`�Ez,�R_�%�m��4Mgx���&oU�����N��M�ZS}%��J1?�g

}�s3�/G��7IMR*�����y���i�K��������WY�.�3�Y�[�d�].����i�v����J��X(�J�������_D�G>����>��^3e[��s�~���__��^���
��9������0����h5���Mg����*�+�e�XBS���/���a2s�Z����(��mh_H������Q��0��8�����"J�����Hux�������5Bk�����H '9�i�C�2�����@�G��L����Qs��VI�L����Qc=X��k�9��Wuq��R����%��������S�N��/���a;DgzYy�pU�d������J9�G��������G�%6g�\t����T��x�x%�a���.,���(��s�O
0]'�5���Y^FGv>Oc�sEW���$��WT��Lz��� ����E6���^���^�2�8����Y�f/>L����\0������4���F�&��%�ew��J3x�������B8K#STs��@��"n��@pS���@p[F$�B���{���Lf�,g�
h�=���F��0�C}.�o�=�/w����M9��Z������x�BcP��i������<�������Q�-�I�h#�AGj�L�'!�r@�I�*�`�d�c����hC^+.�*��K�	�m�9W�~F!���!��\�X�I%Y#b���l:��U�7P��=��  ��3��8#��3�l�=��x�7����2�^cF�p��,+�4�J~R���q�Bl

51��Z;��������J���.9S������t<�����Y�����h��}�������VMx�������#�.a��N2�����mM�?n�T�=��5kw�l?D�v\��{��l�����g����[��P������V({?=�6�X�M�����I2��������������p�A����B/�gO���������G�jN�����|����D%�C�m�T�����G��L�1���kmv2LU}�Q^P�|If�U��p�Xu�
{>��F}q:�'^���.0(�bI:����_D�O6������o��JH����3�J�st����]u�w��������K�|#i���k�8���7Z������`��J��{O,o��RMo:����)�i�C��
6����'�zL������?������
%I�HVE{�
fp�6E�(�N�g������a~�)o���PnSd�X.P)����x�X���g���_*��
��Kv�>����"d$��y���|CH������������V�R��l13������GxP����=m?8I�����6�%�e�6�2�����V�<��yd�Z�
�=��a���������W�����"�G6���9T��1���K���U���u�5>L�����h������l��#8E����#��'�����1�T�bD))���Xp��iR�e8��pn=[`8�d:���c��M�l#S	�V�>�`��F�u������ _9Z��FK�|��cW��HM��m����\<������d�<N��I���BD?1���������Wl������T�5�K/dD�X��z������%�~��d��=i�)C�M���I�$O�m�j�����<�j=����~����?8������;ob#��oH{�3�u'P|�V�c��� rbS���1`�g� �n��Y��-�F���u�����������G������B�S��N��,���A�%	��V��P&$�'�?Q.���c�lJ���*Ob���t����5�7d��s����\��(^"N��RTG��w>M��U���r���
����X������D�������]W�K�XX��y+�_���Yhm����A�Y���_#^�%<Jd��%�l�<@��O����w"?_�z��J�g���v5�����^�_
?�����d�������w�����P�2i������Y���e�F�v�*^v�6�s����H<����R2�3���^u�82f�x��l8�?*����7x�4$�wrh^�>[2����m�D��ON>��6��i��OJ��3A`�������c�q����=�D ��pA����,
�%�<��K�,@�k8z�������1�t�'�[�/<�y*�Q��PNY�Wt���c��E�����}.�d��[$%�����=y����|T9]�D��+&������d���`���_L������ds�$��/�p�6lra�w�-���R�}�z~���M��|�$�W�������)�aM��T}�lg�g�v��d�~���m�������{���A 	n���������<��=��2o�b��������	V�ID�A ��]8J�u>��o�:|������[/nY���R���]P)�;e@{Q&���Z��Z�$r��]��o���S������0���(�P��[���#��W�L�p��4�7��~�hJH���;H�Hly���f��Y2z�,���L�|�-��!�����hJI��t�����P����p��ts]Kt����g����Kv'���=�Wv�i�9&h
�`x	}����Lf#.���F���.y)
��FX6:i��_�����3�6q3���[��Im��U.�&� �����L�-����s�����o���'t`i>I�b�����YyXMFL���:�O^l>c����-~__#�:z%�)I��B����fG/1��:�%�A�1n�\g���|?����H	���0�v����Y�K�9�Hw�����g�
��$��������{�|�sE-1��{�>�7'��������:�	��H��)�+@_l�k�GN��eLp,|�����z�e��E�y	�O�~���d���A:���u�r��Mah�th
)e2:U��ss��+���u.��[�GmGF��y��l��eq<�G#�!���%u%�M0�sG��a�?.s����	�T���4��O�>��SM�E_�7(��F��WB�����K�t�Z�[+V���y~���������������C�������^�����H�v����Eg�bK�i���{|��������`����/U�U_��5�%.W�Vx�9��;5���_����T��p\�o���x��l�����<�[O��k�=�����,m=�������������������|�l���h�*�o�?R���pUf�/�������w6����q,�$��
T�3 t�u���-�V[����m�p�;��2�2���b{IKU�l��}a
{�M�{[�=A�������,.�sj��)��e�H������C��R���Tx�@�
5�38t�v^<��;`�h����6������	�t��eL��h<�����������A���#i�0���t��&�p1g�|�7BS�n�J�n�0$NL~��.��+�ORcO�0��i����m:2N&FKO(�Z+>��,���^J���,��J�4�<����[roA��P|n��|�uGG|���O@����H��I09�{OFY���������K:��0��)��4����x�2kc�4r�8\���z��lc���^r�O-g���"����*����Rtx$ess��8�1�E�x�O�*r��V�C%���������	��Gb���.���-a�~I�(��f�pl�����i��jg����g�n\���z	~��+��l
Z��m�ziXTD\��<r�l��e ��o>G��3f��
�A�s�kJ�O8+�����u���J��W$ ����\��J���Y#O��d���-AW�[K�ph�!!"��2����g��9Y0����r�E���<�T&M�^i��wIb���7��k��ro�C�2���d�RO��0�('��c[B�x`�`F�vF�	@�V�x���`�[T�u���D�pG���4N������I���`�U����?��b��m�o�{*@H�x����	s;RqSuO(��=�'�R�9��Bt{�i�M�J��u����������Y%�>���9�J@M:��w:�����n��c����Tc����T���bc��m�)l=�u��/��������Q�h�i��ql?�p�eF%E
��}Y�r*�U�����h��J�V$��Q��CR�����{���##��U1��X���p�d"��(P�z���8�8P��#�(�F3�����J��r�'�t�fd������P��*��x���@@�����>w�M>%�O������2v��
����4��G�.��c�
����Gt�$�x���+���"kh�z���}��U��j��V��{[On��.�����vxpTP
����m�����h��R|<?���"uS���UPd�
a���!��#{.$��m����b��@��u�8�L������[��C��R�99G"u+���v�Z-�����E�c�Q�b�|�����O<�b�����<h���qsk�sl��V�Z�������6kEg��d�������n�%���)�B���?��
�e�������P��l���%n�
��������S^�?����f��SD��7����C����~�[����@���,�'Y�~1���k�A0��ZH��CK��z:�END�S�A����0o{��=dZ��\��W��Yg#�*�������Mw�m���YA��n?}�����pUT����`���4��v���J�
���v��(xO�N�f��b!V��1�����	��u:�3��!����9t�\����P�O����Y��`.�J& ����t����k�@��m�&��e6�^�i_������W*��U0��<ZO��|)Ufe����N����D�@M����d�����As5FX�3N�V�1�=�$������y]��o��#��l:AQ)~�KK����Q����\|�g���g���q�4C�1]|+:`j��!+��P�<�^�`���.���j�bx45��������%W.�W�'`��uU��V���4���9�-��I���9�����As�A����Z'�)����z�;�*.]C�9��6�����s��}�����%��Ty�O,��.��U����dgPRgW	�>)�8�l�'�u/�'�3�gk���'���)��h��&A}=�l����_�
�?c�#'_.�j���Y|��8��H��V�Ul��+�$a����b��s&�g�<H��S�������y��z�~p<����)8�\]��(�}�p��JmPaA�(b)(n����2��p�q�Xe>y�����+7@����vy�C��2Fc�SbC��9�����>bl��u�-��A&\|�M�YK
W.�Hk�fR���,Z4ZkD"CT�GJ��:h�E��jvq$Y���>��ej����-�� dN3fk�lI��93yR��9+�`�
�*�l������)����~�JY����g�A}O�6��F�@�.�c��l����S��=L�MC��,,���WF�7�e�8�����a}��c�v(��:��g�/���g�.���@��m6��e�=�FB]��_*������q��9e����y��% �m����w��i~��$���A�����t�,f0��c5�s��U��1�2x�i�rq%7�s�x��1��4��<$P�(���jY�k�Z�'�W�T�%JfH��i��4$������	PP�jD�_���?_:B���c$�zs(�4���������#�cv�����~z���T����f��EkF>I�i��-s�X��i���t���h�`KS��s/Z��Ep=���STD'��pS =���2��B?,s�h�O���0�*#o"�j��hj#��:q��^���s���@�-�-l]Q�1z���M'[�1�F!e�wk�G����8�Z��r?��2�Hx3W�Zy�P����������Gh�����L��Z�ANz#�V������H�_P�1��U��>���:1�"c��8�&�������s�������V��n��7��X3j<0�c����U��w	5�;m��J��\b��}�����j�J��'�n�%���0������E���@���R`�n�I<#z2V�E�5���:ZDhmD����@�N,���-��L�Cb�����A�3�GK�H�N(���f�#�r�����4$u���f�A�@2�T��V��1z�(4�lfN�q?������V���O
�_ff���"�x+�{>�(���9E��Z��x����~�-���w��f;
[�WJK�Q=�;yg�����Xd $�������i������@��nt�ED@����4g�"nk*��HT�(�������;O��xd�Y�Y�{��	�,������L���%H�W�4�Q�j�I��&�_[��������l@��9�Im�RR��"�����"��4,�U����|wt�r�'� �7b(�
a�-���_��TW��F��6�|�Y�I��i�r�����+>Y�v��u�^�{<d��n��D�Vw��,#4�o@��xN^����I��Lh|1�m���MobqD�0��[1����>������2����k�4qL7�K�BjE�b+z��Kt���r���������^�)�����/����
����tSB�����H/�=��Q�?�C��5c�3�O7�3�����\~����#w_���t�1��oY��C�c�d��9��fcv�E����b�Z
xg��J�<���������e����A5Q����r����G�!wv�S/N���Z������O�Ym��k�c�����>vm�t`�YO�������'V�o�
L� ;X�m{��m���-8�����k��8g�u��s��%�sFg3������%�s��=��W{�MiKkl��{��S
tx���i6�
W���wOi�;�/��������=R�~��������dk}����dc�E�x�zn����|�a0AJ]�z������!���H����s_]���}m���[�I�2��h�������0n��r���IMs���D�*�Q$��X����W�4�����R}��s�8�-���?�����rs�^:A��y>�n�:��o���qnYzp0o<�7�e��u.[�����m]�,h��m=���l��)>_5X�����#Qt+�c��ph�{��'��.Uc�o�\���A��N��}O���}�����,�������f.1i����^��<��+[�_<�������gF��
�Y������Y�p����m���RM��<G��k1K�;��'�$�4����{%)z�L�����=k�?�����?`�� 8wC�c�QG	�*�Oe@��q>/�&�(X����2����9�f�|�b�9����M��\�m&�u���a��������L�?�0���	��}X}4��Sr!~n��`�*�KLZ���){�z����\<��a2����76 B��$��*�������#c_h���C�8��������P9y6�?�]�G�^���h�X�Q2Yi.��E^.<��R���.w��3��r�1�XSGNW���9�����D�=����H�Z���/=��}�^�2^Za��.(>~�B>6���C�m�]D\�����k�`�f�k/p/�����/�l)����f������������K�TNv�=>:=���t�|x��}������������5�	:����C�U�n�1�"J���E���K��{���`�1S���N��l���N?�AD��{�|����"z��)���v�l=�z:0������V��<�I{�-.����!�6�t���j�J�~!����������c�+����"+����K�H�}�+��/�g�����;��!��bzrz1b��A��S~-�&�%����gfS.����t��^�a<��tN�'y����5@�=]�.!���T�;�
���4�����Q�������>�����}���%{+�^���V���dH<�xJl�%BL��P�ri����|�������?^Eo�t,������m����0_d�����7[��'�7^l��'���=�/�wZ���B�^o�"B �e�����83�~�_�|M���FM��A���
���.��b[<1���m�W��cfg��l1g�9t���3�I�-�G��S�����R84��98�����P�<����AD��N��i/�����J�������W
���|��y�����Q����G��GTyq���{��7����\m+�
���E�-�VB_H�Q	~������nO����5)�$�lcF�|�_�r[�w;{�h�g#���$=�'�C\�DTR���������.�nD����z"��J�t8�:g������%n-�K]ds��|}d���tp���x��atm>� [c�M�;�ar������2m�v~y���
]:R��7>7��9������h4�Y�wh������U����t���|>���������MBl������fzu��B���q���4x����E�]j�!���a���Qfy�o�$��<��X�"5�e�y�[Gd��w������������������������$w�Z���h��dDne������i��_�����a����Wrg�}x�y���sA���C��7���j>;��H��~���4w�g������?��[^����i�w51#����av�eGo���i�����.��������'i��������)��r����7a�M�m�u(^����>-��A�S����+~'����I2H����
o�C</�RI�>��(��x��3
,\��H4[���Q�����%1��JO���l�bc�^���Axy
LE�7��'~�����T�.�h�y{`g�k�o���}�I��B&���g�����4��^����5���Z�T[�����d��(�b�2x�QI&	0��G+�w�!��Fhg�;����T �@����N���
�.����������'wZX�oi=}�k���o(��%o3]�"���.i��]�SDy��'�"&3�$��R#.�\4e)I�}�AT��2�����Z���e�|���"3�5���x3��A0S�'�05��R�,lS�0[asF����[JH������ 9�(K;��^���v��T����t��M4�s"�0����������1l�I��m:�q�F.F�6z�h���c�;Y����?�w��>!��1bn���8���-R/���F:?�R����
��g�����4���ei�m�t1"+�YSc�lXQ���[�CN�[���^�|�_����\���������no�{��W�H~#��5�q�#��Ur%#�����4��29��w������.���U�-O�>j�.�}�wiiO/}���������C��hj��R^��.�?�
�����pW�kI�]x�=����tv����G�j�al�W Y;s���6'����C�����G����E!w4��5�VtJ���f���W}$����k��lN��F(l($:�p����4-w�s�'�cX��Q.)�qN	���d�W����^Ci���0'�y"e���3
���������E����Ar��9�9a���2��c��`B�M�����+�K�|���@|�N��cz�h�@�K�sZg�Shj�$�m�����,�#>���$C��I�nC�D�#��LRO��=b�	U~r>Zpx�Q.G�yJ�����r�0��2�Wi�NI$�?�����A�9D3C+�?�����*���;�5�!]Cl���	�����[h��i����L�+J���A�"_7�N8��~&���L2v��%��D�_~���H���w-N)�i8c�x�}�(�������I��~s�R�����>��#th�F�e]�F��	
-���H�&K,2{��`����?�&�K��I{�����5�(
�V�B����hii���h��}�K����O�N/h��*��,�(O=�>z$��1�E���*[1��'_��I{fr�S��B�������oaD�������I������q�v��@����n��`N��8���.�<����NK���-q�������u�����7Y6%w����@�k5-��X��Mw0�h���`!��'����A�|�:����-������76wV�����V���~�m�}��"���_����K}D.A��n����{�tx������������_,k���#g,%\]��>99:���0�'�(]�����]VM��r����JOa���8�������}����S���@����F23��q�Y���������O��g�
����dsv�6,�t
jD�*C�<0-��ai�Z����j��&��^\WTI��E
����m����c(�����o;����*��,\jE��y��9c�7T�e� ��ud9�';)��/�� H��Ed�G�L�z��#x~r�����`��_P�O��o�Q����d/�v8�9�(��Z����s �
�s���J���J�y�H|�~�����;Gf��w��*����SY������P5g�8rg���yB�=k�Bx�6*�!2�b�e��.}��C"ub���zF��%yn�^���u��DA�cJ��f�\X��:62��������Q�*����p�)��������ji�Bmt$T�
l~�t'��5���wT���;r�����������!�Y���+�s]�UB���R����7C��*�z�x6^���r�'	lVa���Jm��{0�a����h����s2,��?�6��k����� ��3;���M}�A�����('�5q���9���r'�q�zv.L��p��R6����2��{M�Q��r6�T���&Z�j:�N/y+
	����14� %���O���K��
:�E��������/�������|(�Lk�����iK3,Fd�26;�(�+%������Y��3^X����$j$��K �,���|�<d�����C������v�(�R���F}NrJ+^_��M�m&n�%du���
�~HEh1����c��h�`a���U�+o���/��f�+s���u!��F�����J��Z
�.�����X�6H���}��cW��Wu��u��.j����J���SF�
�~:q�*�st�� ����<�4��&�Py�E�������0H���7����'r�|A&��������B���>T�S�en�x-���C����`��]��A�S��������),��V��_Y�\J�4d���YL�9f�Q3;�d�{����d�k�������.�2@|������!!9���~����*�u���<6�����{
L]]7�&���9VOn�,���<��g�a:��G��4N�
+�C~G�z�����o������9?�I�Nn� 7�����@��v�$����70��w�V��]�4 ��@�t���D�������)��{��B���z���:u%���0�,r�L#@��}V��Jj��N������_}��A����k�oi~'�����vw�l��wtr�������������o�����kE�eJ�Jn%�e���&�����b�u��v�^�j�x�9k��G��������W�E���`+�r=!Z��K���d�`����Y��b�|�Y�b��qg$���VT���(K2����G�j��P�\=?d�AzQ����bFf6L�&i@Lo	8�(�/Z���w�&���Y�R����������S�����c�������~��Q����{�I��b�%����[�J�+��#�~��M���:����!�z�G����������>fh���aY9��"F��1[���2����t�\�m1H��.�u�K�v
����L�	���������=�;��h<N��������5�b��Ak�'��zL�5��f
/cd2�l�����7���bV�����c2�b�m�yOT�Rm8rO�xBP+%�q)�2��j2r4}7
���5��5�d0 -��M��)�*���WA����� ��t�5P?Hbu&�i�^���]�Ru�.���zH������T�����$�g�]w�7�����Q�j����s�w�~���A�����vw��d�/����o�'�����"�,.sD'���$�)b��0��ij~v|���?�o{��i$8������bV�xL�~�H
l��%at?���w��6����.��9��7���E�������J�Z-
�z�p����DH�.��?���2�0��KC�>�L/�-h��Z|Dr�)I�������
�r��<�!�
7}��&�"�O�1R���0��������]l��'���e����nGd|����	Z�L#��(��������h�O�nqj���a�MD���U�%�"o����(�/
N����l���0GF]Q��X ��Nh���iwY�H^cF�������C*+���V�g�9��O�<���Y���Q|eF3��=[$~b~�,��x���&gGJ����N��� �������^��<��>��F?�Q�U������y:���@W@����;9��U�$���hpI�5#8����$e/|��^4��h�
��i�E��|d!�.+���u^��B��X>��>��g+�3LM��)��Jp�J�\���M.`9k��8���P����9��w*��L�m�C��c��������G�lig�D6��QM�Q�������p.|Pp� A���26_��M;t�h�������h�o�dFk��y�*����!�ST�0�U+/���������t4^WVW����T�W�"�P�(���$����[�I\sy������$S��-W��u������=����DS��s^���w���
���V)d���+����N�_��^L�8����OI�#���n�^���4N���������U����K�������
P�a
�gI8�L{�d%�z/�B����?H�x9��U<�#���e6���E�#�EX�-o�K����P��W�p�E]������`���h�0(��`����ZT�S'�R�*aH�4�t�^Uu<�)Agh*�g��L�m�T ��O����?��0�Y8`V��Sx�������'��
�y-J%q�����Ql��geW�����Cws+�B4�4U��i�4�l�XP��D�t����N�DG8�� ��~�����Elu��?@���a>�=�~��{v���6���&�����~���wv�����y1����#�=d��"]�h�WX���
���F ��~�C��B��8��t43
�����AwZ�+$�NpK���D�
��r��Xq8����3�=R#��(�#Z�����-��}��nK
�P�^&_�~�K����*$����4����M��D!>Wc#	�f���x�YM�o���������~Y���d���Y��,�?R�*1�8J����H9���Q�P�{�t��u����A41l�I��b2~��l.��.�� �4M�������Ic�+�����k/�L���Z���)�\X�"��w	N8��X�O��t���Y�Q��.��JX�=����`J_|o���7a�Ql���)����:/��$�^>So���Y9����}5���T����5�,�{Z\R|LJ���������=ig`�}
{�"
�F��xn7�:~2���E��b��p|@C���i����,�� �������9xj��n���{���su� ��l�)�Dd�����[V������;P�������������pf�v�S�,�'�������Q���c��yxkn��Vn%T6���������B�|��V%����+t0����`6d��4��	
�21'�Q�0:Em�.���%�$���gt<����?������^E��?�~����
�J�x�`�R:>#O���lC��b��� BnO���ga���U{���(�&��}��JYW�M^��Z{�A���p[���o�+�>|��J	R|������o���'G���'�9���f�@dc�f�}w�7�����oB{,��|����;m16�~O���F�<>��y���}{�nw:�g��%��	���K%5�mZo��f�����k�g�%b/87�1=rq�4<q2v���y����HSrEb-"q�7l ��C�f%D������[����&9�e�	�����qYq�r�.�4zH�B����4�^~i���(�v����y�|��������<h"������z45����!��>����d����O��G���~��$�A���
U�XL
��0��<X�
��k�!.��n��!U����Mmb��.�g�b#�j���eCda�_L.!Pj���Z����5�Q��d�r%�4F�On����Amm�3��������6���Y����%q]v�g���-)m�����(�U
P���#s��}�Z���\^����x���K6gF\+Y�P�����2W3=B����0Pp.�����]95��w�[��Eb`�\����x[`cVf @N@��52������@�8�o���:m���w�vw�M_�L&��������Nr��2-�)�V���=wx�><#J��
%�-xZ\vn�|��;��'*|��%:#�>�u��5�������}#�h�+X��X�{{��'d[�����O*FX��s-e��ZP��x�i;���l�<�J�l����GkH�W�X�?��p��PT���L����]�
�zQ���0�kl��l�(,���e�X-��h��M�;���u��tj�'�?�
_qG��ukWm��Jv]������pH
��x�=]�w&*f�+�����\�W�����7E��wYPt]
^O*l�0�<e����}�{j{"T^��e��(*gK���QeVp���T�!mj���,��U����6�n���8���*���0����][O��������Z��t�okU4�!��tz�6�3Q��o_��������H����g�_��HC����m04V��6�q�J@���b��0�G�p�V|������.���p��@��,�����6[�
�+�
����XVk����l8Q���8!�s*���Tu�yU�xM�O�`�����N���SCiV�Y�+������`vd��(����#�L.�f�-	:������;�����C�;�� D��
!�[k�	���Jf3��&���\�b={�C�p[� �O��'~��(�b�S����|���]s��D���Pb�LB�����/��J�I-z���YV�>�NU����QP�����
��~XP�A���r%�t����>m�!�n���r
�\	�x��`+V���sX����/���_��Z]�-z�G-�5_(C����H�a�WJ��6�9�F���	lEf)t{Wc�(B����������K.��_��4'O���P�	�C�jm���{'���
�]�L��M�g��
/r�D�@�3���^�q�bfE�����u`��;8zg���(�YpD~KH�I~���
��o@y������OU(�����?����	����"���A��S3�*N��\�K�b�?��u�G�����A����
��"�P�u�AL��`#�4TN�SU��q�W������X\��l����uvHPO~�~�9�>����!�/30h�i1���[[����V��__��������f�4~���e������Ne;.!&d����=�0��iP^����\��������^3qL�=��[�R�5�:Z������s���J��3}���r$����x��/������QSL=<�H�
����u�M���P�R*���i|����z�T����A�?:�%#�!/�����P��i8����sP�D���� ����]�USO&��F�I�g�h)��JY���������OC�o�7	������c��I3�GT7�^AV&��\i�����>�"wF�������W�2�A�B]2-�k�g��bE���}���q��l*�*%���}R
Q�m���?]eYIK����d���`�����+��5]�~%p�<	"�r,�1�H��@��� l��|!���;M��)b8�j��&�������-c����obie^��^f3�*��e-�~�[Zt����y=]�{����^Ni�A�7+���>���eFO`&T����-��Uy��{N�y�	���eO�/����	�`�Er��fq T���0x�$�0Jb
<������!��8�h1��^ca����&�J9��d��k������u_���{pn�[6G����$���M=c�{>�q|��N�[�!�������B��~��""fj�\�������*)'#%w��� JP��c�=\w�h��Wl8"i�Lx�+n�tM|ul����:�RG�����j�2�-W`=S��V`Y	�N8�	��8G��i��Vb����QX��$
PC��U#�/�f�3�Y	��_���|9k�-A�I{4g��������J|I��E���`�����C��I��"?yV�mo�P0�bL��:�.�#%��fv��M��)���iH+U��S?���<�VPxG�+��9���WF(��4��9��3����deF7����m��4d���������'���6����"<��XX�	��%/��!�c��ck�;��
8�^o�_��[�U��W��l(�M�H�T>��y�Y���=p�"B�-����f����j5C&Z?�����3>��\���m�[!��A�%�����b��<������9��/��)3���w��+��VWD2�0A�n��U�E����5iM�}_*�p��(��^W{��V3�a��g�W�]��X
is�-���n:��.|
����������$���M8 ��g�!J�T�d����U�������e���<������D�{F��bu	!��d���]a�+M�y�.�c;��|���:����t�i��.����f?0w���t�C����e

���[5���B�z��Z���>�y�#d��u�����s�H���_�����*If�9�a�"�/����cg@���j����9��������p~�?z����u=�3�s����f�����8e\����pl���Y�j0����9�_Euxy$O�0��4�|t/�!�� 6���"��D��M'�.c9��3��n�j��?Q{t�izo:*8���
9���)u��4�ir����3�
��G�h6zP�W�����d�����i�^�3����LfJ�F
K$It�]�0���,�Z%u�J��&{���������"Y���-<�����A.��p��&<�8�g��~��S+�yI��XX���	XDa�b���:��TL������^x,�%H�~*
��r�����d��������'�Ri�m'�\���x�`�����D	�Iv������uD�����M�rm�+&�����f�D��G��@�������S��Ib�=������N�P��Z��]5�7��8��*n��H70{���E���d6|h�E�W&C}����Au����wt��=m����[��R�6�b�3	�8iC
i��w��(ofV���K(l�	�����C��(}��bJ����Ac�����w�\��([*Ww#� 'vd����	
��bGab��sM�nn"u�^rt�����V���f�y��s�:�O��[�.#}zZM�I��	Z�t��J��h@��������r*yA?Ql�`�w�ld���_slv�(|�T�V�S������|�q�"���O���1��R�n��y�C�������/>;Y�y����J����
]Y��0�����L�9���+�%N�$s*�����������J���������Ur��P|�JQg:D`�rmp��Y��6�#��^Sx7���}��f$�XS#$��%_b�u\�q�?<�/��!���DqD$���*�PJ�Z2g������T����Y�:a�F�R��d�%_.��c������c�K.A��i�B+,8j(2�k���������H�~Bl����J�"������TDO��o��Q��=���#�f���d�(�N��W��cK�m�l 2*d�k�h�F�!s4�qh����tMXd��A��,���&�R���g������S�{*�������q��w#�z7�<�����o
F�����cU=��?*Y���>�
�L�cuA7b~��x������L/Y�'����J����S�Q���8j��������|pT��Q��*�6i��4��R����l:��?�S���c�9I���P�o�*<������N��ii0x���P��.��VR
�B��HfK [8tH�_C�V�1��2��^��x�y�-`��`2E�/"����E����q^����|B���
d��a�#t���'��Z~���f��������+�+G���������V<�$�F��<���|��������C�#	��:���k����O�����RL��ktr������=��ww7z�����ddC/������s���<�05�P�,D�rwZ�E�����@R�a�e>�I`�����q���c�!�+y��k���A������k��,����'��`�z�9E�z6�
��H�
�"�y�a��/>��<n��,cI&�4����a�n�!����/Z'����nz��C�l�<Kl�G���]����{r	Z��T�i����7`������
p
�="�����  A�;%���XSZ�����G��!��\a�#n����"��C�Y��M9?k0`{��iJ#^tZ�7|�J��Ml��-�r�+=���c&k�aEy�m�
�'q��z��Cs���IkJ��ETV�q��$DPo��h����`�$i=��#��j<��{�L����y�.�=�z�4���{�4'���A��BL�PYc�a��*c��"P���x:���,)��g\�d�sc�����=���&4��X�X�)�EW�D��������	���:$�@u��Y���62��M��Mi��jN`�0���B�O/�Swl�nY�)����(X�w��~�\�uF`��qk5�^�+������x��)EFv�t�<�FT��w<�K/G���~D:�.�3xp�����Rf�%�4�a�N�SX�
�X�����hW(1���8� �d�.I����.���zh�zKa>@�`X^$�m)hJY�����s���4:����q�*+6�(��$���m�7o���G2��������<+K��|�wY"*c2
���U�P�}��������G(Q�,�Y�J������+���������<Swz'���~Y)L�Av�5R���.�K�@3��@��uis���H�ee�q���)N�E�bA�.����z~��Z�KF�8�&>���Rg1!5��)��kTN2�*&����`�K�tN]�����d���&�[f)��<����h���������}�������=0d����DyZzA������$����������g*u��*��>��=����;�{�'�H7��XZ�\�4�|���q��k���]�nrc�����"���h��GZ�*u������r+Rm�����+z����I1�;����{x�PN��$��D_06J���A�{�����rI���.)�H�..�R��u��*�x����
=D��
�����X�)��s�>��K�i��.0D�����r<�o
_�rDR���R|Azq���nIj�
_X�V������Ba������|[�-�!�Q���*.(���eA
p���S�����}��
U�W;Cx"����lH����{�������$�:��,7X�#�����Cpf�T�n/4�#�g��~`��]w3*�4��aM�)�, 'j�_..�����)��F��pD����?��3]�zX6��V�-P<� D�5���g�=���ED����i����d�<��v-���bf+5����C�P5jf:���^���#a�1�O���7���D�hD
B���w'n�����<������������\���'J�U#0�f��g�;���Z\�b3����"�?�N��f�QUz��z������2R�I�k-�����y�����k�����l�Ct[������
���������\L��,�C��/��_zTU��l�`�mT9h9���_���hP�y|�ew�c2������������S5T��I8�����Vp���"�Z��hi��Lx.er-,����x�heM=%v�T�F���J�������g3=�>,[�����xq��l	h�=/���hf^q%�r�sZ�x���w�����Q�wO�~L����ifpa%M��y
j�T���������x�_fS��^9�-���x9fc�����l8�����!���@'��P�Rh5$L��n6�������5�;B/�-# ��S������N����4p��VH�y��\�Cu�^�����d��i|'&�(v������l|��:�4�4c����k�C�����
�t��aU�`��"�HY�
o*|�G�������q�u���Z�'VqC��9����47�H
�6�=��Xg��80�:0S��R�_�s�ui]4���7���V�.J�R1��Q��w��L&���	��ph���|���bH�Sr��Tm��O�X����~.(���
\Kpp9�[�Rv<@n�j=�������]��
�jM�
��Qo�=��r�:�5\�;�F����A���J�Xy���m fV��21h!<��T1�t+z��d9��O�Yni@&�=.?4�>�iJ�� ��R9Z6��#�������J�j�`�����~��UQ�NuW��*��B���(<�?),+��/U&�)��T��zt!��06�tj�ua�E���e��a
%��)3�����((����=b�-Qjxs[Y�9]�L���h���+��w�������}������*2ys�s��#f9��?���C|~7O���BY��o�1�R��z�i���x�N��uq����E�-}Y�������R1&$���V�����.�\rs�3
\(��"@�
�@bR"���&�,����h�N��D��M��+���d�\T'.�>*��B�a-C�Y�eb3a�QO�a���}�d�3��A�y��w����7���D�u����2�5����� �(�~�[�=M9�UK,k�"�3x���HF�,�����)�����Xl_�U�e=���V��N��7`a������q=%�������K����c;�u]���~��Z��-����/� ����N�M�~�^*3�K�z�+�y���� ��s�����T�H+v��7�V��pW�]BAQx�K��2lf�0x�zB*�L�n�����T�$|E���6��]���eE]u�^m�"&3�y�oZ[Y��7�"�l�%1-�e���F��t��p�V^���JV�J�j�z"���h��ps�����V���<;�b'��@�{��B1X��B�QA�����������ht����
��F�H+$n�?��Y��������q�&I�B�Oi)����SG�9S���	��h?��1��K���f��I�6����b"��0��*;����K�p./��	(�:�3��*����x�}�C^a��������i�{�'j:�|�����5�</��9�:����^:��Y������3_^.���nswU�t��w�`xt��U����w�!;�u`���rW�2#��q���pfJ��e�az�Qt���I<l�xm� �@�����N�3��6�_���4`�1��	�j�-F���q��t�0����-�H��W�;�!��A
P��`Q�t���R���b��'�V��B�!������H�"�������Sz�a��q
8����$�G�Q�Hy���%�Z�r��R�,�u�z��������7XH�"�<"U��o�������{��(�����5��IQ��h3�Y6T��n�i�&��*zG�P2����
t��B� �n��4�o"�-J��NqE�6Q��X��P����������������a@����	 h�l�J��.Q.0�ecB���ml�8������+<����D������D�Q�z5���/��M���O�?x��$��
�c�)��I�5�4�r�[�?�6>�a��b�l1.��LI���O:K��}^�n�G�)���������q�qe9�b����J����<?�c?��Q��d��~M+��R.�
����������A���@e�{,��.?W�Cw2����G��l���Vl��cF������������>�X� #V��1ytf#������:Rl��LA:��,@
I�C�������EZ0��/�9o�������N��������No4B�)�AX;��J�;:�>MA"�����#���1��cL�R�h��_]-9{$�O�d�����s�l�G ���r�����s����:���:�I��%I
#�{���y����l4$w�0��C �	�����u�%�:��Q��Lm���eb�`�%��z���T��0�K�~6����z�*Y����Q��y���C&�V����F��D�$�����0���G���xnKd-��Hc��S��+���u���d����*��FY������Z�Z�4���U�����?h��($�:i��l����/$����s�h~(je���,97��4o4��MT��bM���o<b�V������K5��}�����K(��4X`�b�?��j���nW8>"��"1��!�A��R`�Y��q0V}��Z���Sa�VW]����[,v!��f�TD��Z��^�]�[{��P����C}���-�}�(�3I�P5��4�j�Kc�Y��!w�0���L��9<2&�Z��Gf�&q+z7��m�G	#����GN�k�����H@�F?���N�!�B��R(�@�u���?���*�K�
1�c�b�'���vi�N�A
h8gRA����(����5Z'���F�
�3�&7QlGu*U��L�5�$=�(���x�N;�/��iC>�=l=���R�����������0CW?����.���nE��O����s���:����������*G+\	*�]���0c��=�C`YV�D������$�vU�MI��Zn?��S��,�PH�H�s��+��"�e
.9�L>�v��)���J�}(����R%uA���)@�g�O���'���Cp��z�����~�{���������s|�>i����i��
?O����8nI8�du��$������@1V����T�����X�\�Dt�i����1�jV��`z� ��DYp�0�F|��m<�-����8k��8�KFB�B���D���&t���0����pW�p\T��j�����N�"�jP@�����Z��,�����T�v��x!���C%�)���j����)��('����s���bz�{b9�9+�����9���o;0��J���`������!A����@�3m&�W(�aK�]\��^���I���Y
-�k�I�����o<�`8@a����eh/>[�Z���\y������`3�����|��������F�~��������7����=#;�8����,��\�&,p�@�>B�I����gC�T��3�14����{.Fb8M���
��t�|Lg#
{F�|&�sd�������@3��6���	$9�R�����/��0�X`>3n�O��������lz�]��*��|X�Q����C�T{��&�c���Q�,E%������,�����?��5?as�%�(�;��6��2|���I����X�y�H���3��*Q���:���Kt��_��+]��5���'Z<=J'��$�Y�Kq��`���^U,-MFT�^�b����R1�T����k~lHA��$/��WD���ALk�����6r��v�6�/��f�_����:�������|P�0/���R�J���t���p����<5��$E�5�I���6���������<+�WT
�")���!�ozy[k�rBV��ph�^���/�^UY!o�I�%�w���'j��)/<*�:�3v.q^&�����/���'@h+�TQM�bo��;8z�{Jq��Q)
J�������������Y��k����>0�^DM�Lt�gs|��fF�z��!���q�L�EB6�L��l�v\�{�\�]F������>%D��x��2.�%c�;�~n���{�'m����nEb������������a��^��j��L9�/��������B2��������_���Lp���Y�E�~O��?����;:��+@���W��g�������8���i2����S���N�kR�)�h�*���n�tTW�/d�����44��|��u����������9Y�T��.���^��WHS�C���dW{��i����L1��H���O��+�K|�����d�J���&.����;d���"�B�}�`u����W����b��J�`5]��[���F�-�
ZJ�)5�i�v
������yh�0���"�t�'���t~���S�`�gJ�������{$�X���b�����/�������L+f�Y�_U������a�p�����9�
(�s+�ed��w�{�,.���[?gIkj.^����/�:8���|:���;��k��,��/B%�t]��$���h���~������?������K��g!�1��|��*
cIIn|�(Y��Vk�7_�BMsT)y��mB�S����0����Wf��k)BT�6w��Rp�0y�!��l��aP������4/�a�|��b��+��Rn~��e�[����B�a*�����,g/�����yz��
L�#~,�a2��u��o���/W��Jo������v{
�|�	q�XZ=I+����?��i�(bDb]N�~�B��R
Q6�A�rt����8��~�> �����������6���j���6LCq��g<mf�H;�B���5:����YS[�Kh��hY"e$�b�iGP��|^Z!^_��q��p.�^	>����S�	
a/���.W���(�����D���x���9�ct��X@-a����@��i[RGk���YT���I���7�=�{���K���E���������P��K��e.:.��0�����	x�����d^�����2*nOf/�1�Q���������^\�j&�n��kk��3?L����P�|�U���J*Z�~e<*�A����52�,�
d����n��Rd�����r�?kH��:���P�>;���.����pA[���.�����.j(,���N_My�����y��[$��w����l��"����%Fo��0i��3���5L���xI���g��%�C�B%i��y�Qg&e�+�2�����;Q-�!i5P��wt��XS=\��1Z�7�u�c�1!�#z��I���DD4��k�Q;�����#JR���$��������_3�V�g#
���D�R���l����Fv�qRCY�vK�s��\�&~���!f��z7�^$5���F�w��y[��e���ze�xa@TOf�!]����Ex�~����5�����ek�iw@`�=�98NnVeo
rA��G}U�L����SR�i����
fOQC^���k6��5��|n�g�Y��I�jl^�����P���J�������.�����|dN��t��������(Z�>�}nY
��s��<��k�b3�	�f`�	]]�B�	�&Gy~���ZiEA#N�Sj-M�oo\K]vG����[������\Id�|v�l����X�����UZ�a�����*�W3�K���4��_W{7�-�D{���wUO��vV.�Xqs�7�_Y��W���\~��T���m(P��+g]IL����
m��\�[���Q�y�\�G1;��F���oW8v�x����^�h>r��T������J���s4%Afx��%�/q9�@��;�5f7����3�:�f��j)���/���g�:!��T4&�x�1�������]1�z�3�R������8��@y���-F��R@y�V5G��1W�#]��p�~,�[E���ph�w6;�vT1hXy�/�K,��f>�/�{�WW����9-�R��\����W�i�t?
�g�����*GN+)��U��5MY'���J6v~Y_Q�/�bpIFN'�dTl�u�OVZ��r�V����~�G������QQQ,
wy����	%q�����M�LI=TuI�V+���Z0`%k�`���q��q�
D?����\�����W��_z#$����2-d	���*��"��-��)�"i������J��
%�2���<%�6��c��I<������4�2G�B,=w�C�����+!J�5��J9���b�k8���o�%=����N:�<Z�nc�]7u�F�D��~������PRq�������y������L���7�9��:b��������@(��U�+�#�/o�y���~��{v��:6��4�v��Wm-o����p�w�9:����;������%�1�ot����J�q��;:8�^,�#)gf�wF�,zd>��|5
���G�(��@`�w���l<L����d������H�$<2��O��9�J�i����Y2����_�������Z�i�M������a�PvPX� ��9Y���,r�s����r�j2��F4�����:r���D�u�]����\<��m{���I�6����N��>�	6
74�����(B�c�~e-���r3�"�5U%�������'}����������T������7\��)�"��}��cM����SK+�I���Ou`J���������$O3�!E�{ANo��|��!���.]m�ha��)-K���_���
��z|D�P�����EZ�1�&'4?iF�%���	I���o_�ygoc�A�]u�����t��<�A��F���.�K���Y
��d��8��B��kF;��F��4R\+��f��(|��u'|UJQ,��q����n��$��`�Nj������� ����������_C�[r�K����v��%����z����i������>���;�b10�g�e�5����������)����N�v\����0\��k�WQCL�8���z?RR��P��8�����/�p<�����|�2�������L1�
��6���<�I��q:ey�����P,������Db�S�B��xd�g%��^���O�����m��`��n�P���d�{�N`���������R����7j�B@6��yZ79���p����g[�=�l���q��&vhg�9Y1A��,��g���=��"����Y+
����q�b��=��:\���O�8���?U���uw������=WB�A��v,3
�INy�,��w�zd�I{#�L�(�S�J�j������Ta��P!�Db��A`��P0$
�o3D��
/^���l�����B@���)���^�������R���;��	��vMy�����v������
U�Z���DmhEh�@�S�1����� �7���w���>��`��Mz�9.'Ep��s�����9�n�I���"!_a���n�<����/��`�
��;B�\�4�p��c44��4~�Z�����pa��9U����������%E�:7�
C�A�Qbd]�pc�$�����[�bp�	Pt�	�?��*��YC�f���������O\��2
���z�j�>�]o����W#���6����z�^�0��ZIT��5$���X7\���K+V�d�"&�L��s���BrT�J]���zr
��.[*w�O����!����k�J'2�qZ	�t��U��S`/,\W
fhI�no�!%�n������]TC��9�[�$�s��2
���;���58��NWZ�
?��x���\L��0z���On���z*v��Z\���O!@����4�M�������K�
��ub�l�QX/��#����Y��K�m��Qzu��	�>��}��}�2�'���h�M���.����D�
�d�;��>���H�	e��{3��)��+�z�A�:H����6��������K$R�������W���������T��:�*����zt��_�?�ip�`�0��c�A���������}�;(Pq�I%b�!��46g�Q�$?���
@�������x9w���R�T�q��Z!.���>]��@�=R�r/��Lp�=~���1�j~�m�@|�|oc����$��.�^�����V�D��0���}������jU~]If�=�cpe���_N�]����
���xx{wh�UE��*���X��Xtc��9�04�1#���f��+P4_\�+����5_�a]N7����}�"��6<u�����K<����0�j�t�ug��h^l��E������\��FJ���O_�����������=��(H�r.��H�����y�Bb��_-
�J�>��7B��^�����^�k9������;��F�/QC�%�R����q�Q�m����N�����ZE��|��%����B��:z�9�����
���r�.����&V���xL������p���f���o��}��9����):G�tl�o�	
{��f/������xY�����BXt�Y;�US��WU��J]�^����7�|I�R��/�eM�AU����,��j�����=+�]���Vm����[9��W�$%����g���#f�[>���(%��n1�PPo���x��M��i(#�o���/gE���4��a�7��9 �[�732�9��L���:�����������o���wO�$��6i���Jx��J:��+lNZ�R������rCR���%��7uvb���^�5�pc{G���������*�iN�[�QX}����=����!Rv�D�"���b�dU�����KH0�g�*UBY�(n��o�����@�W���6
����!�Hjw�p��\�1�����C�
���vmT�1&�
���K��`��]���q>�=��1��zL���P��z/:_��e���8 isccgkk���6����_<����x���`ymm-z�O>?�x�����b/�����6Z��f���������o�?K���U:�&����7�Y7z�h�����'�/Z��|z1IN�� z��u���1��w�l6���-��]�~S��F�$0�`
b�=c��{���[����MG	���W:�
gf�W������Jx��z��k!:WUZ��1�5��}��`�HR�9G}����l�����cs	,b��T�7�a�����FA�������#�F�kX$�i2m�(0�z46�yv�����N�.c������{����^D\��������%��F6���y�)B����ba�����B���Rr�[���$�so��x4I-aX�`�Sn5�������������6������H����'jB���y�_��-�=d�4�O(�6z)}_�U��qU��j�������u���)^+�W�� 3{����~A��s84)8E���r�.����y.��5�*|�2��X+].O.���y��5�����:�,���B{�4���e�f���# 5&z��F[Z�������@�u����v����869�(#!ef�3�Pr=�"�i�G_v�����d6�����`i��&�G�,2��,"������%������i�������_������z����O��`E��3-Q�����4vy�|�V�i��������>�NSg�C"��'@7������n��t�>%�7�jR\��+�/�^���f��2o�Dd��J�{���1�X{��##IIt�m���c���{���� ���B��D�,��0���H�W�\GCI��t�B���7���� w�K��u�H�.�����ke�����,$�;c�Tk�P;/����#����RT�� �e6L
g9���e�����z�j��O�g,<�ny���^X
�j'�x����7�z�+��F�N�R��}:j�����R���UtZ�v+e5�����|��JD��7x��_�Ry�.E.�V���_~�$`:�����.���y��������,����$���N����D�hI����?d���n�-0� �
�V�i��Jj�k#����;^�v6����f9	�k���� ���Gk�t����*~��=b�������tkBsJ#���{��
�X�^���i\X��o�Z����J��C+-��AF9J�S�y���*=5��y�{�������
C�j�����q�m������4��uy�������J�3VX��
+��i��� �6����T�|&XN�����W����E����|����o!�>�3���2�AT��x/�WO+�?>|���
s��5���M��&a�>V^���A��7H5��=���snD���/�^�?��c"X�����]�gZ��2�{�>�i5�?����m#��o�S ��6iQ��~Lw9#K���,y%y��lDB�$�H������}U�*@�v��33g7-�B=n������h=$
l$�^�k��BV��1-E}��3������&3���x��n^���jE�K�:��PB 
������C�?M��g���Z%�$X����f?/��� ��E���g�|���<$�b�<�<"x�|2�B��m�(wFyU�5Y6���o<}4�L"��r�@l����&+&����|szv.%_|+-"L���x������P��j"��R��M��&[p��A�:f�2+�<xF���m�AM��Z3��C�@]��>����uS�J�������v�
�I��=��b����CH�*&����m/j�
�����.�����\Vs�I��H�2�����>I_��}�w�#�M}�P�k��i,������������eQ�����S\��.��Z���4(����$Z4y�	���W�/��@>G}��}v�c�9z�&L[}�~Q���!9u�|�r����j�r���_w�(���������4rb�'�tz�)$�2H��muq��o�]��G �0$eK�����y�����;����J�j��W��������;���p�����rF���
�t�8�	W��b�g���-�� ��Ul����1����%4GW�[���������A����{�+O�(��9��s\��������z����Wj�n����_�>������w�z����?}��������$���%����'���S��M���2��''/��������3sR���������U�)z8�p*�d������H^��
"����.�+��d.�@a��6��;���M����*gu��B9����R���8��|��vG%7/S���pT|}�'��v[���u<E�;[��|�\�����k>��by9��xJ@	��&/�"��1-�5��'�|���m Z*�����*���f2�(��S�P4���n�}J��_�\�������A��{� Q�(�K
m8�1��v������������M��ho�i����\vs`A��:�i����!�-J��U��Bz$!��(C����n�R�TF�0���-�I����+&���4M���p���j�N��}�v�o+�2���i��6��zK����?{���{\
?�a�'�/�v�G/NN8~��{����v�����������3fpy��3f�������?��b�5M�U��n�/L��#�`
��W�b����LQz�^ �WD��X�����y�����
���J����+z_�H#Po�����Z*��o��i;xV+9nc&�]>�Uq����J���q8j���r�c��0L��y���ttt���'�q����p�&��"�Y�j�1�j%���������0��r@���"�1���]�aa��\Z�UV1ur����@�R���Y~�x��A�`���I����oC�j�
��6�8��	����k�B���D�"��T�5����<�<�^��A����C�����S������G������Y\��
���dC����x�!�f�=.�������
����b�YE���\^]�JS#��.�����C����%5mK��u�
��@1`�$���J�z�����c��%��#1P�FN�&����`�fW1����d[W[��^%�8����F>�g���{>��#`K@�k�1s�:�=����^��z�"6��$��,�%BYY�R,����W#^C������n�~��I~le�.�����j���D���t�f�-?����Z=8xT!����x]0�����VX�\4�[{���A.�P��^��`k6t��Qw���I���b�� 
-*����[��K��u0�����&^�����VH��B�s<X���#`e��NN�#��bX9u@�� ��|����4
A:���m���F�&�b�O����<f�e�,�L����p;?�<w�������z�t���z��a�]d���g���Lj�CD;��4x�=������08�Z)
� g6�3���M������+�g�4�������7$m#"m(�Z6�B�c~y�cLH�Pi��
w'��vy��V|)`�|�'�.�����\��C�T�`��4���xXM
����������Yw�0�������I��J��������@	N�$Cg�m�x }���}���X�[�\�jBT*xt��eB�Z�yCw����W�A~#L-���_3o�/E|���zT��mz;��c�5��g���c�uV��As�Er�+���
	�C�eQm�����RF���pl�����}�����c�j��Q.���J|~�����
m���+y���Y3C��3��$�O�b���k����:��>\������J�;in�m���:5���f�{a�C�=2H�/�����V�)
����0�\�"��mj��(6����akY���aYFH��+�R3q�F&���4�8��g�;-������u��+�^���.�#���T]}��P����R�W4���m����������S�]��$uh�uV�Q�v���DW*��<�|�a"��n_�0��2�GhW���*{�m@*��u=S=5)��*�#q�=���j_��!.o�6���^Q.mX}yc����a�u��Y9856�����`[�'O����ecc���Azz�6��Z���Lv,�[Fch�_	��-��O"W��5g�	.��Y_]��	��e| �i�2��G��0l,y�Rt��g�ItZ��8����c+v�G������P��U;�O��g���������pet�`�t�^W�uAm�F��oJt���lv�v���/��
6~�w���O����IC��� hb�q�m~�]�#���������
�FQ*�:x0�[�t	�\���������q<J���H��3ouw8�n��k��<h�b��>5
c>��mUK%���a������@)@t�1�#0�X���h ���m�I�A�T
�N93DR�����|��b4�F��]M�XM�FC?�z���r3�-(��d7ejTd!oa���]+z�9���	b�MQ��X���8~�t���`��9n2�C��g��&G/vK��.��'R�yc�a�	E2�=��B��*�N�L����o�}�%~�~��� ��o��A�[��+��.�i[��<B�-mO��N�P��3��b.�0�.�<C��]�~A�M�y��w
�KYnD�� sM�~�1���]L�'����#��2>������6J�~9zsfx���S�59��G`���t[���l�wz%��I����IlB]��1��/��G�[+C
[���5l�v����@f�|�d5>S�����T[K}d�=��X��L�s���~��M���etV$�wY6���xi���9�5����&-��f�nL�h+�Ql�#���o��ht=`M���t���@�r�HS����?�4�@��R�����M��.�k�
�`Z)V��1���F�}���t��F�t��Y)���7w��t:G��mY]����Y�\���d��~��n��@h�����F�y�<�<8�*��bn�� ��$����u���pj�-�F1M�M���p/q��~6�|�Q}�X-4~���!�b3Vi)&05]Z��n�4$�Z'���;_�������MQ�O��rz-{J���
H�b%B��xS�F��	�{
1Dk�z�5g4����_q�i�k�L�NR��6���J�����t}o�
��}V��>p�1�b�|u�J��,6���"c���N�U��LC�,��������)T5o9?��i|�OZ��t;���Np�,��H��$}M�����Oh���_d�%$�p�M �Q����;@��As�/��A�%^��I����u������@���U��Y:e�{���{�Z,�Ds�w����\�T�v[�x��s����m�����C��P�##����l���0��-gy�|(n�l������7N�t4@\��a�� �)�~��1?RqU��x��s�>�z���������ZG9\�v*�Jfh��kFXb>����	!d����$�/��s�@��-L��&��0y����(��p��[�D��c��?�T9K����K;���-����woyfz�<t�����K�������q��BPV��(�e�����^��%���G��=����>��}Z��USz�]�
>pQ&P�=�@��N\��f���z�.����q������o�F8�������o[4b���������������)������#�'���f��VbP{�Ef��Ef
��I?���9����|.c
����/�9{L���'��k
�������.������"��QdqT������C����U���)z-�c��m#��S�y&������"7-�0�q:����lOgQ�Sf���K��R�+$F!�"#����x���hF���J��y�]�#q�-��Y�����m'9><�����P/���
����AR��{�M?'	�n���������*�~h�U���96��RDy��L��!���b2�1����':d�JK^o	��c��#8~���j�������bj
�������;��~�������m�L?�c��8��d�H�N���iV�;����i:����~F�����T�u�����j���
PoG4�~�� ����@Bis3h��0�
�P��8p�t�����������P��02wY�H��/�,M�l�����x��9����}GDA�X
kb[��m��'��f����+���)������kF8����lM?�]��a��0�6�Z�)�'��v������~4�a��i.,[8�-�J�$x��-bU���G8M�������5�3n�pC}V*����$C����.n�PX\M�a��h6���6�����E�E6��U
w��0p�M(M���bn���AS��4u����z�R�8O31����E^��R��[�9:_	�u�N���+i&�R;/�.���#�
=���pX�v��0f������a���2���hN�'� a���\�WI����~|V2����"��Ks�L��&1\�t��~86b+��e
&1�6���z�������`j#=��$�U �Z���iA�2��.��a�.b����0���)X��u��o6w��g{����-�Gcq�"��!*\��9������,:�'���g������q��������jZW\�V������0�I0�\�#���m����;�<�:�"�Q7�I��e��A��Y���ow�����hw
�=�Z��8�Y\��&����W�N ��Mi������V������jh�8,-L��:�y�&:�ij2E���'
��=�7�^�',��~dH6�_~2$a���KxTq	WL�L��D�-��,��������_�J�����F"6!�T�/��^�l��/��_�X�+����cy'�p�v�JrtK4B��l���5�&�}'��v)��i>Ek%�/k"8�)�����b�UZ_�U���1��"*�3K8
��M(.�g��0�������nh� �sBru�2��I���6P�t��J0o����(��F�H��;r{����5_2"�r�����O�9t�ee	O��03�5�1EJk�����9�Hx�m
>\v�<]�p�M�<O�����r#�x�%��OQ)�n�4d��a"kI�W�!���-�a�������q���	q���{����]���F |����4�M�p��"��{�0l�a����[F�H���pv����5[-���[1:����QA����g�>|�_'V����C
�Lq?���P���S�F�0}�K�|��_���J�s�Y��/�U��U��G��W����]�Kg�
fy=N'F�h1\
o�|���-O��1N�*��K����M�
*�wr� �A���zQ-��Hj���WgS�$���`S�`P�R����
���z���,w�x^o��tF�7��"4HTd�`^~]N'�.��0^����a�?}
������=B)+�3�
�Gx�N�Fg���������!y"A^�� Lw��m�xz���X�'�������>�pS�n�H�-���7?F��MK�� +����^��������n��� ����x8����Q�#�[��@p�	L�6�`�G5�m
�nf$���H��L�z~���hy��98��Xr������3=�^H7|YLoK:3T�����p:�T��et�%� �U;0�eH]6��`��T�\J�����i�t�<�yl[b/�f������T���m���a�:a��J$?Qn�_v?����#������
V<��~o�f��YE� �M���������XKi$�)�
sJ�l����	I��Y}4�-xI
�����UN�$vJ���T<��3]�/������j�3�o�fu����2�mE(���53�^M�f#Q����N�tV�99A
�.��_K�t�g0�4 ��>���3/>{'9=8z}pzvxv~p�w02��=>8>�����B�(#��z/v�^x~.�I�#��>#�i��h������"���nn�[���"�o��aZ�w�KL��I�����o������V20��q ���c�@��3q�z��������x,��`��^��?�����C�[k�1���YX�H��
���r-f�m��2�Q�J�w{t�a�=������5aB8�3I�Kj�y�\��t�|��j���k�L�J�����s�����%AC1��|��:q\��k��in(��K@�����2�OS���MY���w�a�s�SQV���ZR��&+�����)�B��t�>��$���3�}Y��hM��u�p�~:�pT�����GAZ��H����������9�	z3):�J!C�����sDT@�L��2R�����q���O�w/@����9����R�L��Kx�H;*���X��s[��l��������g�'H��,u�~�������FP�6v�b��"u.�Honl����!;����I�?��w�������48�0y�]��9P�ULj'	.�^����a�S�!jS�������%&�����!b�E�e`�L�(%�H�1w1�2��n���&
��u�_�K�n�jSJet2!�@���-9�V����V�J���Fn�Q^����H�F�mB����8T|rf���I�W���9��gae��+���?o�?�W����@>i�g�o9�zK�6:F:c�D�Mq��S\`Ft��~,�<�R��^��kC%���ib��D�.�[oI%��"Hx+�5�YgF���u[��_���5�3e=Q|)���/A���$���$�����J����X�?��9;����pob<]3k�)��AW���j����V�6��&�9o�t��!��9;M�n��S"�����1�-�&�Y[�>y��W����&�sM����01P9�&���H��L���%N�����J�$�4���/���yY-�I��J���j[��:���)~u�����=
��T[�:��#*Q���E��i��E�lpO~���S�����!�
O��,<�&���V!�+��RE�����%���z����O���S}��FC3h������������e]a^Q��D�����ZB���6'�A{�{o��9��S3����������B��-��M�b��5~���1d"����Z���-\,�rY�y,�u�h�f�bU��8��������>���-$�8��E+�j�p���.�9*�h�-KCm[m` ���I���I#__������2}v���Gj�1{���9��y�`�����t�F�f�-�fo�7�I������|�N4R@������C���r��Ur�2��uR?���VU���/����V���Wt��;���v)�] 
���j������j�.Z�_[�/l�Q����������Dx�,Q�5���e����3z��1hP���o<����c�d��k��un��0�������;�]2���[N�`������`D����xY]1 4-c�Z��`F/r��XC!X)�vg}�0[:�{���lX�.Y��?!]�;�k,1�����B!]N��:����~<:yI>����o���g�;���(��5�Mq��.���wg��Ex�I�B��vc�z������ �&t fUjD�E
�{.O��!����Vf;�R��fe
(�v�	'���xC�h�h�]���%jH�(�^x����zM)�YB���:k��7G���|��4t��K�`6���oKK�ZiL�v����r�2H9o�/o�/"{ u���$"S8�v�����)O�k������@�O0�H`C�Ep,W��N������o�!����r'��d�1/�I���y}m!i���O����z[���7��f�m�K��=��>�X����!��w3�����f�����1�{G
1B��z��/����#89YC��3m�;E�m�j��<�2�������X�(M��EL�Y�E�(8��$��R����F��W9�\0�%���_���';F��#���o]s��;
,���]��K�"+m/�M��e�], ~!�b�+����@�/:��
�G�����B�y
c�,/F��y����p���M���!�f���D_����o'&�H�$�
�t���(�:�
y��|�Y�_Bt��b1a�[���;>��M����;�����.�*��v��Z�����k�$EX�����H6)#v�7��e?������ac�����4�����5<�T|.��E#p�c��j@R�i:�Eq�L���"����O$c�������[�#`��$T���G�a�!�b��y�c�U6�\����>�M�����3�A�,e���Bnh���|G��.��(��2��:40�.��5�XG}�[�x�}=�!�'��;���5��	���F���b#���:J���yUBR!u���&_���������F �A�,I����L���W��fE��RXlO1a�:$�7������q1���$�u�$a��>QV�"��Y9�Z?6V�;�(y�5���V(�I�jz#�}@
���F�E����)�p���M�D�%�� hE�Q���Ar���$	�s
0HW>�����lE"x1���Uk�L����n�X���S�/Fyq�us5���'��m+�p��.��������A0�����T&�dg�S-��e&�4�f����`�L||0���Y��z��N���z�L�|��8�6^�/<�g;b�����Pw�������������K��4����[��f����K�@5
�spw�
��[��$��2������0�I=��%Dp�4.����'��Ph_��q�WY�<v��S��C
�������v�����<�K���y��'<o������Zb-�n",�-�������V�0�!�[�*�=���Y��Q�rdm�S��[b`��s��.�Wr�z^�o���������������E���VN�3��e>��@�����\�����#MO�sN��2w9����.��P�E'8�����)���,��fdj��C)�Gf ��SL��b��_�Qd��0�#1�]B!-P(��=jl�+�N����_c�Y�F0�s�4�2�z��>Z�#��v��]��Q�qY�����F��(���$q�AR�R�r��-e�*����7@���So�����am�w~�*�F��z-�
�k#i�����nay��gVS���� 4P����$�y8��u�1!%��t�L����5+"��%�9p��>�\��e��p�(1���2������f�,����'.�}��C#1��P�@�y�H����EU��H����I:�� �C�d��I�z ��@��
��:�{������S��(�����g�E��	)d����t�A�_r��7zN�����m	� z��,�����)Vh�N@@�Q����m��JA�J�����T�u('p/A����*�bec�9e���������Q�oG;;�����Q�
���KqH�����K���[�(���$o1��FT�U�fg���V
FD��m����X�2U19��3��k����=������ �D����d#�������%C4h�r����^�����F"�K/����}����
h�6����|�sE;���'U��>��'$���=�
%����_D�3B�}��5�X���%��=�`22�:t&�7����,#'�) uP��i�N���U�
�GF�j���f:�eK�*IfV�lD����v|w��R�����l��r�~%L��r�O�Na��F����|����jv �a����{[���-��]�����l ���8�)�����>(�P��+�2*�btrt	�$8"����N��	�K�#��)�do�~l������X�6���N����Si����#(i��n
���D�~�A�
\�c�z$t�+v=�=\m�����u���f�fKr�]�����K^�E��Jp@(�(����t�/������&4���8������X+�����O�@��%X�����W�4�7����H���z��{RiG���e�;ct$]�'{��T�^J]'b>�����
0t�8{��C����f�
��"���&Bb��a�-B~���f�S%�����(]�hi���+�9��3��\����Y���Y>���z����ql2k��yM���3���e�d[�0���
�xu2��X�����'�f�tHi��K�v	]@�"�W(y���U��p�RTXu�����$t2A�#y���m@=�a�<_x�UA����HF?��h�!���
�c�F�g��E�D��$������H2M2-uf#~�y3.p?�p�;wG{�9F2�#'����0r��5�����r�U�3�9�i�ZR�� 4�c�\(Rc��C�9��Pl&�4�RC~9"5��k���s��gV����~$�
��������B�E�W>����	s������l��|�����Q����c�	��`����1��B��5�o�"^I�o���{���	��+��;�M��D��);e�/�(D�7B��(��6
�7h�����C,
��'��&$���a��
��h�@sfqG�������b4���c(�8�	s�$�4W�Z��*"�>�z�A_�R���%�*(�y��|0{>h�
��R�r^1����������bnD����|]�	����]���]��()�����
T��e�,���o���|���l�����	C����������$�e�\4�t���5�������.nN�!/�������n�A������lz�+~]a|���F�	����*�����-���+���x���sw�"zy�I��X(�X��'������ZFL!l����,�������7R���4�-�_H%������~��_��T�/���Y��~��&�=���x-`j	�W��� �m?����F��WK.���5k	���9��=�� O(��%_:��DDt�R�3��{S����a4PP���������4�6�l��r�����<������� 3�35�����s0�b�J�i5���t����@m#�/2FB��[��3�6l"��.��� (PD������A5"�U��������t��PN�p&����EE@\�D<�z0j���1/���[�v�U�����5�<��$���R�{'�Qd�X����;�n�l�~Y��5�t��N}�#�'ZX�t��tV'��`Y������H������z5�>��M)�����V����H�,�@9�_����z_9,�����eWD�r��^\Cy=���G���q�&
��W�
m�X�4!��^�
����r��\O�#uO+x����Bl�]�F� �
����{=�9N�qw ������1~�����8�/�Of(%�����g�O�k�X�\�jwz�{�������l�F�u�^Z���o���N��:�L<�+iC^�:��e>e=A��)����>olMq�����}�g���wW�����Z6[Md"C�8Ii|j=�MW<%{T�Z��������w}r��*yOx�aH������x�=��k�>it�����
q�(�8��Z�����T��vIvu�'u
G��O
���P�h�*��!�IL*CT7o�0�����5o�����;������]�M~�����c�m�]���'0�O�;1��s�oC��J
[MV�����6mE~����x��Rq�
��r��JV�����o�x����;p�&���/����m�D����|k&\���
%�\��hr1��j������K��p����{w�b�������SC�\��n��l��5B`�{���P��t�a���u���c�*������=8`���_����u�����v'����Y�d���Crf��O�����b|��,C����"o�9�
�K
��y��
J���5H����f�����c�����������@�kq�s��yVAf �o���`��e�	��s��jK���a!`mPq=<@A���x�T88��j��7���J���}��(#zj87���:N�]���j:��08�K3a���X=����m�d.^Y5^4���2P���Gs18�G���r�T(+3���h��H1Om-7�E�=M��S�����l�<�4~����Z�|��3�\�Y�Ae�z������+~XRZ\����-Z<E�����`6�MA�P����,��t���9���,�������s���W��H�����E��Q
lA��ap,���*����rP�z�+�U�}���b��o{��g^9y�n�A��
9������O�����o�� �������!+e���������+r�Y�������@���]>����s���<b�LXdZ/�sb�GI\t}�,���" -@4��`uL���z��a�?�����c�?����b�xes`3��G�Y`X��%&�;��-�}�����%��9qv��-�������Y�%�5Ll
;h����@=�I�US�����`�$r�E�E	D�c��>��c����������MLN�2�*�#�-�o�G4J�b���o�@_����d���y����S���������6{�����}��d;`��D��#�����VX����%��\�iO��IDP �c6X��	#sk�A�E=t	kl>	���G<����LH��y�z6��%#j,�J@�RL#�Z��9��d�����p�RE�"��EP�]�DY�I3J���)�+-��zn��d&���>�z���N�|DK(9���!!��P#��16��'8$���H.�����:�*-7h���XeF7� Q�\�[��h�iN�������y ��'�'�/N�=���1Wd��`��n���|i
O���M�!����n�NAD����������Q���N�$;JK�DiK	�5�lj�$���@��$d��5s<�j����n_k���X�����u�^����Y��r)�D�&����2���]��!���#�)�H��� ���(��
�5p�?��C(%G�������A�Bf�s�X���4n\p{t�]�h�����Q���t_ob$� J�d�25l6��|����K���<�K����r�6g��Y�s�����q���$���g��!
�xw���2�s%���z;r�l�b���K����h�
JT*n��6�`��3�L{���f�
._��F[����f?
wid�����~�kj�5����B��5W
���7o�dm4�����t,l��@z~�J�l�h�O"L�I��#]D�JJd��;�u�x�����Ay~����u�7iw���.7y�I�i�-�9����C����$�6�r�+���Uz�U=�n~s�-������k��C$�{6�>�t~�.	F�#l����'���c�U=\#�>@��e��"�7R�v���^D�2Q\Q���R��bI�y�c�fV�4N���bL�H>yauWE7`f@k��Q_kH����~�f�5�{\�����.,N��;Y�onX{��f^gmO��[�x<���s���&-n���$�w7��zA��~��M����4������f�������^���=a���dTR{��KzE����H������ah������!���j���;�l�HPk��k�E��N�;�|-'���I��2�j�S�kFB~��m����l�k1���3�BtF?PyM.`��=F@�d�H�R77+��������������v��tGm�����n�v~s�+,&�F�}C�~Qfrw��w>�,G9�����*��}���}��c5������A/����:���j����_T��s�R�_!���x���|(^��`1�`��nv���Kt[�D�f��t�1�Zv�/b���Pk�*}�T����G:-���
���q	);a�}��K��7VN��"����R����G����g�{��'�CI�w�������<��Q(�\VzC�6��f�>$,���:�����$����?<�Sz��C�����L������AL�7O��, !Mad��g����j=H�/�ll��[/k��*40k
A�"S \	�u�p�	V�a4��j��AK~�%��f\5���\-���K��\���F�#^�%�7�q��������c�k��* �5@�p�HbP��El�U�fh�������-D
������=��4����� ��
�X��-��S�\B=xpt1��2��\�x�����8��J\��*\;[�Tk�����"�H������!I��A�<�oE��J�_��@t���u|��m��@��b?M�����b�a�&���iu��P�C6.��������#J���j��o�
��u��;jM�ZG�&P�3!u�H�����*�TH���j��G���2�3�@����0����j������r�O~���|������A��z�[`|����X:���<(:#�xo�G������u�-�u�EKs�	w8�=���I%�}�������� �����;��H
���4	2'�C����*�'	���\%��9��O,�6[�`���GOq���U�2	���D.�7��b.9�d�O�V�*�������M�`���q|O<�w�����.��r)���tDd��5G:�����F.I�w���4��|U�A�����y%p:�M�CV=�U9|.����E�!��@M;�rW�@�1�`>��a�s��i��$�����H����x;x��-�Zs��g/`.^�,��jdH��W%���V�PE*����0>�$�/�a��
�%��%U1��x���T�C!�C�Ue������L�N���,777s�H���`;��x�����	��tV�L�H�C�z�j��]�r� ��E�<�1&e��PV�������S��?�{�#�p:��� ���8iC�����V��79��a�zk��1�1��W���)^��+U��y�������G�e��cc��jnjYQ�3vT�@��!W)�5jU^��\�,�D����8��T�������������Lo%�Y��������JJX�~�N��M�_�r��JXnto��
��?��uQ�N������s��;NBV?R6x��:�Fm'�Adx� :�G����a��/C����P�1��!iJ�:P1���99Z������
RXB	�#���^���a�2L�Z�\�w���V�M�����s�;���������������R�(�1�o)��3���~�F3w���65U�`E�R�>����i��6&���dhq�.�q�D��t��E�����Zm����}��rm*��P?��U���,�\�{Y�_KL����V�������|0��g��8������BMf���&0�4�*K�L�u@�W`��������6��M3B�GF�7�>�c����K�����M��p�=R��
M��^�yy8`>��"x�R����R^@UZ`~H�b��[l&F|�e�d�����.����Yx��u^��b�(���2�dF���"��n��6����	1�&�r#� i>v����4�>X�?kxO*y����fN"�Z�p�:^K�x(��DC
F2�'R\�N�~-ms��EX�(�d����d�{\h�q����l�5��F��*��������������`�������u)F�
��\���>Yn����
�@��Vr6���������,�Eb�H��V��c��[Ubp��[�t�Ta��f�q�TsS��]�-&w�,�,�3�iL�%����utf�[�Q��c��*F5�,GpN>L����1����h���|tv�n�>�s�{�-|~�A����o
�z��HAg��t������/�0K��
<��W��0�}������q���ED�(rQ��x?�jt����m[����E��r��+�y�@��[��!��Pp�9;dtE�����Qx���l�����%��?6��g:��UZ�n�����E�(�rT^r�jY�p��	��(�EB��K���\UFWp;������A.l�U�{h:����9����b6T�TmMe P?�
&�5G[�E:Mo��B�?W�@�q�����
�0h��0��<J�?��n�E>����<m@^�7lP����2�\B"Z	�����rx�=��]�CW���/��
���]��t���T|Y,�)��K9+�qs� }��&�:���A�Fs+�z�&r��"W G�R1��`>�-����=Q�F�t�����s�%L����|��08�4��4���?C���m���R�m�z���O���FE����)�����bn$\���s���N�W��[�p����S1�����
�?�}�O���}�#S#���-��N	W�O��,G!�:_���H/P5�F�����eAZy�;��B��_~I"��<���[��"|�T��`��a�1���#���3��[��f.u�p���C��^���
P9*A�	ob����r?\X ���	����I���G�Y��������Q-���9��@cy����N�1'�h1t��ok�]�ii	#�Pt�Pe�%��$�|6�&����2��T�Aqa:���CN��u����@6"���
�^��^1��a��xD��kk}��D&����M��o���i�x��c+����Tm�)#��`���q�9�
^�����L��E��~��\���;���#�}���hjW���Ctf�m�J�,���ui&W9��a\<�*�u�tI�f���X��	|����<TD0U�PI	x@�e�7�L���q	��Y�j4�:o^R����f'���
OnP��-"�����O1v�b�V#x(b�S����t8X���de��1��
i�F���2{�4{�j��x������7��G��
<a��Y�=,df�~�{|p|����'��Rv�#�!�B�E����(g`b��E��n��s�*��x��h`�=(]-�J?��Y}��?���@F
qn�D�T��9F���}������}WU9�]|��4j	2�t���"AZ���zo<�Ym;GZ�q
��v�@�[��
�8�w�������� ��X@�]��.��B�D!�#(������w�����9��zRi7���F<��>�lZ4n�G���,�T��(]/ O.l�;_~�'~��K�W�U�L�+�.#��������y�M�w7
�:4�5��P��{�%CTt������|}H�w�;�1���<���R5Uj����1���>Cp����'3�(w�b��5(���cly_���`~�x��g��AM�*/H��L��
�MAm:}��{kW�w����$
�9���9<]+����y	��2'�e��|����J����S��
��rT�}Q���'�����a����%}��r��wxS��������g@l^�����\���,�������(����\:a�(edJ�iV\-��.�$�����,<K ��5kE\E#)�Y!H�L����,����B]�M�d�����I�����M�+��n�����
>Z�����n��f
~�KC6�t��4���
�  %���8�D�W`gP,�%�%7����$t��*��56Ws�e;��70�����q���h��P����������3;��Hs������O5f~�B5B���qu���9d}Z,_2������a�| �z�'^��M~h
d����-KGR������Gl����5}�d�hm�}��l6_��-&3*�E;�#[Ow�������.��
y\:f����b���v��\�<������8�M��R�)W�f@ts[����c�Z
��Og��
��%�
��;�1+Z����n��5����+P>�~=�������9�a@�N���(�g�)L�q��iM>k�6��b���x��dV��;a�\P���5d�|8�Ym�q/n���&�*��G��e�*�#h�3]��8yd/TmkuI��Rn��r6_���u��r����YT�C�������~/!qBD�� ��r�b�ao������PLj���c�Y�)��}3���3�����%�M���,��|��2�F�	C���<�����!�W~y��MG30�WVEj���%���0�|}�ho4���_�]pQ_E`��p�F���c��']���47rbC����&E�b�H��N�{���}�%����cSr�HN�T��uW�A|Jab���i����7��������L����������9=#��������Uq����i��v�>�����j���~�KJ'qQ�f v<�-;����vA��n\���������`�����v��c!���"~B4���36�?=�YM�&�������B�i��z�s3,���'���6���Uk����(t�O����:%�?o�7�hCT����������
�*�j��h���M������yTO�������l��y��w.����e��7Y��^XJSY=�D�F4��P������o�/��x�����vd��c�;��W?6%j8GP?e~+�H\�)��&�A����
�,��8�%���&��xcwf�f/������U�rk�3�
u�H+���:�
�BP*$��7�[1����?z���ZH��2�F�wU���j�YO�NaZO�zcZ�p&�(��@t�\mP}�bP]��S��hX���"�2���K�b�3FS��bd4\#�@�HS"�$mZ]��
~���j��S�������8�l;���+��z����w���
`G%P�o#m��4�#ya�����������JN��#��2������Yu�����%��1'��
�2�.�;���A��n���6�w�.��IU�A�p���r�|�T������	����J,0���b����qKL���es	.4����_���"K���>=xq�#��������������?^�����p��D�&��5�����]j�_\�w�� ���a��7�G��0����bxr�b�7<�;1��'g��|���vO��^�O��!��,�?�/�U�����+����}4?��Dx*���?�,9��4�T��@+?���3N���gX���j�����%�9�	H�������*���g������1���P!�|����B	8�������%���\3*
�>�����
"��Kz�*\��r��2�5�K�	u�K�3������O^W%�u�{�c��Oz�v�F�^�3���C�t�@�`l]������V��<W�c������vF���dC��U'���'�2����.��8;�)�����?���su��jcAi�%O���������������;���B��`�����������,-���:
b�"R�Y���b�x������\�^�n�.<�h�42�R��Q
�)o$�/�L� d5�R�H)�x�hh�=�.�r�P-��#����X�!�ZE�']�h�m�f�����?5'�\O#3��z2��A\\�wx�0I4�/`�7$����@�T��#�*!�����#��S#z�N�x�76�C3%)�;�N�ZC$�&t,���G%���;��.��SR{�@T{e�0�8L�<��vO�Tl|������ym����U#�K���N+9��^&>f+/G�F`MVn2�"�g�/��nK�Z��L�Z��2r���R�%�����0+9VV]�gUK��.�'_�������K�d;]���J��(�_�;�>�vD�9�kt�����a���M%�%��i���w%$���5
�������?n��)8F��/�)]�S��(��d�NW����>5*�%.�;2�56�+:.�N�r/���X�L��AH��������Z�7C���P���
���
�H���[GO�Mf��<4���H���)�A�C�
�L'��g��F���i��4�	;�%����c���D��P��(��I�)b��8��"D�}���^go�?x�����C�-�+X
'/�`�L�_5m.�.N�3.��P���j��&�����Wh����p��@���x�f�����
$����g�~g��{���R�e��a[�,�_���(��|�]W�9y�B������^n�����BF���	�/��=��k��K�[���u]��!m��)���qnQ�w���0���%��L�����q�>��.�}`g�l$�l�E����`������
o����E�<=���W�C-�C��5�]�s$g}I&�����O�����S�� ���+]I��nv=6E����Fm2+��=9r[��%��������1��f6���;EM;�=��z]In�#�e'r\�l"�����"j�E>��2���d���N7��oy�����K��{]gB��f�}q������5����M��D] ���L�J�v]-��Y;��e�U����,�Qo�X���[@�Ev[rH8+���Q:Vc�v'�F�;�����/2;C��g�u�����;�nN�A�Y��z�1vC�%�����[��A�:
�/"�qt"�1}��j,@�-y�������T�.�����|��U���u��6�e�V
!(�$H.����=L�"i�8*��*KH�;���H�/@�)iK������#�n�l�N}�$N,��g��}���Z}��������W�x&
�+e���:R������U����1���r�%2�ASHEM�����D��<�<,UEs��KU6��c(�DgO��G���1�<�Q���dF����t�� ���c��K_PiV�2�<6p��L��Pnj)��l5�&U_
f��&P���J	V���"/zE�����8��'/3J�����|c�o����";�++������#
`U���W��_X9�dF���}w�q<-*�8�4h����	[	+
v����"��O��j3�����~�enx��-��B%15
�������B��`D��)��e�#��A��f�g������=�D�z[_XNw�m������}0�[S��aN���:�
���v�>B��O���O��#���	�i�r���]9�a�X�*rIm���Da���X�T/���9MF��^)��	'(>�r��_0^�w\�&-?���G159�r�$	QE3��8D�C|>>����#�&��GZ�@��-g�T�cg�Y���<On��uM
M;b?V�!�	�37t���I� ���p83��|�����L�������(�D�ekr+e����>�{OT3,�������XN��p=^~ �sqH����g:d�#��0� a����g�g�������r6����-�%����+,������o���8���/����&��^U �t0�!z�	JR#��?,^s|H���r�d�a,�c��:�����TT�U)��TA����X�7avQ���X�1���>��'(��-h���xZ��bn�z^:9@�����p�
�ri��*&H6�u�(��L{������U�l@NQ�l���`:�I��oVb,��k�;D>E?"���s4TU�����SAl�ut��@�@��i>%y�� �C��B#��h����Rtx������J�n2��������F���.���������ZO�fb�	2���d��P�M��Ds�X'b��W��[Q����y�2�������M�KO�3�"o�8�B���>9e����Wz�����N����Qc���&^��#b�A���M*7��J�>���k#��	y����qbS���s	�W���X@""��Lvk���NR�J�+��|[����02����t���3�g������4E;4{18��x�2�����T��D��-����{�g��6�������Q#���	:��H�1��v�a=?ol��1���0�g�wK���Y���_�u�����	G�m5���
HW����x�����}���p���B������"��(�u��������Nu�����G)�0��,�!�a����%x�r�$�1]�lH!:H�6,y��������j(�������x�����(8�\&�?���(
s���F8�XH���q�����z��Y
�Y���FM�~^��T�SV��j�����jT���9�N0!U��{H>�5+;��������`���J	�X����$��l���b�����+)� �G�q��2$6����
��p*�
�a-�p2K��l@(BX�]��!�b�D��t��8�bf�� (|��eXH����A�x����'|�#o�t�.�p��z�:^�R��YL3�7�h��h�%�7�//���+X��u5~�mS�c�����l1���[���N�����N�N��'O���?�#P�'��������������A���I��qaT�{w����[��d�$�x:|���������/��XE�m���zH�sHZ3����Sq�0W��v��K_tN������ ?c���Y
-e�K~Y@�����cs��G����P�f����}q�a.���aV�H����%7�L�R�{B�C"��D\���������@����� ����(�����IuR_el�y�I�e�Y�*�.q}�R��/d��$��M��F0@���9dW���x�������M:�,gs���~���m���XI�5�@&>�B\��@�6T�.�W�(B���� H3��
��!;N��D�@�b�|c�<S��
��1s������������A�1�6Yt^�,/]+y����QX0���5�)�IE�7WK�N;#�������������s0zq���~b���Na�7��yPm��#l6��v��K�I���a�*���N��Q@J��G��O���>?��h-��OwL�>�h.����`��7ox���e~`�d��W������K��~�3�F�v�~@�h�MR���������S�������^�+�h;L����9���}�
���o��t|B���<m��
�w��o#�s���+&}xlz<������<��������^� ��K�4r�iv�@���{�&d�H��*+U.O�W��g
����c\�`�`���a^<��O����]�
�:�oI��������	X6DHq�@(�RMXED�Xn?+o�����w����.m���e��������Yw��5x��b��^�B�v�>�oZ��7m�W^������I�=5�hwg1.�7�B���6pk�������3:�5�f����~��c�xa�$��2Nk���7��v��&[7�r�����U`����C�R��f�j�_?�K��/O���������������l�I����y�-�����Z����d����)=�oX�����O�_?y���i����>J^N��t�������O��Q��+z����c��?����|��5{F�0^��k��z�������GTpC��tW��l
)���h1�x���g/ho�+}n���9��Q��V��A���Lfy8H��=���`�f�T=����L���<�������G�������&�~�a]���U���`�my{�����'`�(`D�l��#@���)��e-�StwG��0���5�9Y�c�Y
�X�~`~��u��������#��z������R>�����������Y���������f�|��.\i'���S����+D�6�^�W��G�O�}� �Z]�+#M]���i~Q����Hg�f������P�}�M���^���*���."o�#�^my
d���Ik���52���Y��D�����b�����/��Sm����>^�18���X���f=r�L}#�.g���9J�+&B�O�T��
P
�Qv���|��� �WK���j����y�u�lQ�)=�eRs�^H6�C.��:���p��(k��;��*����p��|51��W�h�����h�j�@m��g��Q"(J�B�0T4���O�q��j�N0�KD�����$����s@��Y�������a}.���8v������P��$U�1��O
wm����w��Cw������;�=�V�iwr��� N^fdF�(�����v4���|z�%�m�G��L>GY*�fx������d�?ZA��P�
���F>�!A�P>7pl�<���;��,Q�u�k;Z��l��`����������K`�"�?~k���h(��'����.'n�����Sc��������!�G�U�z���.��;��+a>���5��W�
o,�t�(�9�c�4
��Q�T
%�TX����D�{-�oC����N�)����>up=��z� D���Fq:_U��XbEH�K��?,,���������[*���#}��9�[X�H��k��>����/�ZC�$%�@��jqfT
���53o���� �O���bY�CC�����]�3
����*ob�2b��_a�{-�]0v1%0@�v �x>2�94}�j���*��X����c�FJ	L�W����>6+qp~v~�f�>����F����E2`/��l�?6w�����l>3�U70'b[�������cS�p��O������y���-R��T��*��2����^�c����h����+�4>d�'� ���l�iFQ�-$�L�4��cN@��?:�c������������_��O�dSR�������mL}�,c��6+������b�mGoT���f%a�R�IG<Aa�F�<���@k��B��E��E6��@�^R�6i;���C��@<�H=�.��e��r"e`{���/";6�&�iL��26��K��N.�q�Eb����7��&H���9s&��U��q�"�	,�����H�Hz�2<l
s�
 ��z���UV`"F����D�Mz;�o��:���o�an6S1����������vH$f��d�&��I�I�uM�Yin$�N�-�.���Y��[��9����&Ac��9
�S�M��}��9"����a��mi����v0�����WU�*��/r�5
��d���g��S���dw�@�B���FJ� ���XQ�7b����t|P���?8��1s�e���!Fa��%�����-!�������t"p��H;�X#�:(�$��������cP��b����:n�Y+��:���p5����IH�H����mt*G�c+��!x������Y3(�����i��e�w
l�����%���w�N��"����"~��}��8���h���������1\��<_�W�9�yR_���5C��e>5��l�cwG�!�c�9m�
�+<���������*���,l�\��AJ{	\���T�'���p.%]d�z{r,)-b=?������aG�l���E�Y1�;3�����@Ms���'���s�|�i)u��.��ZmT�%��T��
Q����K� �\��%�69c@�A��5�3=��n��U��9�{b���Fu�\����
2v� ��a �2+U<��2�����vF�rU�����O���K3e3oZ���,�[�r��f
�/����@��ho������@��XZ{��Ot�I+}"��XT�|�o��JQ����E:2��TJ����l�-�X�4���� ��Kr#�}�0��M�O?�cG����z���ks*�����	�X5P�:2V=N�g��d(G��o8��"��+� ���n���_��Y|*����fP �����s�s&��r4��H�����-l�	�3r���|u6o���H9���0�QHSGh�Bs]_d��ki���dwd�nO@
�D:>0����ob~'�`y�06krW������at8�$/8��N"*��G����1��(�sg���y����AN�/Wd����'=�g'/�GG���)2�����i��d��l��>�*2�Ia����r�K�C"�!��#L:_>[�SJ�7���3� �<�S�	�����</�������8�C���g��o}H����,\���Iw�@5qW���;'/JW�]��s�%E�7VH���M��U�\���'���5��\"��g��s
Y[��v�`hN?�&���q��/$�[?���N�Wj�"����QAv��-
�y��[�	�>�NFd��kT�	1l��p6,��
���0sov��t^#����O��;����r�ur��^���l=��V�s���Be%�dd]�K�%�VF�n���xLA~>/�����,��M�������6���� `:��I�q��%sTP��'��)X��>P���0��CD��t+
��Hr�{&{}��n�=��^/�mKZ����F���!�U�$,��
�LA�^9z���ny����l��$RK�,����� �[������]U4�5u�S��"T�e�}F,���E��D��r"����������2[�CK��c�����P�}56��z�\(���yg�+r��?"k��aY��~�\� ��"^��?fm\�]��9�-�������Z��=���t��AE
���;�:����!�t��	�o��7B��\?�9��]��f��%a�*%$zV�dek����;���	��=7�Q]���@���
{M>KB��L7hp����g�=�=��.��14��W�H����I�����|+�tCd��|}���C�;2^�����G����`
R�[���Z�F�!-���&Ka��u����
��H����2�UZ��8G��zF%�#�kAh�����H��_V��
~WE��Zo��E��?KV6\+�`��`~�
N0����h������F ���>?����i��|`���w�e��d������p�����^��e�rW�J(�9.
���J�y��a�GSIu
�:D���-������S��W///�q�xGh9��I6A1���f&i+jPO<�H�..�3��y�EK�W�4��B���u�����y�$������H������8Hp�r\D.��5M�w�����W�.St��U	,9mF0������
2�-O_�v��?;.[�$���������v
���)`8H�o�6-J��i,��+�^����	�%���7��~kz=u�\0h����@6�M���:�9
P3����d|��������0�����G(��0���on�5�G�@�E���S�Wg&D��Xj�'�f�9c����|c�r��r��p)�����R=�H���X4�����4�E�(w�b�q�3��2���1e|���uuEn-�w�T���3������{H�����1�B���d�����A�o���}>���.wI����nF�S�h4����L �[r>�{�%L�������hw
����[j5k�T�����,�s���0;
���W�9�^
�����!��������������������H�����������m��^�����&�MM�da���v�����VCpw�A5L,����'��z�N��u���O��9�[��]$+�OgW�T�/��O���!'�Tm�i�*��%���4����-+�~tl���u����S�t��-p���d����z�pkK�>;d���G��"J>��
-�r�
���>�4wvy���o�@�B��	�����B������Hu"�����
��%�f��P��k��
f�N��h��>��<��]�&�������������'�?�����Y	w����"�
��~������u���u,�.0W|.� �x�O��;��#Ti(*�2��|�gD>��t�	��H�M8OmU�{\��P
��uq(+������{��Bw+������t�N<��I��e��:����Za�U�!��1�B-�XL�+/���c��Q,"������>���]�}|����Y��0�@�2N9q��4�?8������t����������6���;�Br�����*;bx6��!��aP����
	5��:��rY]fqwY7�[��Y��M���-1�H���*��f�����?-��v%�x<C���gZ-S-HCW_x��!0���K�B>�J27�t�6����\�<�+��2Nd��_�]������MMX�fmP%�A��J60�B|����
���z�\Qg��YmD����
��@X��|��������J�����~C�L�~����yY)`��7;e�����yr|r���=<��i
@�?���G>`��4��CL�%�l�����r*6i�+X���;�����\��{	P����_����`]��A��|�>q!�o�!���X\���:<bj!��F�)�}P��M���0�\���Mc?�'����$��.��<�$�:_��P��P�
�hT�LoU�s
����-��>��V�����[/_���~%g���?� a�� +���8����1���K�!��g���1�\t�ES��=)���M�
��qN���>����S�i�	N-��{1+�^��DD�E�pKD2s'h��]�Ux����DWG���S�A�3��I�f>D��O]�� �.�wuXL�L�%��H��k����%Z�CSW�D8:��Z[�/����"
������������.q������D������Y�q�����h�	
���)	��}W�O��JB%�*n���u������y��!�u7��"�l�����������HK_
8`0�+g������b�|���$�_���5~o��0t$j/8��Yvm�3�����9E�������h�
h��$��s@��FQ���N'FN���]>�J�-4`<�.��U����W��VE��4.ehMo�A�vj��uu�XZ8��R���9���JeQ7�a���;3]��d�ezh}�m�����	��c���ch���t��V_��/����	����sp��	��V���Z<��R������bg'�z����l�B���3���VB�~c�m�4�����]��
�KQ$=�j�W�h�Hp*���u�<�.�"&�3��&��vE��	�0�wY6��C~��� WFR�|SR��	a������#b	��������H�d��x�Tt
�; �uL�K��s
��K�/'�;D��n��Buw2K6������hs�e-�WX&���K��ldw�7j1@ySkD"�A��Q@G���O��T� ������'�+�B)dh��8I�_������C�6K!����G>�k��FP
jH*�(�����I������o�����b+P6'��=�+$`~���C��\ �X��Q�b��)��+�0��f`�T;5���LLk�N���^������r6r�Q=�������|�j>�*�LT�!��{���/1���@O$>�|�[�{�5��;>�%��i��z9�����cW�$����Q�5G�b��K9���]�����aq�TC?��\��A�*��*2�A1/e�Y�U��SV'��-������a�"��
�#�4d����r�`��Y�E9���K�0Q�y�T����w�c1��/G`0���r{@j}*#��Y���Vq���$,�.3�]�\���B��^R"-{���gL.������y�� O	�����'x�86���=��l�Mr
v�#�V
��o�nQ�i�.�%�����B���^���z"�!-��n-%#H~�[3J���+��U���uFA5�5���2cr+��pA�-�����\w�~�S�6���D��9�ot���������������k�R�Dp��Rm��w�^Z$>�}�kJw�sA�aH���)7�m"bX�Y@}a9�P��-6>�	.��x
��RUu=��e����_�O^���:�4j��v�[����}�I�p6��
��0�����o�+���V��6��G�3k�l�b�WK!��ml��|v��@PIz/�X�rM�S�Y�_���N��8��gce��`������x5mop�@8�5	��,���
��de;,��&j��Y*���{����Z�-��b:P�7���H��|�aY�*f�������U>�J��!�f�T�M����.�VZ��@#�z�}	�nje�|dTd�ml�����������������'���
n��f���~=�O������Q}w[��R2�lc�6T���tA#���3��!��HR���v������n�`@���������P����Vl�����Q7323Oc^���q`��P�d�P�Y��Q`��&���Dl�!�'3FsJ�,���(�L�q^�ZN��0��u�b�D���0��������������m��a$2���L�9��/`�ijVxl�
m��~����{{I�7�7�5��6XqV�s��m�u�m����)�B�5PLX������K���m�;���g��*+2322322�������g���v�gHa���yws��W�\R��	�v�E�� ��-�f<y�E <g�D�S���-���A��^�{
Fp�4�S��l)Q7|;����n���ay+VW���1���\�T�1F3�O�/������i^1�H��M��M��V���������J����k��Q�-
H���Q��r���Z����;WCg����
����e=&$y�CfL_�}<J��1���O!h�;��O���d>��'B	�%jx-�=�Y09}����?�=��������B<I����@���7����}�
����TM1d��p�e��)�Jc������Bk��G:��ekB!�f���g,����#st����!w7N����)���2Rf&�q�zT�+���q�j5��
2�I+C`2������������(\���^�{!��je������	�_�Z��&�+�1t����5��r'�y.	~XP����+�Y,�[z����.�.sg"4��/�(F��"^�u?�]��~��@��a*�Z
�t+3F����e����wD�D���=�J6N��mD����;�bdh����l��&@��%[8|J��h�I��/�M�!���{��s��#�^�6��h"�A�Z-,����"���#"����Lw=[��s�Bm�]��;���Yo�[]�41������W�-0<,��F�$����]o�vG��XS'�?��!���<�U���:p%�����Z
��M��"h:!V8 �����s	r��{��(XL#�0��F�����Q����Q�U�5a���x9E�81�$������������C� �
����N1�e��m$��i�<�,�8)B8u�����O:�|�������8x��GRP2%��E%SV�^H�������Y�`�!��� ��x5C�sN��q����V'��#�B��!,0G���8����������KH�F���m7�5��f��m��m�U��lU*���j���M���lZ�m7�����Fj�5�~��M���������g|=���.�NZ/-x\�-������V?�6���Z��A���/e9��0���TvO@?/�#E:����G�,��h��,�H�v���,5�q�o��'�q�29>�O&�Z����(JeF�B]�~�+1�[�q�=���.\W������'�����{X������������h����-��z�����������r������7��w���b�����/NSP
5b}3������7�=��P��8gS�A:s(���{�������h�v?���/Wg�i��H#�71X�;�a�����#��b�{�����:�{�M\zi�e���k�+�4=�]v/zW��[q�������[��D�`�����6��f�!�;�	���u�(�k��������'r�����o6k�nk�Im��`��5���B��wJ�����j��d������a�[��<T�%L�v��D�Ln,�\��'�r���-��������������~��>���"7	�\RT1qF�37��0#Q�2�gdmMh����2P*9��N��G��z�.��+z���
���)��+&����M���{\���Z��7���S.�������q[�>�K��X|��h�$��4@2>�o���+���b�,�3��
������B�[�%�o�<��k��[�+���" h	d�Q�6*�����S(�8L@rB��[��8&L���HF����h	�J�z`�)C��`��+����0����WA��?j�Z��sV�z
 t+�������@���wfau�/,O��]����>B6`9��Uy������
�;���|T�T_��d����.Qk��C���������Gy?<�R��R]�E�
z��=��FS�`���V��R���V))��s�:	[�F"b.2��o
���y���z���"m��.|��\q+c��
��R{;��vw��-�����*������/��P�=x������O��.W�Kl��������
C�*;��}GK�5���_A��!s��K�G��F_���&��\zzQ�&K��m����wv����q�f�,���v\=�V���p&nu�nM����6����N�8I�1M�?(gT���[NJxY���,\��AK�������%���)��E�{sq����]%���^u��3|����|q�F�]c
�/��ni�

��Y��>B\6w�C�zWD/��
���g�o��V��f�eX��!�C�g~�V�sJ�x����Ww�������:���n�x�QKKF^I�*��f����z!4=�r�2[3��;������9o�Jn��'Bn���I�=���pQ\�F2y��>�r��Nz-�x����2/��9�ku�Fcp5�!l�:�hA��u?�K��q�*0&jW����enP���/#�r�Nt���1Y��C4y�6~Hh����7�3�<�v+ ��S��U8���/��oM�#���\1�w�
o0��n�a�#��de8�a��:KP���kT]�� l�wD�ub�\����;m	���$�.�brN�I�_]�����]_��-��������!��C���
yR��qB��A'�.!CO��n��jQ��A�"�����1uu��'l��&��&��������������*�G\�S���D�)Pw��ma����,��P��GT��P��j�g���w����Ux.uo�G>�D+���'���s�zAh�������q�����h7�'x��
GAuE4��}:��JB��.�%��,?�����kK�J�����~��;o������,��� ���J�p��R-�1��4��9��0IsG=� ��@"$	�������b�0���������~�{���)(`����r�����G�F���Tj'�v�����w��
z[7a�,�c�]�%��A�L�����*��u����^l�
!*����0.��f��L�k� �78#����NbK�������`O�*��r��
�.���nM�����^�R�M���>�`�� �/�F!��:���z�s�tE�smYL uw�o_��+�ImO���@�����m����\�[[�,��7����.���b�g�t��F���B6�|����C�����`��$�;�@���ye���&�_��2u?t�df�
M�oy�t��}0��e����V�q��LB�R����XwZ>���XN�����X.g�0�s��UF:�M�vu2���*��q��U����_6��Ig�C9=>F�R��qUL����r�������\���
����N{� ����n:���E�b����zN���
_�w0��^9�Y�?��9���y��3��.z��s�A�n��qo� ���Pj�t�yQ~���9��(;�06�n|;���o��O��f��I$)�\��`���lg����s2+R6�pZ�`aI%O`b�+����C���t7����k7+��Ic������[��$w2]���/���I0h��K���%�9�#�+I��@Ut�r1�����X��9y������j�q'��\o��@s0}���_�C7��z���Z�~v�}h@38��,h����UF�y�����l�*��F��z
�o�V�(��hK���,���cq.G��������g�[����~��1��*f������A��@H*�������!|eW"H�*Lw�pQ�������U��>:n�G��O+�����	�z�E���],��
Q�G<�{T�b[c��EJ�]��l� �-��g�����F���,I�F���3�����i�[�;24�q�r��D+F��H���v��y�T�Ik���7�$��9>iW[���@�z�Vo������,J[1�f�:zz���^���o�^�$���\M�K�H~�n4��|�D�u�>A���Q�7S�8�������P�34)�K�-��+Zw}|-�0�c>�QX@�U��_��wgCt���^	���wY����+��"��Q��(�)�����b��'F)5���d^|4�����"�*+�DE��?�!~�($���
�,�!���!|�,!U�p�bJF�3dD�����%*z���h�?RI�{��WD��-~�<�n[2��,.:�HE�u�����^8���������]rTX[i�#�p����N[I�K����r���XO������d/�_:���^���}v/�I0�8���*]3�h2'+����r�b��P�C�e]�	$3Dm
����]U��q�}Ro��:������p��eEI���P-W�0��
0��p;�x���������#����]n��g�L��}`����2�9f"�b���w��t�����p�`�WYacC!$�8�`a��8��4��L��c/\}���VV�t�'�I����81��k����/��y�� <{@js�G���q2*;�P��L�����S���T�f�G�u."��*NJN��n���v�B�p�!H1\b�D�z��R&��(���w�3���LL1�����'Y��p������B1��s��e'���%*�����i
S/�����~R���s�����J�r=���CpI &�$
�$��*#R����U_�����?���E?V�x9���",��������_,���SUI�a���%�J�����,x���v��/��0+��7�B-���w�EDs�LQ6��,#�T���(�=�w�W%#>�=F���0_a�Gxde�`�qw*�!���~�h��)k��x\v�5���P_�Y�� ����i� �����e��h�v��R-p���6B�&_�
�HIaUZ
�F�5#rqv~�z��T�'������T��De
@�J/0�.=�/;W�V��wVHJBta\[7������)�����Dy��wcf��y��Q��J�#`8�#������������Y�:��k���K[��?X<�|��u��eL=���%�(�hd[���0���cj�<��P������F��l�[���b�p��My~�9
�=�������>t��������!zt
`��B*#Q�/u�O���b5�B��Jc�?~D	?�
j����=S3��d�0���Lg����Lq�G�����Y�)$�Q��%5@����6��3>R�!�{���p2��MO���cd�O���B�&�NrM5I�uH}�h����
���@7x��	G����h^o��$��3���m�Sgf��C�G��]��Hw��rAls�1X����V��>���1�G�]�5�����;7	t���N{d1��H{
�3�p:���?
�P#�/0e����+�������'���DIXT�p��aeUT���EA�{�nxD*�'����]�OHX�`+��q�f����r!F�T6�dW>�5ca�T��H�"����a�4?Bh��"7Ar�e/�`��o��iZ��H����
��.�T�C�����\2`��*�H����)���b�-�����Q�j�N�h]�&cf���~J�KUM'j�p~P��k��,;L��YW�hL�/�c�Wc��Il�	�G�Hu,��)�������O�s����r�%z}#�n���%�����yg��]���n2��vl�J42��G��t�7�X"��1�0;YlM ��.�G��l"	Eu�6�aY��X�o��@�UcS�O�H2Y�)�d�~�� �V�\5��/Ic�(�
�*,
mc"��,f�������e�����/��������la�}X��u5�	_�'*��g�tl	�A�z0A��Etn����Gi%P�*���PF���[��7)�"��� me~�Z����(M9w�3��M��P(�Y^��B���\��+���E����W��Hhs�����i7�2�j��G�`Q4o��-5��5�\Sk���{5
��Ir��y�K�*�
S/6D����`�p��<r���R~�6�������:���K!�jz��[�n)!WK�P�1�����y���9S�vH�� +���3�R�w?�����5���������}^SW�;�|�E�7������
�[���H;�nF�,%	ct��e�������-'��PK`W(�Fp
�`��3�%��\��@�E( �>����C������U��#�!~���
[B����"��EG��Z�K��29�8Jx���up��?%A������6������0`��s%a��NBR��}�!��p��A�:Ck��t������"�S��a�8���|2e���Ne��Q��}�>x)m��Y�h���C���L��1���8*�C���r��pd0N�����=��a����e��z����
]���������aJ�lEp��(��<1{�>�i�3�,1$VwR1�d�kh�����d=pJy1����X��IP.�1�+�)��9�p��r����2����H��1o�f��:\�R��o��o�W�+���������[�B���������$n�<2���J�i�A^O�K-��� �X3����n�T�����V�X��/WB����0��X��%����p+��x���Z!s�o��/7�Z;"�v����K��|K���4:�D��;�JI`�2��	JB���eY����WK�*���'��pBR(�.�y�^�q������U��>M����M�Y���a��M��:�f\���4[y|Xt3:n�8&v<�h�z!�^�X����|-�M�$Wf�AMN$�����"I��\4-����H|��&rBa�*ta��`LvC�10A�y��r�%�$x��!��gO4������"���>�(mn�l
+Q��haL���wr���_��r��e�\c�$i����Z�x�X��t��V�|���m+�D�u�Z�e�R�A��}b���������|gp��>����<���-*���s����������V���0�H�-��>1�_�$,rK����?}��<mA�K�(u�JG�aq�i�����*(eM�\a`���f�h�|�� =6�*D��*�X������}��)�������b$�u�r0M�������?��ED^_��t�ZR`�I�����7[�����h���CNh���S�N��`�������wB�EU,:����hP� ����4��:{�i��=fy���k-��Z��l�u���k��o��@����R���w.#M+[Fw]!�� ���qi���2�L�V�BD	�ry%"Pj6�q�������d)5����[^P<$�<�]�`����jyG���Ri5N�������kR��/o�B %��l�r#�v);Vi[x���a���LIRN0B��	f
�|��q�NbQ[�����*��@TjL�Z>���.�=<��^���� )�e�Z�N�"4�>9��T2��R49����2U��]i��X��m��,o���q����=9B�
qk�GJ�3���O��w�7��&��M=�om��lZi��.��{�<�����Y|��(� )J�
~��%�m�.p$El3���	�`+��!8��% ��Dg��,�����Ou�K�M�F��[�7����}��2������xxs�V0w�c�(�V���0�1~�a��>"I7�@�b����uzEO��x��F����'~����G��I}CLq�L�TE��`��z�DW�g������bR/�R���	g[1��d	LA�_�/����~/�'�Ut��X��8�c��Y�,jwQ����;����g�\9n�ZV�B�w�� �����B��P>�E{��eq!�
2��C��k0�����16^B�AT�J<q:����t����$!��v���B��x@�����w}5�i`SP0U
g=�(�]_]eUq6`79�������Y(S���Sb�5N��$��������rT����A=�/1��VA�����:J�M���0g���G�;!�����b%���7g�w�n�
��@RBc����%��@��zF:^���Gh�z��aFF���wC���z��w��.�����v�S��%N�JHN�_ 'HY�98~������?��8���C^�#{=|��FdC��1.�1
�BO����`.t�������*�����Yk�4���J��{
�j�7�H�$��b&��@�K������6�� |���'pl����e��x�. K0@�[� �+�M�2�&h�Hgx��^l�	{c1���O��;L���[�x!�I�u<���L�Y��L�t��-�/�Y�`QN��J���x�z��Q'�V�%!s7��������>,~od�)��#�3����Z%�%��F���F��J
�$��
`�u�xt����������V��/t�x��!S�.w1��1�������lEEl��7���r�#�jU�1����R�m��I��e�QQ�P������ok�+��f�[�yF�*�.9��+�RM�KAY�y����s���\Id�:(sQ�f�����E/���[n��'[{��x������������S	����!F(H�H� k=<qX�����,��+�����9���;�5Z!�G��4Z��>�;a�uD/>��}g�F�����}���t�����/��Y��@�,c�m�*�o=�MU�9A�����I�?�u(g����Sa�f�=('�A���J����[��*���p�S*r�����@��31��1G5c�I�S����ICs�s����r�uz������bQ�;���-�������P�&s���7���{s�U�@�8\G8������b��II%�L��%^i��

�-��%E�9v�e���C/�P�Ih�N�C��*��0�$xJ�l��lz������*^�-�b�!,������w��Z����Zv���i�7�S��>mB��v�u��Z�r�A���3��*]m�k5�K��������:�4�d����8�{�|GJ=���x�����^��*Ck��4�f�	�������8�:�QE���
rV��P�D�Nu�E����H�uF������
���t�M�By9�T*���9���,h�-����T�L'dZ�`�?K��1y�(�b!%��`�eN�`S������0����z��3�h��� �)�P�1�yX���a	������w���w��R'��Z�t�`��	��0�I-#�$C3���y�T��@^^ �*3����%$aNE��o��
���.M-��|olk3-	������s
o��xb�����������iPR	�M>s��?p!/��n�.%��^z#��DU����q�v$8"����1KR��&wQ�8Tv�V�����g����+&���@��N����
���e|���_�&��W&��I��G�:V����X��|iA,;���7��l����q�����X�~y�[�/,&�V���LQS����9�V���7_(��84�s���S��*\�P���{�e���:��n�uj���h��z��j��0���`����B�+�+X>K��/�~6�ro=_�dp�x�[	b�hM���?9�Tj�v�����Q�b���RR%�p��e��X��F���s����������.q��{7�/���|Y��|�8�4�#��s����``�����U�&��Or>Y/���X��J�������j��2q�!��G#��whB;IL�<!�@���;3'E-��L7n��Z�vR�T���/�U7+A�FZ)��)��W�sv�������d
Z��x�>�4Q��Z��������n�`���a�kD�,.��L��f���E��y;��i��U�Q�~��[&�:�s���t��������?�Y�����Y�k���'t�oN�+
1]���c�����=�2�lt���7|�jj�iG�G`����"�����''��	f��06|R��+�L�0�B����� ��������B�e`�_����F����
t)E��U�a�TJ%�,
T�`t�"�p"��X���YR�9�"���_8�h���J�;���wAr��P�f� 6��C��HU��q�dY>T���(�o�_��W*$������g�w����Xf^����0Ot]�w�k�Zo���B$��Q�g`����}'��%��H�G1JaE��}�a��U�}?\��k�4%|�Y��@�4LYH%���y{^v��3�r���ma���e�5����WLK����<���B���2|s��or��f����� ��C�ckL��F���(M���h��%��i1)j��`z��|��H;���Zk��]
[M8��^��}g����d��:��k��|��:wp�bJb�[�j}}u��2G��a��d���
���q���s��k$�F�S�*�%~=:�w;C�:�,h`�|��*j51���&2�Fq���F���8<��V*Y��-��r������)a5|��.���:��:J�.b���U����G�
cQ,|q$����_�H��]N�p��B(��||H��2
<P��GF��;��`�����Y,
�
�����n��[�Rv��%T�;�������_���I{\��-�K'C�M�A�#ZY�'��
���6B�*��Yh���oT$n9��q����2�sz��#-��,�	�1[��a�y3�_����9�;�?��s���M��������w���D�oA�����#(��ao�'Y�?��/G���;�=�a�����6r=?��'�-KA��U[c��mU*
��Y?���Y�s��d U"�����#��P�C|��'?�?��H�^�����"��E���%�]��$'I�%���:9��j��i�r2y��7��=$���F�{&��}���
��a�J$4��� ��������3��x��#�k�76�Gx�4�O��b����2�!��RRO���+�G���f#p�.���piJ-���_��!N\u��y_���d5�I��G�
�-#�f��4~$� ^�`.����\�A���#i�F�':��i�y.s���17H���D��l9\��3t��B%>�:�����������(�_��z�������E�#�/���
�7Q�C�$g����]Kf��:�{Fa�v�dJ��v�$����;�@b�f�<�q�Pb��k�fK����!aW0Z�"��X������N��F8(����+�8�3<b���	����!Lo"Z
&�g�	:�����
�:�2���������L����n�P�Z�}�0z@<�	��Q�C�p@l�e�����_�}�lV4��2w�WT��D}��j�D
�.:!G�a8����D�55��*�h�]�:.�!H�i6i�o��?���������<@��B������P�`{�����%j��V���B�c~�����a�{1��h%����^�J�x1�d�0���=w�����JD�h4*��k��v��
��u��J��H��p���~���A!����D��A|�,�g$o���
��������������EXj���=���Jez4=�7O2C97��W��bh�E��/���e��"����<*n�|���
>�|��(�jO���E����%�X�F8 ������n�g����(Hwg�-ly9�������^�+�7OZ�z����P�����'�G�*��m���K�M��;t�W���%���ZL�����P�4v���ke<{�W*�KZe���4���]pj�=k���W������S���+���	�����E�a�VR�
^~��;�C"�!A�P��=�A��.zW��HU�����%��F��}h4%���)4r���?|�~��f!����A#�'s�{����{R���"��K�] �������:��B�`dm��G���dk�z�\���Mu��f�}�~����	�@|���3���12���yD'm��K�T�qpvbiU9d��Ls�wM�e��v��0� ��Ph�gq����C�
��?�al�)@��"������Zc<n�T*~��>�N������i�n
��<�(lO�S�j=P}s�V������%�&�h��w�W2��7�G�L}7!
� �=xm|��!��)9H(y/_h-os���Tj���P��H���������;��pN�v�Ym5��E)wP�Rx�jP����$���iK�m���h�5�,
QWY� �"�����	�M+�F����U�=�C^�`��Tt�qJ!e<��w[Yp������fcR�+�����#��n�A�]X~Gf�E
�X��3�)E�P���3��.+�����\?����w�S���Z�@����9:�6�2G��/�y���p*�!
w�C�Th`p
����1��:Q��1�h�F�4��c*	����|�X���O��}|�J��}` ���"���fYb�x|F�d�yI�=u0)����M��R����]��N�F�v�lN��yK3��J�zad+-pih4>��b��W,e��|T�B~#e���������~��t�X�*w/ssb�Cc�sb��0��2Z��z4i�'*%a/q�F��u2�B�a@4�sh��]�x�{J�(��+G�N~���	!�	G�w-���H�)��8>|@���M�Yh�!������aEe<S�<����(F���8T��)������{��.eJMd`��I�����f�R�Aj�H����%jX�h��~Ln����4T���k���b�KAA��Zuj ��9
��"����D���>������/��JfvG�Xy&��X|z��w����Z��I���"yO��"D���T����J
�pz��M�e'�����&^�E�Q�s��6���D�������x��P���;��Y&B0��
�w�_,�������Y��Q?����,�%%2x��Uf�����C��Ct����N-�������W|h|v��]J��#f� �m��Hr)u�������$��OP��p��H�[���<�b�P��X~Yp�f�y|���+p��h�~��o������.�:���,N�mA�4�hL�>5�����p0U��+�������QOdF���%Af��t+j���cKl=TXo=VYkG���<o���[z������0P�����>���uU4��m���u?�����i���*G����������	��L�������x�k��
�����C���iq'tXL��� M�=v��0Rd��D!�o�@W1���o����\�n���J^��������X����d�v)i��`������T���������f��)��U
���@�"���
	K:E������b��,B����w�H0�D%��
a~� @b��.PA�_���N�)m�d�M�f�1(�1�`�y�5H+��$�� #�x�b�2�)���D���%���	uf���+8juv�Vr���c��e1W#��������a���4=J8�����������{yc4�����f�A$i��AB
Zw��?_�|8��.=��R��Z�����)X73�>|�k{m��D���*Yo�d����Q��������H.��}�;������x�����T����
����r�" X�	Py�53\|"��,�P�(/Z��O0�&N�b!��	f��ED,�(%����ljt!&�Ji����j��h��a0M?��2a0E.k
��tk�K����|��	#��v��bp[a�r�Dm�����,�c��O��)yp�T,���69.�"�`���B(�K	���;�.R�U�Azs�_�w%�M����.���}Jl&{�rcc{�ua�)c�+�F�UT/N��'?��E*G�*NA.�`��y�1�O�4��Nu�5�]+j(��`��C�k(n.���,+!�PA��P��d�s��Eh�ZGd�p!~��r���zY�����g�����C�?
���.�3�Db������)Vw�my�c�<t���b��{���]�M��g�(-��t6��R����2��A5�-����������9���M_�Q�t�~%������
$-T��&CZ��������n�
[6\.=����@������a�|�=����T�������\����IA`P}�/LK���Pc�Cu^CP��(����e�C���B{�zy�
���I:�&�w���d�mH��g���J����u�4�"���#GU�D�^�^+�#|�i���F��i���H�&;�G�67����dj�����	����
v�����}�	�_�����B�!�rg�?P�?��E,.���N"GT�=�P�EE�;�N(�����=�"b�(J4~df��O/�es"���hb#sCPT���4��@�Z����������Xm�K��B� ,p���^Nyn�<����y�����{���z����O�Z�^�8��X��D��
���~�qP�<�X�������<����b�C��O�Z.������yC��W�qx�MsW�7i*��(�����[^����^�Ub����P����T����sF��rRJ�T�)�dg.U��5X��>Y�nI���J��i���.
�\oa��!���PR���y�tp-��Dg�����IS�!�X����B�H�����S'I0�B�����BEP}����wR��:K����,Fs_��A�;'B^YI�^i�2;�,�{���8��w��������>d�+/dNw?�;FbyVM��c�q��F"]�i��p5�&��b-�w��L�|?�	��0r�����c�E���/���8�3r���3z�}����,���&�t�b��_�X"��TeI���T5(��<}e���4
b��hU0�v!�����K�j�0���U�p`�X_�������6Ds4�!c�\�u���9[��7���$�L��L6]����v2A�G���$��I,�E����sG��K1���9��e�����k��Q�	������&���^C�X��ZB�[S��J�nIS���mcaV3T��P:���-It�Fcj�d2D�[��;���=�������b����`�T���Q�0��Cv��k�J�����2�O�2h�pm��������5����Jc!nnR���l��2��%54}���J�I��&��D
��Ea�>��t=��	x����"�P��[/�^���j�=�j�J�^��~��e%������(���m�v��
�0^�BH|e�A�?��	�����u�5xb�?�7���I��k��F�K�<q��y�/0[;f!�(�������d%xAv1;�>��Y��6
�:9q+��7�������VV����J�C5�S�����Q��k~�o#d���O������{��#H*zvs����f�<����__
�W����a(�)ID���u3����X�"��c0K����\�8���M����p9����d�H���0&t~���9�:��^\�D=�����x���X����G����#��������YwP����m=@iD�l���~�������,�&�{���c���j��������+��~�?>e�8���0E�c5|��:���]��pfI��{�������|�D6��${��%�������n�������v��}��(�Nj�Nl
T�4F
���h�+����$~4
��l�T\���(#����o��X�T�K�����P����R�c��|���:v.��b��H�a�(��Cp6�*;��f�B?�.x�C��X?9���	���D���o8,���B�>PV^���B��I'i=RWj�����^}\�3|3}v������&o��	r�����(� |w�2X�8<��X,>�e�|�<��$$@-����#WYvd?U�y��'��_��R&t!c� �����X�Z��"��+�Lg�8+����FmG�\��UK�eq�#��A��&�|����X�����'�$++�k�Z�H�Qg���v��Ggo�]���^�>��e�
�u,�������.�sx�nV�k�����GE���%6��[Go:o��E�.y������W��w5���9}��E:v!~�������6��oo������Q���[�d�$�w��l*��������"a��m�~����1��1��u{wV~��U(Pee%�@a��
Vp.\
UTK4<��e���F�v^�]�PP9j��)w�� ^�����)��D�o����0Zbj��E����\�����	���Qr7!������� ����|�K��s�����`>�f����y������i1c7K�Np����Z
�����UPa���9�o�����\��g��cd�/�o�U�n��N��
�0@�1	P;z�R"�6�k^S�E��O�F)�JlzAz��`����.��q���m����d.��<-�r�|��-Z�S�v�[�_�Y������9��^�#=�{'�9�U*���qk�7'������Q&�j���/�������T������G�
'��I���&2;9����M��������'�6�i� KI\#67F���r%W����������=�4��
�<o��!����Nh���CKX�2�I����hr$�������u�����;������x�nc��D�<����=����p�?wt_�����\���������C��.�34
�g8��{"�f��$r!Y�/�s�@���C��M�O�%\��v�M���j��8i���i��I��F!�2����Gb}��������q8�9��kW�E����);��s�W���!:A���=�%�Q\���$���)�����o���9M#3 ��$i��������R���
�`G`������]��).�&����c�p��#�u$z�>?���2e�M�������.7g�cf �����?��c����� }>`�@G��j��T��{�������
���g��r��.����
���ef��:�)6�c����_Rr�/�k�*�v;7u����.��<�V�3q�M��w6a���kg��&��
���~5�wYD_�"h�2m�M����N������\��YF�:���'7��>�d��'�	~��	^�	��]�7�9�����/"
�4M����z�zO��e]�
+�W��x���WV}�*U�|d��RU�f9�������b��X��f���{�;#~Sw��\�^�+ �ewE=Q=�'�����@�"5R���.�����J>����
G����:�aM���U,v�7����������~O�\�_��,|�����-gi��2�@.g�����^u�=���9����/)��K!��]�|����{g���o��^rI��R�7��RX�T��`S���Z����TQ���f�J~�Fu���m���z��J�_�wVo�+������jO~u�Z�\�7R��+���F������J���������a~|3UjC�_�� ��N����|Wuk�:�/S�6H��T����W�d�-_
�w��� �����j��Z�6����3���2��6mM�^�Z�x����x�����;M&�x����^���}�G:QQ��AwA����E�G9��Y0>�bX���" +�[;�E%������;���"�&00]d
��P���3z8��):��+	����puj�wd5b�u��z�b�8���O�,�*��@�4v$?u<"�+>9@	���`8+�#����pF	���RJ0���{'��~�	����o!��2@"&��Xg�-�����q��,vo	��S�.�71�D�
��M"��'���O���8/��d>t���� �����$$�����3��p/vG�.��O��v��nE�#�W"	!��Z
�Dz��D�y&��	����n��);��

�h�� ���^N��R�X�[D_0�rd��1$	�S�@��pyU9�}��p��E��]�z�+�n�Y?9jy�����M���Uh�������4��-?\�����R7aD�ulP�Z��:x2�J��#W.7p�n``�
s���`F��U��g����:$`�.`���@�o�(�o)t!��������y>p�&���+��<�B�3�bc
^���;Q�R���n�DO�R%�*
sr�Z.��A��)���U'RPY��a���U����}��j~d�����e�($V�0:�XC�V8H��@7e�Sd�"��d=y��;UT�X
�J�r���'	�1$�lXb��%2�A��0"��LR�(s�<��~#��H��N�*	{��G�w]^�^�6�5���c�W��XbO����G��8���og����>�Q6�����b�����{Nx�p�P�qw:��2�K�e���# �^	����B�H���l��A=�w����Ar'��p�a�0��@�
6�#`rI�'r�ct�	������&���%z�Y�J��n����i�Z�x��O�������i���f]H�%B���-*��iarI��?	P����T�
?O��j� q���_�����x�
=z�yY(�%�hT�}�E/�>C��0�� 9��f$�����H�H�(��� ����"J����^8�v����M�4l��cq��U0{!xc�Y�������A�Fvb}@c�a���;�/;W��!���3
����~��{.����#j���d���u^����Yc��{ih�X��5�N�I�i0�����5M���L��k��|��0����$s+��XU��>��*��=��DC����� ��;A%q���
�8��j�@wU"��������q��+�_C�_�%����P�����s�P���g�	=�?U�;�V��:ujL��I�AZ��RzO_���$QK�Tl�F�.��������������H������y���tR�.�����q0���C�������Q�Y}r�|��j�lx���5�O�ZQf.�v���VT��R:��Z9��BH����0���6$�]�i��m��
H
�U�b�ZY�A3
��e�%� (d�
4�-��4w��T���o:�	�l�	���~����	_���e�_��E"D���
8���`� �Q@C�T�z��(2t-o�+������0�S�TB�����[���U�B*�:j�������}R��[��C����04�+���w_�{#u���P��2Cry���m�bJ���������-�	����E�y���n�������l�>$22H#��	��*��!,J�����B�"��������!`a�"IK/�"9��	hI��*)	'��">��
z����@a<�@��������6�
`��i��zP�*��5pa��*������v��4U�90���N�c+�Ro�ryO�]�U9�'V������&u�E(?j/���.mZ������]���������b� � ���!J��La>Cg����=��i�Gr�1@�i���S."�G�o%�^V7o2����PoG���YL�9X�#a����"&��I_S5@8|�dT�9�j��Y�{�
t|(Z�:#�u�X����������AS��U�k�0�������]x�� �P�4�Bj��}��	���>��O���>��tf�TnwS�T!��o_��b���R�E����N�X"H�*e
#HV1����x������Z.�e�����l�<�pAEyY���B��j���l	3�Z�j��C��;\C���O�c-l�4c
�"v��}��s�$fV��Q3��i<�
�9��'����h����Y���114;���~
��$?:�,v���+�RX\G����:/_�V=��j%��Z������]_
��Z)��@��\��Q�j��X���_H@�q��j�JTM���O��_���6�d6�:
C�O�:�����|s-�0z���^u.�#�����B��1�?��+\���;}Ckr�X�q���^T�QI�4�������
�����e�w
��W�.�%�|�f�4������e�3/V��;��#��l)�C����RF{X���!����f[�MVU|#
�s`s�~�h�d���R�
�`�PhqIR��f����k��H�?��"|P+�w��R����F�T+�M�$��G�P�
��5xJ��XD�Y�R����
;���V��u�*�d�A|�>Hl������N��i�h
�=r
��:��R����</�R%^N�7/�]@��s1g��NMJ�e�����J#��\i�4?5?��0:���L�4�q*e���}�WJbbn��o�w'��c@rs��-�=(����zC�h�v��I�8��yT�y7�U�����R�I^
V<��,B��Z-�[\���D�I�K�]�8�X?���3�p;��5���SWo*��x������l-����$GN���D�M�U����+?�cR�{Z�Zd�!%��p�HY*�1�^r�0^� �n;�����U/'E�2���J��R;[�m5��k�Q����������)
�T�"�)�nb��ab#L�5�l��rA����2�j`�q~"{C�eT�h�$_��y.����X/J�n���
�v��s�dt������!e���c2<L��~Z�g��f���&�AD�y���Y>O��������Mp����mAt��2�w��8y(��l�������^j&"���3�(�,dfr��Ld��#Sp>'T�������~���l���'�|�.����	i)�="���<!���~��_-%�k�.������y��EL���e�X��^���\\��y�QZXc\V9��D�����l7B=f���L�E���9����"_���N�q�`�? �8�����.�����u����[�����9X���Iox�L��`���4�*}���/��N�,x��}�r
1�.XK�x���%�;���/.���$��>�	���L
C�k���Br��`��G?�XR|"(��U3	�b������q��FoH��S Eh>������1!�NZ/=�#�
(SN|����D��k����I�����[5��9�F������BnJ��X��fF�����q����.�N��9����B��?���X��
A�G�Q����8��9HS��D��6�d4�3�K��FP�I�N�8���X"�� H�^��8)Q`<r���U����/��
��S��>�����$\�3��	
����GlL8���}W���-4���Y5����������*��x� ����$�>�F`�Q$�X\�~d�����@z��8[{T���y�g����Z�7�F���5b�
� j�yg�����M1[�n��AG�7U������U�@4W�g��8'����4��P�^>
?��9���k��)M|����5�0l,�1�@��Nl]1j#�H��T��8�S�T����e�93�9���wzy��)��)C������m��.�Z��9�4�*_��V��u	���
���>�yR�m� �2�z�\�)�/yI_s�_hs���a�o�T;&i�d����eo�=���u�d*�y���N"��"��~�	��`��m^���q�jP")�D.z*�'{d3S��I�)8�l|�-��f��:�dD�@���y+B��Im����WHM���M��N1�_���y��mB��1f���`�������Px�q1S�o�lb�����2D�Pr�Tr�\����r�-}�0�$��8�<�0����$��T����8/�e���~�u���������E��{5
�r��;�����X�h,�Exl���,�W��8=��"��n���u.��Qp[tK`�(�Kv����
�	�����{v!���g�{W��������%�����E����?�7"����|Im,�VI���t�J�^�iV�L��Al8|A+f�y��l& �����������z���%J��GRh�������1��?rg��X�S��������-���k���|�)�Zi����o�IV���bz �\���|�o���q���"q��G�A�Ro'h:qt��Q�H��r����]U��
���Au��^�1��I,���I-�Ue��z^=�O���3~��������uD�H�5x:o1�g�
�.S��\";�?�ve���;t����Q��|�m����-�@��wr��]3X��/���������h���T�,U������"�����
����r��������:Ui�I���
��+��La�9��.��F��Z�{���"����@�"�CL�%hL]��>�
�_��.��ed.| ;REf�5I���l)Lkr�J� �R1�\e!���e����^����/�(K8�����.��-Rp*+^�������/H���<��tS��$eb�w�A�C��"�Ms_/'t�y���/���=G���U�|���u���$s}0�gU��D��%0�!��rA�����eH���Y\NP���K�����)�NS��Ry(qX7��6�"g����Pnngu����`5��v|&��N;F��M��H��n�����l����%#tZ��r���g�l����-��=����9��>]L~�B�Lf��Hj��<RmQu���&����������J��x\UQ�������mT���;�(=.zWP&X��h/�zS���������#�5;����/�����5w�ia���Q'\f�����6j��q����Y��r���.�R�iP
[1���&��1:����A���a$�9������1
��c���e�&��P�*��T?W�9e�{��N����������R�s=���y�@�FU:���:�����������7q����K+���=�z��
pc���\�i1�}�Z��}���|Z�O1�)�
�cj�����D�r*�"��)eQ��*[32�m�"����R��l3�
�|���e�T��te1�Yr��N�_�rb	��J���{�cs�������{�b�u��E&�9Rg�����t�<�d���A�e������?Jw��O^��x.��LF~��W��7:a�!{��-�	H���?E����
�_���5���0��D����,0�to��b�;MYo=�?�Z�[j����,An����N�J.�xR���i�����f�X�����7�:$?��EJ�h=��7�8�~W��d�z��F������0t�Z���*7\���b7��2(��@2���"�}���2�U�������e���n
XR��"@�7+�{Mp�gt��dZqRu��tW�N,���b�EE�+#��
�'	7@&!�y�9:�]��?�XN7��� ���]����N�p�%���+�������xV�,J��+��Y��]�4"����~0����U�W�6e����	g����'�
S�nP1��"��&}QI�.��X�A�h������1��n�L�@d���.a2�������rc����*�Da��LfX���������6��8R�W����D��F"��P{*�t��Q^�6m\��|���Pa��t����#�������i�+��cj*�n]k����
|*�\���O��,����l_� )'�5C�3�t��*�U �81�o�I��
{�0�����?��bw��~I�&��~��{%>�U�t�r��?���x���+�%��#I��������:u�_q����I�A�x-t >T	��>9O��%�X��j�����3�8���A
�p��J����J]����\nx���otz��
���?d^!Y�Qw����l,��My�,K�_/��B�� ���3:� P�W_�+�.�����9������w�m�9������H��mt}��y���n�E���H�!fg����h|~��\���T������������b��M2�����L	#��Ue�l�+��T�|i�ak)�S)��/�x����S����!g��U��1RJvbX��n���6i_��M^-�$���v0J�Vp���A�$6�8�<�i��'~63m���b'�`if��}b��K�d�kyYb<��O������llvi�1Xr���qL$���z�u�{��u��8�8d�3��&�����>46�|6q�wo�f�jt�k�@`��������
��n�*Q��GN����pB�w5��]�^o���c'\�y)t�]�&\!]M�I0V!2�x��H��e�^������}��m[�w;�I��|���M�#5�~C�i�a��&�H\�/�x�[�����gYo����������w���
����Xj(,%��[gW/I;�T����~�0zF��o����Y��~�F
�|�B�V�Vno�Q95"���x�&�C������?��O3�Q,�"fk�w����?�"e�_!<k$���S'���D�E4|T� �'O	|�j��������+�&FAIm��s�h�w��Ig!���[]��?���w���l}'�`��� 7�lx��~����jTD����euQ�x�����U�D�,OIY7��z���{�g�h�������}�x�����������5�����n��U�]����q�59�k����"�6��}q��j�3�T2z�MYu���!�{���E�&NQ��|%�>"�<�QJ�r(�CT��7�I5Ph>1a�&M��wm$��7vjh�A7����)Y�kA�"p�1Y ���E�>���X�#�	M�0���p8�L�����Oc��/C-�����t����+A���i	]Q������
3���i��j�i"���;d>)��8/vm��D�����t��:K��L,����K����$������V�L/m�*Vv������!T���E
8-'G1���Z���{-�f;�y�1��h�����br�Xc~}~��u��,�L�(��F����[t�x�4bSBF������J�O��N5q�4g��b�f(�{N��`Ut�����8AI,�����a�����0�nE��z8?���j0��������EaT|,h�g�2u?��������~����W�b��l������1�.��Q��a�������w��c!3>I���4���r�ax�FQ
�����gKRwz��*e~�Y�2�v]e�It
4�T`� 5�Y�A���,r�.K��� i�R�'�
U9���i����-����_]�2�j\��E�rkD`l�s���4�I����g�������##|�	R&BR�A����C����Ce�$���\���(��B�+����83:���u?V }�aL�&aBwc6�{d��/~O��&��4m��	VX�,I.�'��?���4�\����)������{��*F#�:o��7�(�2��Q�d>|l
8H��9��Q���G���U�����Z���9Rf� ���-1Y���B���o���l;����&����Q-�w.T�1���!H�L�&A�k�;�O�lHC�	)$4��=�������2�.��xd�'�*K����[@�����l��-�/H�.G`�SH:����pff3�>��#z(���������t>������ivO?l��y����y��bb��;~�Fij�i��{u^
%I���
%�X�ZF�����������H=�F�p!Q��W��9��q��b�cJv��*��t�C�T�*
_X,��f��mg�X(����g�Mt����i��g�^��O��������y���Vh�2��3��#O(t@�!�
C���NncR�/��EBd���J3O�8�R�������`=���L��J~�/�2X�������$��<�P���T���������4�1\������t~��5Zb�d�Ax���#�`^�������5.f���	����2�{Jz�����Y�P%-�/42"�Dl��o�T��:���J\F���3pa�e�~A	F!����O��!�% �_7\�.��c@����E'e�����a)��o)�=�)��@bD�W����B_Y
��2��]��9��t���Rad�D(������hg��A�&������\f,A����(?���x��2�I��*��!B�oP���IM�'�{1�sgy���L�H�����b�@P�K����S�5�{g�S:m�\4���p�����L�'
u)oz��e8�[<�4oj� &H88*��rf2"!T
q���TQz:s�y����R�Cf3D�$E1�?�
��8vJ_����� (���q�N��C�ox�SB��6��a�=�p��������
�5���Wj����|`2�Yu
Cr��Z9��,�e#���Blx��.������tJ������jq$?~#>SF�Hk	�h�����T��]����d�������7KFv.���$����!f{ D��M���D��xQ"�>���00r�x���|'�d��v#|%-����TnX�QGL��SL*Ux->��y�S�OBf�]O��5���a�?e�pZ�M��4�.O�����
�����w��h�'����p���+����d1�q�6O�8�����
�`����]�}��	����o��%v5)���Vs��=�aQ�b�����BUI$
E�&%�Q���������_|��Y�Df#���-�K�
�� 5dX��gw���=-�,o�[JQL����Gnt�U�=2$����Ny3����8n����hM*����~�9��)$���~����0cjG���2Ge��v�F�F���F����;�=�%����m����i�Rh|����7j���h�������t��Q��m�����fW��`����;�_�<h_��J��S�W�P�;@7�U\� :��;=�"�w�0����_�T�O���\��gIx��#�<i��X�Y�y�g��
���d�)��P!`��c!:���*J����f�[���,���`2`�/�e��C����py����s���LU����� �A���hz�ps�w�:}��G�h6~��������3�"�I������>J������m3r��
��+z�3`����������Q�������Q����n�7e�q����1vl�Y��!���~`U��������
j���#�6gx�{t�����$�+Y(��^�u������HJx�H�������p�1��IK��<I*M���CM���t����bp�P�|�F���S�D�EL9g��x��2U������BsuS���d��"D�&�B��h�T�K���'��~a����������m�?��rJ�4?��N6���-x��MgE��(o�q)����Ij��d��.���
s����z"�����6&CH�^lb����n,��_�QWR�e���pn�]O]p���;��g�B��4k���Q�kW*~�;�7k���sX�X���(�'�6����d&7]2������,�C��W����
�gU:�U��g��-a�8��B��,�S,�zYr�<YoY�s�����x%lo}�3#�����n||���$���2���� \��0	����8S���a�Bd�o����������v�M�-K�\��������F��l��Z�:9:�I���B�J�8��4��0���gtAI�?��O)i�d��o^�{=�����w�t���A4EC�4D�A���sp)�jR�M�R�WQ���?S��������������C���pV��k!%�#�r=������&��Ks!Ek.���S����X�Y�?L�����1��G��_�T�~�1n�ns'�P�6��*�bQo	���LB:��:��������1�b�7C`.=���	����F�5O��n&�vW����l�/�mMP��;}r������y��l���e�u�����������\A����s�Cv}Y�������l���J������9�r"�D����v$&���AT��<	&���6��jML���Q�=>9n�O�,J�I�U
w{A���>���p�t`�X������������q,����O1�&6D��X��b	�lt{@J�M�<�8����~��.�=�si�-�>�F�%��kuuuUu]O���P����_���F������I
>&A=��a<�f=���R�l���C���@��� ����K��zKz��+��D��x�2L����E�&�2C`5��+���V�^Ts�898��>a�	SV����x�L$��H��Z1�V���]Tg�������W8kQ�����;9-�>��2�0��2���b2��5J ���,ecY0�Gtq�i���T�}��D�l��q�NQ]��c{7���4���Tu�;�oN��O<�������2���+�����z�����3kiF���!>���4��Am�(��+��g��'^@�;��a�9,�
�Q�4���_��;%4'}	������2����D�{�=���Ko(R���a@��C��L��	aR�R�0I�;o�-`�O����E�u���~9=��N������������hY\��L)?�,U�~����I��~�>����
��z��+:.������-��:(��s�s�o�D)>;����/;��>�9[������E�s�A��u����3BE/�"���HjO�LR^���<���N}���[���)r���q�����]�uI(���8;�����4�cM�5�����L�Q���������[oVG ��n��5����{�f�8+��XrI��?�(":/��)P\d���~g�F�_X�~�u��>���\���s���V�u|�>��������tK}-t0D��4,$2A��2�����c�e����G ���r�Wj�O�*����2�HE��o���_k��U@+W
o���m�/�Y�V����nJ�,����X�t_K��"�N=aM�Y9������R9cK@���?p�1����� ��P�%��H����s<���=�	�����@���7�d�|Z�J�j�A���3�!�|>e �Yf�R��q��!W]{d?>b{��$��xS��c���� ���s:����D��p��5X9�pq�FP�Q�
Y4��7���;�_�-1���A���f�1���EGi$}/��	j.R0��LPS�N��]1c��d3���������(D���	sYL)���8�,|��7��%�qy��,�1�ZG��_,'��7�0�'��p����0<���<����\z��*9�
�L�s����Q
��U^Ye�)����;�J(���Q��l���~�l�Q�9��O��G������w���ER������'YEjp�#�e.�%�}x�����8��0s��
Y���JC�����M�mz)m��_qK~�Z(�����gG��6 ����|��8���d��n���uztG����	0-�\�
��|��e�}a)p�>y���!l����:=;m�Z�������4
�a�m�U�q��t��re��JB'��J���V��C��9J���Y���������s�w1wI����L�A�BU>�:�2*a.:����%���+�m��e�U����,\�W���X\	����#?�TV���(^�P?mpzg�FB�3��'A��i7��j�������'��h�o�c@S���}�Q�������7o@�3)������A^��D�I�b�������Cp��A�v�WDw�"�s��p�(�w�p�-�Mz!��)�������G�!�}�.��������#T���!H�-g���a�k��ffZd�Cj�����F��/�+�{x�����gQ�����r�FZ1�
����0��	�t����R�1��g��v��$����i�u��x6��n�xY�����M���0x#$���21�pR$W>��z��j2
�\����F�����1.��f�W��rm0Z�=zq�SK��eR���v��fY���(C��% Y������[i.+B��g���S�|�9��z�����i�'����0�4b�z��x���?��F�-�3�{�K�6$��#������i��"���[��H��<���-������D�J������T(��5w\v�acT�`|X�YD�oJ��t����% r^�.z������Y�s�*�����)>j]^���:��<AE�g����������NO#_��V��F_����T�x1	<?��O���GO�k�&glSJ��Y��#�u�F��Zs������Z�M%I�\����[C�����6+�/��F��Wp���s�\{�'���^���������z���/�g'	����������$��A�oz���/N����~�/\�{��)���������������s�u6t�����AE��VP��Hd-u���q�_��/������VH3E�c3[	i�`+#�lE������ ?%��zHGQ�(���<�����e��-&�����`T��P�����-��t�����
�`A(oB��;� mD=~�w�T(5
��������A�d3@cKO��b%���H&�?U�y*8���d����H����n�����\-�+�[�W���Tq�b�T�V��S|����5Zi�P�
����'����&?/1S�T�V��Z��{mX�<�T���R�Q�����8t�^����P���[w�����S����f�:��j>���|��������p���o�|���S��z�s��s:� ��q��XyZ,9�E8�w{����X~��z��8|
�����"Z��V�=�#�rw�EG����v��p`�)����l�p.g���51��p9�����9�~�*	��5��-�E�-S����\�r��i��_�������o�(mt�'����
��S�����R6��F���
�����2
��v��0��'�w�.�~��\g?�~��am�����zhar�6�k`w�B!RR�f�c� �Z�
�%V�/R����ki�o��[(�5q�G�K����P�+5K�a�gc��3T�����C��0F
2��]`L��v9?�./���L_�Oi-q�?��d<���
�{h��b7E��m�A�;iZU�����$\��4G^-�A"���\{O��R�a{�/�:�+��7�Ct�����o>�����m`<
�o�7��(����,�����3��8�K?XL��^��s	�L�B[^�6�5oTq��8�2v���XAdi��[o���������v0��1�������^�ia�|���sZ.u������+T$�{�Pr�\�<������5�+$=2O�\pH�|�7������]��O�
u��W0��������������p���x�y��=/>~�s�>�Y��t
U�, ���C������
���m1�,�A�
�����>2#.G��_o[/�%��KW����ha(BE�LL�o�Kn��@�L7�QR�[�����e�2(?�E_��#���h�92�#Q�j3
���tV�JK��;��w���h�\"���drO��#E.0�'�7\L����� ���1k�U�H�5, *�`�}�=R��P�m�B���������n������#�����;������Mf���w���M�9
�t�s���>@51+�a����`}#Bbj�.3������CO"�a;Y��V)bd������}4�����|}u]��
d
��Ji<��H@��.(o�=7�����rk�~�4�t3�O�_�.N�+"[�$��t�
\�^������p�o�s������8?�A�2Yg��fp����9�<>����r�c���P���^�q���u~j_�{N��|�?���<^����� <��tG��x����q���
�����b #0�n������)4)�[���yv������������[��S����#�N3��.G���O�3,A"uLdv�4$|�6����.���!������(����$��(�r���I�t}�!J�-N�^0Q�( ��nw���&<����K�%@��1�B�Y�f2�LFY�T6��[��XYg�y't��Y"�*������	HT��f6)�^vR�	��9}��W1i���J8�u�9�M������R����N��K�������/�����F.�a�TZ�� mR�U���rw!����F��,�m�s0.�������]k��\���>��\��Eh��PME�����E�6R��wJ��0�H/R��-i�hX���Z��$���u5�L^�iz2
F6��,�|d�`��,���M3�P<'����n�9Q*����?���g�NB�8��DLk�����?�����_^�r:�d��J����i�	o�����9���o���Bor��x}�:i?v�[��.1����}1A�3�������
���
?�D�~��{z�k���,V�x%~/��djP�q+����Fs\���_I.~�N���,��[G����;v.��T��x������O/:/���(����M�+��X��/���[L"7h�1��<9�h�PW��4�����t!�o	$���$x�;�
6y��g����tvvN~y���Y�.M|�\�C��)�������8i��0a
p�i`:X_���O(�����0��(�s��=���=]K&n���#��~����i�?�AA�e�'
�~��C����${�(�pBg����GLg�~�����Iz�zq���x�����>ou1O��N"4?J3v�s��Y��y��$!L=�g�����?���rHP��h�:|�8�3�������[���2TKX�f�G�V�9�T�����L�v����e���,o���5�%>�qN?bk�R}ol\k�� �=0��x
4�Z��g�����R�L�wN_��r3I�vA�
v�y7��V�UO,��3_��@����?TQ�wR�;�#N;���,����&u�*U����Iw��Q-�KAA�l[-�����{}S}���6�AQ�hZv��"�n1m����s.�?�T��_]���u4w*7P�"���H8�wX�����������w1�@������G9�h�����Hj�q��H����hn9S�s��0�&����(0��Rh�������1z�����4��n�����uz }R����)�F�"a�X
p����>9��
�2������$�U��(l	f��?�u�2j�n��PFI�q$��l8��1������@Mu��v�EBFp�������VN�n���k�x�)"oJez�>=J��;�N�3�G*��K���",��.���R�D�:����H������!?�����W������"�b	8��B>�cdG�f,�83�����
jK���"%�3��3<��Y��PT�q�0xr���������:!�SW3�/j�#�b�5v�V���9xEy�0�]7��E���Q
�E�T
���oL�13�����|��$�^��t?�CgT�CtC��,�|_���/�3��"�U��x�-_\\�,w���3�,��-:���������4gR
_�a��rig��IAr����g�l��f#2�~Wl��{����Z���\���PiQ.��a�T1���~��.�m���r�<T�f4�e�3����S.e�}�GO�W���Qc�%U�4S���	�^�M!)�E]���x���A��N��T*E����Zd�T�t��j1zF�X\l2����wz��u|�	��k�q���S$oy c�!j���Dql+���(|J.7�M(A38S5MX���8�����d���s2;!�C%�"u�w�46��Y�f��|�lM�H6<q��o�Fs�2g���omd��\q��T�I��0������ay9�H��	y�����oX�/�������
��[�`!�L`|��$�a{Q��v�1H��v:B���t�
ZS���dB�:�����,��mFT�E���%U����������WL���&�*��c��hC�.�8wNa�H��	(Y�6�3����ey�����K����M�D�E#��y�$�1��?��%cQ��`:_��������H�=�@�m�c�R���������XY���>�����X�Gk����7�
$t����C'�Pn���M����������t�#*��$�f�
����	���
S�t�[H�?]��(�SN�E�����{-�j&vvP��Xiz��c�4�E����w�g�������R)�<�R�|�d�T �����9DT�t�����5)l�H�A
{����
��\:���,�H���*-�3��)�\�s�d8�|��!��_y����;"�zB�BR!�J�$b�'���;��P��x�y�����W��}�s�A9�8 �|"0����%$����i��w�HU�0=�u��S���6w�G��
���r��?sD�����EP����	�z���9��9/���\#���G������?2��Z�G�|:*�z#�}l�w��M��)7��%H��d�v�����V�i�I�Q����f!q��1�6�\�n�U��x9�p���(��8��_��5����LQf���;

q���KK������k���#���+�f���\��TA�a���J��1ls~+W������}����#u�Q�E<��$a+�sD3,)`>du�J- ��)k�ih}9�������	K��d$�~pb��Q|mX�Phy@�L��r�0Q�O�8�����C�4	|<8$cD�x�6��g�w��^�
��m#z�R�2��{�;]�V�t,�)2t�oX������Ba����[(����}�����N����m���� a:��W����w5����Z�Q��n#��y�rV���<���������7���z9��]������������*c�TU*���Z�]�Q{p6��Fy0h������_���J��h���#�OoBKg�n00HF��]���]�S�\aF�mI�8�W�~��Y�U���=<���>��HY�a����$���
o���
���S����fa�� w /�|������:�s�m!H?}������upM_�o��G�:����xJ���a�SN�IQ8������v6z�`�f���n�F�)��'���&f�3q"����-�n����f�Q��\�.E]�����{7<��t2D}$����*z��q=��R'��r��?c�0X���^b(�"�<����M���x����j��>y�F98o������Y����\������l����r�����r��u<�=|��f?�������?o���pE���]�#�O���%f������0$�w��\�����T_���>���4�qVeY.���	>���w2C�����'��c:4��Y"��p���
�f�R�Bd�}�R�����^��j�eP�z�����M�CZG��������Mp��b�[�A�+^eX-�^����b�R�'������!��0�q�R��J��d���/��(����%�C�@��RW�L����t�I������SZqv��LX�?�m��	�$9��R��45����f*N���a���-$9�4�����wY$m����p�'��o2�B���[�R�3���h�����&3Z�	���_b
�m
���CZgC��i���	S	_��P�F�����9���Nd���p
D�=������#��wYiz����CA����?OYw��K{����pb�eUGUU�C���k�w��7�%���&���!��
���A6@N\^N0��l6H���>q��3��>��:���A���T���BG�M���
h��8�=a`tXI+�6
�b��n� />���$�l�����#YWl!n0�{x�H���`2z�0��@|L%|��wr<����D���<��"�J����$�Rfq<V���-W=8Q�q�~u����V��)�	�8�T�P4�������ZXG'g��2mownn>}�"��`��'���h-`0b`��}�"������'5�
�uo\
M�4���Z1}����.^���s��j����l21b������d:Y��x���m���<��LQ��^�����������=�9�0��"�z��9x����&d1:gi�-�Vz�z�@���(���0��EXN;G�(&�����&1J1�lL�c��C ��0���P"�`
QNY����Q�s��0^N0Y����1/w�����Xp��Q<���p�l�u�f�4[��yXI�l��\p����w�Q.���x���2/D��1R~'��(I�|@}|(RNtF;b���&s��{oz�x0��r��"2
����tf�bO�`�l�0�l�f�W��9
l��EP��Q����+o������\-'�e-�q��W	�p.�]wC����y�K)����Z��$x���V	M�.`G����3�k+8N5/����U|S�����c��1q[x/��e�T�:���aH��H#~�Nzd��3TVq[�9�����P�NF�����RF�
h���!!X0 O���e������$��Y�2��o�ka�e��LS�bJ��l�q���HJi$�x����~���{C�r'�^#��v�Z�9iuN�/��?�^���z
�1�HOjW���a��X�lD��MR��<�;��2JQUL��Z�����>��W1�#�*B.Nv���0k�MS�/A��D�@�#���&�h����3G�0���:�i�������	3/�����j�v`V$�?���J��"�rYM4"�%��y����&��/Fhn)�fAoD���{E��,��5J����J�>�� ��X!�)�����a+"������t���~	�����f�q<T�RX=�k�w��*=F��*U�}�HiE���`�|<rk\�D�q��&��S�x�w%2�s��SSl��P_`tra����uQ!Fx9��?��
�k)��i,}�$��Bu����q�Z����B�H�1�YH��E���>�2;�HY)�L�D�";2(��q�4�B���f�w3�o�$�3f��`G�0s�7nH2������C&�6������z!���*{s���y��`��%&@�E�kg1X18�:'������*�'��Z��y��)6�#-;�e��?����X ���Q(��@�I#���a�t���Y��BaV]�"ht�H�W��lkvK����8��i{\-*��h��:�A���EgL�����Y��J��A��'p"Kc���gG�q8���4��[92-����i����BN�W��1�:LT�t�v;�v[�vK�K��� ��
`D}����
�mb��������9�$ �f1�
�D����)����O�b��:y���2�V;���a�|�:���=U�)�4%���`�1T-�n?E����}��a�D�!�V���
��<���L�����WS�4[xa5]����p�*
F�����-��n(�kx�/�P7rLI��'�F�"�)7�V�{�����Jy���B�����������v4�uj��n�%�>�i�yK ���a;;1�H�q�D�����^�`�' �����UX���O|�(OS��
���9����=��$v���Q�y����	r0R<l����Rl�93f��0�b�O&A����rq�������r8��?.�
��p<h��Wi�����O)/C�uD��W���f0�:�"L���7��9oK���V���^����N�4�����-���0�
��}���J�J�+S~g��1����<���!������m7y�E)����q�9�mFr�m��.��uj��	�C�6��6�pm���?um�^�zm�@��NF�W������)�\�8��
,P�����`,��/��;_��-u����
�(�u8Z�	��r����������-Lbov�a�g������p�}v(\/����Zn��>��	^$������ #n���R%�3��_����M�au�O����&^8���E��O�q��@yS��(���u/W(�.2qKg4��}�����@���s��wq�m�j���|
�9�X�y�%���`:%<�#/����!PD�������6r�m��������&T�f��(q�R�S�������a�j�\HP����=�?1�@�\�#��X�4������b4����99��@>�BL�C3��p*�`�"]�#�>���?����R���b�:	��('�@��V�xE/Ca�W�d�F6��R��'%D�g���V�����U��k���4�f���
�c����=�I�������C}D���l	b9�O)��c6�Y}p��{�}����j����C;�<�(yQ4������b�(����n��r������V���?��5���F�1�U������<h��J�2���/����*�����K�%3:!Z:!Z��zT�;�D�l����G]HQ�
	4e2-��'K�����Y���,c&|t��]MJ�H�F���;���?�e��k^�P5���������>��eH\�{q�'X ^����V^�Q�*��3c''�:CY&����p<F�jq�q�5JdX��jdG���}�6!���y��������gd�l�A������������[q[i�/k��`k�9���kp�o�����Ro%�vC�q�O19F�Q�:��R��VM"��`�6R���q,�DvG�%������h�Z�#1��Tr$�WaW�M�%F`��`;��GYUMaC���\�m><<;j��z�/_v;���S����-cv:X�&���I�P�	jV���'s��eUH/W�a��������g��*���P�e�?��;G��V�u��hw���E
W����n����������x&7���P���y1E<3�T�F�r����	�k�
���L[���1����B^��'@��+�C1����P�4g�1����46�A��I���{�:����:�T�M�����	~��t�����-��/�uC�cX����c�N�'����*��_�v��);  �m"D�=���X��`z���Co9�(A
U1���}E^��������wr���mF-���������>�
���/`���n+��s�������)T'��*<�^��������'���mv���G�+���I8��0����OY�n��W�R��I��"�$m8v�'��8r��kM�
!��M��������OG�1���U�����P�����&�2����q��@����oT�Y�*���?���p$������x;�}oAe���� Q�Bt�6)���F_O����kad '<qXfGv�OY=)�����>� �36f���i��i9�8I'G�C��:��9a- �w����y;nHb�}�D� ���zBC2�P4���=b������`��U9Ci���cAv3icS@���c6���u���9��tM�I���b��(�"Y
(� ��������x��Zy�Hl�a{��y7#&�8?�u���,'��bG�<L�G���!��Z�:0�/�y�jP�$r�1<�q�T�D�x�!����],|�����<�?_���. ���P�UP"Z������za���J����2����l������7��@d����e^<��AFX���hr2[��7n��p6�*��>�*N��5��:Dr���^�f]%�+B��Q�y$��a�C'��s��,��
���b����>=���q$f��M�����I����Nuc�~�6�|"�������������"$d~r��5���������}��Y���M�
A����������Z�����?������������4�T����5�r�+6�Uw������k_H�W'��f"�tL�����������-�x�M�q�oC�%�9���@�A#S���;a)��������V��5\���h�&QZq�xL���3X�|�I=)\���XI���R8�
�A"��������]�����#"a��f�Ua��(w�Z,.O�^�hK���X��}	��k����^�O��VH��A���7�z�\�{n�P��*�J��'[&���1�zLb*I���d�����N��o �*�E���8�����x��P�������(�^\-�7�_�����v�~�����5��3q/
6���p��5�n�8��S
wl��Bi%�b�y%�<A��)�2��������'[�>C�7��3���R�R-�+���@ 9oN���%i^��{��b�a� ��82����P{X�u~�9<;}XY.��#��%tB�������`s�B�	���//���E�4a9���'a7���D�����q�U�y�������^�O����������wv�=l�����%����/|�-4�yJ|W��+�s�f"-d�YQ7ZXf���;��R�-��mN��c{ M>u���
�������,N���|�?���H~���7����������m�
��5R�(X�/������.0	�y����YcX�����L��S���4-W��c�(���w�S����j��b�}G�r�c>ZC�|N�8���6�S���0��y�(iP����j{�q�L�+�t��x	��%�v
���M,����&LR �?��K�{~��`���<�s��Bf"'��iv��J�<�d{��I#k�L=�f
V���A�-��'���{��������6�t@k���p�\�O�q;�v_����[h��r�&g���K��Z5����)o�!'�1|��[��s��rcg/v��]{�W���� �g�M�e��������&���Z��FL�Pk���^c��~,�tG�=fE�{������L0g��O�+�YyKYhy&bJ*����xd�"�4�����U;��iN��}��D�ls�.�y74c��_OZo�_a���
G��ZX����Lh�6F��1�Y�$�~t��������/}f���i��o&�>�w��N���U�E����%t�G�5T�`G@K��8��2�%T�yi8{��A��F�~-Q�*�o��f�-�xt��Oy��`��yG������
t���T��"����f�m��l�U���n����_b�O�90���s$��
o����U�mv� ��)��t~����]\�6|��h��[��I5��k�M���u��P���4a���5w����|"����
������z�2@���!/�9IQ��Tu�:���a��ta(m�R,�+�1"�&��F~�u���$�l�?,��L���?)cNA�T�������������m8\�X�@��P�
�N�1r2�������)B~��QK����b��Bh��4��h  RB(���<&��vNq���v�10�>F�����Dk�+�����fk�S=W^���Y'pJ��J�B���	��dk��Drd���s� ���\�E�@�� ��6E��F�H��B1D�����WAn7n�v�--�3u��C_8�������)����F�9�(����1�H�-nk�nOKP)3��bI�������D5
a�g0�F��/���f��p�Ec�&���
��K�w�u���	�D"��{��	c���l�`��(�7���������V�
f�6���������&�ib#b�)���+f��<�sj��bN�mDe���0��>��:]���9��?��&
�.���d.:H>'��H�n-Sn�I���$����L����1X��y�����s������#�,���	�y�d�����"�A�����)����p8$����w�^����gK�_v�������L^�� ������������M���))�����*�D\,��#N"yi+�-E�c
@�������qX�����p����@��%��1��:�.��*�����"���.�b9T���+'F����"�������s���,(�����$�<L!��ia�S��k���XZ���Q��d����6��yFR�e)�rD�t����f2��"��������B\3�xr�nap2��dF(@	TcF�������a�;��i8Y8�uH+�R��0����Z��c��YV�~�M�R���5�u���"���q�%�7����+r����"��H��'q_�(;�dH;-�)-*�:��mV>��;I����-�[nl)R����VO%�U`�7�0���
aY�gr��\�$�������F���;���<WW7
��	�A�p?IA��F��Q�S�I���\-�-��N)[��&������D^�"����#1j�g!,��s��(���
�D�@�A`�(�.����3&�Z��u~pE���0PcG�Y6�4l�6:�����+��:v������$�_�
�ny������a��p��9G�����UJ�W"X��E(2������OZ&�p'� &�{9?�C(���8���[6%0��b�y�Nv��T�2�\��8����������z�@i�����*B(���_��I�%��
(�oa�xV�N�,�a�	S

&�g`�� q��o��G�������Wa|'u����.s��c����P{�^EfW�OS�d����P�b�H��xH��J<�(p���sV|�S�%����`;�&��3�s��Q�jH���0gR+W�l(&�x���/�)H9�-��S����|��LZx����1���	�rs���)RX��-�r|��va�b
T[(�
M�7\�a���V���#�>k�
�qA���D��$I?��X)Q5���C��	��VV�WQ��`G��w��p����3M��T��t����W��d��u����o���/�F��.��#�?�F�c�z��CK�T�/Rm)fQf��0���-)31���DJ�Z�����
%]x�'�����@N�Y�<��!`( ��- ���K���tR6g���tfB3t&p������~�!��Zu�WW��������W%�Rg?�����`tby>aT���m�9q�_�m����������<�'.6H����BG��HuT��7&�����#=������(w=�J*Q�!�u�'��%�6�n�R����MV��]H���@�4W��Y�@�M�~�Qw�l�c�$`�j��}�,�P����d��jT��3$n��U�|��	������:�.2������?�;g�N�rN�6��&���S�yb��h�R�p@��w�����(�B��b]��I��J�'y����-��_���OQ.tk0"����X�FK:E]������R�'7%�X��x���������2
�"��$���%�'�)�^!�����hX,A�d2���������+�A�NvD�M����ac| n2����4�";t��DO�����1]��KwOs��/G+�\:�R+$�*r��n�;���[q:������)��p��]�I�X���7��=!����;D((�E�re����8\�	K?O���}BM���%�=��h�pc1Ed�a���p�3��a�����S�+����N5�j�\�XY})U��4�
	��6���>��L����?����3��w�����5�k'���f�:�������j��MK��KW��P��$�0h�8/���#�RLs����6HI���:EQ#I�P���7i<6
�O���������!��T��!�����
��}����{M.E��.A�Z�(bb@��:c�faB��9��b{{��h0�����//�K�����sV\��'be�H�/}9P�}�N9���<���&(����Y;A�ZN'���q�?���W���H"Yc��j%��Rz��3s:����16��b�-a����H�|E�Q�,/�P
��Q�A@"�S����J.��;�x�[(0 �n&[��	��a�R(��,ogv��z��Z�5����
]�b>sa>U],fN2�d����
�|��_�+8I��n�����i�pi;�1��9��.���?O�����:d3l��/�p�I2 ���*c����@fBH��2V����
 �
|��teP��'x]����%@$hl��M6�ki_E�\��r���������:1�,��P�L)o���.`��i�#����7�,��Oo��C���F��&�"�g��It�E���i�3�I��'x���\��B����Nk��������o2���sf)�=BqS�G�C�n���-'�P���Wx��
E��c��ga��UP����'�$
>d�x��������D����d�p��B��a��7#Z��#{���$��3G�!>L4d�7^'�B���Wsf��
O����T'��#��`�EqdQ����b�&,+�1Kb�\U��;��X����=X��=b�{�>�B�G5w�<)��1�;p�qm`+�!��j$I� �W�[����C�,���Zg`�!��P��n� �����-b��jC��c���3t'4*�N����|h�������F_>�I�U�q��
k<IF���S-VL�0���*Fj��"��Q\�[Dzi_[��k�a��vSc6���;=|�Q}R�\"����I��g����B���h��e�}+E,��B�j�*fjmL'�/9�����|xa�i:��V��l���o��"C�.M����_���\�k
�)���C�CG�$�4Y��P!���+#�jjt�h<�Z2o���bR���P9�[I(�)��l��J�?�Uq^{�Gd�D�f39�DR��Ph��!5�����2����wg9y�M�v���Rv������U�&�14M���t!���y��U���tJ�X0q���U"�6�Z.M�5�ej�~�>yg'���D�,Q�Tb��M�B��h"��?Rb5�KD��~"�P����l�xK�����j*G��D��(��$:#���j[�c����M?���}9RJ���j���j�(����$���]�"yl�E��<h�����4>0���4�S�@J�j3m�q���S�k�p�7���lk����i�x���#���jL�/��k	���tN�>�t����M����LoQh�����$A �Pq�DW���T��z��xsk�?��e��#�����b�(rS�����q�w}�1��e��o�z��E��u��������U��/�d.*1.@��v%`�:��0g���3��*jHsgB�9�(����i,������U:j��]�e����Va�R���I�VT*����x<��4H4(�G�.Q3|�����=6�a���]�i�X��_(c�4����}��x��	��c^��|RVm���^Gl��!��*N�
f���A�y���J�
��8�����:�;yo(U�{�a����l���l�;�����)��ud_o�%��pr_��	pe�*��n�k���<�D2������O��)%�@����>bog����
/u���T1�Bw-[�jS�U�)�u�n�vWc�Y�;)����x���>��#mv�7��]�������������3X�P8�0L,3%A���(�1�#�h#t~��)��N"�	;�?s$�i�Q��S�QX�W���[�lFl�3�vy$��uF�wI��"3�<Z�H�
KI�\��:�L���}��-7�b���
Wsd�8���l��6�H�?�K�@��E��6K(��%]�s�onB���L�C_d�XD��VYdk��i������y�W���l���1j���y�:��F��q��9{��"�b��'��@�RU�l�hs��_�E6�p��xClx��"��V���Z=�;2#}�������!lAgI�����^<�
t�]9�
�/}?c|�H�s*��7y ���N�H���h�R�����&�T��CQ�/��M[�g�:�o�H�c�����\�����M	y��'���"���B����;h�!��/}�����[�s:��J��������:{�8�����?�|��0"'`2Y�	��(���B\;uRKA�S��S@3��25�S�c�-6���$�4������y���9���a�2����3�?mH2Tn�(�����I(�;V��~l�ziY'��e\��v����;v$=5�i�HH����	>H4)|i�Z�l�	)Th�>�I�����O�S2��L\Ku��l�$�oR�\�]������Z���hP.eu%fI
5*f��QN�p{��i7�/I��*�����T��XM���;�#��C<��7,���`�[W�qe�>#1M���Z�6D���g	E4�����w���Z��?L$W�}���)Rb�}"l_C0l3&�d���������4���Vm<R�+ED-a�[^
s�]����F����9�h1�vU�#���)����k��9��
�"����.1B������\f�z|��c��=1�������<�S
���B��Rn)�)���@���!�Z������X�N�y�)���e~a�k��,k�V��F����������Erx�_��\�a���^_���x��4I~�{;x)���&8P*�B�{WS��$�J�M2��9��YuZY�t)����/���O���U`����Q^�@.j?a>�����q��e���?��q�txc:7�8�Z2��,'�C�'���#��f�7-��O��p3AI�U\0��\e(����a����n�JZ�g{��QM^���co=]�J��x�����%���x	����|)o}����F�)�M�./��;[s��)��wM�^82�"L��F_4S[6�V�%7�29<�X��`R�����lD�A�����]�X��i9��9��S
�BB\�$yyW��OK�-��%�F}E�e����&�hz<��!��'{A��3�P���@5�����c�z|�������7k���N��������E�56����>C��1��r�`ib��m�7-��7:\�@��P~W�Y$�����%�[��XBCL@]#��l4�"K�J�����-�)]<���^P������g���N#�7y^�;E����s��]�{=z��R����;}��_v��{mN����JIz�%��2�F��+u�R�>�����V�m��i_���������!&4���D�1�,�k����w�i�&����>O�N�����
DG�R��U7�����~a��(W�dh�I����EN(�s7^�6���TH2N�����2��������f��I�����9��y�X�
��2�I�z<������Y�Z/���[�>��u<+���Z�3�{,	��S����f����'d�1C�p��:��Y�|,[����^�	L���Q GF�H>
}����� ��sHV��ag'���Z�0&�����8���a>�-�?�| Wq�����J��������Kg�m�[�D���/.����S���5�����\.K�H��b�V�+����C�_�Wv���^l+�q�Yi�.,��Q���R�>(U���������������@����#m6}L�Af��<Q�lq��%�%p*�L�d�0��D����:�NE�J�&�Z��p�Svh����~����X\A�P�q��v���r��4rlFL�0��^j��O0;q���6=���v��W3�:%g?���Wo�*RB�B�+�����U�+�U�W�?�'��	�D�K��w+��M������q�DZ\m�F��Q���C��HK��.��T,?�����'�VHv���l��6�����7u)R��h�h=�y<�q�w�g,�u�^��'Ym3�d�7���ZZ���l{�D�8������ju7�9�L�3���~��1ksuh�����L����^�Tf�u�D��J�f&J�g@��>�['��8o�D�~�����>���}?��
��mj���o��I���&:����
!k0���c��
�a����j���<:�M�J)��W3J�/�����K�]gW&��k$�����9���0�%\����������+��	T���<]B��H��<LK/���"n*���GiN*)�,�p�v�����D�����.
jn�2��F����?h�����7������O�V~S>q�������hp�����j0
C��H�z����\�_U%X����p����@M�U���A$��%����j��r��no�`%����EQ������6�B�Eo��N�	"7�ZR�Y��W��h�EZ�Q�B��r�#��w����g��V�Gq�,���n��RZ�-����W}-��/.��4��+(��F��9��:����3�o��[f�����S#3�@��|y�M�@���^�uOZ��B:aC����QBU������}��W�r�	�'.�9-4�1��R��������������P�z��H�g����2�����P������soeN����t����_�2�G��W�^�P*��p��F��48�V*���C���u���:��g"K����p@����/Kl&-�6�m�6E��7�I��oZ�d�3�������+�R�kT�������A��1������� +��B9�����������6�q*���z����1�����������N/�L�c��N�Qa�����t!��KO[��������/*�����J��
2�i������epOaz4��p���"���(��p����k���~�V����,�\��'p�"a7_R.�'����,�#�\������b9_���3o!����	n��c�L���s%��s^p���1�����42���x����O�&�D�v�|�I�X��M�����������S
��ou_��q��^�L���
J�j_���Y�HX{�1`)����{�������~u�/lk��E<z
�|��qg����I"k���y,9�x��F!���peA
M6r���%�����8coH2a�����O����.EXZ@���/������v������!�����������MF���p�B4
�Kp�G������������%N*2��e��V�r��x�b���'F�@f�Uzy:���������i�'\��CVr��c��"G�@�VA�*0,�>�3(���.Id���D$N
�0��q�����5�2[�M����~Om`GL�rP�

�Ly��N����?\�|�;w'��������f��r��V�q�A�z�n���	%��i�����f��`��G�<%���H\�)=%fP#[�z�-��o6y����-J��������m�B8�p{�I~��e��?>;���{���E��[Y���{��&f$h��n��>�`��f�|��:i�������x�����[�������=)���~2"��
e8`#k���mi1(�����<$�C��������2�)��2.+�}���l���D�����7�|@'�) ��/0S�V��d�w�Sh��M�^�K�2�&�>z�����.)���=e�U��a�/a������9+�������,G��r�E��o`r��;�G�W�I= |����
|�n��/Z/����	�������Z��"���p?�%N�Ug� �J���b�������v�1�,��Z�|��MgZ,��A��^�r�n�����Q%f�r���z,�<��x�x�x�h1FE �V(H �'l������|1���Lh(|f�d�o��"w����(�n1,����B������;|�D�/��� �Co%Ti�xzy�
w�a�/T���s%!��M�f��U�����7�y$t�'���2z9U)���0e�N�"����Xfb������2����2��N"��f�}��p��5���{��0�����D7�B6��RU\)rB��z:�`��N�8�����3�����HN&�R���1*�kb��$+�+cS������:�����{�*��z-���Gr��1���
o$��f�<���}����D�
.)��QmK�3xu�}��H���ad���j����#k��$6O1������dD�6�o�M'8E�if��xt��gT�!.�$X�-�+8��Zs�u���%F=h5�>z���0|9�$�D
9;#C;���d$�]���g�W���sP�\�fGBf�0�1g"D@4t������F��]���4	&������&�n
W�m�Dw�l����~��N#�jE��S��������Wx�����d~h�����G��R�������C����
��:���~�����R�OP���O:-���� �O�l�J�Z�9&
YMq&�_�Q���F����f��Y%1����:(�������"�[�O0X>_~��������#��#�����[L�T������C�R���z�(��k�����9>�YRS�RAj!)��7��q�����
���.�vB�(��9��J�����u��*��BPd�n���1�Gmh���9i?uZ������
�%d�J��+�TEmZ
���J�*��/(Q������!�������[81m8K�VN�Q�Q!RdJ����Y����r�.�8��r��s(k��d`�H_k=��2IJ[�w=P��P���Hu�9g��F�7�����ORs��ha(�.�M�I_�	OG��_����R�a]�&?cW�"�k1�����<�,�|4� ����^?��i��X]/0�c��x� 	����Q(@��W�>���h��/���v���4?������bY4�u�=�:I�.�QP�fB��e$�hn<U�������Q��;��l.f��`�#PY�6�$Dz��
yB�T8�K�������[i��� ;����@���wD��;���}z�������Sv��f�f&��(l<p`D�JP�I^D��Io�v|b�r�����c�H�A!��u�8���n������BeS|3�3R�aS����(g��`a����� �\�8 h���R�K��4�/H��'�]!�� �l'��|����y��s�a�AY�p�w������~�P;��'�|����u
I��Mduf�l��V#����s��U����!]�����>��"�$�ZV2S��s�?�Z`�r�h$����E��?<���95K���6�{�y����#9x�(���`��M9@�C��v���2J��+���"I��f~�RA	��K����Y��$�.
��c�l���� �Bqtt����U45������1����'c��-w��1�:<�B9BQ"��������q�[����
J�%c����Soi�S�d�A6+�T"[��Q���+i�����lG�YD8(����n��8"c��8����s��	�C��������@u��W������_l���N�<�U�f��l�FB<����a�_h'�[|!�Lq���0�z���lt�)<�	QJ^��f����`��-�^q�����F�0�K��pT����[�����8Y��d��K��a�g���2��R}��p�!��f��N�n�����y�9������U����F�Z"��0U�k'U�����R~���H^�pY�
��m�����>��gF@��
�0�T?<`�L��Q��V�/'W,tmM��O{/���K�J�X�
���V�W��JV��c	OcX����[��gC�K������������r������.�����q1���C���p�<5�,�C��S����Fpx����/�[�`��|9>���G`��[�<���o�����oR5��p@9O-��I4�;���C�]�:��~p
@�H/O�����4�:�����q��{���:���q:�����wq��f��0�1�)Z�1L��\���_)�^�d��q��8;F�]�'����2O��c��!�%rB���]v�hw�:q/����x��_�������C��0�2V(��`8�-Y�f����HF��!�������3���T��VD������a`/}���zm�W��B�T�*eoPvS���N�	e��%`7�m)��m��bk�2c`/���x�t]��,j~�\v�nq_��v�5J/�kTi�����^2F]�]���2gp�Nf|y1����d(������+��d�\�'����vV��
�'b)�o��"�io�F���t[�'�W��`�N��{�
�Wb��� y$���BF
9�E����}�l����=�o�v����oS��'��� :6����p1_�����G[n�F17n2�e���1�9w���B��M���E,�{teA#��#�=��HY������8�����Pe�Y�m6k�R�m��0����\�#-����|���R�3�(B�H�x9�P���->9����|9�xFPD;E�{���9���������y�������"2����
�~~i�C��%��>�������� ��(��������b1!|d�-+�W����/5����C��I����o&yL�����]~L��FBac����Z�7�"�%�2n�M=�T�S0��H������
���Ppn��8e?�f�'���
�$a����������e@k ��~�g�a�@CC��S�����nr�������R���h�M%�Y8�V���Q��7����V>�Z��I�H��Ked,�C�l�`��Tw�=wC��C�w��(�������;�V�=����h��,Vc��`���q7��H�A�'��cXd���
��.��)���9"����SUe88��E����{�$��z�+|���E��	��8���j���^�)F�Z�t�+[0a+�X��&�u�a����g����A�K(�P��{4��������AH@a����C��>M������{����osP��
o-2]�\/���{�-;��``�I����9�x��k�9�NoCSGJ�A��
MX�%�S���Z��n$�xOo�I�%Ck�z<�U���B�9�}��m�����)�pn��������qFn�q���s���%��=;���o4�t��j�������=4��VEco2]���upd�84���]�<��w+���_-)�"XU����Q���!�2���%�.z&������qzv�9l?�q��be�����&��	%};Y,h��v��-���$�n��R�Xx���/���J�=<;�<9�|.�����8��^���������N���yk����r���9�4�jw?�:���,R�5����Y�A�Q���Holg�M_��:���c���3tH�N_]�W#�����v��������)�
���:����v������
c	 �c�X���*0B� �n�T�Jo+�Hc��q�����K}t�5�r���!M��w�2�D�������!���cFe���+�6�;�����Fqs�uO^x�$�G��7_aq$����[����+'J*Ph����_���[�E~~�>�,�q:=h#������M;
�:��L��@����8?��6I0"���V-Uh�����s)��	oj�[�NN:_�����%wB���Qs���!?8�*���������	�zT=�K��_@:��"�a���8q�_����C]��k&-��>���������q�p�1
�J_�>���g;��Fvv�����
q�y�Hc��%F��b�����@*����4��P��0��Kf+7>�9�s���F���)}>��!���r�����`Y�,����/��G�uob��pj�{� '�V�"
F��`�e�����P82������I���H�
7_e~0�����u����H���2��G��.�?z<��}���)2�/�����[��{�SX��x�����,d�c�<������vi�i���S���szDh~C/n�$�a9g��O���@"�����[|�-)_~s��MY*=JD�|���`��[������$��i|�s%�(l�-}�'��32����'�E��{G����b���7�<�H��=����>�|����@VTG�C�w"���������e9���f-�|�S?:����&&G�|��pC/'���>���I�n��ao}iy��vW��T ���'����L��p�y��kw�����]���]�>tR�����/�<�����b����W�I��[X��)n� ��i��u�2��r�/���h����b{i��T���
�\����T��Y�����m���L��Z�,�,�+��Y�Y��T�
�i��l[�r��l�6�r�2��
o+e�*U��*�%����Z��Y�[^���fk6���,S�-}����mH��m�F�2�F��g���M�<��e@n��>�n�}K0��J�%sK�v��u�
��J���
���Y�RmZ��U,��[���[���p-j�-}6-H
�6��R�2��[J�J�� u�d9WJe�<Ke���*�.U-�g����Z��l������O(5,x[jV��W.Z��lc?���Y.�,}�-H]�X��r�6�j�2����u�Q���r��d���+�����,�
���
�+�Q�����e=+uR]L_�J��'T������%[�6~�Z�L�Z��Z���Z����,Y�iY��kA������-�Y�X��V�l�Z���ju��5,������M^��,����e�B��l��n!���o�Mo����j�-H
���g�r�5��#�aC�f�r,7m�"p���kV,H��Z�=���5,,O�����5�6�-��[���v�o[�����[l�3����.0�H�����k]���f-�H���-�����$ W�#�[��n�"(��E/�_�H���tz��e@�J����k�H
RP����-��[���,H
�Iz�p*[^6�U<RW�t>��o��t��lB���~��-���>keR�����[��n����r��M��^Ng?�z%����jY�z����b:��6,����X��������n��~~�M�z6+�nZ� n�|B�XL��R�d{Y�5k4�>����}��t��\��Xr-KV��N}Y*��I�TJG�R�Bo�b:R�J���P*[�R����(KP���K�f:�(U,�g�R��g��N�J6A��6}*��o��t2^����!-K�8}@�J�qlB�AW�5���R�r���(}*�Z��^�[��R�hA����(5,�b�QOgyJ@5-/K�y6-Z�i�WJ�f:R������&�--,l����M�x�K��Yv-H]r�>��EP,���"I�\LGj `���\��P��E�P���U���V,�b��H1�@��������2t�> �~�\s�������\��@K�U��E�P�W��\��>}_�Q� u��~�A��t�U@e�}-/���G�hQXU��m�XMG�J���~T\���d��U�z:RWJ�t��+�\��,��J��� a�S)[�
p5�����N��m_�����J�r�R��l/��fm�b��Z�R�X�P�[�W/Z_�\-T�5�b7�4iXE�	-���\�T�%�vhV-�i�����yV���t���IM���g$��fK��j�r�]-�����T�U�A�Z��CU���V���U�RO?��U�}H�H]������6�$��W�5��i^u��mZ�m��Z�n���,_(�X�(4
�>��oDdf~I��l�6�lI@TeFfF���Q��r[E"/�
D���F���<J��D�%�$"=�-�L-�b�yX��l�3"N_�zX�]K�EL�	"-�m�Y��K���}(&���B��3����A��:-��a�h#�>m�-���mY�����&j�`�\�Mfgb����^����8M,��Y�5IzZ�:K����C��	v�$6��}
p�211B�%��8&j�=�~$6C��
vu%�����!V?�0��6�,��	��P�a��(��$N,�����$��G�dX~��fY�4�D�d�#�,�"M;������H��f�a�R�z�z��������Pd�.Yj���A��$%oZBOSQ�d����C1�C�����O��'�3�4����<P�0{c�#�%��f�����1'-��z��b�������Y�cI��:#%%��@�BK�)mL��M��b����1Vy2��g�D�q���2V�2�M�gf[����b��r�D�!.�n	A����Uub�& ���`C��)4I����	�f�NP���{��	����ZEF��$ �7�\�><}ifg�Mi�-�D���@��1����e�����E�	�*�[/�����`�B�~����D@�y��b�q�)Mm���K�~���;F����<K|3�~p8�+�����Y�������P~0��I�&jNZ�K����CO=?
l@l�V���#�P,��L-�GF8F�z����e����l��&��r�B�o��#&0���L�8����P�����&���_t1{b|��	=%`��6H�����P�����w��t��)tD^XD����C�o9J
�0�*Z�W���I��Xq	#z�����:��?�-�"S���)�G"`jY�,��h��P��&����+���qlZ��(����Hm�}�8���&�(��Q�
E������0��6�4��zQ�3v��'������z��c�I����X�2�0c�P���Y��tG	t�x�������e�)=%`��:�p|-�bB&/
�#L�	m|8}�o���%��,$5��>C����������K�p�Pb��������?&��?D+m1���tK"��\�l�YJ}zJ�O�9<}ih1���di��a0�dB�&0��0i��$	��CI-�b�Exf.�oB@���1��<z�e��``��59O��Pb��E�^
����� �-���&F(�����YA��.�L�X���@�,
�
0�DM�a����P��0�*��F��
S�8-�!�Mp�)H�����xb��P$ Va}��B ;�1��|���f}L�>;�P�0�}�e��Uc�P�� �5�P�CO���1Q3O��Zo}���$ .fC20�r���o	=�� �D��8���P��(�.�������<����0q�)ga��-�e~�bC��t����
��C��6 vX������u�|AbC����� ��l�B���>�$Q�CO	h!j*�C	=l(�_	�gh)fC��I]������\���q>/�>�S������
�z��e8>����KF*^�����~�cC����,�l|.����x����MF(��Q�
E����&�p1?�U�����!V�1K�i�p�����!6	h��bK1��Wq���H����|n���# �P2l(��k�o��K�1��>��8��OBu��&���OS�U��R���^O>K�YBO���1Qs��3���H�	�?i/X���$J��[�G�����Y��b(�)�'�������b~K&F(�p��O�u�zR>�0B���E��
��X~f1�������Uf!�,������>	��}�k)fp�Z��
p�q�i��1$���p�������`�-q1��%-O_��W�����$���p�������99n�R����@�#��g�'!p��l
F(��Wc�zB�e�|%�]\�&�9�-�e���I����X����%�-�l��LHv`#��I3�5�,����O��L������ �#�p�){��\�����12��C	-�l�b6z8��&����ZL�a����PDa��+���b9S���=%���F�#�<l(���MH���l���7a��8�4�b��$��g�bC1�,�CA��b6�0�rp�8���R1Q�!�����E�8�L`��l���e8�4H\������6C��8�/H\��DC��J��&\�/�������@@l(I��5��R�&H��	l��>���i��� �,�p`�b�E��:%K7k�?d.=%9��	A���1�,��"�K����l�,J������T@��2N��l|=�T��3t=O@\��SC����#����n��}\���l��!���R�&�\O[W!�z��p��|��c�����_!E��\�
%���B/�����b|�[��  6C���H�o)f�yn6�x������O��J���z>�� ,�e!�����:����\_�u("%�#��1����(�����b6a`9&��@�E��'�z2!����U��66&�0��g������d\�����V�>#�4L�������mr4�3���!�$����$q���M/����c��ocK@S�8��D+�7!{_��9�q��?D�����Z�g��z�a���SZ�:	��d�cC1$��FB�Q�������'��D������� �dU�zaj)f���Le|@h=e���:���a�bC1$��3�����+�����F^O	&��r=�c�zB����G�u���
���%/��\7��:Zxafs%�o�{���W�B�E1����'�9�!w+�>g ������+1�3BNH@�C�/m����PV0Jb��@(�hCKSb9�@(�e?-K�a}��PSa �q���#Z����.�@�2�B��<uVBm��XOf�m�X7g ��h!j[�6H�Z�c0�B��������(h�1�B�>�mJbk����e�e�[��d ��#.&oZ���}��Z���
b_mK��/�k��0����E�%t���C�@��+F@�O#�M�l�����lu��=��k���@��$ ��{V	h��!����	�=���g��i#�{�#7���{�	���\*���#|��N!��/�O>�^NB��%"M�2	�P@��!N��,Y����O�h�oc|z�%|������2�Y�i	�'��^�����r,���Yb��T��������	��q����6}(����'�����x�S��VO��8r��6C1��
\�5�KFFV\<KTF���,@	B@����8�$b[�������b�,�8	��s�#{"�}My���Wx(>�}" ��" ���<K&-��g�# �+# �H��H�!qx0�3��w���%���8N��8�0��� �j$���#I�Wt[�6����q�\���ckJ@������-7�D���T��R�P��7{`��Kp�p��8���;-Dm�t&��P�R]q�P�#�	�c�	h[���G�kQ?|�b(���	���#.m������?}�g"D�����-KFb/�o��  �� ��������g�D�z��p6�8&�-\3���C@��#���Y���	�x(�k�8'O_�s����,Y���"������P��m��{?8��e?��q����Y�:��~f1}K� q�!q�bX��#��^�8T�g�����/��y�/��Y2P#�}��X�C�3m9q	A��{#���Y������X��	���	�3�#����p�v���OK)1�|�q��CA���	�3�� �����M,�� ��K+�gj��)�O@:�E�2��X�)�a�BWp ��a��m��z�t��-�K���B��  ��A@\��,p�?>,D�a�x(��P��O<}!��B@�[8�pm��V�%��G�q%�(��a�������-���Z;��%Kq}��@���MZH3�����IZ�?�9�,��������Y����x�Y�r�G�f�$�0��0��XE�x�"���,�"���Y����Wb#��P�b\�-��%~��}��Zu4K�	i����E��P�2\�����D[J�s�l���������	�+%F�m�����Cq�+B��$�%,q�JZ��j1B�j�L����W�����D���q���W2�bKi��JO@��[*�F�o[�3��k��z����b(&��K@���x}������/q�`.�����f(&!�nL@�?!	qE�����E���$���	�+G�b(r-���?���B6qmmZBO���&�E� ;�	������=\�<��&'�i	=M}\�����;iJC1��6�6�mj�`q�(�,�����9%��$r�$�j����#|�-��i��O�����q"D(s�m���d��	��� �Et�@�D�Yn����b(f����p�B|cH�E��ZBO��o��m��C1���B@��g��`hQ���z�e�����]K����T��]�g����� =e�u�xJ	T�g�oK�j0��crDKF@|�S�Zn��9��o	�
��$$41c3�}���-b���.6���y.���X~����b���p���!�����w�q�6��9�#b�#�"l(�NGV-�# ���`� d)�B@|_,7�` 6c���# ��#��ps�z8�4�-�Cb�%�i(?c����o����-�-�!�-�1������Qb�R�\<#x(����o������Kc�zJ@|_jXnZ% 6I��������o�% ����.=�9�#d��RcC1,���%���cf�����\K%����)f���j�8�\��.�w�C���1�(,����"2�-D���6c��N_h�_�C�1�\����N@|�x&�O@l(�\K%��Hq���c.��\�dU�,7��Og������_F��O���a�'3p��P#[��(��bL�64���R�(fOn��G�R�|�=&j��\!��3��(�-��L��d�a���8-DG�<��C������q>/�,�P�X�g���S����:���'��PL��0��g�\�N_�[�mb	=%`��:	q�	{M�\�BO�YK�k&X� ���3Ip�)QP��:���b(�n��6��O�Io�4k)MN@zJ�N�'!
p<�
���F�%�Q���������	=�SK�����fC1c/��&q�Y�=&fd�bV�^OH��e6C�Lp�R�����1�b���%������8�6�2l(&.}P����M�r�bB�~M4}�x����n{��*.	�u�>#\�&�9��Z�C���$�|H�����l���x�R���l��� ��2��}�iBU=�,��v����P��I�I�x1.fC@l%D{��J���0�D�YnF!��L��I�.f����
�?>�-��	_7�
q>R"7�A`�B���!��<�������b�,�e	��
���Hw��l�B�#	��J���0�DD8_����.=T�D�T?H���O��S��D�A�p(��
�D*5C���MZn�&u�gs����Oz!0���Ih1I3��O)�	�Y���KQA��.z^�����I`C��~Y���ex;D�7I���&�J-�G�bC1�2���3k0��-��%����$\L�q��'$�i�E}(��I�8����{,��t�����H8r�}�n����
_5��}���Dx*&��m7�$�����f�v���
a��L2��wo�%�kS B����$��
������49
3��3�p�-q�i��	&��R(IS���f��@@\�&�H��f����8���)&j�i�\aF��" .f�d������'Y�CO�x&�,���R���"�#��K]K1��vUK�8�!%���R�8�������
E����6$���@F��+�g)���!H�����})��+����V��e T?R/��H)�����)������w��
m#\�����YK~WA�C�CO����)�����6���b6�����p� �2L�����V8����"{��\�&
B��'�3�Q�������f��/%�e�<;
-�l��0'�� ����d�b�C\�8
#l(r��i�`�J��e�����Ez�P��#��A*6	��M��R�&��g�l�4�	U;�H5�����Rl(���U�4vq1�!�[*������w1B!��r�3��`|�&�8�fPg�q�i��X�M�r���P�hb���l�t�r�]���)���������y�����fSK1���8���������0Q������)��\.���b6��=�~d��C����*�'�����H��\�/Yf)f�f
�	�'E@zJV���icC�#�k@P�����x2�q1��
�[��V\�tzJ�"��_����;���p�C�Z��d���c�
"�YBO3.j�B|=F�WJ``��W�_�L�8n���p�i�������12���b�.c����M�s�J��^�'8�����tKb���X<Y`�/������� ��5� ����4-D��z�,H����uU�,��!���eN��*OF�	&j6fp�C1#��������o������-�-8�"�k ���cp�V�p����zf+f�E�����qFSM��8>���x�G6�����Y��b6�&X�ml	h�8b%�p�	���\�l(f��,������x�q�i�X�������$��bFZt�������ie�����'��CO3�0Q�>��l(fi��ad���
Y�X���R���z��"�\|=F�y�P���n6��!R����e��!��p���1Qg��1H�AC��a��\�y����o_�Op��~�&����~98U1w�i>��&����v������O���8��s=�_:���j4�u�I�p.��{�W��kgR��IQ�A���Q���Y1��o��,�.��71/�vWg}�{8[(kT���r�/������w�r���z[���|�d4���C��j���/�y�T���������3
����A� l��-
�������3�7�b��B���ok��<y��9;�������S�c�����6`uU�/�-��������|v�v:�Y���o����O�N���[����g�/h�:��7.���g6�AYT������XA�~4����yqrtxv���~���������6��<wz7�"W�c1���:;:A/9
����^�q����3������hy��&`�F���xi��U����0�}.�/���x_�������4`����DS������t����@g?�y�����^��i��I�����i�W&�5h���!��h8t��.H_��Y���������i>���b|^�/�����w��6F�A����4+������|��;�"��R{L�wilgg�n�����^�;;���:����;��G�DU�����W�xg������G�����3'Em1����j�[hM;-����~���s��V��b<p���Q��H�b��>��V�
g�Z���Q���7��.G<��9��czfP| }���3�?&DF��q3����0��iiH;��J�cf�����t���P�A�f']����P�U1�;8����w�����������s�f��~Z�B���!�tV�OF��!���c4�����X����e>�f��bFmPJ�e�t�U��M~���)V���O?���u�mL�k7��U9(**u���~X�A�&!�Z�fa����d�������1�!��7%=���)_���
��U<�DP�2k�k_�uX�/I���2�+���E1�Z�QZ>e���{��>�/^�#�>�u�Nx!e�'}��*nUm
�'$y���H���:W�{Z��l����T\���9a�W�| rC����b�VH��1c�+�ScCZ"�������c
�bV��
y���u���|<�[q1��7�)���>Yh�K�/����2��bL gH��p+�Y���sr�����#����8;~������8/f�rF�����'oN�%z�����F����w?(4���O7v�}���NS�������y|S.hih�+�\���	��7/�%����c�2e��������w���QqE�n�O�Z���	>��l��C����O������Z�����5�xx����wD�����������!�3��o>k���S�TM���^9g�Z��\>~�|+?��vI����5�3	�o_�>>�������)|~����~Srk��@����M��I�>�gt�����5S���>&5�%'u�|�_�xN	��f?���5�����n$��)�%IY�����g�1���m���/>�b��QE�"[�JC�5uD���{Q��_@oZ��>���4&/��E
�����'�7�q��T1����9m��.�G�3�q��e��������)��Y���*��WPd���C!��]������o��������B���?�y;������o��N��h�w�����u���n������XX��2|�����
,m������G_f}m���.{�����,�ol[������n�M��/$��{Q���X�����/�B�?+<����
-y9a^89��fNo$Ao���������^m��U1���+���������K�
g������}~����/V����d������e����B���G_�O��-��_6�������-�=��e����}wc��=|�u>L{\���<������&�F�=&�j���/�=yJI�=��{�����Os"�;��J|�C{I����+�0t/H��~�C�q��
����_|��Y���*�-#�
�����|;+��6p��(���	�`��^Fq�FA���~��n/s^����:^������i97��'��d^���|_.����,��c��W��E?�?�xI*�/���yS~p��HC|��O���qIY�8]��O�:��;<{���f������)'i����J�"���W���t�Ji���	�l>�C�U���f�Z����c�/�������1��U^9��Y�!I�������8����B��;�����������F��T���W����R/~��+k?k��]]�E�����z�-oP���B��������I�_��
�k������$�����-����o�&����:X\M;_L/�F�L�z�:�f,�i�tB�v��y?�Hh�_.��E�R-����G�|��	BQ6v��,�A�:<������J��#pY/���].����q��&d�I ����.�������1A[y�Q���#�{�V��	Wlc5����
��E�~�������~0��i��|5�	�����'8{$�$&������yo1�Yd����������V��mi���1.{�x��pN������#�������6�>��#��������,���;��gE��e [�����G�L:��3����o���n����O}��`�vw�4�z�f�(��������v:d}��!a���(�9C�^Z�B��9Y�w���O�P[�jf��x��I`�� �?~y��������O�_�m��="��S-z���w��vo��O����o��)5������MuJjCbP\���6��A4�
q�����FJS��W;�W�����kN���|:�F*N��UN2c-��8w�9��J���iODn���H�Ivd?�<���5('�C*���n1�H���g�_��d���v=qN^w"�p���L�g���.��������u�n?-oou�g7����`z���;������sb���f�"V6�@�}Y�-���f}�:���%8�������W#N��M�~_�G���7��Z[�����$���AR�%wL�N��)����}�����(���W���]���v��NZ�J���H�t;8W7��h�+D>��Lsv����8�Xt	�+��=_�*��x� ��|�a��E�'��h��l�J�,�Ng����`�Y�
na���B90���jc��H)7[��;7�|��'��no���&�����G���A1����v6�-Ko�)�I����7s���������G���rv�����-������ �h4t�O����'c����	/�o|tr��d��F��3fS�������G�'�>q>�1)^�|wtF}���tv������F�����������X�@��x���b�~�Le��V�m��7E�-�Zb8������q�>�&�
�>�����D������+�lW������;���7X�=%�g!�
�-� X�pN�G���Y�uVN;����x�^(j��������ow;d��h��b�B���LoH��e��n=�:
�K��B�o���7��'�������]����M���X�9�zkz�Z]���?�=~)��sy[��
���^�DR���G�����H��X���k'��>:��8���2��i~3.��`+$�5"�I�)�K��nYx���tg'wh�vN�c1��JIB�+��=���/�#VH��A���BP�{W��=���ttqq�'�K�DiM#������Ni��G�f|�W���h�B�-Q9�������}$J�@w�z�Dt�Q���~z�����_����-Ua6��Z*;�������
�T����L56)'{-X>���+��T��%+?W��
9[���5cUM]�..�����g�s�e5�NcU������;����Mf��P���`�xV���~!sN}��I�����	���2��S
�
��z����tz-���y��,�J+�}��r��|�$h�O���O�dK�M'���N�c����M�p��_�f1�i?���M�9���������R�����(�[���z��&�E~Ycv����9k�BI������]~Q��zu���5�:z�r�(>�
�$8+o����l�s7f$�hU1���J	�O��x���q��x1�d�m����zO�<�Y�b�g����S��\UY��Ek��S����b�������Qg��!�'�ES{�u�����b�b/������,���VxzyS��Q�WF���h>�6���m��rg��r��!q2)4;�g�%�r�z���i�Zo6M�]S�����=���,o����XO��	b�e3-\��m;gSm�M���q(��5cL��S�j�'��3�W���=�DB=<���\��O�2t����4D��k.[Bc�W�a��5z�N����Ds���A���)q���C���B6���%Y6/��B�o��FEO��T�	������j�:9!A��������X7���T�)���Y9��d�d��0nX�sS��x]<6
�v,d�������E 7��J���4�cn�\okZQ���p���[����N����z�h�uX��_��>Z2�i9��lL����i��a��9��$.!��k4�}�,X2�v�l�10s\�!���L����RZ�6N��dP-��S9���������x������fj��Q[/hk,���.k9��^c����-jn�N9���J+oh���@�&3��nr"|}���w���hz���[��;z�-m���_g����-{��[Kd������������-������SaW�=�l���Q�'��,/��X��f���4��������w~yyW-5��t��A1��pTo�������R~�s �rp�S����/6w�5� ���r�b�V. ���j>�����v��?�u��o��0Pt�5�w'G�O�D�_k����^|�wv$7������L�9�����e>��m��\���� ����Y���1�(��������'J+��s�"I�����|�O�Y��yq���js�,k�Q>��>1�?����y�p������R}U�>U��A�6n�}���5PP	o����H[��y�����D�����=��l����~��lpM_�C��S'��{B6Q��{�1�����o�
��?[����je�#ex����^����T��
�[���_6_�e�1�`�01p�~�5�������U��������N��h�)S�zV�G�Gj���������6�/�w���w���^����a�M)���G��wa�K�lk�Q��5}��T��x?	��y� ��.����s�]a��0�
ger�O�AW�}�y>_>6^��W_�\���hbVs�ke
!���Q�r�~����O����{APdkJW�����>.��������W������w��gF��[K@s��U��Q]jS�I�d��w��,�'m�wGo�N�_,����y/��;����\y����#2�5�]�gA�gP�c�.��
{�$T��y��(���2���
�hT{�g
��b���|w
��7���S�8R7�`DI����^����-�����'��VJ����W�k�[��+���8O������Y�&��8�if���	�^HEB��������OZ�QO=���r�T�X�b�*���y��$;���I����g��w0x�Oj4���v���`����}�l�|����3�����M�
�BY���s���$�?�Q���S$al�Pb��|[��f���}E��Lj���s�]n����]���
�N���6&����o���S���t��
�`�f��;a��j��%��-����4�_��;-.�e�+]��0'
XL��j2=2�e%�������Og��"W��s���f��F�.���mtf�������x��7v>�ym@�x\I���Y>jU�sv��2 ��d�VK@Kh(�Xj���h��!�At^se�0���FOHq��������s�i���vVX��gdwz�9�������;���I��^��1q���H�Z�	3i����~O��#����@��L��.��itI�w�����z�#�����t�s��}p�'��u��+�t��O2���\�@b�������V�ies�]���#.� ���|�s����`�������zF	���dE��������w'[�Z��'���v6"B�q:.���L��z���m���NmYj�l^���&��������@���}�������������������&#�(�@_U��
���1Y���A��R�,	�MUK�����|S=�ow�n���9��c���z���;B����������xVTs=�V
�X�S3�&����R��JsJ��6�o����Qk7��Cz7�$��i��9[#�b/F�[w�rcpK.?�����������������6�TqQm�������K�9�]e*������:|��[��X�^�L�}���hS_;���JCo�TM%��`|o�
���rVh
�_���#��O?������0�jOU�C�Dc�z�����:8�Jf#�y6�@)�P/[iC��4���V���M�::rX5��/��R�����(����S����o������|{xv�J��[
�rr��rI|]B_��'4q{�����e��d)�����H���J���R���FJ���������&�)������|"����x�a4�J�'0���_���Z�����?��	�������n��?w�}\��Yy���A��"g`|<���6�\�V�6v���z�-�i�Rw;��<h&�5Q
�zX��^�wnji�������!�����(���#���GF
�=��$���t�� :�_�{gx��O�����a'��k�k���N�������_)������>5�3q�����`Hy����`�������p�!I�5s&D��a%���5W�gem�"5-��^�g<�~�
%�����9���Z�W*��\��s��=]���
�������m
���U�rF�`�b����q/��������K|����CZ�0	���������	)�A���|��_����J�"K�&K�C��}�u�&�
�Z���W���j^�4b��O�y����6�E������8)��b���y���<@��<�6^�������'��|>���#���:�:xqxTL�~����z���������~��ga���������������E:�,�?V�����{�����g��"��a!���J%��PwG)���)���2�:�����Kx�N�_�>��^��J"��������bf����\�dGz���Pxq�\��sV����yhu�qj��;��zio�n�{w��hw�q���������Y�W��~m>��Yg7�M�"zs�&�X��$J|T_+�6v��\���u'��G�5'(Fd��TrbW�$	�u9H]�S[��&��F�$8j��9�k$��kM��)h=R�M�8��*6v���q�� P�>�u��'>�x(��e(��t�~ ��������s������i�N��h��A�����ET�&sG%vI]�)�'v��3��Q�l��|�����$�����x���o��U?��vR���-jY�t���n+��}�#]
IO��]/#����a2.��O�Vy<NJ)Q�
|i��Iv��W��Sr�k��"34�������r�:����<�%��@�5=m�'_�'�U5�Z��w������hC��R�3tuKf,��!X��
I��x���5��>�h^�!$���n�+�����Q}H��J"T9BB;�5�Y���w.�y���W��P��K�*DZ�^!���Vp�r\��:��W����v�s��Vi�M)|i(���
��������u��0eU�aV�V%���S���k�������Y���K���H��_�������������F�v�8��
~������\n�^TG�����o��DN��7et���3�/����K�E�njy�Dx_.Nf���/Yc�yu�6��p����\���AoWs8�C)�g���DP�_uc���`��=?���\z��T����N��?��k7l������$���_��i=&�V����'�$�����8������������B�y]����B�_�|����$�*�U��hQW2V�O���(�z#F�inL��e>���I��Yz������i����������s��3]��.Wh\� d�8���j��������>DS���������X]F��YU��������<�[\�6����������6/l���T�� eTU.-���(�j���$k�&��
��ZM�B�SU2��K�&�����z'��d�C���j�d���*B��(LS�2��-	r�9
��	.-C,u,]__�-�\���.�r���e�YUf����M�4���	����p����*
�����$�sX��F������/d�6
�V�\�VO�����D�����	���F�<��v��sKU&j�7m-*�2u���X�.g����X������D����|������/�&��0��F�R��*��U�Q>������]�E�WR��`�RUY���I�;��J
1�q�����I�L�r���o�:Q��^��fK��N�%�k�!I-g,h�� ��6u���C:[�AO$�e1V
 Q�G2�*��X���%�r��� ����k�;����&��#&E]9@�R��4DQ����9�
�vs"2�kG�UD�MM&)gx6W ��gN�����k�4E���J������4\�F��<'Fd��s���o�E�'�&�VL���{����0&�h���]�jqi-exJ�,��v{Ke��IS��-�t;��� �R);�a�����g�&�����Q�[�<������)g2�:}����7��H��$��TbfM�Zi5�?V1�bX�69J����9[3��uO�J���[3�i�K��BL��P������������s\��7�3�����dO�
n<V���P�Jy�������h��b~�.�����P)�������`����os����������*:��_�z��h�J��7Ar��������������dp�<Sf6��*�#��������Y�`��k�/�P\=����V]"�pU��Y�eT�T�u��H�Zb�h��](����\L�A�"���N�P��.3�`���W��������5_���;���5�S�)�c���&�-R_I������R��.�4��p�B�#U]��J@X�D�s6�l�����v���.?���D�W���w�[�-��Hj�����,�e-?YD��+�h_�f��+�6����.usS�v4�gN���I��*Y��$f�]]�LukJe�����kr���)-��Zk���^�QU/i���-#9Xe�U���������od�� �����I6&�����@�D.|q	���{5���7���.�AyM�u�����-m���6vk%g�K�L������<�z9�Xq7n-" .)�+���b�����R���
a������]\���a���]#D��v��������BY���H�����0�������%�����@�8��N���������ttrz���zDK[#=�%8�c���ic�'����h�=J�����}T����Bq�+�g�7ic���U#D<�28��ss��#R�Kw.d�U��R3�&�^�e����5M�j���.f���z�L{�R5v���jqEm�A��U����)�q��Y�E���B=�Q�3��f*��6����P�.�a$� K��>����AbH��R���K��e%������FW\T<.�:F�l��h��=5��my�K�(����7�Ne�x�+���[��Z
���I��S�`���KNl�*����o�8Gc��R�3Qs���Gj��h��T�9������Ag2{�i3�E��_*��3�r4��D�\��%"#�|+���i 1nX^�u��	�H��u����	JR<�-E��X�S�SMT��2�������fm�b_�)u���jSl�Y�P)���Tm���S��r5hI���s�E�T#�>��$7������������5�fX�������(j|��-��=7g
r8b��}��5����I;Q��^�D�u8���z�?!� s��q9�4j^��'vW���H3�����}c���9���4�����9d�	��h���dw��h���|���`Z�u�B�]�����q��*R����/!�:1ZFS�{��7h�kQi9M8-URa�%��MQ�QX��D�D���>�b��cU����#+$�����	{N��ZVH�U���(����Me�q�k7������~1US�P�������G����������������L��}�0���k�R�;JpTM�29����y���M �OL�������T�a(�h'�b������M�R�Q���f�p�	���*��AK��RI���Iq}Y����D����>{azE�de"V�K.�VC^q�����t
�V-���p=M2������"��a5c^���Pz��=t�[ke�e�&���m��#ux�e��)�i��S�\G����T�9�~=+��v��F��q4��U���������f�&z�LY�����>���
���WV��r���])g��h�5��\�������8�=R���B���Dd���j�6B������U����_�7%s�1���n,�Rj]��ua���0E;����~��_�n��� Mq����V��=�3�=�����<KF2������[�U���9���8*�G��L���-5)v7H���3����N?@Z���2�7���I�y��E�4�'�b^��o���@��bS����R��	��T(V��:R�>vr�������"o)8���d�T�+�����jccO������H`'),����:��}�y�*�����Hc�����
ft��!�c�i`�����y���r�����O�);���K��MsB���������I:_�+H��~!��:'�i\���=�4�)��2�O���������)_7 �8W�"�Y��&T_8\B_��y����$�����T���G�s1�p11/���A��-7�����&��f����AB��b��v�M�e�^�=@�9�m.���M�_O�	��k����1������i���.�L�����Q��U)�/�t��+���h�\�F���9+�Z�X��+
��;���	��w����|F�i��)�U��6v.y���������lg��g��F�.������*�r��54�+�E��������P��P�T�dP!9��H�dF$������7��BnW���������z|o��l��#�[rI_�BR����*e0�R�V�xP����\wl��>����������c!�m��]���h�_��]��E���u*8��c�kBzO�����������^��r��XR9[������|.*��qs�F��k�N7�����^#��1E{r�)5�Z!GH���:�?�����`A�,�	��F��L��e�;�U���:��U����Gb\��l�(���_v�k���T<�:��_?���=mBO��f�ek�Q
�h.2��3�B3����!Tg^��Qr��F���j����,���/�����I��\�C+E���0��������I�z���n���K|$��i���^�4�"O��
���z�`8t�<�(���W������Y��h)8/M����0	�u�Uu���:���M���a��%�����\�q]���^�T�1a���ke
H?K3��Fm{��J,�}�W�������>]����W��J��sE#�)�5j��Z�[�	���^(%"�n��E������VR��V�����NI%y�1t��6�����3?����gM�Tw&��xuR6�d��N?�7u�>��<	��=2I�n�APp�����x�/UH�M(w�����$��R��y����S�R���'o_;��N"��������)Q_}x��x|��C�z��������z�a~�K��Kvv��������������E_I�N �G��q�+�_�F9<�m9�8�97�����
q���R�\�kyv _��fd��-���l:]���]ZW���w��y�G
>;0��=�v�������i�YU��#`s�����qm����*5�$l��J�����O �C��!��q����:��i����3��]�D4&�
3R:r%��:�f�A�D���e&W*�����9�[H��>���vy������6�$/C�;1�����h��g��\� ����{�bP/��
H|��1zy�L3����U����F����D�����>"i��@[yG�3)�@�t`{F����nak-U����An�/+�����dY���d�rFbZSvAM9�N	�Fk���W����6��R(?r��S'H����q�'"��[��s���Myw�aTj�
B1���m�Mo��i�o�m
����_��;�a�kY�DR���^�����<qn�8�O<�eA�)�}z�3s��"����4��iX�.�J=+X��,-�,��(�����E��Q���q���� ����/��7����N7��w���f1���74q��z�� ���X0M<a�z#����_g�uK��7C%��gD����$}<;hA�	�5o���%�j��}�%�K(5qMNIu�n(2'�&�u�&���q*
P�7�����1�c�0���)�������cf���>��GE;K������
�mN�����p�T�������L����a���]}��c���|�����^�U��+��%�
�D(��s�b�{V	���#m)6*J��zj������A^���\rn
f;�����u�F+S����g�vz3�n�&��=c����^�AsO(�������
�l��v���e�ko��M�s+[��e���o?i5o���2^����o�rN� �����D2	�
�HjM�]Qg!~���[�M��sz�����3�r��S:��T8k��V#�$��c*~�������H��g����v�+mH�����8�����+u�L��
�`Ws�y��}��~��L[�Z���}F�����e�O�4�yM+���4"cc\}�s���#*��5���/�&���n���B�)����	I��FR��l��	Y%��s��4�V?-�����
����c��?�C����}���-8���������x�[�A��s������|���l�:t����
�D�6@��/,��r:4m��Wl(����������������b��%h{M�m���vbrs�fd������x�8�W�t�6��`�ee$m�2K�y�1~_pH\���>�������r	��/�����6������/��t���y�N�tt�d���
��f����:���U�8��Q~�EV�.�2����%�A���9��5���L?e$l#_���&Qt"{~d�~��DpQb���������m1��,f�Z�5��}�*$��dI�x�8������H >��H��������4��[���[�CJ�:�����RG��@{��l������W�7����n�����r:�i�J�
F�R��l�s:��eB/���N�����T��)�����5u8���9�j��B�.��>�n��
t�!�������-J�j��B{�6������mO����A�x�0����q?(�(v-.�5-��B�yJ�EG|����+���/^��s�u��~���������o�*��P���D�����uz�����,���E^=�����k7���"��������� �,�jCkVx�!��=Ya�K*��(9�e^�Ge:SC������
i���������?�a����\@�����O��^;�-���{p�=��F�}�{
�����b��z�Z/j�yk�����e�K�������;�����
���q����A����_��9%h��
�������b����@��������&W#�z�� N7�����w��]�Q�����������;���N<������%�8H�>'kV_�+����7��+p�}dRw������!����M�Z�q"�dKM�!���IK�4?����������bz�"S��b��-Y��
rUI�}��1r�����<2]��p�9����c���)�G�L�,%�q#��Yph��s����bz7�i�GD<��V��U�j`~3�-�&���4P���t�g�M���?�|����>I9�o>V���y��g�+-������w�:NU\4o�GYQl?(��;���~��c4���]��k��v��%������g~��ny���w�4&�P����B���R;&�fS-:I�g�����6=�VUh����H0z��i����p��:����kl�[h���a�����`�$\r�4�.{*��
���>��4M�K�����&�\�!�c)����M"���/���\e�7��u�4'i�R�C��{�\7&^l������
�d/�}{���W���V$�dR�lR�I���=��S�U�Z9*���2x�u9Zu�����t1b�@��������!��#-����Pw�E�����s{r�^��=��� ���]�w����T_�U�)Spu�)0����$H�5��/5����Np��SAJ"^��v5�������~q�����]C*z�����R��C��Z���-�?������n���8y�,��CT'��1�]3�R��G�a$i��]��wT�ZAv������Zt[�C�}�S������|���u�{Sp�a��\�������
��]�3fO�����[��l��h�J%\�C�8�T5���*P������j�����3�{�\S���~���M"���ne���~Be��
��q���mN?5?iC���m��2]h���>[_�M������0LOR�a>��A��Gn2���?�LR��5S����+q�v {A��;m���E��<:���l�Tr�*�3���5~�&�����/���2;��A�K��u6������6-4�h&rc�����u68fcK�O�C>���g�K(�K1�
:qx����@nI�6s�E��}N���,pt�sG�$�:����g�Q��0�M��5��::`7����[�6&���mxS�L�������y���5?(w���)-6-fR�~R����s�+YT�<{U��[mE�����u2������
S���-��j!_9����V��P�<������J�5iW5\���G�Mu���E�����\*�}]Me���Nk��R�C�q���_���j�T
PSRH*,�K���V�w)dBc����9��N>�����)8��.�*g5US:W���
���u���Y���i)@�S��uwu����E������� ��S����K����\r����e6��������'!�\��s.���J5��TT's��U�*��C�77���r�������Q���z�\�o���SV���q]]R]�TM#l
�UZ�QYg�����2��!*��{:�u�3��+��fz�h�n]�F�lJ���2��&���$��f\�������V�\M��l|cHG�c�L���nk;T��{���/�$�%sO]��31U�DE���������ID��@�m�Ox���%lvU�����g�y�RUI���yo���,U�h\�mi*�����{�����}�/f��K$���r�j��A����-}�Os�z1.{��`U���k')���Y��J���b�k6�X���h�ma��*�����hL��.�de4s��!���v��
J���&m;MK����cV���y��P�����9�YW����?�����h�Y��V�\U���V�\Y�9S��BU�US��[p�R�4�zEM�bU����\1WE�t�SR���[�|�\TP�n2��5!?f�US������j�i)g�t��oU��|��Q;D�����B�j��_����������y�<|>��������y�<|>��������y�<|>��������y�<|>��������y�<|>������-��
#305Thomas Munro
thomas.munro@gmail.com
In reply to: Antonin Houska (#304)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Nov 12, 2020 at 10:15 PM Antonin Houska <ah@cybertec.at> wrote:

Thomas Munro <thomas.munro@gmail.com> wrote:

Thanks. We decided to redesign a couple of aspects of the undo
storage and record layers that this patch was intended to demonstrate,
and work on that is underway. More on that soon.

As my boss expressed in his recent blog post, we'd like to contribute to the
zheap development, and a couple of developers from other companies are
interested in this as well. Amit Kapila suggested that the "cleanup of
orphaned files" feature is a good start point in getting the code into PG
core, so I've spent some time on it and tried to rebase the patch set.

Hi Antonin,

I saw that -- great news! -- and have been meaning to write for a
while. I think I am nearly ready to talk about it again. I agree
100% that it's worth trying to do something much simpler than a new
access manager, and this was the simplest useful feature solving a
real-world-problem-that-people-actually-have we could come up with
(based on an idea from Robert). I think it needs a convincing
explanation for why there is no scenario where the relfilenode is
recycled for a new unlucky table before the rollback is executed,
which might depend on details that you might be working on/changing
(scenarios where you execute undo twice because you forgot you already
did it).

In fact what I did is not mere rebasing against the current master branch -
I've also (besides various bug fixes) done some design changes.

Incorporated the new Undo Record Set (URS) infrastructure
---------------------------------------------------------

This is also pointed out in [0].

I started from [1] and tried to implement some missing parts (e.g. proper
closing of the URSs after crash), introduced UNDO_DEBUG preprocessor macro
which makes the undo log segments very small and fixed some bugs that the
small segments exposed.

Cool! Getting up to speed on all these made up concepts like URS, and
getting all these pieces assembled and rebased and up and running is
already quite something, let alone adding missing parts and debugging.

The most significant change I've done was removal of the undo requests from
checkpoint. I could not find any particular bug / race conditions related to
including the requests into the checkpoint, but I concluded that it's easier
to think about consistency and checkpoint timings if we scan the undo log on
restart (after recovery has finished) and create the requests from scratch.

Interesting. I guess that would be closer to textbook three-phase ARIES.

[2] shows where I ended up before I started to rebase this patchset.

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

I suppose the simplest useful system would be one does the work at
startup before allowing connections, and also in regular backends, and
panics if a backend ever exits while it has pending undo (panic =
"goto crash recovery"). Then you don't have to deal with undo workers
running at the same time as regular sessions which might run into
trouble reacquiring locks (for an AM I mean), or due to OIDs being
recycled with multiple checkpoints, or undo work that gets deferred
until the next restart of the server.

Since the concept of undo requests is closely related to the undo worker, I
removed undorequest.c too. The new (much simpler) undo worker gets the
information on incomplete / aborted transactions from the undo log as
mentioned above.

SMGR enhancement
----------------

I used the 0001 patch from [3] rather than [4], although it's more invasive
because I noticed somewhere in the discussion that there should be no reserved
database OID for the undo log. (InvalidOid cannot be used because it's already
in use for shared catalogs.)

I give up thinking about the colour of the BufferTag shed and went
back to magic database 9, mainly because there seemed to be more
pressing matters. I don't even think it's that crazy to store this
type of system-wide data in pseudo databases, and I know of other
systems that do similar sorts of things without blinking...

Following are a few areas which are not implemented yet because more
discussion is needed there:

Hmm. I'm thinking about these questions.

#306Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#304)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Since the concept of undo requests is closely related to the undo worker, I
removed undorequest.c too. The new (much simpler) undo worker gets the
information on incomplete / aborted transactions from the undo log as
mentioned above.

SMGR enhancement
----------------

I used the 0001 patch from [3] rather than [4], although it's more invasive
because I noticed somewhere in the discussion that there should be no reserved
database OID for the undo log. (InvalidOid cannot be used because it's already
in use for shared catalogs.)

Components added
----------------

pg_undo_dump utility and test framework for undoread.c. BTW, undoread.c seems
to need some refactoring.

Following are a few areas which are not implemented yet because more
discussion is needed there:

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed.

Why can't we have a separate discard worker which keeps on scanning
the undorecords and discard accordingly? Giving the onus of foreground
process might be tricky because say discard worker is not up to speed
and we ran out of space to pass such information for each commit/abort
request.

Do not execute the same undo record multiple times
--------------------------------------------------

Although I've noticed in the zheap code that it checks whether particular undo
action was already undone, I think this functionality fits better in the URS
layer.

If you want to track at undo record level, then won't it lead to
performance overhead and probably additional WAL overhead considering
this action needs to be WAL-logged. I think recording at page-level
might be a better idea.

I can spend more time on this project, but need a hint which part I should
focus on.

I can easily imagine that this needs a lot of work and I can try to
help with this as much as possible from my side. I feel at this stage
we should try to focus on undo-related work (to start with you can
look at finishing the undoprocessing work for which I have shared some
thoughts) and then probably at some point in time we need to rebase
zheap over this.

--
With Regards,
Amit Kapila.

#307Antonin Houska
ah@cybertec.at
In reply to: Thomas Munro (#305)
Re: POC: Cleaning up orphaned files using undo logs

Thomas Munro <thomas.munro@gmail.com> wrote:

On Thu, Nov 12, 2020 at 10:15 PM Antonin Houska <ah@cybertec.at> wrote:

I saw that -- great news! -- and have been meaning to write for a
while. I think I am nearly ready to talk about it again.

I'm looking forward to it :-)

100% that it's worth trying to do something much simpler than a new
access manager, and this was the simplest useful feature solving a
real-world-problem-that-people-actually-have we could come up with
(based on an idea from Robert). I think it needs a convincing
explanation for why there is no scenario where the relfilenode is
recycled for a new unlucky table before the rollback is executed,
which might depend on details that you might be working on/changing
(scenarios where you execute undo twice because you forgot you already
did it).

Oh, I haven't thought about this problem yet. That might be another reason for
the undo log infrastructure to record the progress somehow.

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

I suppose the simplest useful system would be one does the work at
startup before allowing connections, and also in regular backends, and
panics if a backend ever exits while it has pending undo (panic =
"goto crash recovery"). Then you don't have to deal with undo workers
running at the same time as regular sessions which might run into
trouble reacquiring locks (for an AM I mean), or due to OIDs being
recycled with multiple checkpoints, or undo work that gets deferred
until the next restart of the server.

I think that zheap can recognize that page has unapplied undo, so we don't
need to reacquire any page lock on restart. However I agree that the
background undo might introduce other concurrency issues. At least for now
it's worth trying to move the cleanup into the startup process. We can
reconsider this when implementing more expensive undo actions, especially the
zheap rollback.

Since the concept of undo requests is closely related to the undo worker, I
removed undorequest.c too. The new (much simpler) undo worker gets the
information on incomplete / aborted transactions from the undo log as
mentioned above.

SMGR enhancement
----------------

I used the 0001 patch from [3] rather than [4], although it's more invasive
because I noticed somewhere in the discussion that there should be no reserved
database OID for the undo log. (InvalidOid cannot be used because it's already
in use for shared catalogs.)

I give up thinking about the colour of the BufferTag shed and went
back to magic database 9, mainly because there seemed to be more
pressing matters. I don't even think it's that crazy to store this
type of system-wide data in pseudo databases, and I know of other
systems that do similar sorts of things without blinking...

ok

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#308Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#306)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Each undo worker applies only transactions for its own database, see
ProcessExistingUndoRequests():

/* We can only process undo of the database we are connected to. */
if (xact_hdr.dboid != MyDatabaseId)
continue;

Nevertheless, as I've just mentioned in my response to Thomas, I admit that we
should try to live w/o the undo worker altogether.

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed.

Why can't we have a separate discard worker which keeps on scanning
the undorecords and discard accordingly? Giving the onus of foreground
process might be tricky because say discard worker is not up to speed
and we ran out of space to pass such information for each commit/abort
request.

Sure, there should be a discard worker. The question is how to make its work
efficient. The initial run after restart probably needs to scan everything
between 'discard' and 'insert' pointers, but then it should process only the
parts created by individual transactions.

Do not execute the same undo record multiple times
--------------------------------------------------

Although I've noticed in the zheap code that it checks whether particular undo
action was already undone, I think this functionality fits better in the URS
layer.

If you want to track at undo record level, then won't it lead to
performance overhead and probably additional WAL overhead considering
this action needs to be WAL-logged. I think recording at page-level
might be a better idea.

I'm not worried about WAL because the undo execution needs to be WAL-logged
anyway - see smgr_undo() in the 0005- part of the patch set. What needs to be
evaluated regarding performance is the (exclusive) locking of the page that
carries the progress information. I'm still not sure whether this info should
be on every page or only in the chunk header. In either case, we have a
problem if there are two or more chunks created by different transactions on
the same page, and if more than on of these transactions need to perform
undo. I tend to believe that this should happen rarely though.

I can spend more time on this project, but need a hint which part I should
focus on.

I can easily imagine that this needs a lot of work and I can try to
help with this as much as possible from my side. I feel at this stage
we should try to focus on undo-related work (to start with you can
look at finishing the undoprocessing work for which I have shared some
thoughts) and then probably at some point in time we need to rebase
zheap over this.

I agree, thanks!

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#309Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#308)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Each undo worker applies only transactions for its own database, see
ProcessExistingUndoRequests():

/* We can only process undo of the database we are connected to. */
if (xact_hdr.dboid != MyDatabaseId)
continue;

Nevertheless, as I've just mentioned in my response to Thomas, I admit that we
should try to live w/o the undo worker altogether.

Okay, but keep in mind that there could be a large amount of undo
(unlike redo which has some limit as we can replay it from the last
checkpoint) which needs to be processed but it might be okay to live
with that for now. Another thing is that it seems we need to connect
to the database to perform it which might appear a bit odd that we
don't allow users to connect to the database but internally we are
connecting it. These are just some points to consider while finalizing
the solution to this.

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed.

Why can't we have a separate discard worker which keeps on scanning
the undorecords and discard accordingly? Giving the onus of foreground
process might be tricky because say discard worker is not up to speed
and we ran out of space to pass such information for each commit/abort
request.

Sure, there should be a discard worker. The question is how to make its work
efficient. The initial run after restart probably needs to scan everything
between 'discard' and 'insert' pointers,

Yeah, such an initial scan would be helpful to identify pending aborts
and allow them to be processed.

but then it should process only the
parts created by individual transactions.

Yeah, it needs to process transaction-by-transaction to see which all
we can discard. Also, note that in Single-User mode we need to discard
undo after commit. I think we also need to maintain
oldestXidHavingUndo for CLOG truncation and transaction-wraparound. We
can't allow CLOG truncation for the transaction whose undo is not
discarded as that could be required by some other transaction. For
similar reasons, we can't allow transaction-wraparound and we need to
integrate this into the existing xid-allocation mechanism. I have
found one of the old patch
(Allow-execution-and-discard-of-undo-by-background-wo) attached where
all these concepts were implemented. Unless you have a reason why we
don't these things, you might want to refer to the attached patch to
either re-use or refer to these ideas. There are a few other things
like undorequest and some undoworker mechanism which you can ignore.

--
With Regards,
Amit Kapila.

#310Amit Kapila
amit.kapila16@gmail.com
In reply to: Amit Kapila (#309)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Sun, Nov 15, 2020 at 11:24 AM Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Each undo worker applies only transactions for its own database, see
ProcessExistingUndoRequests():

/* We can only process undo of the database we are connected to. */
if (xact_hdr.dboid != MyDatabaseId)
continue;

Nevertheless, as I've just mentioned in my response to Thomas, I admit that we
should try to live w/o the undo worker altogether.

Okay, but keep in mind that there could be a large amount of undo
(unlike redo which has some limit as we can replay it from the last
checkpoint) which needs to be processed but it might be okay to live
with that for now. Another thing is that it seems we need to connect
to the database to perform it which might appear a bit odd that we
don't allow users to connect to the database but internally we are
connecting it. These are just some points to consider while finalizing
the solution to this.

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed.

Why can't we have a separate discard worker which keeps on scanning
the undorecords and discard accordingly? Giving the onus of foreground
process might be tricky because say discard worker is not up to speed
and we ran out of space to pass such information for each commit/abort
request.

Sure, there should be a discard worker. The question is how to make its work
efficient. The initial run after restart probably needs to scan everything
between 'discard' and 'insert' pointers,

Yeah, such an initial scan would be helpful to identify pending aborts
and allow them to be processed.

but then it should process only the
parts created by individual transactions.

Yeah, it needs to process transaction-by-transaction to see which all
we can discard. Also, note that in Single-User mode we need to discard
undo after commit. I think we also need to maintain
oldestXidHavingUndo for CLOG truncation and transaction-wraparound. We
can't allow CLOG truncation for the transaction whose undo is not
discarded as that could be required by some other transaction. For
similar reasons, we can't allow transaction-wraparound and we need to
integrate this into the existing xid-allocation mechanism. I have
found one of the old patch
(Allow-execution-and-discard-of-undo-by-background-wo) attached

oops, forgot to attach the patch, doing now.

--
With Regards,
Amit Kapila.

Attachments:

Allow-execution-and-discard-of-undo-by-background-wo.patchapplication/octet-stream; name=Allow-execution-and-discard-of-undo-by-background-wo.patchDownload
From 65d1f14206ee55928505e5c3f26899a48d11e9a5 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 16:03:49 +0530
Subject: [PATCH 13/22] Allow execution and discard of undo by background
 workers.

Undo launcher is responsible for launching the workers iff there is some
work available in one of the work queues and there are more workers
available.  The worker is launched to handle requests for a particular
database.

The discard worker is responsible for discarding the undo log of
transactions that are committed and all-visible or are rolled-back.  It
also registers the request for aborted transactions in the work queues.
It iterates through all the active logs one-by-one and tries to discard the
transactions that are old enough to matter.

We don't allow any transaction older than 2^31 to have pending undo actions.
Also, we have a hard limit on the number of transactions that can have
pending undo which is proportional to pending_undo_queue_size.

Amit Kapila, Dilip Kumar and Kuntal Ghosh with inputs from Andres Freund,
Robert Haas and Thomas Munro.
---
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/varsup.c           |  35 +-
 src/backend/access/transam/xact.c             |   5 +
 src/backend/access/transam/xlog.c             |  29 +
 src/backend/access/undo/Makefile              |   4 +-
 src/backend/access/undo/README.UndoProcessing |  77 ++
 src/backend/access/undo/discardworker.c       | 215 +++++
 src/backend/access/undo/undoaccess.c          |  52 ++
 src/backend/access/undo/undodiscard.c         | 490 ++++++++++
 src/backend/access/undo/undorequest.c         | 205 ++++-
 src/backend/access/undo/undoworker.c          | 841 ++++++++++++++++++
 src/backend/commands/tablecmds.c              |   5 +
 src/backend/postmaster/bgworker.c             |  11 +
 src/backend/postmaster/pgstat.c               |   9 +
 src/backend/postmaster/postmaster.c           |  11 +
 src/backend/storage/ipc/ipci.c                |   6 +
 src/backend/storage/lmgr/lwlocknames.txt      |   1 +
 src/backend/storage/lmgr/proc.c               |   2 +
 src/backend/utils/misc/guc.c                  |  22 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/access/discardworker.h            |  20 +
 src/include/access/transam.h                  |  10 +
 src/include/access/undodiscard.h              |  26 +
 src/include/access/undoworker.h               |  29 +
 src/include/catalog/pg_control.h              |   9 +
 src/include/nodes/primnodes.h                 |   3 +-
 src/include/pgstat.h                          |   5 +-
 src/include/postmaster/postmaster.h           |   1 +
 src/include/storage/proc.h                    |   4 +
 src/include/storage/procarray.h               |   7 +-
 src/test/regress/expected/sysviews.out        |   3 +-
 31 files changed, 2112 insertions(+), 30 deletions(-)
 create mode 100644 src/backend/access/undo/discardworker.c
 create mode 100644 src/backend/access/undo/undodiscard.c
 create mode 100644 src/backend/access/undo/undoworker.c
 create mode 100644 src/include/access/discardworker.h
 create mode 100644 src/include/access/undodiscard.h
 create mode 100644 src/include/access/undoworker.h

diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 33060f30429..4b00d7d83f7 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -48,7 +48,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
 						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
 						 "oldest/newest commit timestamp xid: %u/%u; "
-						 "oldest running xid %u; %s",
+						 "oldest running xid %u; "
+						 "oldest full xid having unapplied undo " UINT64_FORMAT "; %s",
 						 (uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
@@ -65,6 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->oldestCommitTsXid,
 						 checkpoint->newestCommitTsXid,
 						 checkpoint->oldestActiveXid,
+						 U64FromFullTransactionId(checkpoint->oldestFullXidHavingUnappliedUndo),
 						 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NEXTOID)
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 31cd5fefecb..2601a95d5de 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -127,14 +127,16 @@ GetNewTransactionId(bool isSubXact)
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
 		{
@@ -147,14 +149,16 @@ GetNewTransactionId(bool isSubXact)
 								oldest_datname,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 			else
 				ereport(WARNING,
 						(errmsg("database with OID %u must be vacuumed within %u transactions",
 								oldest_datoid,
 								xidWrapLimit - xid),
 						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots or\n"
+								 "increase max_undo_workers to allow execution of pending undo.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -334,9 +338,23 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	TransactionId xidStopLimit;
 	TransactionId xidWrapLimit;
 	TransactionId curXid;
+	TransactionId oldestXidHavingUndo;
+	FullTransactionId oldestFullXidHavingUndo;
 
 	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
 
+	/*
+	 * To determine the last safe xid that can be allocated, we need to
+	 * consider oldestXidHavingUnapplied Undo because this is the oldest xid
+	 * whose undo is not yet discarded so this is still a valid xid in the
+	 * system.
+	 */
+	oldestFullXidHavingUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+	oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+	if (TransactionIdIsValid(oldestXidHavingUndo))
+		oldest_datfrozenxid = Min(oldest_datfrozenxid, oldestXidHavingUndo);
+
 	/*
 	 * The place where we actually get into deep trouble is halfway around
 	 * from the oldest potentially-existing XID.  (This calculation is
@@ -433,6 +451,9 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 		 * Note: it's also possible that get_database_name fails and returns
 		 * NULL, for example because the database just got dropped.  We'll
 		 * still warn, even though the warning might now be unnecessary.
+		 *
+		 * XXX Can we easily distinguish that the problem is due to unapplied
+		 * undo or some old open transactions?
 		 */
 		if (IsTransactionState())
 			oldest_datname = get_database_name(oldest_datoid);
@@ -445,14 +466,16 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 							oldest_datname,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 		else
 			ereport(WARNING,
 					(errmsg("database with OID %u must be vacuumed within %u transactions",
 							oldest_datoid,
 							xidWrapLimit - curXid),
 					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots, or\n"
+							 "increase max_undo_workers to allow execution of pending undo.")));
 	}
 }
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 77e3a44f185..a65a98e5203 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undodiscard.h"
 #include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2273,6 +2274,10 @@ CommitTransaction(void)
 	AtEOXact_ApplyLauncher(true);
 	pgstat_report_xact_timestamp(0);
 
+	/* In single user mode, discard all the undo logs, once committed. */
+	if (!IsUnderPostmaster)
+		UndoLogDiscardAll();
+
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
 	s->curTransactionOwner = NULL;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 9e9c257eecf..a29e4f798d3 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5159,6 +5159,7 @@ BootStrapXLOG(void)
 	checkPoint.newestCommitTsXid = InvalidTransactionId;
 	checkPoint.time = (pg_time_t) time(NULL);
 	checkPoint.oldestActiveXid = InvalidTransactionId;
+	checkPoint.oldestFullXidHavingUnappliedUndo = InvalidFullTransactionId;
 
 	ShmemVariableCache->nextFullXid = checkPoint.nextFullXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
@@ -6625,6 +6626,9 @@ StartupXLOG(void)
 			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
 							 checkPoint.oldestCommitTsXid,
 							 checkPoint.newestCommitTsXid)));
+	ereport(DEBUG1,
+			(errmsg_internal("oldest xid with epoch having undo: " UINT64_FORMAT,
+							 U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo))));
 	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid)))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -6641,6 +6645,10 @@ StartupXLOG(void)
 					 checkPoint.newestCommitTsXid);
 	XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 
+	/* Read oldest xid having undo from checkpoint and set in proc global. */
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+		U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 	/*
 	 * Initialize replication slots, before there's a chance to remove
 	 * required resources.
@@ -7329,7 +7337,13 @@ StartupXLOG(void)
 	 * end-of-recovery steps fail.
 	 */
 	if (InRecovery)
+	{
 		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+		ResetUndoLogs(UNDO_UNLOGGED);
+	}
+
+	/* Always reset temporary undo logs. */
+	ResetUndoLogs(UNDO_TEMP);
 
 	/*
 	 * We don't need the latch anymore. It's not strictly necessary to disown
@@ -8726,6 +8740,10 @@ CreateCheckPoint(int flags)
 		checkPoint.nextOid += ShmemVariableCache->oidCount;
 	LWLockRelease(OidGenLock);
 
+	checkPoint.oldestFullXidHavingUnappliedUndo =
+		FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+
+
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
 							 &checkPoint.nextMultiOffset,
@@ -9638,6 +9656,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceOldest(checkPoint.oldestMulti,
 							   checkPoint.oldestMultiDB);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * No need to set oldestClogXid here as well; it'll be set when we
 		 * redo an xl_clog_truncate if it changed since initialization.
@@ -9695,12 +9716,17 @@ xlog_redo(XLogReaderState *record)
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid;
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
 
 		/* Update shared-memory copy of checkpoint XID/epoch */
 		SpinLockAcquire(&XLogCtl->info_lck);
 		XLogCtl->ckptFullXid = checkPoint.nextFullXid;
 		SpinLockRelease(&XLogCtl->info_lck);
 
+		ControlFile->checkPointCopy.oldestFullXidHavingUnappliedUndo =
+			checkPoint.oldestFullXidHavingUnappliedUndo;
+
 		/*
 		 * We should've already switched to the new TLI before replaying this
 		 * record.
@@ -9740,6 +9766,9 @@ xlog_redo(XLogReaderState *record)
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
 
+		pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+			U64FromFullTransactionId(checkPoint.oldestFullXidHavingUnappliedUndo));
+
 		/*
 		 * NB: This may perform multixact truncation when replaying WAL
 		 * generated by an older primary.
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 68696bc81a8..b4e7bab7d40 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
-	   undorequest.o
+OBJS = discardworker.o undoaccess.o undoaction.o undoactionxlog.o undodiscard.o \
+	   undolog.o undorecord.o undorequest.o undoworker.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
index e0caf9efeb6..4d6973ba0e2 100644
--- a/src/backend/access/undo/README.UndoProcessing
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -37,3 +37,80 @@ tables are only accessible in the backend that has created them.  We can't
 postpone applying undo actions for subtransactions as the modifications
 made by aborted subtransaction must not be visible even if the main transaction
 commits.
+
+Undo Requests and Undo workers
+-------------------------------
+To improve the efficiency of the rollbacks, we create three queues and a hash
+table for the rollback requests.  A Xid based priority queue will allow us to
+process the requests of older transactions and help us to move
+oldesdXidHavingUnappliedUndo (this is a xid-horizon below which all the
+transactions are visible) forward.  A size-based queue which will help us to
+perform the rollbacks of larger aborts in a timely fashion, so that we don't get
+stuck while processing them during discard of the logs.  An error queue to hold
+the requests for transactions that failed to apply its undo.  The rollback hash
+table is used to avoid duplicate undo requests by backends and discard worker.
+The table must be able to accommodate all active undo requests.  The undo
+requests must appear in both xid and size requests queues or neither.  As of now,
+we process the requests from these queues in a round-robin fashion to give equal
+priority to all three types of requests.
+
+Note that, if the request queues are full, then we put backpressure on backends
+to complete the requests by themselves.  There is an exception to it where when
+error queue becomes full, we just mark the request as 'invalid' and continue to
+process other requests if any.  The discard worker will find this errored
+transaction at later point of time and again add it to the request queues.
+
+We have the hard limit (proportional to the size of the rollback hash table)
+for the number of transactions that can have pending undo.  This can help us
+in computing the value of oldestXidHavingUnappliedUndo and allowing us not to
+accumulate pending undo for a long time which will eventually block the
+discard of undo.
+
+In a running system, scanning the rollback hash table will give us the value of
+oldestXidHavingUnappliedUndo, however, after startup, we need to once scan all
+the undo logs and populate the rollback hash table.  After startup, we allow
+connections, but don't allow transactions that want to write undo till the
+rollback hash table is initialized.
+
+To process the request, we get the request from one of the queues, search it in
+hash table and mark it as in-progress and then remove from the respective queue.
+After that, we perform the request which means apply the undo actions and
+remove it from the hash table.
+
+To apply the undo actions, we collect the undo records in bulk and try to
+process them together.  We ensure to update the transaction's progress at
+regular intervals so that after a crash we can skip already applied undo.  The
+undo apply progress is updated in terms of the number of blocks processed.
+Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED indicates that all the
+undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED indicates that no undo action
+has been applied yet and any other value indicates that we have applied undo
+partially and after crash recovery, we need to start processing the undo from
+the same location.
+
+Undo launcher is responsible for launching the workers iff there is some work
+available in one of the work queues and there are more workers available.  The
+worker is launched to handle requests for a particular database.  Each undo
+worker then starts reading from one of the queues the requests for that
+particular database.  A worker would peek into each queue for the requests from
+a particular database, if it needs to switch a database in less than
+undo_worker_quantum ms after starting.  Also, if there is no work, it lingers
+for UNDO_WORKER_LINGER_MS.  This avoids restarting the workers too frequently.
+
+Discard Worker
+---------------
+The discard worker is responsible for discarding the undo log of transactions
+that are committed and all-visible or are rolled-back.  It also registers the
+request for aborted transactions in the work queues.  It iterates through all
+the active logs one-by-one and try to discard the transactions that are old
+enough to matter.
+
+For transactions that span across multiple logs, the log for committed and
+all-visible transactions are discarded separately for each log.  This is
+possible as the transactions that span across logs have separate transaction
+header for each log.  For aborted transactions, we try to process the actions
+of the entire transaction at one-shot as we need to perform the actions
+starting from end location to start location.  However, it is possible that the
+later portion of the transaction that is overflowed into a separate log can be
+processed separately if we encounter the corresponding log first.  If we want
+we can combine the log for processing in that case as well, but there is no
+clear advantage of the same.
diff --git a/src/backend/access/undo/discardworker.c b/src/backend/access/undo/discardworker.c
new file mode 100644
index 00000000000..f3246432392
--- /dev/null
+++ b/src/backend/access/undo/discardworker.c
@@ -0,0 +1,215 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.c
+ *	  The undo discard worker for asynchronous undo management.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/discardworker.c
+ *
+ * The main responsibility of the discard worker is to discard the undo log
+ * of transactions that are committed and all-visible or are rolledback.  It
+ * also registers the request for aborted transactions in the work queues.
+ * To know more about work queues, see undorequest.c.  It iterates through all
+ * the active logs one-by-one and try to discard the transactions that are old
+ * enough to matter.
+ *
+ * For tranasctions that spans across multiple logs, the log for committed and
+ * all-visible transactions are discarded seprately for each log.  This is
+ * possible as the transactions that span across logs have separate transaction
+ * header for each log.  For aborted transactions, we try to process the actions
+ * of entire transaction at one-shot as we need to perform the actions starting
+ * from end location to start location.  However, it is possbile that the later
+ * portion of transaction that is overflowed into a separate log can be
+ * processed separately if we encounter the corresponding log first.  If we
+ * want we can combine the log for processing in that case as well, but there
+ * is no clear advantage of the same.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include <unistd.h>
+
+#include "access/discardworker.h"
+#include "access/undodiscard.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+#include "tcop/tcopprot.h"
+#include "utils/guc.h"
+#include "utils/resowner.h"
+
+static void undoworker_sigterm_handler(SIGNAL_ARGS);
+
+/* minimum and maximum sleep time for discard worker */
+#define MIN_NAPTIME_PER_CYCLE 100L
+#define DELAYED_NAPTIME 10 * MIN_NAPTIME_PER_CYCLE
+#define MAX_NAPTIME_PER_CYCLE 100 * MIN_NAPTIME_PER_CYCLE
+
+static volatile sig_atomic_t got_SIGTERM = false;
+static bool hibernate = false;
+static long wait_time = MIN_NAPTIME_PER_CYCLE;
+static bool am_discard_worker = false;
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+undoworker_sigterm_handler(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * DiscardWorkerRegister -- Register a undo discard worker.
+ */
+void
+DiscardWorkerRegister(void)
+{
+	BackgroundWorker bgw;
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "discard worker");
+	sprintf(bgw.bgw_library_name, "postgres");
+	sprintf(bgw.bgw_function_name, "DiscardWorkerMain");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum) 0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * DiscardWorkerMain -- Main loop for the undo discard worker.
+ */
+void
+DiscardWorkerMain(Datum main_arg)
+{
+	ereport(LOG,
+			(errmsg("discard worker started")));
+
+	/* Establish signal handlers. */
+	pqsignal(SIGTERM, undoworker_sigterm_handler);
+	BackgroundWorkerUnblockSignals();
+
+	am_discard_worker = true;
+
+	/* Make it easy to identify our processes. */
+	SetConfigOption("application_name", MyBgworkerEntry->bgw_name,
+					PGC_USERSET, PGC_S_SESSION);
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Scan all the undo logs and intialize the rollback hash table with all
+	 * the pending rollback requests.  This need to be done as a first step
+	 * because only after this the transactions will be allowed to write new
+	 * undo.  See comments atop UndoLogProcess.
+	 */
+	UndoLogProcess();
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		TransactionId OldestXmin,
+					oldestXidHavingUndo;
+		FullTransactionId oldestFullXidHavingUndo;
+		int			rc;
+
+		/*
+		 * It is okay to ignore vacuum transaction here, as we can discard the
+		 * undo of the vacuuming transaction if the transaction is committed.
+		 * We don't need to hold its undo for the visibility purpose.
+		 */
+		OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
+								   PROCARRAY_FLAGS_VACUUM);
+
+		oldestFullXidHavingUndo =
+			FullTransactionIdFromU64(pg_atomic_read_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo));
+		oldestXidHavingUndo = XidFromFullTransactionId(oldestFullXidHavingUndo);
+
+		/*
+		 * Call the discard routine if oldestXidHavingUndo is lagging behind
+		 * OldestXmin.
+		 */
+		if (OldestXmin != InvalidTransactionId &&
+			TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
+		{
+			UndoDiscard(OldestXmin, &hibernate);
+
+			/*
+			 * If we got some undo logs to discard or discarded something,
+			 * then reset the wait_time as we have got work to do.  Note that
+			 * if there are some undologs that cannot be discarded, then above
+			 * condition will remain unsatisfied till oldestXmin remains
+			 * unchanged and the wait_time will not reset in that case.
+			 */
+			if (!hibernate)
+				wait_time = MIN_NAPTIME_PER_CYCLE;
+		}
+
+		/* Wait for more work. */
+		rc = WaitLatch(&MyProc->procLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					   wait_time,
+					   WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(&MyProc->procLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		/*
+		 * Increase the wait_time based on the length of inactivity.  If
+		 * wait_time is within one second, then increment it by 100 ms at a
+		 * time.  Henceforth, increment it one second at a time, till it
+		 * reaches ten seconds.  Never increase the wait_time more than ten
+		 * seconds, it will be too much of waiting otherwise.
+		 */
+		if (rc & WL_TIMEOUT && hibernate)
+		{
+			wait_time += (wait_time < DELAYED_NAPTIME ?
+						  MIN_NAPTIME_PER_CYCLE : DELAYED_NAPTIME);
+			if (wait_time > MAX_NAPTIME_PER_CYCLE)
+				wait_time = MAX_NAPTIME_PER_CYCLE;
+		}
+
+		/* emergency bailout if postmaster has died */
+		if (rc & WL_POSTMASTER_DEATH)
+			proc_exit(1);
+	}
+
+	/* Normal exit from discard worker */
+	ereport(LOG,
+			(errmsg("discard worker shutting down")));
+
+	proc_exit(0);
+}
+
+/*
+ * IsDiscardProcess -- Tells whether the current process is a discard worker
+ *	process.
+ */
+bool
+IsDiscardProcess(void)
+{
+	return am_discard_worker;
+}
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index da06f57ae81..14a1cfc174c 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -66,6 +66,7 @@
 #include "storage/buf.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/proc.h"
 
 /*
  * Structure to hold the prepared undo information.  PreparedUndoInsert will
@@ -103,6 +104,13 @@ static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
 							  RelFileNode rnode, BlockNumber blk,
 							  ReadBufferMode rbm);
 
+/*
+ * Defines the number of times we try to wait for rollback hash table to get
+ * initialized.  After these many attempts it will return error and the user
+ * can retry the operation.
+ */
+#define ROLLBACK_HT_INIT_WAIT_TRY      60
+
 /*
  * Prepare undo record update
  *
@@ -657,6 +665,50 @@ PrepareUndoInsert(UndoRecordInsertContext *context,
 	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
 	PreparedUndoSpace *prepared_undo;
 
+	if (!InRecovery && IsUnderPostmaster)
+	{
+		int try_count = 0;
+
+		/*
+		 * If we are not in a recovery and not in a single-user-mode, then undo
+		 * generation should not be allowed until we have scanned all the undo
+		 * logs and initialized the hash table with all the aborted
+		 * transaction entries.  See detailed comments in UndoLogProcess.
+		 */
+		while (!ProcGlobal->rollbackHTInitialized)
+		{
+			/* Error out after trying for one minute. */
+			if (try_count > ROLLBACK_HT_INIT_WAIT_TRY)
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
+						 errmsg("rollback hash table is not yet initialized, wait for sometime and try again")));
+
+			/*
+			 * Rollback hash table is not yet intialized, sleep for 1 second
+			 * and try again.
+			 */
+			pg_usleep(1000000L);
+			try_count++;
+		}
+	}
+
+	/*
+	 * If the rollback hash table is already full (excluding one additional
+	 * space for each backend) then don't allow to generate any new undo until
+	 * we apply some of the pending requests and create some space in the hash
+	 * table to accept new rollback requests.  Leave the enough slots in the
+	 * hash table so that there is space for all the backends to register at
+	 * least one request.  This is to protect the situation where one backend
+	 * keep consuming slots reserve for the other backends and suddenly there
+	 * is concurrent undo request from all the backends.  So we always keep
+	 * the space reserve for MaxBackends.
+	 */
+	if (ProcGlobal->xactsHavingPendingUndo >
+		(UndoRollbackHashTableSize() - MaxBackends))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("max limit for pending rollback request has reached, wait for sometime and try again")));
+
 	/* Already reached maximum prepared limit. */
 	if (context->nprepared_undo == context->max_prepared_undo)
 		elog(ERROR, "already reached the maximum prepared limit");
diff --git a/src/backend/access/undo/undodiscard.c b/src/backend/access/undo/undodiscard.c
new file mode 100644
index 00000000000..31f42231d27
--- /dev/null
+++ b/src/backend/access/undo/undodiscard.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------------------------
+ *
+ * undodiscard.c
+ *	  discard undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undodiscard.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/undodiscard.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlog_internal.h"
+#include "catalog/pg_tablespace.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/shmem.h"
+
+/*
+ * Discard as many record sets as we can from the undo log occupying a given
+ * slot, considering the given xmin horizon.  If we encounter a record set
+ * that needs to be rolled back, register a rollback request.  Set *hibernate
+ * to false if work was done.
+ */
+static void
+UndoDiscardOneLog(UndoLogSlot *slot, TransactionId xmin, bool *hibernate)
+{
+	UndoRecPtr	undo_recptr;
+	UndoRecPtr	next_urecptr = InvalidUndoRecPtr;
+	bool	need_discard = false;
+	TransactionId	latest_discardxid = InvalidTransactionId;
+	UndoLogNumber logno;
+
+	/*
+	 * Currently we expect only one discard worker to be active at any time,
+	 * but in future we might have more than one, and superuser maintenance
+	 * functions might also discard data concurrently.  So we we have to
+	 * assume that the given slot could be recycled underneath us any time we
+	 * don't hold one of the locks that prevents that.  We'll detect that by
+	 * the log number changing.
+	 */
+	LWLockAcquire(&slot->discard_lock, LW_SHARED);
+	logno = slot->logno;
+	if (UndoRecPtrIsValid(slot->oldest_data))
+	{
+		undo_recptr = slot->oldest_data;
+		LWLockRelease(&slot->discard_lock);
+	}
+	else
+	{
+		LWLockRelease(&slot->discard_lock);
+		undo_recptr = UndoLogGetOldestRecord(logno, NULL);
+	}
+
+	/* There might not be any undo log and hibernation might be needed. */
+	*hibernate = true;
+
+	StartTransactionCommand();
+
+	/* Loop until we run out of discardable transactions in the given log. */
+	do
+	{
+		UnpackedUndoRecord	*uur = NULL;
+		UndoRecPtr	next_insert;
+		FullTransactionId	undofxid = InvalidFullTransactionId;
+		TransactionId wait_xid = InvalidTransactionId;
+		bool pending_abort = false;
+		bool request_rollback = false;
+		UndoStatus status;
+		UndoRecordFetchContext	context;
+
+		next_insert = UndoLogGetNextInsertPtr(logno);
+
+		/* There must be some undo data for a transaction. */
+		Assert(next_insert != undo_recptr);
+
+		/* Fetch the undo record for the given undo_recptr. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, undo_recptr);
+		FinishUndoFetch(&context);
+
+		if (uur != NULL)
+		{
+			if (UndoRecPtrGetCategory(undo_recptr) == UNDO_SHARED)
+			{
+				/*
+				 * For the "shared" category, we only discard when the
+				 * rm_undo_status callback tells us we can.
+				 */
+				status = RmgrTable[uur->uur_rmid].rm_undo_status(uur, &wait_xid);
+
+				Assert((status == UNDO_STATUS_WAIT_XMIN &&
+						TransactionIdIsValid(wait_xid)) ^
+						(status == UNDO_STATUS_DISCARD &&
+						!TransactionIdIsValid(wait_xid)));
+			}
+			else
+			{
+				TransactionId xid = XidFromFullTransactionId(uur->uur_fxid);
+
+				/*
+				 * Otherwise we use the CLOG and xmin to decide whether to
+				 * wait, discard or roll back.
+				 *
+				 * XXX: We've added the transaction-in-progress check to
+				 * avoid xids of in-progress autovacuum as those are not
+				 * computed for oldestxmin calculation.  See
+				 * DiscardWorkerMain.
+				 */
+				if (TransactionIdDidCommit(xid))
+				{
+					/*
+					 * If this record set's xid isn't before the xmin
+					 * horizon, we'll have to wait before we can discard
+					 * it.
+					 */
+					if (TransactionIdFollowsOrEquals(xid, xmin))
+						wait_xid = xid;
+
+				}
+				else if (!TransactionIdIsInProgress(xid))
+				{
+					/*
+					 * If it hasn't been applied already, then we'll ask
+					 * for it to be applied now.  Otherwise it'll be
+					 * discarded.
+					 */
+					if (!IsXactApplyProgressCompleted(uur->uur_group->urec_progress))
+						request_rollback = true;
+				}
+				else
+				{
+					/*
+					 * It's either in progress or isn't yet before the
+					 * xmin horizon, so we'll have to wait.
+					 */
+					wait_xid = XidFromFullTransactionId(uur->uur_fxid);
+				}
+			}
+
+			/*
+			 * Add the aborted transaction to the rollback request queues.
+			 *
+			 * We can ignore the abort for transactions whose corresponding
+			 * database doesn't exist.
+			 */
+			if (request_rollback && dbid_exists(uur->uur_group->urec_dbid))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr,
+										   undo_recptr,
+										   uur->uur_group->urec_dbid,
+										   uur->uur_fxid);
+
+				pending_abort = true;
+			}
+
+			next_urecptr = uur->uur_group->urec_next_group;
+			undofxid = uur->uur_fxid;
+
+			UndoRecordRelease(uur);
+			uur = NULL;
+		}
+
+		/*
+		 * We can discard upto this point when one of following conditions is
+		 * met: (a) we need to wait for a transaction first. (b) there is no
+		 * more log to process. (c) the transaction undo in current log is
+		 * finished. (d) there is a pending abort.
+		 */
+		if (TransactionIdIsValid(wait_xid) ||
+			next_urecptr == InvalidUndoRecPtr ||
+			UndoRecPtrGetLogNo(next_urecptr) != logno ||
+			pending_abort)
+		{
+			/* Hey, I got some undo log to discard, can not hibernate now. */
+			*hibernate = false;
+
+			/*
+			 * If we don't need to wait for this transaction and this is not
+			 * an aborted transaction, then we can discard it as well.
+			 */
+			if (!TransactionIdIsValid(wait_xid) && !pending_abort)
+			{
+				/*
+				 * It is safe to use next_insert as the location till which we
+				 * want to discard in this case.  If something new has been
+				 * added after we have fetched this transaction's record, it
+				 * won't be considered in this pass of discard.
+				 */
+				undo_recptr = next_insert;
+				latest_discardxid = XidFromFullTransactionId(undofxid);
+				need_discard = true;
+
+				/* We don't have anything more to discard. */
+				undofxid = InvalidFullTransactionId;
+			}
+
+			/* Update the shared memory state. */
+			LWLockAcquire(&slot->discard_lock, LW_EXCLUSIVE);
+
+			/*
+			 * If the slot has been recycling while we were thinking about it,
+			 * we have to abandon the operation.
+			 */
+			if (slot->logno != logno)
+			{
+				LWLockRelease(&slot->discard_lock);
+				break;
+			}
+
+			/* Update the slot information for the next pass of discard. */
+			slot->wait_fxmin = undofxid;
+			slot->oldest_data = undo_recptr;
+
+			LWLockRelease(&slot->discard_lock);
+
+			if (need_discard)
+			{
+				LWLockAcquire(&slot->discard_update_lock, LW_EXCLUSIVE);
+				UndoLogDiscard(undo_recptr, latest_discardxid);
+				LWLockRelease(&slot->discard_update_lock);
+			}
+
+			break;
+		}
+
+		/*
+		 * This transaction is smaller than the xmin so lets jump to the next
+		 * transaction.
+		 */
+		undo_recptr = next_urecptr;
+		latest_discardxid = XidFromFullTransactionId(undofxid);
+
+		/* The fetched undo record must be release by now. */
+		Assert(uur == NULL);
+
+		/* If we reach here, this means there is something to discard. */
+		need_discard = true;
+	} while (true);
+
+	CommitTransactionCommand();
+}
+
+/*
+ * Scan all the undo logs and register the aborted transactions.  This is
+ * called as a first function from the discard worker and only after this pass
+ * over undo logs is complete, new undo is allowed to be written in the
+ * system.  This is required because after crash recovery we don't know the
+ * exact number of aborted transactions whose rollback request is pending and
+ * we can not allow new undo request if we already have the request equal to
+ * hash table size.  So before allowing any new transaction to start writing
+ * the undo we need to make sure that we know exact number of pending
+ * requests.
+ */
+void
+UndoLogProcess()
+{
+	UndoLogSlot *slot = NULL;
+
+	/*
+	 * We need to perform this in a transaction because (a) we need resource
+	 * owner to scan the logs and (b) TransactionIdIsInProgress requires us to
+	 * be in transaction.
+	 */
+	StartTransactionCommand();
+
+	/*
+	 * Loop through all the valid undo logs and scan them transaction by
+	 * transaction to find non-commited transactions if any and register them
+	 * in the rollback hash table.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		UndoRecPtr	undo_recptr;
+		UnpackedUndoRecord	*uur = NULL;
+
+		/* We do not execute shared (non-transactional) undo records. */
+		if (slot->meta.category == UNDO_SHARED)
+			continue;
+
+		/* Start scanning the log from the last discard point. */
+		undo_recptr = UndoLogGetOldestRecord(slot->logno, NULL);
+
+		/* Loop until we scan complete log. */
+		while (1)
+		{
+			TransactionId xid;
+			UndoRecordFetchContext	context;
+
+			/* Done with this log. */
+			if (!UndoRecPtrIsValid(undo_recptr))
+				break;
+
+			/* Fetch the undo record for the given undo_recptr. */
+			BeginUndoFetch(&context);
+			uur = UndoFetchRecord(&context, undo_recptr);
+			FinishUndoFetch(&context);
+
+			Assert(uur != NULL);
+
+			xid = XidFromFullTransactionId(uur->uur_fxid);
+
+			/*
+			 * Register the rollback request for all uncommitted and not in
+			 * progress transactions whose undo apply progress is still not
+			 * completed.  Even though we don't allow any new transactions to
+			 * write undo until this first pass is completed, there might be
+			 * some prepared transactions which are still in progress, so we
+			 * don't include such transactions.
+			 */
+			if (!TransactionIdDidCommit(xid) &&
+				!TransactionIdIsInProgress(xid) &&
+				!IsXactApplyProgressCompleted(uur->uur_group->urec_progress))
+			{
+				(void) RegisterUndoRequest(InvalidUndoRecPtr, undo_recptr,
+										   uur->uur_group->urec_dbid,
+										   uur->uur_fxid);
+			}
+
+			/*
+			 * Go to the next transaction in the same log.  If uur_next is
+			 * point to the undo record pointer in the different log then we are
+			 * done with this log so just set undo_recptr to InvalidUndoRecPtr.
+			 */
+			if (UndoRecPtrGetLogNo(undo_recptr) ==
+				UndoRecPtrGetLogNo(uur->uur_group->urec_next_group))
+				undo_recptr = uur->uur_group->urec_next_group;
+			else
+				undo_recptr = InvalidUndoRecPtr;
+
+			/* Release memory for the current record. */
+			UndoRecordRelease(uur);
+		}
+	}
+
+	CommitTransactionCommand();
+
+	/* Allow the transactions to start writting undo. */
+	ProcGlobal->rollbackHTInitialized = true;
+}
+
+/*
+ * Discard the undo for all the transactions whose xid is smaller than
+ * oldestXmin
+ */
+void
+UndoDiscard(TransactionId oldestXmin, bool *hibernate)
+{
+	FullTransactionId oldestXidHavingUndo;
+	UndoLogSlot *slot = NULL;
+	uint32	epoch;
+
+	/*
+	 * If all the undo logs are discarded, then oldestXidHavingUndo should be
+	 * oldestXmin.  As of now, we don't allow more than 2 billion xids in the
+	 * system, so we can rely on the epoch retrieved with GetEpochForXid.
+	 */
+	epoch = GetEpochForXid(oldestXmin);
+	oldestXidHavingUndo = FullTransactionIdFromEpochAndXid(epoch, oldestXmin);
+
+	/*
+	 * Iterate through all the active logs and one-by-one try to discard the
+	 * transactions that are old enough to matter.
+	 *
+	 * XXX Ideally we can arrange undo logs so that we can efficiently find
+	 * those with oldest_xid < oldestXmin, but for now we'll just scan all of
+	 * them.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/*
+		 * If the log is already discarded, then we are done.  It is important
+		 * to first check this to ensure that tablespace containing this log
+		 * doesn't get dropped concurrently.
+		 *
+		 * We don't have to worry about slot recycling and check the logno
+		 * here, since we don't care about the identity of this slot, we're
+		 * visiting all of them.
+		 */
+		LWLockAcquire(&slot->mutex, LW_SHARED);
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+		{
+			LWLockRelease(&slot->mutex);
+			continue;
+		}
+		LWLockRelease(&slot->mutex);
+
+		/* We can't process temporary undo logs. */
+		if (slot->meta.category == UNDO_TEMP)
+			continue;
+
+		/*
+		 * If the first xid of the undo log is smaller than the xmin then try
+		 * to discard the undo log.
+		 */
+		if (!FullTransactionIdIsValid(slot->wait_fxmin) ||
+			FullTransactionIdPrecedes(slot->wait_fxmin, oldestXidHavingUndo))
+		{
+			/* Process the undo log. */
+			UndoDiscardOneLog(slot, oldestXmin, hibernate);
+		}
+	}
+
+	/* Get the smallest of 'xid having pending undo' and 'oldestXmin' */
+	oldestXidHavingUndo = RollbackHTGetOldestFullXid(oldestXidHavingUndo);
+
+	/*
+	 * Update the oldestFullXidHavingUnappliedUndo in the shared memory.
+	 *
+	 * XXX: In future, if multiple workers can perform discard then we may
+	 * need to use compare and swap for updating the shared memory value.
+	 */
+	Assert(FullTransactionIdIsValid(oldestXidHavingUndo));
+	pg_atomic_write_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo,
+						U64FromFullTransactionId(oldestXidHavingUndo));
+}
+
+/*
+ * Discard all the logs.  This is particularly required in single user mode
+ * where at the commit time we discard all the undo logs.
+ */
+void
+UndoLogDiscardAll(void)
+{
+	UndoLogSlot *slot = NULL;
+
+	Assert(!IsUnderPostmaster);
+
+	/*
+	 * No locks are required for discard, since this called only in single
+	 * user mode.
+	 */
+	while ((slot = UndoLogNextSlot(slot)))
+	{
+		/* If the log is already discarded, then we are done. */
+		if (slot->meta.discard == slot->meta.unlogged.insert)
+			continue;
+
+		/*
+		 * Process the undo log.
+		 */
+		UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+					   InvalidTransactionId);
+	}
+
+}
+
+/*
+ * Discard the undo logs for temp tables.
+ */
+void
+TempUndoDiscard(UndoLogNumber logno)
+{
+	UndoLogSlot *slot = UndoLogGetSlot(logno, false);
+
+	/*
+	 * Discard the undo log for temp table only. Ensure that there is
+	 * something to be discarded there.
+	 */
+	Assert (slot->meta.category == UNDO_TEMP);
+
+	/*
+	 * If the log is already discarded, then we are done.  It is important
+	 * to first check this to ensure that tablespace containing this log
+	 * doesn't get dropped concurrently.
+	 */
+	LWLockAcquire(&slot->mutex, LW_SHARED);
+	if (slot->meta.discard == slot->meta.unlogged.insert)
+	{
+		LWLockRelease(&slot->mutex);
+		return;
+	}
+	LWLockRelease(&slot->mutex);
+
+	/* Process the undo log. */
+	UndoLogDiscard(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert),
+				   InvalidTransactionId);
+}
diff --git a/src/backend/access/undo/undorequest.c b/src/backend/access/undo/undorequest.c
index 17e7c25b6cc..8c110e8f968 100644
--- a/src/backend/access/undo/undorequest.c
+++ b/src/backend/access/undo/undorequest.c
@@ -54,10 +54,12 @@
 #include "postgres.h"
 #include "miscadmin.h"
 
+#include "access/discardworker.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_database.h"
@@ -880,9 +882,6 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 		 */
 		if (UndoRecPtrIsDiscarded(next_urecptr))
 		{
-			UndoLogOffset next_insert;
-
-			next_insert = UndoLogGetNextInsertPtr(slot->logno);
 			Assert(UndoRecPtrIsValid(next_insert));
 
 			last_log_start_urecptr = urecptr;
@@ -898,9 +897,6 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 		 * this log for request size computation.
 		 */
 		{
-			UndoLogOffset next_insert;
-
-			next_insert = UndoLogGetNextInsertPtr(slot->logno);
 			Assert(UndoRecPtrIsValid(next_insert));
 
 			last_log_start_urecptr = urecptr;
@@ -927,6 +923,138 @@ FindUndoEndLocationAndSize(UndoRecPtr start_urecptr,
 	return sz;
 }
 
+/*
+ * Fetch the start urec pointer for the transaction and the undo request size.
+ *
+ * start_urecptr_inout - This is an INOUT parameter.  If a transaction has
+ * overflowed to multiple undo logs, the caller can set start_urecptr_inout
+ * to a location of any of the undo logs where the transaction has written its
+ * first record for that particular log.  Given that, this function calculates
+ * the undo location where the transaction has inserted its first undo record.
+ * If a transaction hasn't overflowed to multiple undo logs, the value of this
+ * parameter remains unchanged.
+ *
+ * The first record of a transaction in each undo log contains a reference to
+ * the first record of this transaction in the previous log.  It finds the
+ * initial location by moving backward in the undo chain of this transaction
+ * across undo logs.  While doing the same, it also calculates the undo size
+ * between the input and output start undo record pointer value.
+ */
+static uint64
+FindUndoStartLocationAndSize(UndoRecPtr *start_urecptr_inout,
+							 FullTransactionId full_xid)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoLogSlot *slot = NULL;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	uint64		sz = 0;
+
+	Assert(start_urecptr_inout);
+
+	urecptr = *start_urecptr_inout;
+	Assert(urecptr != InvalidUndoRecPtr);
+
+	/*
+	 * A backend always set the start undo record pointer to the first undo
+	 * record inserted by this transaction.  Hence, we don't have to proceed
+	 * further.
+	 */
+	if (!IsDiscardProcess())
+		return sz;
+
+	/*
+	 * Since the discard worker processes the undo logs sequentially, it's
+	 * possible that start undo record pointer doesn't refer to the actual
+	 * start of the transaction.  Instead, it may refer to the start location
+	 * of the transaction in any of the subsequent logs.  In that case, we've
+	 * to find the actual start location of the transaction by going backwards
+	 * in the chain.
+	 */
+	while (true)
+	{
+		UndoLogOffset next_insert;
+		UndoRecordFetchContext	context;
+
+		/*
+		 * Fetch the log and undo record corresponding to the current undo
+		 * pointer.
+		 */
+		if ((slot == NULL) || (UndoRecPtrGetLogNo(urecptr) != slot->logno))
+			slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		Assert(slot != NULL);
+
+		/* The corresponding log must be ahead urecptr. */
+		Assert(MakeUndoRecPtr(slot->logno, slot->meta.unlogged.insert) >= urecptr);
+
+		/* Fetch the undo record. */
+		BeginUndoFetch(&context);
+		uur = UndoFetchRecord(&context, urecptr);
+		FinishUndoFetch(&context);
+
+		/*
+		 * Since the rollback isn't completed for this transaction, this undo
+		 * record can't be discarded.
+		 */
+		Assert (uur != NULL);
+
+		/* The undo must belongs to a same transaction. */
+		Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+		/*
+		 * Since this is the first undo record of this transaction in this
+		 * log, this must include the transaction header.
+		 */
+		Assert(uur->uur_group != NULL);
+
+		/*
+		 * If this is the first undo record of this transaction, return from
+		 * here.
+		 */
+		if ((uur->uur_info & UREC_INFO_LOGSWITCH) == 0)
+		{
+			UndoRecordRelease(uur);
+			break;
+		}
+
+		/*
+		 * This is a start of a overflowed transaction header, so it must have
+		 * a valid pointer to previous log's start transaction header.
+		 */
+		Assert(UndoRecPtrIsValid(uur->uur_logswitch->urec_prevlogstart));
+
+
+		/*
+		 * Find the previous log from which the transaction is overflowed
+		 * to current log.
+		 */
+		urecptr = uur->uur_logswitch->urec_prevlogstart;
+		slot = UndoLogGetSlot(UndoRecPtrGetLogNo(urecptr), false);
+
+		/*
+		 * When a transaction overflows to a new undo log, it's guaranteed
+		 * that this transaction will be the last transaction in the previous
+		 * log and we mark that log as full so that no other transaction can
+		 * write in that log further.  Check UndoLogAllocate for details.
+		 *
+		 * So, to find the undo size in the previous log, we've to find the
+		 * next insert location of the previous log and subtract current
+		 * transaction's start location in the previous log from it.
+		 */
+		next_insert = UndoLogGetNextInsertPtr(slot->logno);
+		Assert(UndoRecPtrIsValid(next_insert));
+
+		sz += (next_insert - urecptr);
+
+		UndoRecordRelease(uur);
+		uur = NULL;
+	}
+
+	*start_urecptr_inout = urecptr;
+
+	return sz;
+}
+
 /*
  * Returns true, if we can push the rollback request to undo wrokers, false,
  * otherwise.
@@ -943,9 +1071,24 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 
 	/*
 	 * We normally push the rollback request to undo workers if the size of
-	 * same is above a certain threshold.
+	 * same is above a certain threshold.  However, discard worker is allowed
+	 * to push any size request provided there is a space in rollback request
+	 * queue.  This is mainly because discard worker can be processing the
+	 * rollback requests after crash recovery when no backend is alive.
+	 *
+	 * We have a race condition where discard worker can process the request
+	 * before the backend which has aborted the transaction in which case
+	 * backend won't do anything.  Normally, this won't happen because
+	 * backends try to apply the undo actions immediately after marking the
+	 * transaction as aborted in the clog.  One way to avoid this race
+	 * condition is that we register the request by backend in hash table but
+	 * not in rollback queues before marking abort in clog and then later add
+	 * them in rollback queues.  However, we are not sure how important it is
+	 * avoid such a race as this won't lead to any problem and OTOH, we might
+	 * need some more trickery in the code to avoid such a race condition.
 	 */
-	if (req_size >= rollback_overflow_size * 1024 * 1024)
+	if (req_size >= rollback_overflow_size * 1024 * 1024 ||
+		IsDiscardProcess())
 	{
 		if (GetXidQueueSize() >= pending_undo_queue_size ||
 			GetSizeQueueSize() >= pending_undo_queue_size)
@@ -971,12 +1114,7 @@ CanPushReqToUndoWorker(UndoRecPtr start_urec_ptr, UndoRecPtr end_urec_ptr,
 		if ((GetXidQueueSize() < pending_undo_queue_size))
 		{
 			Assert(GetSizeQueueSize() < pending_undo_queue_size);
-
-			/*
-			 * XXX - Here, we should return true once we have background
-			 * worker facility.
-			 */
-			return false;
+			return true;
 		}
 	}
 
@@ -1416,6 +1554,12 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	Assert(UndoRecPtrIsValid(start_urec_ptr));
 	Assert(dbid != InvalidOid);
 
+	/*
+	 * The discard worker can only send the start undo record pointer of a
+	 * transaction.  It doesn't set the end_urec_ptr.
+	 */
+	Assert(IsDiscardProcess() || UndoRecPtrIsValid(end_urec_ptr));
+
 	/*
 	 * Find the rollback request size and the end_urec_ptr (in case of discard
 	 * worker only).
@@ -1431,6 +1575,14 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 	if (!UndoRecPtrIsValid(end_urec_ptr))
 		return false;
 
+	/*
+	 * For registering a rollback request, we always store the full transaction
+	 * ID and the first undo record pointer inserted by this transaction.  This
+	 * ensures that backends and discard worker don't register the same request
+	 * twice.
+	 */
+	req_size += FindUndoStartLocationAndSize(&start_urec_ptr, full_xid);
+
 	LWLockAcquire(RollbackRequestLock, LW_EXCLUSIVE);
 
 	/*
@@ -1446,7 +1598,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 										   HASH_ENTER_NULL, &found);
 
 	/*
-	 * It can only fail, if the value of pending_undo_queue_size or
+	 * Except the first pass over the undo logs by discard worker, the hash
+	 * table can never be full.
+	 */
+	Assert(!ProcGlobal->rollbackHTInitialized || (rh != NULL));
+
+	/*
+	 * It can only fail, if  the value of pending_undo_queue_size or
 	 * max_connections guc is reduced after restart of the server.
 	 */
 	if (rh == NULL)
@@ -1494,10 +1652,16 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 		}
 		/*
 		 * The request can't be pushed into the undo worker queue.  The
-		 * backends will try executing by itself.
+		 * backends will try executing by itself.  The discard worker will
+		 * keep the entry into the rollback hash table with
+		 * UNDO_REQUEST_INVALID status.  Such requests will be added in the
+		 * undo worker queues in the subsequent passes over undo logs by
+		 * discard worker.
 		 */
-		else
+		else if (!IsDiscardProcess())
 			rh->status = UNDO_REQUEST_INPROGRESS;
+		else
+			rh->status = UNDO_REQUEST_INVALID;
 	}
 	else if (!UndoRequestIsValid(rh) && can_push)
 	{
@@ -1525,6 +1689,13 @@ RegisterUndoRequest(UndoRecPtr end_urec_ptr, UndoRecPtr start_urec_ptr,
 
 	LWLockRelease(RollbackRequestLock);
 
+	/*
+	 * If we are able to successfully push the request, wakeup the undo worker
+	 * so that it can be processed in a timely fashion.
+	 */
+	if (pushed)
+		WakeupUndoWorker(dbid);
+
 	return pushed;
 }
 
diff --git a/src/backend/access/undo/undoworker.c b/src/backend/access/undo/undoworker.c
new file mode 100644
index 00000000000..374144514f8
--- /dev/null
+++ b/src/backend/access/undo/undoworker.c
@@ -0,0 +1,841 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.c
+ *	  undo launcher and undo worker process.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/undoworker.c
+ *
+ * Undo launcher is responsible for launching the workers iff there is some
+ * work available in one of work queues and there are more workers available.
+ * To know more about work queues, see undorequest.c.  The worker is launched
+ * to handle requests for a particular database.
+ *
+ * Each undo worker then starts reading from one of the queues the requests
+ * for that particular database.  A worker would peek into each queue for the
+ * requests from a particular database, if it needs to switch a database in
+ * less than undo_worker_quantum ms after starting.  Also, if there is no
+ * work, it lingers for UNDO_WORKER_LINGER_MS.  This avoids restarting
+ * the workers too frequently.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/table.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
+#include "access/xact.h"
+#include "funcapi.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "postmaster/fork_process.h"
+#include "postmaster/postmaster.h"
+#include "replication/slot.h"
+#include "replication/worker_internal.h"
+#include "storage/ipc.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+#include "tcop/tcopprot.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+/*
+ * GUC parameters
+ */
+int			max_undo_workers = 4;
+
+/*
+ * If a worker would need to switch databases in less than undo_worker_quantum
+ * (10s as default) after starting, it peeks a few entries deep into each
+ * queue to see whether there's work for that database.
+ */
+int			undo_worker_quantum_ms = 10000;
+
+/* max sleep time between cycles (100 milliseconds) */
+#define DEFAULT_NAPTIME_PER_CYCLE 100L
+
+/*
+ * Time for which undo worker can linger if there is no work, in
+ * milliseconds.  This has to be more than UNDO_FAILURE_RETRY_DELAY_MS,
+ * otherwise, worker can exit before retrying the failed requests.
+ */
+#define UNDO_WORKER_LINGER_MS 20000
+
+/* Flags set by signal handlers */
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t got_SIGTERM = false;
+
+static TimestampTz last_xact_processed_at;
+
+typedef struct UndoApplyWorker
+{
+	/* Indicates if this slot is used or free. */
+	bool		in_use;
+
+	/* Increased every time the slot is taken by new worker. */
+	uint16		generation;
+
+	/* Pointer to proc array. NULL if not running. */
+	PGPROC	   *proc;
+
+	/* Database id this worker is connected to. */
+	Oid			dbid;
+
+	/* this tells whether worker is lingering. */
+	bool		lingering;
+
+	/*
+	 * This tells the undo worker from which undo worker queue it should start
+	 * processing.
+	 */
+	UndoWorkerQueueType undo_worker_queue;
+} UndoApplyWorker;
+
+UndoApplyWorker *MyUndoWorker = NULL;
+
+typedef struct UndoApplyCtxStruct
+{
+	/* Supervisor process. */
+	pid_t		launcher_pid;
+
+	/* latch to wake up undo launcher. */
+	Latch	   *undo_launcher_latch;
+
+	/* Background workers. */
+	UndoApplyWorker workers[FLEXIBLE_ARRAY_MEMBER];
+} UndoApplyCtxStruct;
+
+UndoApplyCtxStruct *UndoApplyCtx;
+
+static void UndoWorkerOnExit(int code, Datum arg);
+static void UndoWorkerCleanup(UndoApplyWorker *worker);
+static void UndoWorkerSetLingering(bool sleep);
+static void UndoWorkerGetRequestInfo(UndoRequestInfo *urinfo);
+static void UndoworkerSigtermHandler(SIGNAL_ARGS);
+
+/*
+ * Cleanup function for undo worker launcher.
+ *
+ * Called on undo worker launcher exit.
+ */
+static void
+UndoLauncherOnExit(int code, Datum arg)
+{
+	UndoApplyCtx->launcher_pid = 0;
+	UndoApplyCtx->undo_launcher_latch = NULL;
+}
+
+/* SIGTERM: set flag to exit at next convenient time */
+static void
+UndoworkerSigtermHandler(SIGNAL_ARGS)
+{
+	got_SIGTERM = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+}
+
+/* SIGHUP: set flag to reload configuration at next convenient time */
+static void
+UndoLauncherSighup(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_SIGHUP = true;
+
+	/* Waken anything waiting on the process latch */
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * Wait for a background worker to start up and attach to the shmem context.
+ *
+ * This is only needed for cleaning up the shared memory in case the worker
+ * fails to attach.
+ */
+static void
+WaitForUndoWorkerAttach(UndoApplyWorker * worker,
+						uint16 generation,
+						BackgroundWorkerHandle *handle)
+{
+	BgwHandleStatus status;
+	int			rc;
+
+	for (;;)
+	{
+		pid_t		pid;
+
+		CHECK_FOR_INTERRUPTS();
+
+		LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+		/* Worker either died or has started; no need to do anything. */
+		if (!worker->in_use || worker->proc)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		LWLockRelease(UndoWorkerLock);
+
+		/* Check if worker has died before attaching, and clean up after it. */
+		status = GetBackgroundWorkerPid(handle, &pid);
+
+		if (status == BGWH_STOPPED)
+		{
+			LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+			/* Ensure that this was indeed the worker we waited for. */
+			if (generation == worker->generation)
+				UndoWorkerCleanup(worker);
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+
+		/*
+		 * We need timeout because we generally don't get notified via latch
+		 * about the worker attach.  But we don't expect to have to wait long.
+		 */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+
+		if (rc & WL_LATCH_SET)
+			ResetLatch(MyLatch);
+	}
+
+	return;
+}
+
+/*
+ * Attach to a slot.
+ */
+static void
+UndoWorkerAttach(int slot)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	MyUndoWorker = &UndoApplyCtx->workers[slot];
+
+	if (!MyUndoWorker->in_use)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("could not attach to undo worker slot"),
+				 errdetail("slot %d is empty", slot)));
+	}
+
+	if (MyUndoWorker->proc)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("could not attach to undo worker slot"),
+				 errdetail("slot %d is already used by another worker",
+						   slot)));
+	}
+
+	MyUndoWorker->proc = MyProc;
+	before_shmem_exit(UndoWorkerOnExit, (Datum) 0);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Returns whether an undo worker is available.
+ */
+static bool
+UndoWorkerIsAvailable(void)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	/* Search for attached workers. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (!w->in_use)
+		{
+			LWLockRelease(UndoWorkerLock);
+			return true;
+		}
+	}
+
+	LWLockRelease(UndoWorkerLock);
+
+	return false;
+}
+
+/* Sets the worker's lingering status. */
+static void
+UndoWorkerSetLingering(bool sleep)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	MyUndoWorker->lingering = sleep;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/* Get the dbid and undo worker queue set by the undo launcher. */
+static void
+UndoWorkerGetRequestInfo(UndoRequestInfo *urinfo)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_SHARED);
+
+	Assert(MyUndoWorker->in_use);
+
+	urinfo->dbid = MyUndoWorker->dbid;
+	urinfo->undo_worker_queue = MyUndoWorker->undo_worker_queue;
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Start new undo apply background worker, if possible.
+ */
+static void
+UndoWorkerLaunch(UndoRequestInfo *urinfo)
+{
+	BackgroundWorker bgw;
+	BackgroundWorkerHandle *bgw_handle;
+	uint16		generation;
+	int			slot = 0;
+	UndoApplyWorker *worker = NULL;
+
+	/*
+	 * We need to do the modification of the shared memory under lock to have
+	 * a consistent view.
+	 */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* Find unused worker slot. */
+	for (slot = 0; slot < max_undo_workers; slot++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[slot];
+
+		if (!w->in_use)
+		{
+			worker = w;
+			break;
+		}
+	}
+
+	/*
+	 * If there are no more free worker slots, inform user about it before
+	 * exiting.
+	 */
+	if (worker == NULL)
+	{
+		LWLockRelease(UndoWorkerLock);
+		ereport(WARNING,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("out of undo worker slots"),
+				 errhint("You might need to increase max_undo_workers.")));
+		return;
+	}
+
+	/* Prepare the worker slot. */
+	worker->in_use = true;
+	worker->proc = NULL;
+	worker->dbid = urinfo->dbid;
+	worker->lingering = false;
+	worker->undo_worker_queue = urinfo->undo_worker_queue;
+	worker->generation++;
+
+	generation = worker->generation;
+	LWLockRelease(UndoWorkerLock);
+
+	/* Register the new dynamic worker. */
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoWorkerMain");
+	snprintf(bgw.bgw_type, BGW_MAXLEN, "undo apply worker");
+	snprintf(bgw.bgw_name, BGW_MAXLEN, "undo apply worker for database id %d",
+			 worker->dbid);
+
+	bgw.bgw_restart_time = BGW_NEVER_RESTART;
+	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_main_arg = Int32GetDatum(slot);
+
+	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
+	{
+		/* Failed to start worker, so clean up the worker slot. */
+		LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+		UndoWorkerCleanup(worker);
+		LWLockRelease(UndoWorkerLock);
+
+		ereport(WARNING,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("out of undo worker slots"),
+				 errhint("You might need to increase max_undo_workers.")));
+
+		return;
+	}
+
+	/* Now wait until it attaches. */
+	WaitForUndoWorkerAttach(worker, generation, bgw_handle);
+
+	return;
+}
+
+/*
+ * Detach the worker (cleans up the worker info).
+ */
+static void
+UndoWorkerDetach(void)
+{
+	/* Block concurrent access. */
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	UndoWorkerCleanup(MyUndoWorker);
+
+	LWLockRelease(UndoWorkerLock);
+}
+
+/*
+ * Clean up worker info.
+ */
+static void
+UndoWorkerCleanup(UndoApplyWorker *worker)
+{
+	Assert(LWLockHeldByMeInMode(UndoWorkerLock, LW_EXCLUSIVE));
+
+	worker->in_use = false;
+	worker->proc = NULL;
+	worker->dbid = InvalidOid;
+	worker->lingering = false;
+	worker->undo_worker_queue = InvalidUndoWorkerQueue;
+}
+
+/*
+ * Cleanup function.
+ *
+ * Called on undo worker exit.
+ */
+static void
+UndoWorkerOnExit(int code, Datum arg)
+{
+	UndoWorkerDetach();
+}
+
+/*
+ * Perform rollback request.  We need to connect to the database for first
+ * request.  This is required because we access system tables while performing
+ * undo actions.
+ */
+static void
+UndoWorkerPerformRequest(UndoRequestInfo *urinfo)
+{
+	bool error = false;
+
+	/* must be connected to the database. */
+	Assert(MyDatabaseId != InvalidOid);
+
+	StartTransactionCommand();
+	PG_TRY();
+	{
+		execute_undo_actions(urinfo->full_xid, urinfo->end_urec_ptr,
+							 urinfo->start_urec_ptr, true);
+	}
+	PG_CATCH();
+	{
+		error = true;
+
+		/*
+		 * Register the unprocessed request in an error queue, so that it can
+		 * be processed in a timely fashion.  If we fail to add the request in
+		 * an error queue, then mark the entry status invalid.  This request
+		 * will be later added back to the queue by the discard worker.
+		 */
+		if (!InsertRequestIntoErrorUndoQueue(urinfo))
+			RollbackHTMarkEntryInvalid(urinfo->full_xid,
+									   urinfo->start_urec_ptr);
+
+		/* Prevent interrupts while cleaning up. */
+		HOLD_INTERRUPTS();
+
+		/* Send the error only to server log. */
+		err_out_to_client(false);
+		EmitErrorReport();
+
+		/*
+		 * Abort the transaction and continue processing pending undo requests.
+		 */
+		AbortOutOfAnyTransaction();
+		FlushErrorState();
+
+		RESUME_INTERRUPTS();
+	}
+	PG_END_TRY();
+
+	if (!error)
+		CommitTransactionCommand();
+}
+
+/*
+ * UndoLauncherShmemSize
+ *		Compute space needed for undo launcher shared memory
+ */
+Size
+UndoLauncherShmemSize(void)
+{
+	Size		size;
+
+	/*
+	 * Need the fixed struct and the array of UndoWorker.
+	 */
+	size = sizeof(UndoApplyCtxStruct);
+	size = MAXALIGN(size);
+	size = add_size(size, mul_size(max_undo_workers,
+								   sizeof(UndoApplyWorker)));
+	return size;
+}
+
+/*
+ * UndoLauncherShmemInit
+ *		Allocate and initialize undo worker launcher shared memory
+ */
+void
+UndoLauncherShmemInit(void)
+{
+	bool		found;
+
+	UndoApplyCtx = (UndoApplyCtxStruct *)
+		ShmemInitStruct("Undo Worker Launcher Data",
+						UndoLauncherShmemSize(),
+						&found);
+
+	if (!found)
+	{
+		int		slot;
+
+		memset(UndoApplyCtx, 0, UndoLauncherShmemSize());
+
+		/* Initialize memory for each worker slot. */
+		for (slot = 0; slot < max_undo_workers; slot++)
+		{
+			UndoApplyWorker *worker = &UndoApplyCtx->workers[slot];
+
+			memset(worker, 0, sizeof(UndoApplyWorker));
+		}
+	}
+}
+
+/*
+ * UndoLauncherRegister
+ *		Register a background worker running the undo worker launcher.
+ */
+void
+UndoLauncherRegister(void)
+{
+	BackgroundWorker bgw;
+
+	if (max_undo_workers == 0)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+				 errmsg("cannot start undo launcher when max_undo_worker = 0")));
+		return;
+	}
+
+	memset(&bgw, 0, sizeof(bgw));
+	bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
+		BGWORKER_BACKEND_DATABASE_CONNECTION;
+	bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
+	snprintf(bgw.bgw_function_name, BGW_MAXLEN, "UndoLauncherMain");
+	snprintf(bgw.bgw_name, BGW_MAXLEN,
+			 "undo worker launcher");
+	snprintf(bgw.bgw_type, BGW_MAXLEN,
+			 "undo worker launcher");
+	bgw.bgw_restart_time = 5;
+	bgw.bgw_notify_pid = 0;
+	bgw.bgw_main_arg = (Datum)0;
+
+	RegisterBackgroundWorker(&bgw);
+}
+
+/*
+ * Main loop for the undo worker launcher process.
+ */
+void
+UndoLauncherMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+
+	ereport(DEBUG1,
+			(errmsg("undo launcher started")));
+
+	before_shmem_exit(UndoLauncherOnExit, (Datum) 0);
+
+	Assert(UndoApplyCtx->launcher_pid == 0);
+	UndoApplyCtx->launcher_pid = MyProcPid;
+
+	/* Establish signal handlers. */
+	pqsignal(SIGHUP, UndoLauncherSighup);
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Establish connection to nailed catalogs. */
+	BackgroundWorkerInitializeConnection(NULL, NULL, 0);
+
+	/*
+	 * Advertise our latch that undo request enqueuer can use to wake us up
+	 * while we're sleeping.
+	 */
+	UndoApplyCtx->undo_launcher_latch = &MyProc->procLatch;
+
+	/* Enter main loop */
+	while (!got_SIGTERM)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		ResetUndoRequestInfo(&urinfo);
+
+		/*
+		 * Here, we have a race condition, which is that even though we have
+		 * checked the availablity of worker before launching it, there is
+		 * still a chance that we don't get worker by the time it tries to
+		 * launch.  We can avoid that race by doing it in lock, but it doesn't
+		 * see worth as this is just a corner case.
+		 */
+		if (UndoGetWork(false, false, &urinfo, NULL) &&
+			UndoWorkerIsAvailable())
+			UndoWorkerLaunch(&urinfo);
+
+		/* Wait for more work. */
+		rc = WaitLatch(MyLatch,
+					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+					   DEFAULT_NAPTIME_PER_CYCLE,
+					   WAIT_EVENT_UNDO_LAUNCHER_MAIN);
+
+		if (rc & WL_LATCH_SET)
+		{
+			ResetLatch(MyLatch);
+			CHECK_FOR_INTERRUPTS();
+		}
+
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+	}
+
+	/* Normal exit from undo launcher main */
+	ereport(LOG,
+			(errmsg("undo launcher shutting down")));
+	proc_exit(0);
+}
+
+/*
+ * UndoWorkerMain -- Main loop for the undo apply worker.
+ */
+void
+UndoWorkerMain(Datum main_arg)
+{
+	UndoRequestInfo urinfo;
+	int			worker_slot = DatumGetInt32(main_arg);
+	bool		in_other_db;
+	bool		found_work;
+	TimestampTz started_at;
+
+	/* Setup signal handling */
+	pqsignal(SIGTERM, UndoworkerSigtermHandler);
+	BackgroundWorkerUnblockSignals();
+
+	/* Attach to slot */
+	UndoWorkerAttach(worker_slot);
+
+	ResetUndoRequestInfo(&urinfo);
+	started_at = GetCurrentTimestamp();
+
+	/*
+	 * Get the dbid where the worker should connect to and get the worker
+	 * request queue from which the worker should start looking for an undo
+	 * request.
+	 */
+	UndoWorkerGetRequestInfo(&urinfo);
+
+	/* Connect to the requested database. */
+	BackgroundWorkerInitializeConnectionByOid(urinfo.dbid, 0, 0);
+
+	/*
+	 * Set the undo worker request queue from which the undo worker should
+	 * start looking for work.
+	 */
+	SetUndoWorkerQueueStart(urinfo.undo_worker_queue);
+
+	/*
+	 * Before attaching the worker, fetch and remove the undo request for
+	 * which the undo launcher has launched this worker.  This restricts the
+	 * undo launcher from launching multiple workers for the same request.
+	 * But, it's possible that the undo request has already been processed by
+	 * other in-progress undo worker.  In that case, we enter the undo worker
+	 * main loop and fetch the next request.
+	 */
+	found_work = UndoGetWork(false, true, &urinfo, &in_other_db);
+
+	if (found_work && !in_other_db)
+	{
+		/* We must have got the pending undo request. */
+		Assert(FullTransactionIdIsValid(urinfo.full_xid));
+		UndoWorkerPerformRequest(&urinfo);
+		last_xact_processed_at = GetCurrentTimestamp();
+	}
+
+	while (!got_SIGTERM)
+	{
+		int			rc;
+		bool		allow_peek;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * We don't want to restart workers frequently, so allow to peek few
+		 * entries ahead, if required.
+		 */
+		allow_peek = !TimestampDifferenceExceeds(started_at,
+												 GetCurrentTimestamp(),
+												 undo_worker_quantum_ms);
+
+		found_work = UndoGetWork(allow_peek, true, &urinfo, &in_other_db);
+
+		if (found_work && in_other_db)
+		{
+			proc_exit(0);
+		}
+		else if (found_work)
+		{
+			/* We must have got the pending undo request. */
+			Assert(FullTransactionIdIsValid(urinfo.full_xid));
+			UndoWorkerPerformRequest(&urinfo);
+			last_xact_processed_at = GetCurrentTimestamp();
+		}
+		else
+		{
+			TimestampTz timeout = 0;
+
+			timeout = TimestampTzPlusMilliseconds(last_xact_processed_at,
+												  UNDO_WORKER_LINGER_MS);
+
+			/*
+			 * We don't need to linger if we have already spent
+			 * UNDO_WORKER_LINGER_MS since last transaction has processed.
+			 */
+			if (timeout <= GetCurrentTimestamp())
+			{
+				proc_exit(0);
+			}
+
+			/*
+			 * Update the shared state to reflect that this worker is
+			 * lingering so that if there is new work request, the requester
+			 * can wake us up.
+			 */
+			UndoWorkerSetLingering(true);
+
+			/* Wait for more work. */
+			rc = WaitLatch(MyLatch,
+						   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   DEFAULT_NAPTIME_PER_CYCLE,
+						   WAIT_EVENT_UNDO_WORKER_MAIN);
+
+			/* reset the shared state. */
+			UndoWorkerSetLingering(false);
+
+			if (rc & WL_LATCH_SET)
+			{
+				ResetLatch(MyLatch);
+				CHECK_FOR_INTERRUPTS();
+			}
+
+			if (got_SIGHUP)
+			{
+				got_SIGHUP = false;
+				ProcessConfigFile(PGC_SIGHUP);
+			}
+		}
+	}
+
+	/* Normal exit from undo worker main */
+	proc_exit(0);
+}
+
+/*
+ * Wake up undo worker so that undo requests can be processed in a timely
+ * fashion.
+ *
+ * We first try to wake up the lingering worker in the given database.  If we
+ * found even one such worker, we are done.
+ *
+ * Next, we try to stop some worker which is lingering, but doesn't belong to
+ * the given database.  We know that any worker which is lingering doesn't have
+ * any pending work, so it is fine to stop it when we know that there is going
+ * to be some work in the other database.
+ *
+ * Finally, we wakeup launcher so that it can either restart the worker we have
+ * stopped or find some other worker who can take up this request.
+ */
+void
+WakeupUndoWorker(Oid dbid)
+{
+	int			i;
+
+	LWLockAcquire(UndoWorkerLock, LW_EXCLUSIVE);
+
+	/* wake up lingering worker in the given database. */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid == dbid)
+		{
+			SetLatch(&w->proc->procLatch);
+
+			LWLockRelease(UndoWorkerLock);
+			return;
+		}
+	}
+
+	/*
+	 * Stop one of the lingering worker which is not processing the requests
+	 * in the given database.
+	 */
+	for (i = 0; i < max_undo_workers; i++)
+	{
+		UndoApplyWorker *w = &UndoApplyCtx->workers[i];
+
+		if (w->in_use && w->lingering && w->dbid != dbid)
+			kill(w->proc->pid, SIGTERM);
+	}
+
+	if (UndoApplyCtx->undo_launcher_latch)
+		SetLatch(UndoApplyCtx->undo_launcher_latch);
+
+	LWLockRelease(UndoWorkerLock);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cceefbdd49f..1d65bf865ae 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/tupconvert.h"
+#include "access/undodiscard.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -14614,6 +14615,10 @@ PreCommit_on_commit_actions(void)
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
 				break;
+			case ONCOMMIT_TEMP_DISCARD:
+				/* Discard temp table undo logs for temp tables. */
+				TempUndoDiscard(oc->relid);
+				break;
 		}
 	}
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index b66b517aca9..b3db6fbf9d2 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -15,7 +15,9 @@
 #include <unistd.h>
 
 #include "libpq/pqsignal.h"
+#include "access/discardworker.h"
 #include "access/parallel.h"
+#include "access/undoworker.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/atomics.h"
@@ -129,6 +131,15 @@ static const struct
 	},
 	{
 		"ApplyWorkerMain", ApplyWorkerMain
+	},
+	{
+		"UndoLauncherMain", UndoLauncherMain
+	},
+	{
+		"UndoWorkerMain", UndoWorkerMain
+	},
+	{
+		"DiscardWorkerMain", DiscardWorkerMain
 	}
 };
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 879aca46f59..d49e548159e 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3679,6 +3679,15 @@ pgstat_get_wait_activity(WaitEventActivity w)
 		case WAIT_EVENT_WAL_WRITER_MAIN:
 			event_name = "WalWriterMain";
 			break;
+		case WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN:
+			event_name = "UndoDiscardWorkerMain";
+			break;
+		case WAIT_EVENT_UNDO_LAUNCHER_MAIN:
+			event_name = "UndoLauncherMain";
+			break;
+		case WAIT_EVENT_UNDO_WORKER_MAIN:
+			event_name = "UndoWorkerMain";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3339804be91..6521efa09e0 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -93,7 +93,9 @@
 #include <pthread.h>
 #endif
 
+#include "access/discardworker.h"
 #include "access/transam.h"
+#include "access/undoworker.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_control.h"
@@ -246,6 +248,8 @@ bool		enable_bonjour = false;
 char	   *bonjour_name;
 bool		restart_after_crash = true;
 
+bool		enable_undo_launcher = true;
+
 /* PIDs of special child processes; 0 when not running */
 static pid_t StartupPID = 0,
 			BgWriterPID = 0,
@@ -982,6 +986,13 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	ApplyLauncherRegister();
 
+	/* Register the Undo worker launcher. */
+	if (enable_undo_launcher)
+		UndoLauncherRegister();
+
+	/* Register the Undo Discard worker. */
+	DiscardWorkerRegister();
+
 	/*
 	 * process any libraries that should be preloaded at postmaster start
 	 */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 12c324925ca..56b5d087b03 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,8 @@
 #include "access/subtrans.h"
 #include "access/twophase.h"
 #include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -149,6 +151,8 @@ CreateSharedMemoryAndSemaphores(int port)
 		size = add_size(size, BTreeShmemSize());
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
+		size = add_size(size, PendingUndoShmemSize());
+		size = add_size(size, UndoLauncherShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -220,6 +224,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	PendingUndoShmemInit();
 
 	/*
 	 * Set up lock manager
@@ -258,6 +263,7 @@ CreateSharedMemoryAndSemaphores(int port)
 	WalSndShmemInit();
 	WalRcvShmemInit();
 	ApplyLauncherShmemInit();
+	UndoLauncherShmemInit();
 
 	/*
 	 * Set up other modules that need some shared memory space
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index 19e4f1f0c4a..1f650561cf6 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -51,3 +51,4 @@ LogicalRepWorkerLock				43
 CLogTruncationLock					44
 UndoLogLock                                      45
 RollbackRequestLock					46
+UndoWorkerLock						47
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 884fa2af52f..61406cff8fd 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -297,7 +297,9 @@ InitProcGlobal(void)
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
 	SpinLockInit(ProcStructLock);
 
+	pg_atomic_init_u64(&ProcGlobal->oldestFullXidHavingUnappliedUndo, 0);
 	ProcGlobal->xactsHavingPendingUndo = 0;
+	ProcGlobal->rollbackHTInitialized = false;
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5479901f340..ba9b11f26a4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -33,6 +33,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/undorequest.h"
+#include "access/undoworker.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "catalog/namespace.h"
@@ -1954,6 +1955,17 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"enable_undo_launcher", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+			gettext_noop("Decides whether to launch an undo worker."),
+			NULL,
+			GUC_NOT_IN_SAMPLE
+		 },
+		 &enable_undo_launcher,
+		 true,
+		 NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -3028,6 +3040,16 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"max_undo_workers", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
+			gettext_noop("Maximum number of undo worker processes."),
+			NULL,
+		},
+		&max_undo_workers,
+		4, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4ab39ddf9ab..ebd9a96e9e9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -608,6 +608,7 @@
 					# requests are pushed to undo workers
 #pending_undo_queue_size = 1024	# size of queue used to register undo
 					# requests
+#max_undo_workers = 4	# maximum undo workers
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/discardworker.h b/src/include/access/discardworker.h
new file mode 100644
index 00000000000..5b065bf11b0
--- /dev/null
+++ b/src/include/access/discardworker.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * discardworker.h
+ *	  Exports from access/undo/discardworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/discardworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _DISCARDWORKER_H
+#define _DISCARDWORKER_H
+
+extern void DiscardWorkerRegister(void);
+extern void DiscardWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern bool IsDiscardProcess(void);
+
+#endif							/* _DISCARDWORKER_H */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 7796f7248c4..e68e74342ac 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -73,6 +73,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 fxid)
+{
+	FullTransactionId result;
+
+	result.value = fxid;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/access/undodiscard.h b/src/include/access/undodiscard.h
new file mode 100644
index 00000000000..282afc04e1f
--- /dev/null
+++ b/src/include/access/undodiscard.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoinsert.h
+ *	  undo discard definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undodiscard.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDODISCARD_H
+#define UNDODISCARD_H
+
+#include "access/undolog.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+#include "storage/lwlock.h"
+
+extern void UndoDiscard(TransactionId xmin, bool *hibernate);
+extern void UndoLogDiscardAll(void);
+extern void TempUndoDiscard(UndoLogNumber);
+extern void UndoLogProcess(void);
+
+#endif							/* UNDODISCARD_H */
diff --git a/src/include/access/undoworker.h b/src/include/access/undoworker.h
new file mode 100644
index 00000000000..1bdc31fac8c
--- /dev/null
+++ b/src/include/access/undoworker.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoworker.h
+ *	  Exports from undoworker.c.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoworker.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _UNDOWORKER_H
+#define _UNDOWORKER_H
+
+/* GUC options */
+extern int max_undo_workers;
+
+/* undo worker sleep time between rounds */
+extern int	UndoWorkerDelay;
+
+extern Size UndoLauncherShmemSize(void);
+extern void UndoLauncherShmemInit(void);
+extern void UndoLauncherRegister(void);
+extern void UndoLauncherMain(Datum main_arg);
+extern void UndoWorkerMain(Datum main_arg) pg_attribute_noreturn();
+extern void WakeupUndoWorker(Oid dbid);
+
+#endif							/* _UNDOWORKER_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ff98d9e91a8..babcc6b1dc0 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -61,6 +61,15 @@ typedef struct CheckPoint
 	 * set to InvalidTransactionId.
 	 */
 	TransactionId oldestActiveXid;
+
+	/*
+	 * Oldest full transaction id which is having unapplied undo.  We include
+	 * this value in the checkpoint record so that whenever server re-starts
+	 * we can use this to initialize the server-wide value for same variable.
+	 * Any Xid prior to this should be all-visible, so if this is not set,
+	 * then the scans might try to fetch undo which can suck the performance.
+	 */
+	FullTransactionId		oldestFullXidHavingUnappliedUndo;
 } CheckPoint;
 
 /* XLOG info values for XLOG rmgr */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 860a84de7c0..cca575bb3be 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -49,7 +49,8 @@ typedef enum OnCommitAction
 	ONCOMMIT_NOOP,				/* No ON COMMIT clause (do nothing) */
 	ONCOMMIT_PRESERVE_ROWS,		/* ON COMMIT PRESERVE ROWS (do nothing) */
 	ONCOMMIT_DELETE_ROWS,		/* ON COMMIT DELETE ROWS */
-	ONCOMMIT_DROP				/* ON COMMIT DROP */
+	ONCOMMIT_DROP,				/* ON COMMIT DROP */
+	ONCOMMIT_TEMP_DISCARD		/* ON COMMIT discard temp table undo logs */
 } OnCommitAction;
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 7edec84d85a..17930ebe499 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -784,7 +784,10 @@ typedef enum
 	WAIT_EVENT_SYSLOGGER_MAIN,
 	WAIT_EVENT_WAL_RECEIVER_MAIN,
 	WAIT_EVENT_WAL_SENDER_MAIN,
-	WAIT_EVENT_WAL_WRITER_MAIN
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	WAIT_EVENT_UNDO_DISCARD_WORKER_MAIN,
+	WAIT_EVENT_UNDO_LAUNCHER_MAIN,
+	WAIT_EVENT_UNDO_WORKER_MAIN
 } WaitEventActivity;
 
 /* ----------
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b692d8be110..b9af96deeea 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -29,6 +29,7 @@ extern bool log_hostname;
 extern bool enable_bonjour;
 extern char *bonjour_name;
 extern bool restart_after_crash;
+extern bool enable_undo_launcher;
 
 #ifdef WIN32
 extern HANDLE PostmasterHandle;
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1b1de7dd18c..ac1e1fad8bf 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -272,8 +272,12 @@ typedef struct PROC_HDR
 	int			startupProcPid;
 	/* Buffer id of the buffer that Startup process waits for pin on, or -1 */
 	int			startupBufferPinWaitBufId;
+	/* Oldest transaction id which is having undo. */
+	pg_atomic_uint64 oldestFullXidHavingUnappliedUndo;
 	/* Number of aborted transactions with pending undo actions. */
 	int			xactsHavingPendingUndo;
+	/* Whether the rollback hash table is initialized after the startup? */
+	bool		rollbackHTInitialized;
 } PROC_HDR;
 
 extern PGDLLIMPORT PROC_HDR *ProcGlobal;
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index da8b672096f..1d12994d7d2 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -27,6 +27,8 @@
  * to avoid forcing to include proc.h when including procarray.h. So if you modify
  * PROC_XXX flags, you need to modify these flags.
  */
+#define		PROCARRAY_AUTOVACUUM_FLAG		0x01	/* currently running
+													 * autovacuum */
 #define		PROCARRAY_VACUUM_FLAG			0x02	/* currently running lazy
 													 * vacuum */
 #define		PROCARRAY_ANALYZE_FLAG			0x04	/* currently running
@@ -41,7 +43,8 @@
  * PGXACT->vacuumFlags. Other flags are used for different purposes and
  * have no corresponding PROC flag equivalent.
  */
-#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_VACUUM_FLAG | \
+#define		PROCARRAY_PROC_FLAGS_MASK	(PROCARRAY_AUTOVACUUM_FLAG | \
+										 PROCARRAY_VACUUM_FLAG | \
 										 PROCARRAY_ANALYZE_FLAG | \
 										 PROCARRAY_LOGICAL_DECODING_FLAG)
 
@@ -50,6 +53,8 @@
 #define		PROCARRAY_FLAGS_DEFAULT			PROCARRAY_LOGICAL_DECODING_FLAG
 /* Ignore vacuum backends */
 #define		PROCARRAY_FLAGS_VACUUM			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_VACUUM_FLAG
+/* Ignore autovacuum worker and backends running vacuum */
+#define		PROCARRAY_FLAGS_AUTOVACUUM		PROCARRAY_FLAGS_DEFAULT | PROCARRAY_AUTOVACUUM_FLAG
 /* Ignore analyze backends */
 #define		PROCARRAY_FLAGS_ANALYZE			PROCARRAY_FLAGS_DEFAULT | PROCARRAY_ANALYZE_FLAG
 /* Ignore both vacuum and analyze backends */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index a1c90eb9057..6034c5e41ad 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -89,7 +89,8 @@ select name, setting from pg_settings where name like 'enable%';
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(17 rows)
+ enable_undo_launcher           | on
+(18 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
-- 
2.22.0

#311Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#308)
2 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

If you want to track at undo record level, then won't it lead to
performance overhead and probably additional WAL overhead considering
this action needs to be WAL-logged. I think recording at page-level
might be a better idea.

I'm not worried about WAL because the undo execution needs to be WAL-logged
anyway - see smgr_undo() in the 0005- part of the patch set. What needs to be
evaluated regarding performance is the (exclusive) locking of the page that
carries the progress information.

That is just for one kind of smgr, think how you will do it for
something like zheap. Their idea is to collect all the undo records
(unless the undo for a transaction is very large) for one zheap-page
and apply them together, so maintaining the status at each undo record
level will surely lead to a large amount of additional WAL. See below
how and why we have decided to do it differently.

I'm still not sure whether this info should
be on every page or only in the chunk header. In either case, we have a
problem if there are two or more chunks created by different transactions on
the same page, and if more than on of these transactions need to perform
undo. I tend to believe that this should happen rarely though.

I think we need to maintain this information at the transaction level
and need to update it after processing a few blocks, at least that is
what was decided and implemented earlier. We also need to update it
when the log is switched or all the actions of the transaction were
applied. The reasoning is that for short transactions it won't matter
and for larger transactions, it is good to update it after a few pages
to avoid WAL and locking overhead. Also, it is better if we collect
the undo in bulk, this is proved to be beneficial for large
transactions. The earlier version of the patch having all these ideas
implemented is attached
(Infrastructure-to-execute-pending-undo-actions and
Provide-interfaces-to-store-and-fetch-undo-records). The second one
has some APIs used by the first one but the main concepts were
implemented in the first one
(Infrastructure-to-execute-pending-undo-actions). I see that in the
current version these can't be used as it is but still it can give us
a good start point and we might be able to either re-use some code and
or ideas from these patches.

--
With Regards,
Amit Kapila.

Attachments:

Infrastructure-to-execute-pending-undo-actions.patchapplication/octet-stream; name=Infrastructure-to-execute-pending-undo-actions.patchDownload
From de633a9d22fc21f1b7fbe95c2dd6ed28dbbee879 Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:27:58 +0530
Subject: [PATCH 11/22] Infrastructure to execute pending undo actions.

To apply the undo actions, we collect the undo records in bulk and try to
process them together.  We ensure to update the transaction's progress at
regular intervals so that after a crash we can skip already applied undo.

This provides a way for users to register a callback for processing the
undo records based on resource manager.

Dilip Kumar, Amit Kapila, Thomas Munro and Kuntal Ghosh with inputs from
Robert Haas
---
 src/backend/access/rmgrdesc/Makefile         |   3 +-
 src/backend/access/rmgrdesc/undoactiondesc.c |  47 ++
 src/backend/access/transam/rmgr.c            |   5 +-
 src/backend/access/undo/Makefile             |   3 +-
 src/backend/access/undo/undoaccess.c         |  42 +-
 src/backend/access/undo/undoaction.c         | 516 +++++++++++++++++++
 src/backend/access/undo/undoactionxlog.c     |  60 +++
 src/backend/replication/logical/decode.c     |   1 +
 src/bin/pg_rewind/parsexlog.c                |   2 +-
 src/bin/pg_waldump/rmgrdesc.c                |   3 +-
 src/include/access/rmgr.h                    |   2 +-
 src/include/access/rmgrlist.h                |  52 +-
 src/include/access/undoaccess.h              |   4 +
 src/include/access/undoaction_xlog.h         |  39 ++
 src/include/access/undorecord.h              |   2 +
 src/include/access/undorequest.h             |   3 -
 src/include/access/xlog_internal.h           |  18 +-
 17 files changed, 766 insertions(+), 36 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/undoactiondesc.c
 create mode 100644 src/backend/access/undo/undoaction.c
 create mode 100644 src/backend/access/undo/undoactionxlog.c
 create mode 100644 src/include/access/undoaction_xlog.h

diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index 91ad1ef8a3d..640d37f37a3 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o logicalmsgdesc.o \
 	   mxactdesc.o nbtdesc.o relmapdesc.o replorigindesc.o seqdesc.o \
-	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undologdesc.o xactdesc.o xlogdesc.o
+	   smgrdesc.o spgdesc.o standbydesc.o tblspcdesc.o undoactiondesc.o \
+	   undologdesc.o xactdesc.o xlogdesc.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/undoactiondesc.c b/src/backend/access/rmgrdesc/undoactiondesc.c
new file mode 100644
index 00000000000..c396582b599
--- /dev/null
+++ b/src/backend/access/rmgrdesc/undoactiondesc.c
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactiondesc.c
+ *	  rmgr descriptor routines for access/undo/undoactionxlog.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/undoactiondesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+
+void
+undoaction_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_UNDO_APPLY_PROGRESS)
+	{
+		xl_undoapply_progress *xlrec = (xl_undoapply_progress *) rec;
+
+		appendStringInfo(buf, "urec_ptr %lu progress %u",
+						 xlrec->urec_ptr, xlrec->progress);
+	}
+}
+
+const char *
+undoaction_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			id = "UNDO_APPLY_PROGRESS";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 8b0537405a9..c57eca240f5 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -18,6 +18,7 @@
 #include "access/multixact.h"
 #include "access/nbtxlog.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -31,8 +32,8 @@
 #include "utils/relmapper.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
-	{ name, redo, desc, identify, startup, cleanup, mask },
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
+	{ name, redo, desc, identify, startup, cleanup, mask, undo, undo_status, undo_desc },
 
 const RmgrData RmgrTable[RM_MAX_ID + 1] = {
 #include "access/rmgrlist.h"
diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 73275028be9..68696bc81a8 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undoaccess.o undolog.o undorecord.o undorequest.o
+OBJS = undoaccess.o undoaction.o undoactionxlog.o undolog.o undorecord.o \
+	   undorequest.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 92496fbbc8a..36080159619 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -271,6 +271,46 @@ PrepareUndoRecordUpdateNext(UndoRecordInsertContext *context,
 	LWLockRelease(&slot->discard_update_lock);
 }
 
+/*
+ * Prepare to update the undo apply progress in the group header.
+ */
+void
+PrepareUndoRecordApplyProgress(UndoRecordInsertContext *context,
+							   UndoRecPtr urecptr, BlockNumber progress)
+{
+	int		index = 0;
+	int		offset;
+
+	Assert(UndoRecPtrIsValid(urecptr));
+
+	/*
+	 * Temporary undo logs are discarded on transaction commit so we don't need
+	 * to do anything.
+	 */
+	if (UndoRecPtrGetCategory(urecptr) == UNDO_TEMP)
+		return;
+
+	/*
+	 * Here, we are preparing to update the undo apply progress of a
+	 * transaction being rolled back.  The undo must not be discarded
+	 * till the transaction is completely rolled back.
+	 */
+	Assert(!UndoRecPtrIsDiscarded(urecptr));
+
+	/* Compute the offset of the urec_progress in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+			 offsetof(UndoRecordGroup, urec_progress);
+
+	index = PrepareUndoRecordUpdate(context, urecptr, sizeof(UndoRecPtr),
+									offset);
+
+	/*
+	 * Set the undo action progress in xact_urec_info, this will be overwritten
+	 * in actual undo record during update phase.
+	 */
+	context->urec_update_info[index].progress = progress;
+}
+
 /*
  * Update the undo record
  *
@@ -278,7 +318,7 @@ PrepareUndoRecordUpdateNext(UndoRecordInsertContext *context,
  * Exact offset to be updated is already computed and necessary buffers are
  * locked during the prepare phase.
  */
-static void
+void
 UndoRecordUpdate(UndoRecordInsertContext *context, int idx)
 {
 	Page		page = NULL;
diff --git a/src/backend/access/undo/undoaction.c b/src/backend/access/undo/undoaction.c
new file mode 100644
index 00000000000..4258d15ebc2
--- /dev/null
+++ b/src/backend/access/undo/undoaction.c
@@ -0,0 +1,516 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction.c
+ *	  execute undo actions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaction.c
+ *
+ * To apply the undo actions, we collect the undo records in bulk and try to
+ * process them together.  We ensure to update the transaction's progress at
+ * regular intervals so that after a crash we can skip already applied undo.
+ * The undo apply progress is updated in terms of the number of blocks
+ * processed.  Undo apply progress value XACT_APPLY_PROGRESS_COMPLETED
+ * indicates that all the undo is applied, XACT_APPLY_PROGRESS_NOT_STARTED
+ * indicates that no undo action has been applied yet and any other value
+ * indicates that we have applied undo partially and after crash recovery, we
+ * need to start processing the undo from the same location.
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "access/undoaction_xlog.h"
+#include "access/undolog.h"
+#include "access/undorequest.h"
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "access/xlog_internal.h"
+#include "nodes/pg_list.h"
+#include "pgstat.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/relfilenodemap.h"
+#include "utils/syscache.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+
+static void UpdateUndoApplyProgress(UndoRecPtr last_log_start_urec_ptr,
+						  BlockNumber block_num);
+static bool UndoAlreadyApplied(FullTransactionId full_xid,
+						UndoRecPtr to_urecptr);
+static void ApplyUndo(UndoRecInfo *urecinfo, int nrecords);
+static void ProcessAndApplyUndo(FullTransactionId full_xid,
+				UndoRecPtr from_urecptr, UndoRecPtr to_urecptr,
+				UndoRecPtr last_log_start_urec_ptr, bool complete_xact);
+
+/*
+ * undo_record_comparator - qsort comparator for undo records.
+ *
+ * This is used to sort undo records for applying undo actions of the
+ * transaction.
+ */
+static int
+undo_record_comparator(const void *left, const void *right)
+{
+	UnpackedUndoRecord *luur = ((UndoRecInfo *) left)->uur;
+	UnpackedUndoRecord *ruur = ((UndoRecInfo *) right)->uur;
+
+	if (luur->uur_rmid < ruur->uur_rmid)
+		return -1;
+	else if (luur->uur_rmid > ruur->uur_rmid)
+		return 1;
+	else if (luur->uur_reloid < ruur->uur_reloid)
+		return -1;
+	else if (luur->uur_reloid > ruur->uur_reloid)
+		return 1;
+	else if (luur->uur_block < ruur->uur_block)
+		return -1;
+	else if (luur->uur_block > ruur->uur_block)
+		return 1;
+	else if (luur->uur_offset < ruur->uur_offset)
+		return -1;
+	else if (luur->uur_offset > ruur->uur_offset)
+		return 1;
+	else if (((UndoRecInfo *) left)->index < ((UndoRecInfo *) right)->index)
+	{
+		/*
+		 * If records are for the same block and offset, then maintain their
+		 * existing order by comparing their index in the array.
+		 */
+		return -1;
+	}
+	else
+		return 1;
+}
+
+/*
+ * UpdateUndoApplyProgress - Updates how far undo actions from a particular
+ * log have been applied while rolling back a transaction.  This progress is
+ * measured in terms of undo block number of the undo log till which the
+ * undo actions have been applied.
+ */
+static void
+UpdateUndoApplyProgress(UndoRecPtr progress_urec_ptr,
+						BlockNumber block_num)
+{
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(progress_urec_ptr));
+
+	/*
+	 * We don't need to update the progress for temp tables as they get
+	 * discraded after startup.
+	 */
+	if (category == UNDO_TEMP)
+		return;
+
+	BeginUndoRecordInsert(&context, category, 1, NULL);
+
+	/*
+	 * Prepare and update the undo apply progress in the transaction header.
+	 */
+	PrepareUndoRecordApplyProgress(&context, progress_urec_ptr, block_num);
+
+	START_CRIT_SECTION();
+
+	/* Update the progress in the transaction header. */
+	UndoRecordUpdate(&context, 0);
+
+	/* WAL log the undo apply progress. */
+	{
+		XLogRecPtr	lsn;
+		xl_undoapply_progress xlrec;
+
+		xlrec.urec_ptr = progress_urec_ptr;
+		xlrec.progress = block_num;
+
+		XLogBeginInsert();
+		XLogRegisterData((char *) &xlrec, sizeof(xlrec));
+
+		RegisterUndoLogBuffers(&context, 1);
+		lsn = XLogInsert(RM_UNDOACTION_ID, XLOG_UNDO_APPLY_PROGRESS);
+		UndoLogBuffersSetLSN(&context, lsn);
+	}
+
+	END_CRIT_SECTION();
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+/*
+ * UndoAlreadyApplied - Retruns true, if the actions are already applied,
+ *	false, otherwise.
+ */
+static bool
+UndoAlreadyApplied(FullTransactionId full_xid, UndoRecPtr to_urecptr)
+{
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecordFetchContext	context;
+
+	/* Fetch the undo record. */
+	BeginUndoFetch(&context);
+	uur = UndoFetchRecord(&context, to_urecptr);
+	FinishUndoFetch(&context);
+
+	/* already processed and discarded */
+	if (uur == NULL)
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		return true;
+	}
+
+	/* already processed */
+	if (IsXactApplyProgressCompleted(uur->uur_group->urec_progress))
+	{
+		/*
+		 * Undo action is already applied, so delete the hash table entry
+		 * if exists.
+		 */
+		RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+		UndoRecordRelease(uur);
+		return true;
+	}
+
+	Assert(FullTransactionIdEquals(full_xid, uur->uur_fxid));
+
+	UndoRecordRelease(uur);
+
+	return false;
+}
+
+/*
+ * ApplyUndo - Invode rmgr specific undo apply functions.
+ *
+ * urecinfo - An array of undo records sorted in the rmgr order.
+ * nrecords - number of records in this array.
+ */
+static void
+ApplyUndo(UndoRecInfo *urecinfo, int nrecords)
+{
+	int			rmgr_start_idx = 0;
+	int			rmgr_nrecords = 0;
+	int			prev_rmid = -1;
+	int			i;
+
+	/* Apply the undo action for each rmgr. */
+	for (i = 0; i < nrecords; i++)
+	{
+		UnpackedUndoRecord *uur = urecinfo[i].uur;
+
+		Assert(uur->uur_rmid >= 0);
+
+		/*
+		 * If this undo is not for the same rmgr then apply all undo
+		 * actions for the previous rmgr.
+		 */
+		if (prev_rmid >= 0 &&
+			prev_rmid != uur->uur_rmid)
+		{
+			Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+			RmgrTable[prev_rmid].rm_undo(rmgr_nrecords,
+										 &urecinfo[rmgr_start_idx]);
+
+			rmgr_start_idx = i;
+			rmgr_nrecords = 0;
+		}
+
+		rmgr_nrecords++;
+		prev_rmid = uur->uur_rmid;
+	}
+
+	/* Apply the last set of the actions. */
+	Assert(urecinfo[rmgr_start_idx].uur->uur_rmid == prev_rmid);
+	RmgrTable[prev_rmid].rm_undo(rmgr_nrecords, &urecinfo[rmgr_start_idx]);
+}
+
+/*
+ * ProcessAndApplyUndo - Fetch undo records and apply actions.
+ *
+ * We always process the undo of the last log when the undo for a transaction
+ * spans across multiple logs.  Then from there onwards the previous undo logs
+ * for the same transaction are processed.
+ *
+ * We also update the undo apply progress in the transaction header so that
+ * after recovery we don't need to process the records that are already
+ * processed.  As we update the progress only after one batch of records,
+ * the crash in-between can cause us to read/apply part of undo records
+ * again but this will never be more than one-batch.  We can further optimize
+ * it by marking the progress in each record, but that has its own downsides
+ * like it will generate more WAL and I/O corresponding to dirty undo buffers.
+ */
+static void
+ProcessAndApplyUndo(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					UndoRecPtr to_urecptr, UndoRecPtr last_log_start_urec_ptr,
+					bool complete_xact)
+{
+	UndoRecInfo *urecinfo;
+	UndoRecPtr	urec_ptr = from_urecptr;
+	int			undo_apply_size;
+
+	/*
+	 * We choose maintenance_work_mem to collect the undo records for
+	 * rollbacks as most of the large rollback requests are done by
+	 * background worker which can be considered as maintainence operation.
+	 * However, we can introduce a new guc for this as well.
+	 */
+	undo_apply_size = maintenance_work_mem * 1024L;
+
+	/*
+	 * Fetch the multiple undo records that can fit into undo_apply_size; sort
+	 * them and then rmgr specific callback to process them.  Repeat this
+	 * until we process all the records for the transaction being rolled back.
+	 */
+	while (true)
+	{
+		BlockNumber	progress_block_num = InvalidBlockNumber;
+		int			i;
+		int			nrecords;
+		bool		log_switched = false;
+		bool		rollback_completed = false;
+		bool		update_progress = false;
+		UndoRecPtr	progress_urec_ptr = InvalidUndoRecPtr;
+		UndoRecInfo	*first_urecinfo;
+		UndoRecInfo	*last_urecinfo;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/*
+		 * Fetch multiple undo records at once.
+		 *
+		 * At a time, we only fetch the undo records from a single undo log.
+		 * Once, we process all the undo records from one undo log, we update
+		 * the last_log_start_urec_ptr and proceed to the previous undo log.
+		 */
+		urecinfo = UndoBulkFetchRecord(&urec_ptr, last_log_start_urec_ptr,
+									   undo_apply_size, &nrecords);
+
+		/*
+		 * Since the rollback of this transaction is in-progress, there will be
+		 * at least one undo record which is not yet discarded.
+		 */
+		Assert(nrecords > 0);
+
+		/*
+		 * Get the required information from first and last undo record before
+		 * we sort all the records.
+		 */
+		first_urecinfo = &urecinfo[0];
+		last_urecinfo = &urecinfo[nrecords - 1];
+		if (IsUndoLogSwitched(last_urecinfo->uur))
+		{
+			UndoRecordLogSwitch *logswitch = last_urecinfo->uur->uur_logswitch;
+
+			/*
+			 * We have crossed the log boundary.  The rest of the undo for
+			 * this transaction is in some other log, the location of which
+			 * can be found from this record.  See commets atop undoaccess.c.
+			 */
+			log_switched = true;
+
+			/*
+			 * The last fetched undo record corresponds to the first undo
+			 * record of the current log.  Once, the undo actions are performed
+			 * from this log, we've to mark the progress as completed.
+			 */
+			progress_urec_ptr = last_urecinfo->urp;
+
+			/*
+			 * We also need to save the start location of this transaction in
+			 * previous log.  This will be used in the next iteration of bulk
+			 * fetch and updating progress location.
+			 */
+			if (complete_xact)
+			{
+				Assert(UndoRecPtrIsValid(logswitch->urec_prevlogstart));
+				last_log_start_urec_ptr = logswitch->urec_prevlogstart;
+			}
+
+			/* We've to update the progress for the current log as completed. */
+			update_progress = true;
+		}
+		else if (complete_xact)
+		{
+			if (UndoRecPtrIsValid(urec_ptr))
+			{
+				/*
+				 * There are still some undo actions pending in this log.  So,
+				 * just update the progress block number.
+				 */
+				progress_block_num = UndoRecPtrGetBlockNum(last_urecinfo->urp);
+
+				/*
+				 * If we've not fetched undo records for more than one undo
+				 * block, we can't update the progress block number.  Because,
+				 * there can still be undo records in this block that needs to
+				 * be applied for rolling back this transaction.
+				 */
+				if (UndoRecPtrGetBlockNum(first_urecinfo->urp) > progress_block_num)
+				{
+					update_progress = true;
+					progress_urec_ptr = last_log_start_urec_ptr;
+				}
+			}
+			else
+			{
+				/*
+				 * Invalid urec_ptr indicates that we have executed all the undo
+				 * actions for this transaction.  So, mark current log header
+				 * as complete.
+				 */
+				Assert(last_log_start_urec_ptr == to_urecptr);
+				rollback_completed = true;
+				update_progress = true;
+				progress_urec_ptr = last_log_start_urec_ptr;
+			}
+		}
+
+		/*
+		 * The undo records must belong to the transaction that is being
+		 * rolled back.
+		 */
+		Assert(FullTransactionIdEquals(full_xid, urecinfo[0].uur->uur_fxid));
+
+		/* Sort the undo record array in order of target blocks. */
+		qsort((void *) urecinfo, nrecords, sizeof(UndoRecInfo),
+			  undo_record_comparator);
+
+		/* Call resource manager specific callbacks to apply actions. */
+		ApplyUndo(urecinfo, nrecords);
+
+		/* Set undo action apply progress if required. */
+		if (update_progress)
+		{
+			Assert(UndoRecPtrIsValid(progress_urec_ptr));
+
+			if (log_switched || rollback_completed)
+			{
+				/*
+				 * We have crossed the log boundary or executed all the undo
+				 * actions for the main transaction.  So, mark current log
+				 * header as complete and set the next progress location in
+				 * the previous log.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										XACT_APPLY_PROGRESS_COMPLETED);
+			}
+			else
+			{
+				/*
+				 * Update the progress block number.  We increase the block
+				 * number by one since the current block might have some undo
+				 * records that are yet to be applied.  But, all undo records
+				 * from the next block must have been applied.
+				 */
+				UpdateUndoApplyProgress(progress_urec_ptr,
+										progress_block_num + 1);
+			}
+		}
+
+		/* Free all undo records. */
+		for (i = 0; i < nrecords; i++)
+			UndoRecordRelease(urecinfo[i].uur);
+
+		/* Free urp array for the current batch of undo records. */
+		pfree(urecinfo);
+
+		/*
+		 * Invalid urec_ptr indicates that we have executed all the undo
+		 * actions for this transaction.
+		 */
+		if (!UndoRecPtrIsValid(urec_ptr))
+			break;
+	}
+}
+
+/*
+ * execute_undo_actions - Execute the undo actions
+ *
+ * full_xid - Transaction id that is getting rolled back.
+ * from_urecptr - undo record pointer from where to start applying undo
+ *				actions.
+ * to_urecptr	- undo record pointer up to which the undo actions need to be
+ *				applied.
+ * complete_xact	- true if rollback is for complete transaction.
+ */
+void
+execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
+					 UndoRecPtr to_urecptr, bool complete_xact)
+{
+	UndoRecPtr last_log_start_urec_ptr = to_urecptr;
+
+	/* 'from' and 'to' pointers must be valid. */
+	Assert(UndoRecPtrIsValid(from_urecptr));
+	Assert(UndoRecPtrIsValid(to_urecptr));
+
+	/*
+	 * Here we compute the last log start urp which is used for fetching the
+	 * undo records and updating the undo action progress.
+	 *
+	 * For rollbacks of subtransaction, we won't be able to calculate the last
+	 * log start urp since we don't have the start urp of the top xid and hence
+	 * we won't be able to follow the transaction chains to find the last log.
+	 */
+	if (complete_xact)
+	{
+		if (UndoRecPtrGetCategory(to_urecptr) == UNDO_TEMP)
+		{
+			UndoRecPtr end_urec_ptr = from_urecptr;
+
+			/*
+			 * For temporary tables, we don't push the rollback request in the
+			 * rollback hash table so we can't directly get the last log start
+			 * urp from there.  Instead, we need to compute it now.
+			 */
+			(void) FindUndoEndLocationAndSize(to_urecptr, &end_urec_ptr,
+											  &last_log_start_urec_ptr,
+											  full_xid);
+		}
+		else
+		{
+			/*
+			 * It is important here to fetch the latest undo record and validate if
+			 * the actions are already executed.  The reason is that it is possible
+			 * that discard worker or backend might try to execute the rollback
+			 * request which is already executed.  For ex., after discard worker
+			 * fetches the record and found that this transaction need to be
+			 * rolledback, backend might concurrently execute the actions and
+			 * remove the request from rollback hash table.
+			 *
+			 * The other case where this will be required is when the transactions
+			 * records span across multiple logs.  Say, we register the
+			 * transaction from the first log and then we encounter the same
+			 * transaction in the second log where its status is still not marked
+			 * as done.  Now, before we try to register the request for the second
+			 * log, the undo worker came along rolled back the previous request
+			 * and removed its hash entry.  In this case, we will successfully
+			 * register the request from the second log and it should be detected
+			 * here.
+			 */
+			if (UndoAlreadyApplied(full_xid, to_urecptr))
+				return;
+
+			last_log_start_urec_ptr =
+				RollbackHTGetLastLogStartUrp(full_xid, to_urecptr);
+		}
+	}
+
+	ProcessAndApplyUndo(full_xid, from_urecptr, to_urecptr,
+						last_log_start_urec_ptr, complete_xact);
+
+	/*
+	 * Undo actions are applied so delete the hash table entry.
+	 */
+	RollbackHTRemoveEntry(full_xid, to_urecptr, false);
+}
diff --git a/src/backend/access/undo/undoactionxlog.c b/src/backend/access/undo/undoactionxlog.c
new file mode 100644
index 00000000000..61e8815b6be
--- /dev/null
+++ b/src/backend/access/undo/undoactionxlog.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoactionxlog.c
+ *	  WAL replay logic for undo actions.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/undo/undoactionxlog.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/undoaction_xlog.h"
+#include "access/undoaccess.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+
+/*
+ * Replay of undo apply progress.
+ */
+static void
+undo_xlog_apply_progress(XLogReaderState *record)
+{
+	xl_undoapply_progress *xlrec = (xl_undoapply_progress *) XLogRecGetData(record);
+	UndoLogCategory category;
+	UndoRecordInsertContext context = {{0}};
+
+	category =
+		UndoLogNumberGetCategory(UndoRecPtrGetLogNo(xlrec->urec_ptr));
+
+	BeginUndoRecordInsert(&context, category, 1, record);
+
+	/* Update the undo apply progress in the transaction header. */
+	PrepareUndoRecordApplyProgress(&context, xlrec->urec_ptr,
+								   xlrec->progress);
+
+	UndoRecordUpdate(&context, 0);
+
+	/* Release undo buffers. */
+	FinishUndoRecordInsert(&context);
+}
+
+void
+undoaction_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	switch (info)
+	{
+		case XLOG_UNDO_APPLY_PROGRESS:
+			undo_xlog_apply_progress(record);
+			break;
+		default:
+			elog(PANIC, "undoaction_redo: unknown op code %u", info);
+	}
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index a4ecad8488a..7293cc7abdc 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -155,6 +155,7 @@ LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *recor
 		case RM_REPLORIGIN_ID:
 		case RM_GENERIC_ID:
 		case RM_UNDOLOG_ID:
+		case RM_UNDOACTION_ID:
 			/* just deal with xid, and done */
 			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
 									buf.origptr);
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 63c3879ead8..d64472daea0 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -28,7 +28,7 @@
  * RmgrNames is an array of resource manager names, to make error messages
  * a bit nicer.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
   name,
 
 static const char *RmgrNames[RM_MAX_ID + 1] = {
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 938150dd915..976f80e9c30 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -20,6 +20,7 @@
 #include "access/nbtxlog.h"
 #include "access/rmgr.h"
 #include "access/spgxlog.h"
+#include "access/undoaction_xlog.h"
 #include "access/undolog_xlog.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
@@ -33,7 +34,7 @@
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
 
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	{ name, desc, identify},
 
 const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
diff --git a/src/include/access/rmgr.h b/src/include/access/rmgr.h
index c9b5c56a4c6..0a3794a44e5 100644
--- a/src/include/access/rmgr.h
+++ b/src/include/access/rmgr.h
@@ -19,7 +19,7 @@ typedef uint8 RmgrId;
  * Note: RM_MAX_ID must fit in RmgrId; widening that type will affect the XLOG
  * file format.
  */
-#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,undo,undo_status,undo_desc) \
 	symname,
 
 typedef enum RmgrIds
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6945e3e9504..b424c3ca5cc 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -24,27 +24,31 @@
  * Changes to this list possibly need an XLOG_PAGE_MAGIC bump.
  */
 
-/* symbol name, textual name, redo, desc, identify, startup, cleanup */
-PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL)
-PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL)
-PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL)
-PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL)
-PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL)
-PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL)
-PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL)
-PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask)
-PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask)
-PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask)
-PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask)
-PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask)
-PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask)
-PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask)
-PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask)
-PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL)
-PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
-PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
-PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
-PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL)
+/*
+ * symbol name, textual name, redo, desc, identify, startup, cleanup, mask,
+ * undo, undo_status, undo_desc
+ */
+PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL, heap_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL, btree_mask, NULL, NULL, NULL)
+PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL, hash_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup, gin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup, gist_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL, seq_mask, NULL, NULL, NULL)
+PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup, spg_mask, NULL, NULL, NULL)
+PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL, brin_mask, NULL, NULL, NULL)
+PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL, NULL, NULL)
+PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOLOG_ID, "UndoLog", undolog_redo, undolog_desc, undolog_identify, NULL, NULL, NULL, NULL, NULL, NULL)
+PG_RMGR(RM_UNDOACTION_ID, "UndoAction", undoaction_redo, undoaction_desc, undoaction_identify, NULL, NULL, NULL, NULL, NULL, NULL)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
index 1a9640c4e76..35593546801 100644
--- a/src/include/access/undoaccess.h
+++ b/src/include/access/undoaccess.h
@@ -13,6 +13,7 @@
 #ifndef UNDOACCESS_H
 #define UNDOACCESS_H
 
+#include "access/transam.h"
 #include "access/undolog.h"
 #include "access/undorecord.h"
 #include "access/xlogdefs.h"
@@ -92,6 +93,9 @@ typedef struct UndoRecordFetchContext
 	UndoRecPtr	urp;			/* Previous undo record pointer. */
 } UndoRecordFetchContext;
 
+extern void PrepareUndoRecordApplyProgress(UndoRecordInsertContext *context,
+									UndoRecPtr urecptr, BlockNumber progress);
+extern void UndoRecordUpdate(UndoRecordInsertContext *context, int idx);
 extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
 								  UndoLogCategory category,
 								  int nprepared,
diff --git a/src/include/access/undoaction_xlog.h b/src/include/access/undoaction_xlog.h
new file mode 100644
index 00000000000..b9e65d1f7a8
--- /dev/null
+++ b/src/include/access/undoaction_xlog.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaction_xlog.h
+ *	  undo action XLOG definitions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaction_xlog.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACTION_XLOG_H
+#define UNDOACTION_XLOG_H
+
+#include "access/undolog.h"
+#include "access/xlogreader.h"
+#include "lib/stringinfo.h"
+#include "storage/off.h"
+
+/*
+ * WAL record definitions for undoactions.c's WAL operations
+ */
+#define XLOG_UNDO_APPLY_PROGRESS	0x00
+
+/* This is what we need to know about undo apply progress */
+typedef struct xl_undoapply_progress
+{
+	UndoRecPtr	urec_ptr;
+	uint32		progress;
+} xl_undoapply_progress;
+
+#define SizeOfUndoActionProgress	(offsetof(xl_undoapply_progress, progress) + sizeof(uint32))
+
+extern void undoaction_redo(XLogReaderState *record);
+extern void undoaction_desc(StringInfo buf, XLogReaderState *record);
+extern const char *undoaction_identify(uint8 info);
+
+#endif							/* UNDOACTION_XLOG_H */
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
index 80beaef0a74..30b7db46ab7 100644
--- a/src/include/access/undorecord.h
+++ b/src/include/access/undorecord.h
@@ -254,6 +254,8 @@ typedef struct UnpackedUndoRecord
 										 * during a transaction. */
 } UnpackedUndoRecord;
 
+#define IsUndoLogSwitched(uur) (uur->uur_logswitch != NULL)
+
 extern size_t UndoRecordExpectedSize(UnpackedUndoRecord *uur);
 extern size_t UndoRecordPayloadSize(UnpackedUndoRecord *uur);
 extern void BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur,
diff --git a/src/include/access/undorequest.h b/src/include/access/undorequest.h
index 64d89ba9720..82640b88f44 100644
--- a/src/include/access/undorequest.h
+++ b/src/include/access/undorequest.h
@@ -224,8 +224,5 @@ extern FullTransactionId RollbackHTGetOldestFullXid(FullTransactionId oldestXmin
 /* functions exposed from undoaction.c */
 extern void execute_undo_actions(FullTransactionId full_xid, UndoRecPtr from_urecptr,
 					 UndoRecPtr to_urecptr, bool nopartial);
-extern bool execute_undo_actions_page(UndoRecInfo *urp_array, int first_idx,
-						  int last_idx, Oid reloid, FullTransactionId full_xid,
-						  BlockNumber blkno, bool blk_chain_complete);
 
 #endif							/* _UNDOREQUEST_H */
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 3f0de6625d7..1ff312f7473 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -19,10 +19,14 @@
 #ifndef XLOG_INTERNAL_H
 #define XLOG_INTERNAL_H
 
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undorecord.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
+#include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
@@ -270,6 +274,15 @@ typedef enum
 	RECOVERY_TARGET_ACTION_SHUTDOWN
 }			RecoveryTargetAction;
 
+/*
+ * Return values for undo status callback functions.
+ */
+typedef enum UndoStatus
+{
+	UNDO_STATUS_WAIT_XMIN,		/* wait until the xmin passes an xid */
+	UNDO_STATUS_DISCARD			/* the record set should be discarded */
+} UndoStatus;
+
 /*
  * Method table for resource managers.
  *
@@ -295,9 +308,12 @@ typedef struct RmgrData
 	void		(*rm_startup) (void);
 	void		(*rm_cleanup) (void);
 	void		(*rm_mask) (char *pagedata, BlockNumber blkno);
+	void		(*rm_undo) (int nrecords, UndoRecInfo *records);
+	UndoStatus	(*rm_undo_status) (UnpackedUndoRecord *record, TransactionId *xid);
+	void		(*rm_undo_desc) (StringInfo buf, UnpackedUndoRecord *record);
 } RmgrData;
 
-extern const RmgrData RmgrTable[];
+extern PGDLLIMPORT const RmgrData RmgrTable[];
 
 /*
  * Exported to support xlog switching from checkpointer
-- 
2.22.0

Provide-interfaces-to-store-and-fetch-undo-records.patchapplication/octet-stream; name=Provide-interfaces-to-store-and-fetch-undo-records.patchDownload
From 917e67b93e86cd7cc962d045f1bb38474912b90f Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilip.kumar@enterprisedb.com>
Date: Mon, 5 Aug 2019 09:39:45 +0530
Subject: [PATCH 07/22] Provide interfaces to store and fetch undo records.

The undo interface layer provides a mechanism for packing/unpacking
the undo records into the undo pages.  We are assuming that the
certain operations are happening in the transaction and those
operations need to insert the undo records for handling the
visibility or for rolling back the effect of the transaction.
This layer provides interfaces for inserting and fetching the
undo records.  The patch will also provide a mechanism for
compressing the undo records.  Basically, if an undo record on
the page have the same fields value as that in the first complete
record on the page then we compress the undo record by avoid
including those field in the record provided the record is from
the same transaction as the first complete record on the page.
And, during fetch we get those informations from the first
complete record of the page.

Author: Dilip Kumar with contributions from Robert Haas, Amit Kapila,
        Thomas Munro, Vignesh C and Rafia Sabih
Reviewed-by: Earlier version of this patch is reviewed by Amit Kapila
Tested-by: Neha Sharma
Discussion: https://www.postgresql.org/message-id/CAFiTN-uVxxopn0UZ64%3DF-sydbETBbGjWapnBikNo1%3DXv78UeFw%40mail.gmail.com
---
 src/backend/access/undo/Makefile             |    2 +-
 src/backend/access/undo/README.undointerface |   47 +
 src/backend/access/undo/undoaccess.c         | 1652 ++++++++++++++++++
 src/backend/access/undo/undorecord.c         | 1014 +++++++++++
 src/backend/storage/page/bufpage.c           |   35 +
 src/include/access/transam.h                 |    1 +
 src/include/access/undoaccess.h              |  118 ++
 src/include/access/undolog.h                 |    6 +-
 src/include/access/undorecord.h              |  274 +++
 src/include/storage/bufpage.h                |   36 +
 10 files changed, 3183 insertions(+), 2 deletions(-)
 create mode 100644 src/backend/access/undo/README.undointerface
 create mode 100644 src/backend/access/undo/undoaccess.c
 create mode 100755 src/backend/access/undo/undorecord.c
 create mode 100644 src/include/access/undoaccess.h
 create mode 100644 src/include/access/undorecord.h

diff --git a/src/backend/access/undo/Makefile b/src/backend/access/undo/Makefile
index 219c6963cf8..049a416f071 100644
--- a/src/backend/access/undo/Makefile
+++ b/src/backend/access/undo/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/undo
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = undolog.o
+OBJS = undoaccess.o undolog.o undorecord.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/undo/README.undointerface b/src/backend/access/undo/README.undointerface
new file mode 100644
index 00000000000..daf40d099ea
--- /dev/null
+++ b/src/backend/access/undo/README.undointerface
@@ -0,0 +1,47 @@
+The undo interface layer is about packing undo records into undo pages.
+We are assuming that certain operations are happening in a transaction and
+those operation might need to insert the undo for the visibility or for
+rolling back the effect of the transaction.  So this layer will provide
+interfaces for inserting and fetching the undo records.
+
+We want those undo records to be as space efficient as possible.  In most cases
+we won't need to write them out to the disk but in some cases when the
+transaction is big we might.  So we don't want them to be big.  In order to
+make that happen we have chosen to do the record compression therein we avoid
+storing the common fields in all the undo record on the page provided the first
+record on the page is from the same transaction.  This layer don't worry about
+compressing the payload data but that is the AM specific information so the AM
+layer might need to worry about compressing that information.
+
+Inserting an undo record
+------------------------
+For inserting an undo record the caller must first prepare undo records then
+insert the prepared undo records.  The prepare will just make the undo space
+ready and lock the necessary buffers and the insert will actually write the
+prepared records into the undo buffers.  There is an option to set the prepare
+limit which allows to prepare the multiple undo records and insert all of them
+in one shot.  This will allow the caller to prepared all the undo records
+(which are required under single WAL logged operation) outside the critical
+section and then insert all of them at once under the critical section.
+
+Fetching an undo record
+------------------------
+To fetch an undo record, a caller must provide a valid undo record pointer.
+The api will read the undo records from the underlying pages and return the
+unpacked undo record.  There is also an interface to bulk fetch the undo
+records which takes the start and end undo record pointers as input and it
+will return the array of unpacked undo records between that range.
+
+Undo record compression:
+------------------------
+Under a single transaction, there are some fields which will be common across
+the undo records, for example, the full transaction id is always the same,
+apart from that, the reloid, cid and rmid can also be same.  So we can avoid
+storing the common information in all the records and we can fetch it from the
+record in which it is stored.  We don't want to do this compression for
+complete transaction otherwise we might need to fetch the undo record across
+pages so we have decided to do the page level compression that is if the first
+record on the page is from the same transaction and if any of these common
+field value is same as the first record of the page then we will not store that
+value in our record. But, while fetching the undo record we will read that
+value from the first complete record of the page and return it to the caller.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
new file mode 100644
index 00000000000..dd9d8a48261
--- /dev/null
+++ b/src/backend/access/undo/undoaccess.c
@@ -0,0 +1,1652 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.c
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undoaccess.c
+ *
+ *	INTERFACE ROUTINES
+ *		BeginUndoRecordInsert	- begin inserting one or multiple undo records
+ *		PrepareUndoInsert		- prepare undo record
+ *		InsertPreparedUndo		- insert prepared undo records
+ *		FinishUndoRecordInsert	- cleanup the insertion
+ *		BeginUndoFetch			- begin undo record fetch
+ *		UndoFetchRecord			- fetch the actual record
+ *		FinishUndoFetch			- cleanup after undo fetch record
+ *		UndoRecordRelease		- Release memory for unpacked undo record
+ *		UndoBulkFetchRecord		- Fetch undo record in bulk
+ *
+ * NOTES:
+ * Undo record layout:
+ *
+ * In each undo log undo records are stored in sequential order.  Each undo
+ * record consists of a undo record header, some optional headers and optional
+ * payload information.  The first undo record of each transaction in each undo
+ * log contains a group header that points to the next transaction's first undo
+ * record in the same undo log. This allows us to discard the entire
+ * transaction's undo log in one-shot.  The callers are not aware of the group
+ * header, it is entirely maintained by the undo interface layer for discarding
+ * the undo logs in groups.
+ *
+ * See undorecord.h for detailed information about the undo record header.
+ *
+ * Multiple logs:
+ *
+ * It is possible that the undo records for a transaction spans multiple undo
+ * logs.  We need some special handling while inserting them to ensure that
+ * discard and rollbacks can work sanely.
+ *
+ * We add a group header for the first record of the transaction for every log
+ * and link those group headers by uur_next_group pointer.  Additionally, In the
+ * first record of the transaction in new log after the log switch we will
+ * include an additional header called log switch header which will keep a
+ * pointer to the first undo record of the transaction in the previous log.
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/undoaccess.h"
+#include "access/undolog.h"
+#include "access/undolog_xlog.h"
+#include "access/undorecord.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablecmds.h"
+#include "miscadmin.h"
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+
+/*
+ * Structure to hold the prepared undo information.  PreparedUndoInsert will
+ * fill this information for each prepared undo.  InsertPreparedUndo will use
+ * this information to insert all prepared undo records.
+ */
+struct PreparedUndoSpace
+{
+	UndoRecPtr	urp;			/* undo record pointer */
+	UnpackedUndoRecord *urec;	/* unpacked undo record */
+	uint16		size;			/* undo record size */
+	int			undo_buffer_idx[MAX_BUFFER_PER_UNDO];	/* undo_buffer array
+														 * index */
+};
+
+/*
+ * Holds the undo buffers informations.  During prepare time (which is called
+ * outside the critical section) we pin and lock all the buffer for inserting
+ * the undo record and store the information in this structure.  Later, during
+ * actual insert we use this information to insert the record into the buffers.
+ */
+struct PreparedUndoBuffer
+{
+	UndoLogNumber logno;		/* Undo log number */
+	BlockNumber blk;			/* block number */
+	Buffer		buf;			/* buffer allocated for the block */
+	bool		zero;			/* new block full of zeroes */
+};
+
+static UnpackedUndoRecord *UndoGetOneRecord(UnpackedUndoRecord *urec,
+											UndoRecPtr urp, RelFileNode rnode,
+											UndoLogCategory category,
+											Buffer *prevbuf);
+static int	UndoGetBufferSlot(UndoRecordInsertContext *context,
+							  RelFileNode rnode, BlockNumber blk,
+							  ReadBufferMode rbm);
+
+/*
+ * Prepare undo record update
+ *
+ * It's a helper function for UndoRecordPrepareUpdateNext
+ *
+ * urecptr - Undo record pointer of the record which needs to be updated.
+ * undo_offset - Offset of the undo record field which needs to be updated.
+ */
+static int
+PrepareUndoRecordUpdate(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+						int size, int undo_offset)
+{
+	BlockNumber cur_blk;
+	RelFileNode rnode;
+	int			starting_byte;
+	int			bufidx;
+	int			index = 0;
+	int			remaining_bytes;
+	UndoRecordUpdateInfo *urec_update_info;
+
+	/*
+	 * Get a free slot to hold the prepared information for updating the undo
+	 * record.
+	 */
+	urec_update_info = &context->urec_update_info[context->nurec_update_info];
+
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/* Remaining bytes on the current block. */
+	remaining_bytes = BLCKSZ - starting_byte;
+
+	/*
+	 * Compute the block number and the offset of the block where we need to
+	 * start updating the undo record.
+	 */
+	if (remaining_bytes <= undo_offset)
+	{
+		/*
+		 * If the given undo record offset is not in this block then go to the
+		 * next block.
+		 */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+		starting_byte += (undo_offset - remaining_bytes);
+	}
+	else
+		starting_byte += undo_offset;
+
+	/* Remember the offset where we need to start updating the undo record. */
+	urec_update_info->offset = starting_byte;
+
+	Assert(size <= sizeof(UndoRecPtr));
+
+	/*
+	 * Loop until we have locked all the buffers which we need to update. At
+	 * the max we will lock two buffers as we are just updating 8 bytes.
+	 * Blocks are locked in the increasing order so we need not to worry about
+	 * the deadlock.  We need to lock all the buffers in the prepare phase and
+	 * the actual update will be done in the update phase under the critical
+	 * section.
+	 */
+	while (1)
+	{
+		/* Read and lock the buffer if not already locked. */
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, RBM_NORMAL);
+
+		/* Should never lock more than 2 buffers. */
+		Assert(index < 2);
+
+		/* Remember the prepared buffer index. */
+		urec_update_info->idx_undo_buffers[index++] = bufidx;
+		size -= (BLCKSZ - starting_byte);
+
+		/*
+		 * If the field we want to update is completely in the current block
+		 * then we are done.  Otherwise we need to go to the next block.
+		 */
+		if (size <= 0)
+			break;
+
+		starting_byte = UndoLogBlockHeaderSize;
+		cur_blk++;
+	}
+
+	/*
+	 * Remember the undo record pointer which we are updating and return the
+	 * index of the undo update info.  The caller will store the next/progress
+	 * field value in it based on which field it wants to update.
+	 */
+	urec_update_info->next = InvalidUndoRecPtr;
+	urec_update_info->progress = 0;
+	urec_update_info->urecptr = urecptr;
+
+	context->nurec_update_info++;
+	return (context->nurec_update_info - 1);
+}
+
+/*
+ * Prepare to update an undo record
+ *
+ * Prepare undo record update info for updating the the uur_next_group field
+ * in the undo record.  This function is called during the prepare phase it will
+ * pin and Lock all the necessary buffers required for updating the undo record.
+ * This function is called for
+ * a. Updating the next_group link in the group header.  This will make sure
+ * that the multiple transaction's undo record in the same log are linked and
+ * that will make it easy to traverse the undo during discard processing
+ * transaction by transaction.
+ * b. For multi-log transaction update the next_group link in the group header
+ * to connect the transaction first undo record across the undo logs.  This is
+ * required for collecting all the undo records of the transaction while
+ * applying the undo actions of the transaction.
+ *
+ * urecptr - undo record pointer of the next group which need to be set in the
+ *			 undo record pointed by prevurp.
+ * prevurp - undo record pointer to be updated.
+ */
+static void
+PrepareUndoRecordUpdateNext(UndoRecordInsertContext *context,
+							UndoRecPtr urecptr, UndoRecPtr prevurp)
+{
+	UndoLogSlot *slot;
+	int			index = 0;
+	int			offset;
+
+	/*
+	 * The absence of previous transaction's undo indicates that this backend
+	 * is preparing its first undo so there is nothing to be updated.
+	 */
+	if (!UndoRecPtrIsValid(prevurp))
+		return;
+
+	slot = UndoLogGetSlot(UndoRecPtrGetLogNo(prevurp), false);
+
+	/*
+	 * Acquire the discard update lock before reading the undo record so that
+	 * the undo record doesn't get discarded while we are reading undo
+	 * buffers.
+	 */
+	LWLockAcquire(&slot->discard_update_lock, LW_SHARED);
+
+	/* Check if it is already discarded. */
+	if (UndoRecPtrIsDiscarded(prevurp))
+	{
+		/* Release lock and return. */
+		LWLockRelease(&slot->discard_update_lock);
+		return;
+	}
+
+	/* Compute the offset of the uur_next_group in the undo record. */
+	offset = SizeOfUndoRecordHeader +
+		offsetof(UndoRecordGroup, urec_next_group);
+
+	index = PrepareUndoRecordUpdate(context, prevurp, sizeof(UndoRecPtr),
+									offset);
+
+	/* Store the next group's undo record pointer in urec_update_info. */
+	context->urec_update_info[index].next = urecptr;
+
+	/*
+	 * We can now release the discard lock as we have already acquired the
+	 * buffer locks.
+	 */
+	LWLockRelease(&slot->discard_update_lock);
+}
+
+/*
+ * Update the undo record
+ *
+ * This will overwrite uur_next_group or uur_progress fields in the undo record.
+ * Exact offset to be updated is already computed and necessary buffers are
+ * locked during the prepare phase.
+ */
+static void
+UndoRecordUpdate(UndoRecordInsertContext *context, int idx)
+{
+	Page		page = NULL;
+	int			i = 0;
+	int			write_bytes;
+	int			write_offset;
+	char	   *sourceptr;
+	UndoRecordUpdateInfo *urec_update_info = &context->urec_update_info[idx];
+
+	/* Whether to update the next or progress. */
+	if (UndoRecPtrIsValid(urec_update_info->next))
+	{
+		sourceptr = (char *) &urec_update_info->next;
+		write_bytes = sizeof(urec_update_info->next);
+	}
+	else
+	{
+		sourceptr = (char *) &urec_update_info->progress;
+		write_bytes = sizeof(urec_update_info->progress);
+	}
+
+	/* Where to start writing in the current block. */
+	write_offset = urec_update_info->offset;
+
+	/*
+	 * Start writing directly from the write offset calculated during prepare
+	 * phase.  And, loop until we write required bytes.
+	 */
+	while (1)
+	{
+		Buffer		buffer;
+		int			buf_idx;
+		int			can_write;
+		char	   *writeptr;
+
+		/* Should never write in more than 2 buffers. */
+		Assert(i < 2);
+
+		buf_idx = urec_update_info->idx_undo_buffers[i];
+		buffer = context->prepared_undo_buffers[buf_idx].buf;
+
+		/* How may bytes can be written in the current page. */
+		can_write = Min((BLCKSZ - write_offset), write_bytes);
+
+		/* If buffer is valid then write it otherwise just skip writing it. */
+		if (BufferIsValid(buffer))
+		{
+			page = BufferGetPage(buffer);
+
+			/* Compute the write pointer; write the buffer and mark it dirty. */
+			writeptr = (char *) page + write_offset;
+			memcpy(writeptr, sourceptr, can_write);
+			MarkBufferDirty(buffer);
+		}
+		else
+			Assert(InRecovery);
+
+		write_bytes -= can_write;
+
+		/*
+		 * If we have no more data to be written the break it otherwise go to
+		 * next block and continue writing there.
+		 */
+		if (write_bytes <= 0)
+			break;
+
+		sourceptr += can_write;
+		write_offset = UndoLogBlockHeaderSize;
+		i++;
+	}
+}
+
+/*
+ * Find or add the block info in the insert context's prepared buffer array.
+ *
+ * If the block is it is present in the prepared buffer array then just return
+ * its index otherwise read and lock the buffer and add an entry.
+ *
+ * Undo log insertions are append-only.  If the caller is writing new data that
+ * begins exactly at the beginning of a page, then there cannot be any useful
+ * data after that point.  In that case RBM_ZERO can be passed in as rbm so that
+ * we can skip a useless read of a disk block.  In all other cases, RBM_NORMAL
+ * should be passed in, to read the page in if it doesn't happen to be already
+ * in the buffer pool.
+ */
+static int
+UndoGetBufferSlot(UndoRecordInsertContext *context,
+				  RelFileNode rnode,
+				  BlockNumber blk,
+				  ReadBufferMode rbm)
+{
+	int			blkIndex;
+	Buffer		buffer;
+	XLogRedoAction action = BLK_NEEDS_REDO;
+	PreparedUndoBuffer *prepared_buffer;
+	UndoLogCategory category = context->alloc_context.category;
+
+	/*
+	 * Search the block in the prepared undo buffer array in our insert
+	 * context if we find it then simply return the index.
+	 */
+	for (blkIndex = 0; blkIndex < context->nprepared_undo_buffer; blkIndex++)
+	{
+		prepared_buffer = &context->prepared_undo_buffers[blkIndex];
+
+		/*
+		 * It's not enough to just compare the block number because this might
+		 * hold the blocks from different undo logs so compare the logno and
+		 * the blkno.
+		 */
+		if ((blk == prepared_buffer->blk) &&
+			(prepared_buffer->logno == rnode.relNode))
+		{
+			/* caller must hold exclusive lock on buffer */
+			Assert(BufferIsLocal(prepared_buffer->buf) ||
+				   LWLockHeldByMeInMode(BufferDescriptorGetContentLock(
+																	   GetBufferDescriptor(prepared_buffer->buf - 1)),
+										LW_EXCLUSIVE));
+			return blkIndex;
+		}
+	}
+
+	/*
+	 * We did not find the block the prepared buffer array so read the buffer
+	 * and add its entry.
+	 */
+	if (InRecovery)
+	{
+		/*
+		 * If block is found then this API will return the locked buffer so we
+		 * need not to lock it outside.
+		 */
+		action = XLogReadBufferForRedoBlock(context->alloc_context.xlog_record,
+											rnode,
+											UndoLogForkNum,
+											blk,
+											rbm,
+											false,
+											&buffer);
+	}
+	else
+	{
+		buffer = ReadBufferWithoutRelcache(rnode,
+										   UndoLogForkNum,
+										   blk,
+										   rbm,
+										   NULL,
+										   RelPersistenceForUndoLogCategory(category));
+
+		/* Lock the buffer */
+		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+	}
+
+	/*
+	 * Get the next empty slot in the prepared buffer array to store the
+	 * information of the new buffer we have locked.
+	 */
+	prepared_buffer =
+		&context->prepared_undo_buffers[context->nprepared_undo_buffer];
+
+	/*
+	 * During the recovery it's possible that the block is completely
+	 * discarded so just store it as InvalidBuffer in the prepared buffer
+	 * entry so that during actual insert we can skip inserting into these
+	 * blocks.
+	 */
+	if (action == BLK_NOTFOUND)
+	{
+		Assert(InRecovery);
+
+		prepared_buffer->buf = InvalidBuffer;
+		prepared_buffer->blk = blk;
+	}
+	else
+	{
+		/*
+		 * Remember the block and buffer information so that during multi
+		 * prepare we don't try to lock the same buffer again.
+		 */
+		prepared_buffer->buf = buffer;
+		prepared_buffer->blk = blk;
+		prepared_buffer->logno = rnode.relNode;
+		prepared_buffer->zero = rbm == RBM_ZERO;
+	}
+
+	context->nprepared_undo_buffer++;
+
+	return blkIndex;
+}
+
+/*
+ * Compress the undo record.
+ *
+ * Read the compression information from the first complete record of the page
+ * and try to compress the input undo record based on the compression
+ * information.
+ */
+static bool
+CompressUndoRecord(UndoRecordInsertContext *context, UndoRecPtr urecptr,
+				   UnpackedUndoRecord *urec, RelFileNode rnode)
+{
+	UndoPageHeader phdr;
+	Buffer		buffer;
+	Page		page;
+	int			bufidx;
+	int			offset;
+	UndoCompressionInfo compression_info;
+
+	/*
+	 * Read the buffer and compute the compression info.  We anyway need to
+	 * lock this buffer for inserting the undo record so locking here will not
+	 * cost us anything.  UndoGetBufferSlot will remember the reference to
+	 * this so that we will not try to read the buffer again.
+	 */
+	bufidx = UndoGetBufferSlot(context, rnode, UndoRecPtrGetBlockNum(urecptr),
+							   RBM_NORMAL);
+	buffer = context->prepared_undo_buffers[bufidx].buf;
+	page = BufferGetPage(buffer);
+	phdr = (UndoPageHeader) page;
+
+	/* Compute the offset of the next complete record on the page. */
+	offset = SizeOfUndoPageHeaderData + phdr->undo_len - phdr->record_offset;
+
+	/*
+	 * If we are inserting the first record after the partial record then
+	 * there is no complete record on the page based on which we can compress
+	 * our record.
+	 */
+	if (UndoRecPtrGetPageOffset(urecptr) == offset)
+		return false;
+
+	/* Read compression information from the record. */
+	UndoRecordGetCompressionInfo(page, offset, &compression_info);
+
+	/*
+	 * If the first record is not for the same transaction id for which we are
+	 * inserting the record then we can not compress this record.
+	 */
+	if (!FullTransactionIdEquals(urec->uur_fxid, compression_info.fxid))
+		return false;
+
+	/*
+	 * Exclude the information from the record which are same as the first
+	 * complete record of the page.
+	 */
+	urec->uur_info &= ~UREC_INFO_FXID;
+
+	if (urec->uur_rmid == compression_info.rmid)
+		urec->uur_info &= ~UREC_INFO_RMID;
+
+	if (urec->uur_reloid == compression_info.reloid)
+		urec->uur_info &= ~UREC_INFO_RELOID;
+
+	if (urec->uur_cid == compression_info.cid)
+		urec->uur_info &= ~UREC_INFO_CID;
+
+	return true;
+}
+
+/*
+ * Begin inserting undo records.
+ *
+ * This function must be called before all the undo records which are going to
+ * get inserted under a single WAL record.
+ * context - Undo insert context,  this will holds the memory and buffer
+ * information for preparing and inserting the undo records.  Caller must hold
+ * this memory until it insert undo record.
+ * category - Undo log category
+ * nprepared - max number of undo records that can be prepared before insert.
+ * xlog_record - In recovery for inserting the undo record the caller must pass
+ * its xlog reader state so that we can identify from which undo logno we need
+ * to allocate the space for the WAL.  To achieve this during DO time the caller
+ * must register all the undo buffer it has updated by calling
+ * RegisterUndoBuffers.
+ */
+void
+BeginUndoRecordInsert(UndoRecordInsertContext *context,
+					  UndoLogCategory category,
+					  int nprepared,
+					  XLogReaderState *xlog_record)
+{
+	uint32		nbuffers;
+
+	/* At least one prepared record should be there. */
+	if (nprepared <= 0)
+		elog(ERROR, "at least one undo record should be prepared");
+
+	/* Initialize undo log context. */
+	UndoLogBeginInsert(&context->alloc_context, category, xlog_record);
+
+	/* Initialize undo insert context. */
+	context->max_prepared_undo = nprepared;
+	context->nprepared_undo = 0;
+	context->nprepared_undo_buffer = 0;
+	context->nurec_update_info = 0;
+
+	/* Allocate memory for prepared undo record space. */
+	context->prepared_undo = (PreparedUndoSpace *) palloc(nprepared *
+														  sizeof(PreparedUndoSpace));
+
+	/* Compute number of buffers. */
+	nbuffers = (nprepared + MAX_UNDO_UPDATE_INFO) * MAX_BUFFER_PER_UNDO;
+
+	/* Allocate memory for the prepared buffers. */
+	context->prepared_undo_buffers =
+		palloc(nbuffers * sizeof(PreparedUndoBuffer));
+}
+
+/*
+ * Prepare to insert an undo record.
+ *
+ * Call PrepareUndoInsert to tell the undo subsystem about the undo record you
+ * intended to insert.  Upon return, the necessary undo buffers are pinned and
+ * locked.
+ *
+ * This should be called outside the  critical section.
+ */
+UndoRecPtr
+PrepareUndoInsert(UndoRecordInsertContext *context,
+				  UnpackedUndoRecord *urec,
+				  Oid dbid)
+{
+	UndoRecordSize size;
+	UndoRecPtr	urecptr = InvalidUndoRecPtr;
+	RelFileNode rnode;
+	UndoRecordSize cur_size = 0;
+	BlockNumber cur_blk;
+	FullTransactionId fxid;
+	int			starting_byte;
+	int			index = 0;
+	int			bufidx;
+	bool		resize = false;
+	ReadBufferMode rbm;
+	bool		need_xact_header;
+	UndoRecPtr	last_xact_start;
+	UndoRecPtr	prevlog_xact_start = InvalidUndoRecPtr;
+	PreparedUndoSpace *prepared_undo;
+
+	/* Already reached maximum prepared limit. */
+	if (context->nprepared_undo == context->max_prepared_undo)
+		elog(ERROR, "already reached the maximum prepared limit");
+
+	/* Extract the full transaction id from the input undo record. */
+	fxid = urec->uur_fxid;
+	Assert(FullTransactionIdIsValid(fxid));
+
+	/*
+	 * We don't yet know if this record needs a transaction header (ie is the
+	 * first undo record for a given transaction in a given undo log), because
+	 * you can only find out by allocating.  We'll resolve this circularity by
+	 * allocating enough space for a transaction header.  Similarly log switch
+	 * will only be detected after allocation so include the log switch header
+	 * and common information because in case of log switch we need to include
+	 * log switch header and also we need to include common header.  After
+	 * allocation, we'll only advance by as many bytes as we turn out to need.
+	 */
+	UndoRecordSetInfo(urec);
+	urec->uur_info |= UREC_INFO_GROUP;
+	urec->uur_info |= UREC_INFO_LOGSWITCH;
+	urec->uur_info |= UREC_INFO_PAGE_COMMON;
+
+	size = UndoRecordExpectedSize(urec);
+
+	/* Allocate space for the undo record. */
+	if (InRecovery)
+	{
+		/*
+		 * We'll figure out where the space needs to be allocated by
+		 * inspecting the xlog_record.  We don't expect to see temporary or
+		 * unlogged undo data here.
+		 */
+		Assert(context->alloc_context.category != UNDO_TEMP &&
+			   context->alloc_context.category != UNDO_UNLOGGED);
+		urecptr = UndoLogAllocateInRecovery(&context->alloc_context,
+											XidFromFullTransactionId(fxid),
+											size,
+											&need_xact_header,
+											&last_xact_start,
+											&prevlog_xact_start);
+	}
+	else
+		urecptr = UndoLogAllocate(&context->alloc_context,
+								  size,
+								  &need_xact_header, &last_xact_start,
+								  &prevlog_xact_start);
+
+	/*
+	 * If we need a group header then allocate memory for it and initialize
+	 * the same.
+	 */
+	if (need_xact_header)
+	{
+		urec->uur_group = palloc(sizeof(UndoRecordGroup));
+		urec->uur_group->urec_dbid = dbid;
+		urec->uur_group->urec_progress = InvalidBlockNumber;
+		urec->uur_group->urec_next_group = InvalidUndoRecPtr;
+	}
+	else
+	{
+		/* We don't need a group header after all. */
+		urec->uur_info &= ~UREC_INFO_GROUP;
+		resize = true;
+		urec->uur_group = NULL;
+	}
+
+	/*
+	 * If undo log got switched then allocate the memory for the log switch
+	 * header and initialize the same.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+	{
+		urec->uur_logswitch = palloc(sizeof(UndoRecordLogSwitch));
+		urec->uur_logswitch->urec_prevlogstart = prevlog_xact_start;
+	}
+	else
+	{
+		/* We don't need a log switch header after all. */
+		urec->uur_info &= ~UREC_INFO_LOGSWITCH;
+		resize = true;
+		urec->uur_logswitch = NULL;
+	}
+
+	/* Populate the rnode for the undo record pointer. */
+	UndoRecPtrAssignRelFileNode(rnode, urecptr);
+
+	/*
+	 * If this is not the first record of the transaction for this log and we
+	 * are not inserting the first record on the page then we can compress
+	 * this record by avoid including the field which are same as the first
+	 * complete record on the page provided that is from the same transaction.
+	 */
+	if (!need_xact_header && !prevlog_xact_start &&
+		UndoRecPtrGetPageOffset(urecptr) != SizeOfUndoPageHeaderData &&
+		CompressUndoRecord(context, urecptr, urec, rnode))
+		resize = true;
+
+	/*
+	 * We might have excluded some of the header from the undo record so
+	 * recompute the expected record size.
+	 */
+	if (resize)
+		size = UndoRecordExpectedSize(urec);
+
+	/*
+	 * If the transaction's undo records are split across the undo logs then
+	 * link the transaction's group header in the previous log to the
+	 * transaction group header in the current log by updating the
+	 * uur_next_group field in the group header.
+	 */
+	if (UndoRecPtrIsValid(prevlog_xact_start))
+		PrepareUndoRecordUpdateNext(context, urecptr, prevlog_xact_start);
+
+	/*
+	 * If there is a physically preceding transaction in this undo log, and we
+	 * are writing the first record for this transaction that is in this undo
+	 * log (not necessarily the first ever for the transaction, because we
+	 * could have switched logs), then we need to set this transaction's group
+	 * header pointer in the previous transaction's group header.
+	 */
+	if (need_xact_header)
+		PrepareUndoRecordUpdateNext(context, urecptr, last_xact_start);
+
+	cur_blk = UndoRecPtrGetBlockNum(urecptr);
+	starting_byte = UndoRecPtrGetPageOffset(urecptr);
+
+	/*
+	 * If we happen to be writing the very first byte into this page, then
+	 * there is no need to read from disk.
+	 */
+	if (starting_byte == UndoLogBlockHeaderSize)
+		rbm = RBM_ZERO;
+	else
+		rbm = RBM_NORMAL;
+
+	/* Get a free slot in the prepare undo space for preparing our undo. */
+	prepared_undo = &context->prepared_undo[context->nprepared_undo];
+
+	/* Loop to lock the required buffers for inserting this undo record. */
+	do
+	{
+		bufidx = UndoGetBufferSlot(context, rnode, cur_blk, rbm);
+		if (cur_size == 0)
+			cur_size = BLCKSZ - starting_byte;
+		else
+			cur_size += BLCKSZ - UndoLogBlockHeaderSize;
+
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(index < MAX_BUFFER_PER_UNDO);
+
+		/* Keep the track of the buffers we have pinned and locked. */
+		prepared_undo->undo_buffer_idx[index++] = bufidx;
+
+		/*
+		 * If we need more pages they'll be all new so we can definitely skip
+		 * reading from disk.
+		 */
+		rbm = RBM_ZERO;
+		cur_blk++;
+	} while (cur_size < size);
+
+	/*
+	 * Advance the local insert pointer in the context past this record so
+	 * that during multi-prepare we get the correct insert location for the
+	 * next record.
+	 */
+	UndoLogAdvance(&context->alloc_context, size);
+
+	/*
+	 * Save prepared undo record information into the context which will be
+	 * used by InsertPreparedUndo to insert the undo record.
+	 */
+	prepared_undo->urec = urec;
+	prepared_undo->urp = urecptr;
+	prepared_undo->size = size;
+	context->nprepared_undo++;
+
+	return urecptr;
+}
+
+/*
+ * Insert one undo record
+ *
+ * Helper function for InsertPreparedUndo.
+ */
+static void
+InsertUndoRecord(UndoRecordInsertContext *context,
+				 PreparedUndoSpace * prepared_undo)
+{
+	Buffer		buffer;
+	Page		page = NULL;
+	int			starting_byte;
+	int			bufidx = 0;
+	UndoPackContext ucontext = {{0}};
+	PreparedUndoBuffer *prepared_buffres = context->prepared_undo_buffers;
+	UnpackedUndoRecord *urec = prepared_undo->urec;
+
+	/* Compute starting offset of the page where to start inserting. */
+	starting_byte = UndoRecPtrGetPageOffset(prepared_undo->urp);
+
+	/* Initiate inserting the undo record. */
+	BeginInsertUndo(&ucontext, urec, prepared_undo->size);
+
+	/* Main loop for writing the undo record. */
+	while (1)
+	{
+		/* undo record can't use buffers more than MAX_BUFFER_PER_UNDO. */
+		Assert(bufidx < MAX_BUFFER_PER_UNDO);
+		buffer = prepared_buffres[prepared_undo->undo_buffer_idx[bufidx]].buf;
+
+		/*
+		 * During recovery, there might be some blocks which are already
+		 * deleted due to some discard command so we can just skip inserting
+		 * into those blocks.
+		 */
+		if (!BufferIsValid(buffer))
+		{
+			Assert(InRecovery);
+
+			/*
+			 * Instead of inserting the actual record this function will just
+			 * update the bookkeeping information in the context.
+			 */
+			SkipInsertingUndoData(&ucontext, BLCKSZ - starting_byte);
+		}
+		else
+		{
+			page = BufferGetPage(buffer);
+
+			/*
+			 * Initialize the page whenever we try to write the first record
+			 * in page.  We start writing immediately after the block header.
+			 */
+			if (starting_byte == UndoLogBlockHeaderSize)
+				UndoPageInit(page, BLCKSZ, urec->uur_info,
+							 ucontext.already_processed, prepared_undo->size);
+
+			/*
+			 * Write undo record data into the page and mark the buffer dirty.
+			 */
+			InsertUndoData(&ucontext, page, starting_byte);
+			MarkBufferDirty(buffer);
+		}
+
+		/* Record insertion is complete. */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+			break;
+
+		/*
+		 * Record couldn't fit in the current block so insert the remaining
+		 * record in the next block.  In next block start inserting right
+		 * after the block header.
+		 */
+		starting_byte = UndoLogBlockHeaderSize;
+		bufidx++;
+	}
+}
+
+/*
+ * Insert previously-prepared undo records.
+ *
+ * This function will write the actual undo record into the buffers which are
+ * already pinned and locked in PreparedUndoInsert, and mark them dirty.  This
+ * step should be performed inside a critical section.
+ */
+void
+InsertPreparedUndo(UndoRecordInsertContext *context)
+{
+	PreparedUndoSpace *prepared_undo;
+	int			idx;
+	int			i;
+
+	/* There must be at least one prepared undo record. */
+	Assert(context->nprepared_undo > 0);
+
+	/*
+	 * This must be called under a critical section or we must be in recovery.
+	 */
+	Assert(InRecovery || CritSectionCount > 0);
+
+	for (idx = 0; idx < context->nprepared_undo; idx++)
+	{
+		prepared_undo = &context->prepared_undo[idx];
+
+		Assert(prepared_undo->size ==
+			   UndoRecordExpectedSize(prepared_undo->urec));
+
+		/* Insert the undo record. */
+		InsertUndoRecord(context, prepared_undo);
+
+		/* Advance the insert pointer past this record. */
+		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
+	}
+
+	/* Update the group header of the previous transaction. */
+	for (i = 0; i < context->nurec_update_info; i++)
+		UndoRecordUpdate(context, i);
+}
+
+/*
+ * Release all the memory and buffer pins hold for inserting the undo records.
+ */
+void
+FinishUndoRecordInsert(UndoRecordInsertContext *context)
+{
+	int			i;
+
+	/* Release buffer pins and locks. */
+	for (i = 0; i < context->nprepared_undo_buffer; i++)
+		if (BufferIsValid(context->prepared_undo_buffers[i].buf))
+			UnlockReleaseBuffer(context->prepared_undo_buffers[i].buf);
+
+	/*
+	 * Release memory for the transaction header and log switch header if we
+	 * have allocated it during the prepare time.
+	 */
+	for (i = 0; i < context->nprepared_undo; i++)
+	{
+		if (context->prepared_undo[i].urec->uur_group)
+			pfree(context->prepared_undo[i].urec->uur_group);
+
+		if (context->prepared_undo[i].urec->uur_logswitch)
+			pfree(context->prepared_undo[i].urec->uur_logswitch);
+	}
+
+	/*
+	 * Free the memory allocated for the prepared undo space and the prepared
+	 * undo buffers.
+	 */
+	pfree(context->prepared_undo_buffers);
+	pfree(context->prepared_undo);
+}
+
+/*
+ * Get undo page compression information.
+ *
+ * Read undo record compression information from the first complete record of
+ * the page and store them in the current unpack context.
+ */
+static void
+UndoPageGetCompressionInfo(UndoPackContext *ucontext, UndoRecPtr urp, Page page)
+{
+	int			offset;
+	UndoPageHeader phdr = (UndoPageHeader) page;
+	UndoCompressionInfo compresssion_info;
+
+	/* Compute the offset of the next complete record on the page. */
+	offset = SizeOfUndoPageHeaderData + phdr->undo_len - phdr->record_offset;
+
+	/* Fetch the undo page compression informations. */
+	UndoRecordGetCompressionInfo(page, offset, &compresssion_info);
+
+	/*
+	 * Get all missing common header information from the first undo record of
+	 * the page.
+	 */
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) == 0)
+		ucontext->urec_rmid = compresssion_info.rmid;
+
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) == 0)
+		ucontext->urec_reloid = compresssion_info.reloid;
+
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_FXID) == 0)
+		ucontext->urec_fxid = compresssion_info.fxid;
+
+	if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) == 0)
+		ucontext->urec_cid = compresssion_info.cid;
+
+	ucontext->urec_hd.urec_info |= UREC_INFO_PAGE_COMMON;
+}
+
+/*
+ * Helper function for UndoFetchRecord and UndoBulkFetchRecord
+ *
+ * curbuf - If an input buffer is valid then this function will not release the
+ * pin on that buffer.  If the buffer is not valid then it will assign curbuf
+ * with the first buffer of the current undo record and also it will keep the
+ * pin on that buffer in a hope that while traversing the undo chain the caller
+ * might want to read the previous undo record from the same block.
+ */
+static UnpackedUndoRecord *
+UndoGetOneRecord(UnpackedUndoRecord *urec, UndoRecPtr urp, RelFileNode rnode,
+				 UndoLogCategory category, Buffer *curbuf)
+{
+	Page		page;
+	int			starting_byte = UndoRecPtrGetPageOffset(urp);
+	BlockNumber cur_blk;
+	UndoPackContext ucontext = {{0}};
+	Buffer		buffer = *curbuf;
+
+	cur_blk = UndoRecPtrGetBlockNum(urp);
+
+	/* Initiate unpacking one undo record. */
+	BeginUnpackUndo(&ucontext);
+
+	while (true)
+	{
+		/* If we already have a buffer then no need to allocate a new one. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum, cur_blk,
+											   RBM_NORMAL, NULL,
+											   RelPersistenceForUndoLogCategory(category));
+
+			/*
+			 * Remember the first buffer where this undo started as next undo
+			 * record what we fetch might fall on the same buffer.
+			 */
+			if (!BufferIsValid(*curbuf))
+				*curbuf = buffer;
+		}
+
+		/* Acquire shared lock on the buffer before reading undo from it. */
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+		page = BufferGetPage(buffer);
+
+		UnpackUndoData(&ucontext, page, starting_byte);
+
+		/*
+		 * We are done if we have reached to the done stage otherwise move to
+		 * next block and continue reading from there.
+		 */
+		if (ucontext.stage == UNDO_PACK_STAGE_DONE)
+		{
+			/*
+			 * If the buffer is input buffer or the first buffer of the record
+			 * then just release the lock and keep the pin.  Otherwise, unlock
+			 * the buffer and also release the pin.
+			 */
+			if (buffer == *curbuf)
+				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+			else
+				UnlockReleaseBuffer(buffer);
+
+			/*
+			 * If any of the common header field is not available in the
+			 * current undo record then we must read it from the first
+			 * complete record of the page.  If any of these common field is
+			 * missing from the undo record then it's guarantee that the first
+			 * complete record on the page must be from the same transaction
+			 * otherwise we will always include all these common fields in the
+			 * undo record.
+			 */
+			if ((ucontext.urec_hd.urec_info & UREC_INFO_PAGE_COMMON) !=
+				UREC_INFO_PAGE_COMMON)
+				UndoPageGetCompressionInfo(&ucontext, urp,
+										   BufferGetPage(*curbuf));
+
+			break;
+		}
+
+		/*
+		 * If the buffer is input buffer or the first buffer of the record
+		 * then just release the lock and keep the pin.  Otherwise, unlock the
+		 * buffer and also release the pin.
+		 */
+		if (buffer == *curbuf)
+			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+		else
+			UnlockReleaseBuffer(buffer);
+
+		buffer = InvalidBuffer;
+
+		/* Go to next block. */
+		cur_blk++;
+		starting_byte = UndoLogBlockHeaderSize;
+	}
+
+	/* Final step of unpacking. */
+	FinishUnpackUndo(&ucontext, urec);
+
+	return urec;
+}
+
+/*
+ * BeginUndoFetch to fetch undo record.
+ */
+void
+BeginUndoFetch(UndoRecordFetchContext *context)
+{
+	context->buffer = InvalidBuffer;
+	context->urp = InvalidUndoRecPtr;
+}
+
+/*
+ * Fetch the undo record for given undo record pointer.
+ *
+ * This will internally allocate the memory for the unpacked undo record which
+ * will hold the pointers to the optional headers and the variable data.
+ * The undo record should be freed by the caller by calling ReleaseUndoRecord.
+ * This function will hold the pin on the buffer where we read the previous undo
+ * record so that when this function is called repeatedly with the same context
+ * then it can be benefitted by avoid reading the buffer again if the current
+ * undo record is in the same buffer.
+ */
+UnpackedUndoRecord *
+UndoFetchRecord(UndoRecordFetchContext *context, UndoRecPtr urp)
+{
+	RelFileNode rnode;
+	int			logno;
+	UndoLogSlot *slot;
+	UnpackedUndoRecord *uur = NULL;
+
+	logno = UndoRecPtrGetLogNo(urp);
+	slot = UndoLogGetSlot(logno, true);
+
+	/*
+	 * If slot is NULL that means undo log number is unknown.  Presumably it
+	 * has been entirely discarded.
+	 */
+	if (slot == NULL)
+		return NULL;
+
+	/*
+	 * Prevent UndoDiscardOneLog() from discarding data while we try to read
+	 * it.  Usually we would acquire log->mutex to read log->meta members, but
+	 * in this case we know that discard can't move without also holding
+	 * log->discard_lock.
+	 *
+	 * In Hot Standby mode log->oldest_data is never initialized because it's
+	 * get updated by undo discard worker whereas in HotStandby undo logs are
+	 * getting discarded using discard WAL.  So in HotStandby we can directly
+	 * check whether the undo record pointer is discarded or not.  But, we can
+	 * not do same for normal case because discard worker can concurrently
+	 * discard the undo logs.
+	 *
+	 * XXX We can avoid this check by always initializing log->oldest_data in
+	 * HotStandby mode as well whenever we apply discard WAL.  But, for doing
+	 * that we need to acquire discard lock just for setting this variable?
+	 */
+	if (InHotStandby)
+	{
+		if (UndoRecPtrIsDiscarded(urp))
+			return NULL;
+	}
+	else
+	{
+		LWLockAcquire(&slot->discard_lock, LW_SHARED);
+		if (slot->logno != logno || urp < slot->oldest_data)
+		{
+			/*
+			 * The slot has been recycled because the undo log was entirely
+			 * discarded, or the pointer is before the oldest data.
+			 */
+			LWLockRelease(&slot->discard_lock);
+			return NULL;
+		}
+	}
+
+	/*
+	 * Allocate memory for holding the undo record, caller should be
+	 * responsible for freeing this memory by calling UndoRecordRelease.
+	 */
+	uur = palloc0(sizeof(UnpackedUndoRecord));
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+
+	/*
+	 * Before fetching the next record check whether we have a valid buffer in
+	 * the context.  If so and if we are reading the current record from the
+	 * same then pass that buffer to fetch the undo record, otherwise release
+	 * the buffer.
+	 */
+	if (BufferIsValid(context->buffer) &&
+		(UndoRecPtrGetLogNo(context->urp) != UndoRecPtrGetLogNo(urp) ||
+		 UndoRecPtrGetBlockNum(context->urp) != UndoRecPtrGetBlockNum(urp)))
+	{
+		ReleaseBuffer(context->buffer);
+		context->buffer = InvalidBuffer;
+	}
+
+	/* Fetch the current undo record. */
+	UndoGetOneRecord(uur, urp, rnode, slot->meta.category, &context->buffer);
+
+	/* Release the discard lock after fetching the record. */
+	if (!InHotStandby)
+		LWLockRelease(&slot->discard_lock);
+
+	context->urp = urp;
+
+	return uur;
+}
+
+/*
+ * Finish undo record fetch.
+ */
+void
+FinishUndoFetch(UndoRecordFetchContext *context)
+{
+	if (BufferIsValid(context->buffer))
+		ReleaseBuffer(context->buffer);
+}
+
+/*
+ * Release the memory of the undo record.
+ *
+ * Release memory of the undo record and other variable data of the undo record
+ * allocated by UndoFetchRecord and UndoBulkFetchRecord.
+ */
+void
+UndoRecordRelease(UnpackedUndoRecord *urec)
+{
+	if (urec->uur_payload.data)
+		pfree(urec->uur_payload.data);
+
+	if (urec->uur_tuple.data)
+		pfree(urec->uur_tuple.data);
+
+	if (urec->uur_group)
+		pfree(urec->uur_group);
+
+	if (urec->uur_logswitch)
+		pfree(urec->uur_logswitch);
+
+	/* Release the memory of the undo record. */
+	pfree(urec);
+}
+
+/*
+ * Prefetch undo pages, if the prefetch_pages are behind the prefetch_target
+ */
+static void
+PrefetchUndoPages(RelFileNode rnode, int prefetch_target, int *prefetch_pages,
+				  BlockNumber to_blkno, BlockNumber from_blkno,
+				  UndoLogCategory category)
+{
+	int			nprefetch;
+	BlockNumber startblock;
+	BlockNumber lastprefetched;
+
+	/* Calculate last prefetched page in the previous iteration. */
+	lastprefetched = from_blkno - *prefetch_pages;
+
+	/* We have already prefetched all the pages of the transaction's undo. */
+	if (lastprefetched <= to_blkno)
+		return;
+
+	/* Calculate number of blocks to be prefetched. */
+	nprefetch =
+		Min(prefetch_target - *prefetch_pages, lastprefetched - to_blkno);
+
+	/* Where to start prefetch. */
+	startblock = lastprefetched - nprefetch;
+
+	while (nprefetch--)
+	{
+		PrefetchBufferWithoutRelcache(rnode, MAIN_FORKNUM, startblock++,
+									  category == UNDO_TEMP);
+		(*prefetch_pages)++;
+	}
+}
+
+/*
+ * Read undo records of the transaction in bulk
+ *
+ * Read undo records between from_urecptr and to_urecptr until we exhaust the
+ * the memory size specified by max_result_size.  This will start reading undo
+ * records starting from from_urecptr.  It will read the transaction's undo
+ * record backwardly all the way upto to_urecptr.
+ *
+ * If we could not read all the records till to_urecptr then the caller should
+ * consume current set of records and call this function again.
+ *
+ * While traversing the undo record if the log switch is encountered then this
+ * API will stop processing further record and set the last record in the
+ * previous log as the from_urecptr so that the caller can call this API again
+ * to fetch the remaining records.
+ *
+ * XXX instead of this API handles this we can make caller to pass from and to
+ * urecptr from the same undo log?
+ *
+ * from_urecptr		- Where to start fetching the undo records.  If we can not
+ *					  read all the records because of memory limit then this
+ *					  will be set to the next from undo record pointer from
+ *					  where we need to start fetching on next call. Otherwise it
+ *					  will be set to InvalidUndoRecPtr.
+ * to_urecptr		- Last undo record pointer to be fetched.
+ * max_result_size	- Memory segment limit to collect undo records.
+ * nrecords			- Number of undo records read.
+ */
+UndoRecInfo *
+UndoBulkFetchRecord(UndoRecPtr *from_urecptr, UndoRecPtr to_urecptr,
+					int max_result_size, int *nrecords)
+{
+	RelFileNode rnode;
+	UndoRecPtr	urecptr,
+				prev_urec_ptr;
+	BlockNumber blkno;
+	BlockNumber to_blkno;
+	Buffer		buffer = InvalidBuffer;
+	UnpackedUndoRecord *uur = NULL;
+	UndoRecInfo *urp_array;
+	int			urp_array_size = 1024;
+	int			urp_index = 0;
+	int			prefetch_target = 0;
+	int			prefetch_pages = 0;
+	Size		total_size = 0;
+
+	/*
+	 * If we are fetching undo records from more than one logs, We can not
+	 * compute how many block to prefetch from the current log because we
+	 * don't know that in how many blocks of the log this transaction has
+	 * inserted the undo.  Hence, we can't use prefetching in this case.
+	 *
+	 * XXX should we always restrict caller to input from and to urecptr from
+	 * the same undo log.  But, it may not be possible for the caller to
+	 * always detect that?
+	 */
+	if (UndoRecPtrGetLogNo(*from_urecptr) == UndoRecPtrGetLogNo(to_urecptr))
+		prefetch_target = target_prefetch_pages;
+
+	/*
+	 * Allocate initial memory to hold the undo record info, we can expand it
+	 * if needed.
+	 */
+	urp_array = (UndoRecInfo *) palloc(sizeof(UndoRecInfo) * urp_array_size);
+	urecptr = *from_urecptr;
+
+	prev_urec_ptr = InvalidUndoRecPtr;
+	*from_urecptr = InvalidUndoRecPtr;
+
+	/* Read undo chain backward until we reach to the first undo record.  */
+	while (1)
+	{
+		BlockNumber from_blkno;
+		UndoLogSlot *slot;
+		UndoLogCategory category;
+		int			logno;
+
+		logno = UndoRecPtrGetLogNo(urecptr);
+		slot = UndoLogGetSlot(logno, true);
+		if (slot == NULL)
+		{
+			if (BufferIsValid(buffer))
+				ReleaseBuffer(buffer);
+			return NULL;
+		}
+		category = slot->meta.category;
+
+		UndoRecPtrAssignRelFileNode(rnode, urecptr);
+		to_blkno = UndoRecPtrGetBlockNum(to_urecptr);
+		from_blkno = UndoRecPtrGetBlockNum(urecptr);
+
+		/* Allocate memory for next undo record. */
+		uur = palloc0(sizeof(UnpackedUndoRecord));
+
+		/*
+		 * If next undo record pointer to be fetched is not on the same block
+		 * then release the old buffer and reduce the prefetch_pages count by
+		 * one as we have consumed one page. Otherwise, just pass the old
+		 * buffer into the UndoGetOneRecord so that it doesn't read the buffer
+		 * again.
+		 */
+		blkno = UndoRecPtrGetBlockNum(urecptr);
+		if (!UndoRecPtrIsValid(prev_urec_ptr) ||
+			UndoRecPtrGetLogNo(prev_urec_ptr) != logno ||
+			UndoRecPtrGetBlockNum(prev_urec_ptr) != blkno)
+		{
+			/* Release the previous buffer */
+			if (BufferIsValid(buffer))
+			{
+				ReleaseBuffer(buffer);
+				buffer = InvalidBuffer;
+			}
+
+			if (prefetch_pages > 0)
+				prefetch_pages--;
+		}
+
+		/*
+		 * If prefetch_pages are behind prefetch_target then it's time to
+		 * prefetch again.
+		 */
+		if (prefetch_pages < prefetch_target)
+			PrefetchUndoPages(rnode, prefetch_target, &prefetch_pages, to_blkno,
+							  from_blkno, category);
+
+		/* Get the undo record. */
+		UndoGetOneRecord(uur, urecptr, rnode, category, &buffer);
+
+		/* Remember the previous undo record pointer. */
+		prev_urec_ptr = urecptr;
+
+		/*
+		 * Calculate the previous undo record pointer of the transaction.  If
+		 * we have detected the log switch then compute the latest undo record
+		 * of the transaction in the previous log and set this as next
+		 * from_urecptr.  On log_switch don't process further record and
+		 * return handle to the caller with appropriate from_urecptr so that
+		 * caller can resume the fetching.
+		 */
+		if (uur->uur_logswitch)
+		{
+			urecptr = UndoGetPrevUrp(uur, prev_urec_ptr, buffer, category);
+			*from_urecptr = urecptr;
+		}
+		else if (prev_urec_ptr == to_urecptr)
+			urecptr = InvalidUndoRecPtr;
+		else
+			urecptr = UndoGetPrevUrp(uur, prev_urec_ptr, buffer, category);
+
+		/*
+		 * We have consumed all the elements of the urp_array so expand its
+		 * size.
+		 */
+		if (urp_index >= urp_array_size)
+		{
+			urp_array_size *= 2;
+			urp_array =
+				repalloc(urp_array, sizeof(UndoRecInfo) * urp_array_size);
+		}
+
+		/* Add entry in the urp_array */
+		urp_array[urp_index].index = urp_index;
+		urp_array[urp_index].urp = prev_urec_ptr;
+		urp_array[urp_index].uur = uur;
+		urp_index++;
+
+		/* We have fetched all the undo records for the transaction. */
+		if (!UndoRecPtrIsValid(urecptr) || (prev_urec_ptr == to_urecptr))
+			break;
+
+		/* Add the size required to store unpacked undo record in memory. */
+		total_size += sizeof(UnpackedUndoRecord) + UndoRecordPayloadSize(uur);
+
+		/*
+		 * Including current record, if we have crossed the memory limit or
+		 * undo log got switched then stop processing more records.  Remember
+		 * to set the from_urecptr so that on next call we can resume fetching
+		 * undo records where we left it.
+		 *
+		 * XXX we need this special handling for the log switch because in
+		 * some cases caller is enable to identify the log boundary but it
+		 * expect us to read the undo record only for one log at a time.
+		 */
+		if (total_size >= max_result_size || uur->uur_logswitch)
+		{
+			*from_urecptr = urecptr;
+			break;
+		}
+	}
+
+	/* Release the last buffer. */
+	if (BufferIsValid(buffer))
+		ReleaseBuffer(buffer);
+
+	*nrecords = urp_index;
+
+	return urp_array;
+}
+
+/*
+ * Register the undo buffers.
+ */
+void
+RegisterUndoLogBuffers(UndoRecordInsertContext *context, uint8 first_block_id)
+{
+	int			idx;
+	int			flags;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+	{
+		flags = context->prepared_undo_buffers[idx].zero
+			? REGBUF_KEEP_DATA_AFTER_CP | REGBUF_WILL_INIT
+			: REGBUF_KEEP_DATA_AFTER_CP;
+		XLogRegisterBuffer(first_block_id + idx,
+						   context->prepared_undo_buffers[idx].buf, flags);
+		UndoLogRegister(&context->alloc_context, first_block_id + idx,
+						context->prepared_undo_buffers[idx].logno);
+	}
+}
+
+/*
+ * Set LSN on undo page.
+*/
+void
+UndoLogBuffersSetLSN(UndoRecordInsertContext *context, XLogRecPtr recptr)
+{
+	int			idx;
+
+	for (idx = 0; idx < context->nprepared_undo_buffer; idx++)
+		PageSetLSN(BufferGetPage(context->prepared_undo_buffers[idx].buf),
+				   recptr);
+}
+
+/*
+ * Read length of the previous undo record.
+ *
+ * This function will take an undo record pointer as an input and read the
+ * length of the previous undo record which is stored at the end of the previous
+ * undo record.  If the undo record is split then this will add the undo block
+ * header size in the total length.
+ */
+static uint16
+UndoGetPrevRecordLen(UndoRecPtr urp, Buffer input_buffer,
+					 UndoLogCategory category)
+{
+	UndoLogOffset offset = UndoRecPtrGetPageOffset(urp) - 1;
+	BlockNumber cur_blk = UndoRecPtrGetBlockNum(urp);
+	Buffer		buffer = input_buffer;
+	Page		page = NULL;
+	char	   *pagedata = NULL;
+	union
+	{
+		char		len_bytes[2];
+		uint16		len;
+	}			prevlen;
+	RelFileNode rnode;
+	int			byte_to_read = 2;
+	char		persistence;
+	uint16		prev_rec_len = 0;
+
+	/* Get relfilenode. */
+	UndoRecPtrAssignRelFileNode(rnode, urp);
+	persistence = RelPersistenceForUndoLogCategory(category);
+
+	if (BufferIsValid(buffer))
+	{
+		page = BufferGetPage(buffer);
+		pagedata = (char *) page;
+	}
+
+	/*
+	 * Length of the previous undo record is store at the end of that record
+	 * so just read the last 2 bytes.  But, these 2 bytes can be split acorss
+	 * pages so we can not directly memcpy these 2 bytes, instead we need to
+	 * read them byte by byte.
+	 */
+	while (byte_to_read > 0)
+	{
+		/* Read buffer if the current buffer is not valid. */
+		if (!BufferIsValid(buffer))
+		{
+			buffer = ReadBufferWithoutRelcache(rnode, UndoLogForkNum,
+											   cur_blk, RBM_NORMAL, NULL,
+											   persistence);
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+			page = BufferGetPage(buffer);
+			pagedata = (char *) page;
+		}
+
+		/*
+		 * Read current prevlen byte from current block if the read offset
+		 * hasn't reach to undo block header.  Otherwise, go to the previous
+		 * block and continue reading from there.
+		 */
+		if (offset >= UndoLogBlockHeaderSize)
+		{
+			prevlen.len_bytes[byte_to_read - 1] = pagedata[offset];
+			byte_to_read -= 1;
+			offset -= 1;
+
+			continue;
+		}
+
+		/*
+		 * Could not read complete prevlen from the current block so go to the
+		 * previous block and start reading from end of the block.
+		 */
+		cur_blk -= 1;
+		offset = BLCKSZ - 1;
+
+		/* Release the current buffer if it is not provide by the caller. */
+		if (input_buffer != buffer)
+			UnlockReleaseBuffer(buffer);
+
+		buffer = InvalidBuffer;
+	}
+
+	prev_rec_len = prevlen.len;
+
+	/*
+	 * If previous undo record is split across the pages then include the
+	 * block header size in its length for computing the start location of the
+	 * previous undo record.
+	 */
+	if (UndoRecPtrGetPageOffset(urp) - UndoLogBlockHeaderSize < prevlen.len)
+		prev_rec_len += UndoLogBlockHeaderSize;
+
+	/* Release the buffer if we have locally read it. */
+	if (input_buffer != buffer)
+		UnlockReleaseBuffer(buffer);
+
+	return prev_rec_len;
+}
+
+/*
+ * Calculate the previous undo record pointer for the transaction.
+ *
+ * This will take current undo record pointer of the transaction as an input
+ * and calculate the previous undo record pointer of the transaction.
+ */
+UndoRecPtr
+UndoGetPrevUrp(UnpackedUndoRecord *uur, UndoRecPtr urp, Buffer buffer,
+			   UndoLogCategory category)
+{
+	UndoLogOffset offset = UndoRecPtrGetOffset(urp);
+	uint16		prevlen;
+
+	/*
+	 * If this is the first record of the transaction for this log then we
+	 * need to get the previous undo record pointer from the previous undo
+	 * log. We can get the logno from the log switch header stored in this
+	 * record and then we can compute the undo record pointer of the last
+	 * record on that log by using the insert location and the length of the
+	 * last record on that log.
+	 */
+	if (uur && uur->uur_logswitch)
+	{
+		UndoLogNumber logno =
+		UndoRecPtrGetLogNo(uur->uur_logswitch->urec_prevlogstart);
+
+		urp = MakeUndoRecPtr(logno, UndoLogGetNextInsertPtr(logno));
+
+		/*
+		 * If we are reading the length from the another undo log then we can
+		 * not use the buffer of the current log so pass invalid buffer.
+		 */
+		prevlen = UndoGetPrevRecordLen(urp, InvalidBuffer, category);
+	}
+	else
+		prevlen = UndoGetPrevRecordLen(urp, buffer, category);
+
+	/*
+	 * We have got the length of the previous record.  Now using the offset of
+	 * the current record and the length of the previous record we can compute
+	 * the undo record pointer of the previous record.
+	 */
+	offset = UndoRecPtrGetOffset(urp);
+
+	/* calculate the previous undo record pointer */
+	return MakeUndoRecPtr(UndoRecPtrGetLogNo(urp), offset - prevlen);
+}
diff --git a/src/backend/access/undo/undorecord.c b/src/backend/access/undo/undorecord.c
new file mode 100755
index 00000000000..25d2973d771
--- /dev/null
+++ b/src/backend/access/undo/undorecord.c
@@ -0,0 +1,1014 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.c
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/undo/undorecord.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/bufmask.h"
+#include "access/subtrans.h"
+#include "access/undorecord.h"
+#include "catalog/pg_tablespace.h"
+#include "storage/block.h"
+
+/* Prototypes for static functions. */
+static bool InsertUndoBytes(char *sourceptr, int sourcelen,
+							char **writeptr, char *endptr,
+							int *total_bytes_written, int *partial_write);
+static bool ReadUndoBytes(char *destptr, int readlen,
+						  char **readptr, char *endptr,
+						  int *total_bytes_read, int *partial_read);
+
+ /*
+  * Compute the header size of the undo record.
+  */
+static size_t
+UndoRecordHeaderSize(uint16 uur_info)
+{
+	size_t		size;
+
+	/* Add fixed header size. */
+	size = SizeOfUndoRecordHeader;
+
+	/* Add optional headers sizes. */
+	if ((uur_info & UREC_INFO_GROUP) != 0)
+		size += SizeOfUndoRecordGroup;
+
+	if ((uur_info & UREC_INFO_RMID) != 0)
+		size += sizeof(RmgrId);
+
+	if ((uur_info & UREC_INFO_RELOID) != 0)
+		size += sizeof(Oid);
+
+	if ((uur_info & UREC_INFO_FXID) != 0)
+		size += sizeof(FullTransactionId);
+
+	if ((uur_info & UREC_INFO_CID) != 0)
+		size += sizeof(CommandId);
+
+	if ((uur_info & UREC_INFO_FORK) != 0)
+		size += sizeof(ForkNumber);
+
+	if ((uur_info & UREC_INFO_PREVUNDO) != 0)
+		size += sizeof(UndoRecPtr);
+
+	if ((uur_info & UREC_INFO_BLOCK) != 0)
+		size += SizeOfUndoRecordBlock;
+
+	if ((uur_info & UREC_INFO_LOGSWITCH) != 0)
+		size += SizeOfUndoRecordLogSwitch;
+
+	if ((uur_info & UREC_INFO_PAYLOAD) != 0)
+		size += SizeOfUndoRecordPayload;
+
+	return size;
+}
+
+/*
+ * Compute the size of the payload data for the unpacked undo record.
+ */
+size_t
+UndoRecordPayloadSize(UnpackedUndoRecord *uur)
+{
+	size_t		size = 0;
+
+	/* Add payload size if record contains payload data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		size += uur->uur_payload.len;
+		size += uur->uur_tuple.len;
+	}
+
+	return size;
+}
+
+/*
+ * Compute the expected size of the undo record.
+ *
+ * This function will take UnpackedUndoRecord as input and base on the uur_info
+ * and the variable headers it will compute the exact size required to store
+ * this record on the page.
+ */
+Size
+UndoRecordExpectedSize(UnpackedUndoRecord *uur)
+{
+	/* Header size + payload data + undo record length. */
+	return UndoRecordHeaderSize(uur->uur_info) +
+		UndoRecordPayloadSize(uur) +
+		sizeof(uint16);
+}
+
+/*
+ * Initiate inserting an undo record.
+ *
+ * This function will initialize the context for inserting and undo record
+ * which will be inserted by calling InsertUndoData.
+ */
+void
+BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur,
+				uint16 undo_len)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+
+	/* Copy undo record header. */
+	ucontext->urec_hd.urec_type = uur->uur_type;
+	ucontext->urec_hd.urec_info = uur->uur_info;
+
+	/* Copy optional headers into the context. */
+	if ((uur->uur_info & UREC_INFO_GROUP) != 0)
+		ucontext->urec_group = *uur->uur_group;
+
+	if ((uur->uur_info & UREC_INFO_RMID) != 0)
+		ucontext->urec_rmid = uur->uur_rmid;
+
+	if ((uur->uur_info & UREC_INFO_RELOID) != 0)
+		ucontext->urec_reloid = uur->uur_reloid;
+
+	if ((uur->uur_info & UREC_INFO_FXID) != 0)
+		ucontext->urec_fxid = uur->uur_fxid;
+
+	if ((uur->uur_info & UREC_INFO_CID) != 0)
+		ucontext->urec_cid = uur->uur_cid;
+
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		ucontext->urec_fork = uur->uur_fork;
+
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		ucontext->urec_prevundo = uur->uur_prevundo;
+
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		ucontext->urec_blk.urec_block = uur->uur_block;
+		ucontext->urec_blk.urec_offset = uur->uur_offset;
+	}
+
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+		ucontext->urec_logswitch = *uur->uur_logswitch;
+
+	/* Copy undo record payload header and data. */
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		ucontext->urec_payload.urec_payload_len = uur->uur_payload.len;
+		ucontext->urec_payload.urec_tuple_len = uur->uur_tuple.len;
+		ucontext->urec_payloaddata = uur->uur_payload.data;
+		ucontext->urec_tupledata = uur->uur_tuple.data;
+	}
+	else
+	{
+		ucontext->urec_payload.urec_payload_len = 0;
+		ucontext->urec_payload.urec_tuple_len = 0;
+	}
+
+	ucontext->undo_len = undo_len;
+}
+
+/*
+ * Insert the undo record into the input page from the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will write the undo record into the page.
+ */
+void
+InsertUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *writeptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			/* Insert undo record header. */
+			if (!InsertUndoBytes((char *) &ucontext->urec_hd,
+								 SizeOfUndoRecordHeader, &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_GROUP;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_GROUP:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_GROUP) != 0)
+			{
+				/* Insert undo record group header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_group,
+									 SizeOfUndoRecordGroup,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_rmid), sizeof(RmgrId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_reloid), sizeof(Oid),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FXID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_fxid), sizeof(FullTransactionId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid(if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!InsertUndoBytes((char *) &(ucontext->urec_cid), sizeof(CommandId),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				/* Insert undo record fork number. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_fork,
+									 sizeof(ForkNumber),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				/* Insert undo record blkprev. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_prevundo,
+									 sizeof(UndoRecPtr),
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				/* Insert undo record block header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_blk,
+									 SizeOfUndoRecordBlock,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				/* Insert undo record log switch header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_logswitch,
+									 SizeOfUndoRecordLogSwitch,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				/* Insert undo record payload header. */
+				if (!InsertUndoBytes((char *) &ucontext->urec_payload,
+									 SizeOfUndoRecordPayload,
+									 &writeptr, endptr,
+									 &ucontext->already_processed,
+									 &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				if (len > 0)
+				{
+					/* Insert payload data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_payloaddata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				if (len > 0)
+				{
+					/* Insert tuple data. */
+					if (!InsertUndoBytes((char *) ucontext->urec_tupledata,
+										 len, &writeptr, endptr,
+										 &ucontext->already_processed,
+										 &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			}
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			/* Insert undo length. */
+			if (!InsertUndoBytes((char *) &ucontext->undo_len,
+								 sizeof(uint16), &writeptr, endptr,
+								 &ucontext->already_processed,
+								 &ucontext->partial_bytes))
+				return;
+
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Skip inserting undo record
+ *
+ * Don't insert the actual undo record instead just update the context data
+ * so that if we need to insert the remaining partial record to the next
+ * block then we have right context.
+ */
+void
+SkipInsertingUndoData(UndoPackContext *ucontext, int bytes_to_skip)
+{
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (bytes_to_skip < SizeOfUndoRecordHeader)
+			{
+				ucontext->partial_bytes = bytes_to_skip;
+				return;
+			}
+			bytes_to_skip -= SizeOfUndoRecordHeader;
+			ucontext->stage = UNDO_PACK_STAGE_GROUP;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_GROUP:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_GROUP) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordGroup)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordGroup;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RMID:
+			/* Write rmid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RMID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(RmgrId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(RmgrId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_RELOID:
+			/* Write reloid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_RELOID) != 0)
+			{
+				if (bytes_to_skip < sizeof(Oid))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(Oid);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_XID:
+			/* Write xid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_XID) != 0)
+			{
+				if (bytes_to_skip < (sizeof(TransactionId)))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(TransactionId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_CID:
+			/* Write cid (if needed and not already done). */
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_CID) != 0)
+			{
+				if (bytes_to_skip < sizeof(CommandId))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(CommandId);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (bytes_to_skip < sizeof(ForkNumber))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(ForkNumber);
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UNDO_PACK_STAGE_PREVUNDO) != 0)
+			{
+				if (bytes_to_skip < sizeof(UndoRecPtr))
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= sizeof(UndoRecPtr);
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordBlock)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordBlock;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordLogSwitch)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordLogSwitch;
+			}
+
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Skip payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (bytes_to_skip < SizeOfUndoRecordPayload)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= SizeOfUndoRecordPayload;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			if (ucontext->urec_payload.urec_payload_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_payload_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_payload_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			if (ucontext->urec_payload.urec_tuple_len > 0)
+			{
+				if (bytes_to_skip < ucontext->urec_payload.urec_tuple_len)
+				{
+					ucontext->partial_bytes = bytes_to_skip;
+					return;
+				}
+				bytes_to_skip -= ucontext->urec_payload.urec_tuple_len;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_UNDO_LENGTH;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_UNDO_LENGTH:
+			ucontext->stage = UNDO_PACK_STAGE_DONE;
+			 /* fall through */ ;
+
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+}
+
+/*
+ * Write undo bytes from a particular source, but only to the extent that
+ * they weren't written previously and will fit.
+ *
+ * 'sourceptr' points to the source data, and 'sourcelen' is the length of
+ * that data in bytes.
+ *
+ * 'writeptr' points to the insertion point for these bytes, and is updated
+ * for whatever we write.  The insertion point must not pass 'endptr', which
+ * represents the end of the buffer into which we are writing.
+ *
+ * 'my_bytes_written' is a pointer to the count of previous-written bytes
+ * from this and following structures in this undo record; that is, any
+ * bytes that are part of previous structures in the record have already
+ * been subtracted out.
+ *
+ * 'total_bytes_written' points to the count of all previously-written bytes,
+ * and must it must be updated for the bytes we write.
+ *
+ * The return value is false if we ran out of space before writing all
+ * the bytes, and otherwise true.
+ */
+static bool
+InsertUndoBytes(char *sourceptr, int sourcelen, char **writeptr, char *endptr,
+				int *total_bytes_written, int *partial_write)
+{
+	int			can_write;
+	int			remaining;
+
+	/* Compute number of bytes we can write. */
+	remaining = sourcelen - *partial_write;
+	can_write = Min(remaining, endptr - *writeptr);
+
+	/* Bail out if no bytes can be written. */
+	if (can_write == 0)
+		return false;
+
+	/* Copy the bytes we can write. */
+	memcpy(*writeptr, sourceptr + *partial_write, can_write);
+
+	/* Update bookkeeping information. */
+	*writeptr += can_write;
+	*total_bytes_written += can_write;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_write < remaining)
+	{
+		*partial_write += can_write;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_write = 0;
+	return true;
+}
+
+/*
+ * Initiate unpacking an undo record.
+ *
+ * This function will initialize the context for unpacking the undo record which
+ * will be unpacked by calling UnpackUndoData.
+ */
+void
+BeginUnpackUndo(UndoPackContext *ucontext)
+{
+	ucontext->stage = UNDO_PACK_STAGE_HEADER;
+	ucontext->already_processed = 0;
+	ucontext->partial_bytes = 0;
+}
+
+/*
+ * Read the undo record from the input page to the unpack undo context.
+ *
+ * Caller can  call this function multiple times until desired stage is reached.
+ * This will read the undo record from the page and store the data into unpack
+ * undo context, which can be later copied to unpacked undo record by calling
+ * FinishUnpackUndo.
+ */
+void
+UnpackUndoData(UndoPackContext *ucontext, Page page, int starting_byte)
+{
+	char	   *readptr = (char *) page + starting_byte;
+	char	   *endptr = (char *) page + BLCKSZ;
+
+	switch (ucontext->stage)
+	{
+		case UNDO_PACK_STAGE_HEADER:
+			if (!ReadUndoBytes((char *) &ucontext->urec_hd,
+							   SizeOfUndoRecordHeader, &readptr, endptr,
+							   &ucontext->already_processed,
+							   &ucontext->partial_bytes))
+				return;
+			ucontext->stage = UNDO_PACK_STAGE_GROUP;
+			/* fall through */
+		case UNDO_PACK_STAGE_GROUP:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_GROUP) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_group,
+								   SizeOfUndoRecordGroup,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RMID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RMID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RMID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_rmid,
+								   sizeof(RmgrId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_RELOID;
+			/* fall through */
+		case UNDO_PACK_STAGE_RELOID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_RELOID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_reloid,
+								   sizeof(Oid),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_XID;
+			/* fall through */
+		case UNDO_PACK_STAGE_XID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FXID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fxid,
+								   sizeof(FullTransactionId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_CID;
+			/* fall through */
+		case UNDO_PACK_STAGE_CID:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_CID) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_cid,
+								   sizeof(CommandId),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_FORKNUM;
+			/* fall through */
+		case UNDO_PACK_STAGE_FORKNUM:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_FORK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_fork,
+								   sizeof(ForkNumber),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PREVUNDO;
+			/* fall through */
+		case UNDO_PACK_STAGE_PREVUNDO:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PREVUNDO) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_prevundo,
+								   sizeof(UndoRecPtr),
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_BLOCK;
+			/* fall through */
+
+		case UNDO_PACK_STAGE_BLOCK:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_BLOCK) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_blk,
+								   SizeOfUndoRecordBlock,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_LOGSWITCH;
+			/* fall through */
+		case UNDO_PACK_STAGE_LOGSWITCH:
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_LOGSWITCH) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_logswitch,
+								   SizeOfUndoRecordLogSwitch,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD:
+			/* Read payload header. */
+			if ((ucontext->urec_hd.urec_info & UREC_INFO_PAYLOAD) != 0)
+			{
+				if (!ReadUndoBytes((char *) &ucontext->urec_payload,
+								   SizeOfUndoRecordPayload,
+								   &readptr, endptr, &ucontext->already_processed,
+								   &ucontext->partial_bytes))
+					return;
+			}
+			ucontext->stage = UNDO_PACK_STAGE_PAYLOAD_DATA;
+			/* fall through */
+		case UNDO_PACK_STAGE_PAYLOAD_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_payload_len;
+
+				/* Allocate memory for the payload data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_payloaddata == NULL)
+						ucontext->urec_payloaddata = (char *) palloc(len);
+
+					/* Read payload data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_payloaddata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+				ucontext->stage = UNDO_PACK_STAGE_TUPLE_DATA;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_TUPLE_DATA:
+			{
+				int			len = ucontext->urec_payload.urec_tuple_len;
+
+				/* Allocate memory for the tuple data if not already done. */
+				if (len > 0)
+				{
+					if (ucontext->urec_tupledata == NULL)
+						ucontext->urec_tupledata = (char *) palloc(len);
+
+					/* Read tuple data. */
+					if (!ReadUndoBytes((char *) ucontext->urec_tupledata, len,
+									   &readptr, endptr, &ucontext->already_processed,
+									   &ucontext->partial_bytes))
+						return;
+				}
+
+				ucontext->stage = UNDO_PACK_STAGE_DONE;
+				/* fall through */
+			}
+		case UNDO_PACK_STAGE_DONE:
+			/* Nothing to be done. */
+			break;
+		default:
+			Assert(0);			/* Invalid stage */
+	}
+
+	return;
+}
+
+/*
+ * Final step of unpacking the undo record.
+ *
+ * Copy the undo record data from the unpack undo context to the input unpacked
+ * undo record.
+ */
+void
+FinishUnpackUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur)
+{
+	/* Copy undo record header. */
+	uur->uur_type = ucontext->urec_hd.urec_type;
+	uur->uur_info = ucontext->urec_hd.urec_info;
+
+	/* Copy undo record group header if it is present. */
+	if ((uur->uur_info & UREC_INFO_GROUP) != 0)
+	{
+		uur->uur_group = palloc(SizeOfUndoRecordGroup);
+		memcpy(uur->uur_group, &ucontext->urec_group, SizeOfUndoRecordGroup);
+	}
+
+	/*
+	 * Copy the common field.  All of these field must present in the final
+	 * unpacked undo record.
+	 */
+	Assert((uur->uur_info & UREC_INFO_PAGE_COMMON) == UREC_INFO_PAGE_COMMON);
+	uur->uur_rmid = ucontext->urec_rmid;
+	uur->uur_reloid = ucontext->urec_reloid;
+	uur->uur_fxid = ucontext->urec_fxid;
+	uur->uur_cid = ucontext->urec_cid;
+
+	/* Copy undo record optional headers. */
+	if ((uur->uur_info & UREC_INFO_FORK) != 0)
+		uur->uur_fork = ucontext->urec_fork;
+
+	if ((uur->uur_info & UREC_INFO_PREVUNDO) != 0)
+		uur->uur_prevundo = ucontext->urec_prevundo;
+
+	if ((uur->uur_info & UREC_INFO_BLOCK) != 0)
+	{
+		uur->uur_block = ucontext->urec_blk.urec_block;
+		uur->uur_offset = ucontext->urec_blk.urec_offset;
+	}
+
+	if ((uur->uur_info & UREC_INFO_LOGSWITCH) != 0)
+	{
+		uur->uur_logswitch = palloc(SizeOfUndoRecordLogSwitch);
+		memcpy(uur->uur_logswitch, &ucontext->urec_logswitch,
+			   SizeOfUndoRecordLogSwitch);
+	}
+
+	if ((uur->uur_info & UREC_INFO_PAYLOAD) != 0)
+	{
+		uur->uur_payload.len = ucontext->urec_payload.urec_payload_len;
+		uur->uur_tuple.len = ucontext->urec_payload.urec_tuple_len;
+
+		/* Read payload data if its length is not 0. */
+		if (uur->uur_payload.len != 0)
+			uur->uur_payload.data = ucontext->urec_payloaddata;
+
+		/* Read tuple data if its length is not 0. */
+		if (uur->uur_tuple.len != 0)
+			uur->uur_tuple.data = ucontext->urec_tupledata;
+	}
+}
+
+/*
+ * Directly read the undo compression info from the undo record starting at
+ * given offset.
+ */
+void
+UndoRecordGetCompressionInfo(Page page, int starting_byte,
+							 UndoCompressionInfo *compresssion_info)
+{
+	UndoRecordHeader urec_hd;
+	char	   *readptr = (char *) page + starting_byte;
+
+	/* Read the main header */
+	memcpy((char *) &urec_hd, readptr, SizeOfUndoRecordHeader);
+	readptr += SizeOfUndoRecordHeader;
+
+	/* If we have the group header then skip it. */
+	if (urec_hd.urec_info & UREC_INFO_GROUP)
+		readptr += SizeOfUndoRecordGroup;
+
+	/* Read compression info. */
+	memcpy((char *) compresssion_info, readptr, sizeof(UndoCompressionInfo));
+}
+
+/*
+ * Read undo bytes into a particular destination,
+ *
+ * 'destptr' points to the source data, and 'readlen' is the length of
+ * that data to be read in bytes.
+ *
+ * 'readptr' points to the read point for these bytes, and is updated
+ * for how much we read.  The read point must not pass 'endptr', which
+ * represents the end of the buffer from which we are reading.
+ *
+ * 'partial_read' is a pointer to the count of previous partial read bytes
+ *
+ * 'total_bytes_read' points to the count of all previously-read bytes,
+ * and must likewise be updated for the bytes we read.
+ *
+ * nocopy if this flag is set true then it will just skip the readlen
+ * size in undo but it will not copy into the buffer.
+ *
+ * The return value is false if we ran out of space before read all
+ * the bytes, and otherwise true.
+ */
+static bool
+ReadUndoBytes(char *destptr, int readlen, char **readptr, char *endptr,
+			  int *total_bytes_read, int *partial_read)
+{
+	int			can_read;
+	int			remaining;
+
+	/* Compute number of bytes we can read. */
+	remaining = readlen - *partial_read;
+	can_read = Min(remaining, endptr - *readptr);
+
+	/* Bail out if no bytes can be read. */
+	if (can_read == 0)
+		return false;
+
+	/* Copy the bytes we can read. */
+	memcpy(destptr + *partial_read, *readptr, can_read);
+
+	/* Update bookkeeping information. */
+	*readptr += can_read;
+	*total_bytes_read += can_read;
+
+	/* Could not read whole data so set the partial_read. */
+	if (can_read < remaining)
+	{
+		*partial_read += can_read;
+		return false;
+	}
+
+	/* Return true only if we wrote the whole thing. */
+	*partial_read = 0;
+
+	return true;
+}
+
+/*
+ * Set uur_info for an UnpackedUndoRecord appropriately based on which fields
+ * are set.
+ */
+void
+UndoRecordSetInfo(UnpackedUndoRecord *uur)
+{
+	/*
+	 * If fork number is not the main fork then we need to store it in the
+	 * undo record so set the flag.
+	 */
+	if (uur->uur_fork != MAIN_FORKNUM)
+		uur->uur_info |= UREC_INFO_FORK;
+
+	/* If prevundo is valid undo record pointer then set the flag. */
+	if (uur->uur_prevundo != InvalidUndoRecPtr)
+		uur->uur_info |= UREC_INFO_PREVUNDO;
+
+	/* If the block number is valid then set the flag for the block header. */
+	if (uur->uur_block != InvalidBlockNumber)
+		uur->uur_info |= UREC_INFO_BLOCK;
+
+	/*
+	 * Either of the payload or the tuple length is non-zero then we need the
+	 * payload header.
+	 */
+	if (uur->uur_payload.len || uur->uur_tuple.len)
+		uur->uur_info |= UREC_INFO_PAYLOAD;
+}
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810e378..548474a032c 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -59,6 +59,41 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	/* p->pd_prune_xid = InvalidTransactionId;		done by above MemSet */
 }
 
+/*
+ * UndoPageInit
+ *		Initializes the contents of an undo page.
+ *		Note that we don't calculate an initial checksum here; that's not done
+ *		until it's time to write.
+ */
+void
+UndoPageInit(Page page, Size pageSize, uint16 uur_info, uint16 record_offset,
+			 uint16 len)
+{
+	UndoPageHeader	p = (UndoPageHeader) page;
+
+	Assert(pageSize == BLCKSZ);
+
+	/* Make sure all fields of page are zero, as well as unused space. */
+	MemSet(p, 0, pageSize);
+
+	p->pd_flags = 0;
+	p->pd_lower = SizeOfUndoPageHeaderData;
+	p->pd_upper = pageSize;
+	p->pd_special = pageSize;
+	p->uur_info = uur_info;
+	p->record_offset = record_offset;
+
+	/*
+	 * If we have partial record on the page then store complete length of the
+	 * undo record and the offset of the undo record which is starting in this
+	 * page.  This will be used to compute the offset of the first complete
+	 * record on the page.
+	 */
+	p->undo_len = (record_offset > 0) ? len : 0;
+
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
+}
+
 
 /*
  * PageIsVerified
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156f..cc005096991 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
diff --git a/src/include/access/undoaccess.h b/src/include/access/undoaccess.h
new file mode 100644
index 00000000000..815db4482f2
--- /dev/null
+++ b/src/include/access/undoaccess.h
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * undoaccess.h
+ *	  entry points for inserting/fetching undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undoaccess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDOACCESS_H
+#define UNDOACCESS_H
+
+#include "access/undolog.h"
+#include "access/undorecord.h"
+#include "access/xlogdefs.h"
+#include "catalog/pg_class.h"
+
+/*
+ * XXX Do we want to support undo tuple size which is more than the BLCKSZ
+ * if not than undo record can spread across 2 buffers at the max.
+ */
+#define MAX_BUFFER_PER_UNDO    2
+
+/*
+ * Maximum number of the UndoRecordUpdateInfo for updating the undo record.
+ * Usually it's 1 for updating next link of previous transaction's header
+ * if we are starting a new transaction.  But, in some cases where the same
+ * transaction is spilled to the next log, we update our own transaction's
+ * header in previous undo log as well as the header of the previous transaction
+ * in the new log.
+ */
+#define MAX_UNDO_UPDATE_INFO	2
+
+typedef struct PreparedUndoSpace PreparedUndoSpace;
+typedef struct PreparedUndoBuffer PreparedUndoBuffer;
+
+/*
+ * Undo record element.  Used for storing the group of undo record in a array
+ * using UndoBulkFetchRecord.
+ */
+typedef struct UndoRecInfo
+{
+	int			index;			/* Index of the element.  For stable qsort. */
+	UndoRecPtr	urp;			/* undo recptr (undo record location). */
+	UnpackedUndoRecord *uur;	/* actual undo record. */
+} UndoRecInfo;
+
+/*
+ * This structure holds the informations for updating the undo record.  We need
+ * to update the group header for various purposes
+ * a) Setting the "uur_next" which points to the next group's first undo record
+ * in the undo log.
+ * b) updating the undo apply progress while applying undo actions.  During
+ * prepare phase we will keep all the information handy in this structure and
+ * that will be used for updating the actual record inside the critical section.
+ */
+typedef struct UndoRecordUpdateInfo
+{
+	UndoRecPtr	urecptr;		/* Undo record pointer to be updated. */
+	uint32		offset;			/* offset in page where to start updating. */
+	UndoRecPtr	next;			/* first urp of the next group which is to be
+								 * set in the group header */
+	BlockNumber progress;		/* undo apply action progress. */
+	int			idx_undo_buffers[MAX_BUFFER_PER_UNDO];
+} UndoRecordUpdateInfo;
+
+/*
+ * Context for preparing and inserting undo records.
+ */
+typedef struct UndoRecordInsertContext
+{
+	UndoLogAllocContext alloc_context;
+	PreparedUndoSpace *prepared_undo;	/* prepared undo. */
+	PreparedUndoBuffer *prepared_undo_buffers;	/* Buffers for prepared undo. */
+	UndoRecordUpdateInfo urec_update_info[MAX_UNDO_UPDATE_INFO];	/* Information for undo
+																	 * update */
+	int			nprepared_undo; /* Number of prepared undo records. */
+	int			max_prepared_undo;	/* Max prepared undo for this operation. */
+	int			nprepared_undo_buffer;	/* Number of undo buffers. */
+	int			nurec_update_info;	/* Number of prepared undo update info. */
+} UndoRecordInsertContext;
+
+/*
+ * Context for fetching the required undo record.
+ */
+typedef struct UndoRecordFetchContext
+{
+	Buffer		buffer;			/* Previous undo record pinned buffer. */
+	UndoRecPtr	urp;			/* Previous undo record pointer. */
+} UndoRecordFetchContext;
+
+extern void BeginUndoRecordInsert(UndoRecordInsertContext *context,
+								  UndoLogCategory category,
+								  int nprepared,
+								  XLogReaderState *xlog_record);
+extern UndoRecPtr PrepareUndoInsert(UndoRecordInsertContext *context,
+									UnpackedUndoRecord *urec, Oid dbid);
+extern void InsertPreparedUndo(UndoRecordInsertContext *context);
+extern void FinishUndoRecordInsert(UndoRecordInsertContext *context);
+extern void BeginUndoFetch(UndoRecordFetchContext *context);
+extern UnpackedUndoRecord *UndoFetchRecord(UndoRecordFetchContext *context,
+										   UndoRecPtr urp);
+extern void FinishUndoFetch(UndoRecordFetchContext *context);
+extern void UndoRecordRelease(UnpackedUndoRecord *urec);
+extern UndoRecInfo *UndoBulkFetchRecord(UndoRecPtr *from_urecptr,
+										UndoRecPtr to_urecptr,
+										int undo_apply_size, int *nrecords);
+extern void RegisterUndoLogBuffers(UndoRecordInsertContext *context,
+								   uint8 first_block_id);
+extern void UndoLogBuffersSetLSN(UndoRecordInsertContext *context,
+								 XLogRecPtr recptr);
+extern UndoRecPtr UndoGetPrevUrp(UnpackedUndoRecord *uur, UndoRecPtr urp,
+								 Buffer buffer, UndoLogCategory category);
+
+#endif							/* UNDOINSERT_H */
diff --git a/src/include/access/undolog.h b/src/include/access/undolog.h
index 01c00a58dee..2d4486978f0 100644
--- a/src/include/access/undolog.h
+++ b/src/include/access/undolog.h
@@ -140,7 +140,7 @@ typedef int UndoLogNumber;
 	(((uint64) (logno) << UndoLogOffsetBits) | (offset))
 
 /* The number of unusable bytes in the header of each block. */
-#define UndoLogBlockHeaderSize SizeOfPageHeaderData
+#define UndoLogBlockHeaderSize SizeOfUndoPageHeaderData
 
 /* The number of usable bytes we can store per block. */
 #define UndoLogUsableBytesPerPage (BLCKSZ - UndoLogBlockHeaderSize)
@@ -170,6 +170,10 @@ typedef int UndoLogNumber;
 #define UndoRecPtrGetPageOffset(urp)			\
 	(UndoRecPtrGetOffset(urp) % BLCKSZ)
 
+/* Compute the undo record pointer offset given the undo rec page offset and the block number. */
+#define UndoRecPageOffsetGetRecPtr(offset, blkno)             \
+	((blkno * BLCKSZ) + offset)
+
 /* Compare two undo checkpoint files to find the oldest file. */
 #define UndoCheckPointFilenamePrecedes(file1, file2)	\
 	(strcmp(file1, file2) < 0)
diff --git a/src/include/access/undorecord.h b/src/include/access/undorecord.h
new file mode 100644
index 00000000000..1c9afba4d6a
--- /dev/null
+++ b/src/include/access/undorecord.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------------------------
+ *
+ * undorecord.h
+ *	  encode and decode undo records
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/undorecord.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UNDORECORD_H
+#define UNDORECORD_H
+
+#include "access/undolog.h"
+#include "access/transam.h"
+#include "lib/stringinfo.h"
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"
+#include "storage/off.h"
+
+/*
+ * The below common information will be stored in the first undo record of the
+ * page.  Every subsequent undo record will not store this information, if it's
+ * the same as the relevant fields in the first record.
+ */
+typedef struct UndoCompressionInfo
+{
+	RmgrId		rmid;			/* rmgr ID */
+	Oid			reloid;			/* relation OID */
+	FullTransactionId fxid;
+	CommandId	cid;			/* command id */
+} UndoCompressionInfo;
+
+/*
+ * If UREC_INFO_GROUP is set, an UndoRecordGroup structure follows.
+ * If UREC_INFO_RMID is set, rmgr id follows.
+ * if UREC_INFO_RELOID	is set, relation oid follows.
+ * If UREC_INFO_FXID	is set, full transaction id follows.
+ * If UREC_INFO_CID	is set, command id follows.
+ * If UREC_INFO_FORK is set, fork number follows.
+ * If UREC_INFO_PREVUNDO is set, previous undo record pointer follows.
+ * If UREC_INFO_BLOCK is set, an UndoRecordBlock structure follows.
+ * If UREC_INFO_LOGSWITCH is set, an UndoRecordLogSwitch structure follows.
+ * If UREC_INFO_PAYLOAD is set, an UndoRecordPayload structure follows.
+ *
+ * When (as will often be the case) multiple structures are present, they
+ * appear in the same order in which the constants are defined here.  That is,
+ * UndoRecordTransaction appears first.
+ */
+#define UREC_INFO_GROUP						0x001
+#define UREC_INFO_RMID						0x002
+#define UREC_INFO_RELOID					0x004
+#define UREC_INFO_FXID						0x008
+#define UREC_INFO_CID						0x010
+#define UREC_INFO_FORK						0x020
+#define UREC_INFO_PREVUNDO					0x040
+#define UREC_INFO_BLOCK						0x080
+#define UREC_INFO_LOGSWITCH					0x100
+#define UREC_INFO_PAYLOAD					0x200
+
+#define UREC_INFO_PAGE_COMMON  (UREC_INFO_RMID | UREC_INFO_RELOID | UREC_INFO_FXID | UREC_INFO_CID)
+
+/*
+ * Every undo record begins with an UndoRecordHeader structure, which is
+ * followed by the additional structures indicated by the contents of
+ * urec_info.  All structures are packed into undo pages without considering
+ * the alignment and without trailing padding bytes. So care must be taken when
+ * reading the header.
+ */
+typedef struct UndoRecordHeader
+{
+	uint8		urec_type;		/* record type code */
+	uint16		urec_info;		/* flag bits */
+} UndoRecordHeader;
+
+#define SizeOfUndoRecordHeader	\
+	(offsetof(UndoRecordHeader, urec_info) + sizeof(uint16))
+
+/*
+ * Undo group header keep the information per undo group which can be discarded
+ * in single shot.  This contains the undo record pointer to the next group.
+ * Additionally, it also contains the progress of undo apply and the dbid.
+ */
+typedef struct UndoRecordGroup
+{
+	/*
+	 * Undo block number where we need to start reading the undo for applying
+	 * the undo action.   InvalidBlockNumber means undo applying hasn't
+	 * started for the transaction and MaxBlockNumber mean undo completely
+	 * applied. And, any other block number means we have applied partial undo
+	 * so next we can start from this block.
+	 */
+	BlockNumber urec_progress;
+	Oid			urec_dbid;		/* database id */
+	UndoRecPtr	urec_next_group;	/* urec pointer of the next group */
+} UndoRecordGroup;
+
+#define SizeOfUndoRecordGroup \
+	(offsetof(UndoRecordGroup, urec_next_group) + sizeof(UndoRecPtr))
+
+/*
+ *  Information for a block to which this record pertains.
+ */
+typedef struct UndoRecordBlock
+{
+	BlockNumber urec_block;		/* block number */
+	OffsetNumber urec_offset;	/* offset number */
+} UndoRecordBlock;
+
+#define SizeOfUndoRecordBlock \
+	(offsetof(UndoRecordBlock, urec_offset) + sizeof(OffsetNumber))
+
+/*
+ * Information of the transaction's undo in the previous log.  If a transaction
+ * is split across the undo logs then this header will be included in the first
+ * undo record of the transaction in next log.
+ */
+typedef struct UndoRecordLogSwitch
+{
+	UndoRecPtr	urec_prevlogstart;	/* Transaction's first undo record pointer
+									 * in previous undo log. */
+} UndoRecordLogSwitch;
+
+#define SizeOfUndoRecordLogSwitch \
+	(offsetof(UndoRecordLogSwitch, urec_prevlogstart) + sizeof(UndoRecPtr))
+
+/*
+ * Information about the amount of payload data and tuple data present
+ * in this record.  The payload bytes immediately follow the structures
+ * specified by flag bits in urec_info, and the tuple bytes follow the
+ * payload bytes.
+ */
+typedef struct UndoRecordPayload
+{
+	uint16		urec_payload_len;	/* # of payload bytes */
+	uint16		urec_tuple_len; /* # of tuple bytes */
+} UndoRecordPayload;
+
+#define SizeOfUndoRecordPayload \
+	(offsetof(UndoRecordPayload, urec_tuple_len) + sizeof(uint16))
+
+typedef enum UndoPackStage
+{
+	UNDO_PACK_STAGE_HEADER,		/* We have not yet processed even the record
+								 * header; we need to do that next. */
+	UNDO_PACK_STAGE_GROUP,		/* The next thing to be processed is the group
+								 * header, if present. */
+	UNDO_PACK_STAGE_RMID,		/* The next thing to be processed is the rmid
+								 * if present */
+
+	UNDO_PACK_STAGE_RELOID,		/* The next thing to be processed is the
+								 * reloid if present */
+
+	UNDO_PACK_STAGE_XID,		/* The next thing to be processed is the xid
+								 * if present */
+
+	UNDO_PACK_STAGE_CID,		/* The next thing to be processed is the cid
+								 * if present */
+
+	UNDO_PACK_STAGE_FORKNUM,	/* The next thing to be processed is the
+								 * relation fork number, if present. */
+	UNDO_PACK_STAGE_PREVUNDO,	/* The next thing to be processed is the prev
+								 * undo info. */
+
+	UNDO_PACK_STAGE_BLOCK,		/* The next thing to be processed is the block
+								 * details, if present. */
+	UNDO_PACK_STAGE_LOGSWITCH,	/* The next thing to be processed is the log
+								 * switch details. */
+	UNDO_PACK_STAGE_PAYLOAD,	/* The next thing to be processed is the
+								 * payload details, if present */
+	UNDO_PACK_STAGE_PAYLOAD_DATA,	/* The next thing to be processed is the
+									 * payload data */
+	UNDO_PACK_STAGE_TUPLE_DATA, /* The next thing to be processed is the tuple
+								 * data */
+	UNDO_PACK_STAGE_UNDO_LENGTH,	/* Next thing to processed is undo length. */
+
+	UNDO_PACK_STAGE_DONE		/* complete */
+} UndoPackStage;
+
+/*
+ * Undo record context for inserting/unpacking undo record.  This will hold
+ * intermediate state of undo record processed so far.
+ */
+typedef struct UndoPackContext
+{
+	UndoRecordHeader urec_hd;	/* Main header */
+	UndoRecordGroup urec_group; /* Undo record group header */
+	RmgrId		urec_rmid;		/* rmgrid */
+	Oid			urec_reloid;	/* relation OID */
+
+	/*
+	 * Transaction id that has modified the tuple for which this undo record
+	 * is written.  We use this to skip the undo records.  See comments atop
+	 * function UndoFetchRecord.
+	 */
+	FullTransactionId urec_fxid;	/* Transaction id */
+	CommandId	urec_cid;		/* command id */
+
+	ForkNumber	urec_fork;		/* Relation fork number */
+	UndoRecPtr	urec_prevundo;	/* Block prev */
+	UndoRecordBlock urec_blk;	/* Block header */
+	UndoRecordLogSwitch urec_logswitch; /* Log switch header */
+	UndoRecordPayload urec_payload; /* Payload data */
+	char	   *urec_payloaddata;
+	char	   *urec_tupledata;
+	uint16		undo_len;		/* Length of the undo record. */
+	int			already_processed;	/* Number of bytes read/written so far */
+	int			partial_bytes;	/* Number of partial bytes read/written */
+	UndoPackStage stage;		/* Undo pack stage */
+} UndoPackContext;
+
+/*
+ * Information that can be used to create an undo record or that can be
+ * extracted from one previously created.  The raw undo record format is
+ * difficult to manage, so this structure provides a convenient intermediate
+ * form that is easier for callers to manage.
+ *
+ * When creating an undo record from an UnpackedUndoRecord, caller should
+ * set uur_info to 0.  It will be initialized by the first call to
+ * UndoRecordSetInfo or InsertUndoRecord.  We do set it in
+ * UndoRecordAllocate for transaction specific header information.
+ *
+ * When an undo record is decoded into an UnpackedUndoRecord, all fields
+ * will be initialized, but those for which no information is available
+ * will be set to invalid or default values, as appropriate.
+ */
+typedef struct UnpackedUndoRecord
+{
+	RmgrId		uur_rmid;		/* rmgr ID */
+	uint8		uur_type;		/* record type code */
+	uint16		uur_info;		/* flag bits */
+	Oid			uur_reloid;		/* relation OID */
+	CommandId	uur_cid;		/* command id */
+	ForkNumber	uur_fork;		/* fork number */
+	UndoRecPtr	uur_prevundo;	/* byte offset of previous undo for block */
+	BlockNumber uur_block;		/* block number */
+	OffsetNumber uur_offset;	/* offset number */
+	FullTransactionId uur_fxid; /* transaction id */
+	StringInfoData uur_payload; /* payload bytes */
+	StringInfoData uur_tuple;	/* tuple bytes */
+
+	/*
+	 * Below header will be internally set by the undo layer.  Above this all
+	 * information should be set by the caller.
+	 */
+	UndoRecordGroup *uur_group; /* Group header, included in the first record
+								 * of the group in a undo log. */
+	UndoRecordLogSwitch *uur_logswitch; /* Log switch header, included in the
+										 * first record of the transaction
+										 * only after undo log is switched
+										 * during a transaction. */
+} UnpackedUndoRecord;
+
+extern size_t UndoRecordExpectedSize(UnpackedUndoRecord *uur);
+extern size_t UndoRecordPayloadSize(UnpackedUndoRecord *uur);
+extern void BeginInsertUndo(UndoPackContext *ucontext, UnpackedUndoRecord *uur,
+							uint16 undo_len);
+extern void InsertUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void SkipInsertingUndoData(UndoPackContext *ucontext,
+								  int bytes_to_skip);
+extern void BeginUnpackUndo(UndoPackContext *ucontext);
+extern void UnpackUndoData(UndoPackContext *ucontext, Page page,
+						   int starting_byte);
+extern void FinishUnpackUndo(UndoPackContext *ucontext,
+							 UnpackedUndoRecord *uur);
+extern void UndoRecordGetCompressionInfo(Page page, int starting_byte,
+										 UndoCompressionInfo *compresssion_info);
+extern void UndoRecordSetInfo(UnpackedUndoRecord *uur);
+
+#endif							/* UNDORECORD_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 4ef6d8ddd4a..5c65b96a0cb 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -215,6 +215,40 @@ typedef PageHeaderData *PageHeader;
  */
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
+/*
+ * Same as PageHeaderData + some additional information to detect partial
+ * undo record on a undo page.
+ *
+ * FIXME : for undo page do we need to keep all the information which is
+ * required for the PageHeaderData e.g. pd_lower, pd_upper, pd_special?
+ */
+typedef struct UndoPageHeaderData
+{
+	/* XXX LSN is member of *any* block, not only page-organized ones */
+	PageXLogRecPtr pd_lsn;		/* LSN: next byte after last byte of xlog
+								 * record for last change to this page */
+	uint16		pd_checksum;	/* checksum */
+	uint16		pd_flags;		/* flag bits, see below */
+	LocationIndex pd_lower;		/* offset to start of free space */
+	LocationIndex pd_upper;		/* offset to end of free space */
+	LocationIndex pd_special;	/* offset to start of special space */
+	uint16		pd_pagesize_version;
+
+	/*
+	 * Below fields required for computing the offset of the first complete
+	 * record on a undo page, which will be used for the undo record compression
+	 * and undo page consistency checking.
+	 */
+	uint16		uur_info;		/* uur_info field of the partial record. */
+	uint16		record_offset;	/* offset of the partial undo record. */
+	uint16		undo_len;		/* Length of the complete undo record. */
+} UndoPageHeaderData;
+
+typedef UndoPageHeaderData *UndoPageHeader;
+
+#define SizeOfUndoPageHeaderData (offsetof(UndoPageHeaderData, undo_len) + \
+								  sizeof(uint16))
+
 /*
  * PageIsEmpty
  *		returns true iff no itemid has been allocated on the page
@@ -419,6 +453,8 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern void UndoPageInit(Page page, Size pageSize, uint16 uur_info,
+						 uint16 record_offset, uint16 len);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
-- 
2.22.0

#312Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#309)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

Since failure to apply leaves unconsistent data, I assume it should always
cause PANIC, shouldn't it? (Thomas seems to assume the same in [1]/messages/by-id/CA+hUKGJL4X1em70rxN1d_EC3rxiVhVd1woHviydW=Hr2PeGBpg@mail.gmail.com.)

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Each undo worker applies only transactions for its own database, see
ProcessExistingUndoRequests():

/* We can only process undo of the database we are connected to. */
if (xact_hdr.dboid != MyDatabaseId)
continue;

Nevertheless, as I've just mentioned in my response to Thomas, I admit that we
should try to live w/o the undo worker altogether.

Okay, but keep in mind that there could be a large amount of undo
(unlike redo which has some limit as we can replay it from the last
checkpoint) which needs to be processed but it might be okay to live
with that for now.

Yes, the information to remove relation file does not take much space in the
undo log.

Another thing is that it seems we need to connect to the database to perform
it which might appear a bit odd that we don't allow users to connect to the
database but internally we are connecting it.

I think the implementation will need to follow the outcome of the part of the
discussion that starts at [2]/messages/by-id/CAH2-Wzk06ypb40z3B8HFiSsTVg961=E0=uQvqARJgT8_4QB2Mg@mail.gmail.com, but I see your concern. I'm thinking why
database connection is not needed to apply WAL but is needed for UNDO. I think
locks make the difference. So maybe we can make the RMGR specific callbacks
(rm_undo) aware of the fact that the cluster is still in the startup state, so
the relations should be opened in NoLock mode?

Discarding
----------

There's no discard worker for the URS infrastructure yet. I thought about
discarding the undo log during checkpoint, but checkpoint should probably do
more straightforward tasks than the calculation of a new discard pointer for
each undo log, so a background worker is needed. A few notes on that:

* until the zheap AM gets added, only the transaction that creates the undo
records needs to access them. This assumption should make the discarding
algorithm a bit simpler. Note that with zheap, the other transactions need
to look for old versions of tuples, so the concept of oldestXidHavingUndo
variable is needed there.

* it's rather simple to pass pointer the URS pointer to the discard worker
when transaction either committed or the undo has been executed.

Why can't we have a separate discard worker which keeps on scanning
the undorecords and discard accordingly? Giving the onus of foreground
process might be tricky because say discard worker is not up to speed
and we ran out of space to pass such information for each commit/abort
request.

Sure, there should be a discard worker. The question is how to make its work
efficient. The initial run after restart probably needs to scan everything
between 'discard' and 'insert' pointers,

Yeah, such an initial scan would be helpful to identify pending aborts
and allow them to be processed.

but then it should process only the
parts created by individual transactions.

Yeah, it needs to process transaction-by-transaction to see which all
we can discard. Also, note that in Single-User mode we need to discard
undo after commit.

ok, I missed this problem so far.

I think we also need to maintain oldestXidHavingUndo for CLOG truncation and
transaction-wraparound. We can't allow CLOG truncation for the transaction
whose undo is not discarded as that could be required by some other
transaction.

Good point. Even the discard worker might need to check the transaction status
when deciding whether undo log of that transaction should be discarded.

For similar reasons, we can't allow transaction-wraparound and
we need to integrate this into the existing xid-allocation mechanism. I have
found one of the old patch
(Allow-execution-and-discard-of-undo-by-background-wo) attached where all
these concepts were implemented. Unless you have a reason why we don't these
things, you might want to refer to the attached patch to either re-use or
refer to these ideas. There are a few other things like undorequest and some
undoworker mechanism which you can ignore.

Thanks.

[1]: /messages/by-id/CA+hUKGJL4X1em70rxN1d_EC3rxiVhVd1woHviydW=Hr2PeGBpg@mail.gmail.com
[2]: /messages/by-id/CAH2-Wzk06ypb40z3B8HFiSsTVg961=E0=uQvqARJgT8_4QB2Mg@mail.gmail.com

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#313Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#312)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Nov 18, 2020 at 4:03 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

Since failure to apply leaves unconsistent data, I assume it should always
cause PANIC, shouldn't it?

But how can we ensure that AbortOutOfAnyTransaction will be called
only in that scenario?

(Thomas seems to assume the same in [1].)

"undo worker" is still there, but it only processes undo requests after server
restart because relation data can only be changed in a transaction - it seems
cleaner to launch a background worker for this than to hack the startup
process.

But, I see there are still multiple undoworkers that are getting
launched and I am not sure if that works correctly because a
particular undoworker is connected to a database and then it starts
processing all the pending undo.

Each undo worker applies only transactions for its own database, see
ProcessExistingUndoRequests():

/* We can only process undo of the database we are connected to. */
if (xact_hdr.dboid != MyDatabaseId)
continue;

Nevertheless, as I've just mentioned in my response to Thomas, I admit that we
should try to live w/o the undo worker altogether.

Okay, but keep in mind that there could be a large amount of undo
(unlike redo which has some limit as we can replay it from the last
checkpoint) which needs to be processed but it might be okay to live
with that for now.

Yes, the information to remove relation file does not take much space in the
undo log.

Another thing is that it seems we need to connect to the database to perform
it which might appear a bit odd that we don't allow users to connect to the
database but internally we are connecting it.

I think the implementation will need to follow the outcome of the part of the
discussion that starts at [2], but I see your concern. I'm thinking why
database connection is not needed to apply WAL but is needed for UNDO. I think
locks make the difference.

Yeah, it would be probably a good idea to see if we can make undo
apply work without db-connection especially if we want to do before
allowing connections. The other possibility could be to let discard
worker do this work lazily after allowing connections.

--
With Regards,
Amit Kapila.

#314Antonin Houska
ah@cybertec.at
In reply to: Antonin Houska (#312)
Re: POC: Cleaning up orphaned files using undo logs

Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

I think we also need to maintain oldestXidHavingUndo for CLOG truncation and
transaction-wraparound. We can't allow CLOG truncation for the transaction
whose undo is not discarded as that could be required by some other
transaction.

Good point. Even the discard worker might need to check the transaction status
when deciding whether undo log of that transaction should be discarded.

In the zheap code [1]https://github.com/EnterpriseDB/zheap/tree/master I see that DiscardWorkerMain() discards undo log up to
OldestXmin:

OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
PROCARRAY_FLAGS_VACUUM);

oldestXidHavingUndo = GetXidFromEpochXid(pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo));

/*
* Call the discard routine if there oldestXidHavingUndo is lagging
* behind OldestXmin.
*/
if (OldestXmin != InvalidTransactionId &&
TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
{
UndoDiscard(OldestXmin, &hibernate);

and that UndoDiscard() eventually advances oldestXidHavingUndo in the shared
memory.

I'm not sure this is correct because, IMO, OldestXmin can advance as soon as
AbortTransaction() has cleared both xid and xmin fields of the transaction's
PGXACT (by calling ProcArrayEndTransactionInternal). However the corresponding
undo log may still be waiting for processing. Am I wrong?

I think that oldestXidHavingUndo should be advanced at the time transaction
commits or when the undo log of an aborted transaction has been applied. Then
the discard worker would simply discard the undo log up to
oldestXidHavingUndo. However, as the transactions whose undo is still not
applied may no longer be registered in the shared memory (proc array), I don't
know how to determine the next value of oldestXidHavingUndo.

Also I wonder if FullTransactionId is needed for oldestXidHavingUndo in the
shared memory rather than plain TransactionId (see
oldestXidWithEpochHavingUndo in PROC_HDR). I think that the value cannot lag
behind nextFullXid by more than 2 billions transactions anyway because in that
case it would cause XID wraparound. (That in turn makes me think that VACUUM
FREEZE should be able to discard undo log too.)

[1]: https://github.com/EnterpriseDB/zheap/tree/master

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#315Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#313)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Nov 18, 2020 at 4:03 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

Since failure to apply leaves unconsistent data, I assume it should always
cause PANIC, shouldn't it?

But how can we ensure that AbortOutOfAnyTransaction will be called
only in that scenario?

I meant that AbortOutOfAnyTransaction should PANIC itself if it sees that
there is unapplied undo, so nothing changes for its callers. Do I still miss
something?

Another thing is that it seems we need to connect to the database to perform
it which might appear a bit odd that we don't allow users to connect to the
database but internally we are connecting it.

I think the implementation will need to follow the outcome of the part of the
discussion that starts at [2], but I see your concern. I'm thinking why
database connection is not needed to apply WAL but is needed for UNDO. I think
locks make the difference.

Yeah, it would be probably a good idea to see if we can make undo
apply work without db-connection especially if we want to do before
allowing connections. The other possibility could be to let discard
worker do this work lazily after allowing connections.

Actually I hit the problem of missing connection when playing with the
"undoxacttest" module. Those tests use table_open() / table_close() functions,
but it might not be necessary for the real RMGRs.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#316Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#315)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Nov 25, 2020 at 8:00 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Nov 18, 2020 at 4:03 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

No background undo
------------------

Reduced complexity of the patch seems to be the priority at the moment. Amit
suggested that cleanup of an orphaned relation file is simple enough to be
done on foreground and I agree.

Yeah, I think we should try and see if we can make it work but I
noticed that there are few places like AbortOutOfAnyTransaction where
we have the assumption that undo will be executed in the background.
We need to deal with it.

I think this is o.k. if we always check for unapplied undo during startup.

Hmm, how it is ok to leave undo (and rely on startup) unless it is a
PANIC error. IIRC, this path is invoked in non-panic errors as well.
Basically, we won't be able to discard such an undo which doesn't seem
like a good idea.

Since failure to apply leaves unconsistent data, I assume it should always
cause PANIC, shouldn't it?

But how can we ensure that AbortOutOfAnyTransaction will be called
only in that scenario?

I meant that AbortOutOfAnyTransaction should PANIC itself if it sees that
there is unapplied undo, so nothing changes for its callers. Do I still miss
something?

Adding PANIC in some generic code-path sounds scary. Why can't we
simply try to execute undo?

Another thing is that it seems we need to connect to the database to perform
it which might appear a bit odd that we don't allow users to connect to the
database but internally we are connecting it.

I think the implementation will need to follow the outcome of the part of the
discussion that starts at [2], but I see your concern. I'm thinking why
database connection is not needed to apply WAL but is needed for UNDO. I think
locks make the difference.

Yeah, it would be probably a good idea to see if we can make undo
apply work without db-connection especially if we want to do before
allowing connections. The other possibility could be to let discard
worker do this work lazily after allowing connections.

Actually I hit the problem of missing connection when playing with the
"undoxacttest" module. Those tests use table_open() / table_close() functions,
but it might not be necessary for the real RMGRs.

How can we apply the action on a page without opening the relation?

--
With Regards,
Amit Kapila.

#317Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#314)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Nov 25, 2020 at 7:47 PM Antonin Houska <ah@cybertec.at> wrote:

Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

I think we also need to maintain oldestXidHavingUndo for CLOG truncation and
transaction-wraparound. We can't allow CLOG truncation for the transaction
whose undo is not discarded as that could be required by some other
transaction.

Good point. Even the discard worker might need to check the transaction status
when deciding whether undo log of that transaction should be discarded.

In the zheap code [1] I see that DiscardWorkerMain() discards undo log up to
OldestXmin:

OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
PROCARRAY_FLAGS_VACUUM);

oldestXidHavingUndo = GetXidFromEpochXid(pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo));

/*
* Call the discard routine if there oldestXidHavingUndo is lagging
* behind OldestXmin.
*/
if (OldestXmin != InvalidTransactionId &&
TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
{
UndoDiscard(OldestXmin, &hibernate);

and that UndoDiscard() eventually advances oldestXidHavingUndo in the shared
memory.

I'm not sure this is correct because, IMO, OldestXmin can advance as soon as
AbortTransaction() has cleared both xid and xmin fields of the transaction's
PGXACT (by calling ProcArrayEndTransactionInternal). However the corresponding
undo log may still be waiting for processing. Am I wrong?

The UndoDiscard->UndoDiscardOneLog ensures that we don't discard the
undo if there is a pending abort.

I think that oldestXidHavingUndo should be advanced at the time transaction
commits or when the undo log of an aborted transaction has been applied.

We can't advance oldestXidHavingUndo just on commit because later we
need to rely on it for visibility, basically any transaction older
than oldestXidHavingUndo should be all-visible.

Then
the discard worker would simply discard the undo log up to
oldestXidHavingUndo. However, as the transactions whose undo is still not
applied may no longer be registered in the shared memory (proc array), I don't
know how to determine the next value of oldestXidHavingUndo.

Also I wonder if FullTransactionId is needed for oldestXidHavingUndo in the
shared memory rather than plain TransactionId (see
oldestXidWithEpochHavingUndo in PROC_HDR). I think that the value cannot lag
behind nextFullXid by more than 2 billions transactions anyway because in that
case it would cause XID wraparound.

You are right but still, it is better to keep it as FullTransactionId
because (a) zheap uses FullTransactionId and we need to compare it
with oldestXidWithEpochHavingUndo for visibility purpose, (b) In
future, we want to get rid this of this limitation for undo as well.

--
With Regards,
Amit Kapila.

#318Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#317)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Nov 25, 2020 at 7:47 PM Antonin Houska <ah@cybertec.at> wrote:

Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

I think we also need to maintain oldestXidHavingUndo for CLOG truncation and
transaction-wraparound. We can't allow CLOG truncation for the transaction
whose undo is not discarded as that could be required by some other
transaction.

Good point. Even the discard worker might need to check the transaction status
when deciding whether undo log of that transaction should be discarded.

In the zheap code [1] I see that DiscardWorkerMain() discards undo log up to
OldestXmin:

OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_AUTOVACUUM |
PROCARRAY_FLAGS_VACUUM);

oldestXidHavingUndo = GetXidFromEpochXid(pg_atomic_read_u64(&ProcGlobal->oldestXidWithEpochHavingUndo));

/*
* Call the discard routine if there oldestXidHavingUndo is lagging
* behind OldestXmin.
*/
if (OldestXmin != InvalidTransactionId &&
TransactionIdPrecedes(oldestXidHavingUndo, OldestXmin))
{
UndoDiscard(OldestXmin, &hibernate);

and that UndoDiscard() eventually advances oldestXidHavingUndo in the shared
memory.

I'm not sure this is correct because, IMO, OldestXmin can advance as soon as
AbortTransaction() has cleared both xid and xmin fields of the transaction's
PGXACT (by calling ProcArrayEndTransactionInternal). However the corresponding
undo log may still be waiting for processing. Am I wrong?

The UndoDiscard->UndoDiscardOneLog ensures that we don't discard the
undo if there is a pending abort.

ok, I should have dug deeper than just reading the header comment of
UndoDiscard(). Checked now and seem to understand why no information is lost.

Nevertheless, I see in the zheap code that the discard worker may need to scan
a lot of undo log each time. While the oldest_xid and oldest_data fields of
UndoLogControl help to skip parts of the log, I'm not sure such information
fits into the undo-record-set (URS) approach. For now I tend to try to
implement the "exhaustive" scan for the URS too, and later let's teach the
discard worker to store some metadata so that the processing is rather
incremental.

I think that oldestXidHavingUndo should be advanced at the time transaction
commits or when the undo log of an aborted transaction has been applied.

We can't advance oldestXidHavingUndo just on commit because later we
need to rely on it for visibility, basically any transaction older
than oldestXidHavingUndo should be all-visible.

ok

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#319Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#316)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Wed, Nov 25, 2020 at 8:00 PM Antonin Houska <ah@cybertec.at> wrote:

I meant that AbortOutOfAnyTransaction should PANIC itself if it sees that
there is unapplied undo, so nothing changes for its callers. Do I still miss
something?

Adding PANIC in some generic code-path sounds scary. Why can't we
simply try to execute undo?

Indeed it should try. I imagined it this way but probably got distracted by
some other thought when writing the email :-)

Actually I hit the problem of missing connection when playing with the
"undoxacttest" module. Those tests use table_open() / table_close() functions,
but it might not be necessary for the real RMGRs.

How can we apply the action on a page without opening the relation?

If the undo record contains RelFileNode, ReadBufferWithoutRelcache() can be
used, just like it happens with WAL. Not sure how much it would affect zheap.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#320Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#311)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

If you want to track at undo record level, then won't it lead to
performance overhead and probably additional WAL overhead considering
this action needs to be WAL-logged. I think recording at page-level
might be a better idea.

I'm not worried about WAL because the undo execution needs to be WAL-logged
anyway - see smgr_undo() in the 0005- part of the patch set. What needs to be
evaluated regarding performance is the (exclusive) locking of the page that
carries the progress information.

That is just for one kind of smgr, think how you will do it for
something like zheap. Their idea is to collect all the undo records
(unless the undo for a transaction is very large) for one zheap-page
and apply them together, so maintaining the status at each undo record
level will surely lead to a large amount of additional WAL. See below
how and why we have decided to do it differently.

I'm still not sure whether this info should
be on every page or only in the chunk header. In either case, we have a
problem if there are two or more chunks created by different transactions on
the same page, and if more than on of these transactions need to perform
undo. I tend to believe that this should happen rarely though.

I think we need to maintain this information at the transaction level
and need to update it after processing a few blocks, at least that is
what was decided and implemented earlier. We also need to update it
when the log is switched or all the actions of the transaction were
applied. The reasoning is that for short transactions it won't matter
and for larger transactions, it is good to update it after a few pages
to avoid WAL and locking overhead. Also, it is better if we collect
the undo in bulk, this is proved to be beneficial for large
transactions.

Attached is what I originally did not include in the patch series, see the
part 0012. I have no better idea so far. The progress information is stored in
the chunk header.

To avoid too frequent locking, maybe the UpdateLastAppliedRecord() function
can be modified so it recognizes when it's necessary to update the progress
info. Also the user (zheap) should think when it should call the function.
Since I've included 0012 now as a prerequisite for discarding (0013),
currently it's only necessary to update the progress at undo log chunk
boundary.

In this version of the patch series I wanted to publish the remaining ideas I
haven't published yet.

The earlier version of the patch having all these ideas
implemented is attached
(Infrastructure-to-execute-pending-undo-actions and
Provide-interfaces-to-store-and-fetch-undo-records). The second one
has some APIs used by the first one but the main concepts were
implemented in the first one
(Infrastructure-to-execute-pending-undo-actions). I see that in the
current version these can't be used as it is but still it can give us
a good start point and we might be able to either re-use some code and
or ideas from these patches.

Is there a branch with these patches applied? They reference some functions
that I don't see in [1]https://github.com/EnterpriseDB/zheap/tree/master. I'd like to examine if / how my approach can be
aligned with the current zheap design.

[1]: https://github.com/EnterpriseDB/zheap/tree/master

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20201204.tgzapplication/x-gzipDownload
����_�}�{�F���*�=��2)��M;�,-��6�V���f�����5IpP�&���_�@��!;�f���D$�>������X�]��h6�f�}�/���F���t|Z����
���O�OX�^���u�VG4,�������>�I�[���P��/kG�#��'��L�?����u��g������!|�r�o������D7�m}aG���V���z�c<���z����h4�V��/����\��t����']�w�?�vc����w'��#n�98��k����-g<i��8���Z.����K�O4a
w���b8���7�e����w��<�eI�nGov��H���&��H:O4/;�����6�Fc�z9�o�D/�O����@
����R0V��++{�R$x��{��e(�x9�2uGL�@�������P����-`���z�3��S��Sw7�S��5,�����W�n�39�TaG�"��#�����<���w>��8��N��7�E��Y|6�d�5� 	�sl���b����dB{<#���\1�a��K��5�����Y����`?�	�,4��$��,#��B��L���=��P��o����{oO�0����x*a0��x�3��g���������ip���O�9���	�	�U�g��N�6L�c�%>a&8����;���B:��sDA�Pn.f���2�%5��+0YF�@�z��T����R5{
��9����0��S�oT���D����7w����r��G^�,C��KqE�������C�2�����3�������	��������w�{G����{�_g�=�?���[������~�s�������C�~�����������[��������]�<
����?���<�]�^��W!��jTK/����k�Z�����L��}���?�:L9W�'��*��K7���7"�����Cw��L��S)�vv����K��U�8L�o_������;�Bm�O|����+�b����[:<����>O�����.�v���\��*�F�g���n������A�./JA�;;��?E��`r��u5q5�z,��������G�^�u�Gw@%�2���Z�����$~7:^9��8
�T�9�v�T���^��I�"D���a�����,
Y��I�m�>
�yh�
�{�H�z���^n����F�@r�a[�@kk.#o&c��!�q����?#?�a���*���(�k���������(�����9v�p����t���n5�C��E�&||
�OM����L�9�VC�+�����)|O�aR�z�zbz�o������Y����|���-\GuX�V���W���`Uv�� �I���=u������;��0���7]�R#�����Ui����6T�&B�ZZ�wS���#2��2����v�rv������%x�zaI��j�z4-�K��J�<>@:f�����V�5���W9�J[����J~����3����I���	J�o� ��m`�K������������u=����o=(J��q��]�	�������q��f��O�-���j���h}PaU���b��7j�l��S���l=V��D(h,��K��k�I����9I����l�% ��^\����o����������_�����D�IT���7�;�g5qr~r3zwq������jPgG�
 I ^����(XJh��,|Zy��b�
�s<�O���j�6J��������N���P��E1��	)�u%�4�q��v���L�eY�j�q���4���B�"�tk�����q-^����
��}��������9�L���?�s��}W��v4���nUU�o;%����!���#;3|g�a�J0��|��������F�,�3�r����E{@v�
."��7�eO�#o&�07����-]��t5��V��h���N������KX����|9�����=#�5j�WYD�v�/�B������.�xPy�*0 ��"*b�����(^��Z"�����g�����U�����H��C)�e�euC/M�����F��3T!M}�e���.������_
�o�����P{����&�*o��6o&��
\����`2���O�mQ��2�f���3��'��:�I���tU��QG����nu�@6�Vw�n�:�Fd�lD[�y��V��(�F����1voS+�@���'����M!�M�}�A�������J������G8]���^
OO�O�����+}�o���L���^�=Tb3�]����������^�W;���B���8+�n�kq	�G��y�H��n�_�2
��\z@"-k��#�����@KT�>)�54��r�4O�k�$����>,������9Rlj���p���T����
�G�h���N:\7xh%7�'����t���G���H<�x/q'C��cX�{H
��n+���:��W��_|w4�t
J�}������Sb���p����
��F�a��
T��7m����'P���-�P\�iPiPi����T?������F~�g���T���ov�]9���ht/����`�f�Z����.��).OtG�^����dF�
�s�G!L�r�k?acM��P���
Zc��L�1�`X�n[���V0nw����
vj ��_ZC%��gY���bw�_a
p�@�������������%���������N]I*5��n���XWT�B��.�>�����q�
�j�S��*�%)6_���q!�b8�������0�CIu	������$�]xPd�����	e|�po4kM<�-��G��F�i�hx����h��� C��O���1|��#xS�����;�����Q���<��������0��s��{���gz�����6H�7�����I�������gH����.Z�j2�fjT�����*�04��U-'�����-|�����Q���:��_kd���g�zl��D9�l7<����l=�^Q���`���b��Y?U7�����/6K�������5��_��88�R�I�x�z������]�3}V�V�(�v���=T�;@]����>���V����H�qw{���:-<2�N�����(<Ga�B�kq
x�v9��(S���QL}����\�E���������?J����D���K�t��zrp�+�bL�kq���:�+"OQ�p����FE��1�%����5�Uk6���`��@�Z�S�g���2v������/�r��SGQ���7�	�����+a���:{�`?� ��o�uy{G�U�v?i�?�F����P��/4�����rdw�J��rVY��
M��'�����e\�3�A�&6���9���cIP$=��0&���Y<����j�K�i���I%�j���Q���N�h���	%�i������[�x�sO�h�:����l9��.�����[���6���8�O�Hf��G�[�QmLzD�-&��8�8��1��W�i5K�=O���o	��S3<yD�T�2����[��%;�@F��L9�uJJ����o���Rg��x�t�=��v{�#]k�����Zb��Rt��@��Z�w���M��������]�4���n����zG%E�O��g�iDdmDo�������wD:��~7$�em��"���o�g�[�+�Q��o�W\]��x��
��C+����s���Q]���@�.�6~��x�����2/��j����o$��V+a�pJ��&v��MJ)�u�v��h�f�^��n���'��*n��7)I����;q�@�
�~���������x��^���51�/�<��v�<�_@*��#j U� ���$�����}��$��n�[���*���������n��'"A�W,�.v�!��Th����w(��
�����pq�H���D����.\�T��I��nt�x�7h������E�tS���.�h0���?����J��	����B�R���2��SiOjLX*�w{����~M�?����	�(��Q��uO"9���I��	��D0{�����
>��	2	eT�cF}��:�����z��R�u����+9}[e��;��5��;�K����p6��r� ����s��y�Z�o\����*<��uI��o+�WWW kOl>���������2���\�qy@�)e�0���0�9�C����o�s�x����4V���=pZ�^�.���hw��VgZ[����������G�q;�F4 ������'�7��(�F���)=�Cf�Q����<1
��FW7i{0�����qmm�v.��0<���G����X�x�����ShB��6Oo�U_��Dk$4��dJ����V��T!�r��%-��G(���=v�Fcs�[OJ�I[5h��"}(�Cjz�f�����*���
�BXq�5�t�T���hu-u<�<�������e�x��'S��~�������|<F�����?8]t�r�{�yB^�������<d���z�z���O�29=��4��@�D5�fP�kWP3S_���~�q��2{�U�}�xK���������pR���5�L����k&��L/n@	�[�/h�����s�f.�_�L)��!G1�������t��[GDr~P��9[J����mt{�n�HKv�v����Z2�k����J"t�p��a�A��D3B.�t��@���iO9S��&Ji�~�
;��<U!�����
�(lnk"�b4f�����:�:X��'�&fxb:�I���|��������M����\*�=x����?@`��<��^�������f*��|�c�p�I;,*A�(�?K���-�v8�>W�4�3�9��O�v��)�X}hX+���<���Z��@cu�Z����G
n 4����t-�P����}*�h������1^__�\�.�O��]�����hLZ�2pC�����wN�DU����>A��t�\�
��)���xj'#1�\�$����jbOo�F���Z�{�Y.g#.�.�?;�5�I�����W�\H��v�����=h���fg0vZ��`�_5���WIRm�2?h��h���%�M ����X�� ��!k"Qc�A_��h��o��,C�n�lZ�/S��y+���Lqe��f���kE�B��j
��>��50�`O�F�%�1
31�����1����9���S��'�r�
6����U����������uV9��X �Al��]��|��Mm=��o2���7Hm�H��#�Y$6mNz��` ���t����z�O5U���b����E���DJ���9�#�W��BY�����!$��c�C)M�Qp{�����@G<���2��(��KRx��	����q���e�&�<\���P��oB�Z�j��3�{�B����f=
n�p;��o��c
N���0,��R�������<���A�I@���P=���*��&��W2B���=�6`$�����V��-�0��99�D0�����������'�p���#R����FkO����H��Z5�;��<jM�U0/$�5�m�?�(
?���:g�L��]�W�y%0�����r�E���LAy��F��f2����0��}��������o��� B���b.�^i�A�>�wy/��8E��������P�T�)H.�������*H<�����(��k�+�9J��E�	��K���m!XB�Xs�i����n�o�w%��.n�,�CpK��8'��������0�`@���~���.�L��m�;|���� ���	��sj���^\[���<vFyg�#pJV;|��pX6��G�����U�?h�~�W�n�1�37�m�-����b��k����Zz)����;����o4��[�-F���/Fq2k��
A��6�9���h\Fzu�m���.����F�/�s}v�u���)�	�%��+�U����L5B�|�8����"�����$G�,@[9tS���d�����	F
L��q���lq���^�tY���E�RG@
>�UQXM���� �W��A�,�Gd�S�UW�P�������?)���fUe��)�E��!^��
���~�����p����S|Vn5���.��(�8�~<�x���*+�����V�o#�p�pb�B'���3��r��fsb9�A�>h�'����Yo`�m�t�d�.���-}�?��0d����q�h�`M���Ta��#�,@�&A+�,#�����V����t�u�cO�fw�^�������DA�|��k���Zn���
��A�D#�&�����Am�q��b5x�m� )@5�?HzF�.O3R����hf������.2�R6���,����T�*cf���h��P"[J�I�j��h�pJ7[�A�m���-�d��D�$�u1�W�?��@f�C�����	PE�I�w5��j�����*�:L�����������|f��X�F��������)<�5?���)�ld����������^��RLA�Hc�z�?����w��[��]G#
��|9E'd�������	�H�=���^w-�(h��R�e��/�{�N=D���y����d��1$[�q'���I��Es"N�O@~C}p�^���J�;XQ|���2���V%o��O�"��7[
t���[�������� �z��$���V�x��lAyWqg�R�~0p�mu��o6�v��r�5|��
h�i�v�V'��t!���=��~�z�f�'��b��T������6j��
��*{��� �VX(S����CUP�~�7U;�38���p`�.��09
����t���sFm���NQ�LsP���c/�y$�>���%�H!rXOp
�==��h�&��������y�'�!�X�Zyd�_4������ibz,�x��SW�KL�p&�.�*M�&\�Y��i����*]z�'rM����v�����2M��:�-"R����}u��K��{���ot�@�i��[q���X��L�(�&��dK�+x�(X�N���Y/ed���8�2�~�5����r�z}��'�����.2m��r�rD��E�OZv��:�+�3L���U�^��ZH��yD��x��F������C�+8$p�����������=�G����l7��v8�?�e�}8����tI
��0`[0(�P
�Dh�G'�j�S
h�$�r���'U�6������;����x�2�%���������db�b;�a��
Y�[�L0���l����4wk�
D�3�a�r�q:�,�#�����<H�E��O���v�(���A�I���^�c� ��\���re5
���	eWo�F�Wg�S��^�O�)����'{��@���$fW��7����A
��ra����pZ�C�n�cr���:�d�����
�\3�?�N��y��bk��B�y�
��Z���/�>9vL�e����S��1�V��j%TQI}PL�-�������l���I{��b�"��4�����6�_�{�7��W|��|�N}����OL������3����*|u����w���F�+,U��$����2	������j�5�u���]�J[(����??�)s�@��I�RMk,�l��lq@a�QQ8����"p{!��_��:��L���!�����'����}�#>p�o�v��y�����guC����e>����I�'�
�5.�x���b�������%����Q_���Rw@1U0���6;���L�,���u����<��?u�
������n�@���.�)2:I����=����0��h�@@�K��`���v)T}�0����zRR�w�Q��	�^��U������KW
]eSA�xEW���.���y���#��R���F{u'C����FCV������,3>S���3t��_R����T���r"��Z0�cZ3]3
�_xo���'��X���{�|�4s_����Nf[�I�������������R��AMU6�j�AW���e��_�{������:b���������wY0�%�%D���%p��<�O�HzJN.�[,e.y?RX���(O���O,if�����te���sZ��u��{J��L���?-�t�'��p�w����������xtu|�����#���;O��o�(���������x��N`G�]_z�����sOi�TqQ}�y�3+mT�J�1`��j�6�8hS8��6G�\�!;|LSV�a�DDR�����������K����/&�T��p��@��-_|�����{[-���lx������'��5����~����a�o��G���W����Y�HooRy�CWPFY)����:Y7	�V:�j�(�5�
)�A�_DAz��k�Fd������p�pr~c����D���"��@�S�r99;���@�X��(�jbfBoM������"4�Q��o^�np�qM>3wG� �o�B5u���c+��T�cQ�)1j:��\�l������W�X��x@oi�
�S�&|�(�����rl������u"��V��4���P���T� 8���nj;;w�s���~���
�{r�L���F��������fJ@������o�Z�Tl��i3Vi����[��nT�Rah��)��6�	tE�
)Lf�$��r]EQ��M4N��.j6=J�_X�~���6e�D`�vSvR��Q�B[8�a�wJ�U�0��?���������Q�5JO��tn���k�
��tU�N��N�����18^����]t�lE�]��3�V�@�@_�K�QaZ��w&n�^�t�������L[�H_���X�&o���<��ue�����muR$��e���?���{�I>�@�l�(�2"���=�n�Z4g����DlN���R`��N�05��g�#���b�|Z5��{L\�$I�bF��r���H������������������
��2�XUk����am�Wa��3I�����D&0r�j�����������L <T����=t{��P��_��=ze�SB����q<�6�HV�P���"_� xI�C��(�����`��.8R0�����"�[[��K���~����U����[_�_�b�z��.�+���S�������
7jF������n�kJ�55�$=�/��#��Y�~G���@����Z!��,�5�Q���.������5�_�
&��&�X�C��O��_��E�_�wJc�H���4�����	R���+)-ZJ���*��EB6��I7��vB�rzGr�x[�{[�����������n���`�X�z�3�'����nu�7:�uaR�d���� �jAE�����2}���bd��*?y������-|�{�lh	�5a�a��]�I�������x��b�=y)������0�~��qe�f�)Ot�J����n�7��8F���	_.��b�
��b�3�T��)�^<�bfu��Mf����j���"3��:����?:<=��!�#^���3fI
�1�a�����	3����5��|�R"����_�
r������@^7�@��Iz%���XoDr`G�A�Ks����_�[�/$�Z@��17��^s�����f���+�ok��8/h^�tr����8��Y8���JY�0�����v�����@��?$n9Z#c|�T��)���o#~��P�By�'�	K���-�c���00����k!C'1*���������(�g/��B��q�TF�J�@��.i�����dW��d������	�yxl�8!w���d6_�Ym�����u[\k'm�W�]S����O��;�6��p�Y"���	]�+"3>wb
��**�%��O�	=��Z�,(*
l�VLE�Ss���,�jh�;������.����*
_����v�J��if����_���n+�T���~.';����j�V�����z��t�gO�+D�|;yI!_�U������k$S�k.[�@�=������V�!J�#���M|������D��B�	��7�?e�d���OX��TB
T��6�y�^�q��� �8D�:�������QC"�
����HW�3�W�vss�.|5�,p�,�RR�(��$�iM_���Y����S1�T P�����%����A(���k��l�E�D/��0a��,����D�SeE����RP3��FM�C$b.��ee���;�&�@�l�^TC�Ij	QRR��#�mS�U��`]?�����u�v����XB���I�=Rn2�F��%�
��X�[�����&��}?�s
}
_HF��z=�-Cv,p��e �(����>O��y�r��0#��^������P�!��bV�f�N�����I�8���oq3�qh������k�j�o�>�o�?���o[��s��}:��tG�p��?��m�����>��o<}��mb	&--G.*��9w"\�b*����+�}����
(��3s�Z��"��ow'V����^�>i�����R�8���*8�����J��q,�xH#)
��r�|r��'�}�]�>r1��y�n/�==pI5�[�UE��w�oK9����c��I�n���pe���\Y���n��a%��99����!:�|��'�l��)���$��X����w(�5��3��������<���^��iM��T�'��J��J�NH�o(�s��>=9g���|�Q����E����E����0���-�0f��k��#b���A���y
��0��?-(-�z��$�;��A�r��\>����0^0{"���K�����N�@�d��79x35������d�l���>H��w+��o��f�p�)��{t�Tj
Pq8��� %-��*n�oUBX�sG�9	1��f���B��{�.�rm�����l�aWA�G3{�@3���m�2Im��a�O��L8���?Ce7]���Zg8����^�=��}�)�8]z#���7,����$�d���'�X�����`�������V����1�;���J���M3I���Z_�^)�x��c����R�D�m��=��N�.[�~�t�{�����(+�w|Q5Pa�.��'r���T����EA�M f w-A=�K�AIx�h�V�>F�5��>^�'��XC��XW}3|�b
`A-�>�X������Y��6�����S���s����!�ZxV����"��F�'G���;h�)Y����$:�M���;_�B���f�^�����(������/`�������N��Y���]G���#FC�/{f@�;O5�a���++nvK����\�EJ"�5�-H���Bjj����B�|�1.l�Yc~�m�Z������|�����7&�����4��4��jU��P�]h`�]^��jx�P�\AG#�@v��<~8>������ft~||t|��i-�?-T"�7�	�="��T�?�~K������X7�b���������$[��.��������+k�\q�Nh�h3���-�C's���}�S6�H���mY�~R~����VT\�9�����x�L�7e���5�2���du�.��L?"��8�bi2~�Z���L�)iuU����h���r7G2tH����t
���������CD����[��XE�/��=�hV����8�&�����'��Hl�������f7���������6wN�Lm�^����JI���^C1�3W��1������|$'�u��I���I���{Ec���'���@\K#����o����������W���y������������&i;�J�0�M�������m��gY
�-zc�d7�+r`��8��]�ba��K��i;?Ar}�f/����F~URi�����?�iMc������rv||+b[�=^���pi��������+�������sct���A]�7C�@�m>�MmwzJ�x�K��Z)�)a%��(r��m�w�������7�� �E���J�Is9�
NQ��9��%){�,�n��&J����$N5NQ]�x+Rxg�VW���xS����	�I9%~v'�7n��z�3nw��^��"�Oy{kP�,IB$�v3�)*�?��\���ow�QKE��?��%k���bb�����^R��2\N�������wO�<���$;�i�1
��k�
�����p���3��4`?B%i��2�V*�l!�*!m�/���?�Y�w%�3���MK%���+�Mh��N?��M������z��_�(�Y��8{����cuC����I�
Y����tM�&���2�VJ��j�l�V�;n8�z��J�i��7���,'�����j�l���A�k�Q�>21/�N?�V���[�l�t�(�.�C�^�]��T����i�eE^�V`�Y�
�5�]��Rj���������+B���e� �Mb�v 26�i����=��X�,��&�ic�����vEI���1i�����I�������`;|�[���,O��a��'���������8r�;��
W�ELT���b�41�J�����Gn��$�g�0J]���TXc�����wt���{=���xs�@���X��Se]W���t@�'_;�_J��E�I��R�����#���v��������:�ns��%���WRF�vQ�q:J�wG�>=2�]\9E�R�%��lD�5
e���N�j�������y0UW�%�#c��+��^��j��T�J��P���+�b���1=[������|��-����h4s�8S��(
] ��;q���xJ`�3�ZQ#����qI`����7�PvtG������9U����qe�R��;8�La���E&�x�����7Y�X�:����kF���gp����4�^��������+�,��fS���b��_�w�����wbGvD^��lg�����'�;���_7GK��}��r���H��Z�j.��~�`E��^��}a��,�������5��"b�"��-�y�
Z[[�zB�(h)��j�L�(:,���*$������+Q�z��$n�������@J:�O�b�]��������;�H��
�]-���J��f���A��rw���70��=��m�gF|����"&��,n1e7�����2�V���>����3�;��^w�M�5��n�1V����J���.������%��6
��`��x1�S����*��O�t�$�t��p��������?�8[��w�?N%[��`{r�I�k-����O� t�`���I����wR$�H*���a��=u����5��E%���%�iw]`��@N�-��S�h)����X'L��#��<��G^"��D����-���se��
0�,^�py/H^���)��������s�s����	���rn>�,<~��`q��`qJS��,F|U�l�T$]����l����+��edu��_[�x�
��(N,����IZ�jyE��t�4�����$����X�2*�
����'5��S�~�E�Pn�'���8���o6����
���i��������o��_�����o�j�h��;y�b�Sh���q+w�zsg�t��d���S����ll7�Nw�	�����[������ZJ�U�H���]���U����&a[�{V�n9)ycX�`�j�������@/��������x�1�*���5�wV-���G��X�x.��5�(������Yw����Z��o�M��
?�V�Q��-���.m��W6�+�T~g�\ Z_r���Xu����f�:�1�Y5N�c��q�Z
Z��H7^�PF�� OV��Q�T~�K/�x��1��[�>��n#j����������,���P��j���B��J7S�e�����.;���_'�6��3Z��8�(-K1�6
����B��8Ct	u�4�]��p���A�[9�8���U-�<C�RK
�:�L�v'�=Ui��S	?� �`y��`��#�b��P�KT���8�u:�]�>��l��}����i���E�A��
���Ih���u_$�����&�li�@�z����
���X�'��7��~�Jc�eiS>����B�
k4�3L#�����F��ir��/���S�ug�8�f�"�����
�1@W�pU@mp�����e��^_���Z������5
�pV��t[3E(c��^��������Jm|Id^��Rn�vS�NQ�8���;����Jx������O�y���_�k?�f�p����zko4
�Nq��UC3��2����\a�t�At<���<��\�VS�F��#N��S�G��;�6kI)�7m�z�����d���V�����������$����T��f�v�`��s?�/�����8P��n���H����O��H��	Lw2���2�L��Scc��I��l��'��!1L/���b��������uoG�
Y�~��)ZZE�e_��"�LW~P��1p1�9�:�Z��03������~ac6,�.�S��e8md����R�m�A��F��Ufl�����x�ku3�n��t���5�f�8��\����w��t��%���������o"6�����0ut��(�W����'��+*���?�����|��e�uJ��\�] EI�Q>���$�D�2l��L���d��5
���H�]!�52��v��g���/Z����h���YUYQ�
�)�s%na/��	`�N����_�{�L�l���������	��������)Lz\2�q�z���d�-��0�`����t������Q&��?���P�Jq����a3��a�"Zazd �(�wa��#D�+v�+����D�T��@�5)��?�1`e�n9�v���8�fV�C\�=���\�X�C�7d���j�/����R����[�a�/Y�"�l�&��[�r'<�����J7�WY&u'�,���&'��E�J�}��S��U*�mVE�6�����!���4O����?��������D.���"O�XQeI:����fr��u���xKO��"�K�D�~mcOQ��������yA��I�H@���������j�J�����B -WP����nO�*o"T���vh��I�o���v���[R��~��l@��u;L�u�,�z�0H�m�)K�O^%�Dx#g2s���� ��p�[����U"��H�[����3�e��j���n��K�b�=�����Nj�	e�^��09o��-�����[�]h3#��aSp~3��,��q����y�U�=+r.h�����n��H�����N6��nL����v����I�|�-���|�����t2a��h�X���0~��H3~�;��;��X@�^�3{J�K!�;R�Y-�4�&��A�F�L�lO���thG�zrx�y��/���F~����I&�ur�6t����o���t8�o���1��=�pA�aB7t�fPe�����/���'��L�����B�X
�����%[3�Z���zO��"Qf�.��u]�����/�VLd���*����M��
(6�n�������������~��lX�F���h4����.�e�����^���V���d-h��_����O����O��n��a�z�f��i6���lZ��/��q_�o��>0{�|����������;��f�A�5����5��1,���[�k�'������l4��8*t-���F�%�'������K1�G�����2�d�o��w�\��S��7�@V�K�.�j���"�h^v/�@�@�w�����N�R�t9�9��p`�~��Ba%��&X��f���7~\f��G��Zqn���c_�����s��8��Y����.i
�#�
a�C��I���Az�w����S|w�H�QF�]"l=�81/��T6*�L�H�PE���$�����rC����X�y��N���p��p���P����i�ud,}I��	�'����5s� &9r�8�*�?
j_g38v�,�:�Q�l��kzc8MvaMg6�%V���������pxut|D�*�7�V�J��j�����0!��5qV0���1x	C�gv(����GbWl�R�W!0��
e������B�.�����<Tv44�[\/��������6}��
�����U$���,��Fd�LJz����u��!���
bYB�Q��$Vo <m�V�pV7��������G��$�$Zj����.���sM��=���[��h�wS���<Y�kd�In@s�~�%�`����W�'�7��6���lx~|~���%��b{�Qi���;@igM,��p2T�����H-[b%��������(gl`��0
%y4�8g������t�t�E�Y���hr�����^i/��S2e}��`�*�������MX��S"�&Ql�u������hnRh������n~��)��rP�z,�����yD����|(z����l�0�mE�3]4�����7�R|Jt1��J��f>�/��x��D�����I�����qd9V����lZ��7u��!�eBX���s(^4�
S���
��'+��Z�P�v[m�a��M��	�M�s.�0���V���"p��n��|5�7gB��B��K�����+^��qB$��%+f��n94���2����&������T"Ix/�&�D�IsOl���	��G@�&��0�	��	 6|��c('�)�an@��H��J�M'�B'@�B�v���~rpA�9H��C[���
��I���)������tl���,�B2<��*�D�aLC��I��%IN��sJ���Uf6�B�;;%�1��pPq�c9������Q��K�4���(�%���@<�)~* RH���FYHB��G�df�����k�Z��������CO+BQz��y#��'Y�+��?�$������#%�]jN����v����b���TZ�@?�a��l	u�2��s�FVI�j������w06Ix����1EE��\^H��#l���6��,#��IM=�s�lRu�o�o��&��7�Lj�9�����\}=E�����;���{?���~6���.WK����6�w�+���M���,�����WfO�\������Q�Of3��8&������3��<�r!� �vMkA&�xA�!5��x)�sX"�o:'"��A�$�>�,���"Iz[*�����N����bsQd���j���aD��2U�E��Oi�|�Q�/�J&�jq3P�dU��������ZF�8�L��:��y{z������$i%J(�Ju��t{���q���F[y3���������jtsrv<�>�~��t�@1����l�S<%��1U����-r�}Ju&k���C��X_�����
�!V���������&��0�����Eu������P����?�P�U�ZXS-N~�/�y"�R����%�4�\�7��l�tL(������q��Fb�#��(8�u��J���r�@���k�*�C��W�M_���������}r/9,�*�5�����E)����
�_1�6�~|7�x=||x�*�
��Krp�;f��`�bd�GRP	*;�3������B�U�	�f�S�:�p��������k�b0n9��n�`b�z���C��R�}��i��w���^��*��kjM�D��r}U��8����������������j8��!�w�u'&�
�3c��S]Sc6�7h	GS�ob����/����M����o��q�{��Hv	������N�Bt_����P��0`�P�2�2��a���?G�	�
h�Q����6�`K��i7���Y+������
�Ns��$�t5*��9O@�K��
�����?�v���8i	>!�
}���f���(v6`�$e��&l��[����[d���V�
+���|0�6�h�N�R%�s�(�Ku'U.y���.�7l(I(c���;�����i+��j����������8��Z�x9&9��&�T�J�n�����x*Cz��D
(��')T��b��j�9P����91V����kr�<7����u&�KHs�G�������5���������~H� p_��d��-�k��6��[2���_3�?�o�%7��?< ���q����`L���Bg���K�K���N 5&��2���mq�*�$�/���iJH�������W����c\�qAXk<�
/�l1�3��&P�-� r}3�9�.����"�x���"#3���6�]�{���E��b��frs�k�E���"��u���$c0�!(61����L\�php����4�����0�jD��-A2��l���
�
�u��@�-�fN%��H�`�*�y|*L�8�d*f4_e^k�u�Z�SVI\�f�-JW�J���`^_�74�U����"�G ���^$��H/�$5w �8�:3���ERH(��1c���F���W��������k7�����V�p�p����z���6M�2	B�����)P�����~�#��TZ9����b�l5+�5��Vk�y
H)�
<�cx(��c�%�	���"�N���9^,�(�����v�`�q�~
�i���!���@� �����A�������?�����UR�,nybCt�b��������.��PN*r��Sq�.cq=�������@�t�]�'����,7���&�ytq~�U)�R�g����R�KkC�^"c��-L��D�i>��$�Q~ �u��#9�����^&�N�O���f3�s]���hKh������b�\z=��{���$OO��q��j5A��~��f�A����i�	H}��+P	]�B���L����o����������5%XbV	4����A��7�f�-f�Q�W���
'�0Ny�TC���m��(��byk}'�� v��N��)p���s'e@��F[����5�&k��
��_(RP�{���@
��#��V��(b������$�l;����M��|R�s���������>�F����1�{�����'��/o��{)���0=P^v���W���5Dg�^��^��4i��G!0��]
25Q�t}pX�d>�?��vv��qbW����5�"x��aQs2r��yE���FI��X
�H���F
O��,S�@m
]������^�Y)dK{���y��O��Bs�)`���S�$�D�u{2i8V�;�~��"�&'�k�����I��rah���\���Ci�.2��p��8��{�aA6�L��j�hP;f���ve0)t@��1�E��PD3�����Fo�/~By?.���V������;���{I�9����-��p5���;.�Y ���q�
��q�kt[���^_'�F�md���	��%�X!�S��,�O���^�K��T�;|�P�������E 	�5��g�����mWY�Z6��@ic?��
z+X	6b�����YC��|��3s��D��=�;�����t�D�@W��a�E8�?�����5[���p�`����x�������6`3[��:I00�@�0���:�Z�K��Y+G��iV$�^DAcc�/�jy��&U	u�=f�eb��i�Wi��D~�PjI��z�������;�Z��+�����b�eP�� f3i������mh��(S2��v������iU��aq|)Si��b}�~+��3���OU������{����������Lu����-��2P�
IlD>8S�dVl'����������F�z�q��q�aw�~��f�����d��H�vvS�Tt9�����������o?�zM�z���9����`�lk��5vM��c���a$ef\r������M��t_�h�wR-�$�^v��d��;��dPb��:��k*���L7g�8T�LE�|��v��t"N5`d��ME@�<b:�gy�������L��l�o*T��L}q(������N��o�'Ef�~I���S�Mp&~�S�F�����6�-U�h�
��FT��6+f�l���e�;���hd��I��tw72�S�zn/�;?�?{E���R&Fs��W����g���X���:�x���g�n�|	H���K���(��RF���|QP�
0���H��G����{��{n�^���C��Ag��HM����B
�G3H5s9��v�����T��������.=���r���WR�du/$T:��������Q���g���e�{��+r<%
{�y�\O��'
���_O���]�E�-��4�n[y��]��	x��X7���\�����S�v_)Y������*���jadC#�SG��f�������(�S���NK���t�������jv&���w��c��F��M��V�	�S�h��q]((\��/��@\��?���EW���-�C:������|s'Y0�UN.Fm��w�<�L>�y��-���K��;��ql7nB����i��g���K�F����db:@ ����(�
BJ�b���S�pTJ���o����7��1H�
� $����}������B�=�
s����[��9�)�rn���@����&�R�A{-�A5c���|��tn}�LF�>:I��f�+�i`��p����<�
W�Z�B�8!�p-|/
�!��a�T��=4��_j��/WT��Gz�	���by{���w���E��T42]���Sg*b�:��;@���=���KT����6i�4����W@��k�%?�.�Z������_�c�y�"|��@P�v
Z���^�A���@�����Z\���&vB�L�<���\�h:�����6_��S�*��	=n�]`V�I�i�u�;���a����&������@a���eiP���CV��l����F�Y{����{w�{�W>�?���a�������@�H���+��*����K
�����wQ�_,�0\�����px,�?���������>��o����I�7��0:���{)�W7����sk��!���?�3���o�/��4����IR���?��eD����1�r���	+��/q<2UmMoj�$5)�Y��7+H>�{��MN�����t���`]�����k�)���t���k&h��t�	���lf�+� ��e([�d�T
S�"�|)�Ft��Wk��"/[��M�9���[�
i��L�������.S��T���U�<U@��m����3R��Q���u�`���3X�����C�����5��h��t�ss#��a=��4�"����Q$�O���i�������0H�mnz��)����S8���[�������p�����0vx��6����ef:�=�zH
�7<p%�����DyE8��;+O&W��4f�?;t=o���^����U6�)�L�5�p�J�3�!�����P!��"b�G�*�f:8�FP)���^��p��03��n��+��8w�OQ��O9�:^�R���u�!5W��sp�����Y%�"o&��-N�Z3���ih�X}t�Z��0�$/��Y����
Mc�����-�O��v�������R�{��nf��|����A(���9�tw��m`�r�ge��� ��$~�UV��{�����SRM~�EX"r����|-D���T�+�V6�B�DWk�s�a�	Y�� 7����hv�a�jkV��]q]/�Z�N��� ����k����z.�ojv���f.W�0�SVOK~�.����G�W:�v#�����IX<���j�[G�X	�Wd��.�_���?N�U������F,k��z���4���m�J�;�3��4mTS�[�~m���D�r�,E���-�I�lr8���,��)����"b�\Q-�!���4�
;=��@����A���|����_ff��[:�9�?��C��c��x���R�An���3n����CN��^i���u+�eg���l2R%�,J\M����j�����y����J��*�T5���v6���8�����A��OYd�%���Mk��[UB��V�1��
1s[�b����X/��j�N��u�b����U�D%��Z�8U^��,-]�~lS���mj���euoS���mj0��]���xR�'LK���I���5��}&���/LS6e��7d������x�u[��p5��R�h".A64h]���_��Ko��:5����n����*f�=j\�V���c{�F����\]�5
43�����SZ��E'p���B�d��(�R��A�*�Y�_)��%/���}s����t��cJOO��H�2��v��&�p��	"�}�����n��w�v{,���xk�+]�|Q��g6��x���:���M���"{���'�x.��Au�<��n�g�vUN��I���T �{N�[c�U�O�v��4����Y�\T�0� s�.�!���*�u�L+����|��I����u�a�1m���:#�����Br+�������J�d�5
q� ��2������=��}�!	���?�/��`-��!(5�A�Z���������H�Y%Ge�pc�m������n��g�X����"�U�=����f���^���<��@�#y/����:�C_�5-����.|�m�8���&��$�SF:�l=,��yt|~s���pxsrq����r�f��
����vE�g�����2?��_����^���(
m����aT�����kVTQM������Y;���s�~<�,��.(��H\�5��}<?��������q���t�g�����<�J��=3��K�uT2�
M������g5A-���'4!�K�O9��OT0~���3�L�{Fv`���8�wd"�xp�3����n8������%��������fr!&��fZ�����s;<��>}�������7T��,R&���Qyj+��d����Q�Yb�
�0[�d�>*��*@��f
�|�O���v���^WU�h�U�6�������mP=ij��0[9������q���((Aa�k�o�Iw�i���q�9�Z{��tAY`��r�%G�����6G�g�i����Y�k��cf(������|��^3��g��j�Y�F-
�aV��S�p��������	�M�����a��+�!%l&�[�l
��ft!�n���GWg��*��o�k�'��_�c���m�"�/5�Y���'�����:�*�Sq5��	]Q`M�[-qb��!�����t0����1���A��@���/h��+�vr$���6�/h�\[=�x�Fe+�LRN{:�Z���v�UeK�n�%oU�����(K9(sz����t��������o����**�h_����o�M�3����{
��������#�������rC���n'������q��;9E!L�@����$*�����XL��N���..?��8�?9�i|���
��V��U��t��������F�{`Yt#��,�1ze��_��-V���*|*�k���\����q�
��~������
�H�1�����[���9F���R�&����=����D��7i|��������0�NWx7Q�n�A8KX����������.K�XEu�}/9��M���i������VJfQ�,��,��[�:����\+2��r|uuxq��x5<�������<��:R������3����'
��`��l90�[8�0"�������xjl���q�0��Ci<	����1��3���vH������������||9�:L0�!����UC���*��c]��E����C���H.V~
qG��o��@��-�s�X�
���O��8���yL, 5$��q�

7>zj�D�W�q�B|�R8�C���q\��h����uH=�<X��
��W�(0�������Tb��u�[p����r����_����I�jY�~�����z)�v���u�1�J/��/2�����g�U��Qo+Cf���-�_zc0�WA�
��*��tI��F�E'yU�M���B(xr~x�v������sv��Q[VQE$�i8W�	��}O�jyd���k��S���\����p�"��.���Cz����8�]����Q{��`��8����'O�0���/��e�V��G�e!�s"���< �qdd �g����qC��~�n1�n\1���ikbu:]������B]�@��)���'��S��-�s�+�		�MV�4���5��1M��R;@��BR�2���(P����(�f�;1��$����2�L�~1��FX�O/�`*N�Z��(��g�<��U\!2�U������5I�� y��:�
��N���uuq�vx��PX~�R��"X������\d� ���|���F���!�Z�G�R���4�T)��=
��0�oD
)=�k���"���Y��;S � 23��G|�=>��M�+^m�@���
q������y%���d�~���0��������N���c��]<E�1e�Cna�t��;��G�8�lSG�)�
bX����y���j~�m!�?#z�%��%����8�9;�
�	�Kf0�t�On	f�3�pL��!=9��Cx��h��.��e"�EP��q9���L0��L�&���	�����G-c�c�n���)Q�d�^��$��N�Hb��/"�V�C��0�7��m��=��X^ihxI��!�;w��
0�p��x3)�ch<={�����
:����_������(����������'�S����f�T�Rn�2J^8nc�s,k\�;��kOl���X^Vm���U9��t��rr�S�QxS)z�\�Zx�PD.~�f�J��Zd3`��tXR
�Fd�nW���9:~7�xzC��(��T�[~}}3��_��5��:B�*|rN�����#'�_���OgKr����0m�m�/��C���/>���]^��|��uI����dD��J�SU�"y�0�1�:����*V���~�J1�/�1�R����(D�5*�a��D�E���1Z*���n��,�QQ�R�p
��J����S�z9�����6��G�<��c$MF�
���B��\\p�,H�+.W]W� B�N�a���P����\�wk���X��b���;	���i�E�
����d����������f���s���R
Nk�qkW2���#/�2��ic�(�P��o��l'�1\�}\�Y�$&��$X��{�_���Z�(�
��������,.���XK~�-#�����$�"E��Ai�s�@�tR6�h|`�HP���s@��B�[�P����;�hp�}���C|���g��?#�����E�,��kNr����PXWn��
��10�����#�d������x������� &f�����������}u�����S����=��K������*!B��a�G��.��n9��?	�GX�l&�w{�[U]���Q��/�XNrf�|
�����Q
���3��58��x���!PY�22#-�#���.m�5��)�5M��m�J�;k����{g�5wtWL:��I�XF��`l��D>�A��Q�CD?�J��yV��b�H)�i���*XJM��"#����c���C��(��Cb����}���,��*Y���9F����� 0����Z
��{��UU;��k�6��[>�j
����LeA�c��3�����j���z��s����W�7#B(WN.�)���zZ���uq���I����6wJ�E��i�q&�x^N�} �*uJ�(���1q9}1��U�u�����'��#J_���v�>g�D\Zz%�j�	��F�M�H�Y��be����0�Kx�)E-SerD����aN����%���Qz�B�'�w�������)0#w<NtS	>��F�T�(������Mu��n�������\���L6��e�L��J�O~�9F�!�U�1C1�,�'!.ZD��=�$WS��,
��������$E����H�K���Dx%������z+���~t�f%O���'����k���ft	���N�9b#�Pj-n�@i�o�t��(���_PE�d!��F��[*�`��	�����N*Q����|�B
�-0��E���j'��
��E�j�}��=��R�oXP����D��f���xV��8>Y����O!��7^�����E*;8���W
[A�gl�����[���De2% Q$�)�\J��m(�j0R��`�Ez,�R_�%g�mX�4Mgx����_�����,I���,�-[2��.�]�Av�4p�zjji%R
r,�j�dLw�����/"�#B`Ww�uO�������s?b���/�U%����)�t�m�e���~��(?\g��>�a�6���3`K$i���*�t��=�������k>��#�J����o������_,,�y�WG����no:��\�Vy^��(K�"���l}i�����S�*w��pE�7mC� �Bz��e�����������l��0P�Q��'F/F�����m��lXc!t�vI���r�3`�=�*����
�}�P�4�
�5��(h�4P�d��X5��%����CiyuQ��x(Ep��[Y�;i�;��l+<E�$����h�Ct����WEK�?�����#P�?�=*/�o~\Ras�7�E��X�KU��W�W������b������87���u�~P�o`���etd��4FX1Wt�Z�A�J}Ee.���'�
��X�Q�a�i�������5)C�3:Xp[��l�����i^���9(�J#I�k�n�A\�Qv���4���(��P*��42E5'=dH+���(7u��(���`D��!�]�@0�G
(���a&�r���v!��a[�n�z	38��B+��6����p7[z�������u9h��(4�!y�F�<Q����3�
�l���N����T�6�t$q���4�p�*t��d�R�JF:
���6��������`�v�s��g"R"/���e�T�5"�������X�yu�Z��	��=��3��8�����K�z����+#��5fd����MC��'�z��,���P���3������8>�����0%�/+>]J��s��;��uO��M����G-8�[�k��O���Y�0"�����$S������|����I����Z�vW��Cj�5*��W������+Z���E�U�J}��k������o��������8�$�L����I���Mk-$��	0*t��Bz����g�7���L?�Ts��~�~��
�5'*�oK��%�H���=z�d���=��X�h��a���������K�0[��������T����6����=�*T�p�AqK��6���"Z��4F��j���&*!	��*�*q���#�3w�U�eJ_�FF/����e�k�QH����h�{�{�{p�>��*��=��q�J5��x�:c�4���>+�P����1i�W���d�6��*�$�"Y��=6��	�	�:��Q:��C��-�u����B�M��c�@��Z�j��3`e6�>col��P�7T@n^
�����~�w!#�U�������B��d�]4>��v����R�e����>���0>�������i��I.�E���P-�.�����F[;\[�~�(b��Qj9�+|����eJ�s��B@_�`K7��T\��$�&�P!�z���F/��kT�fB���0���v"��!V~6������h�����[����S/x��S����L��bM����I���|&���l������E2��)�6!��L%�Z�����y��g3�o�|�h��-���[�]��#5���-�V�p��'[�
��y������y�0�~b8`����3'=��6���_��+jz�^��H�l!�g%���K2;����^�{�>hS�������6jI���(���u�m=�n=���y����=pv��#�sww<��F\)����%f��N��t���.�
1@�>��R��c����!@���
�~�[���W�^=�5.�[]��)� v��x-#�h�pw-�8;Y�a?��K����O�LH�O�~�\�_��@���'*��qU
����/#�X�3�k$�!n��	����?Q�D��	���b��|��s��������fQ�!�ZO�]�%�[����9���V�������V�v�E�����ae�����F��Kx��AK0�*y����8-\��,D.~�����������,jd��]����~�1��W�2��g������&+��e�N�7��M-v�J_q���T��U��^m���eG�x��9-�d
g|�C��qd�X�0��p�T��
|�o�viH^����}�dN���F�>k��|8>;m02����������^���S.v�	F��{�����|����>�(p����Rt�.������u�>sT��
���%��n�����D������,���������
�9�\p%�Z��HJ���=�z����|T9]�D��+&������d���`���_L������ds�$��/�p�6lra�w�-���R�}�z~���M��|�$�W�������)�aM��T�z����[;bw2J����v���l���{��
�}� ���������g[[����"_�M�B��T�=��8�j<�5����G)�����MT�O����01���[V���Ta�yFTEEJ�N�^�I�7����"���v���O�c��{���nV	�V�u�A�E��-b��U�+[&t8�~��Gz�@4%$KA��S$��_�k���,=OL�Ln&�>��`�o��tF4�$Lq:L�vGA(|K�o�Mr���%:��^e�3��C�%��I|��+���4�4x0������`&�NdP���i���sQ��,�4S�/S�o��y���`�-������*k����A\t&�@�hW���[c^��7�e�:�4�$W1����
���<��
#�\�D������c�.s�������^��DJ�f�g�����K�:��tIkPb�[4���-�O/$z;R�<��&h�y��m�/R�](j ��Y����
��d��f��y�s�=X���������=n����U����V��yc$o�����
�/��5�#�v�2&8�osK�I���������'i?���M2E���� �����q9���04k:4��2��K������[����������#�f��EG
6k���8����Lj����&����r����9����u��jx��-��x�DSr��r���@}��%�����d':�R:]������{i�������n/�m����i��P��������9�>�_&��}���v���3Y�����u�=�g���EWf0K����*����������f+<�g������/�ycd�LP8.�7x\^<��y6��[���
��������:��-���'O���������'|jno�x��-_D+��j'G���J�����#j>\����#���m�������m�,���U���D�B���c��������m�p�;��2�2���b{IKU�l��}a
{�M�{[�=A�������,.�sj��)��e�H������C��R���Tx�@�
5�38t�w^<��;`�h����6������	�t��eL��h<�����������A���#i�0���t��&�p1g�|�7BS�n�J�n�0$NL~��.��+�ORcO�0��i����m:2N&FKO(�Z+>��,���^J���,��J�4�<����[roA��P|n��|�uGG|���O@����H��I09�{OFY���������K:��0��)��4����x�2kc�4r�8\���z��lc���^r�O-g���"����*����Rtx$ess��8�1�E�x�O�*r��V�C%���������	��Gb���.���-a�~I�(��f�pl�����i��jg����g�n\���z	~��+��l
Z��m�ziXTD\��<r�l��e ��o>G��3f��
�A�s�kJ�O8+�����u���J��W$ ����\��J���Y#O��d���mAW�[K�ph�!!"��2����g��9Y0����r�E���<�T&M�^i��wIb���7��k��ro�C�2���d�RO��0�('��c[B�x`�`F�vF�	@�V�x��	�����bsg����,.4��i��#���6�G}���8[Ut(��S���b�T������C(�v�����06PF�{O.�#�7rX����.H���3�f��b���O}�g���(�����Vj�97���-�F��u�F�7oeX�{vO������}>�����[���-������������F�0���	G]fTR�{!��5(��_E�������TnEb�.=$�z����zi:2��XSM�%L�'�I&��������[���:����n4�����t**7!R*A�lF�	�(
u�����G�L
Tji���s��S2��j�(.(cWPX~�P��(N �|����O;V�`�,{|D�^@��������8,������K�����?_���]�`�������V,�
��[n�G�����������H�h+��S����a/R7U(�]E&��<l���N0��B2H���@?�+&
=^��3�$Z��l�]:T;*U���q$R�2`]1`�����.���_�>6�/��ou�����S�;]�/R��9�<nn�x��Z���Z��������f���W��O7��f����j�#su����E���4��o���+'��%8�.�:[g�f���C*|����tA����B���k��Y�#�����Tw��![�N`?��f�vB���g����i��zc��5� �Q_-$���%gD�	��"'���� C������=T�2�bI�z��������Z������[��;�6}��� �x�>y���dF�*�h��x0ILg��e��XJ%W\�Y�[P�'�'i3�t�+����En���~��:��X�����:k�zG��'hb�s�,aU0�J%�{��s:�F���\�^��6d�O�2�G����	�c����+�v���?�@�'�n��*�2�s�h��
��u�C�&�Y��u2g��P���#,��K���n�[�T�ACA�<��x��D��c6������%�dq���a�wR.>���R����a��W�!���.�05�K���~�hH/F���G�KlG�X1<��lI����U��+�+�����*�g+{�F���zp������\�����oZ�\��9��}FiS-
����B�g=�_������t����U������{�V���L�<�'}y���*@����`�3(���|�N�|6��S�������3��B�������
M��g����X6��gD�/i���1���/�f5I��,>nf��L$�~y+�*6g����0OmF��F�9��[����)V������S����H�R?8M�gc���~�.^e�>R8iH�6�� |�
�av�m�~�8�8[�2�<�gY�����U]a����pu����)�!��G���P16��:��Q� .�������+E�5R3)}�S-�5"�!��#%sF��"Td5�����H�l����25_Z���s�
2�3�5x�$����<)���m0L�}m6��t��������Y?i�,r���3����'g��Z�g h����LD6J���)��&��!��S�]N�+����?�2a��d��[~��>��1V�
�zm�`��3��EA�3�m�K|A ��6�j�2�I#�.��/��j��L�[�8]�����F�<�����nAr�;��4�v{�����{�_~n�f3������9a��c���f���W�8����M<���FG�NC��c��AQ�,�5l���\��A��%3���4U�����z
}q�(�}�
���q���/!D�?�1V=�9�hx�k�[��|���1��P����^?�aMD��Z�p����5#����p�����L���4�q`�Uqi4s���)r����\�"�����)*�PN�)��yEg�]r���R�����PBK��7�C��B4�KM����J��m�9Ca_ ���6��(��=J�������D�������=(���p���U�~��e���f�5����Bn�����+�%y��&����~)�����F��t+F����mT?y�����A���}&aaub�E�P�q�MZ��L�K���Q���S��K�(Unp��f�x`��j[�[����j�w����h+��#�p[��;�v�D�Od� J��1a����)/X����)y���i����?�xF�5V�E�5���:ZDhmD����@�N,���-��L�Cb�����A�3�GK�H�N(���f�#�r�����4$u���f�A�@2�T��V��1z�(4�lfN�q?������V���O
�_ff���"�x+�{>�(���9E��Z��x����~�-���w��f;
[�WJK�Q=�d�� ��@H�g��U�J��K��I��������k1i��/D��T'��^Q���Oi���le.�e�qf)g
�E�O&|�<bsH�s33u��� e_��|G��A&u��$~m���������7��'�U�JI����F
��_�`s��pW��~����
�I�$�\����6�}����~a8��R]u�b�����Q�g5&�N���/�J�n��d�n�-"��zi������M��ZI�q#,��������9yU�~[&]�2-����=B�/4���)���o�H>3k��K�\��N�#�Y��1��/������7/��*�n����kc��F,0�X{��d���\��[P�Sv66����D9A�'���^x{������b�k�|s��9cws�A�C.��e����/m�T�����,l�!�1���1{��7fw^��|�*����w6�<���L���,�/#�n�����#N?b�����zq*�N�(�P��f�z��j;�H�]��k
�gv��k�����zb>o���?����n`��2l�/x�vv�0l�A�5-t�X{��9���.,��3:�������(y����A����lJ[Z`k�����j��k��L��W��>�{J��i~1}X�`=UN��`=��������^�������o%�;�;��{����n/X��1���'���;�R��������:�����������%��.�����y.��sJj��r���IMs���D�*�Q$��X����W�4�����R}��s�8�-���?�����rs�^:A��y>�n�:��o���qnYzp0o<�7�e��u.[�����m]�,h��m?�1�l��%\|�j���/-G >��V�����<���O��]���-���;~]#��q�
���hc�����}Yj1S;=��\b�2!�=���y~!W��xz9�]����������'������^����,9����9y�b��b�*w��O4Ii0�9�JR���0�	s�{�~l��o�
IAp��X����U�9���rS)�$|^�6L�Q��[#.'Fe:�S�sL+�����&��g/���"s�:��T��9���i<L�;����C3p��N��gl'��a����O��p��Yb�Uc��/Q0igj����]�F�r
��[��@�;���H�T�z�8���fLnwc���}����xg
2F�c�B��� �Pv��z�Z��bqF�d���jy���sJq[���������bM9]��2��3>lri��B6�"1\hM���������{��xi���B����u
��p"���!vqQN^s�e�5������X�/6w`>���uv�
�w�i�p����kR[p��%�b*'�f���QT�?�o>�}�>��������Q���Y���W{��!�*_7��v��N�"�A]�%��=�_^����)vu�c'hW6g�}�� "Q�=�����[z�M������-s�=l��ol�o'O�����7��B:��o�(@W�.�&���R���hj�\]L:�@�"�=���,��;�O����]��2���|6X{=�_��8B�.�'�#&���A��:��R�`�OP����~f6�r�\+H��u1�hH�{�WXl�X������\;N5�8���`~�I�������~����Z���M����Ia�]��b�E�j���N��S�7���Z"T��U+���?�������-��U��O��*�}�����l��E���(�~������/���z��x���y�N�^\h���\D���d���gF�O������:���61��Y���U��_l�' �\}�Th�������Y$*�_��k�:�����{R�rK��z�:�����>v�G��4%'OF&�h�z������s�K"sn��R��-�-1��BD���?_E'o�w��}r�5f9@��?�U^�;h�~8�M86�5W���{ki��DK����{�E��_�G7�������-;tM�c�I ��?���������^+Z����5	G����	��W:��g�"�Ak��lN���]7"Y��Q=�Tf�}:�]�3���hv����.�9�
d�>��|t:��Ey<u�0��6�u�����j���0�n��c�J�6o;��o���.)������F�^�R4��,��4St���	�*MM�E�d�O�
��]�
�R��g��&
!6�^���T3��LV!������xr<@�����.���y��0���(�<���ua�IPM�j���L�<���#������������;�����'����������$w�Z���hu��E�VF��}������E�.�QQN.Yl{%�w���g���=8��[?�~��/����~����w/�N�q�|��|���c���U=h���W3��^�f���^v����&���j��2�W:���p��6����*~�~	�b�P~ ��zv�t�v]���U�h���b+?���./��w�~?I��Ar��Wx��yy�J���Y��F	����i(`��p�F��"�����G.��,Wzbe�e��*�<����k`*�0�w?�k�������u��@���@�[8�^�~��cH�52�g?SL�>o�9P��b�����g������
Mf$c�G������J2I����<Za�
�d6B;+��e�g�Y��.��dt���oXtam?I��/�w�����%��z�dk���o(��%o3]�"���.i��]�SDy��'�"&3�$��R#.�\4e)I�}�AT��2�����Z���e�|���"3�5���x3��A0S�'�05��R�,lS�0[asF����[JH������ 9�(K;��^���v��T����t��M4�s"�0����������1l�I��m:�q�F.F�6z�h���c�;Y����?�w��>!��1bn���8���mR/���F:?�R����
��g�����4���ei�m�t1"+�YSc�lXQ���[�CN�[���^�|�_����\���������no�{��+\$?��f���������*���zL�yW�T|��ZO�;�J�m�x~�������m�sZ�>����4���������������!uf45��)/�[��������GT�+�����.<������x:����Y��#H5�0�}�+���9^�b������!f]�����������;�y�~+:%zt~3M���>�g���5�e6��I#6J8��Af����9���1,o�(��8��?sn_2��G}�s��4@rK���<��@���rbsbKzj���F�K� 9I��������x�Q��o�0����ev���%p���d >L'|�1=c4�I����9-���)4�t��6��hv	e����ll����$G�!u����@&�'��1���*?9-8��(�#��
%��txC�n@Z��4p�$��~F�{i� ��������{D�D�W����������!6By��xi�C�-�	���4�IqT���WK� �B��]'��c?�z��
�^���i��/���j�DD�����4�1|<�>	�XK�X�yb�$�T�9g)��C�	V�N�:
�r#
��.�Q��\����Y�A$s�%��
�I�]�Tp��M��]��=MT���i�D+Z��V|Y���{pp�w�>S���i������B���U�
�FK��z=�O]���"]�Dx���UI���r���=3��)hT!��wi���X��0���^�p{�$�_{~�P�8l�P} F�	�k��H0'Bx�f�~��T��c�?�������8�H�g���:WZ����,���m|lI�������h,���;q4]nI�F��G�����a�k�T�Vdu��Nh����;+Q��lg���h����>�V~�Yq�/Q{���>"� MX7�^u�=�?:<�K����]�M�p��/�5TZ��3��.�h����Fh��T��Yv]��.�����@��W�Ec���q�c��{����W~���ZiE��f�m Oj�s����8�,CH^�������=��,�Bp� ����
 ��ZQ�J�� L��c�E������Z$��)��UR�dQ�����b��%��D��s8�����a��3�J�?�Z�my��b���
�b�4HgdYC��NJ�s��.7R>p���9��^9����(�i/e:������eT%�43�K���j�2
@k�V����������`�.��c���p� �������xb���Y������J f� �T�?lw6�8T�&B��3�0f���@O���^��
�F����DpY�"�K����H��7��Q!hI���� @n]&*Q����@!��;�7����e��q��tk�
�>9�k���/�D&-�z�ZZp@�P�	���_���1o
.�����k��\q�7�&�*�2�dnHe�)���
��Ff����P#��T')��4��P%�
��%^��Wy�@��I�Ul�@�R[w��q�p�?���>��K{G��������|�?������1@S�x���o9
��dM��E�w��o��Ic����/,������pv��L��^�|�����M9��h��V����K���B��eza�5H	&z���m�Rh���c' �wg��K�&,����9J�%��>�9�@�����"���N5��J��98=��D����4&/+�����H)�#t/�'�~�>�G����v�����>J��zs��Q���������>ySr���b	C����B�RZ5(��Xp#Z<XFX��b���u�����Y����"�x]����_(F���R���B�'CwF��%5V�
�~"~_8����U���q]����7���zw��������N����h4����n19�x�	,T@�a���9(�`8�/@�����x��\&_�	0� ~7���e9������j��#^Kf1��>i=��zWcx��T=lp�rj��n
��U��WV%�� 
�t.c�w��p��'����u:E"�����ddFt�u�K��E}�m��}HHN�����t*|��k]���6��`6"#�^SW�
���r�j���5��1(���h��>�Q��qG+��t�J���Q�z��@���o>��2=�g8��������4B����N�DVz��F v����1�����������R A�Z<1e��PU���Q�P�8\������E.�i�������_�B�W�i��v�������<���3��� ����w�������v��NN>�������=u��mx��w���L�Q��d��o�T�D��Z�_��n������X�O:gm>�h��C�r�^���������ClE_�'D�8�^�����99��=�"�^<=�y�<��3��Q+�us�����o���Q|��o4�6W��Yt�^�g&�����
��C�[�G7������ ����I�2uV���|��@�"��<�������v��������*��t/G���t�t������~�?:�����J�#�H��0p�+8��h��-l������+�6�e�+3����zqXVN1�����z�a����C�|�]5Wj[�{��f]���]C�+�/A}���-���:�cwO�*8�������q:4�_��_����{A����(��F�u�������L7[�8|1�
+���m�������b��A��T��S>��J	e\��)6����M����n
sv
8H��fcaJ�J�7�;�U� r�~2�< p
���X��i���F�-q�T�K����C�$*(!��5Usxe�9I�f�]����|id���k�����c�}{��������=9��K�}����	�����*�����Ir�x$Ip���|"�t����f�G�O�[��G�g	�+u�������0���_#R[�{I�G�Oh��]f��*,%��c�r����x��p��7������V���*�p���=0��"���#���=Lo�����S��o�2���~J�v������+G���(/Ox���MF���	����l��b?������&�w����h�w�e�|�������g�2���7��#8�b#%(����[��A0's�g�Qw�uI���m�(4�����+c( �c4*��GW��>����sm�]�:���Qt��g�������/����~���{�:�(���eVw�f_��wy����>�5����������z,:�S��$��v��n����1�$������|m��7�)$s�Nb.���0�s��N��CU(�r�$\e����g5I�1��{/oCE�p�pQ�h3Y���
(�%h����> ��*�O���
�S�6w�����������x��B���*��>4T�e�v�=�]�J(%�c���P+�����.*'=��.[�$�
>8DT�w�fpy~k' �\<HP�>v�����)x�]+�����$l:.Z�1���rh^��p7-q���'�?F�
��hu5e�.�'4���������7U����=�2
�7A!I���u�\^����t�2?�T}n���f�h��(ya����#����������]����=4�U
�nl���j/����p�� ��Cd�1�S������[�+v;M��q�$�$��|f�ph*��0.�<am��@��Y�n��2FI�����!��4�:^��tO��6�w�M&�q����c���A������#T���>;DQ�wi�:�.��'�1
�$X������T�	���J�-��1��WU��eJ��J���?n���-��,�����'�r�Ux����%��k�B���	���p^��RI�.���s#��Y�U������������'MU�uZ+�?�5��14u��� �S$�Nb/H�����n?v[������|��{���=���!��
>�����������9i��1(1@�r^�����lb�H����Vk"|��i��b��1��_���p?��B>���%r��V�
	�����$QDo���};VN��$m�f����#����=&q�;d�s}����B2�����������x(�
Ia1r=��Bf��|��$Q�����G����='^lV��;�������_V��-�4� `�<'����JL4�o�(~RE��&4�C=���.�fd���<nM��}R�<�����8&����K�2�&��E�A  ��huj���J������K#����V�3w�C7��C��]B���>V��<"�A�g�d�$������G�5dk)�R��[��`�M�y�a`��d*'��A3&	�����D��aFV4@a({_��6�> f�E
*����R#�j�yd�fiO��y_���H���k'^�[�
�����1CuQ���/<���vZc���0�9�o!�?��`��"���.:����)�\]'Ho#[q�5�rn���U��u���k�T  3*(�_$�i7i3������-��s�j��F6�t��3�w{�����u��[	���l�/�<�Fe=��1��U������
����9����9��:A��L��IsT/�NQ[���bi	;��v�Om�+��o��.l�WQ 3���_��w���2�2�y�����S1�a�Pw���B%��������Y�����d��rp������w���R��}�W����i*��=�=����
��_��R�y�'�8�[��������IyN����$����b�]���2�������*�c+��N[�M�������Q'�O:�=kw���������tv�{�j����/��RIM{��,���{�}���Y~�����wL�\\9
O�����o�5'�6��\�X�H��M�����Y	�6���c��-�{�INu�o;��n#r\V�\�@@0���P)6l����_��6/
6���0"a�')�<z)�#!���w�6��MM=E �����{ii�?�5�|��]��4<��3��;�|y\�j��I�����a���Wa9R`�9�E�#�M�54������Y��M,]�E#��\lDP-rY�l�,l��	��#J��v4QKp�=�"�f3J1�L[��;��(�����<��mz��TS�����:p6��Z���"��.�,����%��2024��J��YU`d�����^+�:���k��A�2�t����k%�
��2TX�j���B���J��"����+�&w�y���H����;��ol��L��	���F���Y0�(���s�V�m��x����nA��K�����v�:��InC�T�e4��
{r�����gDi�]�D��O������tg��D���Dgd����{a����[�2}�o��/v��Kyo�����lk\�B��I���bc;w����_�v��6mg�s�
��W��-Bv�^ �h
)�j���nz��X���5` wQ��S�_/j�]fw��������t��,�����w�	y�4�A�q�N����'_�+���n��
�/Y��k���Ia���!������D�L|���w��K�����������q�.��K���I�m����������OxOmO��������CE�l��35���
�y��3�MM���E9���Z�FC�mY�����X��V&Wu��Kc�����|V~A+z���m����5��N/��t&����+�b2<���yV�U�����k�iX@�
�����>�Y	�`�q^��F���.����<0����� >h���U� \��f+Z�}%U�ty��j�\���
�"��|'�qN�2����"�������Y=��iv�xj(��<�Yc^��;wZ���E5]z���"���%Agq#�u>v����0u�{�����!�uk
9A>�V�l���$tt���P��`�y�Cn�$�)<��/8�Q�z����T�/=v�kN"Y�(�5J��I{��8�%�_�7�EO��1����C����9�6
j7�#c6����"�+*:��3R�d��r�3"����<����:@N��+�/rl���]~kA>[#���x���?R���E�������`�_I=�J)��f1��(��7���,�n�j�E(BR9U��Z�S�q�E�������	��J>a��W�
�}��`����Q��+�)T�)���`0[�E��r�����6NY���hWr����pG���V��<����o	�0��Q�V3�
(oX��|��
e���������?����VD�;?hZqjpA��������R���av�����{�� H�_Y_�2P����7�	T,bd�����|�J�5���s``V��k��-�����	�c���7����h�r�2�F�#o�����9iu+����)\N��hl�I�m�@X�=�	<K�T���bB��;��?��u����+;!}��*��5������A)�^���uy�mn�8���k�D�?��'[�A*G�}������b*�<�x5���3���P}��[��$�}
�)��z���'9o��Ju�KNd���^2��"�<�
e����sh(�91�N�NzK	�9��/��_5�D`RMm��Dlq���B�����5/n�YM�i[�4;�6!p}#�@���N��?FO���?���	�
�2��8�J��������3Z�������@�Q:��i�^�>��$+���������gS1W)�����R��l3���*�JZrF��$3�����f�\9����:�+Y�k�I��c���F�$��7�a+�����i:�N�aT��5��e^Doo� f6@�H}K+����2��U�,k�C����������������'��p�rJZ�Yi~��)&v,3z3���n�h��������pre�[�L�^�/{�vx��N���(��6���*����s&�QS���tw�}�AG��d�����%�xU6�T����'��_��}����t��s�����8��-%!�5�m�{������?u���
����5�B���1S��u8.<��7UI9)��~�Q��Z�3 ����DK7�b�!(H��d�;�\q[�k��c��w/��:R�W��1 l���2f�C�J�u��M��X�9
\O[��G4��e'i���aI5��=��8�J���R>�,��Y�o	"M��9{m�����W�K��/�~&p�]��� �
^H������0��m{����	�`���Yv�)�F7�;
mr�Oh7��HCZ�u������Q���;�^�������2B������������&+3��W_Uo[�!�_���n�e?�����������2��OH_,yA}����k|[���8�7P���zs�*��z���edCq�h�EZ���U�3�������hRm���6��U�2���`\�X��������d�m��
�&�/Yf��������H�<�X�L}�L�(��u���|_������"�A�	jw[���-bv/��Ik����R���kG@����2��H���q
+O>����p�R��jH��mQ�w�!��v���P}��6����� ���eh���?sQ2���%��..�����$g�� /#>E��g��^$��3��g�K9V$��
#]i���u9����3�7�9$.��MsNvY$D6�X0������(��s=�,kh�����|:�������v�1�#!����O~����G������M�h�W�H2[����1}1U�$�;�>�Uo���A�G��_g���#��C����I���c
5���^�)���m��`K��:V�������*���� yj��'�9��{�i���|t��� ��m:�tc�y-f�%u3W|������L��{�P�1�n��pL�S��M�+��O�qV��=�F���b�Ze��$'�DNN��j�Q��df2SJ6RX"I������f�*��UZ�5��������,��2w�m����&r���[�5�Q�9<+��+o�ZY�K��~���t�L��"����� �iM�b�T�����c!-A:�Si8O��]@���%3e��}v����=a�Jl;��2�(�C��,��%J�M�3�4����#�7��m2�k�]1���M�0'�?��2�>����-L��A`����u������'���1��o���Uq�/E���+4�(��e-� ����C+�(��2�+��>
�s��'������i��8VV��`-�p��2��C�I��IjH�����$@y3�:~^BaSO����<BNE��dS�6|��������ED�P���9�#�($D�LP��;
cG@�k�8v�p�����k������`�M6C������q|'��t���j�N�N���U*_GjT�=����S����bC[�Kf#+���c��E��r��8�R&�,~���C����L|B�����u3v�{*�-(������������k��lT|'W���Bh<H�!��\�d� ��I��\�/q
'�S�8���vG7��}��'PZ���}����K����[T�:��#���k�+����p���I�g����!&����K6#����!���(���;�������	}�-
�(&Z�#"�&^T�Rr���9+\F]���J�� ���	�6��b$�-�ri����p<wm�_r	�L�Za�QC�i-X;������d%_?`�G��b�l��U�8��h����l}�"z���~����j����>Q�0�m$+G	vb���p|�Xbm�d�Q!�\s^@�4J���Cs�����k�"���
|`�N��(01��$�/8��������S!��w���^��Yw����a�/6T|k0J�(T�7��A��Q��7���`U�gr����`'��f�����`z�r�>)-��W��ng�
����H�Q�puu���\G�����X�������P��I���!��B�~%d��y����B5.���Ibd�0��|�V�����6�tb�OK���v���v1��z�jB��D2[���C��J�B(��N�	�0P����0�+4�+lo��)~�OOD/�-WL�������l �`�
��C��78I���S}4���6u���_q]9
��@���>�����`%��0����!���0�
�?�,2x�I�X�����\�46�~���� �bb�_�������L������������%#z��Ll���C�4�����1�e!b����,�N��8E��4(��M
C��F���<O�
y_���XC^
��$�5�\�g�G�?	
����)J��	U`�D�l���s;�}9��O�q�EgK2�����W�cv�
y%�
~�:�GOt�_rg��YZ`��8�u4�r��|8��c8H��H���L��-���<7}U��h(�������	"�)��]���R4��=j��d�
{q;���������m��Y��KVNS�������U�`�mbkUh�{_���3Y�
3�(�S�h�l`?��o�3��tN�[S�-�������&!�z��F���vS%I�1?�7T���\4@��d����t�3t	�)��C�����4���8���
2bB��#��T��������)��eIQ�>�%{�KG�����$W�0�)���R�RN.��'�f~t�4Nd�(�!D�K�r������o��lJ#f>PsK�x�u�/Bz���c�v��O!�n-F��+�e����*��3���[[���:h]!�����.��c�M)2�k�����4�r<����]z�8�4�>�#��Yv�����F���2+,I��
�Ptz��"��n���g�@���D�B�������'KuI�F��p�%o�C��[
��5��"1�hKAS��P�E-��xD���A��5��� PY��0HGiN%y���lk�ySn�=������]�YY�������Q�Q���?�r�:�K>L��o>B��dI��7P"���w}_)/=���u&�]����;�m����Ja���@��"u�Xt�]���z�H�K�3.f�@r,+�[/�Oq��.�z�u�~,�����
^2B�q7�����:s��	�����L��\�r���U1��~�k^����p����8'�tT6���0K��y�E���}pL���g���v�������� {<5]'���z����w'A�U�]���=S���U9���m�������9��=��@�	@�����A��|D�kV]��_��>p�]��')<_�F[�<��T�������[�j[�&��]��,��N���a�����0���prE&m'���Q|�
����_p��K��nu�H�FRvq�������gV������m�!J��P�\��>w�N��������^
LS�u�!���D��a|S�2�#�� ������SG4wKR�m��
����VT�
��N�d��o	$���WUqA���,j�K~=�r}m��#�n�������IF7&`C��Xo��n�G��%	��1Wg���e��@e�3��*v{Y���	=������Q1����[h�Mye9Q��rqA�`��,L��4��#2.���	�h��z�������l���!r���?�����-7(":�4�L���N&+�q��k�W�3[��l_�&j��Q3���\E�Rt	����~��n��1xG>'J�@#"h���8q������g�L����G��xU�Bm�>Q���7k,>����)W��������&�A��u��7[����cf����\����ZL:p\k9�d|��S-$�E][4�%��e��J���MTH��l4�F����bz6��ga�-}�������2|e3�o��A��Fx.��$E�
���.���	�p��e��GD����RVL�	O�%�������FDKS$d�s)�kaY5U��[D+k��(�k��4��TW�}$���p%=����a��4�.v���<fK@��Xx�y1�>E3��+A��S����.��;���ww���{r�c�@>����M3��+iz�6�kP��"���t�,���2��7���o15����1#�X��\f�������E�:i^���B�!a��~u����l�-6���z�m1������.}w�������5�BJ������B�{��\���x$��L�#81�D���$x����e�w����S�=�]��*�����n��K�����YF�RVxS��?��=�xlFt�c�3��8��<�����4n���a�FR��A�9��:�|����XX���2���������c�K������)=v�ruQ����p���X�%g�0��M���C����S�&C*������j��~J�Z\�.��`�sA��,n�Z���	 ����r�VC��\8�-�_u��pUHTk�T�(�z���M������p�Y4jLnV�8wT2�����EmY0���Z�@��A�I
��9�[��%�I�~��rK2y�q��Y�NSB�q������D�q���d^V�T�����x��s����B�
u��"_W��j��kE��p�IaYy��2�O�E����������A�S����(�]-U�h(!'N��<EA��E��l�R����BH���
f����@�_D^�W��~N��77����}��U���S�3�1�a?�I,����yB��b�������R���O��%�St�T������-
�h��R�7��$��1q A�m���v�|�pu!������Q(�B����T+�E6�`	��
Gt6%��l��_�U�$���:q1�qPy="kR����.�	��zr<�[%C��(�R��U�3���<�q��'�p�K�=�������7}�F��3�b��i�)�Z�cYS���CwF2�6��`�&L�NA��}��`�"�b-�9P�(�����uRf�L7V?H��)1����}��_���f����Zh����+����o�
�'~���m��s��nB���R��]���\�c&hm6^��.d\�zDZ�~���*/,�����
���_�t�� `3��)���:�S�d�v��l4���%�+�?�iM��P�,+��{��j�1�a������F(�Q�e��(��i	-���P5�X0���t������T�:TV���F;���tU|�%�bX(������;y���+U���v��
��7P��h�������F�KF�&8W �6�DZ!�p��I����U�����_��50!H��/xJK�nWw8�:
���mM���nF������^��=4K�Ob����%	E�!�peT�	T]��sy��O�@�	���(W���������
+��'�,7fLc@�+>Q�	�x�#Pe �6���y�-�9��i��V������x��uE�\���r���Wu������S����[�����Zx���sE������������3.(
�3S�-������d�L�a��k��4�}mtb���e��7����O�UK�h1�<��3��#�Qn�F�����
�~�R����"��S�.��,�3>a��`iG�%N�E"��N�v�>�z�[��S���~T')<���bE��t��/��j�����e����'��g�����BI����|3�t,�g����0E� %������LM���D�������w�O�4�5W�;z��9�<��T�|*�v�U�	h~A�hQ:|u�C�(r������z^�z�4o�u�V��,f�8��&7�NA�e[U�>w�r��.� %nc�����]�4\y�����$:
}g��� B����!�E|1o������S%YmVP3N!�M��Y����7�����sf�qa�dJU-}�Y����� v+�8�N!����H<���[�+�)���@��W��e>��i��1v�*�$V�k�X��6�r�W�x�|�����lo���*���ca&u��B�{�Y��],=���`3(\��b�}3Z�-�������W���������B����3�G��LN���bs�f
���gjH����FD���.��%�})�y�D�%,/]uH�%e�^��uz�FH�����Tz��!�i
Q��?����Qcr��E[���j��� �~�'�u���{d�?9����\���;VV������N"�x(Ija��D���3�%�f�!�k�q��L0��]��-1��	��Zdj��T.�/a�s�������Y\"���q(�`���T���gp��x��7�SUe2���bT�4Z�� 
&Y=�l
����>�P~�s["k�~FZ\��D^�_'�c��'Kt��V)�7����������]���F�r�F���A[�G!��Ise���n�|!Q,}P��D�CQ+s�X%dA���l�y���o����k��u|�C���%�����X��m���]��\BY���{#��6U���}u���!V�)��r�X�����
O������k�����o�
�����<���b�Y�7�B *�������������@�j�e�c�Eo���G��I�����t��T�^������)���F��Y^�g�7���y�3���=2�6�[��Y��h=J�?d�-8r_�\F�7��<�v:�@D�B�����������W\�T�y��<	��K�t�R@��8�
B_�uE�=��:���L4�n���0�q�b;�S��f��9&��@!�O��t��}9EO�y��a�!we�����f4����l�������>�uy=v+w} ^�|�3��!/����~����~~P9Z�JP����W��e���(���$��t���$����mJ���r��D���d��B�'@����^1y0H.kp�9e��cGO�O�U��3@9���*��Ob=�|*�=��'�����������7���w�=d~����I�??tN;gm�y�����qK���'�c�%a��������.,�����$�0����� ��L3M$�T��l�+�'���<��4�+�^h���hqm���YK��y_2�:��'�dE7�M����^������������W�K|e��u�	V��G�}���d��P��h��zX��Q=*9M���W������hN	TF9!�O�3LF�K���q�Y�^wX��9���5}����U����W,E~��TfR�i3��B�[B�����Z��M�|]��Ph]+M�f���{�a�
S=d�-C{��b�2]���
f���)u�g<�����l����(0
��ow?����9h���	���a�(�d�H$�*7a�����L"'��>j�z6�)����^�'�s1"�i��uV�>���X�c:Q�3r�3aX� ���%��$u�9��9�uL ����0w�����������p�~���D����f��6����W�,@��B�RM�v�Z��6A��F��f)*���p��Tfa��%��I���	��.D����0��L��c���L�<�o�������E�$>�q��U�zU���A^�GXB������_��?�X�A�<���Q:���$��
\�{F�����bii2����s���t/���)��W^�_�cC
�%yq��"���bZ�5���l�����K��Y�6��"�V��1�f6eW&����y�h�`��W"��#l��C�X�'���&)���M���)����VE��Y1��j�I�^/
�|����Z��:��C#�:��}����
y[N�.���<Q+-Ly�Q������s��2��5|9���<8B[���j*{K�������P���T�JiP:��=��w�wOv����']��m������ �h�f�{<�����03���vA�4��d/��e��f#����3�
�2���5����)!rw��cF�qq-�����s{�O��?��hs],8ww+�
�L��vM_d.3��z�T�d��|Y��L6���Fz�y�Xg��-����|\g�+,�Z-��{�%����E����tuXe�B���8�����?0�)\VL���w,��j-4t��\�jME�V�/�Wp���"Gx!��T_�4��4������]�����������@�p�=l�r��B��z@=U$���p��N�f����D�>/}:�\q_����<�~�$sW*�g6q9��GP�!3��1��;���$����k�Ub���}����w5�oV�R�O��g�H3�kPHNE���C���D�����>�����+����2�?S-=U����#q�Jm�;|O��}I.�O}��fZ1k����r���@(��lV���!W@��[�,#[.��#eqin�+���9KZSs�*<�w~	���!������f���8�\3�fq�|*�u����%y��G���;,_Pt�>�Q��6UF���\�|8�)����PQKJr���E�r��rpX����j��J�$l�E����}�����2S_K�r��kF�������
1��f�_�����'U�y��3�Cv_I�4�r�k^-K��RE���SAn~�g9{�m������Kf�l`��+`�)����}c��}�r�Wz�,%�.���k��kO�;����IZY.����9M�D#�r��SBp�R���������������o�;�AO?%��to��5�iO�T���p�w��`�Cm<�i3�F��_�M/P��9$������J^Bc4G�)#��H;�B����
��2��cF��s9�J��\�����OP{?�v��7dDy<��%��'��s����\�3M�j��t��
�L��:Zp����"��N:xg������X��]�_���-jE��������X�*-&X".s!�q����9$t/~L��Ul��'�B,W���h�Qq{2{���`�
�=0V��>������V3�`�p[��_[C���a�O��������R�WR����+�QQ
�7�,@xW���Ie!W K��u�W�� �N��,��YC�����"�����wa�vt��
��w��-W�uQCa�m��p�j�Ke��ml���"qp��3$p�}�d����
���(1z�%�I����t�a2�P�]�K
��=�@�.�b*I���:3)#�]y�QN&��j9I������;��*����2���������=��	�����H"%d&"�i�]X��b���&FdQ��P�� 1W���H��2���2P<i��|&��2_��f��m^5�������[
�����6�;�
1���"����0��+4����%.��+K��z2�zG��-�����0��'������,[�L#������� pr�*{S��=��,`�F�����M��/Fo0{��z`&��]��7�q0�%�s+<��J�NzWc�����.��V�W�~`]-��w��=\�#s����������hD������s�j��{���g^��s�iO�0CL��j*L�49��C���J+
qR�Rki*�x{�8p�Z��;2��^��V�X��J";��g�e�F��hWwN<��"�T�hDT���	^�l��T������oA%�����zr��rq��������tG��~���[���.n�@�BM\9��Jb�,���Whkt����J�.���c�
?����5j\�x��������N���F���d��v�<�ePJ.���	(	2�c��-!�}��qB�����1�9~
L�	�A��0�gU�Hi-7��x	�M>S�	�H���1���0T.��Lw��������B^F�M����/l1�������9B'��B�BM����c9��(���Cs���������A�����Xb�L7��~)���x���Z����i�����R��?���*Lc��Qh=G���U9rZI��������`h�:	~�V������REx9@�K2r:&�b;��;}��2-��K�j�V��3=BE���L���bi����w���H(���7�meJ���Kj%��X�$�����+Ys��e�s$�;U �i��P��:�t���"���!���E��i!K�$ �T)���n�uLI��f�dU"�.m(���G�)�����{��H�Qn��'��9Rb��+2�'V�]	Q���P����v�_���EK&(�9E�=v�q���u�����5�'r?�3�EG6����,%�d����������f"���Q�I�����7uv=BYG�r]�i|y���|���������������KF�jky��]��������aw���)����o��(���|���}�U:������i�b��I93��3d�#������Q���=�D����S�f�a����%�|����G�&�p����������V�L3������M�7�R�l����B�H�@o�U��NF
��������j4�g��3�5��uP���X�7���>�������#�������(����6o��gN�H�9�p||trf�)M�Q��Q�����wD���+#ha�@ ���!���(n����8��X=�N�������o�������M����k�_k������ZZ�NZ��|�SR�����E�F'�x�q
)��rz3����
a��v�j��@SlLiY�8���r��mh����#J�*�d�%D/���q4�8��I�0�-a��NH"G�b�;{� ���B�{'�^����Y�Z�7��$t�]�]$�Ro� ���!�����X3�9�4Z,���Z�o4��D���X���8��"P�b�~��^7��vC&��XSvR���5<��yF~]l��@����:��\��p��C�/-a?������<L�h6��������������$��8�,�i�,Wt�Mu�dHO���p:��Z������b�^#��b��a'����������uG�}� �����+|E������\��g���d��o~�Y6���Mzf���(��,~~L�b�/6�D�%���%�#3?+��jM
~jL}�m���u������$�88���v���������VT��z.�t��P��L�����������=��w��8d��=�w�h�a�v���s�l��l~�����,(�N\
����L//�Q~�������h�d�	����bg���c�}��Xg$��z��c�QLr�f��4@���c 3��H�idRG)��U�T�x�=6���
���
Y&+����x��!Q�~+�!��`Wx��e����F����L�T��r�]���O�V����0�&<TO�5��"�)��
��7T�k�r������N������F�p��(cZ�F#��&B�3p6�����h�e�{�JPN&���YC$@�>0��|M�m�{�)�t:��x<��I6��A@s!���2d�-��f���kI��jg������|TI2��#�g�MR�P��6U�G��u=�q�M���R�Onij��y'@Q�
 &d�T{(�0Of
����jn�v��l5�ae4G��^��56|21���Q�#�F��Im0Q���Xa26#��0��%kH��g�n�����V��E
L���C�"7��������O��
�]�T�>��3V�C&�W����Nd�������t{�
�^X�������2�2CJ�t5�9�����s��.It�D=�e���w�Qkp@���~V��������;a�/-���W��T�z�*v�����B�,>y%�i<����/!���8m����f��^�$G�@Q�3���?�J�x�'���*�}x������e�O^k"�<����\���c'�b��.vw�K}`��� ����f<��S��W�����u�>e�'�m�'T���/���H��G�Q�_��
���w/��o��^�uDU$	"b/U���9��8 ����a8e�<�$e}v�!�7=�24wP8��Z3�J�lC�
il�6�I~���h5'�]�9�r��������) �*�B\:�s}��e��{���^F���Xz� �+b��������*X�"��
/fQ}I��]��4�����0J�&�aNQ������A��������{����p�����7����������������UF	�=�������s�ah�cF�'�9���W�h���W��k�:���n�]�
�VEvmx�WC#XY�x�#a��L1���������K����k��W���-	��pe����9��{�?Q�8<2�\����	"9�G����K�Z���}�!�o��7��Qk3I�;�r(��5%��wN����_���Kf��M��!�x�L!�Nu���w[���;�.�K�Y�u�(?�u�8zs�����R���+��]y�M�������������v���/���)����?r,�E�? Rt���H�f��?�^������
ME�	�����vp��"�/��h�����8GY�ob�����_�5������9�WY�)�$�{V��z����	G��r����+HJW���&y�G���|�c�QJ���2b����8����a�c�PF.9��)�7^�����i|�No�Qs@f�tofd�sb��
:�?t��������1O�u����I�m����`�B�t$
W��8�V��{3��{����OEK�n���X����kz����N��'���UR��d������w;{J�I3�C�$�F�@E��g+�p7����;b��`���U���LQ������;/&�,��%F�m.��C
.�9�����.^�hc�=�A��������4cL��7a���� Z[� ���|�{,��c�����
��,��^t��M�dcq
@�������2��m�����x��tc���g������q?�����������?�#Z�hmD���'[/���]wk�����{��t�M8(���o&H�n�����;k[�/Z��|z1IN�� z��u���1��w�l6���m��]�~S��F�$0�`
b�=c��{���[����MG	���W:�
gf�W������Jx��z��k!:WUZ��1�5��}��`�HR�9G}����l�����cs	,b��T�7�a�����FA�������#�F�kX$�i2m�(0�z46�yv�����N�.c������{����^D\��������%��F6���y�)B����ba�����B���Rr�[���$�so��x4I-aX�`�Sn5�������������6������H����'jB���y�_��-�=d�4�O(�6z)}_�U��qU��j�������u���)^+�W�� 3{����~A��s84)8E���r�.����y.��5�*|�2��X+].O.���y��5�����:�,���B{�4���e�f���# 5&z��F[Z�������@�u����v����869�(#!ef�3�Pr=�"�i�G_v�����d6�����`i��&�G�,2��,"������%������i�������_������z����O��`E��3-Q�����4vy�|�V�i��������>�NSg�C"��'@7������n��t�>%�7�jR\��+�/�^���f��2o�Dd��J�{���1�X{��##IIt�m���c���{���� ���B��D�,��0���H�W�\GCI��t�B���7���� w�K��u�H�.�����ke�����,$�;c�Tk�P;/����#����RT�� �e6L
g9���e�����z�j��O�g,<�ny���^X
�j'�x����7�z�+��F�N�R��}:j�����R���UtZ�v+e5�����|��JD��7x��_�Ry�.E.�V���_~�$`:�����.���y��������,����$���N����D�hI����?d���n�-0� �
�V�i��Jj�k#����;^�v6����f9	�k���� ���Gk�t����*~��=b�������tkBsJ#���{��
�X�^���i\X��o�Z����J��C+-��AF9J�S�y���*=5��y�{�������
C�j�����q�m������4��uy�������J�3VX��
+��i��� �6����T�|&XN�����W����E����|����o!�>�3���2�AT��x/�WO+�?>|��������\i�L�h��MZ��c�V�=�$��-K^I�l�DB�$��em����}U�*@�v�����M�P(���}|��b7�|�s����M�M�J)�=�e$}�
R�sMh���F�p����s��[�lLx�Q[�{�l��:u���	��C��D�a��zH�H����������1+E���3�������&3���|�H�/#��V��M��!��D�$`CE(( �PK��32��^e��SMk�I�hcG��`���si�RP�����a�I<X�>�w!L��Y���3��*��,j�c��7�|t�L"��r�@l���B�.+^6���������|��04��0A�Ffs������
��	Eh.P�*U���QO�%g	����CRf��O��������6h��{k���V�[h���a���@%{HW��,#��]qR���u�S��`�!�A�'����}/j�������.��_��\�r�����e����i{�������:���NCe�����ly{S�^;3[e��,�.,����B
�Y.t���Km%����E�~��������.�/1_��>����;G)�^}�~U������:R����
�_��a=`��/��;,J�Wqb~=�;��9��'�tz�-K�e��3;�(�F1�������0a��-��[+����`�D�I}
�V�U��2�V[|�n�F?R�]T���3�&?W�������H8��#�w����[?&>��A�}k�b�g� %e�Mi��p�}G-6h�������v�9�pW� (������]����e�o�z��Z�+��)����g�M�pq9���Q��"�z����)�k��O?}���px���w|r��7���M���2��''/��������3�
R��(�������U)z8�p*�d������H^�����m���� �sP�#�
�C��m��&8&����Y���P�(�l~)��j��F>��Z
�����)a�?����W ���"����8g-W�����[>B�{�g��5/}|���]��I���&��yV�c�k��'�|`6��k(ZJsSU�hd���f2*)��S�P5���n�}J��_|]����a��`�v�� Qu>w������q��c�L'AvN�����wrE�.���k{��e�;d�m���M����Kv�R~x����I��+���*<���D/��%��_eK��M�X��5�������r���3���pvb�_�.�mMR����5F�6��zG��>�={���{\
?ca�+�/������'�?�}�]���~;}�zt|r�zx���\�k�����o����?��f���v���k8������3W@�Nh���(%�g���x�^QY�m�&7j2�1��9|1���v\I��w�~E��-ij�[���j8�jU�;L�4�Uk%n�K�;|����@$��*U����ms����
�~�����������?Iz�sx�,��4a�'b7�7�v��_�V��=�Q�����'�Y���9A������C�O�.���KK��&f`N����>5���������	^����v�m�_�zDG���6,��7��T��k�k�@D��D�"l�L�
����2�\���N��A�C�����s7������G���/_;2�8^�j��K�����AMf��F�0�i1.n%�����BIe������#��.�����}gP�E������N�����	��a��I���������(�1;�`���A-�9i��6$��4�����|���$\
45��
�q�V���b,L���{�|dG �`An���Fj�d>z�o=&�	���A�7I�������x�X<r3�]�F��&�	�7���}O���(:H\�������Cu�tA�����)I�Z=:x&T!����D�ZK�Mk<m����<�Z�C&�0�@VA05[�e{S;�P��j�p�b��@n���n�����_�Z���h��u��������mf�J�����) �~3�rz"nA�)F�S4�LB���n��N�����%���������e������`Y2�'�)��n��
�]��N�t�j<�u���z������no����
��0��vH�S�}���;�����^�FAP�8������m�.�N
v;����R#[0��|8fX�>!i�iC�������������4��}w'��vy�@+�0��I2��|���Ks1I����,������{�����M�-8�H�������������G]
�u��]���in�7$JpJ�g):clG�i����S
b����B��V��A�FB�	yj]�
���[Ym:�����^�!����������
��bo��9���c��YB��7Y	���P��������$�b�,����O����al�������)�/���<	�Q!���K�~�����	m���#y���[7C��3�6%�O�`���cn�Q3, u*�}��3��	}��vwR�*���4=�_�j�m�4�������I*}��ev�������~�/?�	.��"������UbT������k�����G,�$:a��$L@��ippr��twZ��������������K�G��G��:��7B��GFK�Ys@�v�(�����<(f�<���OR���f5/a�����J������&"�=w������B��n�]9rd�q��T���F�:��hu��������E�$P�H�����n+y�����
pi���[�,�������������T��\y���\+[[;�
��5�H���-vfr`��b�C�:������xx%rY����N�p(�Jt}uz
$���� ik����m��\������I���\T'�����	���l[��>z���^j��^�%xv=x��&u��z��V�������.�
�����u����v�nq\��������f���	�M�7���C0]L����'���������������FU*�:z0\�
�t	x����������8��A{�9=��V���SFz��~�A-�P�)��i���p���J
�eC���AKX��
 ;���W,FQrk -��}��<�T
�N93DR������|�'�b6�������FS��0��.xn��C����l��C�[j3�}y����mN��{�����k���_�5�
8��2�M�bB�����������e�0�������a�	�"���
��
1C^��E&����7D���?�~��c�.axP�V��"�J�h�Kf� yk�G�C���	`ki�DQu�>S9(�q�p��z�����o;@�����X.e����e��5�g��K��i�P[d��@���}�����o^�������sM�F'�G����F�1�P=��u�N�D��zR;d�w�PWbmLu���>�-��Z�a��X��n�j���6��uQ�����S�:�q�V"R�k�</V?A����^j��\�T�c����eY�#�����A6���!@���sG6������#��G�������Q����_x�.r(]n��4�*��P�}N#���,E�+������B��� H��b*���X.o�->Zf>�������g�je�����Z���R���V����w�*�[i��bO���n �@l�����B�y�<�"�j*��0�E
��eBe�:��`�il-��b�X�-9�[�N��A�(b2�>��V�Z�����g�4��X�e>!��te���i����m���ZYn}����F.�Um�Q���4�)�g��!�������0bX�M�8j�}�]�����"`��v%t���m_Jg�
�6���F��v,>���D�����������3��SVb���N'��q�;�^�;

%��3}����\���������'����Ow�J����rH�f�Lr���5��OK,���&\Z�E&YB�KmY�:�ck*l����_����k���'��%����C�t�Z%  �)�f�����#B��j����M���rRU�Mi����~7��v�K�w-��j
��g'eK�~�p�����	������^�����s����b����"\���|���J�r���}.�K��7�U�U��+k�e����L�l��v���|Ny���d����$�-��s�@��
L�<&��~����(��p��S���#�(|�9�L�rv��.J/���~�L�"�R�I<�Y�ys��D�������k������qQ���#���'F�2+�9?
�
rl %{vH����9V�2��r����
V����j��%�����`�HK��� 00���[�-V��w����V�����`>�K��l��]��w�<��tLa���/o1>��W9�'���������������������
��!�\�7�j��_sv���w
�q�' q���V���#:>w���_D���k��'�j��{�Gu������K���hh�O]���G�z���BnZD0�q:����bOgQ���j�=��O���h�!�*#���+�x���hz��7j�/��m�#�`�������!�		�nr|xd'���!�^"'�J3�9�d���6��$ ��|p��XD�+Tb}���H�Kl~�������H�C���d�1b�%=�'Zt�Rk^���n��G������������cq5��z����(���x�
%�A�;�6}�� ��b��[���R+�`���(�w�
n�L����\.�glPx�����NZ���6�����vD��F������H����J��Q[!=��<�=������GA��D��0T�$�|��z$���e��&Y�hD�<���~��#��0N��5�wn�m��n��t��1�{E?�6Ep����~�'_�Z�����k~3�c�������XJ�
���te�mC���f3��?��eg���V����T�E������)�U�Cz���x��Nc��J%�����d������-:
�WS�_Xw"�
��h���k~6}GUi��E��������&����{`q1'�j��`)�)d���A c3��!~'�����H�y^��Q��S�:_�uN���+i&�R�]2]�P��K�3Z���qi�� f������a���4���XN�('� a��o�������=|��`�k5LjE�S��<�fUE<b8���06b�eX���Cl�s�n}�������#�������(VaYT�Z��&t+�| ���4��Eqs�f���#��F�&`���V�l�X�I��%��/.T�C0"DA����c�1��|���AG��nu��I�V�W\���x�����+��9����f=	F���}\n�O[:y�C�6'����g��{Z3���j��#on����Q����
�v�������"�fWh�����j����)�E#r����W�u2H���=YMM\#���)p������&����;j���(K��9<a�X�}"R��GQa�dS��'}a&OH���*.�����!�H��bDd�o�4~y*(|� 93�������P�d�{��
�T.^�bQ�����.+;Y�k�����cD�Q�>d��hm��%�{!��C�~H�)z�(!U��3�2���,6Q��uN�1m�
���e	'���0J��F���W��\�[�O74W���,�*��l��u�w}��V��_�
�n���,�P#q�F�m��i����7l5�p���'s�����O��3�5�1��!�V2%��f����n+�Y���������oJ����]����en�P/��82��U
9�;�
����(Z��Ua�����0��E���������8�i��UJl	K%���	k#�l!3-mS^8��e��]��X�q?������)/�t��M8�R���������������"����3G����������E��\q?����<�E�`��f����ts��
	���d�6__�#�
.��,5�/�Fc#1�.@�8
n���8�����p=�a�b�7\1J�8-���/-�&+�T�r�(�A���jY���Hj��WgS#$U��������P��3f-��==BYn��0��li�on�Uh��������N�Y�/a��8=3�&�����"����l���������ltv><?����}]��9q��g���O��#r;���=�wtrv�O�������
������'?E��}��{aV���;�H�W�yv���QLF�� ���DY�om
@p�
L�6���'��O�fA���Jx&;��-�=?c��]�%�8+*�8���t���-r�Ccfr�����(�N&%��gI�Hc����)#A���2]H	2T[���N�G�;v���f������T0������ `��aj�J$_Q/��ov?��@�����yA+VX��B7��EE�(��F��AYQ6��6:K�%��5�����x���'K�����Ak�C�H�l�|��r��%	�S*el?�����U�R�	�� V�����4��U9���oYQ���XPt*r7��c���j��
��(j�w�����|�!�c�����^���l?�����������������������������R'���D���;�����H&ak��S���
H���b��O��+(~m�ps��J���=~���ip`��&1�w79;2�r�potx|x���%#�;�A�M�G��=W�G�)�/�����:�����~�#P��zu�����s������dUY��%�kp��������uZ����
���k�',,�&���K*R#������K���J�];��e*W�-��LL�+t���Wa]t����x���Uz�w�E���f�{	�����b�,�)���[����w�a�s���(g)��d��OV�gc��S������C��$���7�}U��h]��q���s|
4�pTX/���� �Lv$�@��?�����9�	z2	�P��P K�@]�9�*�x!�g
�qLz�B�6��'�����n��l���y�&
��%<�f��>*���Z��s[����!EA]�����[�V ���h��v�b�!��S�3������
R�����V��
r�9|��4��=z�~��q����Z�AM����$��=��JH�&�a���(��B�����T��)�������%&�k��T���y�e`�L�(f�����UfL7����'5��u�_��B7r���2��:�Sa��i�����j���[8�����t����<Mr��p��W���~8�t*���s�:�V�<���K��@��=b�`���J�>������SXD����`�2�����8������X~y�R��=���J����V���]8���J>�e��F���g�q�+N�md�m�.w[�W�%���|]
z^����$��EAJ|�&����|����������AMt�'OW�Z��_pd������j�s(����Mz���F��o�������|6���)��������_�~��o�<f�+u���[~�nQ6�;`�D�r�M"N�>����N���'��k��F��A!I'�A����K��bQ�K����R&iD�_e�����?%��Y
<@e��
P��cF%��hg�tvVyl�=���i7��\S�X��c���'��I~�SL�J�����xk���g��Z��F�����oy�O�7�Hb�F<t�{������l+,J��ff65�Z����9��x��C�{#�< ���O��7;z���B��-����	b��7~���1"�d��Z�H���.�t�*�=�:E,���RXU��7 �Xz5�C��Z�����!_G0��\�2+�1�<��b������4����������9��,���E>�hk^nP-����x��������������&Z�Zp����@$<p&��h�y�������hG�
Hu�P��3r(>=@�W=J�WS8N�g�}`M�Lz�~^.o�����~t�����]z�t�Gn{5���tP�+��-�6��F��H������D|��E{����zF��%n����#9�M<AL����������=�+�����Y�Ln8���{��x�#��X.������k�.�}F�8�(�#j
A�R���&z�a�t�/��%��2M��B����7b"����1�B���WU���tt��bl������=��`��/X���o���v���;kg�E.}���vc�z������ �& f�ejT�e
�{.O��&����Ff'r+�x���:Pb��.�����	U����q)���D�!XQ>�,��[��B�����	���l�p^�=:?T����KC�p�,F����
�����$�/"48SN])����M�Ad7��ek7��N���������F������Eo��,��t'��i�H��
}��	Y`� ��B����~�;i��C6��R\��h�F���kKI�@5~�M�����fW�Y�5� �1h�����\�[����A�4p	8u�;w�;��@ �m5��q�
#^B1�'G�s���#{�&����;r�D�y^�H�+���^m�kR�,����X$��F&������6^�rA8K���������*�G���<����]:�(�\��w��<}EV!�N,� 7D�u�\~���������%�� <�C�#F�L~o���p���=��#�<���TjB�E�~��W<��$���9z��,� V������S��	v�$�G� "�=J��!v�g�����V �f���0��/��9/lJm'�:���Y�e�
��}5�A����tH�� ���cH6)#v�'��e_[��7l��c���<�4�����5<�T�[T0�z���~*����i:�Eq�J���En���O$c�������S�n#�$$V�A�=`X}H(�\u�EA_�Dr��8�F�E�C�':i\�@��@�wF7(��L���R�u��s����M�/���K9W|�Z�}���n�X
G}�[�x�c=�"�'�>������|��v]]���r�QL3��R8�zQ�THMuq�)V�#?4A��
OZC9��i2Uz����������T��v���CZ���c3��j>F^�{!5�aU�H{@�O�d���8+&X��bU��|%O�f����
6�5@K1���h��.0����D���B
t-i�������7s��o$� I�S� x��nn�`;(���\�Wm���<�qv3�&��Z��j{1*�;���e�w>A�nGy���t@�����d�n�06���2��;Q���9������
����.��sqa�'�T/W�i_2So�������n�����y�+�^���tuW�z�9�N�9��dZH��rMKI��F�C�@�����QI��y>�TcNy��,8u�O���9v�$�,��Y��>_�+���[�Gq?��&�B����t~�u{(c'Y:��R�.}����7�-��G�I_:A���>=�y��=�.��k�w)�h�G<5�����i����B��������E-G�v:���P]X�lu�_���x����S�2�A*�Y4D�i�.��*X��;��XP���r��f��r�f2���y��Xp������I�W~!w�X�����K.��c��R����^7H!?r���b���,6H�E}mAP0X6�����)�4@�V �;t�W��"?;�	���Y���a�A0=HS-���:i��}�({IYDz��h����
�bE�8~AR��R�rX��&n���w@c��SO����������;?I�E�Nq=����������p���0<��s�)cG�M
Tch%�G/����v�kL�}���*#����x`E���d:&UJ*���r��L'(6��a8�"l����(p'/V�9v�����d�/�S �0�KZ��m%ge��G�8�2 �T*'��c���^���X	a��j7����E�F��k�q�Uv�r8OE�#�%���"E���\���������SnwN��w���iAz�b(��,����t)�g�F@;�^�t��lL�JA�J�h~��T�u'p(A�j�2�Jec�2�B��R���\�Q�OG�,�����Q,
��n��I�F����~<�E�;'%�n�$o��ujT\�������<�����?,
�:�Z��p�3����g��_d@cC�P�ab���h�FB����1���5h5���s?��
����F`���W�R����z�����z{T�>~+:�t>)����9A�p�� g4t�5������}����X��%��9�u�_d:�w�L~`��f/c����`����B*������"�Q�����Y��t�������2�P��spz�Q�@/e�j���]R�����V����U�	�N�������p��f�
��h�}q�����TY>�bwOb�fqF�7����S�����CW���������K�#�P�����;AN����t���t ��m��M��<������a���c����TnR<���3Jo=�f8x"?������[�������:�p���P���?���(ZwUp�Fb����]��d@��(���������-L��F�WB�lm����P�	o~��	(�0-��P$�����ax�(�H�h+����O��2;�� ���b�S#��>��|����R�:�	nU|�lT�����������l15���@y%zim�$V���"��I�TF9%P�
�oH�EY�F�_SC()����
Y^#f&���4&N��%KXS��d6�{*��N#��g�K������n�{
����d�~���e��tN2(�����1q���)���*y��7���h
�)A��-����6�%Q�	��f)�v{�qV.����)Tb������j�qW��M�#7J������
�:��:F�f�.��q��tZj��}�o3�p?������~�-�/�%���0���1�����q�U�38�n�BR���4�m�Z���>;�<��r\~}��ib���rD6|7kjp	"��b���A�"!�����K..l�x!TY�~�Kl���pE0�k/iy����������n�������Lx���#J�b�)�����m�~B�+I�)J�g���5��q��n���IL�����Yj�e	���<r��$GhM�#��%��f ����Pt�������>
2L��A-��Y�����s���1����<<���=���W7O�L}���6��������
�2�q��-)WA�xL�'�����Mm��K��yM�����7�W��A�k������+���}'"�7��"@M��d�*W�
�+3e}���x�����:�����j{B$�W?B�r���m���b�����jYKZ���k4��	]K��>�N�%/��Uk�n}���5lfdM*=��.�������1A�QA�x�\��x�qf�����������0�����h�+�8	����OjMq����*���R����H�d��hUJ[��P|�@*}5�}�������b'U�:0t��[<(�����9��@�	�_*�A��| ��5��WH.����nk�����������'��r���/5����L���Mc]&0��@	���� �/b{p
��"����:�
�X���������3�3!���tZ0����{@M6-g���.�n{�e^PY8�E�tBHYp�H�.�gs�&
���@O�
�>��d2��b�z,pjD���;�)���K����B=5"�|O�������z���{���0����n�8Q����Sc�EG�+a!��r@�3�t"k��t����v{d�]��5��(7��kh�2���AD�A�������V� ����~�B������Y���z�>��])��J��t]�Y$Gy������l��W��.x.�b9����?���P_��e&���f���I� �Q��B���MH��X�h�o��8�����#sOx2<�z������@@��
�F	
w:�s<�N��@���s�G��a��q�XB����|C*G�m�M������<����9�!�n_5�"�n�$���u	���'��
�l6��d�]��G��`��I����O�+~�����1��U�#�I�1���-�&�������k"�WY�������t�S�W�F�������H�~�;���@��w���/���~���!�@��tSO�������%!eg\�-��3'U`a��.�PC>�i@b>�BBwHA��Q`�����&'�T�����!�����
O�/8��;z�����]�I~����U�c�m�Y���g���;1��K�oC���uQ� ���vmE~���	S��uR�ckRg�W�ckE�zI$�/�!�#B� A���c���c"��m����-�:����m�#�B�5(��KDD5&`�����O��M�������SASK������#�[t.�N���(&T�']"���.S<�8b,Q%5�l��i�>@V$-H�U�>([���6k{&)������,V�~�S0K��|^g��J����~�/fW�
�F������~�J���
��M�j�&u���	�Y�1���R�c��2IM�$��U�Y	i���e���	X���+0����^�����j������6�%����9�U;=��9/���n]�{�����`�H
'Ft�]�Y�+��"�
��}*i�����	K6�4`�9Bc����xdU���N2��,y�+M�` �O���r�T�)3
�z�h]�H�X�6V��t��O�J�j�b&�zt���4��/�:���C~o���tyf���`P�F�Q�'���u��/�|wK��;"E~���C0��M�e(x�9h��c:�B��By�����O�t[f��ji1M492K�"]����DR7�>qPe�&����"�����������������r���A�f2��������q�|���4�������!+c����2�l�*\G1��@{q�$(�C�
���/9�����#v���N��9'�|���E���`��Tib�6��17R��{}&c`���������/��@�������M��\Nee�c6� L(q^��[������=%��>q�uL�-���w����[�!�5�j
��;�@1�I�%S���E�E����"�bv��1,�xw�������<?.�����T�1��?���\���h�����m�@�/�Q�z2���`o^���b*�a�q=���o?�#�������G�j
D�[��w��������&.����@��L���F��Z�2�:(�X����G�����BH���\=��Du���������=�A��4#����4����&���`I��+�%k������2����Z-LsU���r��3�������
����i��h��>�I�;��I��'F���G�I��t��^,3c�_�����-!�l���n�����G�g�'F_~5������sN���c�U��$X:����.Z��Sr��]M������S@��� �����u�A�f����dG��P�:D��Z�.$����d$�������5���('�}�	^�����m	�u�\����lj{���f����������]>�!�Z��:S���U����@q�p4�������B)9�e�����2H���.~
�~�pp����v<��Q��)���l_���	�|%�e����:_>����	�1���1�.�is�	_���a������<J���;����V��$q�X�nG��M�
D������4�����JX�n
2�)��3s�#�nr��/���v�w���n����$�U���:�:��b�J.���Gl�
�O��������s�726�m���w[��k -?n\���h^��0eMZ��i[���$X���x��jW���+��S^\�a��a���7i����0y�����S�K�k�8��j���$���_����T�����3�iULsW�`���1 ��9���1:?z�����Z���v!��cU����
��N�������4�E
g��S.�+��5^JD\�	��q���z������W�	�'�[�U�p3�7��������x�������8�:���S>�V����������X��v���/�������IC�=�:���.�����[��:��
��#6���]w�7����qW�sm������]��ld$�s��W����cX����p��1�`�6�]�?pfo��Z#a����5��A�k�Q���Z���W�Z5�:����K��O}��H�t���9R3�@�
��E!{����,74���%��naF��#;	'9~������#v������*����N��~}�-"&�D�}C�~Uarw��w���G9����Fy����m?���A�^q1w��dy���f�]���1~�F����>^���Q�]=��hV1�)D�>Y�;��������6w�7y�#���D��������m:����I��)I Gl�#���;���
����q);���>f�~��+'k�/�]
J������tx|6�;?<9��<��%CN�i�Y�(�E.J��P��t^q`�VU�l�ium���^a����.=����e��C&�M�x�z &���N��. ���02��=�]lk5n�/Wn��4���������b���	W��n��`�&@SaJX�y��YR�l�U��(��
�/�$�\����79�%\�q��n4����_&]h�h��W#���"�Aj�!W���F�<����w��H5c�l�w�!��}��
��}�*��[��S�\B-x/p�bqYf��W[&<�NX��Fq��k@������J�%K��,<�-"�d8���(���	������x+��T���
��=)A�9n4�
�������1���)����DM��FDZ�3�|����-bhE�&�E��K�s���o�	R�u"��M�ZEk&���:�������&����B�Q�r^�����]���X�U��D�����9����'��^�}���@��K~QO�����}K'w���y+����t�y$���p������O0����biNk�m�����%>b�������0�����Q:��H�����	
'9C����T�(��$	����J]�C��a1���M0��::~�'����,>�r�.Q������K� '�����UU�r�9%B�-�1�&�3���8>'��e�W�U��m���	L�tD���5G�������@.io���{F�J����������R	8������^@����abD��jQtR�^;�rW�@�1�`�����9O��t��IzQ�M#��f��z���Q��$k������o��d��W=�U�~Uj����qa�qR&/(;��X,���x�U�JT����^%�l�qF�z w�U�]!�C,Te������L�N���YnNn�������NR��#QCUl�l.�e"J�+�Z��b��3$��,��6�	)3f�2Z]����������C�G?�N�����i�3a��6����i��{#�s��������$Zx^�	��x
W�N���i�!�����Y��1�}�����B�3�����e��J������� �2e��&jfC�����c���47D��c^2�<.���E����#�VzQ����t��wb��> +(��@��hN�B�����y]�-���	���s�w���~�l�N�q 75�tG��
���������	K����~=����=��O��NET�7�Mn�{��K������%X�f�ZP�=��i�7Z
�x���O5�4���
[���>������a��`x�C�[�R
S�8 ��`��[D=������w4��hZ#���������k2f%G������kVO�����n����{q���
gX��/�Sn�iB%���bC��
�_�g��s�J��a��@������h?~�ZZ��R�,�?�`%\�7��R�����p6��7M�����,E��+�Hvh�����R���M3B�Gf�7�>[���?�����[7��{u�����>�8�/)o��:�(�#)�g*��&~i���P���d�������_f�'2�2��Km��n����@]����9
�jG��=�Q�v`�H.�j��
��*�C�-�	��&H��]`9 X�R<�����'��<ZgJl3;B-n�p�:�>��P*�Y�5���?s�u���8��}N��:?
3���>���W�x��2[~
�������
9ym@���B�b�de�2 ���@9����bd�@��|rX���;���X��Y7Pm2H��87��y��%\�H�iY���}�A�v�I!�|b����)��L=N���b���8K������%:�<����z=kn��{�A�X����#����#�'�fxy�m��|�wzx>:;��w��s-z�#r~�N5���O��z�H�`�C:`�mEJ�:�3y���5�C�����C~ ��Q�\OTD���u��#�F�O�N��u��K)\l�IPpnn�y.n�0lFT��3B��w����[s�!����e�@��L�/���� ���n�Vi��9o�
�rT\2n������	��(�Ab��C����*���v��]m"8�l�U�h:�����9�����0
9UYW(��4���i�hK>H��-Q���j�=�>��T��A����&�(-��-t+���6>��I�X�8?aAQ�\?���Hs�D4.<D�q%W�Ps��]�CW���/��
��w\��t��u*>��/��W�{�"o���OP���I'<~=��hN�[�k���7����U@���T��:�O�kiz�r�!j`��N61�R4������������V%Z������
00����?��������R�[ml��CI�c�DE�F���F�l�i0���Lc�%�xYL����Y�
a�����S1�������?���?�t�:�}�#�F�]�&G����[Y�J�u��b"�@��

k�?r<vWs��#�	F
�V~�%��n}��G�OM������������3�Q���]9XI����^��j��
p�Rc��H�J
��B�s��Jj�D%*1�ML_�[n�D�zY a|\"7��4�b��#��Qux�8�A�c�,�W���/��sr��}'��������v!"�
U���*�/�tU����Y6������@��t��1/���Hz����=�t#��Q)1����B�Nl-�
�z�=�h^�[��N�2�K��8Q���18M���ylE#X���=U���H0%�BWt0�c����.Zl]�$�[�ml]����`��Vv��o�q���vdk���`��V�d�Bi���.��*���zX���v�:[R�,�b�d��9���*|#���@LU6TRn�bUB�M���`k\B)���j7I�7/)�}_{p�������7����
����a����b�V�x������a7^�ag���/+3D��z��&-�����c��������������.�����	����a!3�������y@�����Q<)�*�p=
2@	.��~@�E�	��Q[�A��G\�y��'�-����}P�Zt�nV����{lK��������:���3F���}����U�����.�LKj�y�t������L����8�YM;#-u�z$l� �����S!���Wx�;�<���(p��%*DAr8�a�FH��$}p���h������������J����5���1{��q�=��fP����t���<��xw>�j'N�8�G�:�"F����W�YF�Yq?�5]�<�����ul��IY
N��������wy����a��FKG�G� �'�6V���J�������������:�����9*����%k
��6J��X^'�;�$�_�������b�-��*��m�����;���tz����
����4�oI���8��z�t�^V��K�Qd)sB���_�muZ�(�X��r�Z���@��D��Uwy�y���:����/��$��I�z��
Z�vl�l�����fxZ(��������B��l\���U�r2%�4�_-��.�$�����,<K$��
[E\E#)�\"�P����,����@]-L�����������W�M�/��n��`��K
��-��O��C��l�����Y6�l����/���AH.
`���)^��!A����8�� �4��vn*x-[l����vk�[��?�>O<��{^?�"1�jI�w�]l&�h�}���?Fn�9��<P{������[�����\_�����>-��T���0�a�� �z��'^��I�+�����)KGR������'��5����$�c%���}��l�X��-.3*�E3�+SOg�������.��5}\���,���[��m+O��{�w��=	b�$�^S����h�����2�1k�T�.U���&k�C\�X;QDb���W���g]���[��K.����V=�L��x��Sjv�-]�$�-��� �s�M+�X?��N��=�~����[mf���9��������	��g���<^��.�o]�e�]�`��*�"h�	f�!�q��������5�-�\�B{�l��=4�������7���
�D���	�oi�V��q��	Q�c��e����cU�a>��n�����TPp���}f�iM�KQ���SY6#��az�:�����Qx�2�Fd��y�K����44�~��*2{`w�5-y6��I��{�F�D3��|����F�M��N)��wD��>�/����'�����T���&.&����u������_R��6%���$�M��[w$�&�	K���B����w��gbn�_��\����	����u@>����s��FM��5��)�V��Q[G~��=�tenj����S-Hm�o��� ���������
�8<��69����3�d>�N��P����j�Dk�j�V^�6��&��'?�ail�?�'��~=
�^��(����I�4��(�S�K���~2�6$�em��+�Z*�����������h�V�6�H����`<�E�U�V�[�V����a�?Ly��6���Z����A$�,�	��n�
�wG/*~�����G��4#;�-���8{�S]��}�S�������JlR=dj�������^B\}���76pg>��������\9��,��\����v��%�0jN����
������F�"q�m&��*y���������m�8Z���d�S�F��S'������:������������
��m>����z?O!�����*v<#�
=#c��2G���%i����W��3���vO��?���f�����s��<�l����=��AE��@	t�w�{��4�#$yiwWL��dIGAQ&�$�H�e�UJ�t�����]M������g�I�d�	����C
�`�,o�����wH�d*��o$��u1E9J�eU>t����Bgy%��w!�o�9�z�[L^��j�l������1��+y��L��B}�����l:��)�L������VY��.T|�,��1g���6t���~�����T��[v	������	�u�/4���7�/�����]=8:x}plF�?���n���?����k�`��C�����'���^����������sH]�;�)B��=�����+�����������A����c�)7v�j�%����c7������"�S���yR{���=��vJ�k�IN�9�������p����cchv�*z�)��O���^�^��<*P<e\����\.)�3B�zz�7�	u����|$�0�B��dvS�z�	v6M�f$q����hJ<	0I��f��[�x���6��LL��Z�X�K�r"8G���9����8�������xQ�V�O ����92Z7�GxO3��R��
�J�f>1���[z����I����+��T�i�p ��(Fd�s�0�������x���������o7�-G4�K�����]��`n)|h���p���te�s��7B��|��r�.�+\�����I�{[FMN����@�$�����F�/j.�����C��!�jg.��A�n���#;��bU����9��J��.���9��K�PNeV�;bO}DU�Z%�\��86�����"�y�%�2����25��l���o��/q�+��[���B�"�}J���j��J����o���F��I5N�C�����>$�<�����R�M)��A�������Hp��?](1[u�\��.a�s@�R���3	��,���K�H��cb���E�Q�������F�����+}����@�0i�����
�e+|��*=�H �L��s!�������t������ �8%��;�F�Rh���'�+���Ee0���3���	U����E��9��8��?����'P�������aS�����6�A3��L�`���@�
�w����uUV�����+Q��X��W\�
�\V�L��t��:/�^"6�(�E :��!��~r�������\����Zr��r�������L�X����;�����YY���w�?%�_������_��
V���!����������0�Rby���\�F@u�@;�F�����f�e\o��g�=��R�o�E+�
B)k�8(��	��_�N��>�L���Wg����K�8xz,yy��Cj�Ev[p��������iRV�n������r^���P�sm�S���	��������3V(R�������C�	ie��^�*W��p:A�z���\!s~'J�
Ksh*�x��M#� �Pl#��?����N��gq}��nR4�(|
 �������3|*����O���i��"Y�m1'U��L�`�I3c��#z� �V3��u�A6 ����s3z�g��(��E��
��h�Q�<${!�~gJ0[ l��"H(Z����Z���n���c��Y<�9�����������t��_�#�6+0+?d��|y�|�
�D���D�I�AE��Z�@f���Y���o2��6���bC�WB���ZP�U��oz��y\1�(`�q��<"AU��Zi9��7�����*+t��n�+�/q��y���fUe����J��RL���
k�6����������ZU���}�T,)3,=�UZ��L��Id�f����H�[[G	�������8�=�l���,O�iH����D��`P���7EdY���	��jg<R�P�������!�4y�&5~a?���{��	iM��i�����������������������
.[����O�O5� K�>����g�\��H�A�nr�E���LV����\��O����������5�X��r�C����j���� T��I^z���/�q���%�o��g�)d^_b)�@�\��C���RC�uZ���X���������V���S
\a����\�*[*2q\���HJ�����)Z�2�L�GK�'�� q.w��;6���A
b��@/�2���������L�US�c[�����Z��27�r; t���f�b��)t����-=�j'H���13 ���[��3:�� �F7��'?���M+���Hmx�gOo�)|8I>�bm��7M����~G#�+�x�!>���r�W��6'������@�&�4������&���SA�O^����j:������G"�����i����No��������`r����
����M��d����%���� >VT{b3��]X�3z�di�_�H�\+�(R�U%Q�
�]��uTS2f?�p��]�g����1��r�����X��T���$����b����,�����cq�f��*�^T�d���]#�M+\���1���}	������R����aD�U	f�����;�3�M��1�d�7���>�W�
<�T����Z?������^4+f��� 6�:��P$h�2�9FnyPU@g`��^�6��'�b����&+�vxaW	;]�/��#j��)tN8��������[��#U��Hg
]��$��u���lQTU�qf�-r�8�;)��H(�����]$��Yz+^������	�1�c���S��1�`[�����b2/3����Y���L�O��FBW��~�E�������������T�y	�Q���"��b�2k������W3�8�N_^��Q�r��WjI
��>��1��f�� 9�5#;����G*ny)�w�e}�lB+��R��V���cG"��A-4���rN�.2��,���`K��
�$7g��
~HCX�Eb6OU��K��'�{
���)o���=����e ������h�>�z(����dS��O�K�P�z�y���S$Ka��j�N�H��^�&T���W����l���to�_^&��W0��r���yL����(�k��ru���N����v�v��'O�����#n�'����Ez��?�|��{�4�x�}x<7�����������-�~��l=�?}�,��Q�����n����#g?�;{���9VPE�8�C��m�N_�}�oB����1����a�g���W"���r����'�����g�nR �m�� ��yq�q!)|�������wO�d��=�J��	Qz��O�N��*�h�>43��������	1�*	z����}~�C�k��D�2�.<�\��L;�SQC!��O_*1A���R�4`�>Ho=���[�e��q
�[g���M:��f��z?k���r����E�pi�&����#Z �=T|%,��������:����X���E��#(�N����`�vb�R����G@��!0t�mF+B>�^�����2-�Et>�`e]�!y����1���AIt+�f�8��b0�0�\p��g������s0zy���~wb����N�W����l��#��G7�^�>�&�.p����G�>3B[��5�Dd2���X��>��?���)��Q����������bR��a|.�������w}��)�J�v��O�x��3�^�~�E���x6m�������m��mm��7����O��������>%7�~��������B�����x�W}��l�������n��Z������������}g�\��:hm�-s?��N���f�Kd����iBQ�$+��T���mmao;k=K�OqM���!����|�P���9��]����f�[����)��I��pA�����P
�c������pun��FCq�3�3�K*���/s�5����AB��f6G�2���l�U^!p6�^�OZ����4���|�R�K�$��T�����h���u_������/8�=�PjF��	��K�$���d\Z���2�%)�
������R�e"��d
��-jr������M-�gO��=������m_�-�����r��$��F5�Y��e�sj�)���gm
_v��z��?�a������;��=��G��iq�N�}pv��2f�bMK��CqL��%0��9i�*_b��18��7����x�����x���f!/�Y��/�)������r��Q�_�|�P�:D��+f��~lL[��w�i;����h���-��7~����U�G��(�ly�/������p����b�����j�.fW%��b��UZAwx�����k�� ��^�&��FF?���k����;JWs��R{#]t�^��� U�-��mZ���mn��?�������h��^�
�����_@��K/�{?B����|�bG���1�[�&)��$���+z���i��/��U�Ei����<?�/�����Y���E�������>��S{Ry�G�x�N�yC�'	P��6<:����n���12�����$b����b0�8����C3����O��~ ���m�Yb=I��e5�0u����%3��(-�x�D5\��������2��M��.y�r�
�A����������&�����9.�������P�az����p���Q�>�g,$���:
�`V����>��������8�:��������� \�M#2��4�{���+O8��6jN0�K��#�����h�����K�:�x�<F��*��G`}����8q��no��P��
$e��u��&u�X�)�`����~��e�>j��b��+��wr��f#N^eKFj+�/*����h�;��-����}����||>��T���* ���b�?Z��P����z��!��0�%6|<d�/\d�[XWU��n��e�/v���������Pt���
r���Q����
ZD}�����"��@�|��.�X���H����iZ#�!�E�U���������N�K����@�s����P�$]-�F�_�c�p�&�8�J RAB\�e�
R}�m5(�5��O{?e&�����9yy�Y5�e��c8�����Y�����1�5��v���P��.KS�TL�p��Y�`qc�+��i�K��W�9��H�3��@��;���a�r)k�|7�k!�h���C��94kw���	q
e���I��/#6��%n�2�9cS0��������L"dxu��~���9�t`3$�����<)#0Y\�?w�nx����������sx%��yYL'v���,��E���ig���^����G]��P������=��8�m8A�+�n��������-��TI]/P�P������N�az���h��V��+�4^��p@���E&��F���%�\����1'`����1w����?�������~���w���)���>����7��H�13���_��0MG�&mCon�D����^��������	�1��c��Jr�D�WBD����j&i'�
h�$C((��R��y:f�����|o��U�A��6�IRI�o��K(�9{.�q0���2D��IZ�%$R���r*��K\�Z�	�4�����4F�-�����7�����|�^es��#�e6�J�7�m_�*����n�����Dms��L�����������R���J�;B`,�$a�L3N+����7�C���_1Koi��2���D��S�pA1UFX�>������
x$����k�(a�)^D���POT��"8U�����[a��,&�#^�KY
��2�DQ��(.�"�VAr�V�2��
�Fx�gJ�5�)���D�/�Vf�����$%��e����qK��{��m
���L�#p��v�X!/(��������R31GP��tb�W��6N�+�b��fs�V)�f���: �8A���E�bAX{��5���9�gMoP2XPjz�5�Ou���+d������aC� �4_�2}�w�����>�q��������I�e8(�E�a��K�r���eah�"�|j�S�����4]L����
���!U��d�����[ �M��l�����Qz��J�M%���N01H�,/2�:�=9���c-?����e1%e�!�{�s~Ei6�EO��n�Sjo8�*���r$�������B�&����Wn�U�Z�/Y���s<b�����jR�����T>3/���/h��@M~0������8l�V?�~�CR�Jq@_���P��hV�8���S���94Sv�2����f)�R���\���2b�h�G{�7�������y�i�t�>�	&��z:E�c�U�o��GiF�H��SV����zw�2q���}�8�4B��SM�A�S��,]h�O�-n���Sj���v����6�B�yX"2u�#�&���P����A62�������;�a���OR���r\ \	a���qkh��+\���'vw\<��a��t0��pCg�u����\���X���\��!�e��f��X��{�Fj�c�����r��C%Mm����rt(����m���dv��nN@��0����w"�''�`e�6jrWh�,6v��Ct8��-�����O�#HX��AfLe3������5f��w���o�>P�!�Pi���g'/�GG���	�������y2���kU���\����v����C �X]RuW,����!���=&��V�,XyF���aB��o�*/����[���fn�����^l��u}���K��p��_P��E��<����S��qD��m\5I����C���q�������$��1n!:[]�k~)�r��CHCw:&6b�����P\H�[>��BVHY���������n��/����L������B�ILD��k
�v�V�i>�`����:b��+$m�p�i�y���|?1.�F�S�J�X��E:�
�cn2TVRJFF��<���%a���;���'����3[v�eL��4]TY����o��A��AcA����,�@��x�����`P�zCy<�W��e"�oN�[�a����3V�]���^�I7�DF��6���<�����~��F4�$�2��V��)K�z�������j���Ba#�(n71�v��>����V�����zNY��@��
�=bi�$.�h������<'xn>��A���W��Zj��{()�'��aP��:�Z��d$�D�!�bQg�����2��(�F�����UY�B.�#����.|��$u���L�q�[�
�0��O3�5�J��d���D���:m�;��u��=��ArDh0�c
$������s���V����#YP�T8��H���,!��*n$C�cPb�T�X�����5���.��y{k�yJ������i9�|��q���
�/��A�zY��:c��J�i����+T���(L��������7�Q���n,m}�����@���R�7{���ZF�����A����i!F�����=�$i_4HU&�I��������`y�G-��t�������2���w�>�4��"u�����1 @jb�%�6�C���'�$�M�;Z��#�#<��"����s!y�
��26/�i���xX�g{�^N�����I���������Q�J1	�t�K��@.��{`{p{[7R�
�"|���1�|y�t�RyNlp�]PL��eYHJ���ZbR�����9��>/����d��^M�/n��m�5��$���8X����b���~�j�q�$A�����"r@F�iB�E.�W��^
�:L14F^%��d4���5E��]'Cf[z�7+���~y�:��E�<\i��O�n��Q�!��T��n� �����>�5xD3���z9�mB{��6��z���zt��Gg��P��}�b6�3gA�34�0��/�"T��[��c������#}�d�`������������"������J�q����Q���/�:�������|>@GF9�����i�1Z���D$�NN$�d�!�A� ��;���e\������L4�L��Ao�D\�y~�]�S���)^�����3I)��-@=
�G���G�z}y�|A�O��F|>�>]�CR��eUL�ks+�h�����@����L�%j�T��Q�� ���/l���Z*k�Tc�a�����QavqE�98=;<;?8�;��~�����Pq��(�:jy��|{|t�����F�����h�,���b�~=<>8>'�o�{��u����O����io�k'�H;F5��������e?��zPh�����]��S�����=�g/oe�vHVr
���J�(�_�z����E'���n��yGn�������B?9���@�ax{|��C�t�`���ds�Gg�2���<�|v�)��"J>���
=r&�d�fs�?Y�3��@�T�[��,�L
-�*���AW(���BC�T���!GV�*(�V`���C����*��:�:b���8K��DJ��w�\58�|�5��o�<��aE��8`JEd%�-�^�+lO����
�������D��\���.>a
�����w}oGh�*�2��|�g�|���H/xr#7!T����=.gV
��5��-�-����DQz�����F})�S)i����-���EPM+�	�j�E<W���f 4�H
5b1��������oF����c����������6�M��h�p|��e��r��i^�p>U�q�]p-�m�������x�'��{���&����p�1�6�H��C~�y���J���vS�,����l����,����������o���p�������d	��,������i5L�0
	����"�?�K�p)UP�I�~���qc�|�S���$�9'�����e��]��&BVA�Y;TI���I�,�iS*�K:�b��#���+j��� ���&���*Q�zSl�����#�_U�?uk�dM�M5��$.�J���
v�|���g����y�rxx$�i �����=@4�f��x)���A� ��v_��������tF��/�^/�e��U>~<X�d�hp�O�K#�"�u5$<�K���p�a��[L
��6~�G|���$G��l�@�����9S8	#i��;�$	�.�����_p���FAf:�@.4�2��v�bIgz���a��/_���~�d���?s'a�B��el����h�����$fF���0:#���Y5��NnIE]%5����\(��M�~��1�0���>p+F`e�K<�h���L)�hCf�= �4��p�	/���|.��h�s*Fq�t2����}0�A��K�%�E��
��i\��D�D]8��\��5��:"�������;ol.�l���Y���"�p�v�uP7W�h%�f�w��f���J�O�7&(�Z�$�'��?�5�\��S���q8�����
�B�K,�p��{�Xd<��C*{�g5O�K_�� ��7`����x9<}�$��v3�l��������`T1���sfQ;D1�h;3:��5��[���f]H�b#���1�b^e�*�=�wt�R�/[4 V�V��qs��+'�y�"�'�Kz�kg�*t�~]���rc��NiN��|�B�@��f�{x���L7�<��ns=4>��{�5Ch�i�C���1�����+	�?L�_~	����Q?�c�dEV;WB:��v�|Ku������d��#����Y/2�F�H��J�
5��/mz}`���RdTx�}���(��d���Z45$<��[��A�Vt���"��)C��	����Y���Cq�����FS�|WR����������%	���}����f�&b< �(xt)~.@a�!v���s��|�a���,����/�d;y�|�
7mo7�E�+���#���q�Gv�Q�����R�7��v]�)�5Y��M_�p����}�2t�#�D�Ws�K
�aH������\g��l���D?A��],\ �db�zV,�$�O��NP��%10���t��9���\!�������r��b����SQf=W�a��}��Z���w_01w����8%�����ig�����/<��G��sp��3���S��W.�����5=q�����2�������.(.i��������X.��r'�%h�|�r�����%_���6�w9,ug���
����i?���F�7�q�{a^��3��t��$�e����}Y�0F�����#�4e����b�h��	�e����%;�(
���Q*H����=�cq��G0��vp�u����b3T���V�@���u�I�W�2�lg	�{A��-,Vf�1��q�'�F]���)A���L��x�}����c�t��\t��Y6�	����bD5��?��y��8(�������R��Z/�LA;�����	Sg�����
$���5��J�=Q���}��T�\#�\%_LaE.�6 ?�P�e\���(3��*����sD(��K��c����;�?���F\��nK�w��V�x���e���'���v�%pZ!��
K��o�R!�����
�j�c�K�������@9/7��_ex�O��������� ����Q��7���W-�N���1��PP�
�X��'�}�_��{�8���9�_a��7��_���!;�e�X�UR��b_�z8��R% T����z��h�`Y~�gRuNy,��������HZ��9o���^u��+PyL��[��u����6X �e%B�(��TT�10�7
k��
[��t�����������aX�*f�
��&����2_�������I'���SB��Zi`��@7��@�n�o��QQ@7��������������7oNN��k����&�/��u�T?I#%��5^}g[��Rr����,"}��C��h$� Ur�v=���&�JMo�������c5����1�!�#���5��d��]b~����/�,f�5�1�B�d��Ivyi6���Q`�����p��Z�}�HLn0Ds
�s����3�9�l�Wl���6uiU���!Q��2�i�q;\?.ke�?�G�N� 3���d_���
���f���"nhJ]�+~J�b~��<����x�G��&[%�<��\#����Z����[���s�9�+[����3���$r�
��y"��A�-$���[��;��
��RX[���7�����2�=�`&�,l$M�1��}�3�U�l�M�1G����=?{��b<����t1y��g�����
��Qm��T�>��o�H���O�$��W��������`��9��BY]�����_�Rt�IJ�O���tF�K��N���)�{l� Q�'��������?�������Nv
B���j����m���k'����A�c`�XV6~�O��os��s�����5�����������V09}����?�=��������B<I����@���7����}�
����TM1d��p�e��)�Jc������Bk��G:��ekB!�f���g,����#st����!w7N����)���2,3�Ic|R=����J]�h�Z�zk��d����r -GG +������d�,
���q���^���ZE�/�&.bd�����������~�k�`��'�r'�y.	~XP����+�Y,�[z����.�.sg"4��/�(F��"^�u��]��~��@��a*�Z
�t+3F����e����wD�D���=�J6N��mD����;�bdh����l��&@��%[8|J��h�I��/�M�!���{��s��#�^�6��h"�A�Z-,����"���#"����Lw=[��s�Bm�]��;���Yo�[]�41��������-0<,��F�$����mo�vG��XS'�?��.���<�U���:p%�����Z
��M��"h:!V8 �����s	r��{��(XL#�0��F�����Q����Q�U�5a���x9E�81�$������������C� �
�����N1�e��m$��i�<�,�8)B8u�����O:�|�������8x��GRP2%��E%SV�\H�������Y�`�!��� ��x5C�sN��q����V'��#�B��!,0G���8����������KH�F���m7�5��f��m��=q�~��8�T�-�u���7��v�i����r8�HEk��]������P<��IX��L���`��������%����5�r:��Lk�6��~R��a!��z���\��cQ�He'����<R���	�|���"o?��<q>�����po7��,�Rs2v�Z��]W*��{�>��nR�(m�U
u�z����b��{���]��������b��];�����]�7uG������H���w������(W�������>\��O_���.
V�8MA)���q� L��o�g?�\���B����M�������A��G��Vo�-��8���\�m�=xXLv#�|��`��l0�-��#��_����&��f���7q����!�b�t� ���w���]uGo�����fhZn��
��?���������&�����<����^����d|�7�5�����y}0K�q��B��wJ�����j��d������a�[��<T�%L�v��D�Ln,b�f���X�nT�0^jG��{&~��W}D�=En���b��2fn�eaF�De,�������8?(e�Tr��%��]�|��1\45:W�J�?:<_/S��W�������qSlI��q��M�=�`E�%�����tX8��!������`�M�$\�H�'����z!x���R�e`�bf�U��t=��Y{��D��6��>�~-B�}��}	�5W-�l>��FE���]��s
e�	HNH�~�����iQ��h|�`�	-aXb���,W�����W���#h��#��B�MP�����r���sX�
z`t+�������@���wfau�/�O��]����>B7`9��U���@�P���
�;���|d�V_��d����2Qk��C����O���6�� K�Z������^���vOu���/��o�����9����Uj��0��N�������L{�[��eG�G�v��"��H[���i[W���O�x���������_;��pg��2�+����e�_��EX'J���^h=����*�q`�-�����UQa��_e����h���]��H<d.pI;�^��+��Q��������6�[J�o�m8`>���C����6�dQ�x&b���������OZ�������"�y�I'I�#��rvA���L�������UL���K������]_��p0��"*_��7�����U����U��;����1(���ei�Y0�@�D;������/�e��#�%a�q���xE��l���[�x����i|aflP�e��A���i�8���[k7��[�O�x�����d|���G��d���h�R�h&M-?�B��*w/�5�/��H���/����{���F��zq��H�\������6�ID�X����$��+��K��~�y)w��i��[7��)^a;����A
|��	\;|��T	2Q��E��-s�*��~��s���O���r������CBB3'����!��C�[� �����9��}zxm".�������Wx��9�����	'+�YH
�������?H`��B�����#������ug���h�H�8��%�v�]�t
M���r��n�zt}E>�^�?
G�;��n��j$kd��;t�M��A(�.!C���n��jQ����"�����1uv��'l��&��&����������� ��2�G�\�S���D�)Pw��ma����,��P��GU�����j�g���w���<Vx>uo��G>�D+���'���s�zA���������>����8i��x��GAuE4��}:��JB0�.�%�;�,?��6���kK�J��/��z��;�������,�������J�p��R-�1��4��9��0IsG=� �
�@"4	�������b��0�����������{����(`����r�����G�F��Q���''���_?�y6�m���� ���,1��f���efWyL�3��b�VQ���/��q��%��fr_�������gwY����������V���� W uy{���]���J�>9�z
����]
B�"hB���
(����"]R�\\H�����%��bR���8.�����	]�Wi.��-�P�����swy��eV��3`��y#�Y��!F��z��!�KVNY��C���w��v������j���`
�����g2C�����<a:Q�>t���@g�z��q��LB�R����XwZ>���XN�����X.g�0�s��UF���m�uT��T*������'���3���?��r(���d�9��2&K�~I�R��Yh�W������h��x���tG7�~���{1���~�@���
_�w0��^9�Y�?z�9���y��3��.z����������@����������D��s�.=Pv�a,lW��v>#��j����;�HRv1�@3�������9Ha^��dV��p������8<J���WV�V��Xe�:>n�O�S��Y������mV']��NB���#c��p���I	mA2`�7�!T���/�y��%�2��.R.��r�v8�"O�w���������d��K����e�^�k�3]�1�a�!�����4�C�����l�Xe��w�����V��n4Z��������i���]��$����"��?W�r��=/�JHK~V��H>����=��ob���J(~��
����l��)~�Wv%����t{
���9-*�[P�{i��N��Z�uR�is��������;Mgw�p�w��c�a���s��1���"��.P�E���	��T�����3�^
z�W#R����$��s���	K���\�4����
�8x1�F�#�b$J��p�~u^8Uu��q��M&I���q����j���������n�����ecm����W=���H1��f���@��i��T�����w��F���wxAD�g��6���F��Lm����O|���B��,�j,����h������x����Fa�WY:�>3��Q�%�?{1$6b��e�:�[�<<6��gF1��m��t��{|` 3�Y�E8��(BO�"�Ax��,BO,*+�,2��d8YF���8��|f�+8��m����V��A�L�T!xh����)���UJ��~���!���I%%/��"�go�����p��a6Pgq	��G*���>g���������t�������JK	�+t4Ov�J�fX"�`e|E����H�|���w�&{��!7^���U��x�O�����aVi��hE�9Y��zN�����
W@�m\�u�'���4h�bLvU��R���'�z���q�>������,+Z*�&�j�*���l��T����S����_���� �M�|�2�C�x=�e����?����1#�����C������]��
#
!9��A��"�����8g��������%G���"�C=�M��T<���w]���~��������R��?Z�T~����Q����Y<g
��6��X��r 6s>�h��p�a�VqRrj''��z�vR6���!�sV����Q�^n���	+J�2���"��1dF;�DL�E��f���I�>#�f�u�o�PL���~c�����D���:,�yZ���y������&��p�!�R�\��~�\��2��1�G��������h���d����O� >p���8^N����Kpn(��A4%�W���n��TU�vX���mI<�R�m����2^��7�����4��BtG��q�P��5�v�.S��+%��%�(�}1�`O���U��~���x|�W��Y9g���s��`�!�wk�%9�c��u���W`VFP7��qf`Z6!a?�h�"����%��E\>}��������"RR��V��b��\����B=�B�	�'1`� �6T�R���+�M����*����P]���0x`<�y��g2�:�K^9:�����y�ia���a����FBt+��*v�ie�G��:��� ����Vv�O+/�c]�`c[O�;p	?
?Y��:!��2���"O 2�o��&���e1��h���!�5uS�_mNe�rr+t..����������b�]�hF�����H�K�D��a�X
w��=�����(��A�b1��gj������fW�B^������ ���|��9�2��8��V��F�<�=�&3qf�J@�`O4�N�`�`?b��	�3�x�L����c�A(���I��!��)��W"q3ly���3�����`4��M>���r��;��|��lsVc�������K]�b�N�4W.�m�?�Wp�������4;1�����f�yc���{�&���{����,Fri��}�N��I���j���n:5��q��;^vzW�d����(	�J���#���.7�(x|����H��d�=����		�l��q�8�!�lQ��W.�
��&���g�fL&�JB6c�R�2���0���G��_���&X���el��M�8Mk���Y�P�]�E��rH�qq�K<��K���`�!E�aT,�e��2Q2JY
��)
�+�d�,y���ir��I�D�2��|~-��e�i��#�J
�I��9�pk��?������#!������t�6���5�c��n������nn�z��^���;�yIz���=p�vFg�����L���o�
���Q7��M-V���r�,�N[P2�����1�HBQ����oX�F�-����#�~������I&��5%������
~CM�n�M�+
����B��!?�Y'4�w�p`�gY?����Bo���Df��[�r&�7]
�������E[�@�L�D5u���1���AZ	��J�}-��|����MJ�H6->H[ �8��>JS�]���|�`#�k�4��=��'�7��J����z�<�lz��i#�\�idD,��M���Z�v�Q8X�"`KM(Ff�BE2���Z({�N�.k�\��b�����r���
Q��j9��n=��}���c�
1z���u��=,3�R���������@j���+�lL�+4��|�c���D����l��J���������O��g�dfM y��%'${�d������'_u��,�&3�z���04=�����/KI�����r�y���3|���y4����\�lX�LmI�!��/Pa
�������.�z��w�����d��������P��n8��mo���������L�3����n����OIEPm5�5l�M�u�d�"d$��`
]IX�������=@�~����gb�����o|]3��������oe�#�!#9�L���d�S��+D�*c_��^H8lV0��c��){ Sq��AL x0���3�0��pN���S4t&�e��Dc>@�,g}�^;��zC�~|nl���pfn��;[�-*J�,O�����iM?K���TfL!�e�.�/YE��A^L�- "h�E�q�
`��,h�0�!��\��j��fb�$� )@���(�Y*��� �g���U�����kwmqG>;����7�_e�5����1��'���y��nZ}����R�:�'�$��3�y��&��4B��w��;������P���5�(?V�-C	�m.`�J3.����V�������M������]��s��*6��$"%����B�2��R@2���,e����2�sY��=����R�L)b���D`��
���e�e���&x�s�r��OGS���;o�`�a1ec|��E���W��,"�V���:���6��^��;V1 �*_Ke�-���YxP���+n��H��%U�:`�4�rf���P��F�
]���nX����2CC0d�c��d�+	�asH�����~&�*/��������>J��$[�Jd;Z����l�]�\�q�b��b�F��7��GZl`���'�%�-4]���2_��f%D�J)�m����|Y��sP�u���y�x������(k)�=����y)�+a�n��.e���a�<�lp!"b4�%=1�8id�OL���2����,;a��O��<O[Aa��#JK]��RiXm��-m%�
JY�/WX�)K���;�-�,0H���
���J,:�b��wrd�*�
"��$g�I`@�L�5!,, �O�l�W�������{�9e�AA6���-e�6j�<����D"�%�����$X�0�G�������jQ�8��32��f&:
���tZ�w�Y�=��ZK9��>�v]�x��"�u�����P�c���c�ie���+��$��3.
2Zf��@��U�(d.�DN�F7���v\�,��\��{���������
���8��NZ�J��h�c�9�P����_�(�@J����F01�Rv�����o������4]-�F�z��3"��������Uy*����"(~�|�/�^{x|��|��AR0����$E&h�}r��d���hr �/)�e�t?���u�����Y�~C�� �M{r��������g�a�o���Q��u�U*�f��l������lZi��.��{�D���"��Z|��(_!9J�
~��U����I[���b�q� �
�qN�f	HA+��0,�dNhb�S���sT����������D�zEf� ����e;���<���U����=��U�n(,c��b����H��M!P�����v�^���e|S���:U*~k���N��6�6��I�Q���	&��1pp�'N�p|������� !�b�!�n���p�CkH��T��5��X�a<���{�YE�]������K1�)�%���v������(�q�M��q�MhYi�
��ip;�D�KG�
!B���"��=�h+�$v�?��x�G��x	�QQ�+����^��9{�������Y���9�W�}-C�7��������	L@�T5�]���tv}u�U��,���,\,|��sN#gQ��%��O�9�h����>i�k����Q�B�Z����8"Z��c���()6e@�����9�����K����o^�����AW(h&I	M������b\���i�x��
�!����3���n��:����^�h���'��O��8�*!9�~�� eA���m�{LF\�0��sc�ya����M�O�9Ad`��@>�t<
I<��J����5�2+z?��T&�Z�������X7���8��r�I*o�LC5�@���/���m��A*�(�WO�.�H�)��r���]@�`<�����A&BW2���e&�M�F����76�����b2	T���w��%���B����x���!�ju���%Z�gH���*���R��I����4���N(�bKB�n����\��}X���((SS�G�'��; 5�J�K���v��)�>0���I��#�F�H�����}/�z/��'_��C�t]�bp�cu	��������
`o����(�G����}�_qI���u�g����,�egy7��?���@��9o	�i��������3H5e/e��I�b����Cr%��|��FUJ`T;Q\��"��W��
�����=�a�^����tLA]y����pu�#�I$B���8����L�f��J�<k���c��
������@-A_����:�}��3B#�yj��>��^��DE��b��EK `�1�6�������x�� ���z	�d���:������y�)1g������`I�^����QC����H��)%�`�;�Y ���n��#��1���)��B���i�\hW�Zn�N�c9�U�7�7e�C�m���
>��)U'a��S��zq}o.������h�c��x?P�~�6)I�D�)���+mUX�������7��B�,�9��*c0	��I�c�>U����A��
Z��MP�R[��@��OZ�~@%+�y);�]�@��7����~����7�S�]�6+�qRs�F�]�P�3(:u�[T��'�ZM���oc#0$$�"
����`!��%���R$*+���0�������~"��o��4�e�c"��kT������,�5Q�S�r����3�`��<$���pC)�U��������T*'��Q�Yoe�]�B��M%���&�:�C�!X�7�9��E�)x%u[�(�p�����������f�������8E�t�!N��R��������K�n
��n4���m�Cw�:�
��(�����O�|�INj�&��5U�c��p��B��U��n^�.! 	s+bO~����O�Hn�KS�e0����LK��#��b��������Xw��%���/>z�q�T�/@��)�\�K(��[�KI������r(Q�zD�`w\�	�������T~��]T$������e����dr.���9�>P��������3�~�=:���I�����d�)1���e����d��l��4_Z��t��M�D=��r&z���S�*������������!j#S�T9%gt����}��WJA-M�\y"��0�
:T-����dY��������~�Z-?#�������ux�}�8��������
������������\E�[�����5��V�7Z������+�������z#�6�JJ!H��3�1&���b��`K��,��{_�+{���������[&�e�3��M���m�T>��."����">�}��v0>9��d�>�b�[�+%��2\���;�1������J�(
��	�$1%���V�������22A���Ok'�v�R9���8ST��qi��.�
_���e�2
��R��hq������D��k!ggT�gH�g�����.�u��m,��P�2	��%; ^���3�5G�VyDI�}l��������]x�����3K��f]�/
�f!\�!k�j��e�9Y�4�t1B�#G@��v<"r�������BF��y�������������1���Z��k7�
�Tt�
�7>���(/$/3� F�dt�� ��AoX�>8l���9}�]J�3mUfX;�R	�K��;����H,��<�.�h��b�y���=���� �s��7$��E��!+T�Y(�Mu�z�1R<g��8Y�(�#J���(���
	l*D��@i���#�;��0��'�]�]�R�����^��1��,|1��/�����p@)�(F)��{���3s�
����~t�����2K����)��7o���;w&^����-L���l�������i��X�c���R"�!Zh��X�o���Cn��l��w`>��s�tl�I���H��������x���=-&E�1bL/����iG:��Zk�9��a�	G���^����g}���  YgRr�:�o[���BLIwK�C���.�R�H?9�����U�����1�ryN1�`������hr�V�����Gg�ng(Z'�
,C���@^E�&����D��(. A��3��!�J%ub��9Z.4�!Q4%���E��rQ�y^G	�EL��juV�����Ta,�������2 ��	|��@`�i��@\������PF���w��t~�=�]4U88���!_��:����b}+S�n��Tt��Z��VkW*�XU���M&�:Jn����Zo�w*������g�qr#�Q����~�e�K>���9�%�~��\g,x�&xD�lI������Z�Z�x����
p�e�B7}(���^�'3�]��n���!+����8Pv��Q�d],(����R� ���!O������T2���,��Wm��#�U�4��f�=�U��-�PI�@�D*���KG�o��1��$���~���:����;�Dt���ywKF�p�IN��K�M��>��j��i������f�����DrY#�=r�>����|�0E�?��_��C�|�[LC��Xa�q������#<���GAa�bRx���pL���'������#��vg��V�fk�4��L���/������Eu�����_��$��#j�����D��qe?�F/p0�b 0�y�v�HZ�������|���&a�m�
r��G�����.N���A@�^�B��}o|�����J�/d�L��a��A��"�����������(�!]��y��*l���|����Q��2��(��)I��'�N/���(O��Pb��k�fK����!aW0Z�"��X������N��F8(����+�8�3<b�������!Lo"Z
&�g�	:�����
�:�2���������L����n^S�Z�}�0z@<�	��Q�C�p@l�e�������}�lV4��2w�GT��D}��j�D
�.:!G�a8����D�55��*�h�]�:.�!H�i6i�o��?���������<@��B������P�`{�����%j��V���B�c~�����a�{1��h%����^�J�x1�d�0���=w�����JD�h4*��k��v��
��m��J��H��p���~���A!����D��A|�,�g$o���
��������������EXj��'�����Jez4mO����P�M���"�Zo��y��2��r�����j���
��p���v��-�$$
����g`vQ�q$q�$��Q#e�2��[��D�i6#
���z[^��y���4>����J~��v�\o�>�
�������(^Eu���|�����r������$Bp�>^�)"�1r�7���N�{~��g?��J��V�0�-�z�p�Z�B��),����<���D���e	k
���NQL���k��S+)�/�y?8�C"�!A�P��=�A��.zW��HU�����%��F��}h4%���)4r����������f!����A#�'s�{����{R���"��K�] �������:��B�`dm��G���dk�z�\���Mu��f�}�~����	�@|���3���12���yD'm��K�T�qpvbiU9d��Ls�wM�e��v��0� ��Ph�'q����C�
��?�al�)@��"��
co:�5��V�R�k��vu��O��
L#wS����Da{��ZU�����������4�'g�$/�4QF�������y���<�d��	i�����k���X%i=O�AB�{�\ky�[@�E�R#�6��g�@�%�om�~��'��'�s�h�4��Vc�pZ�r�*����Y�O;���iK�m���h�5�,
QWY� �"�����	�M+�F����U��C^�`��Tt�qJ!e<��w[Yp������fcR�+�����#��n�A�]X~Gf�E
�X��3�)E�P���3��.+�����#\?����w�S���Z�@����9:�6�2G��/�y���p*�!
w�C�Th`p
����1��:Q��1�h�F�4��c*	����|�X���O��}|�J��}` ���"���fYb�x|F�d�yI�=u0)����M��R����]��N�F
@+&�������l�b�0����4
4��Hf�S���S`>*�f!��2�eV����Of��q:Y�f���91��1�91�y����C=�4������8��x�:�S��0����94��.S<�=�P����#o��V�����������B�w$�YJ�����k��
�&�,4��}E�O���2�)E���mA#���P*���|j�	q����@M�2�&20Q�$El��t3`)X� 5a$N���5,u��T?&�i��~*�f���5L��	E�����Ea�:5�����`�{_�d��yV��da~F�y��?%3�#r�<yO,>=x���Cuy-J��}�x��'MA"EuA��p[v%�O8��?�&��Md�	�\��"� �9�s�Y�h��	Ar
���KP�pP(�C�O�,!�	�c���;G�/�M������������h	�O���d�*3t��h�!���!:��ud'�e��r����o+>4>;��.���w�3f�6�Q$��:����
rU��M�'(F�[�E�t���-\�cr�k(�},�,8Q3�<>�\�8FR�Z�������^��Z���_'�� _K4�J���
L��}8��d����{ULy��'2#Tr�� 3�k���Pc���%�*�����#qQi�7���-����k�f�we��C�A���*I�6^���:��c�?gw��H`e�#��DgtxX���b�y&[r����E<�%������1�%3����`Z�	SD2#)H�p�]<8���,Q��,�U�%?@�[y��.������W��f�e�?:����0Y��]J,!����g*��F��^Cbk3x�O�*�u�s |I�V��%���d�
���]1p`�{����u$
@���]�0?�{�	1�|� �/��J�Z�����F���K3�R�����<�$�^D}�H��u<v1m�����|"��`��
e����:3�M����:;a
+�l����q�����a^��bLF�0@��P�%	�Lh_YJL���ux���1�����a�I3� �4\�� ��;E��/g>��D��U)_~�����t���K�K���6��d�V�y��7T��R���dc�����i$c�����H�p�X<�rO�p*vG�`@ZNQ9H�,��<���.>�HiK(d��g�'R'F���3z�""�U��BP�Z65��g�4���a5�i�z�0���|�0�"�5�d���%��H�w>����PG�|[1���w9V�6��}p�{�1������<�M*��K�mA0�`�T!���}�����|
���� ���������a�Pf�A����>%���C���=����������k#�*��	'I����o����R� �{�������H��v�:����5�aW��h�!f�57AEL����}(� �b���b���K�"�^�#2Q�?@p9HNj�,`��L�3��U�t�!����\����H"1jB��V��;�����1�F���B1��=�f�.�&��3M��jF:jR)}M�X�o�����@��Q��b|
��������(}:@�����xM@�������!-���hKj�[�����-.��D���| �nVD���h���w�m*��K���b.DbKX�� 0�>����DSW�1��:� (YF�Q�k�2���bSE�=k�<X�pa�$_�;��U��6���3�e]%v�E��:��
��Iv����*o"R/r���>��4�|A#
���4_ln�d���Z����l25a�O������Q�;��n������/j��x{n����L9�3���(��O�"Kr��
'�#*��H���"�\'�y�S
PC��c1s%
�33������9�cJ4����!(*��\�U�a-�eS�wh�A��^��������{�\8ixJ/�<�cA��d�<NhK��=\uh�O����p-t/T��t,�Z"���CU?�8�Du,WLC���HbQ�w�y������b-��B����!�����8<_�����4�vy�V�����
�zWy[��*���kc����]�r����9�
HG9)%V*��v�3�*��,p_��T�$@Kz%�
���KC���f��0��bVV()���<j:��}M�3����q��)�t�Bs�n!s�O�@����$�Z!�bRdV�"
�>����;��G���b��s��/z����!���K��}��q�=��f�[�;���~�Qku2��2�����#�<�&\���8xb#����4Yd��G�@^�
�;��F�t�����J�iO�^�����c�����R��9�_�=��Zg�S���
E�`�a��q�/w,�ux���k�	t����|��2L�i����*t�?�H~KU�%z5�GH��*w80q��R	pu�R�D�9���1s.������-���J�^a�w&�W&���l�E;� �=[�X���$��yAV��#�����c�������_���5��(�wf�r@��	}bh��s,�A-����)�i%q���������0��
*nx(���C��$:C�15B2���B�����d��q��b1U�P^�q*[@�(A�q�!;t��b%^�t�p�'u�Y���w��d�d��s�dA���77)[sL6B~�V�����O�N��$��Rzb"h��0�|We�����wgnVo(�����~/{�~�����Z�R����_�z�\��:)���2�|����B��[��L�7����7�������N�Ol���F�!:���c
���q	�'�9��fk�,�e>�=_���/�.f��'�2�O��F��N�n�2������ng�����h+��Fy��p{:r����D����c�|�r!������������8?<w�_������s��E��0��$�Jx��?���X�"��c0K����\�8���M����p9����d�H���0&t~���9�:�����^\�D=�����x���X����G����#��������YwP����m=@iD�l���~�����O�XM����?�pw+*���c�}�)�'�W�-�|�@q�~xr���������s�w}���%��i$Nr�b��['�1&���,!���Gb��M_L��f���NN���N�R���J���@u�@c���Q�v���M��J�G��1���K����2����Fx��He�4��(oxU���*�;V��#�%��s��>��E�
D����U���@�{4���t���/���9f �O(��&J%�@}�a9�G�����r=E��Mj��H]��Cn�{�aI���=h����|�"��U�T�
*���I��QA���E�0px��X|�����y:SHH�Z�Wc�{����~�j��O�9��O�L�B�6A�_�X�Z��"��+�Lg�8+���FmG�\��UK�eq�#��N��&�|���w�X�����'�$++�k�Z�H�Qg���v��Ggo�^���^�>��e��u,�������.�sx�nV�+�����GE���%6��[Go:���E�.y��������w5���9}��E:v!~�������6��oo������Q���[�d�$�w��l*��������"a��m�~����1��1��u{wV~��U(Pee%�@a��
Vp.\
UTK4<��e���F�v^�]�PP9j��)w�� ^�����)��D�o����0Zbj��E����\�����	���Qr7!�������� ����|�K��s�����`>�f����y������i1c7K�Np����Z
�����UPa���9�n�����\��g��cd���o�U�n��N���
�0@�1	P;z�R"�6�k^S�E��O�F)�JlzAz��`����-��3q���m����d.��<-�r�|��-Z�S�v�[�_�Y������9��^�#=�{����*�����5������N�I�(�B��P��U�Ah�d*_�~�xT�#������yg��R���&�SNPL�H��k��4N��$��?��#u��
��+m���o\�����Ss��W���x����q'4K���%
,u��$����hr��R~m:n����iZ��ih��~�������3�����{����9q��,�s��s*�**/��B������8���Yr"	�\HV����9�4��wc���{	�9�y�c��Z�4�'G�Z�v��Q�.oG���X��?-��=$�Gt{�x��s�����=o��d������!d�N�eg~�m�*��?� �`�*&�6�[�iv��D����/I~2i��+i� �-��,��,�$��sW�g�g�����Dsa�.���e����� ���L�~i��+�������X��t���������<d���H���/�Q���7������~���sC5������y�����?��cg�v��_�E�
��{83�����������7��M�*x���� ���L�~i��M�1������3m��A�q�_�]������L�~q������,�%�x&�o~�����
�����O��'��}��9}�Wi��{WD�uk�m�`�����/M������^����CYW������-^����U_�JU(5B��T����Y��?p�s>��.�X�b���o2��������x)�@�����
�]QOTO�I����8�/�H��d����2;������//{��p��9�cX���h��]�����{�u�����S"���3>_cp-.c�E���L=���z�k���W�~�7�f& �K� ��R�eW!���)���Y��r����\F����M���Um<�������l��5U��j����_��Q]��]5&��o��������������A�����_��V�6������J������~=5)��o�2m��_�>m�_M��P��W�6���S�6l?�T������T�
�-���j�U*Y~�WC����G9���!���_�V��~��o��w��LD4�M[����V*^��>O<���z����t4�������H'*Jz<� �z�F<b���(��!��?��^�z��^dE�cka�����wr��`Qd��&�����r���~F���#EG�t%���{�N����F,��2^�\��s��	�Qe\@�������G�x�'�!Y�g%q$�b���(�[��CJ	�Y�}�$XB��# ��-����QH�dA����%P��9�0���-�[pJ���&f�VCC��Id!��$�1�����������.��d��� ����s>��qx���H4�����i!��4��H|�C�
@$!�:CK�Ho��9��>>�\8�M3e��\A�
ss�D��J��T}J�'���E��(G�
C� 9%
4��W���Wz���X4�5{A�w���6���Q����\8�l���B�=�Z��X�W��n���|���O��	#J�c���NNZ�B��@�Yv����n��.L_an�3wL�h��Y���_�_��L���bcPh��-E�-�.������@0���D�@p���[��[lL���up'
�_*y7��-C��	R�V�aNY�P5h��7�Sr��D�*��9�_r�������^��� ���T�!����JFgk����
�����~��]@�W��'5t��
��Q�YC�PC�$�3�D}�
K�2�D��:��F����I�eN����o$�_�9�iY%a�_�����k�k�&���:~���uKl��.�\�|�������M�v�J8@��U`�)Z!���]�������Uw7��)���Y:�;����0{8��(D�����=�f�W�C{8��$w���
��`�<b&�|"W�0&@w���q@����k�j^���U��n��I>jq�V�%i����z@��xXn����i���@OQ"����^�&�$1X������Ou��� ������&g�<@��`!>
<����������bQ�QB�F��_����3T�
�h
�SloF�>�M���$����
b���*����������jw���$�Jc���!8��[�7�7fa�EL��k��p�ad7!�746v���#���s����m9�����������}-�=��>w�O�?;Y�9�W
�5�����f��\X��d�D���^�_����o�Tpj�V�.��s�L�M2����U���3�_����H4D�^��8	����P'{[����X��tW%�^��
_���l�B�E1T�uY� j�!X`
Z���=�
�hx����S���h���S�������5/���e�|O����L��k$�*�z�/�?875�1�����`yv=��Q@�N
2�Eb���=�Z���c(TX`7��>J�"�O����wU��
O]��q�1�Ua��9�}S��*�ZJB�[+'�Wi���!A�c�;B� �b����K8M@�����S��j�P�Q+k{c hF�������V�������.���|YC�UG=�{��;�z������]�8���u��������H�hr�~B�Sw��:
h��jW���@����-rEw��8|Ft��"`�JH_B�rWv�����H�W�B��R:_�O�w�-u������~��������n�y�*Y�a�\fH.��
TL����P��|�0�_��h5�������V�\��-���@Fid#C��;��Ue��>�E�z��T(V~��8�i�W�+���.�S��AK�|YII8q��A��h��k�D���M27����D��V40�i?�e�W�Aa�$���*�����>��a��pT�L���kf81�S���J����=�v�V�x�X{0�
���������D����i��FOvY?f 3�:Nj2^T�������E#�`(]�2��!�I_��D�)��A�Q�c��O��TX�M���{Y���*��PC�i��'1!�t`I��=���'���$}M�����QI�`s���f���K���he��0��cQF2Z����cGM�nT����t�R� &��W�v!���V��BY��V�V��^&����C?�����>��1R��M�S�T>C�}����&"�OH�E ���38!cq� ���5� Y��
:��'[VL>6k�|4����;����$��du,>Q��!K��%�8�j);�%�9V�\p
3�>5���I��5$N�����N�z��YbG��3��<+���g� ������bJ��g�N�R�����s��Il4��\�����AOx���Kaq-Kh<&���x�[�,����(�j�Z�N�v}5k�l>�!�sI��F�9_`]3t�Z	 Y}����u*Q5�k�?�")G�����(m>��t��{�������U��{�����?��o
�V�D��pm�k����!�c���zQ�%q����:`�v/4�bR:���(p?]��T����q�(n[B�����X���$c�,*��,O�C�:J�aG�d$n��m
24YU��(l�����.�q��yfJ:(L��B��%I�g�m�W^�Xo"���/��A����K�BHu?_�oS��7���cWg@�*T(���)�cmf-K�fc*�x�[7�9�@�Y�� ���N;q�.���) ��5n���^XH�_�����J�x9��<�w=�Q.����V:5)1[���:�Ns*�h>s�����������SWh3%�����E^U(���^��1�I��R���](�����l�"#�
�9(�A�Af&��g�QM���W5#SjfJ�'y)X����9j��oq��&�/	w�>T�b�@tO��,��X�����N]��,���C(�RO������9�6CXz96�Vm����h�I}�eh1j]���<�x6��"e���Pz���x]�8��H���KVU��%���jO(e�J�l��.��F��v�x>��4HS!�,T�Q����il�����0��!@��a�#���r����y���
��Qq���|l�Q��Ozc�(i�I��j4x��w����Eo0|4`2jn��������0�i9���Jx��*�`���v�g�<��/Bv��c6��
��a�%Z�0�"D�1�_������	n�O�z��������l�����W2�5���uL��� P1g*{�S:�U�v#���:�8�
j�t���G0�D�����������3�~��$�E��|K�<����1����b�{{e>pRrq�z��Fia�qY�8S�N��=���2y�TL2\���|-�c:�R�9����,��C��G@h�����g��s�7Vl�*�&��`q�;*'���3�����������b���;Y�,��>���5�d�`-��5����x�22��`;����8&`#�W05Q��
�B
�]�5bX��I��N`I���\���I�#<(�@��4zC���)B��/��,��	!Ht��z���l@�r�S��8H��%��\k�>}L���X������Y5��]d^rS�d�B�7�@0"��
�#�.u�u�����l�����r����^������V>����F|�x�q��A��'�D�	p ����1]2t7���L:t��A�F�9<A-�"��I���������L}|��T((&�2��!6�
$���q�NPP��p<bc������Zh��o��e�,��������O��tOPY��+��&�H&A8��4�x�"I��b� �%8�(��3�]������F�m�s>�e������6�OD��T0Q��;��XL6n
��v[
:*���|?L,�������=�?�9	�.f����!����Q�����!u%^���=Hi�[��/����`c��yZ�ub���P�DZF��v���2�:e�/���a�a-}��{�;�Nq�vOZ�vVv@�nC�p���5�1�yU����X���=8���Hl>o`��0�i���
lCY�1�3��H1?x�K���0H�B����xS��1IC&K�8��7.�x����g��$S���Cd@tuY7�%��+�M���`�l�r����V�YH�f r�S)�=� ����V^H�M�d�m�p%6���!$#�B&��[*�]Hj�u _H��Bjj<��n���P�t�q��u�nb'�1S4�k���6'U��"�c���jgx�g��T,��!��������/�H��]h�C�Y%�V�y�i�iE0�%��>%�����y)-K���P���z�������w/�����h���n��E���*Fc�,�ck=��f�����������vC���s�`����[KGq\���o��Lx$�����qN<{���&�4���/�7��/.z�7�����	���Kj+`9�J��M��U��
L�Bd2��5b+�q�Z1K�Ue3����P�t����N�-QZ�<�B�]�����	���;��x���X�����gmYvF_����L�W�rH���|�<O�2��^�Q��T����X}�H�d��;v�����=�
2��z;	@������2E�8��3�f���j@m��T�x���Y����Xz��RXPv�/����$_�<��{[O�=qoYG���P�]���Sx��p�2���%�c�sjW6�L+�C7]�Ze�������?���4��'��5����+Q��L��.����9ME��RQE*
��-2q��8*��0�L.g:��19M�S������	��9�R!�����2�l�*�5�W�; b����
/R>�$�^���E|����������PF���#UdfX�������&g��	R/�U�i�Zf������]�����S�N����/�� ����e*��*��)���d0��
����J7y�IR&�|g4;d�,��4��rB���29_��3p�xX5�w��Y��N2��y�Q�Ht�Q��pl-�?�k�Pf��Z����%	�o�4����z��q�4�<�!���uS��ls*r6}+��vV7Q8�~
V#.o�gr8���$�c�-��(/�T����>N�q����Q2B��I.���{v!�6�����I@�c?:��Y�����(�d�����v��#�Uw�Yj�)�_O��}��l�4��Q�EP%�a����FE��������we�����"�7�NL������>82Y����2��+�ZSp��F]�u�ev�ni�n����O���n��!.��o�"+%����0�h�����������9$�F���,i���	���8v�>X� l�l���L�S��S��78����L-������)U?��
Q����jT��<0������(~�)P:��i���H���c��o�7��0�S���U����Y��%�������;����(|��H4+�*�m�R�
��5#C��+"i�)�9�6����7��\�I��IWf0�%�H����-'�������<6�h/�|��;�G�.�X��Yd��#uV����Mg�3H����[�[{������t���������,�d���yu}}��1�WaXq�p�������	�S�Z!1}(��U�]���C�I�h���s
A�F�(&��������/�_S�U�0e�r+�]�uZ�Pr�����WL+.�����4����%g4�1�!���t(R:�G�9���I�����7���������7\�������T���3��_�A	e�!&��kfu��b�P����
C�vPtS���W:�!X1�k�{=��'�����T��vb';-*�\A=��o�?I�2	��k��Q��2
��r�1�_��n�Zg�Gw��.An�\��e70Wf��zfQ"_�������B���0����������R�)�/�L8�&Wf<�o��t���]��59���HZtQ�*H�D+������aE�p�d�;� S��u	�~I�`_�<��V��U�&�S_f2����L�~���`?���"�B�U��$:P�d0����z�Sq�c?��B�i��P��������
���+��Q�L��fN]al���PP���p��ZnwLl��S�s��w�}��d���e�2�I9I���Y���#�V�`����)�xkN��W����>5�I��(��s�K�5Y����+������sd�P��/��[�U_B,��I�TG������{����x�Nj*�k���J���y*-���Ugg���)���uj�S�W"?^�V�G�r�������n(��!�
����3�?of��`�o�KfY�p-�z��5�8Q���Y���*��b_�ta�\|5$�9�e���o��A>�o��G*$n����~���g�v��-R�G:�0;���p�D3�����BG�u���}�������t�o��?o�i^�~�@gJ�6�*�dK_	��j�KK[K��J1m}a�K��8��Z���
9(W�����P��:�u�M4��I��dh�j$��p��Q����]�'������L��8���i�� ��;qK3�����,^R%�\���y}Bf�4���gc�K���;��^8�C`"����C�c��m���i�!3���4�l�~� ���A����{�5�T�s�_��\��5��l���ukU�
h�<�p��M�z�������z��o�c8�:�K��oj7�
�Bh�N�Y��)����F�-+����eEw�[XVl�J��9O�V��G_m2�������L��d6!D����������>�z��<d���
�.����?o��t��RCa))��8�zI�y�J�� ��3��!�3��~C�5��
�K4
Px�K����r{;0����}d��61b�7dg��Yf|��b1[K��Fg�9)��
�Y#!%T�:)~@��&
.����Q<yJ�CU��Y���^��%01�Jj�>�SE�����M:Im��j����;�-�e�;��N��f���P�����W�"R&�'-����k<���%��fyJ������U�+?�D�v��%����S�������v�]=|f���`���Ew�6�
��L��[�I��M����������7����R���We�ar����Aw�V��8E�����G��L��F)!��(Q���&�@����m�4M3���X�nW�����q��*�.�d��	����dq�0�:���&b�x'4��'Nk�
�TC05��S(z�?�m;���~�j�9�������e$tE)2@�B^�b2(�p�B�M�y���~�����|����Vouf|����
f�,��2���
�,-�j{~������6Z�3��-8�X�����:K�LPA��1\4����L�Hk}nr��X����)�4���7���!c�������]x3��r01O�L+iO�o}���Q��QL	q�Z�+!>E~D:������6C1�sz���`����~�����r[|�v.���z��v�PY����~H��j
*�[pY�QF����V|*S�}o8z��]��w�<-y�����!�� ����BL�?��K,(�}��92����,K����)���n�� ��h9{�$u�G��R���*�i�Uf�D�@3O&�iR�4���"����T�I�v+��|�P���	��:m�R�����*�����,^d*�F���9'/�KS�������qf+���.�X:2��!�a"$�4H��>���8�;Tf�Ar���E���|.��R�Z��3�#n(�X�a���a&t7`�Gf������n��M�V��`�����r�y?�����H����<������0X�w��b42���v�~c�(C>eI������d�]O��?��&����j�j���m~�T�F����������!4+!������3��Jj���+��]�B)�)����l��������F�4�`��BB3�����K��+��"��W@�zq��b���D�{8�v.��L����r6<��cx����gf6���=���������(�NM��������f����f�����^��G�*!����W0h�������W��P�����P�%�e�������������h`�e�ye��v.����S��VT������zVh0��b�f5��o:�B]v?�m���m�8�LSN>�����}�G�����v�����B��Y��,~yB�B���Vz�,vr�R~��."��.V
�yb�i�:67Lg���y�e
>U��xi����-f���'������r�����(���tf�Y���H�}$����
���c$S
���dy��u��d4�q1�l4�O�|���E�S:����@�"�*i|�1��'bS�~C�
tx�It$V�2
?���.��J0
i����|���.�>����2w��_���^-u-:)��@v�
�@i�}K��yN��#2��������j�P����8pH�
��Y\G��
���#s$B�D%6�UF;�`V:7����4��2c	���F�)�}�����M��TI%�|��}�t�Nj�>	���I�;��?grD�?����@��2^B�������1��;���i�����e/���F��e��8i�Ky���,�����yS�1A��QQ��3�	�R��`�l�>����d8���Kl���-�|���j&�������Pg��V*������o��:�\C��<� ��~4�;�)���A�Fz�I��X#��]r�7�e��4>��$��[�5q���j��@X�������dd�2��,Jb�T�������F4��A�!"�DU������+%�e��� i��'�r���*WB��b�M�k��~�E��K(}���zR�y)g��M���mh�
I���<q���N�&
���9�uy��^8�����Vp�� ���dd�-[��.66�b#��,v��@��m��D���Bs�jNZ�i��(�?U�a��[XU%d4����t�D�����6������u�'��'�0��iZY�b��1_�N����z1{Z���,p)����*�9����6��;dHN���(�y3����8n����hM*����~�9��)$���~����0cjG���2Ge6v,E��/����/����0�=�%����j����iMh|����7j�r[4�I��COGK�Qx��}�6�f�B"��dc0U^����/i���F%d�)h�|!���b�?��:	�NO�L)e/����#��S�#!���D�����
OZ�5tx����cCs?'y
r!T�f�X��~������1n-�R!�=��@���d@Y�F�P�3��>\���?�xv9@�����<9�mPP2���)�����N_���3����106.':�L�H}��f&	{����s4�$i���e�B����������y���z�~��6o�al��o��gT���o��Ud���&�)Z��|��������{_�����|5����0}��,�H���F>'i��\�]��@��o�8���~�W�� u#����1<R�CAO�#�)D�J���P�.��{�?�\�9�6������T�$dS��?(+������C;�=�����\���?Ygz[���>��!�}
:*c\)]����y/la��6��tS�za�'��~A�f��G����*����r]��Hu�M<.���B��8I
��"��TUa@`�� 9dA��<����d�)��M,������^��3�h��J�����|������������pg���]���f��:>jx��J��{G��f���~K��p�dv�'�y2��.K���U��h������g_�����*����������B��J�P|�)�w�,9R���,�9o�n�A��x��>a���d���$7���wx�H�A@d�o�L|��?�FH��d��0e��7��e��t'���i�h�,�����6�i�Q�4'n�Y��$���F�V�P�(
m������ G��`��)����~�����F?u�7������
pb�"��hH8��6�]x���`VM*��	��5��*J����g������@R�=����v����,���`$\���`�����������\H��k%�����"r���,�b1=jL������T�U����ns'�P�6��*�bQo	���LB:���:�������3�1�b�7C�
.=���	����F	5O��n&�vW����l�/�mMP��;}r������y��l���e�������/�s�s}K�z����eUb��/��A&ba�����(��!n��������AR�J�I0����q�)����Xl��q�(dQJO��R�����j�)6|�A���Q���w��p��8@@�u<-���_��
����ntKK
<����i�	�����"�;��M��\���bud��X���v��.��]/��R'�y
��V�E7!Jg�A9�aR1(3�&��qW���WU�v��(��i��eU��������������V���.�������}�UE��k.H072�M���'}W���z_�*^�+�(��0F��4�a�$I����8D�M��~��X6s�(�%�j��}g��4������;�	�e+-��ce0[�0����t��V��`��^6������q\����lH7���Y������F9
n�^�Xg�y���S����N�{�����,?�J�b���f�c���%,�D�H�s�<�$�Xlk�9���KwO��dK�w�(�3}��������X+F�����~�r���P���������p���
�o���C,/���x���e�
��3�/�&��J�����e��
>#Lr{���a������w�:#FP{K������c�����}�R~,YmV+�Zc��������aU�4��Wt\+�L�_��W��,����e��e,����M��������xp�l0�?v;�m<����7���/�A�X�p?�"����^���>���1�M�����^�9���i��s�����.��?�J�_��_��.�i�X��&����x:�s����qsPq���p�uKn���}3�%�I�`��b�%T�?�8":���)P\d��7~gN~DwZX�~�M��>���\��.r���n���}�o]\�c/a)���Z�`j��	,�iXH$VH>�e�����~�W=h�s`���fb��n\��?��0�{��)#M#�9�~�	kV!�\4��km��p�r�zu��wS�`1$�]�V���Z>C�v�	k���_���2#cK@��D:p�1����� ��P�t��H����s<���=N������@����d��Y�J�j�A��`�3�!�f>e &Y&oR��q��!W�xd�=b���$��xS�rM��7� ���s6����D��p�(��u_�(\���e��AC����
�r�qg�K�%F��7����B��
�A����5b��n���~7S�����eW�X�+����)a3}2�g3
��p��SJ��� ��
�(���t	d�[���
S6��
���{���!����4�w;*��.�"��-����J#�5S�Q�v�v��}|��WAV�d���&�������x�!h���(GO�8��o�'@H#G������wz����]�F��S
T"58e���2���>�v�k�n�*M���t�,i�H��%�������q�^JC��W��_�
�As8��~��g���6 ����|��8�~�kc��n���uvtG����)0-��\�
��|��u�}i)p�>}���!)����uv~���Hm�sW��	�a�m�U�q��t��re��JJ'��J���V��C�I8N���Y�������r�w1wI��1L�A�BU>�:�2*a.:�]�SK-��W�+��	�X	��$%wf�Dp�^��a������4�����*N�<��������@3[5��I�>	��H�bIOU���|�����]FS��R&5�[�2�>���;��
(|&�;�c�]�0(����>F0-���_
o�;�I���"�g�=D�i�+M�o�[�hl������5C:��z����(���@�+?G�C�Q��[� �~��PQ������^F�"sRCx�V��0��}�];��#C��(>���l��5�J�����s�����AN,��ajb�+px�drYmGQ�Lz�]��uW7�J&�M��������;�T)�w7B�?�,S���F����^�&����:)ho�Y�JE�<�vx�5F�p��f�_o!�}�o�4�L�7������Y��!����qx	H�e�?��k�V���Hv:��c��9=q�9^{��8�������o�������K9���\����m�����<�
�d���c�j������6������>��{�z3r���q\�7J�r�w�Jm\(��Z�+6*U�IIJC�!�7���M������x�&�D���e�z~��<�v��s�����I������������S$Qt?p~�1 I><��*����,���m�O�k����a�$M������K[������+�l��Ya���6�$Y)5���ZY�G�R��U�IXoj-����H.�D�����!���up����k#|�+8�����M�=����[��p�M��	B�|=[Q����������7�n��dR�
`��T�7�N��''��v[?���b�=}����fV�M�sE���l�9�::�_���CE��VP��Hd-u���M�����'^;����VH3E�c3[	i�`+#�lE������ ?%��zH�Q!R$�5<�����e�J�,&�����`T��P�����-��t�����
�`A(oB��;� mD=~�w�T(5
��������� �P��T�<+��|k4�#1�s�T���`R^X5.�����"e��������r�\.;n�^+��U��N�T*�J��)>����m�a(���lz��q�������u�U�����ry\ox�f�8�K�b}X-
��_����;tNA�����;��s��)�r�b3���l5��f��y�w���@Z���^�
z��^NrN�9��"�Sl>�6����~����z�w8{�;?_�.�8��n�WJ��X)BAE.�����(JUk���pJ�L���d�8��s9��%xw�a�v��9G�V
�I���E���1W�]�n����_���z�wK���rWf}�U;�J#mph�������<���&�������0\�7�HYwT��^���q�#��(���<�\ut#���/��:�)��*�x#P��6�C3��v��vW(b%yLl"V�T���� �������r�T1�-�w��&N���z�s�v:�J�v���A��UA@5��9�K�0k����	2��]�K���v9?�./���4/��������P:|vK��4�H�k�[|`~'��j��x<������������m����{�X`�����zu���@"�]V�k��`�����i���������m��S>��������}����x��I���6t.e��-���k�^�F���7�����H����f���?=?�:i��kX�H����W��Z�;_g~���K�v/��q����v�J�E�#��5j����a
��H�&��]^��e���9Vb#B����p�B�n���5��48W�v���mG�����c(��v�������2���S��
fi�������o�����d	
�W`w5_��qq8
��z�z�,a�\��5�F�C*Jdb2��\�p���E����������_,��A��)�/�������/EP����
��U�1P0��VViIFas����[r��(��3�;-�S������IASf�t�1��up�Z=GU���9�_
��=Xu_q�� 3���p���3�s�
o~�J��L��� �H�E��_��`��G��>�F��v�����=r���zS��A�+������PFu���>1�� X��H�����/�����2@�N&��:�Y���o�w2@�$�E�C6__�vq�0��J��qp@"v�Ay��5-L��[cX�����(�mq>�m�T8}����H�U�����
��d����v���x���n�����������s�;g������	>^��;8��"
f������P���C����s2O�3�)�v����\|��)���������D�X�THs.%�f�`��^��;g���`E&/m0���7�`P��{o9�@a��.�0$:���t�#�?�	����$mH��4�����-���!������(�����;q%6>mV/X�e�B,!���Y�!��8�y!�*<@!��������+0��TN���0k�����%n'��d��Je�����}L��u���@�xe%B�2�	{����Du
jf�*_'�p����9���L�����;g��9�:��c-1^��tz�t���G� b�������W���%1,�5�e(H��dU����}�o��������F��K��;z�Br����m���}$z���O���B5�{H����H��}��}(�Aa��^��-i�hX���Z��$'C���",�?MO&B���������L�����������x1s�O<'N%w���7������I�g����� PRv~��z��v���3N��NzP�py�=�9�5y��]v;�������T�M.Z�o�Z����I���
�l��?�+A �B>�?���p���N�O ����gJ6��o�;��~�~25(���m��9��~���#���S}�7K��:o��E���{�UkQ7�D��W��w��g����QKsO���T�,���h(�-&1��[4��A�:4�_��1�$Mzph/)5��-�����8��t��`��Zt�!pV�P�l/7`������-}�)���W�:��~�f�!��X����q�	�h��H+�������]B1Su'���E�����y7���Z2qc,ud���c�nOO]�{b�Z�J������
0~�JGbDw�
't�^9/^pXt��g~��������W�]|��1i�/���Vs����B�wi�����8`�1�Z$����,�<����R��HM�/�aEy��W}6z5�y���V�j	���D������9�;�	���<�������-������'6�9Fl�Sz���kMr����Q<$+�}-��g�M_n)y��;g���?��4����������N�{�K~����w2��|:�U��CRs�f���w����3_'����D
�����D��x)(��
`�������^�T_�����(^<�9��Lnw�h��i	��90�=�u��.��Dt:�;��D�P���Hx�wX�������Q���y����F3�(/-(�3��I� 0������`PX
%W�\:�C�`��;n��I-��/-B�B2�;����������m�\���N��C��q�:#hU$����Sgc�aW0P/nm=-A:X5����`&���y�(�6�[e��#	����[&w���5������/R�h��g�&�]��p�u;��^����Law
��}��(��@:}|�����/�>|���(g\!��D�� ��H������!?���m��q�����E����XN���O��Q�@4���[P��@5��s���"���s�3<�l������va���{�kG!UKrtB���"f_�~@���k����,�9�s����aB1�n����vA����1���Nc���ce>�#���^��t?�CgT�CtC��,�|_��(������"�U��x�-_\\�Tv���3�,��-:���������4gR
_�a��rig��IAr��1�g�l��F_#2�~Sl��{����Z���\J��PiQ.��a�T1���~��&�e���r�<T�f4�e�3�|��S.e�}�GO�W���Qc�%U�4S���	^�M!)�E]��x���A��N�/#�"���F��Y$������Z�^$@,.����u_�;=�}u�:9���B�5�8C}�)\�<�1�5��aA�8�i�M>���&�����&�N�Rs���,�h%���N���R���A�]!����F��t:f[�(�
O\�������E��o��6�[x�����e�8&��K�ra�fX^�:��<����w���,�W�������
��[�`!�t_|��$�a{Q��v�����t�6�/�4���D8���uu�%|Y�������5�'K(*���g�BO)>����]0M�U��'���@-\q���6�(�$P�n;l�)t~��������/�.b�U49y�grxB�M��WD����XO�h�E����|%���g>�B�"����R����yHE
^��w:X0c��bdED�6���V"�b9�1���h7��
���C�B�I��"45[C���;T#H�3������x���;f0�&x�w*m S�o!uD��f�#��7�(��N��=��8ZD�L����q���;' i2��i� ���iGE_)�<��<X�N+�������*h:Y�����6�E$m� �=V�&y�\@.�a�v$�j��JK�����)c�	E2f�������<�_x�eo=!�F!��h%x1���������x(�b��<�wpx�+���9'?���Lv>��NT��bC�4s.:G��K��:P���`.H�����i�Ks9A�_8"&g����(y�����;_w��S����{Q��n�T�i����	oS�����	�@�`��#Y>�K��>6����&y
����R�Vq�	l;QK�zu'H�4����(����R����Hk�I�Z.�@��������8��H��j�/e���kN���SQ���$������?�bI�fP����1�LV�5Z�w�j.�H@� �0����U�}�6�9����@�������WF���:�(�"q�	���9��0U2��~�i����44���������	K��d$�~pb��QmX�Phy@�L��r�0Q�O&rd�)��<i�xpH�����m����/�^���A������ZeT/�pw,������XPSd���a����2�
�����n�/�o��!b<�z:A����(l+��N	���\�������W�W�������a��:"���)3U~����A�~����|���[��b���Z������C��C�o����+�����:���q�Q���hP.�@3~�������7��X�H������Y/����rw+� F�"W�a[��N�j����d�g����wO.8���|DVf����#��8��{,8B�7����0��YX,8���/��~{��x�N��Z[��O_#��z��t��������.�m1/�����q����wR�M�|h��z���?���"M�.|���p
��	� ���H��H�)8=j���v'�����=���KQW�D� ���-�a1�Q��08
�
���_��o���z�����#��������(����G�`�&���s�Z-�����QN���g�����}Q�:��O��%��>[~���\��.�}l������.������\_���7���7����~��iy{��t/�?����:�yJ5������K�,/mhD,UY����4�u�	�;������L����1�Y�rYb��p���
7�
�D��H�l����N�Z48U�-s�z���TlF��sH�h;������>���-V|�eAh��U�����
��?(�+�z9���E�!?�Ea��R�L-U��3����_�/Q��W��YQ�rJO��2%�);)Z��+�������s����@$J-HpP���Ijcn��T��+���O�[Hj����3
5)��H��:`�J��O :�f���K��(��-f�����!iOLf��/R�����)8�)\v��iM����!��L%|%�C�*���_����B;�}�.�i��,��N@7b<O��}{�e��i��=A�"s
�<g��-���{����-�:����DT	�z��z��}S_�zo�Y�?�\����"������	&9 	���]��nu{6[V'\'65(6���R�����Y
������(����E�w�m$�%��u��D.��"��-�
�w��t1LF/�Y��U >�>��+9���)Y�OE���c�_��C��8��T��LS6��n����X*�*n�Ag��k�x2��q�(��8�f���K�G-���3Uk���;77��z���-p����@~�0�0������I�s`/sF
k�a�WF�Bu\/��c��0/^����%����rnV
����M&?L_���`2�L'��[oa_U��{�����4���������G�������1����%��S��\7%S��@l1}
�{������B�����0*�r��9rD1y���k#�8��D�;���h�q�.�:�K�#���p��:J�|���i�H��7��W�q�8�7(�K�B8�Dr�:��-a@lx��.�*"�r���3S,���o������z�I�y2���w�0;��GZ���C�?�3���@-,���?xSf�J��}��2*�?w�;;�c x���b���.����B>q74R~�
�����]��M�w��MD����	��wD^�;Sj����P����|3���h(H�=�)u��W0>��J-���F����4�����G�+�z!nk4�=�[���I�dD4��;�L�eqH�}��(j6��w9���p9J��N(��
��� �;���+�N�J������f��=��I�F:��n�J�5�TArM�U�_E�I)���0��0���}����5���J�V;lj%���f�1�9�T2�b�0E�d���D.�B���JlYp��h����V���������i��-�`3�����M��5�[���d;"�Oo����q�R|P-��������5�$�_�i�)�#2�w�Bp%�W��G��ZdwcP�6�&�@I�A�n��w�F���(���[�5	�Vp�bB�������pl|b5�;�V$s��q��@E����w��`�y���
�,��p���r���D^��~uZd��P�<��j��x�#A�t"�/���thl@���t.���"���_���e ��3l�FRW��yP�/��h��������u�6��I�:��?c^c���M��P����>��"��S"�q#{�����+n.��C�|��*x	��0%����-�oa�����	t�0IJ��@�H1�#��t2������SD����$,�����:���KUG����Z�����8l���+�i�.G���Ai�|=R�x�f�
�
In&<�q�������z+��C�!\/��;R�����t�q���Y4@QN�YBg[n���Y:0��~�Vc���24�XT��]J<�q��?����^ ���Rh��@�Iq#K(�i�$t��4Z8�REa�]��ht�Z���$�����u�q ��N��Uj�cZT�Z;�#���EgL�����	�b��5�w�N�i����������%*�9W�LQ� }�	6���0������\0��/A����s=�5�e����X?k�kGs���4��q�U�*��o��q���#����MT���F3W�n����������a��S5��6@�8ol���k�@0\d�v E��E������'H}<\��Dv����>�����j� /��?���(?�X �&��K7�&�5���b���4������������b`/5�����a
o����QU�n����M3n,�I�):�i�\{K ���a;o1��H�q�]�����k�`�' '�B��UX���Or��O������tE�M��&;Nj7���%����H*0�����\�(��|Q�dVLc+�e�43��KZ������j�����Ba8����0��N27]����"�~��	F�������n��
)r��+Y ��JWx�96�FqM����hp���'LT��2�E�A�$S�RxL��l�}�bC�k���s�n���?n�M^�FQ�=��@�_yN�B��\��{S����:��T7
�C�6��6�pm���?um�^�zm�@��NF�W������)�\�:�]
,P)����`,��/��;_�����
Fo�-
me�Yq��J�f�?��#=�m��#�)?�X���q��j��
��g���b��O|`��f���9�������f-_*�\�mL9KK���;��v�j6!���>9~F<�x��7�^}Ph?�����C���L���7�{x�D�}��[:�	��+����
*�&��8g���n���'^�
<�Xf��i7������T�D������8�d@AO��0/���5s��e&?�C=D2���F�1���n+b�}�1�A��y{$,h����l���;�%�M��#��p�L�DAb=�dq��| �z @�-���be8g0Y�^��(��v��b�k)�B�R1D��J�SK�,�o+E����]D����d�F��R���XD��j���V�����u�"m��8��<��7r���4������rd�o��e���!���d&v���KC'���j���P����W-���xh)�r��E�
g�<BlFQD���s���[������n������C�_5���G~�YkTn�QW�M���A�X�����\������U#�/�����X��X��S�Q�����Wp��7�$>���pmH�)�ii�<[zl��/�e���&|t�+W�&X/��1z�����#x\�[-��j�0j�U�9,��#�����2����
|]�)&�7 �����V�R� ����8�:U
��0�=�.�#*�V���S�Q"C��#�DL�
0�c�)�G�C��O��V�"#�gcF*_�,�d����4\�������(���0c��A!�w����+�^�1�Vo7�X'���c���svY.e�rO[L��J]q1�%��r�����vGD������ yu�p��_b&����>��"h
[�g��o
����Q&��z��s��8��:'������`���:�'=BU'����D,�L���U!�p\��������{��u^�0��G@q����:��/Z��i����S�5\9Z���5�z�n>cb{�}���oC�C�������8R!^���%��&]�VL�g��g�����������8��^A��������s�y�O�M{Nc�t_������������J��hr�]<�N��k�?�����l�p�0|	�����'��cv�=�oMU��cfO�Yxl� �Q�>��`z��Co9�(A
U1����E^������~��wr���mF-���������9�
���/a���i+��s��F�;t/1S�Nn�Ux2�,4HC?����'���mvWg�G�+�L�I9"�&�a�G)��D����b�����GEnHB�p��N3*�Y��!��5�N�l��Fjt��Z;.\�?�6�#*FTAZ�L�l��E���K�Q����3D���P�f��\�S���;��������������rS:�D
�����V�J}=��'��������a���>e��xn��/�<��7Xdcf����Q�/-�'���wHVP_;� '�$`�����c�!o�
I�[����$\O�0<:�������TKL��J3����{��H�����1fS�����l�C�IWh�diNj*��(��������o������I��O�����?]�q3b2���^�oU���d�X�H�������^8�6_����E�"�<#T�)��iEy�;S� @�e&��Ea_u0*�*���/��>�H�*�l�����z���E��kF=�Res%�l� �$G	c��7����7 2���`�2^<��AFX���hr2[��7n R�|<����>�*N�����:Dr�32p�J��J��Eh�R�{� �{:��ST&�@n8�6/�x����IF �#1c|n�E��L��5v��8�cGp��' ����?C�������7~^���OnaZx�E{5?�RPX�����<P��]!h����r���U��_��?����ai0�x~s\-UF�J�8*��^����cw���r�-�I��:��.)0a��J)��X�V���L�������_�q�Sk�4%}�����.��Eb����r�F�����m�$J+�I������XjR�
�06$�B�
����A H�J�_H�������vd�
��4�CT�Uht��uj��<n�^�iKM�x,@������5����^�O��WH��A���7�z�\�{n�P��*�J���[
��QCA���T�R�7����WG�n�_@"U���qw�a��jBQ�~AfX����zq��P~}�/'���1�gb��W��e�^l*��QM�k�rqX��jl�$BiFJ���s,�<A��)�2��������g[�>C�7��3���R��ze��GH��Se���mIZ�� ��{�;&�0y����DI�e
�=���������,��A�F�J9�����|�q��P��������_g�"Y��m��l��taZ�{xq���u�s��p�G���g���#hF�����h��3��_u�=aD-ct�����/��#�<%�)s����3������-,�������r���#�mN��c{ M>w����������,N���|�������&?��o�[�<��������[=k�Q�_���9�]b����1�����������%����XiZ�x���Qz��o��b�a��J���}C�r�c>Z*�|��8����6�s���0��E�(iP����j{�q�L�+�t��x	��%Gv
�L�M,�����&LR �?��K�{~��`���<�:r�b 3��y�4��r�Y����	Z�'��!3�X�1(X��1��[�?L]g+���CS+EM�m��~����U����0���'v�1��`5��)��"�#��M�������g"�j�������
��d,���4n���E��/����%v�5^���~�@��u7��a#Q���;c���'�������_�&��7�&X.��Z{��.����i-h�`��,�)$"���������*6L�"�T%na��(��DXh��
��v2:����>B���W�|���+�nh�7�X�?���^c��_#Nf�%��!�����>Rm�!�c���YR��h��!�_��_���M��L�}������"��q�U����)t�G�5T�`G@K��8��2�%T�yi8{��A��F�>'Q�*�o��f�-�xt��Oy��`��yG������
t���T��Z����f�e��l�U���n����_a���90���$��
o������2;_�����^:?��rp�.�q��U4��-�Z���N~�5�&p�:�T��U[��w}
���;��y�p���$��-���D��45�T�C^bs��D�S������
���EDiC
�b	_��	��8Y�4���K�M e{�nq�c����Ms
�����0_^�,�V��Tm�l��"}�ZJ��Jm�u�������������L���C2���D~B[���1?F���B���1��x�s�+��N��B���a�1�4/�'Z\i�.��4[#�����L=�:�{�R��V��lL,$�XC|%�#�h�^��y]���FZd�M
2�hS�{)kUT(��@A�����'�%�c�������`���z��_y~73�E#�tcy���
�
z	���A�>~�����T��~���������R^�F
a�g0�F��/���f��r�Ec�����
��K�w�u��[���D"����	c���l�`��(�?���������VqRf�v������|$�M8��F�t
���+f��<�sj��bN����W�n��}	Uu����s���M]���)�� ����ci��TL&�����#j�I�����S�c�\���K�%�-�J;��G2Y:M��:��bk�=+D����Ki��SvO�i�pH�-7�;��3xy�Q�h=[���#_���`�:UE>T-�As<wb��Q ����>1E"�r�{�T�������r�I$/me�E�ht��ZPq6��#K�C#��5n���
�b��$C>&�����[�x�>wA%��U���Y���s��e_l#���Pc��p]��ZD�65��5tN�w��%�RU����)�w� -�r��{���K���	��Lq`���9�hQ��,%P�h��SX[�NF�T�rZ5�^[TS�M5���[���(��
P��Q�9-u"���'�N��}N�e������V� �������|'���,��~�Mo�������:�$Z�i����j1�;%��m*�R���"q('��}5��p��!������(�x,
Dm�R��p���n�g�*�(py��E�Ga]f�z*a��~t	s����~!�8�1�Dy� �����hp?s�� �����BC1xBeD0�OR��"�Q�}�TEp�r��K��DK8�S�rG�	g�G� �-���H�' �D�Z�YK1�\d�
�r^����@a;`�(�.����3��Z��u�sE���0PcG�Y6�6l�6>�����+�h�Xj���F��������y������?~�^s�
x������}D���0a|�$�/�~�2Q�;)11@����B ~�!�*��r4S+�_���d7�D�S�j^����n��������&j!��
�"���q���m�4Y��������	�gU�4��Rf��=����"Y�$q��������5�����N�*��+]�F��x�57��������������/�Q��%r"a�)�!��*����FZ7/Y�)O-��v�G�0�(��q�2�|��I�����TCj��?@� �Z���gC1���OiL��Ilq&����
�k\4`��{.�,���)�g�����5�L��
�o����M�ck��B	Wh*��r�Gf&�2�h����Y��-�,?M$�������~+��&���`��7|�����,����2�*��r�y�\7*gX�#e�z�'�v����XR�^}�EtaFc��5�c���B��X��B}�jK1�2C=����r�H����}/ RB7����E�m(����s�9I��q��99f!�L������r��PN�/U�b�I������	�����J������B� �j�1_]�G3���C�;S_�]K���6Go��<U|d��]P�*�
�d�M.|�s��o�S_4�v2�0@�B��3C��"�Q]��D�(`&TnG�)�u��^.��r����R��HQw|��Pbks`�&+��\@Y�d�\���/��IsEy�����u���8�K
��f��W/�
��K~�Bvo�P�r��!)8p#�[���j�����Y�#�"�N>�Y��s��s���)��qicM`*�l0�	�'���(�	d����wX����b�{!�}+�%���,��~�719��"�����B�#�^(i���iI���y?x7��A�����,#+Xs���r=S:�PF�Y��D�.X��p�S��8�`�����DJ&�
��
���:�����eG�M��{��6���&�oY�NA,�CG�jA4�Lm/}����x1�{���9Z���9�Z�-mW��7#��W.���	$��N�����[���N��"���y��	�N_G�!�@A!,
��(r�h�����NX��s�	5}6@���6�1�������j:�M�Ph���qb���fB]��&B�;�,cht�����K�:�Ax��oH�f�Yg4���$g�Lno���n����������_;�$�#`���8
L����Q`D}��7xF@p5�
�^J�
�����q�?��`����]��AJ
Ld�! D�4iBTo����������dZh�X������F�
������bc���>����{�G��j�k��H�&�6������&���~@��w��P-p~yQ^*��.]�����.z*V���G��������#/D�G_����6k�"�R�����7��;�y�s��*�:I$���Z)����^~�":��v���S[1���p�.����{�D1z�4�K;�B�a�! ����e�V%o�=�i
<��g�a��'��?�0�)��N���);�y3�]-���D����g1��0���.'�b�RZ�`�1���/�����Nb7]vA��U�4�������n���������P��K�W��c8�$��S�1����U �BH$����7)�@�&��-���O��4
�)J:�H��e�ll�������m��I������:	�,��P�4�����.`��i�#����7�,��Oo�;�C���F��&�"�g�it�E���i�3�I��'x���\��B����Nk����h�W����%��������1������f��/�����?bC��6�x��YX?~@�+17�F���;i��+^ ��&��y�"����:�$��P��1F����V5�����m�q����&2�G^��B����sf>�
O���G�N.�=G,�0�x����EA�c�MXV8-]HsI]T)�(�c��w�`��������T
�������k�1��c����b�M�F����pE���Dx��7�\��,X�E�9Q�hc�� �����-f��jC��c���3t'4*�N����|h��������_>�I�U�q��
k<KG����+Q���a�U�����2P@Fq�"l�U�}me�(�����MM�I��������'�h%6���7?�����0[���MT`XDd�J�������J�ZG�����\��������&/�bt0��p���!���E�h]��Q������V7*Dvg����8�tMwJ�$[����q��M���Nh���[�K�
����n%���0+��{�*i�4V�y�	��m��-�Q��:C��'���>�J���>W�z����U7q����3�8)PED������0qN�i�����Q������W��S��B�#~�Jd���P��i;���L���O�'����(�7K���Hlr���A�M���'�CJ�y��2�O$����
o)�QtzP
@����|��M��Tg��Rm��D9�c1g��v_������e�5�Z�Jm&`�0	�� w�H��tQ�'��d ����)� MF��+��U��L��}�����T8�7���l���\����[*�[���1��4�%`�[J�9��$��+
6�G��q0�C��3s���,�C�el�]IK:�����C^�G�V��G=_����q$�����:��o�*��8�������������z�v����}�}��8�7EU��KEB��J��*�]	X�NE�)�Y�A����R�
i�M�4�e���� #�G�r���s��Zf�t�h�vG��FX�{��4s+*�����^2��fO��
���N��>��D�Y��0nK���4{�
O�/�5I��@U�>�o�������1���
>)�6��J�c���
a�	��38�
����H�x%�Ci����MZ'���7�����0��I}6`�k����������:�/�����p89�/����YYc���{�=�G$��'?�c����bJ�5Pex����_��[��+�ey�Kiz)U����]������i�h�n]����8������~Sl���[�����6�����.'
BIJ�[��n�b�,M(}Q,�J��)�Q&Y�������;�\@:��'�|���x��>D�N�Ga�_�����g3b�������3�P�Jb�g�9`���ZG�lXJ��B�������lm��6��m��#����=�V�nS�T�s����Yd�lC����\��J0���6t��>�d8�EV��A�:m�E&u-�FH�"W��2���?���O�0F
u48/T�8���9#jv�^o�����������8��T�([5����f��;\���^s���Ub�@�V����H����,���}[�Y��t+�����]|WNxC.�K��D
�p��9��{�Gy ���N�H���h�R�����&�T��CQ�/��M[�g�:�o�H�c�����\���/�M	y��'���"���B����;h�!��?�����}���t�.k����Q�u��u��p���s#���3aDN�d��	�P��!0?��v�3 ���)��
aE���S�c�-6����x����Gb������F�0�I�_w����6$*��z�U{^������	+�m?6A�����2.Ub�Q���1%�IO#G�4k$$kzD��$����Z�l�	)Th�>�i�N���O�S��	Eq������Iv�$�\�]������Z.��hP.eu%fI
5*�pU��L�����n�_���+T{9h?�����kGwLG)h�x�-oX�K��d�����F���4I�.0j���/^���[�r�9�
k1�~�"����<�5H����1|
����4���[��/�`��jK�Z��H����Dhoy=�	v�[��>�����h��D�UQ��:���F�����g�7D������L\ �7�K7r������s\�����l�,6T��Yn(x�

����R�S�O���s�C��>4���w�b�:1�����������_��1[���c���W�'C_����~{yt�33���*�%��I�����a���K���m���.t��q5J�a���$���U��EM�a��=&����}���k�^QP8�0�k�%@��z~fQ4N�@S����a����0��o��M:N��L�:�	�����jx��>�Y�M��sn4�LPzwE�2�U���a���J��JZ�g{�e���80��co=]�J��x�����%���x	��n`���>F��f������q���T��-�9L�v���K�/�Z�ox�/���-������1L�h�_i0)nAP~g6"X��N��g�.�L,b��4������)�y!%.q����ThO�O%����@�>���2cM�
cDT4=
�R����� ������-PD�|���O���i�V��������q8]�����w��kl�s��}�Z��u������ nZ���t�������J�H%���
�3Jf��9������"F�1�h:z��8>�l���R��t��#~AIwv{�S��F:�)��y!�E����e�wux�����NHu���N��u�sr�m��9�������BK`�ej�f�1(V�n��}j
�-���Y58���'%7�C���CLh>�a�Pc�YD�QcS���RM�
#}����A22��0���q7�����~a��(W�dh�I����EN(�s�^�.���TH2N�����2���������h���i�m�sD����8	�eN���xv�n���Z�^U}��}���xV*�����g2�X�
�3��f���h����c�&2�N�M�����X�d��(zM'0q
[G��b�4����v� b�!Y�������je��x�����P~�A�=��B���r����[���A(�9U��~p�"�_�z��������<l����gy5�X�����b���Z,������?��u��~�Qn���sk�a��,�#oX��,{���F������A�_	++����-G�l��
���y�0���EK�K8�T�`=�ja�h����
u"����=��%L��X/������9I��(�[�����@��HR��0��c3�`J��0�R�����A����w�d���s����)�)9��U��dT��
�Dy}5����^���F�y����J���zzw��R�\S�z�+2��H��M0RA��Dl��!th���H��b*�sy���3N+$��AfC��D��oH�k\
���'8�g	��~�"�p��W+��IV��2��M����Vl�#�<�7��nQ�N�J����������N�i?@��utuh�����L�]��^��(�6�����f-�(�����=oI�l 
I4������I�y�}R�����&6@����Z�!�v��p'-M�����
!k0���c��
�a����j���<���&J�����������:��������x��qATq8G������t�X8<��`��}%�9�������KHR	���i�Eb�[bU�M��P���QF'��h�]�}�qa�g{*���R�5�R�^�P��
`���q����<Y������o�'�;x�7
�8�0=Y
��b�=@p�WO�w��#�����5\SNy�<S����3w4���4����SM�Q�s����T��Y� �(j�8:~\[��S������I=A��CK
;�c����YQ���D�Q�~Q�y�����n�s�����}/.�����QJk��z{r~�G��m������D3+��b�>`T?2gX^���S~&�-Z|�,;;|j�3�s�_�{S4�`�������%|��N����|:J���0���/�����Z�=����;��F?�k9�\��w�����2���=�F��)�>R����&�L�� +(T�{0���\�[����te�U�I���2�G��W�^�P*��p��F��48���F��!Yp���L}w����3�%n��K8 �/m���IK�
|kx�f��=���"&���eK:S!���nI��2���F��-qO�N�d�Sm�������R
,��)9:Q/�/��n=1j������On�����Dt�>,(t*xf��(��$��z8~0M�����%*�K�F���'���L������ o��N|���-�����(S������n�#�����)����#^��(G��Y���s���E�}I���!��&�x��r�OS�ck��|5O�����/LO'x���pHg����+�����s�|K����D��)�|\���MVw}����P�N�Qn�'����u\��y(M-]�__�Q��~�{��9^{0�Sb*(u�����������c�R�
1=��a)���
���_��7�x��r
"��
K#�)�D�>��Xr���-�BN�C��2��l��g�JA�>s��4��d�v�=��Z�7G]������k_��q��?�g�K��C�u/�/8=�����L%%�@�h\���p��Q���'j��! �K�T6dl�������e�>5�:3lO����&���l�A����W�����q��4����~3�E�\7�@��TU`X}xgP��]��D���H�>�a}�t�>khe�B���O-<��nl`GL�rP�

�Ly��N����?\�|�;w'�����<-2�� �;X
�^����A�V&��~7J��a���gP���D/�#q��H��)1��r�#�Ku~���C��(��lQ�V���~i���N��������������V�-�m��j�5(=�>�bF�f����\����ZVii��H������|4
��������}�/Y�����I�'#���P��0�6*������8�K�CR=�*H���(c�Z�(*�������F���`It��K�{#�t����3e1o�ZN}?�V�_���,3l����{��=���.��s��P�)A���>:@������|����M�r�!!�;�l�
&�>��sv{�����X/��b�7��E������o�"A�j����u��(b�Z�C^��_up	B�dK�.��Y��yk'C��
�5l�wj=�L���/�7�v����.�-����W��l�B�U<W����U�.�n�-����
	$���Y����/��BSv��	-����p���M^P��R�4��-���v86Sh4=Q�y����Zrd�c���*����N�����X��� �0�\I�Ca~��]��6)�u�0z�t�'���2z9U)Y�a����E8m�+����!��u�)�e$�;����;;��7��56��s��V�oe�����F�w��8�JUq�D�	����x�y�c4:���~�&�8�FFDr2I���o���"�&���O�"����16�������S�}�A���2O����y$7A�����Fb}h���jl��f>�&
mpI7�*h[
���S�#G�X�#��T{��g��Y��%�y�`E?�'#r�q|�o:�:@L3����K�<�*�s�$��o	_�e��s���>.1*��@�9����G���)%y&V���9���'#y�����h>{��T
��:��H5;2+���9!*��;�d�8��Wj6=�2O���I0L���n�0�vk�n�'��ds�E��{lu[V+^����-]�JM�����F
����C@�4�\?�v���~D����P���/TX���O���7G]���|�*��^�x�iQLd��|B�fKWz�r��0i�j�3!����Z�-5����6���*����WZ�������'��:�\lU?�`A�|���'����G���^������l���*�C�yK��k���p��Ub�[\�J=��LgIMI\J���H9
�BO��q8�jW�?4�/��	]<�����B*E�����!����
A�A�]�$�H�8=jC�u��i�����DX^a.I��]B������L��Q������1`*�U�b9������V��A>F����i����k�dubE�t�8*���;+'����]��e-W�l��k���RB&Ii���6j�����5�L����������Aj�z�-E�E��6��2���������XJ7�k��g�*V�q-�{:��b�G�%��Q>H.�����Z�H��W������l��f��R��( ��1@��=;��K>�����#��9��h���X-�:���f�$M�~+(X	3!Z��2b4�<U���;���]<��m.a��b�#pY7�$�z���yB�T<�K����Kn��i^dEv�[���,�I�,��	w��k��(�������#���=DYEb��c�����m�b��O�x3��9�p_���.�M#��T
������Y�}��w�G�
���[$�a0#=6�����x�������I���#���^x e�qO����4�����
��vr����&�`/Zg�C�`es��"F����8��<������?�����|��H���h"�s����l5�� Xgl\5{ �/o��l��
��g &A�����������S��E#an57a����K7��,�hS��`�)����H�2�{�v@�$3����N1~�	~@��(����������NdH%��<e
�'f���X�8�����o���D
����S�N��$�f�,�cb��@��[���c�y��D9FQb��������q�Y����
J�%cw��=��W����"Yu��
#���&u�~uu�"�B��Q���6�����v��M��O�����W�w�>��bH��^x��v�n[�N���6�
��Uj�����,����H	hR�\G��v������_}?�����H��������x�,�cu�l]T���+���x�(F�riT�Ju�a���$�X���~�Av�����N�����xF��){)�7�WM~7�� �pbu�'���q��������Y��9�?��D�a��?�N>��g��1���	���?���$%���u}
������[�`
�"�~x���(����p�0_N�Y���&n�oz/���K�J�X�
���V�W��J
V����������[��gK�M�����������r������.�����s1�bp�C_E8N�G�,�C��s����Fpx�����OZ�0���|99���G`��[�<��������[��jL=��r�[,��h�749'?��1�tu��|��<�b���qu�h8�t�z�����E����:�F&��t�~
�m�����~�`�cS��S��a�{w�R������/���qz
���OQu��e�<��6CK��h�;������u�nC1�%�����!������af�P���p[���>��}m��(qC:L%��9�g�s?(��VD ���j��0����h�j�6�+�A�P��j��7(�F�����iJrv��59�-������Ab��13���/�M��*�B��W�ew��52�����Q���~W�z� uqv8�n�����;�Q���tN_H��< K�]�������}�j�!4r��D�Z�o*:�����[
����F��s�:9�s���:`��Q:|p\�1hx�����GG2R�!�(���Cd�N�7�Q~����Xw�6��z�K����\{!���6��������m�(������2[}���;�|��g�G��jeL�}ET�X���O�*T�*x1��0��|q��T�a�q��Z�Tt�9'l����s���:^���
-U�@��B$���W�	��^���Co:������h��(���{��5[�������|n87�t��SHF��a�����g�%br.�Pz?�NTJNT����9MU���?�)�#SnyX����"�����@��@?�����(���	�G�P`$�A�1r���TS#��1h[�_/�+i��|���X*�+��A,�f�{fZx[�[(87UO��2 ��������I�0�`����Abvu�:�5k�0�3�0a��%M��)�LM��n�79Hw������dZD����,����z�1hT��7�O���"K�ZJ�i�H��K�X��{��C!���x�<�*���~6��aQ���;0���$Z��<�f��r��F�X}������h����#)����Oa�i��*D7��~J����q�\���*��qu.��W|��%ap��[^���/�k��P�H(����P����"S��=�pSK��ue&lK��d�6�=���Y@bc���mP��%
u(F�����b@�cIE�
J!$��a�X�1�j�&������k����osT��=
o-2]�\/�\�=����00�$J���J<���5��C�w��#�� ���&��*L��	��q7m|������u���?Y=�*�V_��t�NC�7Gt�C��87�����;��8�	��8�|�����O��u���7Q�X��c����Va�Z�B����7��d��:82��}ll�>e������h�����g�*�Tw��{~��g��y��z��(������qv~�9l?����e�����&��	e}7Y,h��v��-�I�$�nv�R�Xx��N.�]S%����\��qB�^���]�w[�m���6z������zjd�&Z}�����8��zB�$Z��g\g9��E���!���CS�8�:�1j����,�I��� Y�]x{�;�p�>������Oqq�AR[����_���jw��l8N������w�5`�-�nK�����gGT���qp��\�['@#��s�n���]���A����i�7�'���B���)F��6�h|�X�Q���y�M��HXn7�[���GL"z����G����=��c<QR�B3��"f.�p��-�����g!���9@9o�%�����p�����
	$�L�^B��
Ph�#�9n��R���O�8�?�����p�������s�EL����(�����DG��3���Wyt������j~�EL�����x�����1��+n<��S��O�����<_2iy����>Vx�an�L�9�6|*}S��c������i<������(���"M���.��"�����|�r�Y�H��<�� �g�l��g"1�|����H=�0���77��Q�����X�$������d�c�z0��}<���o��Q�_��V}4�s�����P86�/���/I���H�
7_e~4�����u����H���3�����?x����}�x��F���G_�y�-�T���).eN<X��@C2���<����=�vi�a���C���sv	Dh�C/o���a9����O���@b��37��[R��2��`��Tz,��������3�T�7��In�i�I�J�Q�z[��O~�flb�7ON�3m���&������o>1x4�"�?��.o�v�R�'��G��z<b��1�����E�	�?�3p��n����<���~���hbr|����7�r����C-?�D��j�	{���wiw�M�����x�z������'����v�_����������C'eL}����������"���}]�d1����������2.Z��Y�:3y�-g~���D�f�������R���6�r���bP��g���nk�aP�2��k��T���d�g�fY�R�6�����m=�eK�e���
�T*6���-��T-����jC�j��g�nyY+Z�����V�L�^��Y/[�W�!u�����
K�MR7-�t�����y*@��}3��e@%���%R�e�����e�li���@�,S�6-}�*��-H��-��m��5��>��rkn�T�����S)��.�,�J�l�g�lY�R�������,���T�Y��[�����	��oK��|����6��l���R��g�������(Wm�6-S�Y��\��e�-7-KV)Z���Z��J�r��,����R����Y�Y��R� 5�E��U�>�Z��m���,����C��e*����|�����e��M�b�\��l�m�lA�Z�����e;�j��T�[�`�a�g��~�m�J�d!5@n-/+�g�e@uI�7,x[oZ�x�&(6l�P�lAj�n�>k���Q��
R7��c�i��5��Y� u�j����Z���<����k66�-��[��Y;��-}����fF9u��#`Fjns��EPt��[���C ���[*����E�+�H��,��[�� ������n�5�[�e,*W�"�[���� ����-��[���,H
���O8�-/f����U��'��m�jV���&�T��Cn�h��p�����-H]����f�Z���r��5�Y/���^1�[\-KVoX��Q4�n�"(���e�
�~����p�E���6m���X��ia?�����R�h��R�d{Y�5k4j�J	�Hs�n���kK�e�J������MJ���J%���f�.���P*[�R�l��x��T�l�R�i&����,U*�������dK@n�S��-x[���x�Z7 CZ���y@����6�|��jM�Y�[W���R���R����ER7,�G�aK����)���,Y���0h��E^)5�f�.K����������q@e�D�}�cyi�?����@n�S)Y�r�nI��������}�\5P��E�P��H�*Fs���X�4�"f����j�|���K��l��r�5#5�����\��@K�*�r��l(��f>W��g���+7��nX��r�"(���YT����nf?*E���R��o+���+������A�I��U���+�����2�+��E_)Y��$�<��EP�Wc_�hFj8����R����J�r�R��l/��fm�b��Z�R�X�P�[�W/Z_�\-T�5�b7�4iXE�	-���\�T�%�vhV-�i���]�<�E����`@=3�������Dln�d����,���R����o�W-[�j��Um��Z)���j�n>��U�}Hf��V��c�Z���^fV�Z���j�rM^����K�n�7�oyY���a�M���Um�g$L#�jE�����S�`�q@�%����Z���0�������7O8s�e�=X�\3#u�ba?�r�< �H�>m�-�LokU�AS�f�����f��|��&���n��y��(���Z�nY���^NOR7f>�^��@L����M�D]�q�� b�T����3#5������E8U���z��O�W*f��^���m�j1=���aP�if?3��J�V�������f��^o��O�,�����������EP����Q�����37T�<��k1=m�M3�?���-�#I>_�W�0;����E�],a��t��gfOuw5�Q��S�-�����oDdfUVuE�,d{�.�-���gd�2"2v=~�b��(���nYl���^��I4�4���&��A]���8��#�{|�=�&<��c��i���	O�P���D<IML�+��d�,�$&��������*q
�b��<����:!����=�
��px�NL�G�$y�'1�&Q`�gl��)��]������3�E��y�rd�]\X�`�
��VT �����c{����UI@�����Ap:]V� �z����"5yz��E��b��7ql�>~���0��W����Ej�����:.k2pL�CP�e�l��[��]O������yE�q�5��s��f��+�����>��N���^��2�������?���+�]O�1 ����
�.���1�����H�A������	�]O7�L@^�����Cx�S1��06,(�����r~@	oOp�7���	������	<������)�d���g��W/�yR����.�<y�B���"���!��^����+����!/����c����gt�^<��R�.o�E/)�������!~��59�2�\��w=u�����y{��E��h��|<c�~&>/��	�j�<��:p������o4�C����3�����]O��H�#yE�!�z9A��0��m`ph �z��	l�����W����C��?��uy�	���'���������}6��<��A��F�k~@�A
#��c�����a��'��(F���S~�#u�]��5����z
��#5^��}�E19�o6��CN��O%4�O8��~���:2��NE��y��c:6�l����)c~�����E1�
[|>�<��r�#�g<j�� r����8	�#��|~�=�����]O�������o���$5	�|5�x2j?����:�h��y�[�$	X��6�\�l�D`:FQ��C��/~�W]�X���>�
c�<
�!��x�SF,R�v�����E�"�K�]��a���\�����<R�hd����'h���h�(�� %~@�z
j��#5�T�YC����U1�'��|��~�����u��Gj�����+�����uC���bD?���_�i���q�#�k�/s=�W��& �3$��tV�����Z������Gj/���+��_�I��">�
cV��#����]Oh@jt*g��;���v%~?}C2`��Y�E�>���)f�1C>���<�B����|2<���Kx����2���>>�
\^Qt�@oC2��S	x�X ~@�A�b^QD�K��	���
aW�fC��
�<I
a��>=>����@�<����uxv��?���)���H���T^Qt#� �F����i����U�]O��wy��>�	yE����E�����	��x����z����#5:X�}��(�h���p�P���J������._��!�
�b����d6nb�<��P����.�k���'��=���>�&��EM�&!��&�z
Z���������������
�l<t�����=>�����g�!������<��(zv��o�'��l����K��3Ap[��Ej�q��}��������|��d6���?<8�,���&�]>L��(������	����+�k��l<}X�!������J������P����5$�����h��G0��<jz&������6�'��?�����|����'^�����g�O/���;2F$}�T|C2�<�>��g��<R�>�������R��H�y��9�X���1���	�z
@�
��8���.�o�6���`��f^@ �z�������g����������0���cx�����W*��������[�����?&���:�x5�CS
?��w=�"��o����x&E���y|2`
!�W���o�9��-]�G�(��w �(zQ��kz�!��;!/'����{���������A�@~@!���F�#u�"��!������)�9���%.�<��x��@������xI��G���<`�y�C���c`J�������Of������m��:-�z��o��4�f#^B�Q�T�l|����C���cp=��v�A}�������|�
���������|�>�Ix�S��y{���� �(����#��!����|���?�z�����1�<�f����e>��<R�v��+[���(��,�������z��H������l|�pM��aR:�>�x��?�*$�(�������Gj?2����o��Z�r��g�����3pxE������d6~`:��
�����>�$<R1/~��E�xV�B:�#u��mhph�C�w=�����>�?��1�y��}��Ofhk������#�w=��#��~^Q�AlaM#>�(~@�A=�?��;�& 8��?�;�� ���"h|>?6$��c�|�2��or=E��#u���c���+�>Hv�<�����
�����#��O�	����y����	I��m���+�����e`������f6O��.F�`/���Oh�i�@���"9,&��O���4 ��"�=�d)Y��@�*!��gd)!y�@��"�4O��#��d�y��@��!�4 �����d98Y��@��9���@VRA +������h@j�����rd�@���%O�2+Yi����@�<y���T�@R;�&�@V	lCv���1!����-���h@j�������Q�z,
����$Y�;�d�&��������u��vy�y[M[��_�`�A�a?=�Z��1�*����b��i4���|��)�y�c`r? ��r�������Do}��@�@��@���@�<MH�����L�3�������*�p��.!0�B��� ���r���� ���
@�n�8[��Q�o�h�oC��,�G��E����>����{�y\�h�W����Q���@��l�*�@Rc���	2�q���&y(������v������L���{��(&���m���[J/�8��S���'
��c��'�2|���`�C~@�T���s�{�8���D�cHM�r�O��}��{M���
C$
��c� �W@�#-�$Q��^p��}���
��cp� �'@��0��� ��@R�'%>�m����� ���	��>�}M�{��xf@j��(�cH=`�~*���/{��������^�x�i@j��3
�����f������}�h����?\� ~��AQ4y����0���!�����:|<�H������a������".��j�@o]�����G�z�����,�|�q0�k����8<�p
Q;��}(��o��(b*?���k
0&�_����B���e!�����������f�kx�}=x���|l
�-�C��
���E�=@>��|�b����A_A?T�O��#3|��ca�!L~@������A���!�#m1p�W,<���
02���)_|��W�<C3��g���������k�}R�!�3��d�B>��|$|�EE=��f#���������gl��y1��df���%E�3dS ��!�m>�
+�6�o}��7`H'�|�!t	�����y5�g� ��4p�=��H�n��T<���~����|���~��f	LY]�y��!�I&�M����k�����
���vt���,�����@����5�������c����O4��
��4(��!��Y������f]��R<I>��-23~�<������t>C��W�4�'���� �3���(!��-"�z�� �s��L�& }���2J$E1H�l~�����!�<f�����?:|�C��t�6���4�C��g� �K�����W� ~�V���m�G�0�3|ahphV���a��# ��4
�m�+=
�Jh����m�����������E1r�l�4���c������32d� �/����GdR#��n@�=!����>;�(0��F!���|�hE���7k�E1�!�|nm\O����
@��z"/'������t����)59�O��i�����gvI��(�����d��
�����ilBj�@��E`9��j��!��#�-\O��?�/��
��%6��
�+��)B�oS��:�!�H�^���(&�z�����!A�o���z���&�`1����(&��Xh��Lb��$6��&	�^
��mC���6d�m��� ��O�o����)j,R����OJ������%a6�Ctr�����N�mx*��������dU����>c��- ��[]���[����`��'0l^Q1y�����j�D�o�a�6�4�_
? �?B'�E����V�����`2��
���}!���yE1t&=��}���/������4t
�CB|%��
(�,�1�;�|�J&? �7��J&? �+�!���
/���!59YF���oC|%�_���4t
����K
=�K��E�/��]�_�����(@�-Z`���i�%���!�j^Q=�k������!M�����1�?����My2���
=�S-�o���ox�9�3:P0y����]�~��(���!�|�!~%�
����? ����������WC�%�O%���B?�_d1q; ��a�������C��3�����_��?��y��������:��B���������^a`�c�%�o����T���=����|�_~��������	�����;@��4}R��
@���Iq���x^���02��0�]O1�'����e�0�
�b����@C|v��x���\�����x��|���&<_�DO|��<��x�P���Q���<RG�!���x��
�B�[�R��w=y��!�x0�E1[�.���@f��
�m���al�+:�8��AQL�J���&a��= &O0@��IM�9�~C��[bRA�Ij��j`�\��'�A�L"�H����y��0IxE1���k62%��l���=!B�&�|��9,R�U7{��MVp�0���3���D�|��y�S�s.���������b�Rq�Of
f��-���%MVT�Cj����T|^Q��IY�$rB>�
y5(�c���y�SF<R;��Q@n�EF��	���&���+����kp=���A~@>��t,0�Y�ycC�!�Tb�~���)�X>���!�,�t�y����@v���0f����x�U����)c�����-�E�&=tT����=V�n���w=EG(���������bD��Y�!�M�^�q���Foo}��e�!��E$�����,0����TT��(/t~������Wh���!�
>��� ��M� �]O#t��� ��(H���
��'o��(

OmG�g�xz|��-����<��Da��
�{�����7�= ����#�'�v<�Of�O���>�x���h*�x�6��E���"X^���l`4>��Q��k�������)��b��W�.�(F���R��4}�|��@��4���G���(�c��'�=�|2�(9�m61$����]O�H
2
�W����` ��&J���#�?>Jb���7�H�$|~���yE�o��mC2���TKl{|�C�?k�m�4 ��
^Q�S��oQ�`�:������+�cH���!�c������c,_ o��M�l���G��|<R�N7��a�~@1/~`�+'������1���064k�/�,h�T\���
���1������`����7�&n��"+�Q�3!5���N�syE����<>�M���c
1O0�F�}F���/���(?�����}C2��w<�����7������H��|>��xE��X��\��l�<Y��"�z�P<R�D0^Q �o�d61�<�����9�X�����P��b^QQ�a������Y�y�E�B����1�]~@>�?>F7������Dx�c^
�����@��4�l^���q�Y���*��	��Of���W��(�U�tA�A���'��~�$�6���(E�SA������1�7�c�Kq���?��.��3�����wx�#1� �z
����8q��1���E�j�oYbHf'��#>�y�S��H
�?|��3��?Tp>�Mb{�Y�V>?(���)��������mx�
P��wHlC2���~�P�b�\OLj�����1|R��|�
��|�=�����Ix����y{|���H\�W4�}��$.��d�!����z�o��xl�%�	�x�$�!�,�>�M��	d��<>�f����)MR{!�<F�E����v���gHf�;M��=+�`H+�$ ��H����AQL����L@R��_�c������ ���	=���y���
��!YQ=1%�I��B>�qK��$p8y�����>HxE1	m>>;	>�
�	z���g���	��y�F����;"�e�!�M��'���@�w=M"��Z�y9!
xE1)�5����'��� E	�?�?�z����#u���c���+�I���0�����a^�M@S�����4AA�Pb��c$��+�	f����x������$��C��;�����:1<���U�F��	�����o^�;��o��&�l���('V�-�j�������W��R�����{s���AX����77�r`�qf]����(����XZ�,�d�V�����������'k\��uV�.����������6d9�����(����\���6(�}	��
���'O��R>
e�e�����u�j{������Ck�O��X_P�`��a�f��q�l����~��E&�
��un_����.�=>���|Jg,�����V7��L�b
_����(���8��g�������_����Z������K8��2�2kk���5)���{�1��T������������C�����������?��=I��5���RA8V�.�����W�����?�9��������z
�<K�9�u�<���u�[5n�������{g��*�N?���K��_s(8u�M>�T��pL����pr|������i�f>���=8���c������������w�J�_��}���P����i��f���_g�����6��$�h�Y�d�hj�����N|?�'S�0Tjq�>��������7k/��n0pl���m����f9Q�;��e%��q1�g�e^�+)JYK�Vs*�]UClA[v��?Z���{b]`��u��M�b�!/V\����HjY�Y�hiV����oW �]�8�?Z��#(3�>�<������` 9�y��u�?��nS�����(��v��'���=�!�$����	��>A�U6���L�i^��e�tU�0��1�����_ �<�v`�kQfS���qeZ1X[�VJ5WX���:]b3P.+�
�B�(���H�!���`��;P���l_��>��E_��yv���)&��@��v�3�Y���a2!�����G��`o_�����v������H�O
(��I��Y�
��U���P��j@k_�Z(� ��Q�e���l�ZJ��	-]����=��|��+F��K�'�7��}>y��V���s������}��x`���a�rD�r"I'��6\��9�8�����51M���D-���l�	d�����|��'��8J�WekK�-*Su��y:�����
J�MIJ���g3-����G��_g3YS��Z�g9��3�������C��N^^��Xe�W�eV�E	�����g'�����?�\�l�N^� ����stp���/��z��/gG����O�
������;�wx��*�q�7-�1	-W�<+S<��G�
(����V���|�*������r��?c
���ZS��& i�������5���:58��z�%{y�*��-�utn��i��p|l]`kmLt�����V~	�
���f�-�30���o�]|�����������S����������E��+��C������w�=K���!5�5���,o~Ma�#��y�Myk����_�H���s���������'�����i�������j��]5W!!��2������M�� 7��_���#���fS��(y����]I\��_Mr�"r�������,�5�%�����'��C=����]y���y&oM?dd��%,7���"�>�	_4��[�������oO�N.O��h���u�.N��������l��>6�u���z_c�.c�6t�?��u����&��u�=����}6�wv�����_����N�Wb���V��[�����y�W�!�}���{��k�P��>��J��WZ���������w5��G�TZ��*+���v���c]m�`��+�
��.P����U��Z�����5}�����0�����������a����l�����O�&?�k��n��)�������������:?�{���qm����6���"H�WL������u���,����E
�x�>l����o'
�ozr����r0��X���A�?,���~V��JJz�L����X���I~�+�+uG�$r'������7
�x:��i�L&I��x����bn�g��,�~B�Y.l�&6��:�/����U�>�����6�4��e6����@yb}W���^ec���'A����]d������f(����������x�+�G90t/��|+77��oY't!���$6�������H:��2)	.��?��	�g���jy]���_����f5/�M�@'p)���HU��&}��wy�����k��W%��T����e����q��O�J�Oo;��U9��q��c�Q�������Xl7�2�W�
Un���M��;z7�����'���*�k��}JI^��n�_T/X#n^T�MgTA�����N���V�L[���0��u���[������j�V��'!0���5�n
X����Z��xI���hO�B@u�N�Wx������AS#?���� �$�e����M��jawa���!��o�u�ck�;��T����	*cE�n��#/������8��I�������p%0x$�&�~Y���*�M&y	
�p�O�mZr���l�Ev�q���jV�����i��������
����������U��W�Q][�Y���d��e�e������_7�666T4�3Jo�oq���������@#�=K�[=G��b���w�t����B��2,l�:�Dl���-X(?�
����Y?�F��������A��G�O.��;zy��$��]���8���j5(����.s2�p�~����N�����R�h�\�EA��o��8����Qc��#�@h@l���k=��R]c�'����4Ra�X�W�0����������J!k?��=#�u�QL"4F���t�h����g�P��C�l^���������3�n�����g��
^�$X�G�����V;q��[/M��[��O�Lo�F����G��[��Zb\��z�
Xc+[=P$_F����� �c (���1$,]���79F�������2�U�x��Z/�CZ��y*�� �S��Fx��p��v���$TS��iy��n]8��� ����.�#P�������������Y�h���c�G�F����{�*+8��w�x�y�0N���G��,�t�6���bf��[]�������i:�2a�L����]\#!�l�}}���.�^��l��R\����=����
����(���	n��^i=^de�}��������(?�,�tx���r,�M����	]��bl�;;������N�p6�&��R��N��Ro�%���\����^^@���..���^���![xn=Z<���G�Y�����q����HT������#�F����Cp����L��B���M���}l�s�L~�c�R��s6�y���g�U�{��z&L\�+B��i�r��8���r|}Ql_���h�)\���8;�����t�B��D[���KQY;L'���q��w����j����::���z�������@?����G���{���c�������!��>=zEu��YQ����qq(�vYn�*4#o��J+�s�w�n�?�r����c�~��D2�5~��������d��(l �.,��Z�{#��V���C�z���j�����6G���C�L�8��I��xK�%�E~u�i������:�F�����Fj
��f���n�O$��De=z{t����='�e";F�`N2D^����w���/����n["5B���sK�@x�v"�B�#��]Q����|O��3�����Z��Q��Y��c���*���h�&��^JwP�4���n�����U1������-��OiS��2���
,�KZs�c�*��`:h��0lq�J�hh����mas���D�G�z����ch���[������#�u?;��<?�#!��o�:��h��+��4Y��9t���t
�F/8���U�h��-X��������?�b�-~���l�Y��rdgo��]�TJ��� 6#z�^ep���O���D���W=k �06�
h`Y|��w��J�~��6�(b���X0��`��h�@�@|�Z^���DQr[��"�� �[�x5�N�>��-E��Y�K����oi����+��;T���R���Eb{mu�����j)\/�
L)����,�j;���T�
��Wr���4����6GE�3:^)�� �8�g�`�Y���>l�p����M"d��.���@���8��#�-���2H�rZ4���P����q��E�	�0��T3��h*�8�U�P� ���_E�����zp�&.�]���4u��q0U��
����:��K�����h�����.b�?,&����6�#�
�Y
�X-���lU]g2���Y�;���TrsDQ��������:9F���N����(��\�
���EY,��fJ<1���X�2Po�GJ�����9�"<}�%1��Z)(��$qH���m/*�u��n7�}G|����N��G��H�Q���	�FG%��xj�1���l��]F�%n�(������H8C%,P��Vz��8��
Nbi0�Sz��"�E��;c	�&�|.e�]4?�_��6K�P�����]bi���N�fPg
�b�����AD8��B�����&����,�L����v-�n5����-�\Ow(��]7��Fk~�EhFXf����`���
��Z�9�j0�7������5��7��.FW�>nmc�Z�{���'D,�����#X��O�x�^}q���S�ihm��F'�4�Q�&������K~���0p/��?����*m�A���r�f�TN ��Ik>���I�;���V����3���{v�����d�^���bK��!�Y�f��vm.��������r;���c�z�� v����5�&�uY���{��VL�SeQ�d�x���M���L������������x��xxc�
�%o���im���"��B�*�}*��d��}wC�(y��c��T��Z���{q/���{�
f^�f�
���
���]�������Y��E�$+7�;�>'8��=�|5��/�2x�E��kz�:�����6�}��[����zPt8j�}��	a��y"��%�X�|���)�d������ZY"�I��k�s�K�m�/[�e�������3WQ��6%�kRG�'O�C;�l���]FM�����$#?
F��L�;�a�-��v�!���D7����~?�]�yQ��e������W�T�T"����RA��2���]T��d���h<q��pb���yY����^M�o��8�c�F�_���E�WXH�������v��y��92
�K*P	1�������������'�gG/��171�E���|��ZI.A��04!��P�I�����#
\hA"W���T�,+0��P������m����RAG����4��ott�R9b��o�p�����Q��z����4����8�B����C�*e��A�u[�s!�V��k��;�d=�j����,3J�-K���*������t�����M�g�t������.�Q,�X$r�[��_��AO�D~�)�X�Q�5��XR����6H#����~gm�<��j`MF�kgKTA��0�{AP���Y\%HfL6E`��E���N��
���*h�VR��^�w�����c���Ob�WU'�l����W������?Md��
��'���~�b��������I^^.���X����;���0r���|�#ccC,��x�B;k3\�e"��k!��f��F~"���6Z�xqvpr~@������*/(���M����L?bW&�%�HA��FP���"Pg):M`�,y6E7�V�=/&��#3\�	�$��t�9�����=������FJz���t�p:�u���d����7�F��sR���C��
��5�#�FnJ���
>���o��`Ih~�K5��-��c�@Q.��?�}���
�|�@O��k'@�U��z��R��:��t Rt��X>�i�h��
m�?���<g���'#��F�(�z�y�2�Y|����Bk���g�]�T��F�<-s@���~��i}��>���H���kqd�uxE�7�^��6@����
z�V�u�N�[@����o�_�����#��`R������b����t�l���w�{��0�-�J���T�&zT����]�;uE��n[����!��Kg���r:(^d�R�I��W�?5�kB�
����5'd\hs��3����=�B�4N�(�q��1Z�KB���;Oc�1�Hv��tIs���$�l��iK�(MQ�D���?���U��,g����[t�"��G���DB�p�&M=�]<PC'x�&��er0|6�:��������U�{�!�������Df��2�
��1��L�����H;�L����'ZV.[kC��0��j��;E�SJ����b������f)LG��O�`}�b������;��-�w��\�	����5��E�����]%$rG���GA����7M`��$<r�����}���hK�C�nz��7 t������p
W��a��B�0����iy�_+�L&���@6�!��h�l�k�<�q-���6���0�����zlKxmf��T��Tm��d�x�-��oCw���4��o��d=�.@
O��Mu���L
X�����'�Q�g�J��H����I4Z����	��c���������5���������N���N~�]w����7B����(f O��J�o��o�u0�y�"�8������	�_�6~��-
�����B�*��������eI~�%����Rh�7��_*J7��c�L&{2=BV�M���F���m���U��l0�1��p��:�?���;����C�?�N��$R7�����|dg�h����t2�&��b�7�����������jae�f��,�r��V�������&����D�r�Z����'?���J,���9���,���do���uV�������|�W�U��O���rQ=��W������yp�-n���r�~v�������������������/��Y|vq�2.?z��������o���lxE���5�(9]V�@��n	�:W�P�YF�&��1l�\�����Wo1u��\&&��\�����T
���poww�W����	�n
��EY(�����A�j�u���=m�x4�=;q���[����.Y������K�~L��s���/Z�A
�����c�g /��~���l��c����vG���5(��@��32��������l�2����*���9�s�*�a$WN���WM-O��:�M��Te�����t
������k���)~0������!�����:s5O�u�D_	�E��������I����@ED�DsKvQ^�����)F����q�d��i<��0F
�.���a
&�g�{G����"�e[:�����i�2�\VK�^rq��V#���GW�8+�)�(�|�V��6v����T)z��_����Mr�'�v08�
l�T�
�lB��\6���K���������i�h�F!PBJ����%5���Q��J��p���� ky)�2K'�H0S�<]�;,�LQ�}P��"1B�;0h��RF-����#$m�d����t|mUq:+�yW9�*f�����pG�-|kW�����3:����x�'�*�H�G��w����#{D�E��2��
h�G��#�{����5��WW�"XS�:L��AGs������?8D8=���j��,���\l*��rj���Z*>���&�n�"f�Mj
_�Z���������n2�,|����W�1��NUD�J��gxI�T%����>B�~���j�j������zN�G�J�Oxle��D��|[�!k*����$`E{�XusW+F����H'n(����0�M���������O�:���d�t�����,y��Q�������D����e��Ao@�1�
��NKzi	CK��&���@�*���,�Dt�8p���r1��mF�Y��Q<Q���C��Opn������4����%������^�zocB�t��,�"
���@��*l����#aT$.���(�H�^#�Q�n� u�iM�@#CE0��K�&��@����T�VP�Z��(�L
J�"@�L5"�@@O��D@���2@Rg���u���zjx�w
��$bt@g���R���=W���/� ^7��^>D�
$G����/�-M�^����6L?Z.�V/��PM[�D����	���+B"<�!N=��&*U�Wm�*!2��L%[�-��H���H�P�&�K������=��DP������P�_�rW�
��l��3|��>���a� !)����*��E�#��(��p�O�1p�9h.�n�'���/_�����P��Y��tH�B�M1���M�*����n�G�:�	��#({Iw������
��Dj�iY�|�,qs�7���1c�<�3�V*�XL�n�F�b�a����L��%�
$l�&�S4�
���+G��py�%���HF��CyHPT�.r�������V���m��������esY;%f��@8b� �8:E(v��\��������k"[��M��j,�vILUJA��v�%����.���>c%�k�<���#�6�)F2����|���M�-R�%rd�I�@
���#QX�1d�$���Q�������@Z�=�)�������|���KR�+�����;�����G��u�p��$��Z�|�'e�fl8W��La�y��	����`�(a~{\mBI'P�!$�&z����%K����
q#��
n��EXtF���G�,_���dM��5ae�y!�j4�FqnI���>��Z����qN�Z�c9��(��Uye%!�����V�KD�Bz�����O�������f����J(���0�l..����v*��W��i-���=������t�����^n�n���HN�iT�j�����F�l�Xp�������L��\��HC�V��.�d%�Q���1��\	�����z;�B��,��&��3<J����9��Y�C�Y���"�RWg���I�V n"�	]����H���WY���] �-�gM]-���4am+7wg�J13!��XK
�����W���Z^�	(��"���'�o5��/���y��T�%�L��YA��0���bg��R�j*#�oL�S]�����b�*�j����Tvk!g�[�D��������r;�Pp�5f-@ L)=����b��.7i"������0�[(���h��K{�VBh��DN@�YtU��B{�##u9D>�e�i*>K��������@'(��n����hV-��><;?:=E$�U�Si�s����O������$&y)-J�����=/&���#��H���cM���4�O�(.��\�&��������j�P_!�2����&Ji���D��o��������P��sK���3��
p��p#'"�%��S�e"�I�E1Z��#i�5��
�2�cB�~���P����<�"���A�H��R���J��eE�IL��~#+�*�t����l4��/��@�G2U���2?-P������`X������	�s�&!{N���]	���a�*	���N�X�3���%�9�U��8�0��\0�T!��6���4�LS�}�����j-�����, "�v�Xdc`D�o�{k�`���5V��������L�FC����)���r�x�j.������
Z�����l1*�Y;W��M�#W��B!�RlS�#���=aj������r\>���7)����v��as��-���L�F:w;)���oa��{/��]�(�C��a������������g�P�h%�'���?���P��Ub�C w����4#��+�8V*=���
��!!%�m����V+M�Nw�D��.�k:��f�i(J���������W P�A2^�vc���qo�����|�����K���������x�\y���3�������"BL��h9���+��f"wQk�(����*u��X�1o�_����DN)R+���.�4?R������<����s}�=q����'����w���detX�?���vT yNT�����bg��4���$�d���������Q��z���U��&RF��b����+��@�9���Ii-���
��C�f�5FVDb���h5�'��K.s@j�0[{��4A��M���p�&0�i�7C��t���n-�	��Y[��\������r[�[�S��S<\�4���9�~[KI[��p��24�"E������uH�����)�y`h�4�R!� C�+�z��a�
��9�t�gF��)N��-D'<�/�hx}��2��X]!��^�5���C������R�Ceh���I��h�!6$��\��C����*i�3�����
��,��q�yq��:������WJF�;"D���\X�Djs�i���������D��R$����M9)��KU}�|��� �w�B�u�^�3�[E+���>��*�M7����
'
U�6�H��Gc@��bE���#�8�k7B�Mi��gdy� W�	2PR��V������=�>d#x�B��  Cjn0��J��2�E*������ W'���*���B�����JAr��1'�e����j�	=�����P�9�]����l_HL�&_��A�pp��h5tlS��e�{�h4KD�	y2�.�������>7@�8�@�2�]�P_Z�B_^�����>�"g���,�T��W�+�0 �t5#
������	A��uC�R`b�hR��!�b��6+����T%�K�m
{�!�e��W���\���>�4���2���1,����i��G����*jDU��8]4"���a4F���o%B������h��P��S�.��~��]8KK`O�FS!��pm�^�9F�5Q-�L���)������F�v���
��"�r"�5$����������t���R@e�D.9X(�>��������fr��T<�c[�n8p���_����4�uL�� �d��P_)
�eU"�
]JX����4F���>sU�)<&���)���5��}d���I�I� ��y*0����KDz�(����5�����]w�+��vK*k[���`�"��Y%�0l����v��j;���W���6&a��3)���r�	qX�e����y�rD?�L�PQ5��f��(+;Y��/|t��i����.66�)D������N"�k�����O�����	����F�Dvh
V�3!��K�!c\A�5��;[*D����@���
ta���6�_A�{_�ZQk�����+?�U0����_��	P�����}�3t�����5~���
���]6u'{�'n�����I��$u�����6��N���aeO�&�l�Rn�}~a�6���:��p��2ad�����)H�te(�(�=��
���A$���Q';�5@�tn[����X@�����UW7�}�4����Wz�������L`�`��z��}�#�����J�(b�uS�+���e6�-�D��|/��p+�U����7������>�����*L���P6\_�M+Z{���~�K�wO�'�����$X��7	����9� �;q�l�\��Q����B��o�Ci�<}���@I�����7��}��{������|j��WY?]|o���n�
x��q���o^l�/���0���^`P����������X0��#�GX�Y:�f���^���g����DS��nAA��g(���Z���/;u3�����d=[��M
#�O�b�W�M=�/�AO�g�����������N�n�6�U�:V�O�����m��mv��'���U�0���t�0EyPw�������^j������C�eRD��������I'�\��I���E�f��[u�3�N�htV�����^��.�7�}G�wI�h��
�w (EQ�F�����|I^RQ�=u1)2��i���fn)��i�����s�D_jr#���b.]}��I,^�4m��-���e �P���L-�WXH[K���:�ORe~Y��5�V�<��gA�D�]��jM�5�|>&H��|v\���[
��L�����:@J8�h���9��BC����V�����-�	E�'��M;�A��j�����_�g�K�w-E�{I1yR�)������zL%.U�KY�E��D���:e��Y:�I�jL�i<���J##X���M�$
�0���&#��
���L��A��%��4�%l���Z�i'�e�C[���v�����U��9u�'�����x���	+^+-�[���o
KD�g�����}<���}L��F��4^T���)�_R�����T���"u���Y5�����0@�����Fz�X�}���[�O�����
���yw)��v;���/����m	�,�~A7����)}!�e�H���d��_u�����������m��N��W��KH��(�������G�<�������%�z��m\S��7����S���l�02�,	�D��Ej����Ec;�4����'634i��E����2�����O��c6����!>�.���/[,�C�l=�����s�Y�y���������-�%��t�,OJ(�@���$m�����q=��
�� �X������k�����tI6%�p�
�bG���>?~xv��i�$�L��0����������
��+t\�W����457�q�gb�s���"���:�3���w��/[����P5)�>���Y�|�S���%zT�_z��e1[��$��q�&�����A��� �\M[4�d6y��j��S�'Y���"�H9����
�/k�$%|�+���UD��������|�A"�s��SRj��Gn�:���jsr
Ty�6@�/t���M��U9���VS��j��^*w��%=��o�n5y�MhB�����-RI������x8�c��B*���?~Y���Tj+�lB���+N	y���9�����1��k�������?�F��U6���"��M������:���ijN����������g����B{�K���v�\k�jNH�d���d�93A�
��&�"��#���F�������dk�Y�"5�]Mc&�@3��e�3D!�<F������@���MF���H*��C,�V�.��e;UK�J����eLa}pE��H���\��/��n�'�O|ts�C���*��tg�D���ZH��y�����e��������p2���5�������v��]WM���������Z!i�4��Za�-�Ehg�d|��_�UB��M������p�N���yAhL�=-�X!{JQ��@�?EzaAt���E]�c����z����{��o�)�����+@b��c�O�����YJ�M^��Q@����f:�=���p�E�-^o�g������_��e�Q-���*��4��X�c�����O.�.�kL�<�"j����������hx;�0�0��z�Wq��*�IV��VQ�T�=��|����z�E��5��g5��\om������ZE	�7��x�bHPoE��V�}v�M�����e��uG��\w�����0�l/���)���������������G�]�e������Gr�3tR�����'�Js9b������L��[���{�b,���z��
�%#�4�C����QK@4?������l��E�r�Z�^,4^�h����P)�#�P����64�UY�J@��CE�������Kd
��IV���N 6����y��Z���*i(�w5�frF@��E�W���7�MI��2Ng����t�#�����,]n��@>$)���
���(��L�
Y^i
����8Uv��b�������N��#��~���n��Q�J��;��T�{{�+\��9�Q�Uo�g���@Doe�H���r�������VN�����U����+|�����3��v��k�B�S��4w��%OC'���0a'\b5'..G"���>��4��y{M�Bo)�u_
��"s�P&��k�/���M�c�E�Q�#�� w��5���'B5Z���\�����|JK\u
��'ER�R�M�X�=���U�\9��G�2p�e:Z�u����b�|��@�)���^��!������W�=������.�G��Ul��}�Ni��b����u����L��%^%��7�]M��,�]S]�ZkQ�w-d��W]
B0�*����/���y�:�h�����<�ZR �XJ�v�\Q=��b�y�|F�W;�}&����!���:H��Y
��R�������E]����4';�C�rw-�--��di�C���u��J"�Q��7�
�����aN��.)�nU�h���zz���3%g,������:���V�J0���w�hN���P���G�I_�@4�2�L��$�4}o<�����������k:z	1�'�������N?U���\�.u���\�4�* �O�W�S>5FZ�#&�s=?��B/����#7s'�Ej���D-�p��W�����fm?_�j��unj��J�D:���Yc7j���
_��U.ur���_4���i���&�i�qGS�}�=�����@��m�>���S��t�@&���'�������J�����m�B:���t�,����?��
*���O)�j#n�n[��
����d��#�uke���q+��%�y0���Nj�~����a���U��YI����D;�]A����EN�v��~�p0�/�`��j�����b�Cm�V��_1���[%SC���=��z���Hk�g5\�6������,@��byL���&3����������s�
��7~��;���%��zj�����S"����L����e�q}G�<M�^�u�T����������p���[�~�b�J�&�pOd��I��C{kM�FP��<_NOd���.�2&�/s���8�����n�\|�zO�R�����^JN��l�M��V�BC�nE0n��o>e�+���7d<8���G��.��c
�N���(��=c_^R��T,X%V���#�2.
�feICD�E�v���J�3�k+���qPg���U�U�^����
[����e]��;�NJL��e�����O
uh=J$��%�i�z;�-{����	�����.Q���"G�@}=/�x���<0y`k�P�x����k�f |��ke�}h'���o�[o��K)�|[���*l����6^?w���] Zv$���d�Vaq�[����B�jV���U������$�/�_��*������9�dB��;�\��!#"��p#��5PQ"�h���}������L"(�v-:����:u�)��c���x��]����.�����e���)%-���:���k������j
���6Wr�X%�P�|��%j'���+��(����*�qq�s�wM�>E	�����'�Cuz�&�+�!���t
��;s��U(K�T� ��*"���i�6�*��[��Jj����"��?�{�4�`9�U�^1�*��0��^I�s����@
h�pB����������7u27����8�q�����B/�#7p"?M}�����R��JKaeM���3�'���������4���
��v6�b�1[Xo$��������Zy?/n����(:���fol��!{F��$�H���t�[�Y�����]���.[�&�
��gRfK�Jh�z����V��n����U6�G7�[��-�Lr��f�<�0�K9�VFU�5s�	J�I�E*���_��I��,���'�h�yLF����C��g�xw{���/�H���X�������S�<�����9Q���Ig��1\��!;��*�	���T���mX4PT��u�xrm��F��Y�U����a�N���e���������4�������f�\`J&�D�7��
��.>)F�B���P�&��WF��]��B��WGE*��0i?2��]������G��<��^�s�Qdp��1%_������Y����>����r��d8���i�IQ��dR����c=P[h>o9��=N������T��>^����u_Vb����u��c���q�Z5�P�^���A����.���k��6A��l�'W�����D����I;���^����mSHy:9�����������fu�Gc��o��h�
���_xQ ?8?#N�������q�:�������Mk`�<�G@���)�^��y
0���E���j�_��4�6>�X..������s�?8�x���&����>�D������N<��zR�"��Z��� T�K�K�,�oC�\T������O�m�����Q��MYXQ��Ob���/���Cl��?��}K@E������y]R}��P�:k�^��Z{zC�@��6���z�c�wk�zs�������6��/�d������@����Mv3^|������{=��8^
����*��?	�Ig��0�����g�AkZ��ikJ�L����_����|��r��_t���
���M�g���������4I� �H���k�$���������$�1=�2e&��J\���z��/������SzSI8�<��5Y������?s���r!j�{z*'2�P���G�����d��{O��z�����ZaM�S��KLO��c1��#"!�f���p���R;��?A��l���'�!"�$��a�������ocV���%�]�.�c���]i�������X�{�8m�@2�����������6�m����Sq)���:[��lk%�,��_��Ai$eb]~��B/M�}�����'�N�U��_����
spBG-
�����7�'�go�[�1���A�7��14$��-����w��]������w/�8?���� �
����h�/�HDNjE&n)���:�1�^*��M��l���6�^f@I\����r�o�_������I&���������
�vH����gfS�wem[���1����lVe�Y�)��h�����}TU[o����P�)�K>�)�taEW�95�O=����a!�mH���x}��
�O��������0@�:��/6�tdH/�<���k|���K	��{�J���:k��e��A)���^������J��B��^X��D�F�IB�^K!�-��2����)�������GA�`G�y(]�M��S�J�j-kN���EQ�z0������|.o$��x3 ��-���2�7�G�������O,u@�dm�y��|��g�����Y]���u�k��i�D
.����� �J�zj�E\Z��(������	���&��)��P~�����FC.3��#1pg(���BH,&%��C���o8l�L?�x���0j�WC��q5��#X�@g}��|[N�����{�����8/T���hg��:���:�Lg�U����/����<?��<x�����U-.\2^���XvF}|�J�Q��$�F���;w
N/k����S�@��|#��&�R����\~�j�����K���������50��?<|{�W�T���[���FS:�M�(��z�1��& ��wh�7@ �S��?t��@H@(��(�.��R�?�f�����t��v^�P1�l�R�Q�iP���R��?
T%q�p!�"j��Of��|`�n� ����Fg
-�i%"���|�Oex��	���)����������������e
lOZ��s��S�e�t�O�;Z�����g���"���3Hj�	�K>n�YJ��p�\s��.��
�T�g]�o�5c�H%�P	��o��
QM/�_/�������p�'
I�"��;TlF"U�&�[�\���U�~��M�G+�$�����Rm���g����i`��cZ�
DI:TH��r�����aC@�>_�����@�
�8��
Y�������5���_g���)���6�C��������
�[N#_}���!e(��e.������	�JH��Fv���
�����V-�.�^�N�p,��|l�����J=�v	�mQO"�E�F���Hoq<;���}�~�6]^�����u���]��T�$Acvu�)z���������b	]'K������C"�_���_aa{�m"=��fb������� ��W[��%��h������o�����Q���BrD���Y��^u�E���I�!izXQ=/�����'o��~��6������4�a�RH$s�����E��Y�V�n������cE=�~���z7���\��������b�����$��aw�U��`z��
�o�t6MJ7���������9m���%\_0���25���nMx�9p����-����*���.��H$I<�1�K�&�(�_�$l�#H��C���z�5�R>��I1w{]��%�H ��Z=����>��@��VTvs����n5t��#z�W%I$�/���������������$*�����&���5*-���;�<Z
���
y����P��BI��X��K��ZM�$��N��ml���*����=fK#CnY�i(u~"��X�]��"n�F���l����F� ������;�D����at�����B�����1��6�j��z���NL������;���?1#�����^h�hPCA�cWfn:�!���.��y��|�C� �`��Sa)$U�2��BS��Q�����|O�.�A/�\=�^����||\�X?�U��/����qS�1[oo� �4i����~��C�$���l0�F*�y�|���D��DW��v��������k�Z
"c�z�Z����S�����6l�j����6�k����Q�������o}K�1��~E����cX�f�1�5ls�^����k��D!*���k��G�
�(@r�A��iA�-��f0(���q��
�Z��X���p��};�Y�����?sx�	��g���Z�N]�!�.[�N?+�
�prt7Di���G
��A���*�D�� �I@��������0�?�B��~7'|�F65R�N5���D�����A?5����_B
�(���<*�PD�L���RreX��N���;x:*>d�����LF��t?��W���y�s���+5'�9����6�Qnca����!�M�1e�}�C(��m�0���T/���	i�w���D�W"��Hs�Z�f���ZM_��%�����G\7N�#��I�D�>�!�b���im�~R�lIZ���eA?t����Y>����MEjP��*{�D8���fD�)��/C�s�G@�b���:�|�P��|��/��2jx(��O���!������d�������������C�s�������J1G4�C{�$��0��������^�������������������1h<�m��/��T�~����B@���O���2���w��X-�=�����������>��������o�'��vZ~D��Y�^��|{��p��j�����o�c�#fnn��sG��QH�e"��WB�x�Q��un�M�VA��jp�b^�qB)���

� ��;� ���z���q�J&D���1�1\��)�s�^"����-������m4�����
���W>����{�����!��l�������&�(����13I�
��J�'�+9"�����d�#ERK[/1����z<^~��m��M���a�W���c
�@Nh�'`/Ma��� 9�)�v���4�)�W_�n����P�?t��C>��Pup��qA\C4C d�O7P��'p��pBe�\�s�
������I��eRT���t7�;� Y�I������g�%���"?������
<�x%��O�y��z�'e�mE��QOX�l�����m�= �1Q��I���R��w��Y�
���P
��i�ZT�
j��%����
_���P�=yu�������������������{n�Vq�����'DSA��n�JA�K��Sqg�BW4?����j�X����L=����r��f�C����'tjV���hm-6kX�,m�IY����_�Q������d_2��o� ����]���T�SEwm������+�O(�
�0=��Yz>,o�����Q���
��"*B&��uo�4�������/%��`��r���%@�|�����v
���56��oh��ru�����,�����$������D!�������=���a�,;������A:g�p��:���x��K�6h��)���d'�71z���br�v���|�7:z���e����y2��Uk�-���P|`}CN�@l�J��s�}a��u���$���m�.��JR����M�dF�qk���z�mp����'^u��e�6 �AW����*kBBv��J��%�/���v�Ii:��M@��������0����0�S�t�#Kw�_]�*38�k�� %;R�����V5:���u�7����Y�?�`G� ������7�md���O�h�	iQ4��V�)YT���J�c��L�@����iI��w�w��ARv2S�E�bI@����w�V]U�l`b�f�Y$/�Q���]�(����
�{�(�+���,k��
���5*�t=�N�!��D���$f�	]7���}�7�@V
rK�q8����1�U�*By)��$�����g?!	���n�C�Z��O��mZ��p������c����u}8j��Q^�<�gV?�[ "���N���~���5e�t�����>
-/v�rO������,!�%�(�(e��-8	� ���X
��N�y��P��H�p������~qr�95��~���t��i�����.P�����%����T�|!�^E��?qn��.���C��7����
���#z��8��;�1;�4�Xm�2v8���xd��4�p���4���eBv�@��I�����|���R_��z�X�['��8�,B���`��F�O
H���)�O�b���0��Q����!���EII,:�xc��3�zOe��]C�(�:�����6K�>z
nd���J��`���J=`���h�����c^��zhSXqH}h����8fJM������4x�a�K�@��A����!o)���[��E���0w�)�T����>�o���\ ����i����������5�j*y�`��de~z�&�x��?zW��W�tOV�������/�h�i�^{[��7�13\��\$��f��A����M�*�B�p�oQ�o�J�-�)������W����RiNkC���6�T"�a�{
�W,�~�������6�f�^J�;�q�)�������c�V��������D����J����G<���}�����W��/+xP@���/���'�*�$��F�/}�h^r������)�+������f7;�To�s��hP��~%�7��J���K�J�O�hv=f.����q5����%��l��#���X��U��� 8�;�+.I-B��{�����a���GY��|��|��=Z��(�4������8�pv�`�6�P^�i�;�=9���B���&;����3��C��kr��-=�eN�UH�����~uX�<,5��J��r���P��f�UV%i
��+��E�&�G2��x?���yV��Xp������]��:��6������a�!�p4������0_��x�K��Yq��Z���$C�QU�U�6b��L������U��q���5�@����M��S�"d�RE���n�����t��y���A�+���hos���g�!s��Ag�b�K;�H8F���\Sf�<��R_����(�aG����D�TR��FJ*V(���z��7��j�T����^�i�M���S��nF����RJql�q 89��9��{�9�������{���(xvqsz���.=�]������JX�
X�Y"mX��o�7��^A��o�����`��L�I�����z��	n�M�*�Xjd3.O�8�M,\@�{;S���E*����L�����s��+&�d��a�f&!e!�8�������u�`Q�n�\5�����2��4�ng���s(�~ >ID��'�P!�=�R��<E�)����B�	u����6���4s��2
��V���B��{���j�z��6�U���n��mrV�&���k���w��P���x(�:J�c#��
������U�|�	MVc�r���c�~���Q��C�3���s�IV�=.v�M,�<�����nF���%M9'�D��A��bar|R���1��a�h����3{6�m��U��"��7�����9>xQ�E]I���P'�����43��5��F�%y��,��imj��|�!��s�%
�s�<�
U����j�V�������e��dA�B���H�\����C���{���/�j����(�T��2�N��S^��"ie_�x��OSZj�{8yo��z�;F�D������[l�����q��H@����rvnU�F�Z�A���K��hP2*K/���d�lPhC��|R��L�1�������7�����>�S������v3Q���b��7c�/���".5��~���
x�

f�K��7�/�F����W\��R��Z��*��`P.���pX��p�Q���j�6���
�����������a>�4q��
�z�:
��X�!P�y;�X�����2�v����Z5$^�&~:�oE��
�V*
`�3o~������;�����=?���%<��O�D��.��T!0�y��u5���|���!Y� u	�2����Q/������R�W�"NHkg% E=\��rxD/Tv����f��*DE.(	V@�KU��*#��CSF0������rJ�n��l�/0�C;��|����h�\�4�uh>Ru_��~��@�?>y������U3�����e� ��.�+�nvb@W��L��Pm�^0\��)��)N��J�qH_�*�/j[����c��!�jB4�1i�����}Hh9	W�+>�j���3�`H6�^R�	�fw�xe%�!�����B���6
P6�����)���f<]I�����u<�<��6#��{�-�<,�8�&"<RCB����v���W�������URM~��\�
(�w���P����F�n�WS(�N����a_F�Cy�l��U������@�bq�	�I�~6���Z����>�]�s<�	��9]e0'Mr(�K�g��W!�E�8�-�v-�!�D&has`�B�~��.���`����_���pg��D�L4"�z�^0��u7����i����	�}�>��pN��<,���/f��2�u$*�/�-�`F��	�ZF+��x6+��
]��(����)KW��+7Ki�$��B��5��IH�`l#�V����Z�D0x9�|��z�	
�z���������*������^��*�|���@������o��|#|rC������:^�A�����7S�Dj`�x�I��G�L�5��F��N7dGA���s�h4��b���V�
�lt�vU���*L��&���&+�H��$�Ry����{�:�D<��c8���GG�[������G�*��\H����>&I�&��@��4�W�[OR/�N�2�Ar�O�X�+x�%�u��$�g��� -}���7Y��S%�	%	�38r����w�{v��W��"�C�������.�R&�dt����/�^`��"���	2����h���e�`��2vA> �:�-�c}��:�Y�Dp��������vLg2:�,�bX�!����vz���K����o�;��y�/�~t�Zf��}��3:��<�/N?v.��O�Mg�s#'K���j�ZA�\,���_�6[��1���2	H�$�E��r��LL^f���9
&��-��h�.f_����`��y �+��A��%��#��� �i��H6�=\�g{K��(d�?�J�E)?�9����T:3m��Q0���s����[�q���� �h'��V����H��T��F�H��Rf��A!F8�GnxT�i>��ek�EJq�|�r6�98rU��~��&o	�=s����T'a����IMrn]U�es�;�,������1���o4u�v��]�Gee��U��Jr>�+V�gnC�<�����
���a��}��$TpECT�h��;�!��+:%M��Ji����W���&R��������C����~�8��:�o�0��k#y�r����!�O:���f���0�9tL��a1u��x��9�����z�_��<�9wb�M�V�8H��V����7w�p����N�
�O��Qj$��t�h�;��w.�b�&��Z�1n�����\�XL"cH���������&<�a���� ���=o��9B��O���o����t#7�n����en�<c���M������`�I��pT�(hB�D:��_iN�n��������F�����|-�I�4�*��d	�x/5Yk�=P�o��#�S&zf9Re�������-;a�rp��@�S������=�	�����eT�C�2�R��5'��q�X���,��]�|���j�G@sr�lU�K�	�o�2���Vb42%�&����E��>�=A�N���s��1�o�)�&e2���94��s����8�K��S���L��
nz~)��%��*��5Y��6dCl�n�e�����%[��Ii��A���7.��W�$���w1�A	����M�������%�j��]j!����q���(���,(uF��-,��M�X�pF�R���tg#
Y�����-I���l��T���?E�Z/I%��-�����q�6b��o��1�Dxa�G�����/��c[L��������HF+8��f�l09b������@���Prc�<5�2a��o�B�A�����C{^r4�m-�������b#fs'VO=�������1X_�s�U���pAf��#n$����&���/�l60��b�Q����+m�B:k|:ds�r0���D����b>�x���dr��x8����qU��%|D���WY�_����# ``W����W����'��t�����?[N�5P�����?	d ���=���7\���i��oK��
_�������V��E!���|,�J�l%���F��2��J�����5�J��:��V���kr�J���mr��S�H�!5�L��<@R���4�?+�k��][�Y+e�0��#XR=f6T� �2b��D�
�����A^e`��5��@}���j%�N� �t�#
^�T,#��j;�K�_ ��QnD�o�������n6T ,�#��`�	�T�F.p�X&��R��{��L`���s��,X�8���J��"����\�����v���>�)g���'�
%GE�!$6�+w��BV�&IK��$��CU�=�/$����}�#	%�7��ul}����m�������[�+���sW(���4���o7g���\zVT�����u�{r}�����S��r*��y���X����fm�h�c�^�RGzCu7��~!F2�B)��v�\��
�M0������&�G���
�n����#�s!?X�C������wM~��j1G�9Su����$��RL��
�<AW�0���D#%�8�&��E��0C�������R�S����A�X��FO�r.l�3��a����^�9�T��o���(���og��vupG��al:�F�=H����7'l$G��y��A��0�M��6��0
<�F:������6v�T<3}����0X��_��#E���j
���*�����!��2��SR���]]�UNj�k�a�a�h�Y`L�t�	�����2�=<��)��R�� ��x�^.`n�z�R(��pz���C���8���M*�h<����N���������L4� ��:����"��L��'EF�B��>�$9����VP��Y���Vq|1T�����1������*��8C=hy)�w�c����6�o����?�=���!#������AL�wZ�I �0����d��'����D�A9��*���#��������.)e�X[�\
�w�_��K<��$���~��R�����E�-�����'�@Z �L�l�g��=�3D���h�SA��C��h�je��t��E!>7E�� �!	�*V�(ZF�=5~�d1�P5y/�������U�K�-�����DE����L5���h��������Ji��zvE3�s���x���"	���h|yTm�
�YIH(��=�A���f�O�D��`�����~��%sqP�~�
��Y��������9%n`�+�M`�4z�����F�����~��I@���Q��z1�^�G:n)�0Em�C�I'��ClB�6p&:i��d�2��^NZ������t<���n���#���-|%���b2������X�i����h0��>-��`��X,�r���p�W�5�@��1��<a�6���jo������i�%�Hx&�H^�u2X�$��P�0������
�J)d��ky�h���"�2k[3;	a�f���	O�/����bE#7I���P�r��%��W�M{�l_��S�N9���T��F�<���+l� r��Rh�nA��7�
#�)�,�~(����>�vH	�$�<H7�y'
�)�8u��-�[A?T����������8y2+K�Np����j�PO�z�������j����E�<���oZ�m )fp��J�������gq>j9��O��"��f���5��a���-�������2TAh�$5q+s���(�ky�I���N���$�Q�6V4w�H}lr	A������K��Yn�w#�W�m�����
������
��l+[~��Ex���}X�')��s�\��(���3��5����?k�>/D�Z�Y�,�74����V7�s����ZF��h�OP8�~Q�b��w[���O[�����qKO��z6�h7�&1%���&j��e-826�W����t.%Af�$������f��t��PYG3�1H
�y�X��'�6Yg#����zXb?�I���RB�S��u_���:�p(��A��qG����`�qL���7�_����6`�`*gZ���z�5��qS�����,��Pl�P���_K�s�|���9>�Yv��U�6�_6�<I(e������`g�U���F�2�N�5�G��m�8{k��BI�N�]�����c���z�<
�x&
������W�
l��%	�l�Id������?-0�u(�=�ef�ut`�k��W��;��%������z�G��r^��
���3���L.��/�����N����_�3���x���^��6e�=>�N�H���(�/�V:�gcb�[�D1Y�J"E���r5yv�9j��m���$�u�����O	s���(U���������[�;k�D,����m�S�h����d+~������DlA�'X,�~�Ym�}W~�?[�R�)������a�F�"���<m�^8��w{��$!���������W�����{��R��`#�r#Jw��6f�A�>����m��t�1�%![�
v�������|�/������V
*�r��H*�]��&U�\'k5vu����
���,9�&�8�����<�D	OqHo�i�n4�����O��0�8�i��O���SH#{���PV�1�F}�I�w��wP��G��#Af���i���C?i��8;�]E��b��:`3�O�A������!�b87�gV�����_������63�[����g����i.�X	������J�WRM`�����0Zat,��X�XB���X}�4�����������_i�~�U*~��j�Z�5q������U�,�-"��(�����m���6���\LQ^���h�r�r6cSv��k������y
9�������Cc����-lF.^�Df��An{A�5s�����m[RfrP+
���^*���_��J���}�M��]W���	U��E�]��a���cf�.k<�]�����������"�M��:�I�[�r�����- �H���#���qZ~w������
&�-t�=h�{��}"��%H^��j1
O1���[�������[����D�L	�u�O�'�GX�����>T����[�N����#���Dn�(�������s�n��/�����h�GwM����0:�7�H�}�@��]��1vCje������I��A�������"��;�9{wz�9~w����'���VA�� �
���e<��>zv�i&:d&���k3�� ����3-�!��)�x�r�	{I��k����������m��M�����r�����Kn]�c/4��&�,���7��8\���k��D�&_jZ_Bf	z�E�����
nf�[R��ul#���;���49���Z�N)b�$J�mK��>a��K�p=�o6��������
����w�VC8�(j4X����������N;���)L��
�R7�.�uz��y�0�W�W���[|�b|��{��U*�7�`�����m�w���h�+;����6F �����Du��B����eg��no)Jxa��?�|��k�Z���z��h�vV�~)@��a�Qj��%S������(G	�8�4�Y�(�XXt�Fc�����p���� >�����uf-����J�o���2�~/+���IpP�'��W�e�'��Ky�gH�z�)�D@<#���DJ���E�����7�����m�f|�X&Q��k��F��:m'[��K��7	���|��������w��d���?�
'?��D85�[�-g.��w��h������v�)4cD	gf�'��1���������dCo8��_��L��Y�� N`Z�(���=�"g
���5rto���
���;o��P�n�^�gm���d����C��� ���L�w�o��!���-@�����`���f�V��a�����U��s�	gQYz�����?���#����d���eV�+�+�����Z����[.TQ�ED�}G�k [��t�f�3 
��??�c�V�!�����
yD����$dI��v{S�B�a�c������n��������A�����b���W��r��i#VU6�F��7>�7~��:6������V���0��e��\)9-�;���:�HJ{�[9�`WH����������C�3K2��#�W	���<�nC���#R�B���.D��N"���#��R0yZ��Mr	�D�$���:�)}3'U�P����_*�k�WiUC��}]Mk�.��MZ~���0#��ZE��/�OqI���mgt�
o���>G����,E�O��0�<C����}��.�%�
����8�Dx?�P��J�q��?���hZ�K �b��A����<��g�[:�������l��8+l�/�*����v������J4jfb�B{0�k�~l��t����L�2S���-��A�+E��kw�3���]�+H��_�	jf�7ZP3T���,!�+�]��%�U4����x%�V�6+���[yms�Z�����KU��
��L���N��-(U�Tc�O���MM�B���>97�^K���<�t{�Em����B�b�uX����Of���F
j5��t���U=����-{�;�e���Vc��
�*����h�c���G�������H�wr����X����

��;A������ZAo�C%�6�F=�0���V��Z/�JT��~�-������yu}y���}���s�7DI��U�"
*�Hr�b���$�Q�����n�9f������g�;���j4���g
�6��v�����g�o��lpWo���g��.�;����85=�nw�����v���]�kw�����v���]�kw�����v���]�kw�����v���]�kw�����v\�^j��h
#321Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#320)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Dec 4, 2020 at 1:50 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

The earlier version of the patch having all these ideas
implemented is attached
(Infrastructure-to-execute-pending-undo-actions and
Provide-interfaces-to-store-and-fetch-undo-records). The second one
has some APIs used by the first one but the main concepts were
implemented in the first one
(Infrastructure-to-execute-pending-undo-actions). I see that in the
current version these can't be used as it is but still it can give us
a good start point and we might be able to either re-use some code and
or ideas from these patches.

Is there a branch with these patches applied? They reference some functions
that I don't see in [1]. I'd like to examine if / how my approach can be
aligned with the current zheap design.

Can you once check in the patch-set attached in the email [1]/messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com -- With Regards, Amit Kapila.?

[1]: /messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com -- With Regards, Amit Kapila.
--
With Regards,
Amit Kapila.

#322Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Antonin Houska (#320)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Dec 04, 2020 at 10:22:42AM +0100, Antonin Houska wrote:
Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Nov 13, 2020 at 6:02 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Nov 12, 2020 at 2:45 PM Antonin Houska <ah@cybertec.at> wrote:

If you want to track at undo record level, then won't it lead to
performance overhead and probably additional WAL overhead considering
this action needs to be WAL-logged. I think recording at page-level
might be a better idea.

I'm not worried about WAL because the undo execution needs to be WAL-logged
anyway - see smgr_undo() in the 0005- part of the patch set. What needs to be
evaluated regarding performance is the (exclusive) locking of the page that
carries the progress information.

That is just for one kind of smgr, think how you will do it for
something like zheap. Their idea is to collect all the undo records
(unless the undo for a transaction is very large) for one zheap-page
and apply them together, so maintaining the status at each undo record
level will surely lead to a large amount of additional WAL. See below
how and why we have decided to do it differently.

I'm still not sure whether this info should
be on every page or only in the chunk header. In either case, we have a
problem if there are two or more chunks created by different transactions on
the same page, and if more than on of these transactions need to perform
undo. I tend to believe that this should happen rarely though.

I think we need to maintain this information at the transaction level
and need to update it after processing a few blocks, at least that is
what was decided and implemented earlier. We also need to update it
when the log is switched or all the actions of the transaction were
applied. The reasoning is that for short transactions it won't matter
and for larger transactions, it is good to update it after a few pages
to avoid WAL and locking overhead. Also, it is better if we collect
the undo in bulk, this is proved to be beneficial for large
transactions.

Attached is what I originally did not include in the patch series, see the
part 0012. I have no better idea so far. The progress information is stored in
the chunk header.

To avoid too frequent locking, maybe the UpdateLastAppliedRecord() function
can be modified so it recognizes when it's necessary to update the progress
info. Also the user (zheap) should think when it should call the function.
Since I've included 0012 now as a prerequisite for discarding (0013),
currently it's only necessary to update the progress at undo log chunk
boundary.

In this version of the patch series I wanted to publish the remaining ideas I
haven't published yet.

Thanks for the updated patch. As I've mentioned off the list I'm slowly
looking through it with the intent to concentrate on undo progress
tracking. But before I will post anything I want to mention couple of
strange issues I see, otherwise I will forget for sure. Maybe it's
already known, but running several times 'make installcheck' against a
freshly build postgres with the patch applied from time to time I
observe various errors.

This one happens on a crash recovery, seems like
UndoRecordSetXLogBufData has usr_type = USRT_INVALID and is involved in
the replay process:

TRAP: FailedAssertion("page_offset + this_page_bytes <= uph->ud_insertion_point", File: "undopage.c", Line: 300)
postgres: startup recovering 000000010000000000000012(ExceptionalCondition+0xa1)[0x558b38b8a350]
postgres: startup recovering 000000010000000000000012(UndoPageSkipOverwrite+0x0)[0x558b38761b7e]
postgres: startup recovering 000000010000000000000012(UndoReplay+0xa1d)[0x558b38766f32]
postgres: startup recovering 000000010000000000000012(XactUndoReplay+0x77)[0x558b38769281]
postgres: startup recovering 000000010000000000000012(smgr_redo+0x1af)[0x558b387aa7bd]

This one is somewhat similar:

TRAP: FailedAssertion("page_offset >= SizeOfUndoPageHeaderData", File: "undopage.c", Line: 287)
postgres: undo worker for database 36893 (ExceptionalCondition+0xa1)[0x5559c90f1350]
postgres: undo worker for database 36893 (UndoPageOverwrite+0xa6)[0x5559c8cc8ae3]
postgres: undo worker for database 36893 (UpdateLastAppliedRecord+0xbe)[0x5559c8ccd008]
postgres: undo worker for database 36893 (smgr_undo+0xa6)[0x5559c8d11989]

There are also here and there messages about not found undo files:

ERROR: cannot open undo segment file 'base/undo/000008.0000020000': No such file or directory
WARNING: failed to undo transaction

I haven't found out the trigger yet, but got an impression that it
happens after create_table tests.

#323Antonin Houska
ah@cybertec.at
In reply to: Dmitry Dolgov (#322)
Re: POC: Cleaning up orphaned files using undo logs

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Thanks for the updated patch. As I've mentioned off the list I'm slowly
looking through it with the intent to concentrate on undo progress
tracking. But before I will post anything I want to mention couple of
strange issues I see, otherwise I will forget for sure. Maybe it's
already known, but running several times 'make installcheck' against a
freshly build postgres with the patch applied from time to time I
observe various errors.

This one happens on a crash recovery, seems like
UndoRecordSetXLogBufData has usr_type = USRT_INVALID and is involved in
the replay process:

TRAP: FailedAssertion("page_offset + this_page_bytes <= uph->ud_insertion_point", File: "undopage.c", Line: 300)
postgres: startup recovering 000000010000000000000012(ExceptionalCondition+0xa1)[0x558b38b8a350]
postgres: startup recovering 000000010000000000000012(UndoPageSkipOverwrite+0x0)[0x558b38761b7e]
postgres: startup recovering 000000010000000000000012(UndoReplay+0xa1d)[0x558b38766f32]
postgres: startup recovering 000000010000000000000012(XactUndoReplay+0x77)[0x558b38769281]
postgres: startup recovering 000000010000000000000012(smgr_redo+0x1af)[0x558b387aa7bd]

This one is somewhat similar:

TRAP: FailedAssertion("page_offset >= SizeOfUndoPageHeaderData", File: "undopage.c", Line: 287)
postgres: undo worker for database 36893 (ExceptionalCondition+0xa1)[0x5559c90f1350]
postgres: undo worker for database 36893 (UndoPageOverwrite+0xa6)[0x5559c8cc8ae3]
postgres: undo worker for database 36893 (UpdateLastAppliedRecord+0xbe)[0x5559c8ccd008]
postgres: undo worker for database 36893 (smgr_undo+0xa6)[0x5559c8d11989]

Well, on repeated run of the test I could also hit the first one. I could fix
it and will post a new version of the patch (along with some other small
changes) this week.

There are also here and there messages about not found undo files:

ERROR: cannot open undo segment file 'base/undo/000008.0000020000': No such file or directory
WARNING: failed to undo transaction

I don't see this one in the log so far, will try again.

Thanks for the report!

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#324Antonin Houska
ah@cybertec.at
In reply to: Antonin Houska (#323)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Antonin Houska <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Thanks for the updated patch. As I've mentioned off the list I'm slowly
looking through it with the intent to concentrate on undo progress
tracking. But before I will post anything I want to mention couple of
strange issues I see, otherwise I will forget for sure. Maybe it's
already known, but running several times 'make installcheck' against a
freshly build postgres with the patch applied from time to time I
observe various errors.

This one happens on a crash recovery, seems like
UndoRecordSetXLogBufData has usr_type = USRT_INVALID and is involved in
the replay process:

TRAP: FailedAssertion("page_offset + this_page_bytes <= uph->ud_insertion_point", File: "undopage.c", Line: 300)
postgres: startup recovering 000000010000000000000012(ExceptionalCondition+0xa1)[0x558b38b8a350]
postgres: startup recovering 000000010000000000000012(UndoPageSkipOverwrite+0x0)[0x558b38761b7e]
postgres: startup recovering 000000010000000000000012(UndoReplay+0xa1d)[0x558b38766f32]
postgres: startup recovering 000000010000000000000012(XactUndoReplay+0x77)[0x558b38769281]
postgres: startup recovering 000000010000000000000012(smgr_redo+0x1af)[0x558b387aa7bd]

This one is somewhat similar:

TRAP: FailedAssertion("page_offset >= SizeOfUndoPageHeaderData", File: "undopage.c", Line: 287)
postgres: undo worker for database 36893 (ExceptionalCondition+0xa1)[0x5559c90f1350]
postgres: undo worker for database 36893 (UndoPageOverwrite+0xa6)[0x5559c8cc8ae3]
postgres: undo worker for database 36893 (UpdateLastAppliedRecord+0xbe)[0x5559c8ccd008]
postgres: undo worker for database 36893 (smgr_undo+0xa6)[0x5559c8d11989]

Well, on repeated run of the test I could also hit the first one. I could fix
it and will post a new version of the patch (along with some other small
changes) this week.

Attached is the next version. Changes done:

* Removed the progress tracking and implemented undo discarding in a simpler
way. Now, instead of maintaining the pointer to the last record applied,
only a boolean field in the chunk header is set when ROLLBACK is
done. This helps to determine whether the undo of a non-committed
transaction can be discarded.

* Removed the "undo worker" that the previous version only used to apply the
undo after crash recovery. The startup process does the work now.

* Umplemented cleanup after crashed CREATE DATABASE and ALTER DATABASE ... SET TABLESPACE.

BTW, I wonder if this change allows these commands to be executed in a
transaction block. I think the reason to prohibit that is to minimize the
window between creation of the files and transaction commit - if the
server crashes in that window, the new database files survive but the
catalog changes don't. But maybe there are other reasons. (I don't claim
it's terribly useful to create database in a transaction block though
because the client cannot connect to it w/o leaving the current
transaction.)

* Reordered the diffs, i.e. moved the discarding in front of the actual
features.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210129.tgzapplication/x-gzipDownload
�HB`�}�{�F���*���CI$���c��mk�k%9�l&?hJ�%k&y���h\<$e����KD�����������*�Z�^�7��������j�v[�g����g��j���'��Z�]k�:������f��D��N��"-�b�>�5���_��"���2���\Mo�c�z��ys9�X3��n1K�������^b�j��[���Z��������v��i�E����a�k�����u������{S��F�Z��[}K�=Y�}G6��q��k�k��������7Wr.��Ik��?�����f^��Y������-�/������a$�P�U+|�}d�����]�E�'�h�b���^�k��
�W��j�W��_��?_\~r�_��"-��=����#1E������XR�c(S�����L��/��	�7�Z�i9��0%ho>���7q�}9��iP����:�p),�f1��P�e �[+����M<�K���	o��]���gSQ��]�
��0p9����6��|pR�'��3��<�S&q����_��E�����9MX�����B���/b���J0�[7 �L%>+������U���������;k����� pG	��u�������7YL	�8���pD��-~����L�w�KX~��=��d2�`�8c-�y3a����N��lsi�c�A�C���zwRx����+��x.|��N����+J��	4�/f8^���x`M��Q1��C]������M�@�.f��}��"�)��a8^�����W@��/��N���?�A`���������m������8��S������pw�9����u8s�~���O�O_��8��A�^�����Vmj���
����v�R��!�B��yS����f����~6�Q-�L���>���j-�V�VSU*�z��ZU�r�ZW��Q��p���y+��P�a��V<���*��&+'��9����_E�����-��|��f:�zfo�(ja[`�f9s�-�r�?���~�x7����S�j���:\�Y\-��h�N�to��e��j��j�VpK��V���*.����A���k�J�&���<��0��n[�c�z����P�w��ec��B_J���b����/���-`\m���������fW�����f�5���9 �\�?]-g?�*:=��=��{l'5a��heep�91���8����;	�i��l���i{�����3�D�l��N�)`8tj�QmO� ��dQ����k�����&����`��{��tvf� y�}��J&�@s0^Ac���G��9J2��\�0_��'1��]H]o;�������t�F�`��{K�!C�?ur_�����4d
�F���j��yN=X�NI���#p��s�d�a��&�[�o|�<�p��_����0��[g1�_s&H#�jO;Y@Wj#~���z��a4�j1�HWNQ����Pd+���b2�*�U�����P�3+U7I�r�����%A�����J�U���@�*X�;��Xt�����NY4m�K�<|iX�F�'9�����KS ?@uH��?�dd�A�Y t��{��j����}����� �T*7.�V�������6l8�U8��-���nw
�e5Gv���������a{,��?�CT��Z��?��hB<������������u�p%�C���bz<#��4�����������������`��������Pq���5Vy�;C|V�g������?�}:�o�����(��bwG����BB��-�IC5�i�e���[�;�gxX��T��{��(�O��b�fp�[���X@���|,)�F�J(�iX���i�:�j�Uk��m�^���LQKI�)*��A�����=���x%��?��r�0�=��zpvu|~%���+G������yN;�^�y{OU�o[����!���#=3|W_6��`rs������hpu�|���T���\ ���A% �t\����&<�_0�����-\��tu:��V�+���v����j��CX����l1�,���=#����g��^m���>�������m8������.@���y(\TI-|���P�,|�e����;�'�������,�[O|
�x�z���}�4��s�����L�x���:kU���7�~�<8������4�����u�U�`�l�(Lz����Ev0���������k�Swb�������s[�����l�*�s��p�)�N��l��lZ���������f��6������xQ��	�7"���V�'�<?~O^��M!�M�~�B�%�����f���������t�������dp"��^��#����d��wr��:����������=qpT^�����xy~Z����p%~�"tBy�Zv`q�(�u�I������v�pz-Q=�����\����A��W�%q\����`1�������`Ssw6��d��?�r[�|����5�q���d����cS���Q���}�{ohne@�~�~gI�q����]��r�����'�PPZ��O�N�C#M��_�WT��}��}���kX�=��F��BQ�����6��6� ������O�����N~����.������+�[�I8��>��Jp0�������6��	.OxK�^���;�I��;��P�C�
���~�
F�kBM�W+X9�nl�`k�uZ�����Q��`T�V�]����*�\�H�����?����43|�����������%���������VoUI*���[��zmUQ]L =<=?�t2�|�*h��v�#��/I��Bt��s	������������?�T@h���-M"���E���xS���Z������Ox�Kj`��
.?\���.�d`��Iv-�4�O|soJ��\��t�Q�����I�[Q�c���4��`Y���?#Z�#�e�B����`Q����u~B0�Q#	{�v�~�0��I�����3U�G�H�j�V��Q��f�0_�r������s�N�������������c�a��urZ����T������yM$�{��u�Jm<�^�n<�w��'O6K����������_��88�R�I�x�f�����7]�3}Q�V�(�f����{�+�u�[c;�Zy������ >2��v��Yo7����[|f�D��8B�����w��G�G�>����|`�}�g��j$r�QM80.=���Q��^NJDy#�L����w��)�$k�����<��!��U�����l�W�q]�^j�rX�F�Sn�7\
��?�Q�k�a'�a7~�h��+�E�:��w�j����/��I���[��&��C���FX7��XEm��������(~�?rH��;
���~�c�s�<v%��p1--YS�&f��YX��e\�1�A�����9���c�Q$9��0&���?����j�K�i���I��j����V���N�h���	��i�������Z,��sGdh�:���jo8��.�����;��6���8:��Hj�gG�]�QbmLrD�
&��8�8�������4��J���I�6�G����
�="�`�f�b�@������/��OF����*%��Xe�8ZUB�3Fr4�;�n�Q�:]��N}�J�@�j��J�mb
��[g���MO����K���n���w?���'��S������|�B�X
��
�����c��Y5IA6�/�����-�@�������L|�z��J���>�����R��UC�p��Sk�8>�
Q�������Ou���A#�U���������m��8�ra�!N	�]��v�N)����c��5Y��ed���-YK��:�1��S���z���m*���/���|H�S����wsI6CW�L�2y����u����N��@�.Apu�I�UR����l��������*��O''�����o��"A�W��.��-)����U�PK$��i�����i�[i5��.�"�f�o�����~����e���D�dS���,�h�o��?���R:�1����B�R���2��i��LX*�{s�&_f^Y�g���XE�}�(��q�C9-��I	����2D0{�����>�)o<dX�cF}��:��s�sz��R�u�9���K9y[e��[��5��[�K����p6��r� ����K��y�Z�o\����*<��uI��nJ����K�����������wF�Hw&�]��J!L��,qN�Pr%�����2�/@�d)}�Pk���q}������v�������� v�$�v�Mf?��c��!

��d����51,����`'a
F�*o�"��0
;=8>���������u���bn"i\E�B[��I��N�����'a7�7������5!SZ�������y�5Z�R%v^��t��h��B9���Fm��(����=��N_Q���j�3�i��$;E�P����B�%QKT _)Z�+b�8������y��c�)��Z�x��+x,�7����I�����'�~v����5n=<F�������.��9?�=�,&/�rt{�r�����O�p5�2��#Z�b�$�`5�\��a3��������/X�K?��Jm��*��Q����PM�pj�	����pR�'�5�L����+&�HM/j@	���i�����s�f���_L)v�!G1qg��%��t��[ED2�a��9]J�c
�/;�3v�U�k���Nm�]IF2���LI��6���|PDo�(��%]�2���S�� 34�D)
[�/_a�B������|R2\���m�A�W���z�C\�R��y���DOD���VP��Q��xC�������"�=�I���A/�1��l����
Q��_p?�A��O3��8iy%�e�W���qK��N����1	fLrN-����A5`#��%@�D��Bu%X���:_�����Gn 4���d%�P����}"gh���jp��\]
.����g'�.Q��^r8"2C�&^j�C��t��'r��m���N�N��F.	�����W�K<�����.r��blI5��7@�F��L,��e/��7�+����|NfR��+���G/R'~�o�{�n���V��Q�S��Nc�_5���WIRm�2?h�i�����M ����X�� ��!�"Vc�A_w�h��o��,�^�lZ�/��yK���Tqe��f���iE�B���
��=F�z���5\��0H��o���t�Tl'feGmS����[l���3����W���bx|�:�Ye��|�<�eRv���z6��\���I�C�`� �� � �"
���FG6FN�R�Uo[����3���h�����
�?�O�t��F��k��_���0D�������,;���Py|�4��}����B���_w.E�!}�*9o6y�4�Z52��=T�gh����sy����09��lc8��l���p��b,�������\!��a�����H�����_�"-���ef6BE�OM��C���85�E�q�rT�����J\
�c.�*�gH��lY�Kqo1�*J��5��p�M$ N}b(��b�!(p����8^��S����S��W���w���dZ���{�D��nb�Uq��yon
��/�����eCp!�����g�����0<��%�s���y<�����S�M%U�}{d(�7�K7��4H�9<T@�+1�O<%�0�ke"��D\
NN�?_�;�����M��r�����k���r� ��B�2"����,�H���u$��f�G���TD��9��2&.W������c�:�w�73�]�3v�����	f��w� #���'N! ��G��c�e����5B���������>PS������V�n�c#��5{H�)����d�(n������i���>@C��VJ
d�wx�\���4K�����Q�X%@��5�0�L�"9,E0S��k���Z����b_���~�m��=j����������T3�SJ/���EQ��5YC]�'��1�{-�s���3�yX���|%�o�~����	(O��]�l'kZuPf�'u������=�1"��pA\��jO��O� :�G�ja�3�eD������/a���g��2y������rp�#^�dC�Z���7����LH��L*�j��$�N85������9�6��~���=� �ju
����s�����������t�J�k���Z0������:NE����o����R�}c@��m�f����L��m�I��-N��G���#N����c���� ��Y�����������':�l�^������c���B�wS6b�pk5Io�>3pSP��F��6P�1"W�U��Z�iuk�Q���jU������X�M:F��(v��d*����
A��6����h\J�g�
������F�X�uuz�Gu���	�	�%��FU����oj� �
Qt��5E���P	��HAq������e�����0������bC�]c8`1����|���v�)|Hs
X�L!*o�a '�����^7,"�$��96(m���|��Ku1	����I�]Yx�CoPa�F��Ep[,�XZ���kAY��������z����2������n`�q%��0�E&\�y-lx�V���j9*�s
m���Gn����
��+�������Y�xK����`�HY�k��������G`]���l�-in,��Ss����dx�bj�(�����,�?�j�j����l�kt���U����*Y�Mnzlr�x7����*
��>����%�E�i��� �R��A�@�nr���B�\�,���a�n���������
Y��w9�ZW�\��=N	Y���GM�p��D�	8��*������f�[
�����%�����&�9�S�tP�^�4�z��V�^��:]��^������tA:�������F��3^_Ql���2&��KdL�|�P����^��\J��6��Q���0&������`�jm
�Q%���H	�|�;
a��r`�]�)����!�FB!LZ��)������>F����YRR� ���8,P�8/��%pE�/v�����H�d�����IE�R��m�&�L"��b��6��v�^����F��o��h��$2S���;�s�?��@5��-���l�7C��X�~������u�u�x��u�m��7�qJ
,��|������1��57������D�6�H�t`#�O����
"X�CJ
�Hb�j-�?�
��VT��\m������$A����5��1M����tK���7G	��F����r��b�n�DZc4���I�c��v!2�����������.hxbT����=o����c7-���7r��=Pe��4sf��c8jU��U�Y�VBOf��_�%-%��#�NJ�SM�!c�$�����r����:�z�]�@�G�n������9������(�QcW�L�]�9��g���T<!�wn����_���a����@�3��#l;&/��s������U�������1��V�)T��N���ye���2x�<~e���b+c<oe6���x�t)�!�U���v���V�~��hw���3��AsMD�#��c�Kw������������u<�I?��LT������i��
����Ryn���
�)W]]���bl�P��
,��&a[�2��dar��9��d�cw2��*��R���[���+?��l��5$���G8ef������\��5�r�Z����#�@��w�"�(��x&f�?�&�(}z���,����y\��.q4�2��A���v���W�����k�F&q��oE��4�6W�o�h����u��������1�\�~)���lN��5(��|io=q/��m#�8,I��i�����_�mu����x��N�\t�S�����c��v�nX
{�y��^���SV96)?������%
��odM�Fq�9�p��K���Kb�H?N����=��B���1dl�n6�2�*:	�+tT�s�#J�e���Zy�>�Z&iO���P��u�[�����K��t
�l���5��,�)+��|���������i�������\�8tu?u:j&�j�����p#����m���W���Y`��N����	�1���Nh�(��Rl:��'�{u�mL1/��5�K���=u4�f�gLyB����J0��Lf���:�q���V��v���5[�I��Z��L�U�Q|�V]����<�U0�rf?,#��Axr
w�+��gNXt��.����1f����*6���t&Uh?;)?�������h��w��,����C��V�����`�P�|���:}ViK5\b�a*��G��M������I��������88��)�'����h�a����J7�z�8U�v�!��>�p$������&%:��>)+UtT�w����jM�==���V7�@���q@���SR��"��YZM��z|���w�����������}��I��{1UE ��������)|�+|�?80����c����9�D o�F�����A��OU�w�C��w���e��a�g��@��&ws����I��A�{0��v��S��I���X��M�-	�/,������a�jM+�-������{a��ad��A2��u�����������ht��a���'�?��D��B�p����� �F[@�C��9CYA���/�)��~���������f���A�� Py�F���:����/]��y�C[�����a���x�@ThF��U�/���@�4�8 ���u��w���R��	�t�y���D`[�B��*�� �����6
��4���Uq�
 y���DD�6�b��I�e�.s�Q�J8��j��^o<��)���!&��s�+qRh���><6���"��Tc�+��@���%.��/���:o3����%�����vF2y�h�I0r0��OX�;y��#���Za��O�5��1��{A>�1��z2�}>Uc��������p��HN`$y H�������������A$qw������9ow�?���?���k����1�^�c���Zd���Tq�W�S9���~����-��������>x4K<����_���{|C���2@��;��3��~1�$��.@@��Uy�u2����H7Qk���z���K�>[����D������5���rO5���m0���XO�,yxo+w�O"Z@��*	+��K��q�,��;Y�%7����$N\9:[�$��a
Mxm�
n��!C�P{��F �}�c���S$)����l��*�����t���+�>��<<B}D>
B�x#>B8��GB�<�4���%�0�G���������3�K9Dqi��G�h�?N�Jh����U��9��������)KKWY$���R"��V�v����fR���y�0��q�r��S"��x��<H@��.^�{A��Y~�_���B�1���rN���v�(�=�{���vkYY��8�J=���rP�>���,Q�Y�p�����UX��}�OA�$]�OJ�7X��jMpXS���46�G�-a`�'�HhJ����go-��E�5M`����=vQ���������G^P&&f2�~���q(��B�	Z�h%�V��
��^o�1b3,{�US~%��G'�|ba<��t���������\x���S��P�����ht��tQK�d]�����e9i����Mi�=��8S�z��m<���6!]�$K���1w��E����R?����� ���A���L�Quc�@2����
Z�
�P��q4��dGt�Xbf�;$�aEv����	���(j��,����R6���F��:&�C�+�ou�X!���.�O�3)ZGni8+�J;�0(,<f��7�����
'�P��W.`���r���d7\9=��X9-���������6|���v������c&��e�RDh�@i����m�-�e�t����T������x�#�-1�����b�](���|E�>������9A��l��{��f��w0��<�L�!_�����
��s@�o}��P�C-���)R�T�����9�H>7@�p���z5��O���|�Q�R��D5#6�K��
�����wr��&K��C��W�Y��B�lS+�`M�-<N�V�8/-N�E�)���G-����!(�U��2�2!@��E�)v��Q���/(�����Qta\�����P������X9XR�`�<$�nD/"��*�<Z%
l����X��7�M�6*�
��*�(�gJ#�����5��M*"5�C}������'p��O�s�.�Mr�x�..�d�����Q�[o.�U%?���Be}L�k+����Jl�	q�%6����B��0�z��cB\[��f��W(a�&(�Z�����y����8�������
�n��0�~���Z��:�`n�k]O&��[d��j�p�M��c��3tzMp�e��������L��[[D�|x���pWE������j��z�1�X��L8��R�g�6s�^}8�,������n����*�q���
J�!�&���l�WG�-�����w ��S�d�5��=A}y�wc/}��t��i��n�V��W�*o������q���
����[V��L�zR��b�ubT���
��|����0����w!���c|T
Y.���/Q���,�+=(i��"~�U|�z��p;)�oJu�b1�t��p2%��(�����)7��*��7oDr�tLL��.��o�u7�SO
���4�aFt�
���*����a�d��.������*}$q�5GS`D��w�*)=���+��P`��`;,S�������MT�������������5g0
r��b0�f�����S���aC^�^�d}e��|`��`�m�����J`<�����n���N�1��J��1jS!�px�����^����:�:b@R��NQb��]����(�R���!��!]��=�'��-�j�������/Ex"��5|"��T[i|����|I����#�'8�P��z�e>��C>������w���t0���V7�dH�m�����550��TN��C)�}T���5��7��KL=w���*.������
U�Ru�������B*�[����n�d�
����#^U��?���@,A����p0���.>L���t�
����L*�M�uv��Y����"��iX����<Q�|6<������S`�^=F���SR%mEx�d�����������r&.��<'
����������<�W'�:�G�W0�t�
FC�/�y�G����tH�#�NB��|)���
O�~�I�t�Z�I��,WTfj�~�����@`��H�`�����4!��)qv��s�b���q��qM3wG��\4���g�J�-�S"P��Eq�����@<s��Q����B����	,b�n��Ce��-#ZD
�
>�
C����t���X�Sa�j�n��Q��`m(�m�bJQX?����O��X_RO���\O~�{|�L���F�������f
@������o�Z�T���j32��['Vwo��p������@w;�
hJC�d��O��y-��t�co/i�]���'�T#$S�����A���V��V�B�-n�P�\�E�#�����e����%E���n�����t�o��g��3����_vt�?^�>����k	��h��<���%�)��$���d��u��g�>j6]����Y'��P}xe�!C�=�Ti�M�E��	X�^5�3����8���o9�V�.���W�b�_.{�����7R�hx���F�xMrx�vs��A��6T��t
��
_�T*�����5�nt��^�Y���m��#���Z*��4;�����J8������uQ��q�8�6���tu<
nv�V��@oj�x���x��`���$��v����s�w � �|���	%������&�6��|o�
������2}���T�D�J���2+�Fd�����?
?����������%b�XVk����am�W�R#h��6�Fib
���d	�������|9<T���p[�-W�_epzE�SB����Q>��Hd-��������A�=�[I����8������i�2�[�
��I����|�����
V;~!K�u���n�����]�/��<:���Q��+ar��.giB��~�9�������S��'�
��Q.M-��y�md]��U3
�4���Q�[�d��p����%�	+��������*���R���
7\��T+>j.$���%�ic8���������s<q���d*�t�"=�P�PN�H.mK��wg�Cx��`�3XTG��6��v���b(��
{���)�\���I���� �a�Ki�E�,�_,Z���rt���
�
��-A���w�]qMg������,zH�M����~�]��\"���x�.F�u���!@�t[f������.^]�t����7�8FZ��1&�>��'�&�4_������jN-��>�ivy���f�Z�3�-���B�;y�����|xpvD�]�>]P�t�*��%"�%��������u�~VK`�L��$o�y)�O]J�������L�X����fH�n���_��#6��V�y�;jsr_�����rF�f�i�D�%��������`dD��;����K4�+nD� ������b+S/J�a����VFS�V.�6�l$���p�.�g��qX<L����
	��/�I�6�t�U��w|����.�B��:f:>N����jLn���p��`��7�P���0Ln1�HN-�����t�CP�>^3��!�Dc"�jx�`LAr��1���:�����*�M�Q�Q�#������������3Rch]����YL���NZ�L����*'7%����	=��r�,(*�x��R��Qs���,x�����
��G��U��L�q���,������������nK�r?h��������>�^�jY]�o9V�Z�J��o��e�~��d%�lGE�a��I��^q�<"�	P������2�Q2i���M,��19���51�lH�8���,O�"��3��i����Q%c�����Dt%pv�Mm&_m%�F�����U^�)J��"�A�=���"�(&*�f��5s�����ye����o�L�,�G�����A��y�$����"���
)���F��JT'C�`j���$H�r�{�_rjFpY��(��y���PwT��(c���5�W`-(�"�t��DH���Q����(����j�������N�J�������p:�K��S�\ i��*R��d-m`�>���2g+���d`������B_C������!�-�-������
}����g���B�<�]3Yj���4�)�)U��0*��R`��D�\YYl���S���z���E�Cdy���o���?ux}�����z���opb�Fo�����
�c����8��(�'Z�j��?l	��vG���I0Ii�8r�0X��Hd��������k��`?DWE{�d��yE����t[]�m`�i�Wk����^�3����?�_��Y��_�!�8,J��������N������kfxu��@�/�nE�t��q��i��m`�:�����e��
���_aQb]�&���pex#4��C.Y�+
�9'���/�_H���Kx��X\��f�+JGF*A���d�`nc�� ����J���c�%�M.��h�bO��j�khAn��j5:n�q�Id��������J�
N���OeA�Q���%C���=�lk���U�R�HUxa���3����m���f��_/2M.��Lq
cDa_�z:�_�(���A��?�hOb���7��X�4�eN�
"O�d�5��`F�c]F���0�{���Vq>�HD#���W����D���G�x!�BU�4596iV��������L�v��������q��.�3�2��e���.��uBe�A�t1	�&z���k�r�o�����Y�=��g����1���O	3��2���=&nt��'}e�����������iX��hdW�=��u��x�)�:-�w�kQB�Z�<��S��(i����N��G�(�P�����.%s�]g��b5�xE	_]g������9����P�s���N������r�]�X��Y�|@0�k��42���)=�j����E�v�;�!��%7
3�-���(�� \E��_�C5�	��)�u�NW����J�hw��<y��R9�tV{o��-�8���<F��1f�MA)�W:u�Qo��
�W{�^_��e2hQk��QT���(�QK{L�������H������K�b1�U���U.IhqbL���A_%I��;�3�"$>@u�����d��3��y$#����E�6��`��k{n'���o��_;y�w�I��������I0I�K��%�H$f��rM%��g���M�B����P3�8$�V��^�����N������������xZ�������������m]���j��kV"]Ql���Mm�gh����y9�La\>���
d>�"qn��Kp#k��#����&*���n4�Z0F�u���;[W@�����+���V�C��Mq�����P��3�0�,�#;f �[We�`���K+�g
����L'���������ek���4���������L��[.���v��]Z�As����M��������U���j�^�P��w������/���������<�O�F����hp��l�d�m��U��c'�=�Bo������J;,i��C��b��}�b��.6L�
��t}r0��<m(�e����n��z��tz=2�ot�M�A'e6�pp�Q�7H�Ln
��AYw�5�f?UL��|�I?�v)��zrS���,��-�[pp�5��6R���3�K�GM9w�k*�N|���{��O��Fq��>=�sZD�Y��t�us�<q����x�����s`^�/*"�,@,��@o�?�8�C-�����|���X��zz#�[���|o8j��dM��&�Rn_G)�6du���K��*�C�����+
]����������&���_TK#�����/���������<QG���������}y���#G/c��L�kd{����U��"�=��� ��wF{$�������FU������{��{uxp6���x~r�FN�5v��Z���}���s����_x;/���5�VQ�	I#A�]R�@G��	�r�n��Yv]�HJ
3�'w9v�� k^�]���D�����u���[vL�������
�E�w�����oy��I�,����'����8 (KkP�,+#��&@�R2��3���3�S��!\oW6�����Md�<�<(�L&_-�/���$��a�f�����B����f�G�%�dc;�_k�
+��0*�Q>��'6����!h;�N���[�V]����G7|R��6jd/0cI�����U��#$A�Y�+������PG9/0����J�%�F������&��'�Os+����dZ���D�Gq����������#3����'�K�f�+P���2C��_s���>� k�22(E_����FM���`�I�����R�/��I�
�3
>O��S�v�u���1��k����������"fl=�m���/������0����Lk�l�{�jV�
�_�j��x}���J-�Q�LV�A���J�,T��,"|�����b�=�7|�|�:I�Wl��!�;�[_���"��B��@���-*��f~`�r6����9(�x�����S-!�u���Vke��Pm��S)�H�o�H�b��$-�9�6x��P�4p�����F�$���O�_)��r���x]�)"���!t.oh�Y}4�r������\���/���������,��z�q�f��:N�����5�r~���9�<�Z�Q�������+��P�
k�q�&�����	q��|�F�1���G����!��%�q v�01i
������0k��������E�xj/���������Pniki���fc/��r�Jzf
~:<�tu��@�(x�:W���y����{�l�e���V{N����F��>F-���Qi�i�D����l]S��6�S@~��T��L�W��hd�4Y;����y��Q�9i�v
�����[cE��I[*�����u��~E��
0S�<�/��:[�N��N���l[��[��("Nb��\�^3��V���[�Q��j�;r<nv��|q;���QaU���2�V8A���&������F�F�_�9}�Qf	�����:��q0�`NnU���4�.k)��z	�8V E!�x����A������#z��:@��%�o��`����26�:J�)e�������;?J[�#A����A&����X���$�M��8D��94�'K%� dN�,�nYZ=���v��Z��+�x��8������>���2["���R-L�i��q�*e��l�N����
V�����Y�Up��~FW�6A��
)��
���(���
C9�@�^����a@N������p����l��h����L����^��+�r��%����b�"9Nk�.�����[�zB�(h��	h������%�BLn���'���{{��!�&�g��3��*�Z���� 3w/���Re��`�9�L����J�p����\v���d�(x��d�k�Kp�s��_H`�!���;���������5��xyE����w��t�������V{���m*��r�6�j�R\}��D��w����{>q��fp��A)Q�������.Y��1y�����&@��g�����8��*QlT
�AabeL6Z�B�
�������k-Je=��1�A����S�8�J�q���W�b����U��~�Nv]�<<�\��g������n�Z���o�9���u,'G|�@�=���x�%ekD$U1�F2��t��2���yU�2��Iy�#|U�_��Tn�a���������xF9�(Q�TmL��WY�Jc�P��[�9-�l�-`��������fX��ES��!K���=}.4�������j�s��Nv�o%vy�	 �p�5�����A����OG�s��������������j��{Hn�-l�[����+����HUKO/����
�G������r���GbW��z����M*o���#�������������O���s��afC_����?��@bx��)z�����]��~�j5�����zy�����Ga���A���z���~
} N��/s>�����`�y�?t��g�U�,�
c����a�����G�>?:%>z�:P7eK�9�n� =>
�]�=qlMt�Q3a)+�}
������39.��N
��i9��aD]��C��v�F�h=S/�\���~W�:����luG��S���F2��.������������h
�0I��
]L��7�;IU@�����:�_:�,0�"����6?P�$Ig�hB�[��6�V~������G�J"�����VU#-�/�t��J/H?M2P`$sjd��,4"m������L�[=Y/���_�nNx��>Ls\�6��~���������������J,dT'��!��*e5�Q��b<v���2bec\lek�F'�;��j�;B�oI��@|�����5�T@�1Fw*�X�X��w�+�g��_�y�"O�{���8�������y%�F�wZ�N��wd�Z�u,9n�v�F�m)�[sKq'2���(��>r���z0���S����[�,�%�+0�ZDr�Nm#����)��S������1�����l8�3��x����&���&�x���w8._���pIo:�������&�{�	�H����U����6��&�|��B�!�����c��o�P�i����[��:�����t2��h 
���o�����Bpm��Q�njA�
~V��%���H����������\�5�"��[���V������o��*�j�9���[��f���E�4=�����2�
o�����z����w�P��V�;jb^�z�5�������j~�q����������s�K����tv��������������zx98=�?__��r�`��>�%�=����e��{W���\�8��57���+���..N�$V���M#��ux*���{��E6�R����9�}qL�����}�����*�����P��P3�x�u�{3o�����<A,�hA�@
��(������f_�UO�v�M������.k�5�R�	%��:_]l�?�HQ/"�?��������7�W�������de����G�^��s��z
�c���gFf�D~�in�X<�T=2��Y��%�,l	�7+S��0&�|W�I}�Z�y�(�
�cr�L� XVG��l��������k����~\?����I�Nlo��x�����l�F+��S�\��������@1]o�z���`1R
�
�a��wU.�aIlG�'�g�4�+����r���Y���+�D��<,��_�R�t�a%�>y�T��k�q���V\�p,� Z+7���$����%���;�Z�i����G��m�r.-�����Jq�<C�}}����iwL�U���:�P�|pB�'�
^�y���1e�
����s���5���_�ft(*:���%Y#2�pg�E�G�5��!��
�`��yF��NC�lP�fC(�{��L�W�����~r�{�iS6S����
k4�S������B��$�����R�mg��(!�Y'��:��.gCq��%;\P�o��Zc�������Q�v�YGr1Z��u!6�d�j{nk�E������|v�?ZwR��\�W�*k%%yCe�LPc�;���B����&<�t&�t�wF�:�PW���h�����{������e2A��-oh���Q��\q&��������>�4��F����s��)�q�>5�xHQgV�f-(�������5��6��n�U��q^��U�e�mQI�WK��|4j�{��8�����=��MP�+q��!��%�4,������tdFM��7���_����a�)&���t������@�Op�
�po�R����se'H>
������7���m�>(���P��P3.
A�9�)�]��pr��9�Y�r3c�b1�Q��
�����:��8=�r�0�3��j�����W��d�N�,p[�`������c�D��A���*~��N�j�`��1���^DD�w�����6��\�V�����(���-�Q��o�Y~�X�^'����9R�d�rY�8�M�����r�s<�����4xr��O�Q�Hy�Y��_���h���R���fYeEQ�h&�����|�K&��;��z�~�
h�R����J;��c�#����j8��O'0�Q�d��Q���K���R��	��8�OO���~e"(p�����u)�M������-��^�&x�O+Lp�y��.ltt��E�����s8#U|:�k}�j��cu�.:
�e�>�7�;�����asVB�^�~�P�o�����0�_jk\bH�A.�w��9�a�/�0�y4�`M2�7'����������{�fR7	D��/�����7��]����{�u�L$TK����R����yb)�"���������vV�g�z�ws	�E"O�XQeI:���n�r��u���8���RG�*����N�2�_[;Z����N-_�K�\?|��bJ���Pj�J��O3L�cI�f�����j�.�r��C�L�IQ�u��`�L�mu��qw��:@�;��l�N���f�(v��.Y�v��>�L���1���B��ZN�d��>���2�w>r_{�=�B��=:I
>X�J������p������o�:Zv&��Pv���
���_��'���Q��������8l�
��s�K�89������Cs����8��&P��o�H�6����0�h#������v�Z�5:vw��:9��y-d��|��'G>�����������g��$��;�H����D�fj��C�r	�p�J�4�E�fR�$5:5���(��>-����h��7�+������j���nd�U�
�)����1���C�]o�1�]y���b��{4�M)O[� �'�N_��'~IY��jE��R�K����;�xk�^+y#�P����$���������3}x�9���t�<�e%�[�	��������nT�jm{�����?�����*�Z�^�7���Z�Q9p�J���A��q�8n`[>e�T�i�s+�o���F4S�g��������l!c��o�n��j�Z�Q�w�M�~�yG���b�����<}���{�>p�F��o�����{6,HD��r���7F�iw�����p"\���wE����
X�ml��8��������"�b�������G�]����@��+q}�(�FO���	�<Q��j�^��b�����b�i���������_��PhIfR1Z��I���k/*3
���m
I;�!��(��Hb6��M�mo�\�&���UQ�T��VZw����
)� 9�����,������mo,,=L�$��?o�)���+��P�;�W�����A�i6<~u|k�#����]LQ�r`j
v������z �����MF�n*���#W��X��� G��G\E�GCAU�t
<���!���A"+��F����aM�f#�������������hpDv5*�'�V;F���������?0!4STxh����>"�7�q����6�5=��]cH��S����
!@jIU(���~��zy�*�����a�@D������)�?p�p�f��B7���^JU�����,t&FT����7K��C
����?��nI���p��TP�����c�Tv����� �h��J����
�{�F�:��eK6��,��_�`��IamJQ$�#
����l�m�
_��T(o���|��GAX���Y����a`gqt�:�!X��I�3�\.����gG|pyzp�Y*~&;H�����F������DJCS�+��T��'����bx6�\.Tn�t�����!+Q �����1��FOx���]U$.�p�0"y �4(u~a��Y�����F2��d�Z���5~��J�`������?v��`Ek�����S���rnSv���eq�2�5�j�e�S��s�5jm����l���{��w��\������d�!v��}���Z}i+�oe+J��x��w'�6����\����6U8�I�0�cn��c�%�%������(���k#O�����M�����-�����)# �������mL���EK������*���7a4*�@9�	�#����{b�MT|�E
����{~��j���#'k$F�]�z�-��Y��d
�C/m����y���e����3�C;��xpvD�Dm\wl�yZ8I!R��t���K1�{d+4Ef�2� �NX��^2('����O�/&���5����J��D
��:��!�u��x���oH��C[����:��	b�1�
,�pL6��n��. +�4����A�$�H���sm��sJ:��U�6�B�[+!�1��X�Q�#9������Qw.�K�4n�QZ���xPD48C��D�m,n��$4Rz�T8 �����\t�r������F��s�)BQx��y#&���+e�����%�����mWqvp�����n�w���8����TZ�@?D�>�����Z36
���2��P�P���8���^��a�8�G8��H&�l����}/B��{IM�[323Su�on��&��7�Lb�9����
�u�w��^a���\�?[��JTg�ey��^�#4*��	���R�XN��
4���o�����=m��<�:�8�����Q������U~@��N��yz<�b!)'�9vEk~*aN�U;��h)�KX"�o�:�O)�����$I���%D�R$IoK��5S���C�8����fGXd�g�j�/� �}�6�t+��]9��V��R*j�i\��i���"�%L�}��BQ��tI�M�����.p�N��J��(�;L�S�`�w'�?\�KJ�V�t��+�h����e�8��o����a���*�����������`x5�~��t.A1
������S<%�f1Ud���-r�Lu&k�=�"��������
b�!R�����g��.��0b����r�8���5%��x��s%����9[��4���X��2�� .��a����4�E����V�L��N��$�"� ���E�(��
���U���o��-�a��_Y7}y�'���OW���x/9&�#*&5��F	�r�r��s���"�m���~��������T>=�b=������1�U����q_���6�M4d|�|�
�FU
�jo��������M8$_��Y��q��7t{�c����%w#�Rl}�����w������(��kjM�D��r}U��8����������������j8�q�H��N��0xN�!$�OtM�!,�C��%V1��!�#���/<F���T\����9{��xSy��#3���.��A��v�({*B�;����b���
���5Cc�
h�q�K^K�����=�n.��!7P�;����&�x�2�3�E���}EU O����
�5��'�Q;�{}7J}�O��F_�%s�a}7�<?X3I���r3������S	�6H�I��f�Y�M'�c����l�n�@��.��p{[	�rI���|S����2RM������y�j,��M"m)�_Y�|^}<��l#��Y��R�h�BA��T��
`�n�1��qCb&4Q
�xq�8�����I��vB��4'��c�hE����D��c��6��zq� ��(������<�F����R�����A�(�>��c�2�����S0���W6�����$7��?< ����6����L�t�Z�:�%��}[U'����
�LJ���@����g���1IV�^�pY_�Q6�Q���k;��i�g����.fp����]�&��\]\�p�=�"�h�9^"!3���:8������Y��|��fr3�k�E���"H%z�uOI0�`t0����Nv(d�/�C����X�!���
�(�LT������s6v)��T�P�:�m ���3'��MF$|0R��,:
���:1��S�5���M��)�H\���-e�R�Z���`^]�7��V����"�G ���^$��H7r�$5�/�8�*3������k��K@&=�V�^a|�Q*�����oI����R�"�1��sd����1*l�e9�����	P����G�P�7C�|MTNT9�_��b�h6(�*Q	j�,^&��Jm���)0F.I����W�"�V���4��3��C���.b�x?�T����5�*�E�90r��l%~��1�Mho�T(����=����������t���R�t����fx��)Fn�S��P|�9�O�1AE�;�g���H�F����@�R8@�j;JI�U%������H�@�h1����,�|"�q��@X�����lht�*�8u�}*�67�Z_�9G[L���=N3���!������IZ��n��j����t���6z5�d��v���>�H:�S	]�\��������o�N]�����~��`�)��S����
�Yj�Yh�����y�I6�0��
�m��(��byk}'���}��L��)p���}+�O��F[X��>jHM8�:��(�B�Hm@�-WG5t�^]�bP�y��}�<����0��$��C/4i/�]�N�Z��c��7�x�%BJ`�G �=B+��������$�����Y���K|_"4����zah���I�\<�i�o�)��j���&����#��q�Um*-��ob�R,jFF�
PR�V�*i98��2�t���I����j��������#Q���C�#�,`!c���9o��*t�/4���&j��W2�%Y�v�Z�������W�����
u�1u���%B����:t���]dy��gQ*s:�,�&�l*L��j�(R;k�a��ve0)tt��1����N��::�������'����
o1�s���v���������(Qto������[m��R���wI��X�V��=���N��J�H.��S�-9&�X��ubQ��b�������;q������0�ow����y 	�5��g��������X:�$l����~Lgk���l��A��=�l���zQ����D�>0��m�(�=���1��zT�y����[+D%�2��lQB��AT�E�6��pCj�&��	L-��_�_]�\�1d#@��f�r��hk,H2f���'Y���{���J��$��"O%u�=f�e"��h���VL"�3�]��Z��n�_s�9�i�7�]��+����h�`�eP���rSi�b�
���eh�o)�����g�����?�o@@�:cX��T�}(�?�����3���OU����G;����������Tu����M��2P�
HlD>8U�dVl�����}��Sq��j�>�9�n��j<��f����D88
'��j������ uv~yzpR����@�&l�C����VYi�<�������o�1�b�0�03�9U��Ad���h��h4�;�+�$�^z�'d��;��`Pb��:��i*���L6g�8T�B�U}��v������F
knSP4��L*l#O���r��I����7�Ii���k�FwI�P�,�[�Qa���R?���g�J&���8yy �5}��������F����V
��������W7bx�*��������C�g/�_"P_���h�%�������6����\��{���M���.���~I��&�����!����F&��<�c:���W:7l�1j�j=4�Q������@��&�CzM!�.��)��:��f�����D�������.=��z:����3�B���*�f@y�r<cH�8]���:=��5�������.����0�C{?Rx�c{&���4"o(�&�v��k���xp���G.��� V��OM��D�'�ME�X6�����,�S�Y�5Z�d�'4dh�o���?���r�q}�i���vG6k�vz�N��X����m�U�%�����B���GZ��}G

#6���8.U@��O�;��2�@�*�vK���7��.�*_��L3�c��Q�d�m2�'�Oh^5h��.�Br�&���Bn���P<;�yZ0����������f;���������8��P���O�Ey�8*�G����������>�H�'��@x!�b��FO�;kuwT4*LO��05�������/g���
�{.�l�)$��$P3Fr%NB�K�T�OehU�q�z5�h]�LC8�s�TN=��U���r��	Qv���&! 40�<4
@1��E�q��FM�rE5L1z��0���bqs��T�7�B}�hd�(�	rg*b�:��:@���\���KT����6i4����W@��m�%?�,|���{>�4��9�_�a�y�"�w�DP���
Zc�v'n�B���@�����Z���&vB�L>|:���i��h��&���6_�z�*��	=n�]`V�I�i�u����c�a����&��g3(Vf�,
���}�J�#��\���Z�9k�]K\Y#�x���C(>Z��L���5��@�H��K��*�����U[��_��0�����^��`�x0�������������������k��Q������;�wy=8��_������?�m���;���_�ua��S����M�[������2c��lT/�VT-P_�xd������kR�VAo:0��|��.����{�[ ���i���z��7�_�Ga�V������]1A#�mL��r�F`3�^���?e([�:P�����\$��/���N��j�fCd���+�@�p&L��VH3m���V�W�����������*&
�/h����|�v�
�0*_������C�nM�%L���6C����
�������"�n -��Q�,�7��jH�b5�S��4rP��A�	`����3��F]����f=�d�u�;��T�|r�$P���Y�$0��,U�������y;��h�4����S�Nf�HN=d�j�5\*d��GF�(�E�L��\*9�/�����fj�Z��X��af���s��r�x�L#��/9���B\m
���"���V/��S���3�b��hh�H}�t�X�������i����

c������?��������3�+�y�%P)��n��HH�1E�$e����)�,�A�h���o���|���������$TUVfVfd<n������S�u��(zrgv����\���9N�R�.�8O���?��u���8�%�Q����c������f�c�7�V\���kK�$U�$*���1����\����T<V���_��Z��]~�|`,��kc�\)��jt�n�c|������_�fe�8<}l\T|��m��o=�D����9��J�b�m�=�6U��*��������Z�@�{�����n��z�vke/7���b�~������v7g��+�U�o}cw�cP+G��=�1U�����&����x��8���.Y-�+�v�X}�rp�usB�� ���<]��11u������l��y�Z����|�jdm�����st�e�~z�1���02���7]�r��c�����n���<)�����T��s;���JT�\-������r{���7��Lg�����E���#���kJ]|�`���(�J^��1��F�X�����Y�)_��y�5m�%����$������Cd�?�>����G?����c��+�f��?+���pF�����7��U�g�wW�������<�����������|�?�	{��c��;�|�,���`U���V�0�cmoo-Y����VW��;��~�~5#���Jmy{������7u�����K)ou�����e���-Kh\[[�dW���r�_��/b������#��}M�9��$�(���&�$�TdG��q���!�)����I>��#ZB}
�`:n����~����X��W�s�6�����������������U�=�a���~���������9}���P%33�83��$����_&W�|��?,/�*�B����:�IFd��������6���m����o��&[;������k��I�nf
���B�n[��z�YV�0���FD��=j�q1!�d�|��B�8$�$�>g��d���4��W�&���mO�d��[]���������L��^���pJ���1|M�Z�����]��J������ |�2�k�CQ�fkV�r��.m�,c=5:�F�c�^5�K���Y�X[�o�;(t�\��8z%��A6��y�����D|
�@�Y���gBrsn�a<HMO�i��b>����e�M����}z"C��sH3��x����.Y��z�.����vC-+U�^x��!���#h~5�Q�K�M&T��!�*�e��v���Ez_�4�������SPDq
<���s?��������<<o\�,q��AGG"j��/�W�����	+cz�����}�E�O�ZZy�_0 ���j�'iWU�Pu��G�0Z���E������F�{`��;�Gb�%��9����'�5S�U�����>fp9�9x����Rs�������y~����I�	$5 ��3R�����n
�~�/7��0��������F�&G�&��E!d3+����Ge1���Os<���Z��q;�A�����AB=�E�����*���9�������;W����W[W���q<wU�q]:}�}����=�-I���`��u������2T�h��/s�����o-�X���$��r� ����o}K�~��%�(e�'�H"�����h�s3����LQ��47�������go;����7��[�*7��q���t�5u�6��>5%��y���X�?"<�!�:��>�s�>����M�z�-]hB����`^��5}*5E�43��N-���9g���Q����l�T,Z�{�zP��[��'[u/���n�]c���v�{��������{p�z��*�� ��e�c�z�������6�j��ru;���T���/����W4<Rw]� u�9�v��tq��Hs���f���Ub@�G�
��5d���I������f�4����=�W��r Nk��/������tn���B5�'����6�#�<b7w w.?���r`��wo��k_�w��omonQ>�pL��M�#��G�k�\v�Q��Sw���1e?r�_Ji��5����dk!����?�`������^��y9U��Mo�J����T�1�_��� ;�dl��@+���K$T����o������`���9d<.){��h�5�q��J��lF����WM�t�������?'�"�!�mB�����\1�O�!7�f�F��&v�'v���M3�����F2 ��:??<=28?}{~��s�~����>���2Q��m~�X4"d2�[�D:��No#��ksz� ��/2m�G='}�$��"n�.�eb�!x.��tw���qFl1���z/M�[g������N.L��5-~���	y������e�`�K>�%K�{�ZQ<�@�����������lL�.(�@f�ttg��xM��Y��(#�5��G3��vh��R������W8Y�Y�W��y�pg�������!�M���vP�)��Bfj���s���5��i@h������������X����[��L9UV����~���L�
���&_x���N��d�Y���DYC��n��Y3��V%�<�S*����fM�W}3����`������9�����N@_�-+R�DFg�����0��o��Y^��s!_�
�'��rb�0
����:q����_;�M��(g�qv��#�xpOk,��Q�������{�E����UO���#~e�z��/�A6N����Ft�3+���6���CYC�u��A�l�������+����#�����m���������H�X��5���U	�;NM6p����F�/TXrsdg�*�@"4$�~����q�v'����zA��#s����
1���6����W=����/�������d��_�lpy~pr�[R�ku�^��i�AZ^������*?��O��Cd��CNI�x��df�������qZ/R�W�'p-�%cRbhx(��=��f��c���5�������l�q��%3�\-	�}K��t{~ol�S9����MQ�#�v��<������� Y�������#'�r[#�=�p��!_��W�s#s�rD���I0�C����rsBCl(�o���'��sw���ecn�V�)Y��gtP����N]r�D�O��5���������Rq�}`+
W�wH�~\u��gE�b ��������s��T���L�&�	�����$*w���i~q�4h�x�QD�H���3&��Y�lB������|�������k8��2�5���K\���#�k����{�zc"������(��{�����������{s|���sp���m#[F�<~��S�Hs���4���{���7�v���~okuu}s+�w����
|i�A�^��fG��w�����Wx��r���������m��Eo-v������z��	�2�=�����s�������%���=q��/.���`_���Dprs���Y~B�ez��Z�������>�@F�y���t(��)UY����_ux���W�1�5��>uc����SU�U-�����nn6��4S=C���B������"�V
n#��w,�/]f#HtQ����"���;�������R����`r*:�"w��a������^}��w�1OS*�N�GF�|U�f����b��7��7�w��-?tf�Y��v�t�+|m�N�M����*WU�Y�����Dgf��^��FW�F����������w��=�9��u�A����yz�>n�<�������
�������{soS�����]�>��U�@B��d����s�Ms��mZ����R����ZS�.n,fk��<�1`����f:�ewCy4�Ek�3�S��aM��7�}f4���t���w�H�#�Q��B��NUZ��m�
��G�f���u9AgAe�!��-k�%��j��zW���rK|T�<�;`����}?����c#�y�
������������}
\p���f�����
L���<l�^��	l�6Y1�M�;�w�D}0��I����L���v�'�+M�xm	�����2�}`+}���4�kcX~�"���o���?Q���kHh��A�2)|L�hHK���I��AM{3-�k6�G���'N��Y�b�n���;r�!�i�t��� ���'��K.�����B\�f<��>Z���K�������DK-�$Mi�LFr-���	Q���A;�kwa&����{���������3�	o��<j�`����Zk���9aS)��uM�M���u��$R���$�������o��f����X�a���D�s����,(c��OO@�"�������z5j��� Qt�5�/�1|i���s\0����#_�1H��2�%E+��g�J0����\V!yn��7C��'�o��_�+�@Y����b��k���P!_-$8V��j_W�*/a�Kx��%/K�H�{A��)�>�q��a��}�lEwi�����c0O\�{`��;'��[1���1>�6�4��vsw�=*%���d�~���Dgg��� J=:;�.�9���I�3��fN����(:�#�k��,���{}����[���#]\G	+�������\t���dt�������e����Qv�M�eTI������`
�����Rt�
�������#�mX���f^���2���]����"w�4�����27��u���q�UxD�u��R9��#��O&����,DY���&�{��{>&~����3O�r���e~�k>�"������rL�1�������%N'R�%g5"!�����Zw���1�j�W_8o�����9�dq�?���y0���s�+
�W����:or5�wt�����*Gu{�N(��m�.{�/��l�@��rN�L
b�9����hZIs
2�t�9�`�Q����k�������r��
��l����0����`5�8�N�����*�+�e�XB�����'���a�/s�Z��V�����mh#���#C��@v��4J73Lg���q�o��l�����-��lXc!t�v?��\E9�aO�J�Q��NEu�>@������7��q,
Z%MT2^]b(���}�2�C�����#.�P��p9���3vR�`x�Vx��I>���h�Ct���WWE�?������)�?�=*/�o~\Ras�w|���P��t���?l�p���#���qn"�������f���7������i��b.�J%�9�����Ib�IO�1�X�`�4�h�f�B!����XRJ��� ��8���g���y=����8�Dg(�$����v��$xPv��Oi��Q0�W9�Tgid�J;z��V�C�Q�n�+g�0�����z�w����S�'�8�Y���lOm!VO�%��P�����l@����l��iS+����<���QhJC�0��U��aQ9�e�\��#�������T��t$q���$�p�*r��d�R�JF:
����k��U�p�;����K�����.5�����:)�jD����Mg�����g5��t�{�g$�q�Mu(�O�&Q9_UF�;|�\3$��6
�����ux8&������X���o�������i�U�H��p)i~S1t���q���}��ha75��t�>h]��}��]�&�o���#�1a��N�iUY����~8�>��{Uk��j�~�B��F%��jM�P��{���;{P�C��R_��Z������A��1�2�+��(o�?FhR�/n�Z��E�rj�
h��v7�������ud����N����3m@t�9Q	���x[:/Eb�%���%SeD������F�RUt���_��j��,$V�������aO�����B��W�$j��������1�}U�
5Q	I��VyP�z��Y������\��>�B^��I��W^�^��
=�&�J�������u+�U��;�|���Jg��N�F�N_@:���@C�^:xb���}x^-���]��OU�$	��h
�����0�H �����'�25l��:��<}�-��������Pk{����o��7�~Q�t�*�/ll���d��]E�H`���!�e�:��������������f�X��l1�m���w�����.9'��~p���y�!m���e��7h����%��'�� F����B�;AX&�9�O�@H�+q��Fx����?���*D��w��%s}�
���:�$����F�+?vx�tdG4p�F�EG�O�����c��ba�RR��Y�"�ZY��~�t��tn��1�y2��C��E��d����W+R^0�p#���Ls��m�����{�^>v����y�&W��e��&�h�\�xhJ��t����+�A�3�[j�\:��}�������H�Q�;�"@FD�`�S���(�-�,������[���q��^�����P����Fa�&���oco���Fs������=9��)��s0
��c#��oH{���u�P|:V�c��� r���+���3k
}7k��_��b�����W�x�KN�B�lh�#�]�a^�H!���]�'�N�v������e��S�����#��(��W�1P6%��j,p\��'1���:������2|��FE�Fn�O/�iB9����;�$���W�h��m~�YTzH�������D����7�����ba��g�,~��f��E����gq7*~�x���(�
��`,�U�!�?qQ�"�Y�\�|=���+������Y������{)��s��Q�2y����������d%�F&�T��<6���Z(����6T�SVy���j�<W�(;��3L�i��R8��P�U�#c����O��R/l��@��o����N���g�T}��e����������#������I����z�*N��9#���_���m
�,��x���]���K�q��/�����y��Qm�+������acgS����PJY�Wt���c��E�����}.�$��[$%as�G����
�G�����l���r�_������M&�����O����n�^:��K6��HbK99	�l�����al���c����h���I"x�C��'��O]��4��A����5���3�(����1<l�V���y��v�B~�>
$��vqq�~G�xwcno]����� Ue�,N�O"B
�x���Q���p3����	x{��$��X���B���]P)u0a@{Q&}��5���I����v��y���}`��U�Uwej�pc�5}H����	��d����/M	�R�r�I�-�����ty���'�I�7�|�jS0r�7��u2%^U��8$=�� ���7�&)�\>�Gwo37��y��x�����n�6�9�M/�1�^:�x:���j��9����`q.j��e�����������M��}0��KbR[<�����{�H�� .:c!�I������1����2��X�������~��"kI�s��������.��en��z��+��HI�L"�4]7;z�Q�w�.i
J�p��:C�|`��B���$������a���e�.���"����z�%*da[�����5>�CNM�`���Zb|��Y����h?���nf����&�#y+>9_)��b�]�=rjwob�ca|�[\���oY5�� /A��C�K�^t�Lc���O��d�n^.��.L��NM!�Lf���wn�5{��q�n�r]�y�vd���7���f�[�~4�I�^RW���:sV����2cb����.�_
Oc��`���MM�E_�7*��F��W>�����K�t��o�+���w�����mo�9�`q�xZG?o�;;n�/�!��DB0��]����_�bK�n�m��{�gg���7���R�q�Kf�7�fX�r�l����l�S��%8o�L��������7|g����W�\�����-���8��qm�W�����q����1�M>5��v<S����&����1��f��v�=�����2�<��om��w��{al�,���U��L�D�B��n�e�jk{����t'�U�^��aOl/i��t�-�/,/�)|o��'�|0���9���N�<2c�����I��S���[`��YJ���
�����	��Cgk��=>r�����1?�f�tL���/cz�G�����`hO,]��
�R��Y����nF�[�5)�k-����!�uT�U[�!q
`B�3t�5]�}�{��9�L��,��h��Q26ZzB�x�Z�I�d��wS�]���
�V���Y�h��%���
�W�.��Yut�8�~�F��F��M"��y>�{2���GTv8��_�	�����M���)8T/@���YSf��9���k�F
��06�]FxC�1J<��az��,�b�x0��J�J������Q����|,bD��=�V�+��
���t�\%�?����<;5v�e=m	+�K
G���6����`��N'��HS��U;[,�vw}���!k��a>S�?��X����,�$����y�F�H��@������dk����n�=����R���J$��MvW���X��E	H���������i�����Mr7�%��%��Y�ba�z�#� DD�[S�C���<'���S��H�'���I��M�qtoH����
Ay��w�<<l��L@'�$�(-�;� �It���P&84����awP�U%��7	v�Eu�X���h�dq�o[����:r�_2iCk�������Q�GW�Rl:�
��wO�o:w?4�bfG*n��	ceV:���:9E
q�!��]���4��t����];�.l��m����eAQB���#���snv��:�����:Vo���N5��+1���c����������������FK����"(cD1��g<��G!L��l�Q�)�@)�^��e���WQ"�RG�1~*�[�X#G�KI��^Fo��^���6V�D�c	S���i�����@ib�&��D�@��N��p�������(���M���j�1��EtB��JS]�� f��9��ZZbj���6��L?��"
�k���4T�&���+%:?����o�~�x|D_���o=z�y�g
��U���c�~�
W[-���j�y���X�9x�X�G������=����H�h+��S����a/R7U(�]�G&��<l���N0��Z2H���@?�+&
=^w/2�$X��k��8T;
U���q$Rh3`]
1`�����>Y~�����bT�o�ouy{}}��)���������,��[;�c��������x�1��ok�Vt�M��5p�����}x�<2W�<�[t��L_T�-��;q�d����5Pg���,p�uh@�=t_�.��"0�J��q|
�5�x�6=�^��?��)��N`?��f�vB��e����i��zc��5� �Q_-$���%gD�	��END�S�A�����0z��=dZ��\�����Yg#5+������M��m���YA��no�n4����*�42����%��]~!���.��=((
�����`�X��v���"��UB��q���,���csy�5l}�?T��a�s�,aU0�J%�{��s:�����\�^��6d�O�2�E��d_�����uW*��e0��<ZO���R������G��_�w�K�`�$1��o���04T5i���x���j2�����$�~�P�?��+��-y��M'(*��ri�SY�<5,{N��Wy6�B�~V8��J_H�4��oDL��#d�
���!,>����Q@-V��&[��x�p�������l���
������fb�����R�+�����Mk�+4��(m
��ap��]�����Uq���S��f����>s�|�����f3�*O1���/�b~1^���#��J��6�'������T�����y���b�P:��(�8��@-��8(�'�
����KZA����u������YM�7���e9��_�J���Y}k��$�S�Q|M��q�������:�*Q\9�}
��W��W�����t�����������B�G
'
�����"�z�B�6���-�O��g�U�'���|��
����]^�P�����X����y��+32�G�����e�<���/��<k���5i���LJu�E�fk�Hd���T��Q�-�Y�."��$+R8���b�L���`0��\r}�����<[�?�{�L�(_�9+�`�
�S������H,�L������V�".����{r�I�5z�v�~������??������4dq��WS��(3��O�L'"�����#��u��n�^[��r����������%� ��`�N�l����P���J@5ao&�-F��|NYhq�r��U	s[� ����z���=It�f�=�/�2]3�L��X���0w����DL��c��\@�
��&�xL�SM�!O	�J����Z��V�I��� U�����p:M7#��dw��B_9
j_����k�s��KG����r��@O`%������:2�z�H��5���#��O�Y�b��3�,��h�����N�2��������=�nU���li�q��E+����0�q����n
�g^�Yf�C��e�-���8��Be�M�Pm�Mm�RS'�1��[s[����/�kzB�nG��y���`L�QH����f���&Z+\U�'�]�	o�"PC�/*��V�!����_��m��[����R�>�Io��J�b6���l����>U��}_E_�3	�s.2��J�o�*�=e*����7��_.�/u�T,��E����S��0V�B�Z�\~�P3�Sk�u�)�Vr�5F���
��*1�*�����~:�TCc�Ps�S��1O�6y���i����?�xF�1R�E�5���:[DhmD��&��@�N,���-��L�b�����I�3��GK�H�N(���f�#�r�����4$u���f�A�@2�T��V��1z�(4�lfN�Q?�����d�����4��2����Q����EQ�'�)��,��{n/��3}h�?<���3�i���R�R�j��"�d�<!��U.l�UKV:��}X
�L�F��G=]�I�����A�mMq���t��m�^_�� �\�� ��R�������dy���D�����-,@���K�-E����]_"�kk����;���
��6�?���TJ���6R|�Z�����J��K��QN�<���FL��!�������!�u����������>�1)�w:�U�xaU�ts�'�v�n�N�Ks��l��mR��J��a�e���
(��J��U��(�_�q�%Di����aqD�4��[1��|5}��%���
eX�����i��n��������f�����R�w7�j���1zm#��?��jQ2�S�	._�-(�);kk�p��TN�y����L��=~�Y�?WC��-s���s���G�9� �)��3������*}���,l�!�9���9�����G/��|�*����w�6�x�4�{��K��H���j�� ��������C��.;�^\��J1��_��^����?e�;�A������z�2����������?���(���Av�L��>O���#�-8�����k��8c>v��s��%�sFg3�����?%�}�>8n�g��MiKkl��{�S
tx���I6�
W�����4�������S���
��)X�y�������������]���J���/X�m=\���c2&H�+XO��1w���`}�)9�Mu����w���WK<����-Z���~����.\;��4��i�5�R�E�)���L��y�Lb.�
�+���I0�����{�#�nn�'w0����-��S���c��&#���s������y���0���0p�2���S�n��e�@��������R|�j���/-G >��V�����<��uD�����-�r����F��:B�=����-3����@LM��6s�I��������rek��g�����h/3�.o�a���/�����VG��TrK5�+�����,U��W�h�(�`�9�VR���`n3�{�zwf��o�
IAp��X�V��U�9�`|�#kxp)�5H���[�-'*e:�S�e��L�������5Jd��[�6�
�q7�T�0���c��t���<�>�0���	��=�{4�@�SV!<}��0�*�0T������35Svguo��O�Fv�-��/����m@J�D�q�K9r3'����#���x��.F�c�@��� �Pv�U���vt����"Gc�&F����$�I6�
���0��y�k��Tv�yu� ���������bS�mst/����$�����'�V���N����EN�����>/T+9?0{�������<��~��M��s�K��g���K�j�t Z�N�K�j�1� ���U��������`���:�\��MP|����p�x����G1�;E=������m%���j�������n=J=�-��$��!Y��cs���e�J��'����������mN�[r����k����Cg}�A~�Of�\_M�+���kg����*��UbH"{�<#�/�Zr��{�X�(\S����������T��A<�
rE�y����M�3�.A���Tt�#'�
���O���9�[�=��J;����>������<)���Vl����]�����s���3v��z��������9k>u��������v�	��u?���|�e/7�����'[�f�eV}�����_�����������Uz���R�S���=����"��L��Z���a��7���Q�7���h[�{��e���Y$*[_���KzuVo���w�p��3#����~R�3��>>������Pe�\�����^�
���i7������m������W������):���������{����������q����n��1���V��[��'Z� "��+���=������U��vo��[�Dp��������%������"����&��9qT���CJ'���,Z��.g��K}�|����r/���2+�������l�������X�s��aV`����f�i��������!�|������������A��+���i|���-I:R�>7�2��9��R���@4h�,�;��3�����s�U�4���)?�S�$�2ohh��_��Pipi����I5���l��K���O�P���@��C-6����0���0�D��ua/HP��j����o�[Gd7��5�������������:��^���pJ��:����Zs-Z^on�H�����-���?DT�R��k�^������e�M���<��@��o��'�����"����O�'��s5M=�ou�����e=h�W��k	_D;�z=�������\�&���j8�2�W:���p�R5��:�*~�|qM����C/�jzh{����O6\��4�BeN����s/���K����<j�����4~�L%J��+�U��v�"=��T�����N������m�6����K
������0�F�6��UTy�!�kW���ao#�~���/�ur������2����trg��^X��u��Z����ML1
���@e��QN�v"\��j�r749��]
VXo�n4�q�?��"�qh���!�Y�N*�<G���t�E%�S����y��f�������x����xKkssk����P�J�f��E�	y]�W�wY�0��x`���bJ5��������S�%���l���(���7i�t������x+���k9Li�Hs���~JA�0Hi�l�q��VA�#!Cou�"j����l�{�f��M2Q]t���!r:���h��`���)g�G�n���u�Y���Ph��xy@���.E���;��1:�h����������!u�bXo�v8z��-������F�SR���=�rh�����4��z`i�m��b��O#6�&��g�-��!��-Vz�WoI��n�a�w.�Q��|���Nw����.���H��^��tT�H~s���L� $�+�)�_NU���i{���p��UA������y-���ho]X�����;�x�������"e81�m)$v���9���A�#*��u[_�IC�Gj�Q<��F���D��n�����G��'i�
����p|�W9z��|q<Z�F3�Y�oFd/��'I^|����f���(�9i��pBJC	|�(�����9�Or���-��2��@g���R����5�VG^s��'R��(�IlNlI�L����yI�#����������i$�}�^�Yt:�n�;NV�o��� �}qY:����{�y��].M�i6��O'���q���m��%���F����=HF��������r�g������{��:���|���b\�0�6�`���=��aie<Y��� �@��3D��A��
��L���#}����g������t�L���?���;�\=���a�A\K��2
�(�Z2
(��:��;L��?L�u��%�����?���#���)�
����OB�����p�U/�un�Y6�Oxw��s�K��H���s�XZZ����Y�A$G��m�
oI�]�Tp������L�HT��i�D3Z��}Y��pp||zx��T���E�?[.��"����U�
�FK��:=�L]�����{	�*�/��~
_��I{fF�S��B�������<jak	{���1'����s��R�J����O(^��G�9��4�:|��������-�.�4��GR>����\Qg����lB����%U&V�j����PA��`��tX$�B�N����_���*����M��������w�����V���Q�M�u-~�Yq�/P{}�k�>��&�x�:�����9�gt����}rp�g��)-��������5Z�������$�c�]���a��$7Pn�m~�X�*������Z�>�����o��vji�m Oja������N1"SHN�������!�lC!f~���l������N@U� [%����e�1�"�R�t�QM��������S���f�TV����3����7��p�M��b�LT��c�R3�-����fl��J�|4Hgd�XN@;d'%�o��.7	R�o���9���7����(�i7ez������eT%�4��K]j�2�7klV���������X�.�"����p� �	E������pn���Y�����G%3,)��;�p����B���A�-OHyX�`�U/�FU#DfXl"��\��O{�B�N���a���$����t�n�(HLI����������B�2��p�V�2
T��?�.���/�D&-�z�ZZp�P�	����a	�c�\@�;}*m�����YVML�U�d����J'FS�W���
��*���Fh
h�NR�da���Jhd=K���T�f9��6�0�b�`���=��0`�Z4�q}�9O��y�e���Fx�����s��~�I����Y8K�+�L-Z�3;r'�q�zv.L����B6�����,��{M�;����pS
5�
T���vz�[XQH��I����)��)~��m_
wV�[���!{w_(4a���W��P2,�&�v��*�fZ��9dlv���WJ&���	G'�<eg���1XI�H��/�@�X����y�����C|n0��k��J����I�7�/�9�)�x�Ix'oJn3[S,!c��?W$�3*���E��nD���+t��@��z��@q}� �2�J�Ho��^7d�����WJ�R�v�����LFRc� �'�����]��~���`0W������y��>^9e������cw��:G
���h�[�CN#^\�) ��`��o0���zF��:�9n�/@���/M*�DYq��A�q:������T��C�OZ�����U���E!��,��B*mU��+K�K��lz��i�#&�4_���.��"1�w
��A243����!U�����}�:!������/�1~O�V���`�S�gF0��{���������r�j���5��1(���p�?�Q��qG+��9t�J���Q�z�� ��Z����0=�g8I�������4B������DVz���v����1���&)�j�����R A�Z<1e��U��P���^�q�N]�_ctK�A����F�^�
5[}��f����_L}��Al���k�o'iv'�����V�����sxz~����u���������������kF�eJ�Jn%�e��K�M�G����u�-���z�
���y����-W����o�^y��]�w��������hg/������5#G9�g^$�����n���^{4��%+�u}c�wEcI�4Y��0�F
5�j����,:H/�3c�Z��L	��!
��"��E�Ek���t���$x�:�R���F_�TV�u�xbYjp;s�R�T��R���O��#�t_:t:��F�b`���r�[Ir��a��O��Sg4S�6�_��j��]�2����mz5?,+��bV�H};f� �_f��I=����+�-�<�e��cs	��������=���q^Zm����p��Gi��Az�"�������w1����5��Qp=&����v3��13/�n�`q�b�T�~=-b����1�Q���a��#j?��9��|<!����zPl59�������
sv8LH��P`J�J�7�;�Ud ��^��< p
���X����un�p[�.v��D�~��rB����<�.�(�����${g��u���Y���(Y��r3���s�����������98??�s�]����9����P��e����8�F<�$8�Cl�&z2�Au���R����'�-#���5���:yV^a,f%�F�x�������F�y���{��h�
K�������{#Y=^�,�������[�[�/��nN8r����	�E���s���=Lo�����S��o�2���~J�t���p����~�PN��'<DQ�&��U���^�q6BFr���agts�S����["��dY.�t��8%���;�T�B�Bf�P�F�w\��V6��?����s2�}6NuG[���������B���(8�2��=F��r������G�uB[u�M��RG�3��<��_`2W1N[!���l�g/���r,�_fu�������p�wm�������H�-��M� j���c����'A4�3�w����y&�]����y�U�����y2��x@�@����;9��U�$���hpI�-Ep���D�^����h�{�x*�����"D���B�]V@.@���s��>b�2%�V�g����t�����f��=��\�r�(��q>�����R�s����T�(����Z���uF9�I�t��� �l��!��xGi���v�Q��A��xe�cg����L��v������'���v��3�)��u�w��lNQ����cT�`����S���zB������������M�}u/b��B�MPH���Rs�:�k.��\rU/�C��d�;��j{�J4Y����Tt��XIi��t|�v����=4�U
�nl���j/��s��p��
{d�1�S���8��[�+`;M��>��$�8�"wf�ph*m�0.�*am��@��Y��n��2�I�~���B�E�z[��tD��6�w����q���c���A�������#T���>;DQ�wi�:�.���'�1
�$X������T�	_)A�-�[�c2No�:����3�)�g��L�m�)$��!Y���_��5,�0�����K�[�r�^���K����8]�i�W(�EV������#m����1�?��'M���:���_�����N�GB;�)��'��C����Q�_��������p5�G��O_|������z�'bQ;=���[���J�����xr3�C��,�%���}����pf�i�X�:DL+���oG���ff������3�N�r��	ni�]�(��a_SJ��+'z{�6`�Gj����>O<fn�;d�s����=2���7����t��x �
Ia1r=�z��zw��$Q�����G����=#^lV��k;W�_[_���E�)�1�j����G�_%&G��t?)�"Yb�!��	�3�n3��sy7�&����)c�r@L�o��e��%Q������A  �Phuj���J�������F���u�Hg���n.,x�|���
�d
}���yN:����,�,Ir�e%�i�k��R0��/����T�0�(6���Q�DN
�.�'�~P�#re3�r�
C��j�W�8�1�hP��4�������U�#�7K{�N�|��M�4��v�'P��)���Y5f�.j_�����S���WNk,��fA5��-����O���������N�7�<�����md+N�� ]����������=tg��j
dFEb��D>�&m�3��;��e�>q������f����L;������>�������F�k�~�����
B�P_��G	T]����UM�`:d��4��	
�21'�Q�-:Em�+�^�%�$����t<�����������~��!~����
�JH$�`�)R:>#O���lC��b��� Bn�;��ga���U{���(�+L����������j���N�P�����qo��V�}N�������=QGi��'��O�<5O�]b]M�&�����*��u�������LU��l�;mq3�~/�O/;F�<;o�����ys�ju�������	���K%*�mZ���f�����k���
b/s87�2�pq�4<q2r����Ys�����+k�S��`Y���7+!���v���%u�7����}`'��mD�����w&�3�*���u�Y��K���E���_,�H��� %�G/�$�A��������GSSO��bo��^X�Of
���o��H���<�I�A���
U�XL
��0��y�x�#��C\�?��$\�B8��=+�����E]4���F�"�)�������[<B`�j1���#.Bk6�����J�h�r���a��Z��g4Zbo@!�g6���Y�hQ2���>q�g���-)m7����(��S
P���Cs��~�Z���\^���������6g�\{XF(���P�+��SPaQ�
���B�Yh_��S�����E�Y$�E����_���6fe&B���Z#���,h	�����}���IA<{�nw���4�d<f=���H��;�mH���'�[aON���i���L�+P����iq��y�Q��,rCT��oK���}����!j�ok_���������`�>b)��1�r��m�;Z����Q��.������k��hAb������{��A��6�E���k�!e_�b�����K���*��dr�]��������eW��]#s����Fa).\'(s�j�.F��cnB�)}� _�8H'����g_�+���n��
�/Y��k���NI�{!��S�{���%*��7�i� �o<+
���5.�����4��T�a��z�l9
�[�����D�����1TQT�V�2S�(������>��4��,��U�G����n���8���*���0����][O�����������t�okV4� ����mL�D��x�W|��hv�fb����,�������40�[`�jKm��������:�an��B�������1��|�
���F�Y^����7�E�WReI�W)�����k�p*�
��QB��Tp(C��<)������]<��u�3M�fW����W�e5k��ktv`�Q����U�H��K�2�P����$�,n�_��K�J�:��CBD�������� �I�d6CKk:�Y�e(�3��<���E
��y���(v=E�iY*��.��5'�,K���$F�$�]��
��������'y��e���!�T��L���#c6����"�+*���3R��s�3"�g��<����:@N��+�/rl���]~kA������_����?S���~����/��)d�y&�0�oJ��6�9��������R�toGQ��#��*�W�{
_\r���Js���%���?������pY�w��������*�pF0���"�N�D9S�L�U�,f�W�+9��XQN�����x�z�R�GT`�7�T���0�������?�Jm�}�B�<����OYIQ`k�PxY3"��4�85�����_a���t(]�� �Q�~t��=�X$�/�.r�'rU^��*12�@C%b>U%��~u�900+�	����k����]�a�������=�}<
��7C�_fb��f1���[[����V��__����������4~����e������Ne;/s!&d����=���4(����p�5X�	����U�d��8�?���J��Em-�Cns���e\Ae��M��l�)�H~�>J�9��������g�SO%RoC���nj���)T�����g��d����*��.9e��A��dH?�Ebx��1C
��Tsb�������sby_�����I-i#�$b�3\4
N
���yq��jBO�R�!���	���Z�����c����3*��P� +��P��,�����.�"wF�������W�2�A�B2-|k�g���bE���}���q��t"�*%���}R�P�m���?]�VIK����d���`�����[��5]�~%p�<	"�r,��H�@��� l�����o1N��V���n�����.�"z{�1�bG��XZ��4&7���
�`Y���<�����~g^OW<��>�����S�x�zK���A�bbg�E�a&T�������Uy��{N�y�	���eO�/����	�`�Er��fq B�K<g`&1�ApEw���t�Om�0���Z��WT�H��	���J���+�w�x�y��~�C����`�4��g�u�'>���D��
��s����Z{�^�����s�.�����������Q?Q��(uB���/ ����DK7�b�!(H��d�;�\q[�k��c��w/��:R�W��1 �I�3e�l��������P�	��+���4=h%&��h�%�N�5�\5���jf�{0�q�� ;R>�,��Y�o"M��9{m�����Wb$q��]/8��.��S�/$Q����EX���ypB��`�F��.�#%��fv��M��9���$��V�@��^2vay������W�?s�����Pp�i"?F3��g�9�����n�����������b��7���\���������Wc`�'�/���>~�|{\{5���Y�|�fp�����J������r�P\=�h���ZbU�����3<{��E�T[0������3C�j�L4-~�-0fg|�1�8��m�[!��A�%�����b��w�5R���q.gL���N�t��:��m��@z_dX\� ����~V�1�VV�5���}��G���
�����2�;O���q
+O�id_u�w�b���~[��?��jD����P�}��6������I�M���������dL��K��c\ZE�aI���=@^F|����z�=O�+�g?�0 V�r�Hx8�F��$��r:����0��9$.��MsNv�'D6�X0�=Sq�Q�J�,4z�	X���o�U�?�]��~R��Q�V�<�!2\����t���9�y�IZ�/�����|�%����0���SEL���3��SY5�f�K�TxT����uV�L?�=�ya�������9F���S��M�u�0����f8�����c5��p�h��?Euxy$O�0��$����$����H>����B��6�h��1��3����/W|������L����)P�1����pL�S���M�[��O\rV��]�F���b�Ze��$'�D��/��v�Q��df2SJ6TX"I������f�*��UZ�5��������,��2��m��+��M�J�
��k���sxV����uje=/	"�	���3a���Vl:��T�5��)S�:|S������O��<U�v�����';k�k�=��	�T`�I�7�(G1"X4f1�,Q�m��if����:"y3��h��\����<�(h�'�(�?(���Z:�i��-���A`����u������'���1��o���Uq��"������(��e-� ����C+�(��1�G^��>a������Q��uY�++oy��s8pH��XC�I��IjHG����$@y3�:O~^BaSO���Dy>����'zI?�m��4���=x���%���%�ru7rb�fQH����p+v&��:�Tq��&R�
G����+k���l���,;G��'���2����D��/��5LgO�T�����{�;��)��������FV8H�5�f���7H�h�q8�L<Y�����)�9��ZM�M(5�f����P��hA��}�����ND^�5g���c�\A�#��4���Ur�)��"'q�s���)�dNe� ���?�7��V�}{�u���G����*�d[(�y��3�? ��	�6�bI��
g�
���|F�(�b��>�?gSo�)�_���/1��.�������p���`��8"�o�Ea(%G	-���e�e�m���	���,p��k#Z,�G2��/7F�1o_����1�%� ��g�B+,8j(2�k'������}�|/!6���e%�c���f���OW+�����c��������!u_���d�0�N�W��cK�m�l 2*d�k�h�F�!s��qh�_��tMXd��A������&�B���g������S�{*���������W�0F��n�yX��M��R-
��
��z�T��59}�X<L�cuM7��6���|)z����J0�d9l�����)�^�}D�F�_����y��<k�%�Q���<6�>4�#�*?T6m�"�i�u���_	�(t�9<�P���#r�?��>��Ux���M9�����d�B{X�(�]|�=H5��kH"�M�l��!A�[!�t'�X(b{q[��E��E���7�Q�����'���+>��������l �`�
�.�C���8I���s}0���6u����r]9
�A��v?�����`%��0����!�d�F�Oz�_q�<D�$�,��N|J�ek?��g��JK1�������bB`@�G�|R�=��
��Z����
���Ll���A�4���
Sc5�\�(��E�G�~<q�L$ehPfS��8_���y�8f����;����(�I�k��&�!��-����S���c��0)��� /���v�����>����e,��"�f���?��m6��p6�E�$=<�MO|u����gi�M������C�������A��E:&UdZ��n�
�-�����\Da���n��e%�H�N�-�"�����o��QkuH&#W���������d�|�&nS�����^�r������=���#m[��@���Jh�r�d
����(O��������g:4'|����%�^��
��2.�"����3�M;�O�$���|��PM �r�a���dF �!��%���B�F[Cw��`�����W4��]�	*k�8�wRel�Z*����'
����}�%J�>7��M��'��0�)���R�RN.����f~t�4Nd�(�!D�KMs���������tB3f�9��
L<��:����^��������SH�[�Q��k>��Il��A|��-�`{������SxC�1����k�	O�pQ9�����.��Uz�a�,�t.��W#GgJ������y(:=WL`�8|ci%�7��@(1���8� �d�.I���~-\t������-����ay��{���)e
�+���E<�
b�� ;��a~���x���4���Fr|�5��On�=�w����]�YY������O$�2"��g	P�u�*|������|�����J�@��# ���}������/A�*�G��vi�,>�qv�5R���.�K�@S��@��uis���H�ee�q���)N�E�bA�.����z~w�Z�KF�8�&>�z�Rg1!5������_o���v��yc~�n��kZ<�t��a�dt+C�,E���1��������_\�N[��w'�����t�P��^���v-�;	��2�z�0�<F��J�������nk�9��������Q�Mb$�V��8
"_�#j\�����b����1��H~b����h�e�#-N��������{���eibH�=�b��G1�;����{x�PN��$��D_02J���A�{���bl�$_�V��j$e��	o)]��y�@��ya��������b��#�p
����O��R`�r����8A��}ad*G$A��-�����h�������j�O�ZQR(l�G���oK�%0d� J�:\U�E�cx�DP\������h{=�u�F�����H22�1�l�*x�N�p�(��,I@���:�=�(3�*�����b���
���3q�k?0p���)c�{���&�'�, 'j�_..�����O��@����=!�!�g�^��$l6��4[�xpA��i�����w�����R�W>���N&+�y��k�/�+f�R����M<�
U�f��!�����h�86������/���;�9Q��A���������4���0O?�g�4��'8�}��*�hc��u���Ya��N�j�g��Lt6&���S~~����*=��z������2T�I'�k-������S�{Q�
jA3w������9iy��=
���3����
>�YB�xC#��_zTU��l�`��U9h9���_���hP�yt�aw�c2������������S5T��I8��sCE��Fs�E`����$2�o)���j�^���V��#Qb�H�iD��t�H����Jz6������i�]��y����'����b�}�f�W�0/�8�%�\yg���������D�|����dV���m����IE����Y>�G�M6o��c�bj�G�c6F���1����{�����t��
�-�VC�����f3]���[l^C�#�"��2b*;e����.��K7Mwkn����q��59�P������w��L�2������~ %�k�g7����7
 ��*���Z��Pie���t�,�����a�o�O�e�,e�7F�A��l���9����R�P���*nH_�b��] ����J�������f�obaAf�������9�^����7��������EiV*f�9,6c���i��:5�-0��Oy�T��bJ�����:�)iq��Z���eV���k	.'�xK�����Z
�Gr�\p�P��SWd��B�Z���G�h�o�m�\��.��y���f� ��qG%s���yQ�6�3��
t�0h!<��T1�t3z��d9��O�Yh@>�{\~X�����5A���r�l4�=F����-���2�n�b�a+^��UQ�NuWdt��{��`��V����W��*��[T*aZ=�	hJD:5���0H���E�2P����r���z��APmqY�.���(5�o[Y�9]�L���h���+��w�/�����]���z��*��f��uG|���C,����YB�
1dy�����J������	���Ot�T������-
�h�O�Zo�+�I,c�@���jeO�7�������Bn�{�����Z(BRaH�@JD1�D��%�[6M�i��H���:�V}�L�������/����yX��"�D�t��L��u��{�����U2����� ��[�;CH
����z"��d��N���i\�}� H4J�����')��j��'�����z�3�!�AFK5a��p
�t��'��ikY���G������2�
X�``���Aj\O�1�,��{M���zl��k�����/�R+���7��D������B�	�O�K����h��V�#��8p�B�U�G�����N�������]BAQx�K���%��.`
�����T6����2���dI����EmZ��*T�������s�����0����Imeq#�^����{l�\�������P5�X0��t������T�:TV���F;���tU|�%�bX(��������<�������`Y��GU�7P�i�?��=��F�F�&8W ���DZ!�p��I����U�����_��50!H��/���"���p:q�SE?���Q�����A	�D�{`���j�q
J(&�C�����*,������`��:39Q�b�w�����;�V0�YO
!Xn�����+>Q�	�x�#P�/�6���y�-�9��i��V_��=�U������:����b6^�m�����N=��ql��0�j�=[t��!d'����V��nf|>���4�L�;�l:L�3���a2�M�
�(�������b���f����<p?�V-q�����t�!.!��{�pK7��f��h�tx�#Hj2,� �L��\
;�8C�T���
pX@��]���8��\���<�|�A8l�':N���Q����#�)O���!Z�a.~V����n��N�+����Y��$(��CR�I�f��X*�:��a�RAJ_�[����[�6��e��d�� i�k��w�%s*�O��@'�.T
����Jc��&����tu�C�(r������z~
�:�i�(�z��yY<�q ���Y������T}�C]6"�qJ��f����]�4\y��6��$:
��FKR�G����� ���7A�?m�����6+(���&M���t��l��l����	�9���0W2!���>�,AC�yi�~Q��w�Z$�����-���V��k���+u��2��4;;G�k�
��5i�hlK��+P<n>��^�G�7��^�Y���0���\!�=�,h�.������zZ��������WFB|��+d�j�`c��H�X!�F����#�^&'v�H�9KS�p��5$�J�OC"[wGi������Y�����:
����j�F�:��#�Da�{L*�����4������t�{�,�1�K���n~y���a�T/��E��fB�=����N�'�\���9WV��/9����S��$�a�u���#�8�8����1v$���k���:��o�@���@&��H�2�Y0���]��L�v5������C9����@�,k~���(y�<UU�!�j+FeqI��z�`��c��pk��#�U��<�%������1���M��_�u�:v��a����*��ZY�=���d�r�|h~���\���=��������9���G@�N��(�>(�y�����yd�� ��`������o����k��]|�C4��%�����X��m���]��\BY���{#��!U���}u���!V�)��r�X�����
O����_��Z�5���z*��������v��.d��t�
���RK��K���c���5W�}����e���s&iC�����R�scL�"�W"����!��2?S�Ap�����v�������q���N�F[�R��!_��#��Q���`$ ~��sn?�	�B��R(�@�u����My���O���j�����iA��G'l?4�3� 4j�+����`���IoHg��t}�Lf��C�Y�H��0��h�1I�0
�}1�����z���5�g�g��I
�3���$�"6�y�c����}�����[����������'�y9�������~�����
W��m��:�/�_�G�X�U%���>2�������	�Z��q����
��ivR{�\�� ���%������=��9o�^���@�T��.�2<��l�������<�\~�8�xxz��������e���}��[��?�o_�/[���,;�������OV�K�p�������.,�����$�0����� ��|f�H#�f��W.O��y����Pz���G>�����g-��|�H�n��W��+�	�h*,�.��l�=�Q&U,��Z_�[�t���H�P>m���'��:%E�%����( ^�r��P�iJe�����<�wF��@e�����9�dx=�q�=�g��u�Eq���5Y���$��
z�����o�A����@�3m&�W(�aK�]\��^���N���Y-�ke��s���Z{V�p��T��g��^[�Z���\y������`3�����|���������F�Q��������������C#�G8����,��\�&,p m��$rr����F�g������}�=#1��+^g�h:�U>���=#g>��92{���MR����\��X��H�s�����D�.�-�����/>%b�}d7����AGo����e2r�j�����2��$�	���k�(m��Y��KKefIZR������9�@���ms�T>Vi�G����X�z�H���g�3jU�^��5t�W���pu�|�W���1jCO4��(�pwp��f.�]����^zU��4Qy{U�9�wJ��VH�'��W^�_�cC
�%yq��"����bZ�5���l�����K��Y����E��>Q�����]��J��������G������
�c=_�F3�(��7)�����X��g�����^'�z�4E2��"ok�YN��x�O��k���D��*+�m9��D�.\W�D-6���
������K�����y ���������-UTS��[2o�O_�R\��zVJ��>���q��svp~��u�:���o�F��k��������9��\2#O��{B���`%cx���,��n6R;��=c��.�7A�i�w�G	���3���k�����_Z����A&E��`�����n*�C{�g��u��0�i��J5QK���uN�ds��1���|G�uf\����/��u&������"~�J��?����;:��+@���W��sg�����)��8���i2����S���N�;R�)�h�*���n�tXW�/d�����44��|��u��������~�>_�T��.���^��WHS�C���dW{��i����L1��H���O��+�K|�����d�J���&.����;d�#�E�6����A9	� 5C��}���j�b��5�]��[�4��Sj�<���R�SQ/�	���a&�(E6��O�!���
'|1�����DKO1���_��b��B��'g��$��Kv�Y���E�M�**�	V�P�r�Y�:'\E{n���l��N�)�3�_���,hM�������%P'�����BG��vg��r�,-����]��lR���~��A_����E����YlSe��%I������bQ>�m���$7	>[�,GY+�����P�UJ� a�.�u'�+��������Z����]3�\.L^�m��l6�!:�����MU�fe3���0S�}%1�@���y�D"��*���v�
r�#(?���n+|�~m��d���#~,�A2��u��o���/W��Jo������v{
�|�	��XZ=I+��c��?��I�(bDb]N�Q�B�}�R
Q6�A~t~z����8��~�D�t(�7��x�e��M{<�Z-�����
�Pj�)6�k���������@�k
�����%4F3�,�2q1#G�*&��V����~3B&���W����w��D}�B���9��U�!#�����h����9w2'ra��$	�%#l�[(_����Z���gd�'u��;c�M���W���K��~�ZQa��'Jx�C�B�X".s!�q���sH�^��������a����6�|��>�9�I���L63�"gIvt��W�7�����HH�
Ip��v���o�����)������{'�h���������D���D�����I�e��g���=ee��s���(n��I�zC}a\��Ut���'pjh�o!P��y�K`x������E5������R�"���Vh_���\�������R�?�H=@;����>��%�������������������(�t�<������]��������o��p	l��"��"�^F��0i���3�Fk�]�����%E������=X��$IT����L���U���T7��0��f;��X��\.mh����C���Z���|[@��Sj"z4���k��;���	hG��<�P���>W�� �3�r�0_�@�b*����[�|�`ERT'pxE�'�>��k���ajp�����1������^y�0���j������L�r�GK�3�X2=�fC�=-����^k����r���,[�N�k��|�����T�*�s4#z:1����#XwH�7]�3r�9S������W���Z�`h���F,�NK�����|l��h@��G��z�1�C�7�Vlg�������UW����
+z�i��������O*�?�Z����LF��fa	]L���Q����*�t���x�8�����f�{\]vO�2����G;6�����������	B������OcR�IL�]FD��������<��V{7���D�����O�vV/�i����+�B��~r��IR���m_ �������*�J(�2l�P���bt�W�
����s����{�p���k����N���A��G���j���t(.Rr�qFM%jx�a��~���b��kA��Q�;��>%�A���g�OLi�wE����+�B�	�Hf�,1aO:��P�6���"E���d�2�l.8�@ ����W��R���j���a.�#�T�88~��[�$�m��������|�:
�wg�/1���~���6�W�H��F4��|CJ�E�����5?2�g���Q*&Q�V\(����%\��L�?�T������*B�Ex��g����N��j��h�Y*YZ�Rgz���?\����bm��������($�|���s�2�P�%�HP�
��$94P`9k.�kS&?G����
�(�J�\g������.����rYI�f���c��n�T��@;��e�R,gU�����!��&�9��!�����������Op7X�HP�y��p���X
���p�8��Z''�#0�-�����Kz������h�u����'�5�7���3|�$�6��-,$g�U��Yk)��d@��4�${k�!p��H�]e@���1������[�s��7/&k�3#�����x�����W��oN�.OO�g���6���p�i��&��������q��N���/���3C����"yj&��I���^r5�G�}A`�o��b6���j�1�������$3r<5��Oxx�9��i�t��EV�
��w��F��?m�H*��cU%����!�r��y�
���hJ�3:e�K..%�`MF�b1|#����x}Y9N������j��'�r�V��yy�{����m���~}zva�)I��pC#�m`:��E�xl��W���C�������K%�y�/0�8�X�<N�_������}�9L\��x���}��3bM�(�?|Ou� �A����)!��K
��%@�e>�TC
B�9r~7S�	g�p@O��Kg��[lTi&y��/�����9Z��O!���!�|HJ3�I	~B���axG���lH����3��mF(����@��>I���|xsQt����r<!�����g��7����'�s����H328���$��������j���;���PB�`'�����]?tB$�9�����0A�"?��CR ��r/��UK�0���#��t�<W?5���UL�t1��5c�;��{|����L�IH�uVp�iJ(Wp������2��	>�tj��������N\�*��$C�P��Kj�0H�b���f�@��G���/�o.�B%�qv8z��q��?��+O��sxg��� ��?�&L1������Db�M������G�W�M~�Ll�

�kV1%����b�ro���W:V�?{�$<�r/p:j;D�E��6��M���������x#���026�z�����$���U�xEK)����������((�M|��k��0
><�>lD�sn�>=�����c:9�)lg&����}��X����z�`?���"�	n����.@O12�Az���(�s]�N�^����d��0��@!��}�Q'0���(�[�����L��3���K�����>�����
��z�?k�������� x���j���	�F��wE���p�g���`/�Nl2����!�/���o���.N��.@�(��%8nJ�"��ue\����6���z0�o�1���1�B���������lqPhe1c��Hs���1Yl1������5'�s�32���?�|TN2�<]%���)� �aq80Pt�d��
!�cf��I�i����IO\�:�/�����"�dh�46��i68<���g]*D����Q~�W�g���F�+��yQ�l#��X�w�C�a	���Q��8J�C���a�p�G[ZX9� �$0I#���E����Q�+5��O��F�f��5��SV1�3U�_�O��A��Jh ������{UVn��j0cZ�d�y�4��j�9�X
A�s4��It�=(e���7tE��8��nW B	?�c<�/�Vcsg=D�����{�Po
�L/��Z\���S��,�y9�i�(����@��J����Ml���_/�7�#�!��;38�?�J���I>�d#@�i�=X�����K-BL4/�Q>E��Q5r��o��Y�N+�,x2�D @������!���z�F��K����>�*d���1�������s�)�\�����^rRU�u��pD��^b��Ds���!�L�J�����$����x /������i����^���I�H��Y�1�Iz����8&�}�99���\����!�*���t���p�k��@-��L�T�c}��ARW:����
��Aa��X#V,�E�%�����K�����p�%��AKQ���o����X~]�g4��cpy���w�l�n�BIg�r<������'���������14c���f�z�����[�d������H���SX��M�T���0v�P�UC�������s��nc�	��tZ��l�(q4�������\�%��M��R��{��a���$r��)�@rxh|�p���)�|�`�WC������������:�+�k����@S,ZA��74�:R������	�����h;�u��T�.6��;���	��P�C����������SDp�J���i*j0��O6w����a����}sL���z��7���������,JX���B�������XDRmBBXd�I:X*�p��E�?*���-�J���9e_�&�a��A���w��@$�������EpO ���U��S�}�-GR�r5�}QV�! S/��3�Qr�����N�Y6D���`S�N�`xK�����nC���n�?PsAK��0<���.��4n�!�C=y������z��tp�{�=/ :�A2�*,�@��PI��p��\k1Q_����q�HOrW����h��QY�^�����������Ho�J���Vz����'�{�	;�U" �F���l�.��U�n�80�q-L���JC�I�~���1�]���I k�M���m:��oc����B��;|��jg6���Ci�r���~�W�bL��FxJG��U��q
&�gU9|���3����
��$7���
��@���SHz��������������/����mm���?]�mll$�F��g��X[___�C�K����J��������_�"t���������O����{����4����<���/6>�z�e�����ev��G�+B��b,fx��*��lIO�C�5�o�������
���g�<#pO��k�u��u������p�0��hFS�6o���p4����sU����\#O����*	'��{q:B���'b��&(m�#u	Q��1�������Q#���W�S�c5����=|�Fb�g�.��I���j��x�
��q@1����{o�P_����K��5�1�:�����Q�����*�b�^�W9�m�h��.$R@����P�r����������&�i�Q�D���V���U�KZ^/�\Tn���� ��=9���?A��;o�k$w#����&����o�m�j�b?
��P���^I����fO�s4��r��^h���^g
x\(��4�9q��9j��V�~��K�m�W����p�FZPyr^'��@o�)lWyE��g�0��Y�$�(���v��J+�5)���F[ZV��O��chD�#|��a�����&�y%���y����#"��a~���&�\�C�pi��<��LMj��y�S&"ix���*�38��=��Km�C��+#;&o���{~~pv1@T���W�'W�3/��x@��9[tj��y�G��x��[g��
D�K���w���f�c�����v5ip�C���E3��?`�y�%��G������5�Z�G��,����/������=}�����
A�:|�>�`�)����;�,��\��d}�X
	�����|��R��<h���w_Ju���+t��L}��5�����H�X�Lx�t�b�������Q��q����P7�8�r�e8,S����r.��(?�}
6c��KqF=<�J�$}���Z��N=�������"�q>��'�$���ql$�AK�]�R���@���������5��6�$Q�n�/���a����	�����DZ�<b���>@�r3d���4]��1�8/*IxD�|����
w���s���J>���&��0��b�x��nLwo��7�~��_lDK��H+�������m���>MH�OmE7��w����q��U���6h�6�)Gw��N3�V���r�*'�����=
�����~��3a��.���Z�qDH����6�)���/�=Q]^C\|CL$;�I�����Q��V�	>���5������D�	'�Poz���g���3Q^��}X��Dj��E��5��Q��r[!����'����2�4���C�U�J	�=}e$}�-R�{M/h���!f�Y�r������	����WLf�Sg��:=K�����������|MCPY�*:2���o�s�7�#��dfQ��������YQ�r�X'ZAJd����"�u������E����QrJ��	�oM�#��l�����y� ���*~�C�,6�3�#��'�)���1��1qg`��%^�Em�q�����G��(bl�-��&KK.�`�b�	�.0�7g�R�����"��Q���WH��
��h.�&�*U���Qo�g	�oc�!.�����A`�A�
>�F����5}�>t���!���T��p��e���+n�L�����;�,6B�t�ka�;���v��o�P������Z]��e5G�$(�,C���L���E���|�9��'
��fM�����M�zm�f��N�ZU\��}<��:l�\����K�k�O�As��P]���\}����!��������s�0a��K���5]���+�C�< ��U��^������Fi�.n����x����S;=9���
!�A���l���;E���?a@p�!)[2��V����`�F�K|
�V�U%�2�V��~�F�	\�CD���A�6���q�=���b�f���-�� ��Ul����1����&4GW�]���������A�\��{�/O�(��=��{\������W�z����Wj�j�������~qy5����^�C*����>WQ���?��Ur���yb����E���>������/C
xr�2�;=�;8;97� ��1O�H�Yeq�����KF� ;A�Z�������n������kH����Hk�y����Y��	l+�rV7�-�3���/e1Z3@���9A�awTr�*�X�������?i|�������)*��<�����G6\��g����u�S"H87y�M�2���P�x��7�!��-DKiUU�VV���L%e0r��f{��M�O��!���U\==?��4���
U�S�RM�7F�;��d:q�s�%�e�~�s(�[�DZ���.�]X�a��w�&���hHv�R~y����I��'���(g����D/��%��_esi����-j����<;M�!'�*��Z��#x��]����L7�v#�My�~ s������:��W��hX�	�����������'o�����og�NN��w���1�����1��<&�����%��i���G�psd���3U�0�P{�.fJ\�����xEd	���(����7�0c��P�������V�����4E�������j������g����f����cY'�����Dy\�����:Br�W����o^�<8��}O�I2B��{gy8�~�	����|���i�ZI�_H��w�t�O>]��9pA������K�O�!���K��*f�N�(�}���!�o{�KL;�;����_
=����g�3�T�H
P�_�^�",�?'�a��
�����I�a����
Fr�����;[:CO��N;���^�k��Kv��(�����0���w��>`&���_*��;�9g%���bq}c+MQ��������V�!8�*.2|���-A�2��5&���H~/Jq�!0��F�f�1;W���@-�9I�H���u�]��bp>0�~�m^oj$j0{��T-.��X�1������:�-A�x��Aj9d~�\��o�&�
���E�wI��Y�H���x�X<r5�=�F��&;	�7����o����:�]�������C
�dA�����~��'$1�zp��B���[�>�`<�-�/-���hl�����\2����B�����m���Cu�*��Q�	@ZT7�����w+�xg?�`f$�}��
�p�������5�D�x
��O�#`e��NN�#��bX9u@�� ��|����4
A:���u���z�&�b�O��_�<f�e�$�����p;?�<��������z�t���z��a�]fw��g���Lj�CD;��4x�=������08�Z)
� g6�3���M������K�g���������7$m#"m(�Z6�B�c~u�cLH�Pi��
wG��vu��V|)`��|�'�.���s��\��C�T�`��4���xXM
����)����Y��0V������I��L��������@	Nx���3��E<��?@LP�>��f,�-z�P5!*<:H�2!O������z���� �������/��7��"����
=�P�6������D}3TK�1�:+�����"9���eS	�C�eQm������RF���pl�����y�����b�j��Q.�G�J|~�����
m���+y��^X3C��3�V�$���b���k����:��>\������Jj�;�n�M���Z5���f�{a�}�=2H�/�������)
����0�\�"��ij_�(6����akY���aYFH��+�R3q�F&���8�8��g�;.������U��K�^���.�#���X]}��P����R��4���m������)/�������7�I��6����#���iKW*��<�x�a"��n_�0��2�GhW���*{�m@*��U=S5)��2�!q�=���j^��!.o����=�^Q.mX}y}����a�u��Y:856����t`[�'�����e}}���Azz�6�����Lv,�[Fch�_	��-��O"W��5g�	.��Y_]��	��E| �i�0��G��0l,y�Rt�g�ItZ��8����c+v��������P��U;�O��g����4����{pe��`�t�^��uAm�F���Kt���l��v���/��
6~�����'����I}��� hb�q4m~�m�#���������
�FQ*�:x0�[�t�\���������I<J���H��so�w8�n���k��<��b��>%5
c���mUK%���a���-a�R(6���C�G`^�(E�%�@Z��.��iAS)�8��IYb[�
G����Dk�v5m�Am5�
�x��b�����������MQ3�"cyKm��/�Zy���it��I�m�z��j��� �U��&{��/s�d,����B+M.�^���].�O�^����
�d{0*���y]��
�����o	�}�%~�~��� ��m��A�[��+��.�i���<B�-mO��N�P��3��b.�0�.�<C��]�~A�
�y��75�KYnD�l!sM�~�1���]L�'����!��
2>������6J�~5xsnx���3�59���50��Fk�-A�p��;�=G�$v�f�$6��������������P��7Zc
k��a{?��0��YM������5 #�V�R��gO=+V?�8S�bz��saS�0l����,�	e�z�0�v���
j�hdr�N��f�nLOi+��Ql�#���o��ht=`M���t���@�r�H3��Q-���id��P��we���WU��0��R�@c2���:�#�0�����z�t��Y*���7vV�t:G��mZ]����Y�\���d��~��n��@h�����B�y�<�<8�*�6cn�� ��$����u���pj�-�F1M�I���-p'q��~6�|�Q}�X-4~���!�b3Vi��(`j��B��$iH��N�S-,��z
��w#��6�(u�t�����
H�b%B��xS�F��	�{
1Dk�z�5g4����_q�i�k�L�NR��&���R����C���7a����KTO8�j1c�:e�al����1n�ul���zO��xN��|k`h���7��F�4�	�'-_|�V�LG�DCJ$�d��>��q�~�c�'4���/3��`��&�U(PxlM��!���9�/��A�%^���i�����{��S�R�" �	�f�����S��j����I6��rRU�mi����~7��v�K�-JB��������%W?�p����	�����&^��+R~�8��Qq1�o�����"����|�������8,��.�K����E�E�o)��q��eh��d�f��v`�%�c����B6]
L=����?�`�
��$�k�����_4�E����g�z/B�����1��Yn�]�^��-�n�N�����x�3�#���s��>n5\jm�F���3���r�E1��wP�/������(`d�@>J���M
��9(Vo�2��r�����U���E5��DJ���`6KK���B
`a���:;��j��o��{�Q�����t�F,�_��U2v��^y��1��iO[��}D��^�$�Jj�>�LU��L���<�G��������ccL�����E6g�	������M�Wz�rX�����S_D`�!�,����<VcH��S��#7�0E�%z,U�mDB�x�<�d?"�;��T��e@�7L�������,J�,���|*��Db���!2�*�������f��p���-��Mt��� n�	�7K ���� ��$'�Gv����%b2�U�4��3H�i�j���I����M����h��J���t��b����Q\2<�wH����#F�4����Y����T�1��)?ga:��q����������^�nu#r���56�_��w!�;b[5����l�x=Y:R���tg��sp�2Ng3C�\������B��*��=s}��j-(�����y?���/�PD���*6�wF�Bz4�i|�z�?��u�S���a���n�FN���Z$`�We�
FY6kZD�	_<��^�~��'�� N,�5�-W��w�~�������|����8���?I]��Z������5��1I�F�FVk4%����s��������f���5�-�
�{%u<R��*�+�#�`TCt���
�w3���>+��
�fR��B�v�o�wh(�^��a��h6���6�����E�y6�e
w�bj�:Q62�������	#0���'�i�����q�fbn#�I�y51JEfo1��<x���ZapW�L<�v^�]4��G
3z�f��ai�����m�?s�K��������9M& �H��}�r�_'��������1��F0�N]��d�U���R�,������,k0�>��?����Ji�����Lm��]�
dQ	��9m �V�|����4�Eq��f���!��N�!`����l�X�A��%�h,�U�C�"A���9cb1��|��EG��uZ��A�V�&:���}<|9�X���5W�U{���16�z�>���Hn�o[8y�C�'��:�'�
�{R3�:�j��#on���G��bl�;��R
-QE��,��F��	Bw��A'W��0
[����^e�d3I^��pe54q
��@R�����&:�ij2E���'
��=�7�^�',��~dH6�_���0YB��%<���
�+&�Q&pA"�GD���A��S����l&�f`�v#�_���lq/W�A��O��_�X�+����cy'�p�v�RrtK4B��l���5�:��E��R�o�|��&J�_TDpFS&��-��f!��������5�c4�ETlf�p2���P\4����~H!fs�o�1��^A89����*�e�����6P�t��J0o����(��F�H��;r{����5_2"�b�����O�9t�Ei	O��03�5�1EJ+����Ss�����
|���'x����S=x���{^�F>�K�#���R�	�fi����D����C�7[�����[[3�:���x���V)�%���+F,�@�He�in�2�p�EO=�ba����o�o7�n�2�S,l���2&�Wl�@��n��@>���Q�N�y8x�AH~�X}^��)L?0�����ByF�NA���
.M���~�b*��Igm~�0WT<6WYj=�]��c�=.�q4���0���p9�a�a>6<1B�0-���/-�:+786�(~��3�BTW�r1�GR�����>&��2�60
�,Z����80j�����r�����f+ Hg�xs_,B�DE6��7�xd�"�������v��g��}�>�#d���b��A�/�����b��`�wt�{�'O$��"�N	���m���]�5�K��^�;:=?��n�b���`I�S������!{�R{/��0!�w���i��j0�n��h�dD�
<���`��@���� �|��'���^
c���������?R�,�����"Z�=;����K.qV4T�q����/��;����r�����(��F%(�FfI Hc�|�SW�
�p-��e:�d(��h�:�&Oi����������Fg*X	�@��HQ�0��0uf%����~�/��N`�����EA+�TX��B3��YE� ����EYQ6���:�RK2S4����?BU���s��h�[��>�5����"zI��J��x��g��_?�_#/���g&@�>��bQ3e���PY}+f����F��y���Y��58�8�?��O����(����K����^|�Nrvp���������d�``�}�{rpr�S��t]�������x���&ak�Ss��H��g���o����6b�����f\^�����iq`��.1�w'9?2C�}�78<9���Z��Hn��@zPv��:�b����k��K�bF������88~���W�������c��5��2�,**aA��&���e>�8N�_��~��{���	�k��p0g�
����t1/&������-C�*����d3�##3�
�a�4+�K��b`��/u4��J/�
�g��P����6��N��d6N��6e}F�����MO�������:6YQ��n��A2�NGosI"�/��/JR������.�q����1������Q�V&'�~ ���on���e�@b��L��N��B���x�.��=�3���8�z��8ln�'�����������E�_&��%lE�z��
�&;V������ �[4�(�g?m�����Iw��E��������{����nRl^��e����V:dGs�V?i���z�q��q��n�Y�q������%��=5Y��v��2��)�]��!;�*�6���m��z!Xb~
���"�?���������4��.fQ����;-�I
D�q�����F��Z�R��L���jK����?_�
X	5Y_�m8�����t�����Mrz����O���~8�t*���s�:V�"���K���,�cz�����'m��c�-R@oa	�F�Hg�(�1.�y���n�����P*"���?{m�$��5Ml�����{�-��SY	o��?���(_q�n#�����\�.S���K�oo~\z�o&�:�� �C��sR(����c��w����?j�����t�����WI\�>���<�[}����j�������n_��4��A�N�D���,6���gm}��c^�R�G�|��(�@��@�,E�V}�#9O0��*�8	\;67z3	:�t�d�/)��Z�fE9w&
�*e�F��l�s�������6K���(,�Sl��rg��D
k�Nqg������m�L�1v� Mq�	C�t�Yx>�M����B�W"��>��[:��?K,���\�<����S}��FC3h��������'���e]aVR��D�L���ZBs���6'�O@{�{o��9��S3�'��������B��-��M�b��5~6��1d"����Z���-\,�jQ�y,�u�h�f�bU��8��������>���-$�8��E+�r�p���*�9*�h�-KCm[m` ���I����
o.��;[�r�j�>��U�#5���E_�e�<M0�J���r
��#�3�G�7���(UU�����@') ��C�r����D��_�*�_�M�:��m��c���f����r~'���+:���AW���.��x5]���tQ�-��-�6��V��H����lg"�_�(��B��2|{��9=��4(q�w�7��l�1b�V����*7�'��H�YQ^����.�d�t�-'k��vCm�W[0"�����?�����1���h���uD���a���>e�-��=�|q6,L��g�?!]�;�+,1�����B!]���*���~<:}E>���woN���g�;���(��5�Mq��.���wg��Ex�I�B�vk�z������ �&t f��ejD�y
�5�������JB�J+�i
)�l���X;���Z���j�M4�����L�`�f���t���$rK:a�������)������S��$|-�p"1m5Y
��\�J� ��+(���y}�1�%)<���5�����"kd3#����Y
yj
qiQ�������#����)~��Y4^�����w>gFjB�N�v��z�����	@b{��^�����1@ae'G������M�. ���B��0������0��Q��j��I��Q.�b�rw)u�T4B�Q8�uN��T��[[��OI�R��f���5w	����������o�]Ap�r:vb�qiY���"21�tz0P�W���m�<bp�d���
2��`����������VS8C���
�&��A�j�������#�o���H�$�������4�,�
y��|��@_A����t���z6-�	;,|�dF�	��;�����-�2�����Z�������7�?�w{�Xl������������+�T3��E�!�?��n�F�a5r������34g��*����a=�x"y���LM+2�]r��Hb��_�n�*�5�1�OL2�	�Lx(Bd!�Q�d�Xr�M8;D2 (��)t����0�����lP:��vE�����2�|������i���VL�McdFAM�J�/taK/v�{�z@1b���9�������\��"����Ms��s��, M����~���G�U
Vw�Bx#
� O$�T�U&`�+�[CX)�����w����b�����B����Z�0
�H�(+n������a�}��S(��Ux���V(�I�<�.��}@`����<#��-�j�I~p����e�C�%g C?��)����[1�KX?G�c��|R��{a�.���bt�����</x����"'���Z���N��j���O�{�V��(T]�G?�Jn���0��J�~���$;��j�(l
3q�6l4O������qF�c�����x��\��d��s4�PH��J|������W�[���t���cjNn �	1_�\R< ����N�?�jTp/E�J���R���mP�:�$����v���,L�!M�	�/ &)���a1���<Y"=�W�5_g��:������tL}q�\:������t^_����t<���7|�����{�]��m�b�&�R8u��x�_�k�J���<9�:��������C)G�v<�����]��0���X%��������Z����xo�=�-��>�`i4�b�d>�A]]�C�jd>������,>����1�D6Q+s�eG��B���Ot����N,���w9�"�E��84��tdb�9���+!6HbEyuA6�s�gu>�&��Rakm+��A���L�5q���'9�B���I��y1 �h'
����d�����mmz�_�^,H�$%0*e���T�O�j*E�&U����o�M?���b;�����oR��3��@�B�'~S^����gQyf5��(�	���Z�&��fO���g)��/y�������^� �Q"���a��
����$�
��(q�#�+��)
<��Ei������=�������Vyf{�Y��X1��1��+�#�C�bmG�sx�A�o�L����<dh������^m[�/��T�>������������ 7�
-tXA.[r�	7IM��u���m�] b(�+����)�N@:�Q���lL\JA�J��~��T�t�p)A��.�2��[C�dP�������L�Q�oG/;�������J���G�g���l6�#�j��,��%�f�$o��ujT�U�`gm��P
D��m����L�4�Rq8	>3��3���{|�0��@���K�"���:l�����j��h�J2����~�P[`&[\	�s/���r�v];ke�*�l��F{T�>��^���sK���y��ZbAt0�1���m�&�D��fk����IN�S,5�"3���]��g�2:�1	HndP�:M�++������3�<����Ta����<n����S�����R�Fekm��LW~%�A�B����NQ��F]���~��N!���@V��-y$����!��#��_��@��/Maqc��)sL}P���'��S�����*#�s?-���N�@$�� �d�����idyb��.(���{�;FL��pz&��-wz�����v���O8��q����Db��bGb�����M�������,�dA������Ac�q>��8T�%��6v��[��]��@���8F0SV�Y_w�
+E�7���['���B���I�s�s����SZ!�Q��&�c�*A�����3���j���pN%��rs"�<��q9��:AG���� ?��ll&0�"7y%rIm"$V�	V�" �Q��<9�H�o:Ey����SG�)���J3+]+f
&�'�4�����[{S����z�T"1�FX=�,N���sG��!hKk*F���}0�b������ePjWib�r�Q��}�J��cfUs���T]��:mV��_������aH�������rn/<F�R��x$A�DkT�����x���Q���l^,��s	��C��B�/�����LK��pe���/$z������~���r�	f���#�<+j���n.�	At���r��4��!M��-����f����\�����X�EL���_
H�����:����`�����"��l#���\��2��PdQ�����x�5������k�#�77^,F{ T��h�1���Wy0e�Qz[M!����e��	��$��lP����eh�������Ns�:��{�Y��������h"�$G�MC�
z*�J�����!4E��I��I�}dd��p���#��Y����9��|wc��]@��	����|���}�w��hM���^�I�[���/ce����p�A�4pb>�4���� ���L�d��������0p5��*������U��}�'�E�j�e�E�����T�0�c��8��F��{�p�fs:��	aj0����A��]k�
�I29f�y-_���_�a�M�����o����+��"�O�����o�Y�����,I�2�������Th1AT�Q�X5 +�0|6�|��6����p��Z�C/����#4���\��$!6k^��Ua���+P�<�BO�	���A��[#U)iI�����T�j��	�'sO��A��k�b��_�XP�+R��{*��&�~�3?r�6�)X���y����Z)��N�k	E�`�Da!���r���.5����LA��NMg]���)G	��� X-�� 
N�,���8�g,���Y�(q.��MLM<F�-8������lZN�Vl:��M�|������`�F�v����U���(�LVWLLU/��(&@b���P�5M\(�F8�����@E@��D<���J���1���;��b���X��H����w�{�k(p��I��
���<D������+[�_��|F����S���a���?XuB�v���[�_����/M�?�z��P�����+��f	N��Ere��������#�����R��}\���;q���Z�aX�)����5��~���U(��(M���h��������<WS�H��
�����[EV�0�Z��k(��N�d�'�q�a/����1x�����0���Of(%�����g�O�k�L��4���`��3�j����jMW��k{iy��%�DYm����s��6�E>�}[6�!�	*�K���%�yc]Y�����+�^�1C��\�oSG�j�l5���?UN�ZOo�<�^���f���#��v�]�\��c�J����~�c�a}�az|h�tSO�<~��m]\6J-���&!�37U�a��]�]�I]C�Q!�S�%t�T���J��Ir������=j{��������#��x��k���w���t��m�k?��}�y0������|r�Fj����N����i+����5p��3�����q����������$�H|^�C�B��A����r��c"�������m�u�m�C[5��s��B��\",�1#��w/�v�uo;������������S}���n��l��5B`�5�PL��L:���~��L�pB=����
�/rLK���i��/���A��lfh�Y�3I?�B��f�7.�g?�����w6��v���x�~!+��
��5���0��������O_k�.�����!�g���s)u���A�$&W����i����@�<�?��	 ����������3(+B8���z.x���Cg���n�����oN������]�]5EmE�S��]4�q����(^�U�>i����]@�	s64��9p��T's�����1M�d��q��U<����?b.�K�B������F��E:��yj#�=���M�4-4�N�g��G���L���/��j=���� L�g��zU�j����re�V8�����&��[4x�|G���`6�MA�P��s�,��x���9���,���?}i�9j�*�b��r`H�������08�}�B��@`T9�������__�$��X+���[�3�}����
r��c�Z���uO}��@5@����	���U��Q��agCW�9��	�`7s
%tQ�La�/��K��������"0a�i�p��UA $q���G�x�
jZ40�|���17R���>�Q0���Vx�J
��� ����Z��&�f.�������K"L(q>��[�Q=<���/�D��!�1��R'_�[�sf����0�5��>�$����2.���8���\[��KJ/�H jw3��n8��F�����?,�����T����0�����~D��.v8���
��E��XO������o��-�T�pxt�e=z������^<�`�d�0�X?:�x�H�&�A��%�}g�m��<�A����0v���
u�g���ZjPjQE��O��l�e��8��n^����Dq�������q-�A��t#�����$����Haa��UD0K�vR�R#x^��J��i53�U	2��uN=�_'K>�TPE����H��>�I�����u>M.�����*��#�h���XfF7�$Q�\�;���}���'������/N���j���������d������I�te7]i]����'���?�&����M��;'��
"nAN�m[mt��(�X|����%V��u���P�8.$����d$��������P;/Pnt�ZSx]��RZ�w$(���
�G�r���Ka� j6�k.
 ���3E����LiD�~����(@qlT�������B)9Ze
���Z2`���ty�
�q�r������zD#Ge�_�H�
�}1���#(�-3���=�`������.r�h�8&���m����g1�}�`�3��9:O�@k���U����U �J��	��v����E�C�>�#��3�kt��S��u]�qN�c�������\�t�����b��5�	���*��Jv�EI1�$�Zi+�!:�X+�����7���x��h�h_��iY���#�&e+E3}a
MZ�ei#��PP"��W�A����h��(�5���7�=��}�I�{���9��M"O���!��pB��.%����[M^�*�k,Q�xw���g�����������-�h�Q9�yW����Y����u�	��������mN�1����(i��nh/L(�� G�x)�p�P?���)�1Zj'�?V^e!���|j5WE7`d@[��Q^+H����~�f�-�{\�����>,NY�[Y�olX��Wf^gMO��[�x<���s���&
N���(�w;�j{Ab�~��M���4>�����z��o����N���=a��Ec��z���VB���"R4�*���@Z"��"���+���qf��Z���n3�0�q���{q����2_���7����D�[.��Jx�8�bx�/y����|%��Yz�#�P���)��l��(��"C������ff%\6��p������Q��s��]�����2�p�-����/����D��o����L�������(�<���1E���Y{����F�x����{�B��z@�p_-��������8�KE�%����^����h
���fgq����=L�M�H��3��d��(fz��������N%	��p��^���0�?�X����������l��)}%A��Z�(�ah.!��bpq�{r��wqxz�Q�+���f�<��Q��[�xCu6�i�n}H	XT��uX���}?H	�[��~x���{�1������������{�n��9dY@0����������x�>\�Y_���^����1 k
?�2S\	u�`��a�3��j���J~�%���[����L-���k��\����F�#Z�A����E�u}���Ak��(��� �p�HbP��El�U�ah������-D����������4��������
aX��-q�c�ZB=xpt1�*3��W�h�����8F�JTF�*T�4$�q�i�����"�HF������!I��A�=<�mE����_��@l���%|��l��@��b?M������b�a�&����gu��(�}6,��������#J���*����
RGu��;jM�ZE+&P�3�t�8�����$�RH������F���4�3�0����0�����������nO~������K�	�M��W�����m9��dr�y0m�{��HI}^�O�y6W�	�]N�+��&������:�(�#�DVW��v��F�#5�
>Sc$���!dL�mR��hL�$�G�s����D�?��l�ld����=�}ss�E�$(��
��l
��D�D��?-[S�+��S@��c4��i^�g�=���-6����J���8Lx2}�
���Z@����B�����$
��=9�3����U�A�_���H��T6�Y�FT��u����U�p�S5�4�/�]����1���5�}
�S5'E�F���.�����m���+P={	s��d��W#C*V�*1��\?�����*N6/(:���X �m��*Q�WRzY���e�������B��X������������S�����#e�����2�	vG��j&���T�e$��H�+Z��b8������1�1)�f��Z]x���������C�G?
���n����m��`��6�J}H�i��{��s���7�1�h�}�:���u\�*ez����l\+Y~Hf
;� ������X�A�<�m�Z�����
�*��.SI�f��u��j*�VnLIr�8s��G�w����eQ�h�{�s�%,J�M��&�/�XA,��7�@S*�?4guQ�N������s��;NAV?R.x��:�FM'�dx� :�{����a���C����P�1��>iJ�:L1���99Z������R�YB	�#���^���`�2H�J�\�����V�M�����u�[���������������R�(U0��n)��3���~�<3�v���&5U�`EWR�H`����X��`V2�8I����z�
��i�"J���f�6�aQ�>dO���
�C��I�*L}u�}���(���&���c�f���Ab	�gQ6�K��p�T���pU�T�I���s3�lRn�\�QFY�:�E+0ZA������5�6L��d�@���o�}6��V?���J�[3������{���$�-����p�|D#E�L�>���-��
��CJ�m>�d31���.�&C�\&5t�T����5�\��N�F�^���q$���UvK����]eO��0����H��K,�)�a�r�Y�5�k��:SZ�9��jqt�E�xU,}���D�{Y3�L��������Z��$t����Qp������T�����(���k����*������K��������P��������u)F�
��\���>Yn����
�@��fr>��������U,��Db�H��Ne�c��Ubp��#[���Ta@�f�qBTs]��]����qY8Y�c���
K��is�������b����wT�<jxY�������c�������������]����h[����j'��S�������c��(�/�0K��
<��c��`�>Hv����q���AD�(rQ��d?�jt�4��m[��������9����P ����M��PF(8��2�"Uco�]���z���h�����%��?4��g:��5Z�j���_[��Bi9(�8n��p��	��(�E���K���LEW`;����6�
�A.l���{h:����7����b6T�TeMe P?��%�G[�E:N���B�?��@�q�����
�0h��0��<J���en�E>����l��6 ��6(����YZn.!���Qs\�U9<��`��.����J��Hi������q:��u*=�����W�{�"j��PG��!'<t=��hn�;o��D.�[U�b������Q*�u�����e4�]Y�5/`�H'�a)`<��Q����oK�a��-�Ap�q~
10h���������c�e�n�=,���KI�b��6�ES:��y��\O�:2���,�U1��b����5`�bX�K�R���0
"{7$1F�F�]�6G����d[Y�B�M>�b�^�fd��5���)i����F
�V~�5�,�N���W�oM����Sm���Ttkx���(b���\
,�s��l/�n1Eus�3x��H�N
��\�S��J�i"G% 1�M^�[���
D�z�!�\<7��4���"��QUx�:�E�0<��h,����_�)�2��"-��7�m��9--!b$�.}�L���U�d�O&����;Fu�J0(.K��Pv�����������F�]�BbR�a��+�2��H�y�a�.;��D��i8V���	�7M���yl=# PU��jM�e$��BW`0n`'������BI��h��/������}��}�.��&�����j��3[���#"A]�����zR���v�:]R�Y0�f�6�#��U�E6M �jl��< �����N&��V��B�,q�I�7/)�|_{q��|m����a���qk���S������T�<,�����:Y�%�����������+����^��}��8xT[�������2f����''��^:C��pT��zd6��R�H;�����J����y��@9�b�cE�o�
l���EV�fe9���������!�������8�Hz�����=su���*������Z%A&C��P�]$H���\���?�m�HK=�1����eQ��@�Cu�k��x�h
I���e���*DN28�bA���k!��~G��A����3���'uv��k�3��c����&}D�	��H]]�����������W�q������}QZ��T���2����A��������yw�����^�M
58��^2DE7yZy������?��Q��� �I���i�Q3$v��9�Wx��[�6t������=[���h#W���e|�����%;,��^��m.5�Qp�|J��F����l
j��S��[�*�������%Q��^`�����J9LV��KxP��9!H/��o[�P���}���l�g�~��r����
?Y=����(�/�3�������R��u�d��0<bC����7��b��E�f� XwE�������kE)� S������F�BHrh�h_���!n�P�V�54�b�M�	u)0|����P2^�������U6w�"i�����)r`7���|��|�Ca�GK���s��,���/ue��f��������1�a����
'�H�
���������������{S�����*N��aG��B�I��P�1n��y�-�R}[�b5QVS�af'�ins>ky����F���[�Fh��?.�w�����O���K�O\PV<,u��Z����KX4�u����16e�H��Y��o�����N�'I+����*�&����b2�2\��;��t���M�N)��zY���c�_1}-�
o[q���#,��N��	�m��zM�6������fC����]�7�0M� ��,�v����Y�Y���w]{{����K.�����V=kL��t��S0���{����|2�����@W�#�vl#�!�@8���^Of���v�5��������>��7���������DZf��z��U�a�4�L�"�8N��U�Z]���e��Gh���|�&yU�������|�����|1���-��KH�Q2"�|�[2�{� z,J#0LG�X���������
�����o���KZ��������V��H�C{���#�� o�lCf��P��+?���oMG0��VEj���%���0�|}�ho4���_�mpQ�D`��p�F���3��'����47rbC����E�b�H>�F�{���}�%u���cSr�HN�T��uW�A|Jab�`�l����7��vzO~�~&����um��~������G]���yq��8VYm�4�^3\�ba�HZ�e�W��%���(s3;�T��jF�{��}U7.i�MLvd�@��o��E�a��c!���"> ��G�����~VD���atZq��Ps����~����Z��O\�~=
�^��(����A�4��(�S�k���~3�6$A5J���`)La:iyvj{�����_���Z�kRDF���=�����%EI��k��\._�V2�7D�%��S[f'��q���3�K��]���RT�[�� 
�����imD���j���:-+�\+���~�V'����D=+V�H=wG�y�H���m�CbD���r�����������^�����]�����K�a�(����<0���U!���	{\.5-��~�|�[��I��Y������lt���%�}�m���;/�E��\����H*r���/m8y���&�c�
��'�����n�Q:��Z���~�]%����`:/�����&��x��r�>p����D�[�}����S�^��#n�Z.��sLH2������ll����yf^W������s%C���A����7#
��Y
GA�RP��h�0�e<���[�H���z��0�$���WY����Bl��o;3s���L���A����P�xy�Z.����dM�[*@r�-cr��������i6Hp���������}}�I������T���Gh���v�yw~�O~��zI������=����4-�)Fw/{~���S3�^�����������A�����)7j�b�%����3�t�7�tlZ���:����u���BYY���7��^�r���nA��8�P�d:'�w�Yy\+�]e*����}��K��+����H���u��TI��LK�=J��|1��6"0�~�V�C0�a���B�	7��g�y���.�LR.&��gh�)~q�z�tWE-&f!a!w�^��I9�t��bl�����d���m�w�����
;��q������u7{�h���1���
f?�}��I���!xH��C�W���&���B��9�W�� �(�
�JH��}P���irg`��N6H��M��a�F0e���wI!RJ�1C����%	��9BV�L�"����5�l�����X����sv��9�:������C������{�-���d�5�~������pDq����H0L�����rm�V!� ��OK�{2HY�i[5J�:n%h0����2�b��D=L�'7�R�B'-7	S�D��<�B���#����dJB�pkI�+9;�����5���L�����gEO�x28Il�/`�R}�u��'�-z��H�(��s�����Qt�n�?��������b'� ��Z��"�%�QI�,c�N�h��B]�3�x���6��O��G���E��{j�w*�
'a������mv�6�v#��_A
%���#P�$-8V�\�������S������n�U;6�W���nfE�#��Cb�U�k,�rnpbS8{7���
Nk��5�DA�����}���`�����@�����68�����8�������D�+���x���cy�[��H:0�����9�%�������y�k	������8�5�Kl��]�����W�Y���{C�#������V0m���ce'�G�U�~�8>�����������g�T����u"yR,VB��evW�7������BR��g'�B*z� (r��
�&\?pb�"���1�
���D��:;x�H����o��6E�|r�'MN)�/7�����J1R.2������$����t��t3�b#"�@\�d1!�@7��6�P\�%��,'���0Y��@����2:�*��=o24N�5
��4�MU\�sB���� ��n�U����~Y*�E��A�-�����V���t�#�|���U�k��^��M��+�i���HCG��a�P|V��\a�����T��WK��~��5a^��BX�0�����W|@������P��EG��3��
���>d�d��2���W�3E�����!}�ce���M�	��*���nG�goik��~�{�*��a��p�jX�B�xH���(�?�U��e����C�a	�
��b���4��C���=l]���?��I������Y[��P���g�M�����z-	��6���,��.�3�j�P�!��<F��C�����C���k~w���j�5R��G�#\kS�[z��n�vnz��:~���������j� ���l���nD!&K�������h�� `�(����I��Y��)�+~��p����s�����|��e����^I?�yw6����
z�&����g,�����H���I�v(��@���r��.'�����H�`7`�-��ooJ���b�1x��"�ui�22�	�/(<���<��3�)CP�F�[��G]�	(�d`��a�f��M�����n�&���"��K��ui��e;���5)�
^��{G�n����].J�!�[~��>�|��,&/���u78��W���5���)����M���]e����f���M���H	���x^��|�WL&���9m�1�m%�<���DM�D���,���0%���!��zU	�V=�!zW^i��x0�������|�<����)C;�B>I�`��ofF!avfzZJhG��;�
S��w\c��$�6����N.6p5��,���a����8�`�7|�*���D�Ms@
n��������~�*I���A#V$y�B	z���R\K��F?-n�rMk�v����h1����`��=/L��K��o�����Yb��hf�j5U�`6J%�a\����N�.��{�O��kf{l
D\��h%_ $���s���0l.�6ZF>GI�����x��=F�An�]"�3�*v���O��������O�_�5��5�����<���
�����-�������FunX%e~|�8������f�A�#��1N<�>�SWx?#�):+�Z`�5jq <sU�cV�#��� ��|��1U~D��RqY�
#�xj%��*����{��2�Sa��DRjI�
�������O]}�����Iw-
���K���]�P9sl���N,�(�$�<�����C6
i[J��J��2Z)���0�2���|�)?a��!�������p��H:��������9�
�.�l��yv��1��4H	��9bAM56���>�����9�X���
����2��
����[7-{"�c�V��E�����\B{B��\��9���_�WW���5����>�6���t����>��f��0��W�5����tM�<�������(�{��������F�/�L� ���({�lj������~�/I6��[������N��`E�����~�<�$X^���D��[#������f�/}�9�����6=9	#_��jh��(B���"�^���\��G�����S���]�����L�o���a�+h�D�r�;F}�3�J�����	�n���@5�b���z�2��HWA5��P�(�F���s}~
c#��p��\elD�!�O�q�#W���/UT�(��;��
�5
`��,�����r,��[R?h����zp����4��g���5mLE���I�4�sb��xm�pBX2�Y�j�>[�������QL�s�"�q?(R\,�M�BCB�zlY_:�����k�ft���������h���E�S������tX�����MM1�Q@�QZT�>]�t	J�?8?8;�=:��������#�����P�����c���b�5��$@l�M`�����W��Y����?" ����,Lu�z����8��h�o�O�L�>�	�����`��7ox���2?6[�U�����t���v�1�qp�{N���z�*����w��g�����O�
W����4���u�����x��|ks,���A�����SR��g�����M��uV�~���}u��k[6l�I�E�8����F�w����IV�E��t��|��5|�����ox��w��M���'J�O|�����p������<���)9%\�wO��s�^����.�\����5����&����Nq� 
?�.�Yo�L�/�+S��n���M����q�}�����_�9\��{k�,�����Y��q�n�%-:0��EG�"7�vcf���q�%/,���b��ZXX��Y�2��R��B�JZ(������k��������G_�.���K�TRz���>���L�E�.'����d��v���XY�mGo����rI�����;HE�}>d$S�=� ����{.�J�d�0�?�1�M�N�����(�lw�]�?��7�HlLPk�5;�wp�0ePX7hLC2S�������C��R���pQfi���8�Q�x6�B�<�����Rd��LTZZ*��5��|"�:�1����uZ��0GU�d���E��)��F�=rAp�p��;g�W��=����OHL��`����djT���y���}Z%z,�-�I�6��+�"+p+Z���[�]�5&�~zG�8�1B��A��9
��3(_7������A�`a��X?�(�E��w�M�H�a@9��x�=W4���w������K
�B:�v0i`PO�iD�"�0���DZNY0-d���/��0/pL�����O`�B}�\�l>���g�F�.�/LVbB?��S+����������A5�S�U���9G3��b�W�r�h{�Z���y���X!������
Y�X��!��^�}Td�fkf=L
(=����6���7�A7��EI��g��U]#�=���/6>�z�e��@K���%���e:N���b��UY,fKz��6�����L�dm�9GR�K��!�i�.5'���?���`Fl60�U�������"��r�e�	�Q���1�as���4M��H��g3�L3')xe:��_'{>����wX�I
����kp9de��Y�g�l������;��0Zd��r����Q�9�pG�!�C�9���"���xKP�H-���I�v%�����{����"�e�0O3q1�0�$���Z(m���q������T�e�b8��a��3-���qf9�*N��x�N\\����S�L��N�L���1��������
C�(����E-��4�#�.����������ld�������t���&�3�t��	.x�!�e.��^�Y)��nV��XkF�rPg�\s��gP���s�H_V��pK��S�gEM�^�`�M�����;��+�������d��y��]���	##��5����U��"���E:|/`$e�pT�Y�k,�	!�s������D�zIn]�G�A%-o�TN�j1��H=���s*�����	������-&�3���l��R���D�����$J@v�^�f����~��Y���.�[ �]b��`�SyKw�M��U\���vR��<�Ck�^LJ�MX��g�V*�`�"���rJ�D!M�m2�;p��_��5�g�����=)Tb���`lM�������z��������l�!6�T��"�����1�S�cJ���W^�0����nH��E��������5�,&��'=���//G�����7G����:���?�[
3�<+��$�v��9����rqEX������}K�h5V���:�`���FQJ%�6�M�}��_q�/�+/(�����4��G����O��@9�`[������D��9���v��$�Wm�TTP��@�������<$����"H&��r��TAo���o�#�l&��;�$��O'���|����'O�f��`�g����	Do����EV�m'���7�$�0-h�^�.���,
�#Z���M�ziZ�� !k�����Q�����`-/���h[����c��8���|Q����0#���������3�������&A��{������-�]�
,�X������2L/��
rr�H�M&U=�=���>�M7�GV�����A�!��H�w>�x�^'	
��kp�u*���m���G���������^Q�471�v��>��T��������z��
7��u�@���P����]�HWG�����e$�_es�#P�������=9�
��z�5���a/�������M������]��b�Gdm�|",�j��_������9�~��>
R=�o/�J�B9r�4����w���p����e\?V���8�����>Z3����n�������S�P�����2.�n5��\�]���D_��M����l�
�z��N2���1��b6:I���i!�/#����$�`�j�i��|��q���EC�>$�
�sr�Or2kt��[���"3
���-7h��������m�{wl��%�������d��0�V�0��_Im1����4'�i#3A�a>MZU�0G���<��#��U�<r6tP2��Wez
�����L�PAAm��E��?�3vA9b^�R��B�*������e�?j������F�H����O����dl>��z��xY�4)ARS�co���<�������X�(w��z��%�0�|����<����O�@�9�V�!����}|~�i�
��L"����%)�+��S�J��g�8�e��?y���F����?E�v�!���j�_,!�mIh�=��/OAR��������q�[]����sf�m����DFFFFF�W(
{1�2fD�JF�K��F�Sw��KH^��<N�</��X�=�n���X+� �����"r�����a>��hRm�A��T�SgL�&#5�|MQs��d;�Z�feUR=�������,�������#�Q��r��M��2���4�\�3�>4�Q/ZK��Q_d�{�<�����x�z��$���[�Ia-:��	
pK��B��7��=Ih�"��<�i�N��,Pl�n��9�Kv�#�Q���}��)�>-c�H#�`�A���Q  �=8t���c���^8}��������M7��1�^R�?e����s�Qz�E���<�J���#��|�Z���p�b�Z�u��ZZ�C�zn�QsyZGR����.�)��(�	-n!�F%>�]����l6,
i4�e�h<1CQ��1k�VS0����+�����J}��p�~�����Lvs��7����������������pM������T����n�����k��U�\ld����c�p��/���'l��r��oq
����������s�I�%�J�T�GM:)X�D�jJ��� `�|��5$9��Y����:xR�,(f����Y�iw�gt��.��p�!���6�"�!�P7�]�]'�r%��\���r���J\�8�(����o�������F���K��;�^�����T.XE����>��#IT��.�|"��cN3���[�������,!�?NA��H��E�>�&sA�H'�2��m���\���S�|6bGzh���}^O+���8q��4X>���1�O�
���|-L�X�'��5��T�91�������?��5E��o�]W�Kn�Z<��"���y
'4�~�8��T��F��S	+�4u�La��$�)�9=D��(���as��H
r���byf5����P���)r+|wy�}��}�^�������`�m$�D��f��d�����`����2]����l6V�9���O]rk������wo�Ug��,����g�p~L�-U<��?8����%b��hlp2�7:s����G�X�{�\&���KH|�"-iU�B� �R)����tE�Y~��E9k�~!�F��t��?p.����9����|����4�i@���V��E�����d�FL��i�r^�?���	�f�w+���0F���2���	�p��O7�cOG���s��a[���H�����T�}��Q|�����`��sG��x��t�����N����R^��G�rv���A����W��R>�=z��{Z}{r\�^��F68
�wz�Td1��G/{���^�V�������U����{�P���\��P>������y������5�'>U�C��
%�	�)����rDd�UK}�Z�E�O'��HL���SS
�"��q�V�$}��!��aJ��\h�!t�)����-!�@"dU�A�}}L�F����
�;d��	���1��)O5��3���!MuZ&f��0,
x���������"5pL���)���@���K���u{�1�J��@������a�}��]v.�@~M�-�����
+���&���S��z�Rk��pQB���`EKr��U���,C���U�J��-�b����������\(h�IA�1�T$�U`��f>$w�`k���2
�g�Ly����������QN��y������v�lPp�u�R�*���}M$�
G����*�A��N3{���)}=���X[8���.��'Kglcg�=����L�8�[�:�~���RWC�xKa^�n�~*��%�M��U�3u���T7���4��������pKi�	�8�9N���z�����'��u�M7�&�xB�A�IQ�:�M�������1����#2���`���J��}65��L������8@��DU�M�0O�yN�g��6�����q��i����hM��m�JB�-�J��zE"!m�Y�`�
o�[YF�X�&�
�7��vL�)�3�
����'���&���0������?i�����8��������V-����#�:	j��.�nkhg
>��.����{�*����4Zz��W��G�3h��vl�!&#�C8Ev�r#W2V�9hY?\�K�q�a4�j]BA���� �C�/��4C)�2��::C<</
c{$@*���*%fv��
��$U��{,q��W2��[=��)c��EZ���:z��Y�q��iQ�������s���w �G�*�'[��T�4�i�_8d>�`H4�0�$Hc���� ��D�@���S:G[S=Z+o�)'�>�
�A��\g��`u}A�����Y��9O�:�:�}D�5(��60T��S>Nr��wU.K�������ex2N��Q�q�U���"�o����0H�M!�'@��??W@>�e�W���n!1�5�Z�@��	c`�A%��8P�����DwG)���|��t�(�������g�c��.�Q7��������M+�Ib q��z�Z&������-#�Q
T�'�Fl�!��@��G�[\���*�1�=���G�L�:5�8�H�u������2�
5���>c&>�87 	)���'��b�0<-�\�(3�4���B��b.1�#>eG/�H��[�slY��O���a�r��]���x��YY`0������J�X�,j��oQ�7*�J�v�-p��3�u&1����6�
	,v���1�T�[��6����~�U�z��7>��Z��z��V����������
��UO����F��P
�]V�nO��.~������}�R�d"E��c[���t�(%$�X�%6=a[�z8�W��#f��/���\���I��I@iOx��|���m�����k[�3���
�(�L����J������RF�����D
Q���\�z���p9X�L( �L�*s�8�Ch�M����xEY#<�
`E�O���r��*�������w	����J�M1h���e_��fOe����8
�"1~�n
������`�wu��
��in�f����z��IV"�RhbKg�hB�?��-B{)�QB�1K�6T\!�E4��*9[rfg�������u1^�)_�������wX-��y���I�t��A��ub��&D�@9C!��S����\PvG�xN��@!�-ae���R`���b�b�>�s �x��rZ�B�.�����4^B*�3;���I��^qy���E&��W����H�
zp���Xw��S���k�Q�4�:@��v�|�����t���I2���e
���<��^+��sT-\�Y�P���`��:����&'���f<mq��iY=dK��4���^<��@�
Bm���S�yd)�V|;����n6��a~-�+Z���P�'m��W"|�����P��i
����CN��Cw�y���Acw��6�GCrkkk�Vs0$��d�~P��;��o}����Z���>o�����b9�>�!q=�|<J��4�O�h�;��O�_�p����P�PD
���3���'�������#�����{,+���L������������Y�{�
_���j�)����,��N�S�$��EeZC
?j���/[S
�6���?��9����#3t���
���86&����X���2�3�G�[����vj���3�k�F���gr��a��r�-{{�+���9�dp�b�#��������"����id�,*[�K��o���rA��vpt;��.��8��zHP���/f�y"�[y�����2��(��\f?o89��������4��6dz�Vf��C�5<��y
�����OTB�v�1V�1rt�F@�8���	�EX�ex3��,o`�d�O	"I-��������3z��y/�zN>y�����AWM�9�m�����L�)��g<����c����.��
NY�m��0+����uB����3����:-u_��`xX�\�I�����N�3h�!3'c<��1���<)���(V���EvfJ�&}C9\�JA��1K���E�pW���x���%����%��2��f������^��Z\bg1�&��0���� M��?���>O�\F1@���t�y/+F
#�/+��g�fI���2���kX^tr
b��"JF����g�\^�z&a+*�BF�������Ft�$Y���aX.y2x(�t'��N>�#����!��1�GQ,8���S;�u��W�r?"P1"o���������SB�i{���A�������xor v��-7�����W
�Ssw��	�m����{�tn]�t������OBtu��-,&�}��:�a�W�`��Wo[}�H?o�����r%�P[A�%�1����|��	z��rB'�R��h����P��O~��v��B�c���V�	�P�@J���^�W����*��������w�x������$^�������c�gB��]U��t��<��UT������U!1��K��0�\��;���[q|������-|��� ��� ���5{�6X�P&@�.�Go<_�3����T�w&������j�qs|�lL�v��������(,J��Q�z���<�%�#�y�<��=S:��on�^��<X������B 8!}��������G��0B3��jLouIx�2l��|m4����
<���/�s��L�����)]���I�`����m�0��,W�`�'����#���d�A��6�������^�~��V����B�&.�o7j�}����,�L���A���EXlb�.�3���2�NIO��7�����`�<k�����h��A�y�A�������>?���W��B��5�T ��-���a������5Z����m�*�rmq�QY�N2�7'QP��L�A����� |������Z�g��TU[����lU��YO����\{t(N�����}<�3��9W�~At�K��r��0��
AD���(�l��y�cK��6(|UV�=�d��!n:�s�yW���*
2�K(�����}%��Le�^�=-�~x�����?��p�!V��C\��������N&;�G�G��A�pg�Q����m<�*Wo>���i�x�;8$>;�B��1��($�P:���yh&�3�Rz���`���i�E�}u��u���R����e��9����1h.����
q^D�]�~�3>)Cr�K����E�0<��G�!1��'Sx�S^E���rR�X+4��O�K���:��HA)��A�Y����������^c{�������b�<!��2���~��n^���f��i����(�!����:�G�=���`H��!�F������{��I���2
�q�J���U��d9��p��#��B��`�-[��
�P�{��pK�{eP��zMy�F�l�T������~�:�D,�no�V��!�:Y�Gt��Q8����f�L6p��aY��z��(H���f�o1�	�0��C���#��tc�
�c�"e+�6mp�U[�� ��wH.+BN�/��p�������I[�j��1����z�/�m����%�����?�8z�R��>��]����C���$6#��ob2��tN�PK��Ee��#P�'��:��������;�%���?�XF�^�Ve����������&fUN��3p�K���x!��G2C��(���6���60���?���]#�k�����x��C��>"9������� �������A�/q�PDW�����H�rq,I$=�Q�qI�}�-�K���~wv��
Zo�����^,����x����P�Z���U5����0���z>x��?��Db�?����tNiV��KX^h�|+�<K�S��l�|
;�=�.�[���^�q�y^��3�k�����`�-����M�(��8����P7��3��cZ�u��������?���r�k�����F�9�����7qQVy�'c�����`W��/���?(dH]@���F#��`�����s��x���8��b4
!���Z����Xzw��c�Va�u1[�����'��]�8��Jxe<g{<k����o��Z����'l�V����;�����;�S�����a�T�~,�l����?�n����`<�b�Y�c��n�E�1���
&����}"��|�����Qe[E^��������h��� u8h����|F>�v�������B\lOoaI.7�����h����4�Z������k�8���Eg����~����E�_S�5����+t������i����������k�������+A�$�6���*�-L��Q�|��
?�Npyy�9i
���N��t��+?�+��s.�Z��^T���:�����8�����\����[=�S>����YY�]n1�r�}	a�y�����S<-Sr�hR�!aE�O`bKj�����X�����d�8�K����������Jre�k1�.�n3��]gS���a�1��z3BEO��r�{xU�-��_�m�bl������<q����2?%����r&+o�?���`r�����n0���G��r������`���8�O�O����ho����_���������p����x�r�%��DV���O�E4B>E��
�%?�]cP0u����b��X�3��[%h�Q^�S��n��F�����Z5az�Eam�qMJ����z/����q�{���VMv�����u)���C],�=��j�@nX3/Du.�5fq3�m�J,�LxT����*v�A����^�7�-I�:���������@J9]������z5���C�b(J%�q�~w^9;�����%�L�F	�BoO��� l���w����W_s
��L�f)�;�%����AS�Js����X}~vS�T�/)��M����&�^���!�����F3����?`�?����K5<C3�&�d��~� ���k����ZA���/�.����=�
���?{1�Q`��U�:�Y.<��3��gF1d��m�����{|` 3�Y�E8J�(BO�"��0�h�'V-r�7jY��*2���.�F!��(Wpf��x
	2���tX�B��(��CS<���#��f��<V�S�9/"Qy�*)lTG�*R�������mK��@��e�;�(\@�T��K���ax���r���U��J"���:�'m��1,x�2�"��K�$���[��7D��L���G:��*��V2�����A�s+�#�h1��]�	�����P*9�R.�RO �j;h�faL{G��V��v������z��?m��i^�AQK�;����.HU��F�s���������U�B�/��DHO��H�sA`��cO0l�;���������LTF1����5����U">'�'��Q4��������Su�g#���&��j@/���17����@9E�^����n�~��A F������p^]�v�\��rL��{~�"���<����?g�&FWv��l� ����I:��5�L*��?�*�����rJ�����@,�q��58X�UF�����V}yL��[��Bm�~,F�|?D
aT�sC�+��(?-�����r������J��5���,���.O���0+��7�B	�.�v�.S������j���ep$�N��b���� O<��"������+����8.���7K�U!)�TCf�\ X���	��O��lT���U��.4����OR���_W����kR�oV��Bf�.��O/���8��"�pzN�=&��Iz�� G�U��IT��
���7*��0x`<�y�B2�1��Vy�����r/ia���~���F�U+k�*v�ya�G��:��� ����Vu�fO+�c]�`g�J���F��v��n5�sL�TOx	k��f(�1d�����/� ��mUvAmNe�rr+����'��`x�>k�;�GW?�9.���S��R'5��h��(Q�]�@�������l�AI1�2���j������f]�B�����^-�SA��D�#
y
+�@.�Di�.`{�Mf�@�vt���'-s"n��7�r����6u{9���:�5��HBC�����L�%��c(:8"���M�@�z��'�����3�:s���:p�9���2��A� ��b������s��z�P��N��<���y}^�!u�^�K�+Kw8{�EH�"�5�����<���(�C
�b��c3�7��E�s)O�)P�H�t�;?"5�@M<��07����T<Of����� �`+��q��d����p!��R5�>#�b�BT��H5�.c���a�4?B�����4q'N�(�`��o��iZ��H����J��.�T�S����Z1�V���H�\��
����[f>-9�����r�`��]���HV�S��L��q�e���U>�V����uV���*U����%�  ��?����1�C��H�!���l�#�ab��1���L���p~�*�y0�+�pc�#�H����xO[���������E;Z�s�0� �@������]^�cU�h+����c�5��R�M����U����m$�C(���1���$
�o�H2Y?��|�i`�&7��x!����A��
T�7xCx�6�q%�*V����)"���e�x��\-:�_�g�U,�!�m���U���/�c�*3cO@����;��.���>�82H+�2T��/�0�O�����Ei����V��<V�i��R�|�7	6���fyE�c<)��+��Yz�:��T��i#�\�i$D"��M���Z�~�Q8��
��.#�e�"��1#����Y@�&�+�X�Sy�,wL�X�,������Q�����4K�1f�`���[����2c�DT��:T��A����X�b�D�a�����5�}h���y	�m&kWwA�w?���������������^SW�k�|�E�#X
�Mfq���-ehz��o=#_���1�%��������Uk���d-f�O=�]��:�-�~��������c��P@�}�;}��p�7����V�/FR0�����l	�t�!�El{aG��y	��<C��M���y�c��T�W�^�����K�+���r���b!]'�)�z����W{)����������f�y��Z�����(���
U��e���_�}��z%m��Y�l���CL��D��{����`��!a$O9��2
�h�L������0|��YNr�D�v����������3M��\����"8��(���0;a�1�4��~��k8�$s�3H#4��\�����\^L�- ��h��*(����		��A4ETZ)��j��fb�$� k���-Q�A.�#��g�pc�u����Z_[\�����(���L�����|4&q���1�.U;k�zR]jY��T5)�	�����4bF��;���vBV��$ ����%����f(����ki@�Mb/#!�o��[�V�Z;"�v����"Ul�Y��4:��"	d&r����*���GL�5���f��-VKK�}���'�
��y��]P!�<��������M��>N����M�Y���a��MC�[�us��Ky
D��<?��97t�klt�Z��;V1 #!_K��[���I�U���+n��H����(/)�����������W�� >�{C�mk��;a`���K�"K]I�
�B���/4������"R���>�(����
+Q�8$4@�	cB������l�����X7��G�m`��������.��j�/"�Y)�������\p��%O=E^���[���x>J������������(9�p[T�c�t+�	���+U"f#XP������!d<^�0��J|�!�$�N�+��[\�Y"(�XzH^�V�
��Mc(��VBj����
��������l�f������@��=�s-$��{W�S��#���qN1��:�^9�y	�k�Y�A�����E���7����{���''AA6����I�Vw��m"1�yJ�$`;	k,��h���z[	U!t�������A531h��t6`�j�L���-�Z�)Y��l�u��a��Y�q��( �5�X��(>���|]WB<#���si�#h�D�}�����,����n�����L�RcJ����E�������2���7w�{���Vk6�����d�&����F)RB���za�.��*���}b�V�#S�����E(�,w�	.;#�I���p��.�|�J�&A���3|��|��������v���Uf�"t�5r���'[x�J�>Y�&B��qZ�F7����U]�V�����)����3,o~d8>���;��������������Q1�������r�����"�{�x�<9�c��;����PE Q&�H��f���97`+�F8��% ��Dg��,��U���t�8N��F������G��&2��j6�j`�)+y�cX�����5[u���L��{���8/�E��1~�1�G>"I�W�@��	����2+���%k�oT"���3�v&�+�y��"�6���7&��`_p�'N�p|�9�N��{'&!��� Ke��pCkH���n�5��X�a>���{�YE�]����6�K1�)����P���������"�`���P�`�NhYY�
��Ip=���sD}F�
�B��aE��B4	2N�m��K0�����96^B/T7*�pZ��_['��]������BlTx@�P�j��^�i���RL���y,J'����&N��&'Q��B��f6�����/��sl�h�+w��;�����rT��@A;�/1��VA�����:L�M���4����G���������4[	=�����]������f����\��$eI*'�9A��D����m��:B���;A��a����������]w�F �|��'>e^�D���D�R������
�Npl0N����q�����0g�;x��FdA�c\�4r=�lA��D�{��"�	]3t�y��yE��l���Q��k�j���Wo������e��"��*�j�.�?�_��������T�Qr-��]��O>�����B�M<��Ik03=�����/3�l�6�l�D���$f��7���Tzd��AdzX��3h��`M2���1..$_����Sr�s�Rv�����R�����*�|�x�z���hJk�������|���������d������ sdYU�%�~���n�2E�G�0���5	i������h�E����K�A���C���:F����|X�;�_��F]B�e�B��"6w�n|w>�!&�)?��+��,>+�n��	h��,@(A������(����=����4nU�]r�]%�RK�����<�����s���\Id�:(�%6k��W����e���{�5��]{:$��]{���pux� d�� 4<qX��D����J;)��9���;����V��G-A_����Zb�|I�B#�xJx���b[��d�"~��p��'0��w����kO���x�� ����e^4�u�Y�-�s����������`N�Q���-QC�3�U�{r�g�B\�����
�+�O$�����������������6��>V@[��C(�j�l�}Q�A4��!�S�O��n��5���$2PNm,�`��
���/�&%+�d
v/�J[h@@j�&/k��c7�$+&���Ee"��:������a�I���������/E�U�
D_~�l�!,����Yu;������eg�H������7���v���7>:��������s��*]�������������5�5�$Y���8���gJ=���x���L�^��*Ck��4����&������r��]�������H��n������o������!���5�+JI��Fc�����C1�����`2�;���U����$Z���~�V&�@os�'a��������-s�S8s�MyX*O6���L�B���Mk*N�>]9A�S*�c�����59�����?0��v��;J���Ze��Q�p�6_i\�ZF�I�f����!e�p��B��U����/  	��H�����I�Hj�KS�d�����L"�}�� ��1���|���\�<}��G�5N��I�h��3%��KE	�u�v)������R%jR�(���=A�o���$�����e�P�yV{�^v/��M'�������r]����T`F�/��'�z��__��K&�KLY��X�N�/L)�M����LB�oj����V��Tg(�,�u���'�%��bri����d5UN���I2=�d�l������w �
Lc �q�m������J�����'�~���_�;|�����<b�>^�;��NOp{M|���+�����q�PQ����6�Gw�V�U�"���}�`�[��;����p��3���Q2%����#��+��8�Xr��`����2Z��%�K�w��@n��e���7���Fg6k~�QA!"X�-��}���k����O�a�y����_(^���������/S��)}4�(|�����(b2TZ��S�w����H��=O��&;����y�z���<���ue���2�����e��RB�_Mq������D�7K�g'T�WH������pa�6���6�[\�y�o�%[ ^���3��F�Vy���{>�r�gE�;������y�Mn�_���
�EAxmB��:�v��	]fn"�_�e�������+�!��9b��������U���a�r���3]�<m���x�_�Q�Q���^=���+��O*:x���	������e��������"�e@��>8l�1��9=�]J�3mQeX;�R	�K��u����PY4�y"]��,��B1�p���m���'`UM��{��,Hn��j�Y*�Mu�z�1S5<g\E�X�w5(�#J���0���
	�*D��P��3�;�G�w"3����'L��]�V�i4���&bV�)X�'b_�1�C;��X�R�b�����0�0��H�~8�G]�4$|�Y��@�4,YH%^_�=�:���x������!���e�5����W,K����<���B�^`��z���r��&kW���dK�!��5!]v3A�J�����h'�V���f5��a0��[��w�i���������7���9��{����I
r�m�9�n3���m�1��,���p�
0��{y��*G��i��d���
���q���s��k$�F�S�*C%��������P4�}�NY�Z]��=���Q A��2�'�!�*ub��9Z
Q!�tM�,p����������X�k��<��@����/����2 ���	|��D`�I���]������PF���w�Ht~�=�]�T88��P���
i{������e7��]w�^o�G��^��_���m��F^n��zr��L�=���]�p���u�Ux�j���oT$n9���Jz�G�9���O�.�	������-I�0_��.��S����T���JY�&wU�����x����w�&��1*"�b���C����7����E��zC@�����0q�r~���I��������v�G���_���{�������m���dJd����t��
ac�O?���������;8��O�A�hTt�d�w��"I��+�y�7F�
�V��{�������6��B��H<�4��l��ST�C����|���N8���B�x��#����#<���'QC�����2���\2OF��+H���N�Cp�.�L�piJ=��_��#N\u�����7�_��$��Z����?G��qe�<�� 	q2�@`.� A���u���':��I�y�����/����T�,ZC�r�8����*|xuJ����������*�Y�������a�����\t.�V_=E����ZT��Z�����3
��C.QR��'J�^�x�$�j��Sn.����z���G�����+�@�C�H��lNB�~�]#dI�H��X�M�9��tA����%P��7�-�������3_������	�|���P��)��J�Uam�Cv��7��n�b��`��}��8|���no�k����W�f���FyS������N�D
5�]tL�����g�+aC0`������w����� A�;����!���������i���P�7�����@�`��Y�{�,�����s����m��r��E,*?^�n�}��C�P	/��,����p�Ho�llD4�F�����f)�`����H����
V��E�]�+^�����3(�1����$7HR~F�},�@qyHX[�U���*�.�����YD�����������j�����7ss���X)������{,�!���/E�W��=xT*]G��'�
>�|��*�jO^��E����%�X�F8 ����E�RO�bL�)� ��������E�/��N�FV�n�W��F�R���{���"%������/QWT^��a^]3��T����b�1F��F@�����O��x�b�R�����}�m�J7����4b���\_�����$
J�9��.:E1�C���N��l,~�^8�C"�!A�P��=��������p���on�|
��C'X��6�����:�����?�����?�G���C#�'s�z���{����2��+�M ������8�t�������tc�Ei����b�\�{�&������70�lI�p	����8#��a�!##��G�d��x)�7N�Z$�Q�b��;���Q��2�B���`�p�uT(���8k����r���0��)@(�"���v������q������5��Bs�(��=y Ql������X0����26
��))�s:MT�p-��z�b�3�.�.��nB��7�[���$8V1CV�S|�R�^��Z���o������B��h"���vwt8v=���	����^QS��Z�����{�sXxM������\
b��A���x��r�zxY����P#�Z�����!�f��
u*:�8��2D��{Ip�$?������ ����?����!��Z$�a�f���vegS���0i�g2�\V>����'�~>6?�������%������):�6�2{��/��w@}�S`Q�Z'B�S8L�7J�4��c���GC7���SIx��d�[�\F�}Zl���W���yF���c��M0��P.���3 ��!�%��4�4��?�}L�5�N?�[7o�������q�8oin�F����������i�O��t��:`�0��a);���h��;)_���N�t���&�p1���*��Y����i��T��x���x�1V�(	{���0���9�Z��	e0���>��9o)��P�-y;-��B?1B�w��Z���Sd)q>|���9�+���0��C�7U�?+���8x�yNw��G�o��PI���c�N��.��5]�����D���E�iV�\�Aj�X����%jX�h��~Ln����4T���.,��	E�����EAVH�d�Bg���-�Q�<��U�0?'�E^#�O�����+�X��O�^����P]^�R?i@>^��I3P�X�� U�p_��
�pz�,�j�N<��'dwM�����8���mb�����5�'"�/��d��P���;^�y&B�0��
�&G�/�M��I��jx�;�m��l	�O��a�d��g�����C���:��8��Se��z������>4?k��:������	+`�(��N4���\��z���(�&�[�H��X��U>�!��������#-���S���	VE��-v�
V�7��gjuj}�!�\��|i�hL�>1�����p0U������*��s���zE��/u�5��Pc�����*�����#qQi�7���-+tl��l���c���!�u]��8�>Z���:��b�?gw��!$�2���l?����8��1�eO������h�����x^:���f��,���b�H&$i�����H�	{�R
��~�a\�Z�����|��1�+y��`�[���cM+[��^����������D�~�F��QCbk3x�dO�*�u�s |Y��
"��fd�����Be��,B����7�X�De%`w�|�A�$���]�A~nTj�:���Uu��l�4��A!%�50
����A����H��!dd�]B[�;��b>��|�����2�xBE�r�|g�A��N��J�M�9v>�Ps53LK>Y�����I����W�����@�WFP|����4�bY��y1$� �S����|���M��_T���o/�g�g`��}�.���l��D���Y�hdyO#K�����������vNy&i�`.�q�gb:��Q� -����eT�R�`��O�R�����������Aj��(.M0���"Bh,�@���d��BB���\'�i��hq�a�L?��<aE�5�$������L�u>����PG�|[1���w96�6��Mp�{�1������<�M*���M���� ��G�x�
�R��|���N����`Ug����������a�Pf�A�0�n������������o]�9Ul�a�l�VE�2�$){���-P4�rT���r�����Kc�����Z�����2�
��0D,!Cqs����@"!����|,���,��;�,B��2&�����I���;�K~F}!�
�:��� �������$��&�N��N���m����(`�a�n"S��	w�vI7�k��T3��P���kJ��}����r\�F�k���-���:�����+Y|..��T m�*���7�b
?F[RK��ud(6lYq��p'"DD�!�&:��E��P�3nS!^&&k![�<"�A�a��,e$��BM��9��dQF����X�.�M����|km����l|M��h3W�����C�)�w�
����7*h�GD$Y7VG������(�V�F��w�|��4�cw�|����Lv���ma�����q?)K:>XS�J2��>v'���._|U,��k�/�d�y�!�ftG9�0|��XR��
L+�D�j��DBU)wPN(�����=����Q�6h<ebV�O����cJ4��sCP���`�B������F_�z�����|Xy��}�a�����r�s;��8M���������U�����n|
�B�B���L�b�����=�P�����P�r�48�<��!}�x���|*d���C}�'��HL;�j��<Nw��t�vyd�W�����
g�����x��y�5�1Uh���C95���i�Q�+)I���Nv���N�A���x��%Z��TwTO�:5�]:�+oa��!���PR���<k:������5����q��)�t�Bs�n!s�O�@����$�Z!�b\fR�"
�>��s6���Q�c�������h��Q�iw�T�++��+mSfg���a���sKr�����9j-n#��-y!s�����1R�Yu�����;����O�Df��y4	�k���#HmdJ�{�&h�V��M{�Se�6�nm��5_rJq�`��`��{����,5�+�&�t�B�q�/,�ux���k�	t����|��2L�iZ�������H����K�j�0����\�����J%@�����>�34�!�\�u���9�T�&�Pr��I���_������d���l�cN�I��yA�SpG��K1���9��e����EK��Q�	������&���^C�X���>�0��������.�k���:���ZPq�Cq��o��$��a����n5�n�6�$���S7��������S�jG��+����)�H�C�����e�j����S����Ek���>�B\��|�1�	�eVyKkh�
>�:�V�:rKM�������$�}��t=_�	x�N��&�P��[/����vG���{8���j�F�o��o���4���g`N�v>�h�z����5�����������`h��M�Q�^�'���z#���T}����b������7~���11F���m���+��r��������w�������Zm��&�=�����:SK!��p��.�Sg����Q��k~��c$���O������{��cH*zruuv�z�w^�t�N�z��A����SY� �%��!�m���$t�H����[�`4���'�9�bv��"�5� �X)Rd�2�	�m���s�&���?��S����P�����������M�q�;E�o>|�i���������5k�Sr��_�)���D�9B����������N�����nE���*�<��D�
���_��O(����'[\������p{M~����y�9�����!�/�~��w!�a�����q�p��'{���V�\��5F~1FDnM���*���)��B�'1���7e'/�-��x*/��(2�I�g$��%V��|i�Y^���9�;��sXm�4��SA�;��2���J����U���@�{8�C�`2�D��B~r�H��uUSMgK}�a9�G�����z9A��]:$H���R;��&��������{�����W� �M~����""��4����vK������ 0px��>�e��z�<��$��zX�b�)7Yu�8��y��'6����R&t!c�����X�z��"���M��8+?u���C��#H�v�j%�����!���(��@p>dE��,+�A
��*��5��z-_��Qg����NO�'o�]�R�_�>��y�
�e"��2�(5�7���E|F�����L��������U�M����e�����|����\���A�X�e��]�_?�c&������������o�d ��g6�+I�5�?��b����OVH���[���xU*
��Ds�/��5�hnx��?��#��5���,����q���e8j����|����-����!F��U�`w�`XY��-�m xd���+��0�h����L>���^:r�������.������
��K{���2������&��'���"���a�_-�x\r�TTp������8�[�t����6����i���{azPU��AQ�x*
�f���)�!U
��h�J�wQ�r�J�g	�Qw�0����1����H���DSFs�����c���!�;��p�a@����{
�p�6G�Z�1�;�����g{�����Q&��'��E�u$�_^y
~���2�w��/�C�<�=d��rs-�f�����h������?��� 
��
q!mX��������3sT`C������?�A�X$Sh�����w������xo�m���_���
��(�!������p{���R�t$�qv�c�c/�����c��5��r9�(����n����d"�A��_�O�<>�_HR��r�H��xHk�j��=3�d��1����Mj��f��Qo4��]���d.��m����M���������a��)���\��/����M��u��8�r6��~Zu6���FQVQ]��*$	�j����+v����i$����/Y5������U�A�����1����T-�\��E���XSa��0~N_��A+=e�Q��r��������=x}��:a+/~Q=�p��5�������AGO� �|�t|�J������%��������+j��O/���3�]��A
�k���uy��/�mp�����$��_�<���M�m����[��7D�������o��~�W���Qvfj��s�F�F������f}��}`���u�o��~���oJ�����g9cj������������'�	~�	^e+~���z� s���k�_T)��u�_�8�5
x��T �J�X�P�%�\���K��#�EH(�i���3�1����������/f`�+�l�&��+=�������_\������=�D����^O�����L�Of���+s �*[�I���3�,�u��L���E"������{}�uu��u�������]�]E16�R �e��g���
�i_�{��7��/���s!����|����;'���>o����qA��Qv��%�Y����`�7]-YQ�c�(��<��R����.��?�S���Pi�[���Mq����������7��*���I*n�_�����I��|W�iU���i��x4UjE�_�Z����b��~����J���T��=���f�U*Yq�B��&�A)~��!���_�6w��������ky9����;���j���z��hw/�O���B��.�F�F�Qw6���D9���>�(��Gl5\8����h�K@���)�E@V�
��+�J�1�n>v�7�
E%�c�]����g��
�3�;���z��T��������n�f��u��r�b��$���O�'��B�~�,q$=u��>�B�H��X3	�0���hA����260TG����`i5���pJD���z�N�eQ��;��`6eF���`��o�)���]"�����.����kt�D�'�O�����2���\�AML��d�*����v���p/q��.@������o[�]�&����+�z�f�k,h,��3,�<D���Rp�8&�N~���WE��}~?�������k���D��|9x��(��#�[U�n_�aZmJhpj�@�w����6�{��������o��*��������u�1��up��Bri�9U�*�)o��y[?:��z�����UGL�7p�.0�{���N�i0��0pU����%�^��K���bcPh��{6��[������9�Y*�]^�+���?�{��[l�p���#
�_*76��-C����T	lJ��l���e�eo�'��d��5��,�����"vt>��R5?�c���S�}��3
	I�'+[(�Z�m�#LK�\�B`U�n��s��J+��Y�XS�H>^$��s�D}�K�2�D�Y���)����Y���,����X���d-�J�^,�����u�k���]��!���0D������|��M���M�v�%����k��'��,�V�[dT,�s���!O@�&�M��3Jd.u�P�O�z�B�J��ma�������=�&��N���������@>�5�Y����*�,�Xy%���3J/ �j�z�i���7
?���&T�4v�$�$[W}_�N�2~����n��0p�:����(����j�O�JV������������ ������!��f�<@t� �5��T��CM�R�����G����/:���*L�A�|�	v�7#��&YE�E����h
B�����������������u��p�	@�%{�[��B��1��!f7F���&\v8M���x�����P��h]�/�v[�)������M�T����GP��N���'�� ��Ra�&���������k�������uw�����tx��[�U(I�1�������,l(�c�`���������D�lj����1�=�����* :ad�2DwU���������QM�D7S�_W%��F�S*�0?b��+��Y��H�O�I�����A��2	4k�[����Q����I@�L��k���Z�:.�/�+	����K�[ �]��m��a���@"5�	M��C���2�
�Bf���)Wd�)�{����Us�GWo���!�5a�Z9�W���ZJJ�[+�jWd���"�!&C�;B� \`����K4I!�����I�`�H�Qk{c�e���������l�ms�$�]�?U����Qg=�{��;�z������]�8���p�������P��hq�~B�[wL���a�d�%@�@9��aEp���Mt+���������1��������W6�k9G�\�<`@J���I��a���u��00�+����_�{#uN;�H�k�2#^y��m�bI/�����s��(�����E�<	fK�fzK[�|�c2f��	������`
V��a��V�vFS�X�������_	�WK$i�%�!��8-��u-��D����7�~��m��s4���p>���MnE��z�i��dIjb
���� :�s��*
Gq��:�eF��6��
�4�(.oI����f)����UH������G�e�/}|@��9n���e��q�(�7��xQ!6b����t����L8�0���n�jX�|$D�F�N`?�"Ra��h�-�����M�Tc��M��8s?�1�Kz&�I�p?[��)�CN�Lm^���N6Gr��n�X�=x
:>���"L9�c,���VN��Z�h�P)���j���X�bQ�U��nr\p���PR2�+����H�	���>��O���>���exOnwS�D�O�^�y!kbB�D�{Q����m��S<�D���@Y��E���s �x�y��c�^HGC��Co+�l���*�#��4?
V��!K��9�8�j.���D��+w&���!��Z��i�'X��_�t��
=H�����p��|\Rs��3N�yGy�E�$X�Y����049��h
����S�5��7��������c2!����W�5���V�F����S�n�'����\������a�m��t������0�Y���U�Th*P��:e�Rv��!��)PO8�t�t��}��UW�ax�9o_�.�C�����R}?gb��W�6��pz���0�!�Q���M}T'�i����o�B��(�s��\�����u�0�/����%�&jY��KT,�Z<����1������f����fqt`H�@��*��� G�U
_���X��s:�0��Q.W���$�+�$Oi�I_y�1;^�B�_���z�kT���I~6��0����W���cW�P�e�@���S:�7�"��Z�l��$T���
n�SB�&������Xl:m%Ip]+���2n�`�^T��_�Q��Uj�|��yi�z���\��k�rl��dQ��n�4�z�Fs�������W'��f*�9�3#�"��,U(G�����1�H��RN(�
��[�{P2=r��F
0� �(hvN�������L[���47c����x��U��Z���p�i����kB*�!?<��)�p;��5���SWg"��|������j-����4E����D�M�U����?�a��[Z�Z$!%^�p�HI �1�^z�0^���v�IY���*^N�UTQ�'��oev��j��]�Q�����M��C�f��BYh���H������
�a��1&�<�!A�#���sh���Y���
��Qq�� ��N�h.�tBC^T�	���{9��m��������<��5�#J����\s�i����B%<KKM��*���
r�g�4���;��9�����m1j�.eZ���qnN����C�'m��M�M�`@P6YH|�&�'����2��|����3�#K�)�*L��|�N�X5\:���	i�=���ELr�������KR�"L]�%N�v!��y�t���� }o��N��-3;��,��WU
1�P���-����S�	W�*�w�aq��4�%��H��>�S�B\#�Y�ep��m��^���;tnCb+�x��A��;*����3���������������;Y�,��>���5�\�`-��5���Mx�0��`;�r��8�7#�W01Q@l
�.
�
	^1,����$�}' R|�
@�_�nr�[[����0����f��@��|�&�������9]�r��o@�j�S��8���%���5#�>&�
����n�yN��:�r������t5#���u �������V�:�6u��HvFpPK�
9A7�TKm11 ��� ���(�i����;$��5�y���6�d4�3�K��FP�I�N�8���Ds'�H�/B���(09������������H��"�1c�����@�X%v�EXY�Gb,8)y��+A@����nVa����c�y���� �l����WB�����1p�h��E�����@fs:p�Y�
�g��X���G�����|��x[��uC}���\#�� �R�w9:�Xl�`�9��>tT�P�����(W���b��qN���9,(ViG� ��|}4|5������=pi�[��/�x�`����<��:���L��P�R�~;��N�I�2��ub�����=��=��O�8t��(J;�:�b��F��j�����tt~`,���N9�&&���� J|��(��CRc��L��R��^�H_r��_hs���a�m��:�@�%�`��F��7���(�:M:Sg�<D�T�����"��~�	��`��m^��E����T�O��]�T
hO�H$���v�bSp��B[(\�M!�s�~���(s�V��yr���C��W�,���Mb�����_�]&,0p�;a���q5X2B��\T���=.&���M�^M��!N)#2%�!%��_��,G�������������v`�&��!}J@%�����>Z���[����:������������p���v��Ei��*Fs�.�sk=��f���U��������vG���u�`����[KGyT��O�Wt`�<������8���u.�i7
����"�����y����X����p�d��s����T6V�[g`�L s-��$����/h�*5/T���Z_BYd�i}�Z�;�Di��H2
St~=:�����T�c�!�{�=@��MK?������f�Zo�D����e�
;��y�&e�q9/g'�����H�G!}�XI�@�v�F)���[$$�!�v�&��2��e2q��`��f!�U����`�/T�X�����Ml��,=Hj)�
(������f�.f�s&��������,cD�������|�)<�m�w��F�R�����+�x�����l�2�SPm���hY���HA{w�!�����J��.������pJK��LT��B�Bf�Lts.�J4wL:������J,N���*�����	��9�R!�����2�l�*�7�W�; "�1��
)b@/Acj����+�~�2��f2������H5�x��Z_��saV�3$y��W����l���Y�c��Z��K9_BQnJ����]�%"[d�TV�L%�W��59����Qx2�Q���/:������f���eX���^M�"� ��%���=G���U�|���e���n=0��U��D��%0� O����$����d�E���iRM�$��-��#:��y�34�g��R�-qXW��>g"g����PankqG���`1��v|&��nNkF��M�������]���j����%CtZR���e�|����-��=����)��>]N�L�\&���H���<2}Qmg���X��������gJ��x\�P������}T��yw��{\���D��s�_���������V9"�G&ku���?��7uESk	�����+�N����-���[���n��!.��;�E6J8
JCa+`0�����q#�@��#
"H><8�$7EX�~��=�!p�X}�lA��J%�������2���I�w
e�Ee�w�'X���FQ!J�����R�;�q�
�/Y�_�X?O(����K�^�$�j$���������F�r���%��M���|Z�O1�)�
�cwj���;��D�r&�"��R����T�fd��tE$m�C�0��f�r�&GYH8��9��b �����������"�������%UL9y'��%��%��b�H�U�2�c���r�B*(����^l�bDf�(�9`=y�.��(	1�q}^v�W:a��!{E5�-�	H���?E����
�_���%���(��D��*�,0�o���;-Yo9���V�=�^e�K� �2�U_gU
��~2.�Q�b�nA���>��u,^Q�C�S��JH�2��	�����*�����N\�����;z�~�m�M�q�]�n�?Sa�;��J)+�k0���g��4UF���C��0$�[��1�����%��txC�b���zF�)�'5'�F�Jw%��N:*vZ4t��4�z���pdB�������e�S��t��
����N�������o_��U70Wf��Ff�D����!��I�B���&A,�d?|O��*�+S7~�0v���:�1�	}�5K7��E������(��E�A����p��{�n���"Y�k2��]��8�����fD��R��c�B�v�&F�/s�a1���r���x���r�T�@�U���z�)I`$!��	����NG~��l�����;E��/�
V	-W��#���<,���6���:�1������F��&���������y������c�����`$�$%3D;�$��#��R�����%�zk.��W8���>3�I��(�{p�K�5Y����+����gTx�P�q,��;�U_C,���:�)��*���Sw �7z}�.���T�W���I���y*-���USg'���)���uj�S�Wb?YN�G�r��������(���{�d
F���W�eb���7e�Y�0\!_/����F9 ��3:��O������c���!Q��RFJ��;�����c�fS�B�VL����<�yVh���0�p��s��NgMt>�.�.�zw�����~,�h������"��U<�R�:���j��2����p������������F�d��S���m�����r��I_)%[	����m�}���J������~���u��-�b�<��.N7f�j|�I��D�t��������?�O��xI��!-/K��9�2+f���8�C��<����q�t��"���G:LA�%��T@���/��>46�b2q����&��tA�]! 0���H�� ���V�m*���N����pB��5�4\���=���b8�6�K���j7��Bh���i��	����F�5-+��/���������^�u����g�Ze\1QC��o�r�2l�Y����<H��?���}���y.�
��]vWQ�Y��������()�B�A�����y�J������!�3j�~C�5�
�K4
Px�K���u��o�33�����mj:�$�o������,E".b��|��*�s,R������K�<
R��1�M\D�G%��x���������	j���aK`b����}<��>�y7��v��,�5��,�3i}�o[���wB6\�Jr���7�C�/�~��
�2a>�hY]7^{�9�h�7�SR�
����Y�*?�T����5��}�gh��;�_��������� �����]�M�B�_�4�����0}@7d����/������J��T�������AV0^�l��?+V�#2)���,��<D�[x��T����m�4�x�Fb�]q���&�qC�8`���$,��p!`t$��L�:�Nh�N�d��TC05��S(z�?Kl;���:��e�u2�t/E=���X�%���iq���0�	�6qX�������C���]��b���������g+��y
�eb�]�XZ����$�������*xY[p^��Sd�%�u����n��Ph�i9=�����������vH��c����E��Tc�y����uCoJ�_&��ke#�����:R2L1�&#�Q���`!����H��I�2/P1`3��`Dh�*	����E^��I����Yk�:�:���u�Py���`?�U�a5E-�kpY�q���V��,T����`x�������zy�/���f!�� ����B,�?��K�M������I�V��A]�����7��j�WN��=[����?PM)��R�����*3LjP��g�������:���"����T���v+5��
��*'WdZ��m��V�X�/����]5.�I������)y	�Y�Z��v~L�Or{IGt�����>�)S!)��AbV�.���	��1�RHt}.ZAtd�S�EIym
&����]�
V?�c#L�n��&D��xt�����tc���80�X�p���X�j�i�����^��"V(����������"4�z�~�2jFq��2����(�t����M���s4��#dN�A��}�jd�6�=���������N�yA��_ik�����VR��pHY�P�[�*2�L����`g w�;L�'p x!c�������O|*�c�^�D��Y2������
�u����=�$���O��y���}����%lkJ�r�}���$�H���I��������"2"@�\=}������};q�,�CX[XI���Gl��0-��.TkD����9��m���L��3	�D2K��k���B��x�T^�����1����h�D#��@��$>�������g���v�o�B�����?�$���������g?q�JR�"� O--�4�JH?>�u/�8���b��Z���JB�w����O;�����0������5G����#��$���w��Q��~��U��)�4�"a7���+`_j<<B	B��r����
�*b�.<Pp�B�8�����y���pz��Pa�q4J��n�c���>���T��|x�H�����a"Vr�7����zi����
=1V2�!q"�������2l�����i%E�e�M�c:HV	��Ix�����YU��G�frt�!��8n�:A��a@�{�X��'#���oo�,Cd=
�*�v��@�Z?za�U��7�0������s�����6����,��x�dnMP0���@�
~��X%
;^Qb�3{���Y'��
�,�y�(�1�0l���t��%�b)�)5���dm<�4��x�����Ehj�H=����g�r;!<9B5������)�9����Ku\�O���U��N2X`�d�FV��q����>�q���E#b^�L�St.��H����v�e�%�[s !�4��IP�n������B��4z:���iP��b^���0�1wX�#}F������h�`�p2�pxl�m��)O���"%��RD�@���j�����f(<��_�@Mo�49����S$P�S��|9��:�\h���^PA�(��/��"�������pm�/
8�,7���zO)����B�j%��D{I�)�C7��cSmA�:�c
���":N�$�������Tg�bj��g��g�ze���4w�h6���0[~��o�;��wm2���%xS��8M
�jm�h5*#�Xl�k�;n4Gu%t�7����%
����r=�pr�_��s�ox���|y�4^�~��xp�>�����-"�vR��j�����k�t��;��H����;�<_OFSo���u�<o�����)&�#9`pX��PB��/'���R.���h)�bDc��J���WI�����z���<����V�7�z���7� #�&CtZ����D��}���Vk�����%�A�9C�HB>�����&�b���!%��an�fk�V����
������g���\�t$��Bj�:�N�i��;��J���i���B�z��#�}�bE��%��z���hF,N��h��D�B��n1��p�������h���`g����!�?e��=�f���:A�D/	��`�1�oI4��jB������lp��]��Bs�^��%X5�h���b:�h�L�
-L|�I��PT^�&b�"m��R�ja�	b�7&Adh�wSTa���Z�E��G�����Y�R�f-�!������0�2�{����_?*���kh�q��^���]��x�u���J���C]�����cf���A@��t�B3�p�f�����Y�~���\t#o��[��a���a�H�:�X�
r`���p������b0����%+�Dpc�������i����Y�Fx�
���^=��Xtsw��O����TjC�+Uj�b�\���Q������"��s���2j����^F��c!�U���s�O$N�Lx����O�>*�����>��]�����{eo/�l9c���"���d���OI���'HR�������'q�4���`9����O�AA�(����M�O��G��/����//w��|3l��*����p����vkP,z�Fc�jV�����i����h;�h7�h3���g,(�@��&���a�����}��M��n������_�j����xH�4����jI��2q��d!W�����v��f��kN����VX1��U)����Q��X��a
�c�|
_PI���4i������e[�S��L*�l�j�>���V�X,�*�a����;m]��m�s���4`G�������
SW
�}��6g9&W
|+�8�-�V0���8��F��)}�C3o�k�d��l���}��z�^�\�~!�rT[x���H��vG�.�H�e$��0T�|=�������L�g��Kj����^J#(�8��Uw�����:CX�n��}8���=���C0����Q����b�����r�:J?I5�AR.���p��5����
�Z�DA&����G��$.�
���i����T��������k&5�����y��C)��e�}�^�k�V�uE�o ,��5+��zK����hO�6��2M��d��&g�<C`5)OY���N����F�8]�9`>	C�����J�
���H�J��X���zQ����#��5ljy��������8�
�f�w�%��<2���b2�v�%O ���,UkY0R��
�&-^�������&��~���e��g�lO9�g]a �S��io��'�X	�����0������k����L�G>���4B��o��?q�]�yp���S?(
�xr
|�|����g�7�4��F�<��6G���>����/��8�K�D������?jL��9�9��.���������_�f���u��v�D�|<��]f�X�s���wu�9��{;;�v��4RIzx�}�����eq��2aJ��Ld�R+5Z�q�Y,�J�A�<���M)���O���(�*�<������B�7`y?t�����R����:�n���I��g~S���>\_a�l3������O���V6f�v6fIm�IJ���oN�����&����I�{����i�����w��:��[��B������l�������������[���s�d�{���R/���1<�jm8`�v����b���`,��J��u�������d
���w<
`N>�O,�
F~u|�����s��R/:�����I�sqsI�� -��p���*P��I���q��������T�������fl��n]��7EU����e��5�{���:hW�������zk0��fv]�g���k����V�7X�m|��9A�4Z��+�c���d��������$��#b�_��� �
�P�y������>D�:8����WE�t���sO��VRk���uK��o������?�#��"�����LP���0�:Fb���$��)S������9���E��N�kD�����G��8<��I����l�����z@���z28Q]��x�R��+t��]�Qjn��F*I?�A����NS�N���2bW�c�`5}�7e3�^��}J1bX���/
+9S���z�}�^a�2�:���b9��\�h����Qw��lT�DmY���]�rC8?���:D���F}����>$Ym��2R�\�g��������N`�/r��}Dg�yf~Bj]������O�����w~����xJ��J�C�92���H�!�����q� 13��Y���H����}o���)Q��x0Wk�f�R,�Z5�\x��{�+���9��$ue=Bd�!��G1���|�S�J�]��������S`Z�z�h�����c������u�6d8����^�%le����:;?��Z�������4m�������F�]n�����"	��#���>'
NsQJ$Bg�	�?���������I�d�t�>����o;c�k����,N-1�_�It���j����rv��f���1�e;���#?S*��d�o��E�^�-�TI(r&��$��Ff(7��k&�����FD�OC���Uh�����K��8�w��~'�;�1XW%��{��'u�Ko���7����$u"�T�����a��+#����Z�0�	=@o���lJ7��s�l�>�����O�W~��w9B�f3o�S�{o9��8���T�^�����!��/+�Z��t_���3������R|�����
#������LF�Iu��.r�`�~�szh)E�RB�E�DQLr�Is�9��a�B��*0���x`+�o�1$��:��d��2	�o��r��j2
�_��/#EI{J�����Mx��K���V��7�W�5��O3�L��d}�?Z�^����r�	�MB% Y�����Q�4�8's�y�GBtr����v=��kZ���ZW�la��|`�f@�x�����F��o��#�:e���Q�cR5r�V�R��z����Mi��{�}sK���������A}XvG�I�(
+��;*�;l���
;>���]�}�V!����5=K��y����O�����/�����������!|�O��W����$����F����c����E��c]�S�3�pr~�9I��bx�k�fx�}Yx��7z�\��0��`�\����������.
�Zk7��A|���N����(�X?�
�z���Y���^�W'�U��mr��	��z���7���z���o.�Orc��w�����$���W1jz�����L������Q�*G�����G����sAI�%�u�/�-
�_���AA��+(�FY�����������(��;�67����#����6�C�����MY��=�����6�/&#J����%�M�.��8+��X&���2�{�`�������}m�.h�L�{d�����8�E %�0��-���48���b�U,���������p�`��r�\i?/�V8Z��U���q���f*�fA��r���(Mv�g�Y/�?�_~���r
��7k�Z�)U��R����2h�
]q�c[:KR��_�����*�q�ZkU��F�����VmK�V��(���4v��)�<{��)7�R������c5/��|����{���O��g��?�w�Zy����q�H���f�w*-�?���;�)�^�K/�U'W��{�����/��/:W���R�y���C�2�Q��R(:����'D������������/=B�\��.�k�x�)��.+��e����a/���fu?E�������Cz��}�EvF����;�g��8�s?+dF��E����:�%�*}����"�s�,���h4!+#i��B�V�R3o���B���z������E�k���X�v�6��+�y��F1���^����!�x|�����D�Y���Gu�e���H�b�P��z(P�b�����]�R`n��h�}qNS�qF
2�����{��#D�v�^�.[`�x(y�F�G�����h�`��Py�Ks ����kfO9����s�4R��p��
��=Q�(�w��F����<�����4�����U�-�	=�-�9�`�:�}eT����c#���u���d���d	�i���w:�����\H�B���]��J#���"�FX!��w��V����GU�[ $U
(��z���Ab�Qi����2bY����.�B59��: �-UY�A�5���ar8��n6X��g%�%0R��5���S����������S��A�"��pa?{0t8��-������m��3��}�������i��o=�������4Ut:���n 0'�,���1�@C�	����/?y���^�p���:��3u�����Eg������/c�|���x��������sg�w��CoAO��Ld����'���5-��d9�^�]������������\r1�yd>X/t.V�;�����(C�*��R�~D��*U��c:�o�z��jz�5��3p������*07��zf�SMl;��6��xNN�x�J��vf�S�p.�&F��S.��8�G��3�R}��u����Et���@0V�j��]`�����8"����R�
��L�T���Z2*c7��rC,�(R2��r;�
29���@r��]����_;����E���M�-5���`�y�z�VN���Pa(�!3	�[I#������1/�4�5��������K��s�����C�������/�g��8�
{���1��x�����]7Rb��K�y��q��/V��<�R�H�������A�FM�t��)�B�-|`���K��y|v�����_�v��iD��"�T+��g�	���H��Y�9�Z�|
 ���d�%����3���!a^5������O�\
�"+���l\[�8��K����)G�)��e�D��������gY�/h@��&�Xg�hr@��_��1�z��U�u?^�e�R��e�x������6�[�v�����J�V)'8ol�-���r��^����?*���#��	��2�k�0�@��K�WoN�I�j�	h���_V9�����|���a�������&�����50sR�#���pld+�q�Q�2	����,�D�����{�VZ�\�"$�;�����U�s�Z���7@�Q�G��j�P������vr�J�O7��QI
������	�<�u�+��Nb�����
O)�"V�l2����F-�>l��zh��7i�p�&#�a�9��*<����+�,���c����5���:���u$�$.E�X��P���^$�D��e$��x��*�Qx�]�
�fG���'��F������uzR�������~�4������s���U��X��f����`#��<���,7�5oP.��4t��W���I][���CbW��jx�O�_��
.Bw����a�����k���`����X���]� 
_�������d����d#y#�_j�x����Sb����y�����"��uLBM�d��>��P�<��9�!�U%�@�Ye����Xs������Y"�db\�G}��'<^�X�9�IHb/��Mk�%����T��/�|�������2�VD�m+F}q���)��I���E��p��.:g��2'2����7�3'O}����:�kZk���+y������f��@���6^NM@�d��C���������!	rH�0'?����e	�'�w����K���tC�N��P�'`L}�xc���0�.=d���|�Ts%h26YR�6�	�bk����I��G����^�e!a������{�$�sX�C.�$?��I���Bo�9~�g2���t�������^��+��Q�q��D�I��s���x8�K
vq.�����n��"�`����(Xeb���'���5O�=�����_��{��Bq#&<Y�<�����#��Kg�z*HJ�J)0�z���3�����H�W���8���Dv�SK����4��tc����-�������
�=����S ������H�E�vr
:C:��F��wN>��O�����%2�������7����S�����ud8l������}d�u�:��Awa���]��Va�,�E<|x�������+.r����l[��?��~��[��`�HxQ��P�������ko����E(
��XNf�����-O�'2�I9�\s�6��q��.��`\mW���V=��TF=17n�f%���8����&���RB�d1L�db	tb!L"�HQ�`k��p��J�oY`����?}��YE0>]����]7��6De)�W��*�@��v�&����,�5�#�����
�FPS�M>��5;��1hTl!�PE_Bp?�}�%���^����H�%��(46��C�J�`����r��Q^����y�����������V�(&��uF���<	�l���X-c�r
�(a�Eu�>G�����ZB��ss�lU-�/]��t���{}�"rcy�t���?u?ya�����?
��!c)2�k����i%�s��*cf���_����Z0���������#@o�"3!�8Vu��r������3>�������})Q�������7o��hm���P(gE	p���AI)�����Fl��cx<��������` �"��$�VI\�q��
��n<NbV���Q���E
�;�Q(�����L(�|���i���e�*vV����[��c�6��r�_D������N����k.7zd��^��*�-w%�"��C'��sb��h�L�"Q-�^��>��*���r_G�N�������@4"����������<�f��CH�S,>U�v�*)�PL(����X��B��Q*V�'2��S��P�i���')PT&�A{��]�=�S�
�����B�1���T�X
�~�����'�<�KE@N�[=�,Ww�&~O�ej��<�cp�XxK��p����&�������\J�;J'@��?B����
�[�� �3�|���I`��<�Pah2��V^���x���K��w�\5��.��	������I�ZF�5��^���X�gf#�B�V�}�w�_e�I�]HVV�=
���h�x~Y�����6��Ubpa����������Tk�q�~�7e��M���K������^�1��Hv����`�4�kU����O�akt��0�	s�|`BJ?�o�����V9_-�	�*��Dx��51�������"G�}���x�oN:oI	��O&i���X��f�t��->Y�<v^�p"y: ����
��t�J�z��c�6y������9U�����1-H�Dq��<8F�8��fPb����5��O_���P��*�o2C���a�&����;�el]���k'����/�v�������/����W������G�+�8JD���*�f�FxRQ�/{�9,x��#q�A7V�M��6�UE\����@���!��5Z[F���)�2sM���2�yc����;C����2���8�&*��"�M���;�,�sJy�6��#�y�q��:S~���g�3i 
��|���_����V�E���u�S��������e�4'��n��k���V�%����Z�����@�i���zI�\�������Z�rs�q��_%T���,���9)�t*��g�2:�p����`����z�E�.�1�r�
�o��7X4�&3�>�����s�{�B��h�i������:� }�7�`���E<w	k�X|a��>�5h���P�r�������H����\������
�~"�Kh�~T��j�7HC6H�-Z�m���)c2@�eOIE�}dl��6
=<��&�
�R�+H����gW�bx��U`N]&�����#r"X(c�K��X����IN�U��<I�-"���C�n�VRMJ�����{�&��J��o4�N��[�)���2"S�]��.Ql�Gv���*S;��`o-�?��@	?B��8���&J��b$l�/��M9RT/����/����?%<{p���������������~P������a����HL7A�7z�������x\i����3!fl���O\k&��;�^(����g�|�6�0�1����U�;a����pb�hz��$ 6i<C,MC:Nb�P��<�X���{�;���-�5q�I�g���U���xy|/"�
9���oo%�3���������7.h��56,5��Xo:�:��f�99������~��.�J����s��>�T���CO�d)8�',+7���r���_JEp�k,=�v�xr����
|4��/G�������
���,
�#:�"OY��S]��Q>�=57��V'���<�@^E�������}�~>
�D�ho"��o���%vo�dll��������������KH�!�&K�<m���G��z���1�Gk�rv��������jcga�H�f�r�9���V��n�j�!�:p��] f�x��71��k���Zu��z�t���!:��8T
�z`:��'@`�1�8���6kS�i'�����g\��
evf��q
�f�b�@���;�����|+���}���I$�G�q�!v�I^�4%�n�f���Y�����av�%	�A����DxpZ�,������w�f7Qi��p�+^c�1����v�OA�0��pN�9rb]��:[��x���P�16=�_,}�\����<6��*�|��e��b5�^y����a��8���w���`#�jw2��YE�x��@��;�d�c��E����pi�f�3BB?:�x���BE���N��z���D�3�q�Ta�J0�0�4����:���E!�y.st���n����/�<�$p'�2�����t��ky%�A��ac,�~	�Bn3s�(Q���3)���#������[�������_e���:�o5��X(����\��0���Q���@���Z$t2�	�cOe!��Ci`��{G$'�f����+Ur�2��Z��#MF�F]��m�l���<���@�����s� ��A�.w�\b'd�"����:JO�+�+�4�GV�����
���8��O���8M<d�L�,s�h�,���pvy���%y_������Ht�;�����S��toO}���&;��x	x��\�3�8"	��������L�yFMi����� �lC�1�c��;q.	���5��F}3�51)�����)����0;���Ve��5�l���F�p��7/.�,I�����^^�_���m�J4����G�2��H���fv�@o�}�E���RFa\����<5�	Ys��!��}�#�����|�.��'r3��Xh����(�����oP�`h����B��UO�r\LI��'9�%S�[pW���+,�y�D�mn�[�+��g���0$�L�@
� �����	�G�V���L9G�����Byi2�	B
���
��p��"3��o>u�S}\0Hv�N	W���*�
>��%r�b�������R�'V������Y��Bn]s)������)�� �:����P�tz�
���T�QU�d*�L'RBoz�B6&�g���]p�m�&C���S.������bc�W�����M��K���p%���br���i�_H!U0����h;�����5��fD��i<2�_����dy�����$���e�l�y��71:"�c�C��Mr/�E�-����.�e�v�2�;����E���6��w��*�5�Fx��
��{Cn�h+>�&s��
EK�&����0>O�e '��r��h�����.��1on�����F��/5�{��Gly����N���������vI3F�4~6'Jj��WCQ|w�\Cdyu����`��� ��~[wxc����C6Wo�4�(I}�$L��'u�B���^x���Z~�wG���#���
������7����,g>]�pj���q?�����b]E(@Cf�k���g��0JWAK�0�����S�q�0��������H��Q�%�]�s�����%^��
���,�;"�r��3�z)<{�~i@-]�y�z���E�S8�j�0���#��<�x������c�G�K�����sB���[�!h��p��K^�JP���{��XKL�U��*���8���O��D���������(JG�
�+��A�M�+L���E^�����2+�/��h��_�������]����7���H����3+W��r9���+ah[`a�j��M`#��b��m2xB���~�������#�f���������u�[$��MY���+)�8z��4Z���
G�PTnp���[%i�7�i�
���M�F?C�vm'qH�0�E�����_h�Ql�I�ha��T[��pA6�zS�= ��L]r=�=%�t��6�K#�aO���i �W"Vc8��"l�%�]����
k�\Ux�u���=�=��9Z����w����p�$#Q���%��W@D�6I�WR��S��(������}|0r'>��?�m���M��@,U�A*)��J��"5��e�*'��rfB������_{1V���N.7������Z{�V�
�CK�����_Vxd(%���*�I���4��[��%!�=
�k�R���	v����[���rb����@*GI��H��J��f��+v��=��<�
���KJ5��W2B��
���^�rL�����8�������~���l����
6R$�H�D��c6��_	�GBG.}k�jD���^��$������x�����"3��n&�1���t���/��#dj�O�(x
���}g�)�vN����2v�]�3-���:|?���lXu�[���,w� ����m��.�=���7QbRZ�9$C,������E-�p&}e����b�lN"qV��m�����)0�y���5=�P���&�b���zF�c��E_���yF��~��mOX���c�l��y2�������3S�t|��:{=�P���M��^�����������]�Wj�F�a����Vs\���� �����C���x���������_�H���u/�;'����s��{�yI��H��{B\$4<��Z:GG����nvS���D�'�b�S&�b��C T�+Q3�,<$/��7^��5&���``|��M�-�e�'`K��W���aP`\�8Jr���U�0xRlL�������X���O�����J#3�u�����O�<�,��Ji�5F��W,����R����,�m>�FF������8��U�2���V �zX>����x�l����*��q���h����;�s������{[/at�,��v�N9��&�$��L�Y����I�����v������!X�`8�,w+��u%j�e����{����j!�98k�!?���3�RN�n��Z����es$Y"h6O��T��
~�0�f�h ��U:4���{!�u�WU(}6j����?��B��e[^�r,�����8	[�32fbe��u�m��+e{$�q&��v�]]8�����	kC+8���@�'�^�u
��#�3Lb}�E�;�O���,1h�3�7���`X8+��.]0�X�_G�� �g8��>��O`���C<C�!���V�`
�h��M����6�Ldy�28I<��	�sR�������3�6Kl��V�46��0�y�7�l!k��Rf�)�;�����7����^&x��55_Y	�#�t�9C���������A��A}���e�L�������fH���4p�"_���MD�*0M|
|�t>c!����>��Rv�r%�('�������q�-�[�b��hx���[�������x�=�
2����C��s$t�><]�Q
G#�A*
�{��;���3������	��HP�����G��SQ����-��#���F>��!�h�
'aq��E{da$h
��l4������|���5���|U��~ -�S�(&���\$��C��>���L���y&��4
���+R	���=,���aCU��A�vhH�\�!Q�MA�$<��B��1	���k�S��t���h�-���a�9u3Q�����4��t#��8�H�E��6{��En�d�N���x���|z�%�cD����z���:���  �> ���2��H"����:,�����Un}4B�]HbD���(u�6����S)��J�m�
�(�k^�wB�Jn�1���r�<!ys
�7���(	B7�vf�N6)�o��=*��G1����'CA����)�W)�G3}b��	9�m��c����_x\�������������7�I�8���/vd9Y���rk[z\����-:%�,*9��CGwe�;u��$�5N8(|������Ld�-"�;dlg�9�cD��)O��j���e���A8�i0�@����������8�r�v��u#����XGm�:�o�-�"=�����)���"�7{u|��]uN/����D�M��*2	��-�&�����;>�jE.�����JB(���y�����Kb��@����:�MT�^����Wx6�!�p�������r�kf>6�c/4������n����U��[{�6-���2ZBf	�bE���O<\Q�I���2�����s��4����$����+;"��S���W*����Y*�r�5l���{e��{e����8���C<��	����P������?�B'���%�F�X,!}��gN�]�����b���������%E��mi/�5}�/���8��=k�>�����0�.���Fx|:�����Ap[ ����e����)��4�z�����\c�FN��9��G�����;���udo�A��zI"��u��mJ>��X#�=���e����X�b��a��?;oP��Fz�;����*��J�%� �������y<\Ar���DLC:.o����������W�M���v�'�-�@��+�R����#�����[�8mmt��N��H�|���!�G�^n����l		�XRw�IG���^��uN�w����N�c3�3!�q�"?u�Lf�+�as\�1��E��p�|�~�`)�1�<�
�&���wWoX���VX��w���8��7^O�SbM������$�j�[�8�n�O�nS�V��������:a��6D1tr��������6�C^������	aD��x��I����Ri�F�*\������*��mKe���%#]��|/�� �G!!S�
0C��2
���0���Z����(O S�?�k�fF�LrQ�dT"rx���>�PM+�P\����*`.����R������_ ���9��#f�D�CX��Y������j�-<�~�����aa]��
�.7�N�*%��+����L�+��U�^����ph(S���&{>�b���4����T�e���]��a�@nP���9�Z�%�^<�B�P�p��+��(����5u��&�wF)��8��~a/��7�`�)���$���ph�����,vt������H�<��G��; ��J����M����&�s��I���I%otQ���Pu����au���!�����b~M��K���V�0U����`�GC�K�j���pIx����GV�Xjs\�j�`�aZb+�h+���M���)G$(�^B\+�������D�����#Z���H�y�,i�B@A�&�U�l����r��^bn�����'o�]���yR��+�����M����00Q���U}0�d��d4)9�(��w���L�k�k���j8��6!�4[g>�U>i�N�M��M�k�qH�(�m"�
$&2���^�6n���tw�Qr
Y7���*�����Y4��f9A��K��q�*7�����J����f�9�;����JH?>�u/�8C���A�R�Ji?��,O)G�d�:��N����n����I�������]��v����������?�	>��)�3j���m��a�+zg���R��WF@h���Y���w��X0.m��c�C���"�patg�3Elk��������i�t����>H=����RbxI�8	���,�l��c��v�I�\�y��ak��U0iQ�o3�O=��J.C�;�IY��{�q�U���^���U��A����FL�*~����y���;:]�{X��$YY����y�������K����L�X�9�3�O#��@���a6"�*y���DXZB- �W�����)q�!���������Q\���.I<���uK������z�5����f���EU9�_J�Uu��ZuT,��V�R��k	v$�j���t.��4����������y�����&���rQ������R���"h� u��e�S���?�h6�|�u���b��������k�P�.Pnw:�~��������C�5�R��@����~�����k���=��E?�����87R�������u�}TS���0�
w���1�m�0!�� ��~((�_|���������)�2���?������+�}0Y��<�##��p�`3o�B�Wx���[�has��r��v�	f���dn�+_�_�7��r W�V�2�I�y�`;�c��N6u�C4�.45B
�������%����p�.��J��������I����u���u��S8E�Z�����J�.U���e���9	��_S�"��\|I���]����hX�a�"��l���Llv����H�IS��8��K���b5���>�NT,p<�Fm�l�������vU*��$��D2���Q���%�-_\���]�z�0S��\/PTq��W`	Ej6���S�����0(�.<{�i\���&/�6�+��W{1��"�k���r��l5q���-�������1�����G''����W:�y����p�+�J�X�����8;hUW.�+���R�^xx�����P�)Z�V~���
�E�p��@`w�����Q�����r�^6��j�Zi8�Z�T�7k�Z�)Uj�f����2���+nz����t�����C���k�Z�ky�z�����R�2��������q���;�p�z��)7�R�����r�c5/��|���4����'���{���������Gp��p�n�y��r���;���r�E���^ur% j�=�a~��|��:|������_n�	����%^��- .��0���X���`��>t�yM����1�$�A���K���78`1��s��)�|
��J�&��n��Wc���`c�T���1YB�h��H�#g<]7����H8�������tK�D�hS�DS�i>x��9A��Q��{�I�#v�D��3�q�\��D���K�����F�������|��5#]N#�A�3�&���� 2a�9p��DA;�`�:������� ��z���E�w���clF�X������(#_���s�Z-���/�`u�,z�����N�[�^�����o����w>_~i�m�z}]��~��s����}\�O��y����c�N��k�/0�x{�;	.�(CA������������-��c�Z�����b�5gTK�?n'�@�C:�r�M(����zn�\�����bS��G������e4y��s��O P��aN����(p��_f���JDG�*%�U����R�-Q��TjX;�*,k��4��rY�Tp�P���Q��Z�9����[�+�b"�DRN�4�h���Z�u�Ew���m���BX$���c���^�Mt�)7I_����
~t���dM�l�	��B(7|�R�>��0t�1Pfz����l>�J�CC>F;O�q�7�:����k���Ts��R�+��cw�I7*�>�Ff��e�]�R�s��9�Es-���~.=oa���;q�����V��J?��!c������r����x�a�f���th$���z�t�k��/�>uo�ng��3�E&!-�7�$R�_ 3��m�������[�2TC����+��s;�(Qn�����
fo��q����MC��|vHk�pz�_� J�$�!�V���u�����
�d|�6Z$Kcx��v�.x��'���xr�?>{sN�`�
N�������X��@<`���sb�UQG����\�U\c�A�{[[jR���7�=��z�
�������`�����z�
�'��3���%~�Q(p���>���e���N��
�X/=���R�-��\O��yq9�P���S��c����"���m�u�~e�����Q� �UP�=7|bd��U��^q�@~���:Ru�G�3A�5��a�g�?�ya^�';�\��A�2(W����y�����c���;1��#�r���uh��hf��|HTB��`A2���]o������$�-@�xUa��qw�,����j��n������pku��#BZ��{�����~��Az�B������������E��4�h�\����}���KzsAp���L0��#VX�1L@\���g���(��m�4|K��fd���|x��C/\"c�X��w_�h�?����@�����t�2MoTn���7�ER�������d�'�&��}���
�,������l9����j�h�'.I���L�����]l�:��5^`*MJO��lz:�@l&S���!��;�M���Ja�lb�p�����14�����&|���S;���'�fG/��s-�"d�������@1�P)aUI����v��4T�l�-h>Z����Hc�!z�1��+##Q�X�=0��L�Q�\��}��U�5���,���X�#A!�������3]�EE4�?���^/X���~c��/R�,(�2� e1�G��l���@y������hON�K��d9���T�����<G����)l&�OBg[L]zW`��	x"���:�x�!:�hQ9�n��K>�r��t�Q���h|�q:(�E�$F �{�����Rk-x�~5�E;�
��7w�����O?�N��3���@#NI�'#.E.S9	��#��������r`�MA��J��F�0Z�p�2�\��w$����8x��%�l�S��	��#�����S�nO;y��\E��+>�L������af,*O�EI��H�%�����/^G��l$���2�X�a'��'���F�B^��������������Jr�$ua��s�9>Cg���0� Y�B���S��M��dT�k�
���*r{f�d�_��N�v���Z�H��w�8�,c���Y��#�	�+N���a��W���TW����W+��>t��w�����r44R]��� %8r��/S`�^r����Le���D�,(c�uF�����]-k�T����\t/{�����a���v��gW��'��P�w�Q$�LV����g$��������c@�V3_k=�} ��\MD�u��30��"!F�WM5ew�9���s�[�	�#��>jr/������Q`�I8�������?jo�_n�����$~�E,�;V�q�[�����]�k�Lg����y"x�\u.��>_�{]�xT(�dx�~��������#g�p����7u��)]M����p�4o���T��e�6�@/a���Rz�O�#��CM��#|#8���.�'������yA���Y�v�3��O-e3#p��I�,	j��A���9�W����r��qZRPOw��o��-�6dp�o�i����%�#yD��+-�=t�}Q�E�!C�)�(����7�Q�0���KM_���)!,���=C-�}��a�dw>��/C|�+oI�H)��d���E��^p�����L������� ]A*���O������"����
�UJIQ�jhB
���4&^�ev���e���#��<��u$	�6��G����l=�����J��Gc��	��/���l3����Wx�8�����
�f��qI_�es�eG>?���4��7nY�U�3D�\����o��.�JmT`�K�r�Z��hS�]���������a�9�%_�1@�.�}�����V����2���J���}b�b���P2����/d������pB�SA���o��wtg����Q��Q�=�k{����7v��z2>;��
}�c�T��X��"b��T"B��Gw��;�������WB�	~+\t�?�R�Ns�3����R�[�����>�s�i�����V��!��V����f%c��Y
r��G�<
����Q��b�Yj���vy�R�U��|++����O���6iY��;s\���d��^wz���G���N�E����D:'2fZ8��������;���m���x^���ZO6d,0*�p��#����s��~����f�,� �G*��n��7��B:U�",o.�wi���������-��H��R�p~���$6��z�����D��c����@�%Z�6b�2o��h��
�0(MD����^�Y)Wj�V�����r�UM��L�'�������M����|�K?�S���u�}��_�Rv�~B�l���������}	��t��1���o����E�r�l��kE�F��v���+��b3��)����{q��L�hF_"��&4�������&r��	���RY{��������k3|��0��T\7h$��������!�\�>�]�.@���BS��4X�-���?vmm+�A�<Y bu�GLkm������W�\�����|�}~�� "
�Q/���(!.<�?stw�Q��G��+��)���D��z�Q����
��������A�K�e��J�n*%���`#���:q��9B5��@LS���_/��T�;�aB�@I���'������/��C��7������:��q���s�J�U��T@@A�M�v_�#�+"�������d<Gk����e�m�Oo�w!6Q�{������|.nQ!v�$��2R�_����-�����d:���|��@O&k�����b�-�Nl�J�$�L,��q�U����:�n�:��@��#�D*�N��-��!�2B��	����N�Ss�����Q~/����D`�xA�QM��O��+��,.�������|��`�i�f
?K���,d��R��>h�G+y�t��Mz#_�|dd���<��'��a����C��8(�v�������1�����>������H����Z�8��6��&���Q��j��?���?�����f��V��a��
��jm���CX����e��V���wC�[�W��[m���)��'*bo�ey��OB�)�#��Q�������\u/���b����W�y��]t�:ZH��'^�F%��Pn���,5P�'O���8�l������]*� Sc�,�������\�884#��w��7Y�-�+\N��Livy���%��D�R��X(��n�c�w��o)�a���6���Qn����\��(s>�i;�|]��g4y�~g�X�����r���6kf�[��\2�2�F����:q��q��:W����1T~?�����A����q�X6*�F�\����96kM$m�����6;�$��?���|�1��k+T���5�_��a��\�44�(tWZT�����'�hU�h ��T�,\J�0�h��{	j+C�c��2J�b���X���3�t|�<��} �i}7��S�9�(���m�w���5��J3�,�~���g8`��S�B�<I#k��kBq5�QoT��H���Y��������R�o��+i����r,���D���~����g����d*��]��<C��bq4,�F�fuX���p������m�L����w�/}9���e���=C�bq85�eoT������Ym�Z�x���=I�h�Fs��WP��~\/��>��()�s������Ri�nF�"FI�f?��Ch��n��Z�C|��j��"�eK!1���,�r���CC�&'�~vg����[�)��iR~*���;���Yl�\'�|�v�v�����7�wf:N�{�:|��r�&G ����]���Cwx�	�-=�/�_!�"�=��pD������s2+�p�u�^�=�D.BtU#?����i�xS���R_��|���>��2�����|vo�����I0%M�3��+�M�����y���:?�6�8��46���n'9eHc�M�aN�*���#�m
������_��R�x�m��Rz8�}�~�p�'��?��u4q������1��hp<��;��;�������?����rQsX$P������������BUK�Ugz��=��*�,|<���!���L�r��R/�����w���3����z� �}������af��� XX�PAB7��@�:M�����jH�G���\�%���;(��}|�j�[,@�[��`w�R�����a
���$������5�?/�Jl���4s�Fa�L_*�:�������
��Y��BB;�O'��W(��)��������.�$9c�)TiN����*yeQ����'�������e�@t3\��f���R^���@��@�K�|�A�<%��w��]������3�@�������40F�@�[�����Q�`=P�	�n�z��n�.�S/�`?�f\�z!`�8}E�����=������e!�����@�B�rU�
���p�L�<qo��OY:,�@�@�����g�d���([D��(����X�#(:�rPW�H���w�qM3�[9y�,�6C]��
U\�8@v�m�m2���%�y.�|;�5�d�)��)�VA\
{���nF[h}O���-	t���>�E�{yy~�?<�v��_dR��<�����Na[��,��b�e��f���&����+��c l������V`ck���������uvI��0?�?2`�)� }���6�EI%{�.G=����04��bY1��C����,�T������=Y��O���|A�G�0��fl�"�-�����/�+'O�:o�W�����W��T�aU�
+��;V��0''h<=���"_�D�UL�p�Ht�8c��:�\�HrI�%�#�C����w&�r��dF�x�qxe�����'��;'�V�~|���������;�kC�{�����!�L�3�����/M���U�8J=���LlF��;O�U���Sr~���S��;:��,Ez�hh�M��U��I�*��S�dy����N�g��v(���J-�`9��/���~��{����u6����-]�%�4�-_���nZol��L�y:���k���s@�d���^�n���s�5�����G���N����Zx&�]FQ�A��w�����y1���RB��	�u�mnP����tH7��������T� 5�tW�	�|���$�-����2�����=D�@������$_����q��`* �v����8��0���5�����d�t����=S��	e?�YZ9�����=f_S�
s/�Lp���wt��5���8��]�0�ms��@.�"H�9A3��(�\�����n����q���=���8b���F�c:��-��=���B	#`_�p?'�#���]:����O� �Ky�z?|�8�(��'�l<�Q�����=
�6�sf�|�"���*aN�&�e��tq���U.�h����q�I���
���wC9���?VIn�+_j�w:�^:���K|���{�	�n\_]��y�S�Y��������/8����������&G��U��z@W���P�����H=������������������(���S�������[���*���7�hc�&n�h���B O��2�4��"Izh�F�
�=k-i�k�["|�N	j����d�P`�j3���el�6�\{�bP�
$	
�&E�i�P3�0*	�S�p���z�9BB�����e�<���B:i��7	�<����)�<�s)�P8E{�3�-����Gs�HwS�5�����N�X�o}��X-f���e����k�g\�$�M�l10�"`cM�������f��D'��������2Hz���I�X�p�^�%��P��}����GJ8�s� ���8��kD�1���0��';�����/Uf��(�T#ZqHD��i����[���@��D���$�����@M�ZT����KE�'"�e���K�50y
)���i����S�y~y���s|%�
,{�f:�{2�{,*��v"��F�C��o����!����oc�@�s�H�s�E�s�C�s�@�s�g���vX��������#���#Q�\����u.�Z+(����6�}H���j�<��O��pX����O�y��Q�O������^�_;j���b��
]?��|�|����3�1��aU�����X�z�\1|p���k,��0Z&i6B
Y�9w0Q��
�$���g\����4�-L1K��G��V-Pw�5�:�)&^���\,��fHV~�U�����6+������k=r�������"u��\���X(���=|5���)i-
�e�8_�l���T�\�)0�l��,I�(]��5C�=/ ��f���Q��qu�B0l�uX���c���	�l6+(��X.��R�Jj{c�6_�:h�p?� 6�J�Hx��� ���d�6����3l�S �#�i_����Q�"������,�0�'l��i��p����A=]��0���&Ke5 W:Gc�pG>�[��X�?%�)���s�H2��99�e�X�T���~4�=��c�P��
����bt��^w�x=Ev�_L�QD(��H4�Q��:wl�+��i�R���RG�J�/�R�w��
�(�H�'��YU��_���0

g�!�	m��L;��-b�B����BbB$
v������9�� 9����L��Q����X<�
�;�(�B;��>x�xz�ag�D�����MmC���/��>��W:g��l5x�U��e�
������+�x@�Fk:2���q�x�����P����Z&�P&b�u�EL�FX%���������!��^��%b�����_���e�d��b��iK!b*r���������7v��.�5��<��m1������.��T���mo�4ENT��W:�T]���I��X-)z��\JWr;�$��#�{��g%E����d=��V���QIu��vS"�����N����b$qU�K���2�1�K������}���O��mg�&�!���_�6���W���io��g�w �w���:����R���MS&��b	v�1H�^���4�t�Q��l"O�=A+�	������t�t����"���.�0�q�WD^����,����n���D��-=����V�x�%����������o���G}�HeU^������^������D(��~�]Nn<��3o������>�y�s;-|n����}�sW=�����e�s�%�����/8��So��V���K/i:~�C��G�@���r��Z��
c2/�=���y�^&���4����������/����v<
7w���*��v��7�������y�,pg�h2���7}h�:�v��Ku�i�{NH�����Y�����
��1}��!�y��R�q�����-f^�f�x������!xbg
��;x�&J��FQe^��7b2bP�
�I2^�}RLE#�;N���{��j���34d�
��������]�&E�2tP:?%�c{Y��AV���d=�u���BE��S�������
!��`�Q��p��^��g<�8�{��V3������?B1�j�<Pd���>d�����y�x�0�����a&-���	H����&����/G��f(��&k�,��}f)a~��
��Q+��z>�`��m�i��h7�c���7���0��*\)�������:� $U�d��F��vr�j��
Bi�L��dW	�U�wP�p�q�}|�Y��,�6�')R�'I.F�T���r2�PdrN��������H�]���M����B}G(�HM�3tA�h�q	)��	KM�+gV"��&7�����Vn���
��v��gW�-��
O$��jx?�+'��������X�7X���a$��g8�)���a�G��4�&<��M��$����L��G��6��wo��{�H�Y�;��$���~F�J"<��?%n��%���<;>{k�����3d�<�?�Kg��i���g����0>��!�V��*l�)S0X�)�G�ae�k����Kap���&�M�$!�*�2aI�A*��;��]��(���h
�N��(��u���=��kMr"�9a�s�0���g���F8���6E���`�# :��.m����_s#>J�%[�����m��>�XDxzc�����T�E*B��4��~�hILF�O��X�2~�
����>�C���9�t��Q7nT���'1���^���u�"O�Jj��l��R#����;��n�#��7D	6�,{�^6�V��E��!!c�)��LY����Jc��!"����kc���"I���8-v4����@���
��cK���������4��$�b��������4���@������$Y�J��y+�/_)�.O�<��B�������&����n��k�3d�:*&X�K+G��$���%6P���?1dnZ��t6��P{S�1L�7�RiVx����Z�[6��^�=`��T%����&�Y�(�`
	�8un���-�g�����+�U���
gx��!\��vS��Sfj���p�=��R�{�M��gd�]������M�~�AD��,qWq�r!�.|�)�T��Vpe�����6��c:'�����0�������6���=y�F}�!�7��lpm�A4;H���.fe��Y��������{�~�`�fk��O����x����aA{�Y���V���0�ZO;�*�4���C�Ng�B��?�1�������d��q��������G�vy{\��������4�.���9:h2f��B+��B��;E	U�=�^���v��z���	I)��P 
���S��c�R�w�������\�e5�FK��F����~�V��q;9j�hHZ;2��6�" �q=S3���uo�lT�f��Vj����nn���^�n���I�0��:���)�o;n��O�J_J���!'����?s	�f`�X�O��������=�F�*�g�E���|����~�KPO���p`��y�����8���E���[���h�BY��3v����#.c*Mp5~s����&�1r��cbt,����%D���+����N�5���)�(?Y�������1@�]��I��2���l:�Qa��� ��W�{-���n��i������6�����R��?�?�����l�*n�Zo[�Vi�l�+���-��F��T�V���%�O�������-��oKb��B�����b
N&�r��0�wJ���)m�7TcV���7�.��TE�&��kb>���>Z"
�5�����y�����`���z��LW�y�J����:����Dh��B�h$c:%[T�Xz��#l�Q�{��L�d���b��s���9j��e���
�i�?���;�V�`�s����~t���kas�b1�S����������r��L#e�K�^��V���P(KaI`�Q���f7:�rt�����~UcK���S��jG�z���h\��n
����>�,��CJ���CjK��(y[|sM����*x�l��(���m$LEn�C��5%Z[����
��hm�dx��&_�[�<�5��b�4��K�����'>��p���ir����8bN��kx\�`�'-9]Y�Z_C_�/������kp3����r����*�&o���&�o�������	����K���B�v���
wT+��q�pn���Y'��vg�����'��`r�,f�������E�}���|8>�V.�=�W�9>�u{�4�^�3b�E6l��D�C������YH�Gh��M({�}����&c|7�G���=�����9~���ir��?���`���7��O�1�fq�^�9%����8U��hF������&�x#�����Y��+K;B-]�����!e%�=���]�}�����^G@���[R������JU�6�����-c��)����S���}�����`���������B�C��#�E�2��q#�`���s��@R�`���Y�l�st���z�	�W�"neF���pX��������L��z���c�PY6��
�(�+�
q������<A���O?���'���d�.NW
C4�$O����4&�
���[��P�5R�8���L�A���������B�g���V��`��*�U���qSn�(<C���H���k�oxl�������5�A�U�=�hlh�8.�:��}I�U���@��04���3q�{������9?u���������C�����+�����jI�s(�>>�u/�����s�3C���_;'��='���{Oa�3OW���r�����)a�����|�8���:�X�gQ�<gIG�u���T���t���+dl21�����V�sp���m�+����H��+�#zE���%3��'rJH�9���(�!�6_EWv�C�����0jeM�pW&��ne�x
M�cKW�H��b#���U��2Cz�>����i{G�0�Y��$����/v�9�L;��%?��x�L^>\Z����e2�������f�l)����3����P(������������LZ��8S8���c�gd�M������\e�
p���Z�p��wz��3-�^k�^
�H�Duv��
w�h�C�r&�s�~�I����������'����(VK�W�v�Op�PO5�|A��9��Q�e�P�e0��j9�CJ����r7!��*�k�^�H�"��zg�Y����O!��]����j������R�?���	�,(�m���R�k�����	�bt���F~]�V
�q��'q�I����F�����.��+��?�p�r������+���|.�����X/���a��S�{������.��u������b�8��b8�N�����cH*N_��V@����G�������{��'�}�7y�{�F����x�����=Z�/E����4�$G����i���#����j}	?z~�=����\eZ��e�m,�'S�B~���V}�nz�G��I��z������6�+�����u��
��EkU�;������������������S�08�.�i�V���m<w�&43fs#Et��5[������E^���r��x��8/����W������P���X_6x���@�Q~��8����b>��:�F�e~I���^���sLc=���w}��YL1|���Ho�I�����0U��U�A�[�+��~:B�$coY���#
�������Z2m`td�7)��|x��������2z.*���=u����������-d,��X"�,4�n�'���et�o��>=:>��n������v��eq�q5��_��[�
�d�'E|.����(~B����9�y����J�K2�$�<5St�)ev*k��V��km?Q^��_J�U���l�M��r��KnK|b���(7�%�o�j���=�GU<��N
g��G�?9
&�)s�w��u~�ZM�vA���m6�nv;��/N����/'��&>�d4��(�9���A]���j�8o�2�b'X�0o'�h��J�"�<:<
f�n��1�CUd��\�R`���
�GF�n�.%V[�32��*f���j�5�F��L����s��vz��������w:iA�@��x��C��v��gA`�c0�����vO�OU��s��1��
�A����x��mU��+�����No%Pe�E`�����p������6���T2h�,�6,�	&�������
/:�	��t���U���(��#	�9p����;����F�*��[� @��8���N�R����~��	;�\���o=�O �>�*���z�%�*����%��[�%=vOt�.��A��sJ���`�mX��S�;�^�\u/�-�*��e��3���X�z�*�p�"*��1������'�-"����v��1�J�k;`>��[j�6�(�,|��$���v���h�"���,�&:�#F�4M%�9������g:��{��b�>����88����	��xF��&sr���b*�4$�������GEadK�*b-bo�jv��g�E1�]8\��
�dx�W��N��U *��Dq�������m������:��v�iQ~��������vR��������1������g�L�������: ��|-l���/�~�DF��|�d����jv&�"e���;�Tc�p��
������p65�{��PQ�����X;h%�9�S(J	K�=����/U�D�i��'w�/�����!u�A<FN�p|�����(6�������@�N��w�O:���?��=8h����i1���������k�x���k8��V�Z:|J^�lM�4�3-�	���o�rf,g�[�~B�+��v.�d>�fh�vc��h4��4w�Td����w�A�"����G���Z��Z�
�'Ou�r^�U,�������Z���7�r����<���H�����'-�r_U.�T��s������Z����y1P-��h�gQ�>�q\���5�v���O�6�XM���:z�u�M���,�+��C�jM����8��Q�0�����
HQ�z���=���������C8&��4	4��S-����m�[�)
w��.R��Y��"��FG�Yh(�<�X�h�E�g#�>2�l����4��}�<.������������:�g�Q�"M�#&9����=�+,�*!�Qo������n�Xz�����v�axj�t=���2��0�k��P9��a�dc�7Q�i�&j�
�K�-�cjo�����`r~���G�(�
��5TU^�-u����������#��N��-�>f)���^���9W#�	�MZV�f4{h�
�K��|��c�HJ
M
u��e��@U��p:�+�v`�s�B�-�T{�~��;�������`|��/j��>R��W��f�!�(2Z
�V�'���K9A,\����1F��������+�w1���-�������m��.W�`����2�N./��?&�~fR��.az*�����)���(���*�����tp������(��x����v��q��9�Qc*�Wi!X���
=m{8�%-b����	��|@ydC�����J������e3<v�P�5��T��-��z��$���&)�����Y�����i�#�P�-��V�5��l9��w�XIQ	��-?5=[��	�;3�H�Ro���N�
�p���Mo-����P	]�R��V��k+2�c���v���k&Ae�v4D�����U:'
�z0��f�������1�~�3��W�M��h���V���f��0+����(P���2h4

W���wC������
c0�!���u�!
���(4`�Ei����Y��Z��`���C��'mZ>58�RM��U��:�3���c�M������O��n��� q�[x��]������)2t�w����{����CQV&:����@�M�61�1�
�?�:��]�\��D��?���������n��] ��m8i���������`LO�7r�����s�����{��_��?���?��<����
��Y?��$��,M� �����zn/��$�����
Z*�-�-��;%���J�n]=�������8��+����{���;�r<����������o����*�5\�����=��<O�a��D�0�%APs��Qt_.C	M�����?��%����#����<c��Q{�%��lP��������)��P����8�4��=�����6����<��i{���e�o���cS�:`
��K�(-��G������bF�}Q1������}���^����%�5"���qX�5?vJ����i��T�@�K����J�V������pV��aj�����l�������#����k����?[���
�V5��Wu�������{��f������2�j���X���,�y�` z�tr���F���q���tp������p�R����/n2��v��u^���l���;���
Vu����^�q2V����ax��,�5�e
�����!
�Y��jNl9�	U��������m�z��_�P��6��!���������/�����j���rw����x������f8~��sxt�r���j�0�R���&G���jr��n�d����}C��
���E��0:��^�-u4������p]������
=[BO�+,S����*�
TE]���z�|
�zp������R�c��FKM����t~�=��������)��������4���w���On���B�l��i���)n������^��B��,�$6����e�Q�-~U���R|�VBa��i��v`^�l����C��j��qvaS�����:���*\L>�T�9�bD����3x��W�k�@��Y���;�,��k���EE��v���b^�5�H0���\�X+2s�Q��Gez�����Zj�)���b4����)o�*Fv�C�������~��Qe���
g�8�F}O�n,=��+�z�@���^GC���F��	;�~TO�����-��]��U��)
�p�C�^V���%�<�h-�_����?6��P�����S�>V���H��F�)�w��1]q&S��(M=��9L�2Hj��VA��m��1��	t��>d�p��Wm<�h2���d_e*��k���X]D��e���0�v�s
u��H_��5B��2:��>i.����|�@�>�hM�����8��>>z�������C����]��*"Z��/�z�T��&��U)��Q�<��`�Ca��7n�f����F3c�
/�a���Z����:���m�P�()[��T���S�Ph`
"�~������T��Pt	�+F(������������4}0��	�Q������m���j�a�����{���r������O$��#���nrI��m�^����o{x	�Be-����R
�tn���m r#Y!(���Q��E��W��G���z�(?�� ���&=��<��Y��Y���z�<��w��9�_F��S�#&�t�hi-B�{��o	��;��:)=)�e��I�H7����}t�w#��S��*&d�0�,0���d�q�V	�9��4+�4��=�;�0�t����r��0.�m.���^3K]Q����yO�|�,a��!�/��>���*=������(,��u�
ey���^�\�O�s�������'!>�� ?�P3�%��O�^CTF��~��L�I9
�nx�IZ�����E�tg���~��|x�/��r�u����xC��]>�}D��EQD�r���,�Y�]Wb�O�`�����g�O�^���UJ���s�2��1���'[�'�=����6�J��V_h��RT14��0��RLJC���0s�i(E���<�B7Y}=�(��@���Y{XA�m���YOs[��u���������c���'�9;l`�_6X�
#����S��^�z~����n?{~|tx�>|�E��<98�����{�J�Z �c��)�F~���1����f�����d��V��
�BW��\����[ir���L�SA�&�����a�?��Q�����NZM�������g�����~C����������~�zC���fN=�����������S��}u|��x��FeX��s��L���}��]������-��C��a-��t����r��`�����|;8���k*8��4��?��:��GI�Z�����W�Q3������.� �����6}(�\Z6������:S`�5���5��)&���.���7�r��+bHN��_54UGA��	I����"q���U
��*����������Y����JeR���z��u�S���;O�����hZ��o6���wn����b=4Gs@s����|�2N#�,�TB/�J^���<7��&���9���������A�z��C3�D{��e�W!���t��k�hP/v��?'&�7��Y�����Ti:����Y�GW��H�m���|��w��dV����h5X�,�����@���-a`��6d�S�����1_��:���"�=���S��WZ���9�//w��z�w����}.Z-|����X�7�T���::��,+8k|0��'�i��V������M�G��gG�!�����������)�Q1U����4R�j2mYy����~���xW�@� �<U����JyH�t6x��c1��2���^�Nz�wfS��MH��BED&�|:��m�������4�p��_c��6v`6�/G�������C	�����p��6>�"��!��_��mQxm<���M:�������vk�C^���VEN����6t�=t��e�|�l����2�&��>.���LT�(�������<�)6g(J����77�l�"]U�B���D���N�a8oh�o�cz�r	�]�����?F�����b��T��L��OvL+�WSm�p�H�:�N���s�]���|/wt-K^(�&u8w�����?�-������=�A%�"JI����:��G
�����@���av0Z4������y&����[�L6����\�q��y�J�M%���`c�`!��'�M�RO�B��HDs+-�(��dp�M]�I�X�U]�-Q��.!C[I�G���t*>��
��{����j67�f&c��\<���=�Z���%�H��������p6�������������<�V
a����S���?���d����Fc�h����[Q�<����.o��+�I� ���Cg]��la���L=��w�^�����]��i���Ya�w�Skh��Q���=]��v���1��V1Qjq�h{�U�T�~�&���tq_a���6i��^]���	o��d���:����Y'v��\RE���{c��{�7w:�S���3���a�X�o��URU��$44=�y��������z��SLO�_]e���4k0ve84�lXR���2�}��n�5�ra�qa�+��
�A7�>�fH^������k��|�]��Noq�!L�i��3�>2�c��d��\|��x%.-Z�T��+��ub�t=���\�]
�}�.����!
�c-�F��nZxC������^u�Y
���``�RF��A�;�6�����Rq##ioe;kJ��[�1��	����^H�y9g�i����k��NL@,mB,,��L9��6W�f��]�T�@ne5:�+T+�a�9�����P�
�K	$�/s��!t��[�U��M�p�J����Mu����nZA~�*�\�
P���}��z���y���Z1n��Z�u�\��9�:��vC����Q�r>�7�r21�g�|�?�V]%yT�����u��P�<3�.��3dy�uHvm�V��b���2nV�p�Q'�\��?6[\���!��M�0�:aed.��:�������W��*C�p��3r5��B�z�m��������)�v�i����s���Qt�PA������U���z���k�G�U�r��f^��%�
A�:S1�V+e������
�{��H�������)�$?����a���K����^�Ev�z�����4�����U��h���
1���*�:�*_��s��(56���?�g������+h��9�<IK+��g�DsBN�)���z�9�U���(�:��V��L��cDE/p���7C��?d��+^����5Z��8���e�g��9���
��K+�Q�nC��E�3#�T��
�U6d��oVv�������4��
�sTm�|5��o�hq�)�]��Q{�^�f�����r�.���R�S������xH�$k�����y�������^������ ]G
�^�f��l���0�.�����%��v�C�����b�I��Cb�3T!x�q�S���;GAVK�g�}S��[�C��P�7S�}�;���E�A���bG�u)u5133��G+��@G)�+�,���O�<��a����5!���,�zD���bP��,���	��g�]���uV�4HZ�V�/>u�DN���lY]Tf~U����1��	G
c�o�=�����\�TOm�����.�~��:�%�i�)�������u(i���]
��������9y�
(8::\"���B9���p�Cgds
�GkH3t�p�������R{B+�Z�3�Rf>��J�����79s��L�*�O-�������%r�<�E��b2�u#p�.�c+fF�&��2��V��Y�-eOuM2yu?:�C�s��Z���J;el���Q�����I��C�W�|�����WY?�5����Y�V��Y9bS����]Qv��^���YU���M���,K~��t���Q9��3���#���x�����Y�#���^K�Y��sqm�
���e��k���f�)����5^4@�L{T��wuB���<,��_
�s��e����:/�����>-��At}��&�a�V������l�:�tfo��>����(�uQ�w������2
�b�I��)3����T�]}�����?��������T&�������+P���LU�����������/�z�_E��j�(<^�4�3fK?�b��f��
>1����oM������^�f��#<�@Z��,��C"�����}�����cszJ������zT�BY(���j��..�*�������q��f���%kZi��1�1(��S�A�5
5���g(4���8=���tI]^���~�E�
C�#/��	��;�����G1K�Rk�Q�4X��3���Cz�v�&�nm��5pk����re���I�6�����bT���Cv!T5�
�HlZ��F�R�9�Z|	���,�ZS�/
kb�f��5���bA5�&��DoF����U�R�d�h��Z���
����T���v��=���V�{�W��k�5��k4Mj���Y[l,	���������=�'�������}k��|�K����������M#��r����s+���T���6�N�E�w���k]� JMGC�f~�h���8�*��+B�lT������]J��W�S;�+�.`c�Ql2]d��M�F�p���F����.��B�VH�x���tT�g�]-����"$��U]�����^��bR(���6�`���{�jj�%/5l�b<#�s��
QT7C
A��L1�Z��|89����G)��X�M�]PX��Q�Q�0��m[��T�[�>���m
2<��K�u�����\g�D���2F�����UWnIHu(��V���0�Y�_�"�C�c�sz.��v��%@$hl���F�����/����5'g��6�,<j��D��]]��Ly���m�8�B����@��X/�~��[������D	W���G)��������]BX�rB'_��A:���@�����HK�Q:�1��7�_0���V�e�D����^��L�v����V��0�/	^���IjgC
&]��_P;H����Z���v�_�Wj�����i�D������%�2wU�\c���XU���F����x��c���L,d���va�����'�x�*����Ju������H�+�B5�[�c,a���j����4��j�����������3
oK����F�-�g-���IS��o�1T��G�Vb�M�4�����t�6gP�!h���sa��aa����5��b1����j����'-�6pV���]+�J4�����3ZJ�
x�Zuk����I�-��P�[�x��z�q�+U���!6�jV�E(��~L�����o���Ri�*m��T�����^~���/i�a�_U_~'��'�v��'�!�����2H>J�
���rq�S�d�]���u�lU��z�-�"���i5:���p_;��}�zv�X��4�c�����M��U!�9�U^��;lk�L�5�
���rg�-�~H,�)�����4�R��NCe�[i(�)��i��J�?�Uq~�jQ�%�1�K��h$uCayC���&�����9/�vO��]tS���=q��3UE����n*�j�%4m�V=t%���y�����/���a�����Bd[��(���v��k5*>~�>ym���U)�}X��,���M���!h"V�?5*�j���,���B������M
;�NE&��u��r�faH�3��x��������������d��)������f��.��^;\�r���v������O�E31���`��[�y�%�@}�et6�LKm�$W^��{��,U��MXo�J����T��g<�U�
������_�2��Z���tNE}�������+'�0�5U9�����$A �����6����%fUeJ�a���G������Q���q�z�n���:��_�T\�q�o}�3<C���'����I�����q��E�o��Z����|���� ���qZ5�k�P�"���,���x��TuC�*������m[� +�G�r���{������X��'�5���a����cn6�[Q1��������X���F�Rm1�l���~��t�
!=6�c��Ty���)�b~^���(��m`Q���\v?��C�*���_�U[�FK��m���oQ���\�r*
�����o����
��>���7cu��S������3���g�>W��}���Z�e�D���[�@e8�-X�\������(y��=�Y��P��>t0C���"Q�r�T�_^�`�������������6�*��#dq��u����FS���N�]iV�N|��[k�{�}��5���I�,�������
5�wp���F����&`��������c��$�������O;���������(�-������z��<�:.��f~���+u����,l��(K�\��6�����}CY[^G�e^�6�O�	��h��jOD��cw]�T�s�4��Y��9��f	
w9����DE�������?�[���]�!B���[N=zGI�*O��t[�+�X`�OC�jW�����Z�;�aT�������n��|?�Z5���V����eYd�	/���|�o�_�V�Eb�@�.>��]��}����Xe�P�`�$7�V��[\=\i���r�����Y1�����t�;��[�x<��_�9�\��ug��T	(��UB]��?%:&X
���%qFe�CA"���H��������$6��!f9�$$t)H,�)d��9���Z�����X���x��?<�����g������:0��z���?�~��(Dn�d���	�PL����r��[�m�A���J\D���S�)�X&�����O�z�����?CK�����a�2�Y�%�l���%�(�U�6�3_��4�Y�)+��>6M��4��0���j����1-];��V�c�HH�����|�hR��V���R��*}�����NI>�O��&T�5������d�$f�+���\K�BV-���hP.U�JL�TjT���[&'�Cg�zN"�=�"X����
!T��-�1d���ly��������6�}�b�,�.0�.lC�,>~�P��oi�������^b+��oU$/|�>gi0�6����2�2&�d��NQ��Z�e��*Z[��jg��}���%l!Bw���-��~?�m�����y6�b"�+�i�uB6��Q�~�J�I������Z!���FnRz�v��-UX�C��d�X�(f�r��eQP�7�rSS�P�?(<������������f+����G*
q-�[�����n(���su�807�9��w�~n�l���~s��S��*����*��6M2?�^�	�
^��y�(����u�S��$.���aP�{B���i�k�<����SL�-���r_W����N�*��V�'Lpzt���'H�)-�	��R��#�a<{���:7�8�Z2��JN�.�x�~8�_8�k�����P5Z&(	��������7U���1<_��R��/-W�V����� ?�.Fs]��5�ny�k�O��Z9g��5�:�|����;l�0��iz�����J���!�	���~�tI���#+vD�7|��M������C����a&�g5��1��� (�+6b6@�-��Z?�2���r��
�W(�w� 7���
���}C��s�!��O�����&��3DT4�����!�����(8T��J�j�@	��]����OO���/�(U���v��?�G�����������5��s��}�Z�|uU������
2@���_��B�>��w��4�F����_�>C���J��
,a!&����1g�4��^�-�Oe��OkU)Z������Bu�(��nS�P��e`������MQ�n��?�����k�����:��cgw�|w���q�x��3zX���VF�U3yD]/��En�z&�+��o�x���V*��T��Ue�*\�=���g]Kt��zW��>-��z0�����Q���!�����g�l*V�����4_�8n�>^�:���s�E9:��.a�H]�����������v�UD.S��~s�Z�����������Hi�/;/w�t�����X��48	��N����xq-�\����(��,��'��E�Ft�X�#���:>^|�������P����a>BN��R�FC�<E����
�j�>l�R������!������wz���y*���ZY�]1��JD6��(�������K�4��+������_=w{}.��Q�Q��
���r�W�1�k������A��7��������I�=��Y����s�� 
�����^�&gi��s��z.�%�tl�t4Z�����U���U>E��uu������D����a�8�AK�.M^�CT'���*e����.E�����8���������y^I
;�<��0�-eu;�!L����#~��;������|2�jR8!�,f�u|g��b�Mn��H	iwvv����a%}����H�Z�Og��v�*�������Jys��*��_�q�@r6��T�T*[�b�:4R�>�i~5�����������tY[�kR�+zM��Z��D�k�^�������zlo�z�������^e�/���Y����a�(2aT�����?��i�\���y�v�Zn���������L�3���mW�]�������1�v
�z~�2G�xRK�D�_M���R�r�{[H����c�j����*���IU������fD�ek��|�Uo������]���Z���������;;}?��^YC�j�K�aJ���ak���2�:7����3�����*�x����� �?`b�+�@��+GMv��O��������mz#$]���t/1��M�5��r�
���N�)������k7v��h\��bz��^��a/��;;y�zy/�E�k�z�9v���R�r�}�7�//�;�7�>�T��e�����\N �������
�'�gQ	6a�um�*�y6P�	�7�E^������wj��D���^��H����uNE��
^_���e�
N�H��l^{�U����|��x�
|��V�*�(��.�|���o�;����:/wO~����Y����:ezDzsp������������)ZA�"�p��T���|�a���j��A�I���F���4[�:�����'�����������".aC,l24T�i|�OT[�g�r��jO�
(���&��j��e1~;��;J�+��/��J�SC�il�^�~�d8����u����\4��L��Y�-JY���ZF��A�un�����s������Z\k���rH<���������w"K<��@� y���S�z���B���D�]wT+������f!�q%����{7p=�����h�JwY�4Y�����������R��PJ����z���Tw��Q�8�{��EO������&�"�y:���b��*��,3*�|����&�������Q9��,H��~�I��;��=���
2�\^v������az,����V�"�������z�vL�9��/��qp9��k:�nY$�U e���:��=*�45~���������l��2��)�����g9\���|�|��-��J1�1�~��&�)�\P�Yw<����C���vj��v��r���:..�����^��<}H-:��/N4�q�OM��&�7
J�h��s��Lc�1`)��v�]���FR�kk��?��#� ��3<,��������o��?B���!�z5�-��x�66���?%/�ZTl9�3K]|������mZ���O��?<;�@h�'��|>��?��Jn�\_���]|�}���(6~q�2���:P!��K����w�I�C_�m���tNqR%c���rT��Fq��x�R:3����ZdeQJ�������>}}|��	���/��\@:;�o��1�+�HU5���G������2Q}������~
�O�"�����6
��Z���;����������Q�hi�&Y���~>���<�uW���`js�!����1��[��$��&��
����V)���Z�Nw����Ap�f�0!v5�j�����G=���Qo_?l���=��9���5?�f��3	
���������������;'?��u�kOo���*��9�bF�2����B(���E���bf���s��S�O��(�
�9�o��D�oJ�����"[#�dD����]�u��&e��aZM���a)�.4f��
�Sa�B�(j"���������`It���{#�
tK
:3M��|>�`������j�������2����}||tL��rz��EQ�R�a.m^��K���	��,�����TG�FH��m�����~�?|g��4���.����?�w_uNw��;�/� �4��z�)���K1�V�����cX�Y�',��a�����1u�1\Y��bw�7�^uo�b�O�� �
i����������(1���Y����Z������_7^=Z�1��1��De/��l�!���Z��eBC;���m9@��M~I� �^7���ZK
�Q��J3���K�Nx�@���
LLg��P�������p��U��M�� �5��E�Ce�'���,��PTW���AC�vz�U�'�*����U��������p��JJLb�V��&6��d�_����o=G�slD�����f�Kz�
^���2��!���HUq��R��-FgC�_���o7�5(����T��N��F����.�[����,�#6t�&�������rx�������l|��[�R�;���#9���@WEw���f�xV�M����h���*�#�Q�H�7��~P��Tt
JO���,�~���1�%M�`�C��~�z�Tz ��W
�?;D��Qktu��U�<�*yX����o���3j{��On������Y5G9����a@qJ��"rvD2�u���������?�`2�?W����f@��k�b�0�1g:h�h�`c7����b6'���l��~��������-Lw����y��kml�Y}��a�����H���S���K��9��x������!�Y�V��;�K�<"N��nQEQ�^k�:���������PD>�(���^{�YqEL�B;K����-��H���+��/�����n1��%�����5��U_�������'��k�V��n��mi/�����F�zo$�w
|���W�5�U}{�Wa�;�2��jG�8��nu����Sw���
���:ZA"��7�zK�M����r������`K�IAEg<QA�dh��
u����
2Hj�tK4�����|����Cg��~����
�%d��*��[3L���8JS�Ac�T�����w
G���8�����g\����j�e��@�-�����6*�����q�����Z9C�����X{��K��&��o���KB&IiW���m��"�[tm�KJZ������'�?����.�=FG�6��Z��h����j�
u��y3cPU�#
��tO/�S=�g�)��U>�l�!��������YO1!��zQP��l��r0����Zr�~����>���|���~�A�_s7��~M�"lZ�u���d�d��~�)�J[�F�vA
�+_�]Ph�||~���B�A�W*���YW����e{�iMJ��	QH�WaD��M���-�-emd	x�Yn����'�������}m>k������C��`��zW{���]��W��T6��i;?������s6#P}=�L�al�	?(�R��?v��g'��������[%��lLz2lj�I{<(#Z2X�em�&	8s��T��#�5�{��d�������\#��&��S:'4)�����p�&�8�I�#���H�Jz=�������l?�w.&g��d���Ml��vu�5����Ygl�h��Y}���+�U�@m�O;'���?���)W���� Z]�`�H��O�"�����-8-k�����1�4���^�J�2�P8���ZY��n1�����*dLY���*��o^�p"�@:(�[��S6�xz��wkW_����$�mU~�H��:���Q�cAS��5����k��[2�^�Z���,�Y��E�5�R�2O�\Qf���zy�K��?Z������>9���E6��l�3(r�������~�!�B�Q�������4��v���zO'���lT�oU������.�z�S^�c`����T?O�!�\\SiU�F���n�fR<��ul+6�����_�},�$�d�������� �����(e���&�����[��F�$L]��a���
zgn~6�r�_lr}��d��(��D{F�B��,��0��'�j@����?�����s4��������x���!�Wm���a�x���i�����g���*x���r����n+�AR
,����qN��#�g������s_^0o�����?�k;���\	]�������-e	��d0H��^�������y�
�!�$��J!����pNT��<��:���v(F�2.�fV�F�W��E>���9=�����p�C����z�{��
�=)8�^���y��n���y�4J{a����M�l:a���	�����{�u��s���������������p{���Gw��������J,�h�~�|��1���6������0���/�8xf~�?A��_�O�������n����{C�4��C�������&�l�A�R�:���wv������?���`���t�������������dq������99}���u��Y�aL�R�a:t:b8���h�bl����WnM�����s�F������6}xH�m.
Yo�S�������6Z����oH_Og
��9z4d���m�^S���T.�������\�g+GaK�����-�Ds��&�fne�g�]D�f���q��y�'���{������#$^'^?	��ohgi�����G���^���5���5���0���������eq����v�8�]��~���<��vv�d�Q�z�w��I��P�a����[{L�,���'���A�� FW?L�����o��#��Q�n����b0���.�%�%#~|�pL���F�A���2��g�X���1M�>4r���-�_�J��*�(�����J�-�F�[����v^��P=k�%,��:e���s=�����y$�WGPJ(��)+��
����:5�T�����m���<�P���:���;�(uj��!,9�7�`I�n��^{P����eV�X�Q�����X�������3t�}ou��Z��	���E(������g��M�>N)B[���xYo���m9e�t���:���p/����5-�h]tN�u���!Er��/{���l2�����:����t�ZH������#�;so@����ua;��C�������1���m��?�^Q�i�t-jn��xm����
1F�(��n�b>T���o�1��Nd���@�C{����49Q�U����K�;F��K�=�$~?�����$��'X���������E �e=m�y���*��fM3TYs
�x���WsI#��_e������j�H(�+�����Cf%���'	o��Q-4l��sS���^���8��"_��m�V
����S�	���A���������0>��&�4Mk��V�~���s�z�������A}%E�]WB���0
|���>H"~�t�<M�<�������T��X^�z[\����P�`�v�T��s��I�HH���-*��}
��n�;8Ru���������y��N��z����K�kwz�_g����Xq��	�?5���Co��D���}L�7�O�����Nv�roG��Y]��������7?g����\��v�24�@O�2��2U��C7���)�<���47�,f������Y�DOXG��U���i)a)��-4��,��i)Ra)([�^�k&�E�r��4�w�w��6��#E��AZ{7(����u��x����qm0�,
W���J<�?���s�#$},��)C
����
��g��KF������y���1�{E��zc�?���F����h��C ��)����w�a����m��+��wl~�����]��
��a#����3��z��w�Uq���U��Yw8Z��Y#����W���ccg�/�u�w?OS_"������\�{����^9�5��?��$'�'U��*��������^������Tf�Lr�0(�0���o�WW4�����i[��T��5��e�i�<���i����(�������*}�I��99=:�}�V>���I���w�4^��K����BAs=W��<�m�<g:y�����,�Kf�X��cj�{m��Zp���,�Yx6���������V�`���k5��s�y�{ro�����m>m��?D��8���'m����0b���
K���������#������8�������s����6����W��� ���k^i�%q�U&:���jj���r7
�;D�6��;�
�#���������7o<b�#G;��QdG�;2�������Lbh�������!��������g!���mTY�N���!����
���x6H 	�Z��
�:)�GC0j��Sf�Vht�������s)���P	 B��|��EL��*s��Y���.���c�|���9r<���������Gxw�Q�Y���k�""n�����7�ui@�/������E�������Io'��J�J_�>��I6����I����y�?t�����Jn��T�S�l|�n�<w�4R�)w�j~ZY��,�-H?�U���!�Q�*��������X{+��O�l��mYf�>�\���}mZ�&�yw���rw"j�i0�����/j�w@����dN�/I�r�H�
\7_e�3���}�:�����/Wu��?T_������!��9Gw��H�?�}���Re�]N1g���)4d!��*���^�e����K���������O�Mnc��{w���
�9=rnwgR���y�y�]I�2�tk����
%�C�}l�3����?�P����M�O�Wj����2W���{a�&v{g����:�v�H}gr��&_�y7���D�����7tp���g��?��������A}��N� j|��/1��������^�����?����������!�>������[�����D���������;:]���@��tw'��l&��h�x�=]�=]�=]��<]5�>tR�9��O�|��Qw_�x?{��w����y��;��*��<�"u�
��5��#�3���<0��R�~ ���ER��L�fSi@�0���}a�|i�~,l��H��>i?�@h6���TB	o�@X�0J[IH�B�Q"cWh6��:���$��g��HH�J�>
����g&!u&��s�yn�O��'0~���	��-�|��@�[/H`�JH
�O�J�	}���|���^"{/�����g& 5�[�Y��{>?�����{��y���e~( �	��/���B����~*�	~*���E�������~���X�3�:�#��E�0�X@� ��@��A&lY�
Hz����$4+!u
�G(��a,�g�H
t���0����6�$�/5+�CQ(L%��E�ba��DX�(�,����=Mb���5�P@�8�C)N�#��<��H$y%�R�V�y��UP"��$�6�2�J�b*�Ci  5P7��X���D�"S	�3W��3IP�_�,�:��c��0�T`y��Gj���f=W=7�Y;���>c~?=7�Q���-F�Gjn�OO=��B�?�>=����x��
�=_`?<_a�x����^���da@A���^�H
R�x� (z��D�x�O��`��`���<�O*$�m���~@��y�+�O���>�@@�8��/E/�xU�7/�g�����<��U��$�:uy��KA�KCa���2��^�����I���Rg����|����w}	J�
�F���pG�}z>�|�'��'l����}�G��y�}��BE�}?���������P {?�x����������a��7_} ��T�@��(���%�2��e@���!]��_t~��W���+����$1�������+ u*�~*�~��,�TS��<3�A�3A^���G�������gDW`a`�����}�#�3�����S�A1�^$	�Gj `������@�'��#5��fCAP��1 R|�Q��+t�H���#5���������W��l���p��>SA�������~� (�����}`���+(�BW���n�#u��<�z���$�e���H�.�~z��J�����:�O%��~�B�Gj���c�1�'����F���f%A1�=a*q(,B������'��B���f���&� (O(�f*<���/�,R&�F���3r�.�`@=��D���H�|������{v�'<�"��._�b�P$��(��*
���"�>���(���(E@/��b��F��L%��D�`o/Ca�� (F���v�tF a�������`jf�.������KyV=�~(��O8��@����G�8�|����S�oa8<��#��)���8�x���W��	�L�P�
�J��������`�	����Y��	�+�@Lx�H"���k`�,��/�����p�g?IP�[�Wu%��OH��g?�0��m	��	�����g?3�{%�a��~(I"��H���?Ad�3Mx�N2��H2AP�e��6u}�s|�@����`z�z����������/�e���M��G�h�LOS	�SW�������!�X����p��5�	F�
��)N�f<yC��P��$5��W27��8pY��I���-? ?�����,��+�G��|~*a�_�Y(���a��:���,�,�y�'��?�$��
\�<k�n�}f�~f����.��b.G�����[F,���i`Yu�H
@�|zn��LY�$�>����g(���{,R����+��X���������f�<3^�F�"�=����p�<��Y��'�AE���Uo��7=����P���E��\V@���b�e?�+�������<R{	��z���HP�y�`���e?���W���z�' ����
�.��'S~?A�����-�������z~H@^�����Cx�����L��!��`z����(��	�
<�n��f <�0��C�<�7=�;9���!���D.Oj�>����� ��� �MO�x���������������g���E��_��' u���[���/�0yV �W�?��U���K���^{<R�1���BAP`��va��#0�3yF4�xS��#�#�_ /(z���@���?���|�!oz
��G�(��(�E����E	�/�����`�@�����>c�� /(z������z����',�����*23�����j�xG	��p���?'���S�����q��'Py���}�#u�]���m"������H��|�� (&�'��F�~�Kq�����	��O��<R'���� (�$��-\9�s�fSOP����L�E�{�_�4�4�,��xLy4��G0��x�L'2����
�b�E��\>�	�c=�#�x�S/����&��
C��f��	O�A��/�,�$���R������e{E���_�L�k�D`/t��`Y�,RC=�� /(�n�,��F|0��<���&��)�}7�������"y�'�.D�0��}>�c�f}�}T�SA���$v��5��b���?��7=����i*���x��y`EL��l���{���y�������~��#�}?�X�������G�~*	����MO���Gj_�/����* ?���:�~��V�-eU]���#u����E�/^��	��)�~���?���MO( 5��S	=^PD�����.l^-�"��
@�������� ����0��f��^X���O�a��-����������E?
z	�l|��O%��M|���#��J�#JyAm.yRe|0?�]a���!d�y���}|�?yA�?�l��z�u��=0���)���H���T2^P�W�o�f�'B�1��}@y�S?	}����o@^P�aiy�.��0�����	��~��<R���g���"�&��	gA��R�����!oK�����p@�	��(�i������������D$~@�����p�y��>����~@�~��">�
�������}��%��)HeRg,@]=�'y}_�
�l4���,p�����i��1������W7������.pY��e��J�� �-`�"u�y|����yA1�wr�Y!�
F�a��N {��.�M����Fcd��������w�`6���
,P�/ oz������5�a�����B0�.PM���(#��������q��f����Gq�P({�y�D��){�~	��#�0"�c�
�l��d<�x^X ��@���:�x����Z�����\1�{-a��ga���P����~���A���&�n��l���o6�4���A�'���~F)/(�`?�.����h�}���Ry��C>~|�+Xq���a�	����T���x�� qy��>@ 	�����$�������J��M0&1�e �H��|~��b�d��f�
�l���y>�=��4�MO�4��� ��
8����m��)H�|�B�� sy�S��x}B��|z� xA��JF��	�(���,��Ipq��C ��10�
{������f��������2-oz����0B�	����0!�M��.�|(]���S|<`7�'>=F���<0��W��_B~�O%����^��������C_H�@^P}��G
}!�M��|����?oz�	���)�_�� ���w�A:e�+`[������W$�=�����z��H�����`6a <��@kX����OD"��1@���t���`�H&�~�)����W�{v�q+�>#�a��?o(�	#�|�5�g���� ��H�<�7/(������i�c_���`��!oz
W+oo�0�}�y��~��fh+������7=���I�����C`[X�H�4�P"�C ���dV��S�O�<*/(�T���S!�M�
�-��|>�P2=�[�G�4��c����!pv�<3�����
��|~����'��G�LH����y>!�b���u]L�8c�?�e��u�k���	3���_��o-#���B�L�r�@	�a1�>��C@i@�iE {��R���,UB K��RB�4�,�E�4O��#��+��2��'�7���)�{��
�@��G��e�o ��T��8�ag�#
H��y^�,�@�=��D ��"��v��������d�z
H���Y$r���Z����Zh�Ji���%C�2%yi���@���%�J������yy���y�y]�-��/�+hn(�g�k�"W0 ��B���� �O��~��������1r��d������5���m�ks����k����(�SB����Gn$���� ���P������Hz� �~@���rJ�_["���EL��
���e1�@�%���g&����/v�����"�	�L�(�+�k(�wT�%��`����������&H ����?�����_�#7������GR<5������V��-<Rc @v�@��O���0���-A(���[�D(��}z��K���	�8�������p<<����"OM�p�O��m��[M����<������	�a����[�E$Jo�y0�g�[�P=����� y��@���RG�%%���6��7��}b$�����)y+Ud��2�D�z*�@)�T��3{����������V���) �`�@AP�R��:���2����-8���x���w��wAQ�� ogah�Y������=����'B$��D�/l\+�f���y_

������$B�����f�����|�BC����v����s/��$(�*?���k��'�_����B�V������|��+�AP���,���h��_�~���P�o��:�?E_� �w@�c1
��u��Ce�\�33�D���<���D����a�@_�?�?���-:.��E���z��R�b6.^�
/f����=�#���(���#t���B�!��)�����g �	� (��?�l"(���������L}_���	�g�� �@��@>C�|

������������]�Pp]���}>����cy�.���@@j4S���"�}���qR(������IQ]"�y��1I&
%A1���5���`�Aa�>�N�t���||���(��M
��	�/a�G@����
���g	���H�.�nV�~bx<�Y_x�V�'�p��x���|� (���,��g$D(�p�`�	���(�#�P����E�@o�^��>J�Xu0������(Q�	�b����0����j�g��h,����|���� ��?|DH��$(���!����R-? !�&"<��q�G���X0h���1���G
@>�i�m"�����[#�����{�X����|B�
�b���p(���'�C@����Q�����<��H�b���(�����a�I~@�`z��|�g���(�E�oV�%)!�|lm
��I�����~����	R�q��	@��xI����LOS���@>�;pJ���
��T���B��G��H0=M%�F~*� (���A ��<�Pp��(����?�/��
��e.�����J&�" ���������I#�AP�>{0����|��(��\#LO���o�	��c�����	�X(�f)�������i���j��g�
��cW�����
@>�O�9c oz��������B��}�|�$��"u�F�������BV�_x`��[��b7$+b�hf����������P,���s��`��O��yA1��y|����j D���0H�����P�����"��t ���`�3�a&a@B(���b���yA1��y�>����1/�@�7=�}!~H�Y2��� ���1�}g��d�
y�p�Y2�	Y2c�����(�/�&'�?��6�,����|���LO��K�!�*yA�>�+���8�����s�������hQ�H��V��b�zA����c$�|�	��c,1~*)���My2���@H��.���B��H����_��/ u����8xA1��������J
���J���5�|��8Lx}<yA1�X��TR�)3>#{��[��/�ehU�(2��hO'�l�q$��0��'H^�`�/3����?�X`?��c��Y�+��8F1jr�f��8B�b�{�A>���~�y���c���	��!y��8��#�=����Aq���y^�	�P��3Ny�S���#u,dF�W����������M�S&<R'!oo�Z�^�@O|�B�k&<�(���I���E<R'��A1uoS!~B|�������)�;�i�����!�i$l������3��4�MO�Tp�':�8�� (f�%��||�8��c��jyR���p!��-�E�y��E|��0�'�Y,��Y"\\���R��6�2^PL\��k6���$.���@^���^�[��
<����=����e\t�����`6	���� oz
���"5y�x ����xB(N��l@��X�f���!�����'�&OP�O%�ELN��$���l��A	�+@%^���0���2����LX�=>�M�/{����'�`z�`�A~@!���P:������!A�~*)��@��x��G�@�/K0�;��Y�n|0�,����J��7=`�#u����%��@^���*<Ri��p�����MO��Gj4*g�z���P�f(�IB!�7���{6Z��x�K/�y�$E�L��IA3Y`������Q\�E�fG>���D/(P�����=��!�x{�$�y��
��������D�p���Ly��CI,��N����6��		jZ�E?�l�8���
�{��C��o6�- ����#�'�v<�f��F�c��}_B4�&<RK�Q�DH]��*��]"�����~&o���7�-��)��R��W��>/(&(��
��a�!>���� oz��q�#u*�J�T���'�f�d����fB�H�k���)������
��	�!��l�5�,0����,�MO1x��Y��K]��>��}�+�I�T-���)��
(u��@>�0<���������"u��!#��WRO���!�S������c��@^�J�l�d����y��n����)��R��@�;�OH}�_�c��`*4+��a4~*~����G||�s���
���M�y4�3�_�25	�1+;���E���G0�`6i
�b�` ���L����()&%�����i(�IC/��8 ���i
b-��a��3N���^��"�0�/Wf���v����"oz�P<RG�,/(��7I#!�M�.�|���aL(���D<R�
�|Q���j�,l�|08!��hT���4���(����h�����`�L�G�8���4��x�MO���������J�"z��"&P>�
�����~@1/J� �H
�#��'r�l���&E.��
��|�oz�}��:2�a�S�|b�T���fg����L�?@��Z��	i���1�,�E�j�oY&�I3��f�	O
���)HeR��f����p}f����������W�����7=~����O���B+@!��!s�`6��F���t��LO3j��������1��@���`���0���yoz��.���|!=F���������Sf������^�'��)�����9��g@�� ��,��`6Y'��3���Y���@4�b>=F$����t��Y ���&G��e\�%�ey2`Mx�Fa�oV�0��?3��y���~���)����"�4
,���c`�V����e�3)�MI�3��x�,-+�����P���(��,vy��,��`6�&�6�2��g�G��	���{%NxA1CsDv�b!�M�H�'���@�7=��~��<��D������F������h%�CI�������f ;�H��|zxA1KC>F�
�l@���$E��J��2���2�O��e/(fe�o6��C����J�E|~��w��(�#u&�����1���B���������L"�?=�&��L��d:pf���]u�����M�g[�>U}���e����p��Nry9�o9�q?w.���N/M�;����� T�]gN1��������vg���X�[��Dw������y�l2����t1�cnP�������'�s�>����y�<��f�
����m�j8pc����h�C�f��q�l��������%h
��:����^9��O��R����u�,�pg����:��3�:�/�&����:��g����;�7�'�'�v��{xt��'��w{��Y_*��&���{�a8R0{;���qos���{�vN�/_���i��Fk��w���y�U�c1�������J����~y�P'�S���z�<����
�C��Qm��Uq�L=�>�kC�r�;�v��]	�4��5���S���F�e*]�c��
x����������i�fn�j��n�L�2�5�@�=��3g{���������`��|6{p��vG�|���/����o�n�H���O����$���%��#���������sv���D���$8hY�����~U�@)9�g�>3�	4�����U�����w�F�`��J7tS���_�����wZ�>9�J�����l{{�~���/��[����j���������D�������	!'=����t��INc�J������a����e�m��z���cu�n�Q8�*���yL
0~�'MMGZ��8$]����s��F��Gu�7�����>2SA�A�������	������,��D�m��8����2O�N�&�����c=T���jD����{������D����������<A?��f�����!�����������K{NF^�n����B�Z��5���5�M|Ds�����?�;�T���/��U����M�;	}m1P������;���G�����C���K�6�
�����]�!��YH
������3�!�����P7�j�k_�+(�!)�}(�=
|�t���F�D�f08��u��'l^���<�>� �H��i����
Q�NH��(?���qUM�w�u�2������7\���� �������.^0�G�BR�NYO�Sg�B���X�ACF!�-��|���c�8����4��������Z���V�z�3�1�R��[�������8~szxt���������LE^�kEaD������g��}H�����W'4����W���!�����������&������Xm��s�"a_Md�hG7�������3���^`+-C=��*W�����j�O��}����l�4��r��?b
��/���	`�?^����"���������������L��OZ<ur�H�Tg����+���DB�O_��o���X���x�>�`<:����S�z8�zx�����T_�Ru���8>u��h?3J=}���>��+Y���|���_sQ��Kr._S�G�����T��m���'w����c0��������<�7[������&�#�b�a����[���l�Dmj�
zSC����iL���lj�3j^�|��(��A���in������\L�}���:�Y�Ge{�e�������]y ZY�@��{���[5"Zn���#���$|0���n�?�9?9�"<	��V�]wuu�~��g$�_�����������������+��l�>��!������U���n���O����/���gn��M�_%&|� �U��[���Et��g��v�av��u��w�P���D+��H�zCG���oXLD�1Z�u��@���*�o��f�~��.]�g�����|V�����?p4����`1��wn�������DO���yc��G�&?\h��a��)�V��_oT*���������j���f�`%Ej/�8����q-����q+��5����w����#d��
��U�wso����W&���jv�_�{�ngW5Z�F��o���gO�$�"��c&w���������o�p�r�����vo���kx�78h�;���Aw����z^�S�����3��S��c��j�vV��cu8MBR��w�<~�����/������{���KR!�>'�|_�1l`�j�?�m<����){��y��5t��zsxu��j6w���+FKN���E�M��R9E������t���)�\7��-d��{��r�D���\S���dF>�x�z=�Fa����m����G�k���������[j����J>�Oh����~B�����:�I���~��0�u���h���/hx�- ���Z:Ly��������D�#���C��9��i�}M���;�U����'��;
���^>������K~s����}�����>*|All��2�d����`�����g��]���#p0�Y���R��h7�I��9d{��������
�djq ����%�X���V���N��]��1�0����E�[����,f�,�����&����ov{���A���w��6��������,dYd 		��I8�������E�L��;���2SV�t�lq����p��qEU���/^�\\�G��^D\����0��c�g�#5��L�d��$�Z�m ����R[[[����'�Mf��P��i�W�?�x�v~�4�t�ve���;w:�rKw!�����!�{����-��Y5���C��>\���Q�/������?]��(���������'G��1��5
���l����|����%��������_��%u���Cc�H��� \���6�����,�1[:��ZFJ�m�Qe[=R�bU��H��2o6�Nb${E�x�$1hI�C�����s�=���K�I��g7���F
��Z�
E�}��1�7{0�d�G�!F���T]�~u�� �o�Z�6O^V�Oqb�[�2��m�_o�^_����C��������Y'H�����/��z�[���/�^��o��D,���������I��I0-<�'�8�1$�[���^4�S�,�{��s��� ��K���~�G��~�J5�{�EC���9�?'��zv��3�N|5��G�_e���<8V�]��<�<2���y�#p6x�{�1&�~���{o���Y/�0��|��HC|�H���7��8 �d>�lc�D��|���f�]^����l�D@Z\��0HF�:���S?���as�	^��@������b�5�c���zF�G���7��}�7����N.����-,���������J��u��0�,�����kn�g�'����
�������:~{u��
F��wAVx�6f��o��o��p����`*��;����������1���
�^�]�a���g����y28�d�@gU�������GEs�[��I4g$��Aw������&H���p�*��^�y��B	6qyu���y5��E�W:9��b:#it�m��r�v�*��w���[�~�������z�%�"l)	g����)����,��k�k�������%�s���a������CB�7I�6��^#9Fs5^�g��z����8�p[������o�
?[����`j�-�BX����<E�WUH������!N(=�������On($����-=�����1Z�Y0��8�(����uB�}��t8e�+�������Yr��dK�j�����
;z�J�o�^0e"��#��������GW�w%�
��u���@���J��Gb���0���������4 ������#(?�9	]�!��m:[6W�jG�1q��J#7�w��4�R��������"���xS�D�*|M}�kNc$�h���kLO	l9� �O:�izz�6�AoXu�H���KmwZ`��o��.^n�����������1��S��N���y���t��:��b>�qVU#��#�67Edn�?�VTiw����������6������5��5�R�$�f��������,u%���^����at��x`����l�s?f���R� k�����kX�kb\�>������|�M���G8�[d~������C�\�	k��Q��$1���`�F��!�'��`{�ufb��8O$v\�p�U1F��D�)��lt����~J�MH�����>{�����C����6�m�!�r~JlX8#^Sb3��1��m9����P���"�9��c�'�������l;�.��N��#�8��V�e0��'g���
<��
����t�e �����b�&-��q��#4�������F����\^]�"~?�=?g�m�
E��1"[pY��,���H�>�l�`T���S8�@Q�S���n:�\���H
Zdf�.�����JA���l��(�����8c%�V��x�7��!�BVv���7MX g��J�d�������m�bu�6���e�%t��U��1�a��R����Z�$��x���l�g�!�O��%'s\��]BL�%N�*+2���6J������)�-�X2�\���j)G��-��A9���r��������x|tz~y�-M�rj�2^�j,�>�9%�5x��
���.���m�z \��4���\0_�L5B��yR�������}�����x�o�����;�������Z�sB,0�����=���������t1��j��FE��KQ^>ffQ��l���G����bo?����"U:Z��%��z���(_���������_��dW8S80W����l�Zkb	
���+���_Ag�������v���T����x��{��7�o/�Y�/�i�a_����d+p����E���f�jIC^��2.���3h��e+S��
��`�]�U��!�'J�l��������v�z��78�k�O�y<��������8�j�qbN��	�2�y��5��!���S�}b���#�Mo��Xy���A�}��:��$�y����hV�1�{����nw=��Z��}�K�r	;M9����q@�3�c2g���7/�=�?�����4���D��H��|�l���)������������N��F�|�8���N��_�=�J�H�6����N��PFb�:m�yd~$W�.,�=.w�.O��+f��7wR.\����bW�]36b�?�S,���Q��!�N����z�^��
�=h��d�����n�3�3�3��Z�O��������
s���xl\B��.��E0]�2N���{�2��w��f�����~S����F�����+��Vv���+��>�q�b�v*�^�
��7Q���}�����>�'��Q]BTd�������3��o^�_���0h����bZ.���suL&�y���������.x�8T��7��!��!l&�6�7iV�����:�?����q����2�����m{��A�^�v���}��+A�;zs����B���4����cM�D8���)�\����<o�wk���3�(�K����h��5�<A�1�p�B�"������T���/��VO��*�����.v E������)��'����#R�s��������
�I��9<v���&a��o�U��B�/Ob��}o���P����sni�m��W�8	_W����9N	������Hnv@���>+�\X �%S�<>/��R���s��~F���$s"��X�O���i�O�����7���z�rw�����nu_V�	�S�\����KF���z�-��G��;AN�2�&[�Zkk�
!Xq ��GV�E�����b-���mk��O)��Gn�.�.���U�������?K��&x�:��?��?�4Nc$K��-~UC�����h��1��K�GX��[��p�������^��O���6���2�bi?|��
�r�)���G�X��[�������^o����o.�
�5��`
�����w��$F�gKb
#F���j���������P��Ze��:��40�CI)O#W��,��(+2�����&���H�[0�i����
���VB���9 Z������A���w	zy?K��i��b��!Y�_�(��_���,��d_^d��y"��9���w&�KO���1S��WB���%^������_v�\�k�N����f+7,!�������W2�Q�_�bxCMm{
��yPI��y2����L$��p��j����}��v���98�[��tSM)B��Kg�(�f:��t��99��vb��lvYB�
8
����BL}n�I��d�R�(42���?�v�Vk�^�����}��;����R�,6]�Xe���bo�t=�O��1To��Y�99�����%����Uo�^_��1t�Dyq����di�uQ���gj�j����N"�M)0������za�F
�~��&@�}����"��x"A}�ZF�yqVlJ�<���g�c�%
�R�l�Q�ib?�&hq�b���	�Zbme�';K�3��;��4*��gW��D�#�o�O9�Q������R/����������=��������:^k�=�]��e=-bvY+��{��B�r�b�h�MH�~�6��z���U���-�h��q�����x�c�o�?U�!��o����:����*�o%�(���� �������%�S�MRidS\��IA�@����
��xP����2�V���}��
��.��X���#��������&�DM� {��%1(�*�	6K'����e�s���5��A�*��QK�s�����%�W�^k?����>�>56+q�	���[vv���Mq�m���n�A������G��,���lS���Ji�"Y�~H��M������)?��yS����'�?��C��q�������a>�s5b�RKv� ��?M���?v�����Z ����_X���������}d��A��~���=���{p����^���{}��{���FAUu�K�G���j<�=x��[Q���wEX��T9�dq��{�Ryi�r���vK��}�o��u�s������B���ICq�z�`~�[��@��7M��7��AX������8�/.����&�������(If���9z��xg�stx�g�o�/[���8��\��?;�w�
�N������\w���������C{>�1���o:����C�o?�H9ZW����d�C��a����h�*��H��	�$��l���E��ZV�����P��	�F:�*+��k���7s}�lXS.��q�D�?���.vC��Y��I�z��$*�$��..JE�-�h�A���'](��t�{K_Y3�����A�I��`������w����.��X�������7�����7U6����n��c�/�$!�S9����P?�\}��_O*f��@S �g��b� ����$�}3A���U�?[��"��q�u���y����_�}��|n/Z~��h�]B#y^l�0���Z�sq�>��[i7���Wz���YL<��>��*�MJ=n@�h�����$igq(g�t�l����r�����dK�����v)�'A���4��k��3���3q$������k�A:�<YG�)_��K�!�q�_�h�F���
,���i�l<��O�n�(����@D�L��A�I(iH��;���"k/����iB�Oxc�U\��)�!{N%q��������VR������!p�;�/���x�K�p[�T.u�[ym7�,D�Na���Q���~B�����s��,�;��8���x���3E�j�ejv�{�$,N�B�v����/x�Lp��U,N7�+��(��`Yo��b��c��Q$Mp52TFO���[�k�Rf���6<���c�1�*=�&��Fw���S3� �Y	��}�6_����>����"�9���,���%�5{�q��A�s�k����x���<S.m�������4e��3��-�EU����
7iy�o��������A:�
Q[��D��@��8�5;K`d-M�����R�z����ht�|������%�6�����m��cE�L&���5?��v/��X"-�vZ�VT�i���eB���TV9�(+?�9�_��M�ijT�neP���V\$^Gq5Ez7i����s�����)��������5���50�;�M!���3J��m�s�b~%��f��.E��/�*}.�����w����g�}��c���?��	g#/�H��KpyU@���d�a��z�a�R��&T���QV��������������C���W@��u1�<��O�+���I�]�N�������2��*bt��j�5h�e;�j=MF�R�W�v7p@��eN���<��)8�gOW��G��]��n�^�����0LHx�%K��� W5]!��$g���/���$��El���Em�|D��t�>%>�t��s���T/���4JVy�'P���"P���`��\T~x3�9ko����d�p������4/��Oe���b�U�D��i���F���rY�@]�������T����z���O��%�w����$�I��W/q\���+0��4� E~.@�WM�*����;oyT�|��c�F�,ou"�����A]�|�?�����a�I~O0q���7��\&g��}1���Q{_��.xS�5��j�����^*T�)��8�>p�;2~o,�������w���S���v�/��k�`�p���\�a �S��0�""}�4zH�������.1�k�da&.R����	�(���qJ�A�h�H����?�7#S�K�;�
�����d}�M ���4�b9)����x�1��*b��l7(�k�����;d�����5�%	NSLq{l;'p8�#<�|&���	�i��y�D�4u��n�1���X �YS�=�N6���c1o;3k�{
���P��e	q��0s0po9'���^,>�I��?FS�2iKK��V
�1��~�����,�#�7���^
�)��$�J��y)�i��x�T�s;����x����!��E���|U���J��_����f_��4�2}����U��JU�jv{���9Ez�I�?�u���K/��N2�r�id<�+Th��
%Z:qU���*�c��j������}�7�n��t�m���6V� Kz*�B������EJ�W�3��%��"�Qe�%�<������>x����zAHBE����\���^�"H��)=-��S�|���{w40[L�e�o���������t��[��Q�/6b��e�������$:=*��b|�8�G}m�����������=���_��kp����?���:~���W���p>F��\��}h^�}e�����9U��mw0�O���������2�������A��e�J?�W%���_��*�^�c������,YU�o�����hw�ViJ�=r��8g��K���vv���P��u�)<E�Hzr��>�;��
�O����{���;����
8���&��F�p����B����;���'bc���t>y�|�������<�T�g��r_0�pTH6?x��c%��}j���!�����=5�@5�b�q���k�r�^uJ|�=����QF����%���t�	����pq:��������H�:
�OE��"�^�l����d��A��$�%������z���I
�J����X���6���6���A�b���SO^��k�|w@���V�
��es������<���x� �G�Cy�zH��P�u�t�/Ow�v�p����2.������&S?�Xm����r�I��2+��Q��<�m~B�D�<����������|8d:��&������xA^0{`t%�Nl7�!�OL�p����u������t�����{(����0S}��zG���s�,va��u>�%���h'6�*W|�,om�$V��X"<C	[��g7��:�]8Ofs�p3�g�A��<O�5�c��L[��;�������q�z�����U��9jfnv!g)��X���1K0��@���g\�x&��k�Et��0���u)H		�����m���C�^T�-�n	���������
���~�J�l1}<������������x�@FY!�\�Rq�.��T=O�� zc�h���S�s��0�p:9�0}�j��������_W�8�������t�flz��f{�pc''��qj�W�N���5�4ef,��i;��b)���^	:"N=�xO��|�@�����W��������j�|�x�Z<_-���W��������_k��y�����(���O�+
���=��k5�vW�6���[:n��$�����(���Q���������^�?g
�m`����r��d��l.)����Y�v����^��ww{�^K������d�r�%��d}��I�	q'������+�E���M�]�R5�[t�\�Kvv�%��8"�.\���N��9U�����(���,%�F��@{^6������M�0���(��U��:X�.���Xl���I�|�����M�c��8�������8�o�Q+�jA�X�L�NC���~��"LP�t�|2qju;m3��hq[�}��z.�&)�����mm��+�B�&����6'z?��,�_"��d;������ebu|�^.
��m��I���A�O����I-���R7.���?�_~kac��}\q������i5w�`B�K	��P7V��{�W��t_dC���mg����\l�)��8:��������&���a���r���d��*�{�����	�����$�S��0�Bg��%V��$^,N�'����6���=��#��@�T���_J���vj=0^�����z�p4Z���;fz������k�hB�����}�>��G�M���A.	JFd��*�66�LL���M�����A�\e;��7�:��#�	K�|\�:~��[�u\��Ng zE�c	�	Z�,dsk�,&H���F����+����x�I��G(���y��	a��n�~Af��Q�7��^Mh�fz�^��m:D/��l�`�'!���4����ml��9<_�[�:��8���lIz����f4�
���n��l 0
i�����?M��;C&O2���;�"�0R���ZH-��#l�g���icc2�u��'=���_�n��4�s0@)������H�}���9X�4w��x���:s�y���c���i_�!-n1i��4�'���8��u���2�uz���_����*�H�z1��MK���Y����u�$�0~��L�7�^^f�����`���"S�n!P��N�
�[e=�����L?�Y��0'��9���9�E!k	�7��8�oN���m��w���a���`��*S	G�+�g����!���D0��~z:�Z���	���g�'�Y�?�����G7xA',��/�R���<er��IaN;�	�����5\��R���Q�{�������C������������SR���$E���!V�[]����vv��[g���k4T���l~�������o�������z����^�'�j�fo�����78�w�������[#���beI����d����&X�TXO�Z�K��>1�4������n������n���~�2m�C5 X�p�A��.�d����N���p���H�����~;��/_s���;*�q�2���;�����!���*�6kv������k����r�%^�TKo�r{��A|o�7h���}��s�]���C�;��6b��K?��<�BL��Az�E�M���Dy"��L5h�0q�JVZK�;;����ZZF'3�����-�U��>g������F����)&[�Hd�M�|���XEl�}dF2�weV���br��������#�U��|�L�k����W�I�bb�*��a�	Z�������Zul���K\��������Q,���c�N�RP��/��i�m�r�����m��,�
lz�U\���sP��I�Lb��qn\��M+���������Yo>��"���Q�X#���}e�r��'���M����mevM�4������0=��HJ7�4�e�NS3i���)���O�q�'����|>
��dL��qzfPC3�
���2�?�������I9�*^PoRwC�����0 R �x��C�,�}�`���Z�SP'M[���%���=.�a5W�����R$�'2�|����H�W��4�6��I�A=9�]xG"Q����r������N���bd��8~�N$s��i�|D��8��Dx?�Yp�{g�FF��@-�a�o4�����K���V��p}�T�q��R�Wg�c�f<k�>�������|t��5 9��M�$	+�Sg����-g|E��'p`�@@�H��M����s,WZ��
���������a��� �pp6�f#��u�Z d�_9�3@��������?6m���o��/������@�FvL���](��Z4\p@K���x����q�<i������,�;�fI�[��u������c���R A*{�:[SI��t��4w_�N�3R;�o�����]���1(��H:�������
�}�.i"�O��s�y3
]��.i2�Px��v�R��z��h��M�V������U� \��T>����-���mmH���k�(/��e���iH��I��O��.�Hl	�4����Q��Z�m_�XT�|��v�M�3i��KCH��
����]����%F��T�	���IV4�O �U����ES��Q���UqB�`8J������e^$�n�}�&S�\��F�#0qh���Y�l���%"QB�!c�p)o��PY��a\x)�l9P�~ c/f�Bl�/Y�G�zDy��'>��`6��k`S�K���F�X����
��Q&(;a�E�VkN�$�g����<��m�~]�^���<�����:*�'��`��B;0 �r�l^��b���{���k��=i��p,�I+�0�"�\�!% �����-.��L��h
u"�S��C����U��6�y���U��N6bQy��n�������l�v��h�-qt)
�`�QZ�����1�a�B`r���&����[����}Sf
�C�h7��pe������L�S��e��`�i�J0��!�M�6�`^s�r��NxO��+���x��+�(���sIc��B�5���f��Y�qx���#E��y���.����'A�\�."^����f�Zf8��c^�����������V�C��Je^��t�\�t�)�����S\�j��^,�R�����z1p{8Qg;W	�.x�9d\����
�h������v���R[� ����*����m<�=�s�rL��Ol)h�Et��,g�n����Q�,�Q���|���Z�����Z�4	�N�;)�o�X��!|��2��Q�x���a������KE������M��������B�CVe�aKe{l���G-��Tk0���*�My�L�������t����;U���3�e� qAn���	\����/�����rc}O�i-��B$�������jY�k��J�(&`��G��4�Q��6��ZY�ej�
�����l�:#�E��Mz���������y�������M�#RY�F�v��KL3��Uv�������Ne;t�k�������'�g��H[+=�%8����Se��&|�0� r��!x��A�����"q����S��6�4�� |�������� `&��5q�]!Y>�#�P���bU�7al2�r��������h�x�F��@�E,q���eM�a�OE=�N2���M�j�LH����B���,
O����zdC*�L�d^
�����������(�[�tp1��md��.�V��G���X~��&�f�/�=;�M���l;8
?��?'�A��d�����%b���������ar��#��"Yc����%�@7���d0Si������or��������iPe�zP������oAz;����e^�����v/`�e@�tX�1�q�S�������q���fm�bc_��r���H��Ux���2��Z�(�h�oI���F4I����D��}�����}bqm��qv��,�������������/��1��=S��k�`1��rh�������� �%�L�CE�HH�j�>�F���:���M���"��?��IO6g0�O8�M����"������}k	0u�h��v�,��5mf��������uA{q�����*�)�K�8'FE0�����h��W��i�I.��M�:z1�aIc��1�i�S��0�_P�q� �]ypx�8
������UTHn����
���j���19�����>���%�2��7�}�<oV�]�e�_���)u=}�0�i�){I����Fd���:S�-Hi���OLeN�XX*���G���N'���+Y��Dw�M��a����Wx�G��U�$
��Iq3����t[#��������
$���=<$�o�|&�%0�������z$�|�7�4�Pr3T^�+��K��!7�T+��M���Y���rx�i+&&i��b;�4<o�<��o�0107��2{�:�M1�c�������#�	����LYJ��D�G��c��F�!z
[�s���8P��*�,�F�Z��\������yQ��f�+si��D���c�ii�Dj����U�\��7u�q�%���-�1/�q(�����4�f����V�^���n�����_�zUO��e�8��Qm"��a������67(8,��x>#���O��D��9}�����
����D�@ u��\�����o8E�7{������xP���&��|�4u�cQ'��c���E�s�1�V�d���W�H�����YB���R���!Fp��W�����`@����0���0��Hc0��-����hr>e�?&/o��2g�e��?�Sgb�����l��p[va��IN�c��x
�$���$6���P������'���%/��Xe�f$��}>���Byfh�H���D��AY�C�{,��S�Ny����X|��X������9�+v�R014IY1'��z��wD5�)�X��x�n<\���0�M��6����	j�Ab�<8;6���0�|a�C�>��q��/�t���c���\U�����t�A�T�^��I��3W�c��9��5�uu����bf\������=N|���fV��u�l����af�6l��z`0
��Ei���]6�C"1������FK���~� J�\����������V��<��e]���X�%="�DG51_�v����q<hL`t������������ata�������/����V��������7W�A������ �cs����Mx:t�^�j�jSP]�kk,�u,z�c7��n��.to��
't&�P����|��2%��<B���S6Xq�:�A���e���z����7&����WT��kk�,0F�3QO	����p�,�l������|g��)��l�O���Ks��"4���r�:���ffgg�ET������{��%���**�W������{��������/���|�Q#�
#325Bruce Momjian
bruce@momjian.us
In reply to: Antonin Houska (#324)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Jan 29, 2021 at 06:30:15PM +0100, Antonin Houska wrote:

Antonin Houska <ah@cybertec.at> wrote:

Well, on repeated run of the test I could also hit the first one. I could fix
it and will post a new version of the patch (along with some other small
changes) this week.

Attached is the next version. Changes done:

Yikes, this patch is 23k lines, and most of it looks like added lines of
code. Is this size expected?

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

The usefulness of a cup is in its emptiness, Bruce Lee

#326Antonin Houska
ah@cybertec.at
In reply to: Bruce Momjian (#325)
Re: POC: Cleaning up orphaned files using undo logs

Bruce Momjian <bruce@momjian.us> wrote:

On Fri, Jan 29, 2021 at 06:30:15PM +0100, Antonin Houska wrote:

Antonin Houska <ah@cybertec.at> wrote:

Well, on repeated run of the test I could also hit the first one. I could fix
it and will post a new version of the patch (along with some other small
changes) this week.

Attached is the next version. Changes done:

Yikes, this patch is 23k lines, and most of it looks like added lines of
code. Is this size expected?

Yes, earlier versions of this patch, e.g. [1]/messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com, were of comparable size. It's
not really an "ordinary patch".

[1]: /messages/by-id/CA+hUKG+MpzRsZFE7ChhRq-Br5VYYi6mafVQ73Af7ahioWo5o8w@mail.gmail.com

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#327tsunakawa.takay@fujitsu.com
tsunakawa.takay@fujitsu.com
In reply to: Antonin Houska (#326)
RE: POC: Cleaning up orphaned files using undo logs

From: Antonin Houska <ah@cybertec.at>

not really an "ordinary patch".

[1]
/messages/by-id/CA+hUKG+MpzRsZFE7ChhR
q-Br5VYYi6mafVQ73Af7ahioWo5o8w%40mail.gmail.com

I'm a bit interested in zheap-related topics. I'm reading this discussion to see what I can do. (But this thread is too long... there are still 13,000 lines out of 45,000 lines.)

What's the latest patch set to look at to achieve the undo infrastructure and its would-be first user, orphan file cleanup? As far as I've read, multiple people posted multiple patch sets, and I don't see how they are related.

Regards
Takayuki Tsunakawa

#328Amit Kapila
amit.kapila16@gmail.com
In reply to: tsunakawa.takay@fujitsu.com (#327)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Feb 3, 2021 at 2:45 PM tsunakawa.takay@fujitsu.com
<tsunakawa.takay@fujitsu.com> wrote:

From: Antonin Houska <ah@cybertec.at>

not really an "ordinary patch".

[1]
/messages/by-id/CA+hUKG+MpzRsZFE7ChhR
q-Br5VYYi6mafVQ73Af7ahioWo5o8w%40mail.gmail.com

I'm a bit interested in zheap-related topics. I'm reading this discussion to see what I can do. (But this thread is too long... there are still 13,000 lines out of 45,000 lines.)

What's the latest patch set to look at to achieve the undo infrastructure and its would-be first user, orphan file cleanup? As far as I've read, multiple people posted multiple patch sets, and I don't see how they are related.

I feel it is good to start with the latest patch-set posted by Antonin [1]/messages/by-id/87363.1611941415@antos.

[1]: /messages/by-id/87363.1611941415@antos

--
With Regards,
Amit Kapila.

#329tsunakawa.takay@fujitsu.com
tsunakawa.takay@fujitsu.com
In reply to: Amit Kapila (#328)
RE: POC: Cleaning up orphaned files using undo logs

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

(1)
+ <indexterm><primary>tablespace</primary><secondary>temporary</secondary></indexterm>

temporary -> undo

(2)
      <term><varname>undo_tablespaces</varname> (<type>string</type>)
+
...
+        The value is a list of names of tablespaces.  When there is more than
+        one name in the list, <productname>PostgreSQL</productname> chooses an
+        arbitrary one.  If the name doesn't correspond to an existing
+        tablespace, the next name is tried, and so on until all names have
+        been tried.  If no valid tablespace is specified, an error is raised.
+        The validation of the name doesn't happen until the first attempt to
+        write undo data.

CREATE privilege needs to be mentioned like temp_tablespaces.

(3)
+        The variable can only be changed before the first statement is
+        executed in a transaction.

Does it include any first statement that doesn't emit undo?

(4)
+      <entry>One row for each undo log, showing current pointers,
+       transactions and backends.
+       See <xref linkend="pg-stat-undo-logs-view"/> for details.

I think this can just be written like "showing usage information about the undo log" just like other statistics views. That way, we can avoid having to modify this sentence when we want to change the content of the view later.

(5)
+     <entry><structfield>discard</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location of the oldest data in this undo log.</entry>

The name does not match the description intuitively. Maybe "oldest"?

BTW, how does this information help users? (I don't mean to say we shouldn't output information that users cannot interpret; other DBMSs output such information probably for technical support staff.)

(6)
+     <entry><structfield>insert</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location where the next data will be written in this undo
+      log.</entry>
...
+     <entry><structfield>end</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location one byte past the end of the allocated physical storage
+      backing this undo log.</entry>

Again, how can these be used? If they are useful to calculate the amount of used space, shouldn't they be bigint?

(7)
@@ -65,7 +65,7 @@
        <structfield>smgrid</structfield> <type>integer</type>
         </para>
         <para>
-         Block storage manager ID.  0 for regular relation data.</entry>
+         Block storage manager ID.  0 for regular relation data.
         </para></entry>
      </row>

I guess this change is mistakenly included?

(8)
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
@@ -216,6 +216,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgtesttiming       SYSTEM "pgtesttiming.sgml">
 <!ENTITY pgupgrade          SYSTEM "pgupgrade.sgml">
 <!ENTITY pgwaldump          SYSTEM "pg_waldump.sgml">
+<!ENTITY pgundodump         SYSTEM "pg_undo_dump.sgml">
 <!ENTITY postgres           SYSTEM "postgres-ref.sgml">

@@ -286,6 +286,7 @@
&pgtesttiming;
&pgupgrade;
&pgwaldump;
+ &pgundodump;
&postgres;

It looks like this list needs to be ordered alphabetically. So, the new line is better placed between pg_test_timing and pg_upgrade?

(9)
I don't want to be disliked because of being picky, but maybe pg_undo_dump should be pg_undodump. Existing commands don't use '_' to separate words after pg_, except for pg_test_fsync and pg_test_timing.

(10)
+   This utility can only be run by the user who installed the server, because
+   it requires read-only access to the data directory.

I guess you copied this from pg_waldump or pg_resetwal, but I'm afraid this should be as follows, which is an excerpt from pg_controldata's page. (The pages for pg_waldump and pg_resetwal should be fixed in a separate thread.)

This utility can only be run by the user who initialized the cluster because it requires read access to the data directory.

(11)
+    The <option>-m</option> option cannot be used if
+    either <option>-c</option> or <option>-l</option> is used.

-l -> -r
Or, why don't we align option characters with pg_waldump? pg_waldump uses -r to filter by rmgr. pg_undodump can output record contents by default like pg_waldump. Considering pg_dump and pg_dumpall also output all data by default, that seems how PostgreSQL commands behave.

(12)
+ <arg choice="opt"><option>startseg</option><arg choice="opt"><option>endseg</option></arg></arg>

startseg and endseg are not described.

(13)
+Undo files backing undo logs in the default tablespace are stored under
...
+Undo log files contain standard page headers as described in the next section,

Fluctuations in expressions can be seen: undo file and undo log file. I think the following "undo data file" fits best. What do you think?

+      <entry><literal>UndoFileRead</literal></entry>
+      <entry>Waiting for a read from an undo data file.</entry>
(14)
+Undo data exists in a 64-bit address space divided into 2^34 undo
+logs, each with a theoretical capacity of 1TB.  The first time a
+backend writes undo, it attaches to an existing undo log whose
+capacity is not yet exhausted and which is not currently being used by
+any other backend; or if no suitable undo log already exists, it
+creates a new one.  To avoid wasting space, each undo log is further
+divided into 1MB segment files, so that segments which are no longer
+needed can be removed (possibly recycling the underlying file by
+renaming it) and segments which are not yet needed do not need to be
+physically created on disk.  An undo segment file has a name like
+<filename>000004.0001200000</filename>, where
+<filename>000004</filename> is the undo log number and
+<filename>0001200000</filename> is the offset of the first byte
+held in the file.

The number of undo logs is not 2^34 but 2^24 (2^64 - 2^40 (1 TB)).

(15) src/backend/access/undo/README
\ No newline at end of file

Let's add a newline.

Regards
Takayuki Tsunakawa

#330Antonin Houska
ah@cybertec.at
In reply to: tsunakawa.takay@fujitsu.com (#329)
Re: POC: Cleaning up orphaned files using undo logs

tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com> wrote:

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

Thanks. I will check your comments when I'll be preparing the next version of
the patch.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#331Antonin Houska
ah@cybertec.at
In reply to: tsunakawa.takay@fujitsu.com (#329)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com> wrote:

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

Thanks.

(1)
+ <indexterm><primary>tablespace</primary><secondary>temporary</secondary></indexterm>

temporary -> undo

Fixed.

(2)
<term><varname>undo_tablespaces</varname> (<type>string</type>)
+
...
+        The value is a list of names of tablespaces.  When there is more than
+        one name in the list, <productname>PostgreSQL</productname> chooses an
+        arbitrary one.  If the name doesn't correspond to an existing
+        tablespace, the next name is tried, and so on until all names have
+        been tried.  If no valid tablespace is specified, an error is raised.
+        The validation of the name doesn't happen until the first attempt to
+        write undo data.

CREATE privilege needs to be mentioned like temp_tablespaces.

Fixed.

(3)
+        The variable can only be changed before the first statement is
+        executed in a transaction.

Does it include any first statement that doesn't emit undo?

Yes, it does. As soon as XID is assigned, the variable can no longer be set.

(4)
+      <entry>One row for each undo log, showing current pointers,
+       transactions and backends.
+       See <xref linkend="pg-stat-undo-logs-view"/> for details.

I think this can just be written like "showing usage information about the
undo log" just like other statistics views. That way, we can avoid having
to modify this sentence when we want to change the content of the view
later.

Done.

(5)
+     <entry><structfield>discard</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location of the oldest data in this undo log.</entry>

The name does not match the description intuitively. Maybe "oldest"?

Discarding of the undo log is an important term used in the code.

BTW, how does this information help users? (I don't mean to say we
shouldn't output information that users cannot interpret; other DBMSs output
such information probably for technical support staff.)

It's for DBA rather than a user. The value indicates whether discarding is
working well or if it's blocked for some reason. If the latter happens, the
undo log can pile up and consume too much disk space.

(6)
+     <entry><structfield>insert</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location where the next data will be written in this undo
+      log.</entry>
...
+     <entry><structfield>end</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Location one byte past the end of the allocated physical storage
+      backing this undo log.</entry>

Again, how can these be used? If they are useful to calculate the amount of used space, shouldn't they be bigint?

bigint is signed, so it cannot express 64-bit number. I think this deserves a
new SQL type for the undo pointer, like pg_lsn for XLOG.

(7)
@@ -65,7 +65,7 @@
<structfield>smgrid</structfield> <type>integer</type>
</para>
<para>
-         Block storage manager ID.  0 for regular relation data.</entry>
+         Block storage manager ID.  0 for regular relation data.
</para></entry>
</row>

I guess this change is mistakenly included?

Fixed.

(8)
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
@@ -216,6 +216,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY pgtesttiming       SYSTEM "pgtesttiming.sgml">
<!ENTITY pgupgrade          SYSTEM "pgupgrade.sgml">
<!ENTITY pgwaldump          SYSTEM "pg_waldump.sgml">
+<!ENTITY pgundodump         SYSTEM "pg_undo_dump.sgml">
<!ENTITY postgres           SYSTEM "postgres-ref.sgml">

@@ -286,6 +286,7 @@
&pgtesttiming;
&pgupgrade;
&pgwaldump;
+ &pgundodump;
&postgres;

It looks like this list needs to be ordered alphabetically. So, the new line is better placed between pg_test_timing and pg_upgrade?

Fixed.

(9)
I don't want to be disliked because of being picky, but maybe pg_undo_dump should be pg_undodump. Existing commands don't use '_' to separate words after pg_, except for pg_test_fsync and pg_test_timing.

Done.

(10)
+   This utility can only be run by the user who installed the server, because
+   it requires read-only access to the data directory.

I guess you copied this from pg_waldump or pg_resetwal, but I'm afraid this should be as follows, which is an excerpt from pg_controldata's page. (The pages for pg_waldump and pg_resetwal should be fixed in a separate thread.)

This utility can only be run by the user who initialized the cluster because it requires read access to the data directory.

Fixed

(11)
+    The <option>-m</option> option cannot be used if
+    either <option>-c</option> or <option>-l</option> is used.

-l -> -r

Fixed.

Or, why don't we align option characters with pg_waldump? pg_waldump uses -r to filter by rmgr. pg_undodump can output record contents by default like pg_waldump. Considering pg_dump and pg_dumpall also output all data by default, that seems how PostgreSQL commands behave.

I've made the -r value (print out the undo records) the default, will consider
using -r for filtering by rmgr.

(12)
+ <arg choice="opt"><option>startseg</option><arg choice="opt"><option>endseg</option></arg></arg>

startseg and endseg are not described.

Fixed. (Of course, this is an evidence that I used pg_waldump as a skeleton
:-))

(13)
+Undo files backing undo logs in the default tablespace are stored under
...
+Undo log files contain standard page headers as described in the next section,

Fluctuations in expressions can be seen: undo file and undo log file. I think the following "undo data file" fits best. What do you think?

+      <entry><literal>UndoFileRead</literal></entry>
+      <entry>Waiting for a read from an undo data file.</entry>

"Undo files backing undo logs ..."

My feeling is that "data files" would be distracting here. I think the point
of this sentence is simply that something resides in a file.

"Undo log files contain standard page headers as described in the next section"

I'm not opposed to "data files" here as there are also other kinds of files
written by undo (at least the metadata written during checkpoint). Changed.

(14)
+Undo data exists in a 64-bit address space divided into 2^34 undo
+logs, each with a theoretical capacity of 1TB.  The first time a
+backend writes undo, it attaches to an existing undo log whose
+capacity is not yet exhausted and which is not currently being used by
+any other backend; or if no suitable undo log already exists, it
+creates a new one.  To avoid wasting space, each undo log is further
+divided into 1MB segment files, so that segments which are no longer
+needed can be removed (possibly recycling the underlying file by
+renaming it) and segments which are not yet needed do not need to be
+physically created on disk.  An undo segment file has a name like
+<filename>000004.0001200000</filename>, where
+<filename>000004</filename> is the undo log number and
+<filename>0001200000</filename> is the offset of the first byte
+held in the file.

The number of undo logs is not 2^34 but 2^24 (2^64 - 2^40 (1 TB)).

Fixed.

(15) src/backend/access/undo/README
\ No newline at end of file

Let's add a newline.

Fixed.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210630.tgzapplication/gzipDownload
�o�`�}�{�F���*�=��2)�o;�-��&�V���f��P��$8(Y3���_�@��!;�f���D$�>������X-��l�F��l��o���ht�m�F�m�g�h��S�F���6�f��
�h����f8��0��u�e��y���"�V����98��h~�:���7�.��s�v5����u[_Z��-�����Z�
��j�L��0�NW4L��7������oz�/���.&����I��ot&]���f�����Vg������v��-����Ka�"5^������f^��"��B|�W�'K|k���~��A��u+z�bE�K����D�!��Z�<���4�/
CT����/��?]
n�?982�?@K�h)"_(��@L� ����/V�+&�)���b�b�>�O��	�?�Y�i9�y0%ho9���?s�wf�����u1:-��v5w�,l�����Hx<���b2��O�>�&�s����"w�,>��J2�W�w������?�j��xF���9bn�$n����j"�>�"���g	C���(�����>��X������	"s��D�A�X4����_`SKL���{k���-�0�&3��`�g8�y&l��q�����g[6�t?G�"`B���������m�f�������0�h�����p�����EA�Pn!���+�U�K��W`��V�����3W�W��Y3h>���z��x�k��������*��������[�8_-���U�Sz)��h�<:zxx�����
�:������
C��=���)���������������ac>����y����?D��F���o����
�t���>��5��~�j�-oV����?�?<<��!.����s��-B�f����~�U�Zz���}_��ZT�^�g��u����zP��l���*���+��j�0�?�T��m�]��x	��~�>v����}���Q����T���Gy q3�+!R����aG�G��[������Z�x��n������������LV������Z�?����,�wg�w��hrL�z��&.FQ�%#�s�%���y���P+�������
T#�����C�E.~�:^;��$
\W~��X�]R-�3;z�&���W����V?�$d��'�v]�(��5?*@��#��e�
[�L�rUm�
��y�$(��6���W�7�������7]�F����]�m�
�P������NX@�8���g�)v�`����q�<�1�hP�L��*�`��i�)@>9�#.{�\�0�
mibB��`��|O#cj�zS�}�|�o���ak��y��������)\Nyb�V��W�GP�W��V�E�@�L�k���K�kAwFG",W�o�r\�����W������V���ji5�T�Z�)�t����[Dn��2�3���".D��,�e��%�������`G��+	���N�[��N_�B �Z�sktL��3����|�69�m���b�������_}xx��T�_NJ_������lY�V�1�z}:���N����!����}X�u=������k��&3bv��2z�\K�M�X8{W�j#7:�sA8[�O$�U�nd��A����||>�\
��o����<x5q����Pc�og��j����f���������
�����X�@�8IQ�r���=��1�B���<����;H��O���j�6���������^���P��E1����M%$����gw��N�^o5�����F�g�ZJ�NY)���O��G��|���x-��+|����������J�K��n���8T������~UV�o{��-U���&Dm�#;3|g�a�JQW����
O��c���tD$g?@�
����#���2�B}�F��}����Y��%$vu\���Z����jv&-��.�H[6���*���A��������Sj������.�Z�`1��J�sy�Xv�-#���e��n�|	�k"�P�*����,q|=��m�/���w����Y�e���fh�[k�p������"��	��J������G B�X\
�gg�31�����
�{�} ����]x��cN���������6j0��0�
�o�U��xw}y.��}���+ �~�!t"��ep��,��
�s�q��zS�P�8�B~-Q=?�$�p��I�H�<��`I/����a1V�G�����`SKo��=7�&j���j�xYQ�t�i��Jn�O<6���EO��7�x�P�z�B��>X�{���8I�������~��P�@�w/�o�/)-����g����H�4������,�}����k�L%�a�z��v��F��}��h���Lx��q�5j
�>�7�FG�)bR��>���n��-�j�$�.�?^�������h�YCZ`���7����#��=�Yx#O�W�I.�����b������Kt���N�r��jV�����?�<�T�����:|�����U��K�P?;������5�t�P�6|���Sk5���n�����7����o�qK�f���������?f��Qf�Ph����(���}�
���-����4&N����X���N�mu�'�`��+��l�@v����Rz���2rpb���a
p�@��������xp�a8Fb4>?���9��m*I����-�3���b9���������|�)�����������|!b������������ ����=>NQ@\���4������.=(�������I����0k&2��&~��B��T�k<�~?B�v/Z-g0���e���E�����T��K��\g>|���($w/.x
DlH�X��DeE������gj�����.�7��S�=��	�����3
Z��e������������vk��U2ah��+[Na��
Z�Z��m+�o���s����d�	���:-�r��mx�q��y|��&R�}��u���<�^�n2����g_0$l���1I;����n9qp����D����C���:�����F��Q���o4�{��w�*���}>|����-��092����i��xd����Qx2���������j��	PF�5 9��������������?x�F�(i�Z��y_.��n���������Z�<���0/����E�����z,kUv&\���F�f�f6:5���j����Y2���������!�-����*NU/�F�A�:���`�$�+�G�����t�����1Vq����j���������.~�����H?X����N@�+Z�+k�T��^�t����+:4��������bn;�E����m2����Q5�mTa^zL[WOM*U�xT����'tzD�UMO(O�x<m��V�b���"G����U{�	eva~D������1y���y�D2�??�����h��#��0�����H�6���N�,T�<MO��#<2�NNLk���iS6�?jo�J�!7Zd�(�h�#PR��M�n�M%�:�����w��z����V���6����+06��K�2%��`����%�7���;��WO�~�W�'���wTR��}�L���������7�b����IA��������-�z@�������L|�z�7R1�������k2��UC�a��sk�8=�����������a=�E������k�'�[�v���G�1|R�����d�Rg�}�6�
������q���H��6�1�nS��8��W���&��8p���rL:S����{M�4#b�_0Y8�m�y�`�W>���@�.�pu�I�U����>�K�R�X��}
U=�������b�ODz��XP]�
B4��������P�J$c�q����x){���h=[�.�"��o�-�c�=����]��M7"C��RLHC4��kh��&���v���]X�caG��kt�G��kMkL
X�w{Mf�~M�������(��
��qN#w^a��
~?	58�`�$����|xP�t�Q���q��t����s.���F�#��(I����;��(���{,W���=������x�3��4��I�s�|�:|DW����KR�[^__^��=�`��s�� �3�Fz����'�B��jB��)ORo�n����K��X=mu��i��v��L'��1��-�:�����D�n���?z��a4��U3���?��"�i
R�W���
U�i6X������__^����H�&��L�-�����:���lo��I�i��1i�x�{�[R[��vY�U:O4�A#�J������n=MR7�N�����Q���i�f�l��G���4]��T�&���D8��'h+�^���@���U�$V�k�����*9����%��������{��<��������n�������(2x�o����Q��d��H���������#�3/��u��V&��xAF�h����jq#�
jf����O<�2[���I�o$/�a)T�"���<N�����)��x�$������c��Vh��a~87h�ri�u���r3o�Yj�{R1���MD$�!T����$�������
N����k�ZV�����Z+�#���m�����JF�o�(���%�:/y+Z3AnFh����_>��
��6O�_��tQt�
z�5Q_2s��OSq�J���z3<1���ZIe>G�S�
5�b��f���r�J�=x��n�G���~!�'^�������fJ��}\`�p��VXT��Q���O[eu6���,\0�9��O�V�)�Xhk���<���F��Acy�����G
n 4����l#�P����}�.�2��hx��F����h|yq�'����=�%��Z���[��b>��Hw�;�p�Ne�]�9���	I��%!`a!�^��
�|��v2]��EN��Q���&�h4���������j>������b'�&3)�?��C����������N���������i������/�|���$�������cZo�v�7�d58)Vh-4�m��H�t����"����4��E�<6)H��)����5B�^���~���VH���"H�i`5��F�����p�f�k��
�����������j�����b�m�{y���i������`���,-��'�b�UN8/�s['e�+%_�g3K�E>��������y����Q/���Z�f���6����}�������tS���*F�W�h���H��5S�e�Z��o�� �w��MHb��6�R�li���4/��_�x��o0����D>`/��]\��&0��3y$���p�*p9	F�&d�������)�����1�C>�;����h�<��Jn���E���P�R����\%8����&��������2� \3��k��v:��X��nq�E�cx�+cY�X\|�{Z�$FD~�������L��&�:�n�RP%{�o�|�?�K6�#,V�����[z-��[�o�����I�|r1�K4�:����tMk���m]���d3�S*����DY�']C������a8��-�3���^�����_pz�O�r����Y~��<_�"oX���~ILm������7=�������+��h|��p?� �{@(�3�6$
��:��;�9K)�wu�����\?�O/P�������N��
��g�����@<��GKW���g
�+��.�����%�;s���Gc�:����J���k�x���U�;��;��9�<x�r��Vw�7����a�?\���\V�T���h~�]�4"UK�A���~��;�?Sk�c��~�+~���Y<���"_�kqmAD������	��YE��@|*bYb���=���[����1���y���)�I�����MBy����o���8���$�%Eco����j4�^w���������x�5����'�;�N~����������D�2ZX�H�,��l��{��� ������O�;,Y�1�J��$��	��+��Q��1�^B%4b!uCq���B��C7UN-�5�vL�i��������V������[����|�����uP�9YV�>y��&H��=AJ�1��T}���A�u��)��H
��[WY*��t>{�l�D�/z�fD�z�z�5+��'y�<�j�|�*G+�����g��e`S�%��v��Z�m�b�� �lV�e#&�q���@�N����Q�7:N��k5���{��JwO� �L;mR�v�3�$��HT7��a�i�T���R���#�.��6a37,#�����Z��YN���mZM���w����#Ki1iI1c<�`��r`����P��� 	�0Y����r+N��p_7$��.E�F�
I�����`F�\�� ��@���v��E�j�5��%�y�S��Pe��~�M��dK��v��7S�
h��v�F����O�\k[��n-��XeU� 2e�5�"�BB'@MS�^�����T��EX������0�j��e�N��z��X,��nMY�)m�[K��������D��N�p:���x�0������L��6�#�9����T7���a��g��x�(�\�[��n���c!7�X]�1�N6'��R�������� D���y���+�������-���R��x���9g�' ��:8a��~D%���V�G�)��J+�Y�>aU����"+�
|��@g�C��-\(����� �z��$���VFx���@y�qg�R����:�e5�n�>�;�^���6;��Z����~l/l��K�X��^Z���GK4�>],� �
�d�o��)���������	�`��49,�Q����v�?�����m��Sv���I����*>���j#0��l��d��T���[���Q��Ri��e��G
Y��z��E������V�:����^����{����U����;�E���X����%&����j�H�������R�V��#k��-:�#Y�+��D. ;����~��vV�I�����BD��0����{�4l�'�'��A�k$�c
�B��l������b��Lc��$�����E���Ix��RF&
J���.#���=��N�o: ]���mns�e�*=�2�����`a(��e�z�:�+�3�]���V������y���V��
C
"/2�wM���V
pH�<VK���7;<]�2z������Y��K��p�x|�mpJ�H0���o�����A��R`,B<:�@�`�j@�%�3.�>�:vi'v��6�	��-���).�V�������i0)��)�x��%���]������T����bg�
D�S�a��"
�x�^F��1f10
��p�Q"@�B<�X��P�>��g/q2�^x�i�u*p�B$�����K�'�E\�=_\^���c|v�>q[�]���<�*�[��H�-� _�})�$",�~ea �S��pZ�C�n�cr���&�d�o���
�\3����N��y��r	k��B�y�
��Z���W/�>9��(�qDZ�xh;��PE%c����v������rm�5��67)lq��"�����?QW�1y��x��^������	o���4:�����K�����������WX��9K�G)9�$@R&R�<{���?����P�1�~����2������%	��X�-����.+�P8ui�M���B&��&��5t��2�z��\G^w�?�f~��[Y�[8|������$�<�J���-�1�o_M�#�X�r�G�.����1�ih[��������6>*uSkd�����e�f�����_~������wh���t���vs����f��$M�f2��#�lB//�mM/����H���������z�IIy��Zq�&LFG��(���6_�}�J�Kr*����*^����;`|���<f�T5G~��A]�!�P>�����cv0)�*	��o1t��|����rj.������j�F�A��6F�L����7�t
��[f�yQ�}Z�
i��������e��k��GJa<�y��~-4�)��4V�Jb�I]f���e��_�[�.�����/2�8�9:H]�.��d���������c�]IO��%{���e���%?.���F+��K������:]Y>~���i��(��.�|&��'����:��8���z{vy��x48���g�Y�]iD��{��E!990�������JhVd��5��7��V�zHM�,.��3�yf��JX�:L��`A�z!
�}��4��n����=�?
#�I3�0�2"irbfj���`��.\��0�K/��T/�p��@��.�|����}�u.&��|x�J&��PL�F��`�� �����b��b��fpq2�>y�.�]J���T�:��L1Ti���=�5��u��y�����"[s���~d�e�Q���|L�k�^�Q���7zd�F�����}�2�~�?���w99�\	�-��M�R�&��'��Q?�S�b��Ibs���U���E��3��qr4	r��)TS����N�H�;����!���������FG^/|%�������v!�K3W�������NN=�Bk��Vm���>�_�>��o��E�1���b��j:@�Umo����U��oa4�����c>fOO���y��<�[:�L	h����<�M�Tk��-2m�jm��ubw�U����_��-�%e��.a����.	[!����D�S��(JE��������g��T�F��B��3@}�H�{C���uB	Q�F�-a^���E��Eh=��yoG���2G���KJ�O��t����k�
�tU�N�OO�����18^����]t�mE�m��3�&V�`�Bg�J��E���i��s�u�j�@���f������	�o��4�9�C�q�<$o��"�7.��/� ��yx{���l���a��W�C�<"?�f�-�B���������������3
b����5��&C7�����N5;]�Q��^�h�WaIZ�h����lt��X�<������pp2��;��Tp���c��Z�=k��y���I���M%���3T{�$>��
6��[�@x(G�+i|��S��G��k|�4�@')��F���e�����~�h�?_�$�!���QHl0���VVS��}���������G:�/���o�������^��@�Oy�
-�G>5*����O�5-�EH�?�9�
��hM�)I��K�����0d���Q�>%�y���d��6K�
dTy��K2��d�Vj�����%�	K����������mQ��*W��h��)O��}t~��
�vX����&-�pe�
��	�"!PY�XP9"C9�#�X�-�K����U���m�`YP���u:����������6�����V
nu���<n��"|h!�rAFU�
��2}���b��*?y�������|6{�li
�5�a�f����I�������x��r��y)������8�N`�����S$�~�<��(�f�C�]�c���G�A�8�@���r�Xl�A���y�����"���'Z��������8;����%���I�4�=����O�_.>^���/����$���Q���������ziJ���R��t)�c�l���
r�x����@^5#A��Kz&�=fC�<����������z1 [����~ Y��(�"���-d`:����#2'�G0��p��w���%�����.ps��	���n���kx.�8{k^��tr����8D�^8���T��WW�V�
�:�X���G~�����1��b���3���Z���\����S���e3H^PG����d8���B��g��^ei�P���U^���A����
ze@v�@��i}@&Y�M�L?�����P"�����Sr*M�G{hT���6��*oM�Q����r�f�G0�Q������R���3���{cm�&�3�x��t�T���9Tu�Jb��Dz>�?�,Y��:�����S'����a�>������N�_~�/�'��u�J��i�G&���_���nk�d��#�$0'}�����k�-�j���:F����5�6���e�|��%u2���)���!�_��/�@Z�K+)%�n�	��"����K9tb���!�����Q��E�	z�(,��%*!��J����i�����>���v�w�G/�� �������t�,���a�����lj8w�\J�y�E_���.`t����M4��Hd��{=��`I��-�{
+2U��?�{Q*-O�*��3��&1�L���':�����U�V��K��CA94�g��	?2�����@dr��)	����B�h�.
���6:�=.���$�WV����XB����IO>��6�N��%�
��X#\��m��&���?�s}5-aH����>�-���J�74��_b����9P�e��z,���5QX3 �B(�`�1`d�������������)�BT�U�����&�X�I^���j��������f�����}�Y�l��`��}���(�3��s�=��
��d�H��r�LZZ �\TX�W������5�>�]k��G����;y�cQu�G�l;�n���V��N�k�n6Up�c����^��HJ�H��E�=���#������y6�<B���9�\�����*uJ��N�����V��i��f��m2��k��>���fC�j�V+g\���>Fw���xaG���6i^@��D����������.�*��`���r�F	_��kCx�TN.��L���J���L�o(�ifr�.����?~"�r��9�o�ys������+
:5(3��������?�����r=��y���)���D�M�af69��0-]x���0+Om�?w:u���x��7SC%,u��XN�`�k�[�V�\�{���;������3�6�>���B]���1H�'��fF�����[�����1��B���(L�����]D�;���"�Ef�ex���Z.�X��~�/
[�F���Y�/�0���
�t��m+���G��{w������H����9u�8�)`���'yA��
?���X�[�l�1T�����2��
��M�Y�H�2��nl��2��k�K���?`��}SRJ�����L�n���&�����8�{�����(+����h�������
m���P���a��X=�7h\��b������N_R%I��b���UD|��<%}�[��H&k '���:U�9X8' �h���	�����k���x 8���S�6E��:�X`iz���f�����D�i�\�+4���;6C�@�-��4��z�i�G���b;�-�����p��|�7��d�������=����kI�ym)�w�J�H�����,�4M��X</��h(FC	)��@��l��Vy7��8=Q1:�0��QN
��)���OC�o�����X^t�n��^�������,8��*�(	h*o'��2��'��C�$7�<��+��Q*Gj�k����?��n�U�����f�������>MA>8���o�v��[�����i�������w��j�7.f��)�r�yRE�6���{x�����)yp������7H��>F������*v�I�����N��9_�����D��jMp�
��k���
�O��h�q��Y��������Q�����LtB��2Q�[`h��Yw���c�<�1G����X[q;Sy��.T�$f�M���%�3EW�e�U>��c2Z��������
V�F��Ux�}��J`��X�,�nP�?��V�9U���{����K�����d�����`'��������wW��7����dx�n�0(?,ZJ�z��?�S\��CX�j�g�D�������7x��\�W���WQx���@����\
�,�m������e����Z_��%�N�l$�"v ]��|��
������gk.%��ZF�$��z�����H����%;$�t���'�����62���=6KJfK�}�KmllO�D��}u]�#���+�Z�r��Q�m}�|���'���)�?�� �IYX
#!����]~�I�1���>�DE��\/6l���?���rI���*�WH��"`r�Z$~��4��Q�S�G��O�����.5�tq�^kE��Ot�����B�-����\���������������1~w�q���o���d��e��1����7FSz�����r`)U��#��/'���[4*�\_^��a�����b|�&����$6���������_�����{���8���3������y}�u��b��ZOI��H����7<a��X�*}��&�n�J��f�O�r����|�qx�r���)[Z�������n1'`�fVz�|���������C��r���������5h����z��&��f���J����+�����l�?�UWU,����*RS%���tT�T��Sx���P�{���<�W��D�]�����dj{(�[��R�\�UV�|+OlB/�CPv���;hM��aP�#�3�d�v� L#k��Ic����JY�?���L�y��zT�
I�Y��G~a6p�"n8�xB�R�u�\>��k�S����oE��x�3��"��UR�Mqy��ewd���lb���ru����5���������f�a�r�q!%�C�|X��&������0(��q3r L����\I�?����!�G�T����'R�/Q:���������f/I��S��q:#R<�X��������MCg�}�MKRN��4Z�f��s�f�n�}�j����w-Z{o[���c�q�����4ie��2��h0YhO���?�s��+���(�_�i�������
W3�=�'����}��=��7�\Gg1��N���0�n�Y���Q%�����*�V�J�U;�/����������W��D23������JH�l�tm�����rR����vpo�>��BcZ�����^[I6�7��� �t�PL?Xk:�H�4p��L�������,�l�t�l�����v�0[���6�IsqyF��Z#���W���kq�:�����q��t�Z��4PAe���1���q4[8��01m-�&k���0k�����y.m�=vy1^__^��m-��S�l"�v 2Nii�����gG��%0��~mc���q8_y~�)���g�m�m��z���z-�2�5�e���6��9Zz�*}�~M�@xI�����>�,A�Z�1�e��,vDK��7*
Z��&����!����-�!��N0M��1$N�o?�70�����$3%���'b���R�q|��}���6v����oGqGs����*A�k���r0��e�F��5�[�$���WRF���Dt���GB�����;C�������SF@�%d/+�Zt"�X��|�����F�c� �E�/��e����~���X�Y[=�g���|w����X�*?��x�Hq��SW���sg�|���E�b-����q����Rx�%!�{J���,�������c�>U����gm�R��{8�=�����!$8���r!rWb�cM��
��l�������V��w����v����t��+ji��
;��/�
�V��?,���&��X��%���>�C�BeY�[���2Z����� '����W�����/Xs��)����&�����:]��:;~!&�)R��G����AW���a���RDA�h�_adrD��/pr���?*����V=�;7L��Xng %�j��v�����6����+����^S-���R�����;�*�C_�V�����H)_������L��
@�l2������v�����V��l|e���k�Q��u:V�n7���hE�Xi;��+-�an(>$~-
��9
`� �Hy��9K3<%�NAx��������{�*����<�|)>�*4-����3���Y�==	�S�kM�����j�������Id����w���8�9�0�$tW����Y����,��P��z�^�;1���7v�����Y���-����+�M���I��x}�� �?jq7�)9���:>���c��1�9'_r����x�����O	*]�=GyS������x�.�G�����_yQ_9	�bt8n����p�9_.)�g������W-9���|t��`��
�W��O���l|������lh ���	��T�IVX!v���J����n������^?�"a�n
�'��8���oi���>/o7������:�V���nh���e�t4;��<
e1�)�n����;`��=[9����e��������N�n�]�:��oZ�~�@������]W������(�/�]����8�m��Y������f�%��$��+S�X�J��${O�mT3������,�	����$KH
d=�V��E�
\����FIgn}'8����U��JA^��W��3X
@�������
�pict��E:6��E&+9SN��S���T�����
��lcT�u��Z�F�����E��t�E
��1��
>�����RP�%����zK{3��D��8>�=���\��V5�}#Y�����<RY����>�S��e���������^s�~����c)�VYN�;CdV�5g�*!��p�F�i����'��m�
�w�ZZ{���8�0e���3
v�H������v����jQ��7�H���`[�|1qQ��c��/����fd��bJ�2�P:�M:�\������^�d3(L��E�J��4[dS*�$��[�f7}����:Z�|��W�8K��1�����mX�Q~�a��n��M"^��mLG�(��^y^���R�u�| 
/�
6��_��e�8���i��N�U��S�p���X[mp�����
�5
�=���t[3E(c��^�������w������<V�'4H�i�7)j���O����+�0�1fc�gk������|�eo�n����������s�!(�@-���x�	&� �����U�����[�K@���j��^b�t�jh<��6�`����;������m��z}�����i�7����e%q����(��A�W�����/�����7p�.�+q��1rC��O��H��qT{��a�zJZ9�>s({L�����Z�������(GO{<��F��Pr�c
/��y�Id�����phi��}	{�3]�A
jF���g�(�D�af��iz��0����]�fn��� ��u����g��	�IZ Ff<e��9�������F'f�-u0%�U
���N��� �����#�1�����i���X���:�������*�b_k��������������Npz�����)�Kr�w�%]FdZW$�-M�a���d�yfJV�ZS�)���>�-�PA c�i�>f�N��D��RY�&�u�%E���"8W�2��/�&�d��������Q��/��q�^�6��x�����>�`����@������4$mo����kT�����g�
���21����s49I����8k�R��M�H1�l���1�#/��FGG��W� ]�js�������^��bV�5��L�FkQ{bi[G���s���|�\�X�C�7d���j�/����Ren��8�Toj��QsM�8��$f��|��V�i��8�M��������r���e}��'Et�9���0�k��vh��P�NX�,�6,A��!Y�?!�I%�,������K0��l�a\t����`���i�)��:��#�^%�o6�F�4�DHR��6�g��A��=;���{����+�&�bq5V�������Fwsw��U���?e��Q%*]���M���:��.����@��O� z��)T���=L����.(���Z�Jhf����2'-R m8�j��*o#��N�0u�v��iw��:���i����iu6�����6L��.Yuu�Q��K�����U��J�7�|I&JY�Lr.�{:!�\U��(v0��W��d`7��zx���j ���J>���hm�ZnB��e7L��i{��D/R���-�����g��)����is��0��|�l�q��g��sM��*����m�S�=�����u�����V��f�l�'��km��<��o�b�b�f��������^�""�a����f�g�����p�<���>��Bg,�z�XK�V���*$��L�^�l�i���8���Wy������>���I.gfc#EJ���*c��Aq�my"f����>\��L��
����@z- q|�w�B�/�K�|@��aJ�J3����j����y-e��C�'^��eVz�������D�)�}����H�N��*d"��
x�c��7�����o���V�?4���4G�F�<8�a�Z.A$>�v�x�m��H�����.[�� �!����
�����VW�F�����hg��M4~�y��V�6C�6�g�{���?�E������v(�kN��^o���V���N��V������3���-�� ���0`�/�?a�r�c3/�`�o!>����%����`?��v�����s_�0nW�!��Z�<���4�/��o@c����/��?]
n�?:�����hI��	Z*�������Y�Qw��Vh��~6��8f����E�����������}�8K�f�[��s�������(8	�O\k�;+���������R���2~��A9@eh���^O���ro���f���w�wV�?q�E���*j[p&���|��P��?U1�*�&��$��DEKd�d(��� �9r�9�*�?
j��s8��a@�
S���|�5�D��}X���	��m��y{>>9�O�'d$�%�lUb�$���@x���&��9@�'.�<X�2/a���
��j���h�l��!�i�T(��~�	�N���\��!?�c �q��z�'f��*��k�U��6]��
�yK������3)����V=��C
����>���nI�n_x��+�T�o0���������r�5�1���b��I�mN��[�O��X]���;��i0����0;i�9�p{m��^rE����&5�I�eI�������������C�����T��}���I����@ioC�`f��p�T����H6n�z�=%�0�����N���S�w>�hP��B���!:���.G9�����a�V����0}J��/[��_����}9w	O5yJ�$�G��wz������-g[�-
��h�
�J$#C���/.���`�9���V0���V�n���s����L��5M�o�t,lN�c"������h����D@Mi^�h��H����D��/n�L/�g�<,����gS��?'��%^!���k�P�Q��md�� M����M_�3�0z���5���"p�n�A��j�o��C��p��bc�V!!�L���8x�Z�����2��`�jLu�O}����#F�NRI@�T$ar����Lj����
M������P�F0Q 9��O����j�`XhP������a������P���A�����]R�����V��#�c�N-o���M�D8�b�PWwd9�STY� 

c�$��#$��r|N��,�su3�A"����DIX����;������Q��K�4��(�����@<(��*�@�����-n��$��z�e
�������^�la:�f_=��$���7b�}rze{2)
��y���%,��H�p�����n�w�����	j*%����Yd��z`7�[�j���k|�@
B���`l.I�q ���})5%AF0�1��T��MW���KM=X���u�o����&�Z7�Lj��f��SC�+G]�Q{��C�������8����+�5���n��
�i<!yH�+�sM��.���e���f�J��v3<�����	O�,��s���&���k�>�\H*������Z��fR�Fl�t7^J�����J���S��^���}~YN�=I�����h����C�8r���`��/��_VaD��g�R�DqPf�{2a ������F��"
_�����+*������� ��//������������pTlL��������� %i)J��}������M��s�:����������������p<���?^_>W���;P���)�u��*r�����?�:����&�S���_�����
�!�������D��&����+���Eu������P���?��7k�~�#�S�����HdY*+�O�E*f���J�S�@2�X��Pm-�2%)�W����$�'���)�@�e�a:)��<-�O�E������?��?~����!����O��Vy��������s��$[������h�~x|�*�F���v7�3Z��(�b1���Cr�"��d?�j����9�$�P��P=b.>u��#��P	x}A��x��]s�]$2�4�m�L�
N��E��&����;k	�z(;���5Ued��]�`$�P�Ks������3���z��[��(0h�X+|��!��OuM�!,��C�%�+��!g)��Z3"�7��t��I�9�5rT!�%�������tZ�����M&����L���A�W�+6��;�h��S��m�l����v,l��@�C�a���b��UN��q/��S^'���qt"t���_�,�`�D,z��'q����'��$Qwx��
X��b�VMR:�ja�jQ���L^�C��������"s��C���<���KT	���%"�*Q�KA�K�s��+�MJF��n��������Um	6���T5q��x�ap=<Q��9�/������lQ���-j����S���E�L(����\��o�1HJ��D�'W�k[D���
Y���O�����T�%���?�#���oN������������H� p$c�����-�l����S2�����K��~s�rCR����@��2����,t�s��������c��[!�I�-�#Pe���e��.���,y�d��~:�|��W�vR�|�]'[L������M��n�����_�+R��+2b1C|�n��ae��I�$y)62��+&7��VX$��+�L�8`Y�R��C������P<���#
N�{b���b�6t��Q%bKP��9��C~C�B(E��6�z�V3'��MV$|0R��">5���\3��2����M-�)�$.����"���~�`��+0o��+�K�avY��#ZiM-��N�;���;p�p�uf`�}m����Ee�$>��r/E���3�dr2�4�7����V�p�����Y=d�`����D��]E[�
Hqt���H����E�2	�;v?���x�4)7Q	j�&����umJ�!e�NC.I�`�p�� ���X��3��8��-{���m�!�T	��b�+�Tb��cc�\YK|O�c��P�J:��-O�#�7�3Z�'�O�g���c�7���_���W���a�69��b _�@�.��s����tC���k"
���C�J�����8��R���_Z� ��������,�|"�IF��@X��2	��S}����e2q����:@eo6�>�8
���&�{�[�*Fa��cz��'J>���N�e����E��|{�&2{�,>���"�M���*�KQ����{b���
���Y$�XT���,1=	Z{�Y���8�f�-f�Q�����'1�x�TB��R�Q|;��:
��R
�(��t5�=��S�B�A��uz�W��z���QCr����H�@A!
Ej
��h�:���jR���E���	�v���m'���j�I{�����P�8�G"}qc��Q!�v���&4��	k�����A��?+�3�������5B��a5�Y��E(^P�4����f�.�� �4T���a0��������jWi�wrPE�&-���d��0���)-g�~h"YN�N5<�
��L��5t��b�~$n{�`xLt��,e�����M�e�T�~
��}�5I^�z�m:����Z��"GM��,����1w�d��%UQ���;tu���dy��	qZD:�,�(������8&d�v�L�����bR���1c�w��B��G�'�5z��J seEw�r�f�L����9H
/(xR|qy �����6�qI�a���nI����N��vzf����R���e��RzN�A.��
a�ZTi�~���0��A����2�2�����a�h$!#��] �l�|5=��*'.�
�"5P��O�l��
V���;h4Y���J?��K6���'&���!������-F��t���
Cv�9�7l���������m��7����M��[�7�������a�� j�m9nu4�
W$�V�����H�������_J��$�pC���{����������Z9���1��8|����L;F�e��uL�6Y���2$R�/����A�����H�6���������*Za���!J�O��P��*��&@*�G����4�A�t~B��f<T>�`h���~�*�/2����k7�;��@i7$���LE�^��T��b��+G�J�d��n�c4�V�Up�~i��&�L�63��(�F���
����0�������YmoO�~����)#s�[5�����rs���������H����T�3����T�����l��Z�I�������WuF7(V���^�yZ-�T���nN�1p���	��m��}���r��Xs����y�lv�F�d`�K���d�����u�q��f���R����	��
��+=)LT�K�������3��b6�Y'�D�E���PJ;TX5"�h�(����`#50���~���&�'a�����a���9�����h���K��KM���4^������FbQ����u/�Gj�MU���z�d�����T���� �/�n�E:�o<�e����W�@l��V��CA���:fjZk���(?�5��I3H5w8��n�����T�������.=���r���WR�du/$�:��������!��0l�z����^���_7$[��.y�x`O��'
��`dO���]�E���4��Zy��]�	x����	���r��������C�b�1�S����OF^��t�'�k�������G���-h��~��o`�����{
C @����lM��e�?�����?!Zb�&����>�#��	���=�+�Y�G���@�a��}�]�����-"+����,��*'��6���d�O���l�]�+�#6vq��n��d��������-����$�7���T�BLA�/P��*t��q�6N��Qq(���\�?��& d�* ���tH�*��GO�{kud4*�aL��05������~�.�9{H�\4�dK
�G`��G�f��P���������uH�NR)����JV2��|3���Y�+--��[����������PX���i*HA�Z�s�/9j��+�a��#=���hl�����E�����P_2�*Jn����X��������{��h���<&h���MZ!�`�7�P9C@���|D��uV�
-Ce����_9`�y�"|��D����ZS��f^�A���@�����Z���vB�L�<���\�f:�g�4��_��3�*��	=n�z�0���$�
����W	��>�%O-����+��a5�����	9���;��.�w�����������xw�z��>@���`�������@�H��k��u'�/)�����R�E�2|yt���p�
��G�������yb�z}��?�?��o����i��4��0>���s���o����ss��!���[�������f]�����R���?���E����1/w���	+�&Q���iBt[%�T�� �,��]6JU/z��w@�2�����}��(�_����l�c��G��n�����&��8P-B�V�t������je�@��r�����MS�"�e+��4Z��y�!M�������;�"�T�5T���b2������F� �����%�`�<�����9�����x�6PKh�h"����P~�r�z�_��J�����T=�C��@�c��G}����Th/��#k�l��
ih�c�p?H����P�V�"u���^�0PG���b�n1�Z�Qf�����3O`U=oi��^�Vt/�
Af�����yBRP���}���
��5�u)�,��JA ������[��Y�V���q���M��+���1���==X^4&!���9pr5�L�m�_��g�W�����8�]��(�=TX
������Y�RN��E�����������[�����(`j��j����a�Gv���q=g5_������.W]�� 
���"t;�a�����
B7���Kw�Z���n���Lw���	�YV�9n�����]R���a��	�j�����]��jw��Y���j�VC��\���~����B�f����a���W���EPig���)������������]y=�,��%k��S�X�K"F���Lza����T>�VcM5�������5�p�N��7+��L�]��nsM�m��?��wi=�v���Z�]��f�9��m���H������r��G���/;���;�����NNc�
�I����aJ*A|�Z~�(��<z{��F����\=<�������,�/�}���T*�^�$�d1�7y(�������Z�Ny=d�J���3�Y�"X���j�!V�b����Rl���j����-Og����v�(P��u���GlL��E�xQ���f����
��0�f6��5����_Ye�E����mk�t;Ub�b�*��\��;Wc����Z�������Ht��q����������.�������;V���.��o��s�����'U�qZ����F��oN�s��yb��v*�^��6��dR�O��9�����l+���d��6|�����x��f����`�~D��������9��Uk���?�fh�7�����z�6+��$���3
�@~���&�����'��D��
E�E��b�L��������KH�4%)O�;8��`��t5#��O�19Q�\1�~��9��������jM\��qa����p�({��#=%]���V3������Wd-����$����?����#�����������f���G4e���v��F��z�5iMzV�h��#��?Z���|#$��!85�I�Z�n������H�Y%�U�pb7^�2����j��gI�����JU�a���C�a�kP/��������	pr3Iw�������Zh�{K��>�;}\x�h&��������.<K
��%�/nN��nN//�T�_��W]�#=�8��"��:��S�Y�KyQ���^�,*��{c�JA�UT�7��>�d�4:� }�v��\���0�cEUz�����#UN<���3������/�9:�s��s|����?�����a���l�f"���g<�J���<&C��L2�
M�_����g5A-��'4!�I�O�4�O�0~���3�L�{F�A���X�wd6���3��%�n9�����%�W9������er!&�eZ�����s;>�
��Gjr��GM�"ie���G��BNV*jkh�)�!^�	���@�c�R��������g����v���UqU��z]�h��j������c��F���S��������E~��e����5���3i��z{�2'F�mY[�,�|�����E�I�����f����g��9U�;����[��|��^3��T��&����}�?�����J�'O����I�\Q��>a"�{\�l�t
0�������.�@V��^��S�������u%|��Eo���������}[����eM����V��3�]PA�������P���(�������~�j�u�_:���}��rM� �L�b������5G�:=Ua����h�\�<�z�j�Ik�LRN�*���3�vD�2����26�*�m��ZY����!����*���b��I.{����U����EEe������
({�t�e��V:=`M�o��0,�&�]�����[/f-=E|:}�r��{�f�LJ3���nh�(� p�E��xq�P��l��<��/�����b!�|�����n]�T�q�,V��Rs2�J�Ci�*��bl3�������<�����1���{���	% }�dh�A���J�jT�K���>T�a�:��]%�\|��8������h�������{�.���_sx
�������E{N
O����������P(�x�b���������VS}���?�?#��ZNKEpT��:i����uY��e%�
V&n�O�`�(v�T+���:��:F�#��t@V�5(�D�)�J�<�WHCP�Z���h��_��wY�;wS�>p���yu,8T_�N��.�>��;�?=�k|nw��[�f��ehyy2�������"�<��x��4��%S�
��54Z2��OD�C������V���������Q�|z�93���qM�K��8|����@���*}���7<�n$��1�xoT�nVA8��@ok"���J�A.�,�e�d�����������L�������VJfQ�,�����0�n� �:\8����ZV�����'0�������������x��1��P��0��<��<����NG
#1V�7������Ub��g�J�X(���3f
CB��f��OG����3��n�H]����b~����W������R")������!�e���S��w�!��p�:�QI?�8����E��ryK���7wC����'��RN�<ZV����@�8M	���i� 7��8C�Z��8�"_�q���(��gu�>sy����P_c�������eb�x�P[�c�^�84c�C�����VmmJ�	����������y-	�@�Wj�~'p�)v�f�~f\e�*��@�_��t�����u��z�2�@��LL#�������f�mX!<�8>�;�������-��[VRE$�Y����S��*����������1#����lt!U�v���[	WWK���Wd��\��R���Q�?*�#7�="������S.��0�/a@Y� ��	wY���'h9�o�H�9`n<{�P2�"���
�W���[��'L���#�+�^*�^�6>�����vhm���]��/"���d�N���ZC1��i��O��2��*��FF���7�'���B�&�S�L���|���ZP�/F������L����z1%c��U�{-�����2!����bD�$I��PeuMO%�D���<{;8�NH,?�
I�}��)e-��Q��� /r�r��	|b�����#
�}����4������PM������(4���h�8�m��%D8������#�����f�0Qc6{���������J�L<6��GQ�Ov4��V�@b��F�."����Y!����8���u�d�c�?��	}FdC�Ej�����\Z4?��������
�C�t��}���������	3��*+������8�������'���kb/SM���t��R�"�[��$c����`251���=���G-c�i�=���)LT�T�^fg%��vN�H2�/#���C����7��m���N�Y^�HxI��_�w��	0t�M{3))g�==�����:���
�������x
ap|<���<�8:�~HA��VaIW��*$.#UW���j��f����V��4������6��d9��(V�o�Xa�&T�D'��:k]U�6jg-���b]��>��3�Ac)��=����s�d�n�����Q���$��zt3��_��5��*~�,|zA�����#'�l6P�OGKr����P$U>t�{���'�E����s���$�.);x{����uYIy���J"�&��Y3��fGr�xPy6�>9f�����\�b�c�
Q�����n�%Qt�@Qb�!��K�����0�G72�U�N�`e��aX|r�a�V�����R]��s*;FRd���a#di����4��r�M�"t� �#��E<7~����V���2~!V�+��w�N�3�pZo�q��Z���"������ZS:=���`7>'~�pRTJ�ic9n��
�U`��7P�icv)N^��k�xy��6\�}\�Y�$&C�$W��{�_��Z�J}
�`��?.����XZD1��_���Qx���a!�
���=���{��6�$]�3��,�k[2s3��e�`�]:������ZZ��@��R��tw��~v<�/yWu�����)�����o������aM��7-�g4���t���w�H���Q��B��NUZ��my.����ex������,�h	B{����IQ���&y�w[�.��G�T�2�������#�W���n�R�����7����7R���uz������U?)G����a ���l����FV���U�U��1Qf��h������ h������zs^����<z���uHn�}����h���?sm����}F�&G���_C��t��I�l�ECZ*�FO��%9,iX��l�Z�b�a�����������C�^C�	2���8�Q�j��C�kD!�Y��1�
]:dv���'p$Z�q��iI;d2�kiD�r�Z������Z�����K���{d����q�[*"�Im�=��Z��|M��V��`]�j�v`o]g���G�x?�3�Qc����$����4_sg�����������.�1W:� ��9i�#P4��Q;e���k�	�\��K+.���:��_�>���A*��1-)Z��X�������J^��������>�/�h�~�L��H=�S/v��f`���5�B�cug����q��������,P�"�����V�R��g?&���ru��	�\��������2�q��D�r�!F�K�="���&6�����GQPR��.u�KKy4/���1����
����5��b�����8�1���Dv�]�%���^_G�����-R���.�C	�������\t���dt�������%<���^v�M��WI����
�:��E��To'O$@M���r�$z������g� >Ma��C��B."���],��3�p����k���o�jQu��BZ���
���	ofq�Q���y�	�����������F7FF�������~�v�����IXb��\�%W��0���_y\gt���Dj��R�F$d��W��1?7�X�������cQ8��,.�gq��"Fp��
��cE����y�_�-.����]3���R�w���B��6����r?��
��FH�� v�������%�4W'�J'�#��U�������A/��+;��0����|X(,��y�7����No2��\�Vy^��(K�����9���o��3��U�l����|��A�Cz��3T�dGm�N�t3�t�o��� ���������L_6���X�?��\E9�9wx�K��%���~�PM5�
�����(h�4P�x��P5��%��2�Ciqy^�W�(EpPU�p�N:��B���u�O~[��At����WE�����Q)Go�T�{T^���4��������.Q��'U�>^�d	~�R���5F�7
���D�SL�	�A���uo�������a�\o����T�+*B�}&_��� ����E6��O�^���G���\������r��$WM�z.�N�q@��PI����	�qIp���7Si��Q0�W9�Tgid�*�z��V�]�Qn���Qnk���D�C����@P@F	���A&�r��v!��A[�
��fp���V��]6�g��n����)���Q�r�h��(4�!y�F�,Q����2�
�l���N��mqN*EQ:�8P{3	#����\`'��E�����B�����K�J;��q�~)U�7�����r9bY'u~	�jld��{X�y8�V��n���b�<B���2� ��v�R���$*�3���cx��5C��l��*�Q���1I�54����j�;�f�g`�O+�"F���KI����Ka%x�������I�����!����#�a��Z5��& ��/�`D�g!,��I�6P�u�����������GQ�f�����(��kTb{���}���h����?T�+���O+'~�~t,�+���p���g��c�&e"��6��,\DP�,����������z^�\��:2����`����3m@t�9Q	���x[:/Eb�%�!��h���=����h��A���������K�0[��������T����6����=�&T��q�1qK������*Z�X7F��j���&*!	��*������#�3w�U��4
_�Fr,/����e�+oQ�����h�{�s�������*��#�F�V4��x�*!#f���b}V��|/<1�c�></%�P�?������
%I�HVE{�
f0wE�(���O)3��!C���S���������\�Bk
-����2��_�7�~Q�t�*��/ll����v������*g�CH��u!�-d��;����h�������>�����A]vrN����9.�E�����7*�1x���<x��� Ntm�C&�8-ZjI���z��A�����5B�.
�Q~��'��bD�������>��9���K��(�'��<Mk�F���$c��q\a�-�$M�=%�)�F�0kcm�����
����6
?�����3��-��c{B�;���2��<��J/�7:��\_�ZKc�\���U�����1vyKuc�<���������~�?KO��&S��m��!C���+�;��N�����sg�Lfo�����"�c2i����5�Bb)����Lc�dZ�N���{��ZV,��K��h�H��ll��Fgsm��!�5���L���+e�#C%�n������b��o�kr���	��N#>�H���i��	nq����;�t�������<�������*�'�6
���_L/�Z�k46�_���f���}H�������9���hH{��Gv�P
�V�p��g���+��Y8k�L0k��_��b�����W�x�����S�l����.�w=f��&�E`�C�v����"d��S�p���#���`�DcUP�%+���p��K1�8������
�}g��Q~N�0�#7r�X�8�������$�VU:3����QJ���j�Mv{�$�P��D�`��Z^x�����ZY��9�\k�v��
]����5�u_��D6@\���V�����I��<x�!r����w`���ogQ#���z�U|S����~��er�n��~�����J(�L���C�o��0k���;�m�*����ej�<W�(;��kN��TA*��-����h��}?���	�Q�n�u P��KC�z'�f}�y�T���m�����������8M�9�Y[[[������N��|�J�
�������&-|��}���hU���g��~�H,?H�*\����Hw�jU.��)�?	��Z�B���c��L������T����d���#�����}����zQ���!�K������c�x����/9����!��\�9�a�Z�Z��9�9AN;��"C���.�Iw/i&H@��P$��x��
�aR���Cz76�����3@�kl��p��$���^��\��N����~�
���s�]��{y2~�����j_��+?�Y�8���y�3�8x�/���&&�����Y��� nYe(��{Nk��,��s����1�~�17���f��Z�c�j�v�g�B�Vx���� �2�������B$"� }�b�;�b��~Go��7��>^J;�*E�j"W��_�T��l��� ��%.D���8>E�(��N�		���6[.9�S.��k����$7 ?n	�AJ�,O���L��<NI��(E �)g�	�����s�2�
v���1�������%*��g�/�>��t���t��P	+I��I^^�G��H��
i^�P��(8����b�6�����c����{��������0V!]
.Pc�'r�.S����8��)��g�p�*���*���y����^2�w]����0
�����$MR���z�S�#]����� ����*�����;Oro�I
�,�|�6�	�!�F��Lt�N����\������2����!�@�� ��u��{�D�w�w3�YU��A��[1�|������A������	���Q�,�����5����E;y)o�\��$�G�	S��Z:O�9[U�r��]ahVth
y�2:U��ss��+���u��b���=�Q���*�G
�|@�xd����L� u%�M��3G��a�=.3����=�D���iL����$�����QZ(���#Q����K���
4__+V�o�y�����=���o�������/���x�����z]��������so������-����s����������`����/U�U����n�/Z���t@�NM�1�Kp��*��P��]��y6��K����������_����$��[����/1��5z���&95_��#Z��p<>���,U����Q�qUf��G�w�����_���B�8�[Ee�|�:����U]g����s�|om��Z����;���^�RS[2x_�QS����)7�Xzo�i���?�v�fm�P���(���-0��,%
q�O`d�P���{E���2��o�����1��f��M���/cz�F��������L,���
��l��YH���nE���j&��7��C4����Z�eA"�q�$�AnG����7}2x�QBcv�����W��	���%��Az� o�rL}_���]$����!��
��-j��
s�Rog��l�'���W%u	�bV����C�*O�r�u`\�q+g)�������g�������,V�����P����?d���(��e��
���M,�@.mo�qW�4GWkb�&6�;�Iy&^�4fu�1�F��C���N�pD�XR�x�|���?��>�fk���-����ka�F��%'�(s3���u���z�e�������l�JD��ev]���T��%������o�c����=<(%kd�<pf��X�,�Y�baf�&"��DD[]NJ����q,��6�`VnY9%H�=IBBJ��
,7�]A���wp�k?,ww�C�hZn%�E��Pd&^NFe_�Dw��������{c��v���:��W�vf�t�,Q���Ds[��L�H�2�=�K������Qu�W�,-����wO`��<�wh"������KG*���7�Vr�T|A�T~Bt�'H��OF��jcv]l����Z��Y8�ot1Ts��@�t�������F��}�
�Vo��HW5N���`��x��,�
��zk�������`z�����~�m���F���qpS�K6f
J��
q(�MZ��.p�d���h��J�Vd�����R�����O��M�FH�a����x��������hz���%�G#EE#�C\������xB.I��TN���,����|V4�e���G�N����%�
��8/P��*+\m�rA)�
s�)V��Y�<J<?:�����~=B�/ i�[F�^An�YC3���E������_W��y��h����N��
��/wc���FP~W]�����_�G�������7GQ��.��0���$6�uL%��>3��B��89g��h����~���b�{�b
����>���9bS#5����R�s�Tb�� �������dM<__�!�u���Cm��1��s��5hBpPiVW<w���5V+>�m��k<P/7���
�i��������
��<�M\9&&���t�� ���:d�B����J��"`�J��r�'���x�6�>X�!��C6����;��9�@���,U.��~1u��k�A���ZH���e7E���"���Q���������z��6d�hI�z������3�Z����8�;�{CX}��� �4�on�h�3�b��i�k ���a��J�=kw������G`Fm��.���M�L�8��4B���������l.�������t��c�&��8gW�W����(�d�5gC�(��W��"x���f��7�kB���x�pQ���l� �8�G������b�L��o������:��;P�����:�1`h�j�\����Y��dL7?H&���P@@��+��-�
9��GP������V����a�wR.>���R���B��k)�\��{�S�������5�����J���c��hjF.��wW!L�V�>,~����DL-{�`���zp�{N|eq���~te�7��-466���u^�a����
���I����M�����V��g����q[9�:��O`V�)�X<��]L����L	ov��AI�^% �5�D�ac&S���B4��S".���'G�G���h�O�A}J�l� ��_�
2�?e�0Q'7�f5	��,>nf�������V�Ul��+��U�����]�s����x�f���y�z��HQ����+.�������|�t������W����TP�*�	E,7�q�]s[�;y��-V�O����|��
�w���]^�P������*]D���s�����hl��u�-�Sf,}�M�YK
W.*Jk��R;��4t4Z+��D\�����Bh�E��jv�HF+�t�\pL8�I����&c�k<cF������,O���E���D�M�== ��2o�:��O�)�\��P���@�6�j
����d��d"���?>�����4dq���+`+ze�q�gZ&��
}�����:�j�R��c�\p���((�A�r�<d9������=�RC����V�� k��b������7*�\�H0�u����Nt=�����z���R�������R	�X���0w���@L��c��\]C0���B��F��ZD��#��BU�,V5^
7?�-Z�Re�((u�mm_����S��#'@A��U������B�����#a�w���������������
�������[�D���%�7,.Z3�%=��T�L�edbu��������K���-=��H����%-��Ll��":���k�W���Y���`�)E��;8%��Pr9T[oG�<����k�����V�3��oAoaC������t6o:���4
)��[��z�b�%GX+\U�']	���BK�B��V�>����_��m���[����R�>�Io��J�b4���noTG�*R���Sq���2��f��!r�U�7z�|Q?w�v�9�
���j�
�����M3B�n+�S���%�L�����J:��j��}��D��jt�R��'4��5��I��1a�s�S���7�y���om	~�~�I<#�)U���Dt:dl��"Fx#����C]�"�?�c9;�ix@�i9��P?�rfS�6OpFO���5�2B��t��G�$���iH�@U����(�d��*0��c�X 
:���I2*�4����W*�r������,W�[��oEuO�'E�0�VX�PS�������%��.z�����V����zn/�N�^{���yBr_�\����V:��}X
����=�(z�������A�-5�!����.D U ��^Q'�\[f<�d�:�e�qf)g
������dy���D��f�^.,@������"U�YC�$�Kc�8<��s�'P{g�R[����y��n�
^hyN��3y�Oq�3�E=���<�7b(�
a�-��_����<�b����tX��jLJ"��*W��*i�9
VQb��\����GV�J�6���k%q����2B�������M��\@��m=����1n{%-^hz�#R������|f�����`�olh�:y��&����|�^H����s���T��
����������V��)1����������5P	,oon�q�o�}�����8���j(��e�76x�7�?d�Y)��~��G����S�ac�7���2�������{/��}�g-�����6����}F�(��#��*[��\�#n$�b��������D���%��^��%��c=�&��v�5��������m�lt�����2%������	���gYA� #*������{[p�q��5+V�wq������o�k�������{��b���igg���=6�-�h�����������d#��<u�{j��i~1��O�����sX���q���=�{�o�����gT�t���}�"�#�zi�������������k�����h}mm{kkqeee��-.//��N�a:�������$Z�G��o�d�r��*7XsQ67]].]����
}`�EQ#�I�{���b��b_3���x��L���i�^�m��Az��\��.oN����t�������"����z,��J&1WO��J`�%\���lmL�0S<7��;�Q��
F�Sh����Oz9P�lI
i�7�w���*	��s��;t���]��m�����Zku�U~|x<�	S�2f��O���=���P�lq�\���#� �����6�����1
�\
�b�[UH�%�-b������r�y��g������h?3�.o�n��O:'����vW��,�������b����1�8V>�WBl�(�af����,��3a�3�������N���!$V���-q��F�w�(EN��4G�6�R�n��3��`�����xN��1�3���W��/�����w���Y�E�C;���i<H�;L���3��+�2��rla'���aP��#W��Kt�����P�mb�R�]j��0�]N�_r��[���^?��A��������1�%H��D�H��lDX�"<U�2>9f�UNNr=e��X�Q�;�&�l���=lq���Bv�1\�PH�����<�;jH�������v4Q�es�Jv�b	lst/����5����,��z����<�����o��Y�W
��w�8:<9%����w�}z��}�����������)��7�	:v�D'�%_5��th�N�B�Am������`��?u��+�����O�T(���G%��3�b�w�zo%����������������g�S�l�s)I�nm�)o���2��J��'����������cN�+r���� k����Kg}��9M�8�8�������I����V(%N~+=��2�d��r��%7����}FbL��Z,�,FqFk]L�*����^+�6e�t�s	,�Nu96��`~�	������S������Z����1�zR�����z�c��|0��Q���k�:`�,P`���mg_Fg��������&z��#�|��~�~:I�<�^n�U�O^������j?^K�����v�U�
����f.�~[��y��S#�'������i5T��T��^�&���_����k�IR�l�k��ov����9��I���m�1�����@N=�	������>w������L�4��.29S�7�+v=_�^�s+��Ihe������1�5��&:~��������{����������~{���n��1���V��[��'Z�0%����>����?�i��U��vo��kR*
�H�����N{��S�v:��h�g#���$='�j���T�DT��%����I���]7"Y���9�Tf�}9�^�1X��pz���.89�
��>8�t:=��Ey<q�0@�6_�|U����u�6�WJ�y���c�{�$�HQ�������h7J���e���|�2��L�����45���)?�&|w�74>I�����(�\y;�wSM��2�2�x���k��i�	�
�����bC�E'�l�/���&�%�I�=#y@���Ejp�"a�p/���n��k������[kk�W�������g�)���$w�Z�k��hy��AX#}W~������E�.�QaZ.�m�J����N;�;�����;;�g�O_\��g�Y���^\�d���4�����g���z��
��&fD?z�b���z����}b��O������_�ts��1��IX��������{��"���^~��B���m�0e���/�-t4]��x7����6����Wg��Z��o�onn`�?{�;�����5-�x�X	����.k+�i��Xf�B���;��b�����C�F���o����8g��e�|��Ef4����M���a��X��� 5���XL��a�q���V�G�|s��2�)6���W���2����Y��cl5JM"�a����A����B�q��m:H1��C�*��f������I�_��Ry1N��}�������n�`��-m�A}�'6��i����Nn��]sX�6Lc�Yw,M��w]��\;=w���~�_���?��^������U����U:,��U0�?�9z6�������^�"Y�FJ>;��\HGu���W����\qP�r�2�].e��e�������
��
�t��c���J"����(�\��q���G;�?�������!��'�}]0�8�Q�����I']�v�;z�X�����8I��A����68iH���*��a|����.GO{���D�F4��u�VtB����v���W}&����k��,`�I#1����FA����9��}�,o)IW�������s���\���r��`4'�y"e���2rf�_���T��%1����,2_������xi'����Iur._f��\�/������]r���3�@���{\��s.b�����&����(m�,����`����W'�o�s�\������vj�������%9���P�>O��H�����LwB"���������p��Z�y�G�Q.`@��O��8{��t�'J��'���P���fE7� !�I�Mu�i-8������3$l7�k�n�h��JY=�b�����G1c}�+qJ�#�)cs����3E�d�0���8���b�)`6�����Z�q�T�P��vY����l�F��A�,�@d������
�6�.h*��OU���)�$*qvQ��Q�h�[�e��������I�T�k�'��h��,k
]�6WU*TY,Qp���u)bZ�����;�Jj�,|�[$��&�NA�
Q�������j���&�:Gc��I���
��cZ���A��P�v��s"��i6�w�H�Q��c,�g�4{�
o����|�]��s��U/n����	9BFG�rqcV���8�L�"q4]�O��Ap��������y��k�T�Vdu��Nh��������sp����x�gl��q���,��~��;�$G�F���u�U��������D��>t�uv��b���~��p�F����X6B#�IE��e���wYEl4�J���/K=��r(���K���\�>zt������M��<���F23��rp_���}������]|�Y���~B�9;
v@:A�:�!�`	������a�'��U��"M�:����:%�nE�e��7���-��yW�w*����/\jE��Y��9c�7T�e� ���a	�l������]n�N�<��#3�j�p�"x~s�����6��_P�O��o�Q����O/�q$�9�(4�a4I2��s �
�sFl������J�Y�H���z�����;�f��wN�*�8��h)Y������#���(\��62f�k<!��>��W!�xT��a����rE�>����:q��{F��%yf�^���r��DA.YJ��f�����ul,d(���;����B����nS4~�'2iq����C����&�V����%�N�Ykp�=��o
��K��c��$\nN���
W^
@	�3�0�����:I!��n�*�U��8,�
l��S���O���`�������`����k�����A�dX�=�x�m���X���A�Mgv������a�>�=G�(��3�h��8�m��4�����0��
����w`���5
�5I��K�Z�M]4�D+PM��%`E!��2�0��_a
?��/-+��qr��;��/�������|(���B���#K3,Fd�26;�+�+%�g����Y��3^(���$j$m�K`�+���|�<d{�c���t.�������(|Q���F}Nr��)^_���M�m�c�%du���
}B�N1����c��h�ZaE���U�+o���/�gf�)-�@�u!����L�������Z
�.�i�IOj�b�7��y*�|�
|��n�Ag���E
��PI��r�q�!��K��\�u�4����@����F��J%`&����|_0��`��F�7�.����D�%%�� �w�{�=q�N�h�9��g6�(��>i=��zW#x���2���F3�pPXH�����R�84�4d��
{����9�������N����]�|� �]y���*,^Q_{��o������'�
��mZ�_����##���������u��a����a��F�����x�t8H�_�(���jR�q2�nX��;*�CO5�^��������vr;0�)m�
=���q��J�W�@��N��Z5�w5����ro���{cuCR ��Q�OLY�?(T�>v�+� ��+�!F��d��a�7��jt��T�f����L;�������<��3��� ���q�p����9���::m���S��#��ux���Zi��E%�������&�����b�u��v�^�j�|�9m��G����z���W�y���`+��zL�w���ui������N�3/����U��|s��H,�B�x����?�%��d]�����h�m����� �k�g������.xBs���E�Ek��<���I�2uV��~y�o�RE�Hy�K���,��L�JQS�r�\�Q�^���}��=�$�{1���t��-n%��CE��J?�7�WpL��L	[�����+�6�i�+3����v~XVN1�����v���of�������+�-i�e��cs	��������;���q^Zs����p�q�Gi��Az�,����������{N����(��@�u�����g�6[�8|1�
*���m�������b��A>�[���"�T��'����K����FP��s@��(K�2�0gW�����,)1�����dx�c=?1/������@�A��39M[�����.�����/� QA)�=�T��+#�I�� ���>����Q�j������;�?��~��s��~��s|�������w�cz�o��*�k���qr�x$Ip���L�d����f���O�[F�G�g	�+u���E_�J�������-��$����'4W�.3�F�����14�$`x��p��7����?���y�2�p���=�1�"���S�#�=Lo�����S��o�2�����)I����i������B����(*�d����P��?�FH-~��}�]����b���z�Y�K'� 6��p� �?��IT�Q�]�ie#����[=���d�l� ������������B���(8�2��=F����9��(y$xQ'�U����,u�1���S���U�!�=�'�C>���x�~B�%&�������+3��.�1�4�.d_�QoA?��F���c����'A4�;�w����y&�c|�N��4�*yopS��;�\�
 �+ a�������U�$�_%���(kFp���$e/|��^4��h�
��i�E��|d!�.+���u��s��s��Ve��k��05ms'8�K��K�
pi+z�7��-��Q8��|�CC�V�
`����u�>M2>�
X��-���N�[`�t��� �l��!�����	os�o��c����+�����X�����7����5/O��#�����)��u�w��lNA�o��!�V0^G��)�wY=��h<.����������[���C-�Pz��W@jnQ'q��U�K�Bp(���R��\����Q��9�&�����:�~h��U��f�J!����_Y���u,�N�b��a@Q5��&��)�V��r�N����O�x�s�3�
|84��H���6]�V L�,����iO���TLcQ��p�q���{�+'!G����l<&?���=C���ZR��|o��R�����!���K��u���=��aP'��U���2N��%�bL��
.\U}<(�R���T�����p�_M��']�l~'?�3�S8`V��Sx��������'��
�y-J%q�����P���geW��{��Cws+b�w
�O�(��uZ+�?�1��Q�v�$C<�S$�!Nb/H������~�"���� _�� ��=�>{���������Dxux����wO� X9/�����r�<�1Y�Kx��
�5�����D��t��V���sG���fF��p���#��V�
	����?I����}G��v�8���I���)�mG���,z4��w���)�Y��Zd��?/�������x �
Ia1r=��B���g��$Q�����G����=#^lV�����W������%t�%�����d��#�����[:����C�$�	��EO����K�Z��<�D���vp��1O9 &�7����$Q{���� ���:5i�v%��t�=�J#����V�3��n.,x�|���X���>V��<%�A�gd�$������G�5dk)�R��[��`�A�y�[`��d"'��A3�	J�����D.���z�
�3��/�������A�6����z�cR�uVM4�L�,�I;�f�W�7)��k��o����X�OF�����K�����1
��[�5NH���n�Z�S�����)������u<O)��:Az��.8�@�3c���2��3d]�w�Z���(O�������pf�~��nY�O�+�7�����a�������4����J�lgK'x��85*������V%����+�|
W5��t$���i�	T�dbN��bLt��bHD�IK�In��O�xjS3w�������D��?����j�%�|d��)_��bl����b1���J!��]i��0}���=��R	����o�am����%�Zy�_�A���p[���og+�>�u��J	R|������o��{��G��'���&f�@dc���}wo���wo	B{$�����������X�'���]�Nw��s���?n�����S����	�o���z��6-�Vx3X�b��5F����9����r�8�k�_=kN�m�)�"���8��
6�u��{��m�����[����&9��
�����qYq�r�.�$zB�B����4�^~i���(�v����y�tRBy�R�GB4F��Nlf}=������!��>���!�d����Wt�#��t?�h�~����B�n���#L'�<�|m��s���G�����,�g��`S�X���F���� ���U�Y��s%�G��Aa^���t{�Eh�f�b0��\�l�Q[���!lyP��|���P���u�lV�[��
��.�, E��%��2024�W�I���ZU`h�����^+�:���k��B�2�t�����J��[*r��`z
�2	]�a�4�\(2����rjr����9�����8���X������$@(�������p�:ZE�|�un���mR�>���-�l�I2��n�L��;�mH�*T'�[aON���a����&�+�����iq��q�Q��,r]T��oKt���o�B{�
Q����}�>�7�����U���v�GJ�qB�5�i!j��ruU�E���;������o-��M����g���U:`���<ZC������G�����*�|��0��(��S��5����F��OG��R\�HP���r]�������S��@�q�N����G_�+���n��
�OY���4�3"�#��|!��C�GO���������� -���g�o�y�����_]�F���
� ��cO�-���7�=�_"T����1TQT���O��QeVp���T�mj�zR��*�#hik
Q�emJ���*���0����][O��������Z��t�okU4� ��tz�6�3Q���^��������H,sw�����1�:��&~;��R��g% �}�y��r��#G�P+z'y(`$���#3\A|p�Qr�W��pif��h������UJ,��s��Z6���Br3J�}�j�d��'E^5^���{��.z�������P��YV��
�Fgv��>hd�F=9M��er��53kI�Y�g������f%L���!"�o@i�ZCN���U2���5	���2�	�s�������|�<�N�y�/E�aY*�M�]���H�%*M�g���wN|	�W�Mj���l�����v�rt����M���
�a���������W�)qJ�9��3�F�N�n ����P���be�.?�� ���H|�/����������?j���B9B���GR���lo��si����Vd�B�w5r�"�����\^-�)���"<��Ns���%���?������pY�w��������*��pF0���"�N�D9S�L�U�,f�W�+9��XQN����x�z�R�GT`���T���0�������?�Jm�}�B�<�������(�e)�?(�����V��\Pq�������_����y�]��C?:o�f,��V��@��*��
b��t���,��e�C�:�����ZaK��4���	�c��Gk/	��X4������A��A��[wnm,{FNZ��}�j
���'�=��A�{.,���%v*�q�1!�����i����Ay~v�sY�����}X�M���c���>\����2�Q���)�6�m�q��������z(����~��t���b"�<�x�3j���������q��q���SJ���+�O2t�\O��X��2�r��^2��"�<�
e����sh(�91�N�NzK	�5��/���h������;���-��SCW)k^�>������cv�mB��F ������~�67������	�
�2��8�J���������-W�N�f_Q��(��F�U�yD���F���wv��������HCJ�I�:Q�����tE5%-9#Bs�Zr���3�k��w�o�t��,��$�@��`F�q6�@��� l�����o�O���1F5]�[��]�E���1bf���7��2�iL.��Y|���w�[Zt����y=]�o��	t'�������oV��;}�����L��C��-�#���&��<�\�>������^�s����~���o�P���H���5%D`�`��
Mb�$���=+���r�������F��K�J$�����Q�����Q��|P{j^4,���vD?5�b�&�zX�3���-3���bs�q.�	�4Q��-7��0j���2Z�=���u�y=�[�#>Cq�I�b��p� O��fq���sF�cE0�����S������	q�l����bRG�Wgi-N�7Ah��o�;�]�}�;�-����*���
N���T�<�
���m��q�8�q�-%!N;��hr@)��U�Nt�rC�Fe���l���XK�(��<�^��p��rG�.~Lv\?1G4c���"g[K�P����w��	j�&��2%����%����[��GJ���p��M��1��0��V�@��~2vp������W�es2���$Q��Ac[5����7����w�~]�0xT�z����'9�����oj�
>�l���	��JK�V�BD.�I�-��zRM(������*��zz���1e~���yZ��yU�3���4���c�0z�'�6����*��pu�}s��`an���c�K���;/}�b�|��Q/�#�3���y~�������W��������w����o/K�QW-!R���X�1�VV�5���j��
���!�~[�4���`�<��jF7�9���a������j���������B����'�0�|km���5��A��x�����b1�x��4�`��+4!�db��BbB_���L�����E6��������������x4��Q�i���kt]!AN�~b$����%N�B��������^�\ ��rT!2_�N���l�u�/@PA
5yR�<�&?Z�G�k��N���Ks�P�M��7dV�~0��d.nEz�`}T9�
�����p!�7�i���(}����B�{����$�V����C����� ��0����}pk�~��b��4�,�yb��Nb�$|b��0�8�P���*e�`C�T����']g�ZwBa.�YJ��{�Vp|���>�D;�w�pf^�C�N6�V@+b
j�jJ�W*��sq�5��%p^8O��4rX�����q_R�
q�?��p5����	c�o6KE�A7�d��QLi�F���&��y��&�����e2�Y>���BW?^������q���d��n7s��?BR���
�@v�C�%g(��1��]�<I��c��������xHg�T�%Y!�I@����(�e�� ���v�
ZJb�2�Fq�k�;=N����BM�b��3+	�����Hx�~�����F\���VM��*������N���K��F��u�l��z��6�A���N���^�S�q����O��\c����-�);�����B�Y*
0&%��x����d�CZYbb�DI�L�Y \�b�Ky�~u�R���V���Q" n�S.�����u|����0Nl��c��{�����9�_�� ����F^F�P�X{���Wh0XQ$����AvZ1I�VxQ�}cB�7��>��s�<��������I��8VV��`�u�
,��P�Y��=T���M��K��gv|�����/a��r�����������q�&��>~���]��#z�$�_����<4�B������X��0�y��M��n~)��_r���7pu��q�M��}��R�pt���u����iEX'�'h
[��*���AQs�MS���O9��@���7p���d���A��96�WY?=�m���*c�J������1nSDs2�I�Z8&[PjV��5x��r��+�>����|'"�?����QK1��\�+��8��?Trb��0'��s���i�d�e� �s�3�m4*��=	vQ]k��F�`�\�-��R�^>�I�6�`���
g)
�����c|�
����dSo�)�kX�������1���	��4l��p�,#�����mEJ�XB���pu�{�*5�S�u���}-�G,�����?��K�x��\�a�B�1GZ,�G
��5V��3j��"��)��%?���#ic������(H�������������_�#������tH�Y0o#Y9L���%M��q�%���82�5���L��:<�qh����|�0��;��9���k�w��u������j��1��������{J�E�fe��I��X����*�5�Z$1��U� ��Q��w��Qw�k����H,����N����1�oy+�|%�a���t�Q���=*k��? �J����Y�/���G��1��U��iO	E�9��J�M�����s
��p�:"'������-[������S���0
/������\��A�a�]C�m	��	�k(�+���;Y���@����/� /�L��$',�E�X����\19�kt�w��O*S���Q6H{����$5V��Q��:�����V��C�^?�}��q�#2Ka����C}�I�p>�~��Vd����h��3�H(A�����t)�1.
BL�T!a�NN^t2�������Fo���r���l�e2�=svE�l����z���m�Om3�:}�H�-P����)�Dq�Y3��<q�D�}%ow|k
yQ4h��W�MDH%^X,4�[/��(QO�TEcR��A^$�����D�^t��$���IzE�0f���W���N��D7_��C�lVp�E��G���}���O�rIv��T�����7`������p
�$���c�� � A�;%���XSZN���"���!��\%�#�����"�C�Y���M9�����%+�)����iY���*�`�mb����~�R<��1��-��(RW�����v�Cm�e�7'�����d�Z�
��G�KBO�������O�������P]'��q�	5�$���tJ|�S������!��wh�A�qK�+�d�.��w��;G�-(@*����'�W��"��	7��M��'W�0�)�����"\t�^�;�<� 8��]�\K0�.y4��#X�F����	�����u��h�3r=���b?��"��O��n-F��/7i����*��3���;[��g;�i!w���.��q�M)��k����R5�r4���]z=?>:�>�#��Zv�����M����c,���
���z��"T����b7Jpj�;������m�A�������_}(�y����h���l�r
|��H,�����<W�E-��xHU���A��u��/����p����*���������{$3���/����d)W���%�2"��oI�P�.�*�]	�����|���	��%o�D���}�Ry��/|�3A�*����	�4�_�
S��]j�4��K�S����6�k�b]��1�}c��p�z)�
H�vQ�X��x�c�&�u��V��"����>�E�CELHD���e
:r��W�IP$K���P�K�tN?�����d��&�[f)"�<��@�h���������}������9�"d����DyZzA������$����������g*u��*�{>C�=�����:'�;�{���#����K�*��U��54������D�#��E
�������8U�"�a�+���V�b���!mW�0���b>� �K^��E�BU8���@�}��(�K�C�p.,�MF�rI���.)���..�R��u�L+0w��
Sz,�%�/ #��b
�P��]���O�+G���Y���1��h�z�rD�5���R|Azq���H��JX�V������Ba�����[�/�!�$_���*.(���EA
p��'�q���}��-U�W;C0�ddpc6$��U������^��G�Dzs��[,�!Pf�	T�(83y�b���
���3�9��`p�]wS*�5����M�)�,(j�_�/������)��F��pD>���?��3]�zX���V��Q<� D�5_��W�=���ED��W�i������<��[v-���bf�5����Z����V9$W���g��Hd4���1n����f�����)N���9|�y��?��)7>���+^U�&F�O���(a��
��( �w�U#��83�f��1�E�x�����?������d�'�%3h�������$��TQ�
jA��������y�y(<
���3;���
>�YB�xO=��_{TU��l�`��U9h9���_U��hPe[R|68���Y��-�~;UC����c��K*L7�+������HHI����������-��5����5Ra�_v�9)%�����M�#�h���!������@w�+^\��[����C����)��W\	������<py��Ig��sX�������F|o�Y\X
�C�i
�Z'��g��dd�0���D��W�A���,E��1(����2L.o��{�.B/��������	����f>�'sn�y
�����o�,j��*�^x��wq'7�d4	���Rz^s�(+@�|��j��{D��L�#81�D���$�����e�[w������=�]���*���D�n���+S���YF�4WxS����kO�#��X��&�,O���D�3���in���mP{Na��,�q`F�2&{`�L����>a�<'����hz#�oJ�a�\!�F�b41��b3��B	��L �C����BUc�)���!�ZL��G����R:�����%�LPf%����r�����x����z$�����/:tE�*$�5m*|��F��v����_�e�>g��1�Y=��l�Q�+#o��
d���x�]&-�'5�*��nE��'Y^A�o�;��s����N8M	YT�TR��&���;���%��R��-X,m�����W��
!*�������=1�l����������*�Re���J5S�G!c�H�F;P)�p�tKl���8e�^�xvE[\��G��%ron+�Y1/�+z�s�m�������A�����3���gAUd�f�X
uG�r�� &U���,!�w�X�<��rcf�T�����c���0������o�B-Z��T\�y;��bLH�t[����38\]�(�f�g
�P��E�B2F��
��G�MPX��%����M��)���Wh����9�N\���9.4)�2������5&`���&x���J�8>P���w�zgI�y~��YOd�X�{���@3��o��F��3}1V�$��V-����������;#Bdt�T�&_��NG�{b�}����({������uRf�L7V�K��	���������<���c��u]���~�����-����/PY����N�M�~�^*3�C���+�y��LG 1�s�����T�H����7�T��pW�]BAQx�S��2lf�0x�zL*�L������X�$|E���6��]��eE]u�\}�"&3�uB�����F(�S�e��0��i	-���P5�/��t������T�:TV���F;���tU��%�bX�������|���+7���v��
��������[M����F�Mp�@�����B��������u��*/�7?�B\��	AR��|�SZ�t�������N�h�:�Fu3��%����Yb|��G�H(
��H(2��+��N����R>�����
�N���D��_[�����+�`���B���1��W|��#����@�s��qD��lA`���N3�{��w~�{G�+r�������l����]U��zt��6��9`v��{�����z�Nd��������|�qQp8��y`�t��g]$�dZ>^8H5P&a�r���L-���)�=
x�~��Z�I���t�!.!��{�pK7��g�hh��{�#Hq2,� �L��\
;�8C�T8��
pX@��=�
n%��\�:9�9�t�A8l	�2���Qa���{#�)O���$�a.~V�������N�+���Y��$(��CfDS��X�;��i�@�+H	�kask6S��z+�f0�l������$Mr�Ue�����C%�S`*�	���`����|�� �� z�� ��m�"q��7�^�u�e]�9/���A#�����-�@�z�V���}�\�S��1s�����q<6kW�EWxj���'�NC����D��I{u ���/��M���O�?x���P���S�c��kVi:��
�D�m|fj�����-����		T��Ig	��K������:�����"�4?�>n1�,��Bl^��$�Ue>��i��;F�k�
��5i�hlKk�+P<n>��^�G�7��^�Y���0���\!�=�,h�.��X�d3(�k��������gFB|��+�f�j�lc��H�X!�F����#�^&'v�H�9KS�p��5$�J�/Cb�wGi������Y�����:
����j�F�:���Pq��+�cR�}G��*HD��Pt�;�3F1x��]*mu���%g���i�,r�����M��|pR.sAP�|�XY�^gr�_':�S��$�a�u��,$�8������1v$2�<�v9�N���['?kM���S�Ll���i��7����
��D�g�P�@�/�'��e����^�%o����:drAm��,.i�^oAL�z,�c���H�@ ��p������0�:����K�NP�`����K�Rz������������@�+�^�����A[�E!���se���n���(�>(�y����w�g�� ��`����t�7yP�g�5i�:���!ZQ��WB�sV,�m7�y�.Zc.�,��`�=���x��M����]�������?p���^,@@�fd�'��X�Y���jM`�WO�]X^vQ�g�n�������R!�WjYaz�w�oqu�@�j�L�c�Eo��f��t�I�����t��T�^������)���F��Y\�g�7���y�+����>5�6�[��i��h=J�?d�-8r_�\F�7��<�v:�@D�B�����'��Qn*r����
1�#���'����i�N���p(���P��6,���Z'�!��F��Cg28Hn����D*���,Fk�Iz�!P���� �t����K��V������2HArf~;��7b3��a<�;>Q>|���,z�V4��$@���U��T���bv@��~~�����
WF�mD�E���e���(���$�?w���$����mB������D���d��B�G@����^1y0H.kp�9e�)�cGO�O�U��3@9����J��_���������l����;k�����{�������=�0���q�����:'��6�<m�Nf��%y����1��0�7��5��b�����z99�%L�*�x1��2�A	cU�2H.O��y�i�W(��f�#����O�������d$D/t��O���nB'�
�K� qw�	�EK!��M|e��u�	V�"���=���d��P��h��zX��Q=*9M�t�W_�����hN	TF9!�/�3H��K���q�Y�ZwX��9���5}����M����W,'��TfR�i3��B�[B�����Z��uTJ�����2�V���������
��X{�>[����b�2]���
d���)u�}<�����l����40
���w>������o���	���a�(�d�H$��\a�Si�]&��Sf�
5R����p�D/�����$]�>V�>���X�#:Q�5r�3aX� ���%��)J�H�8W1�1�$RI���_��E�^��g�
���O�=��f��6����W�,@��BSM�v�Zj�6A���F��f)*���p��Tfa��%��I���1��.D����0��L��c���L�<�g������+��$>�Q��U�zU���A^�GXB�s]��~1�c�10�d@��G��{��$7+p!������9�e�����<�!�C����-����y����
)��U����(��?�i���B��������.��e9Zo��T[]T�����]��J����YR�]��D�?N��xO����R��S�����LmS
�_������b~Em/�b�e"�����Zs�l�:��#�:�=���J7y[N*[���<QK-Ly�Q������s��2��5|9���<8B[���j�M{K�������P���T�JiP:����u�v�w>�O��]��Om���WxmU4]3�=���E�b�y���C"
�(���ld�b��H��������:������>%D���{��2.�%c�;vj���{�'m����nEb�������]�T�����z�T�d��|Y��L6����=�<J�3����]>�3��g���m����k�"�p�X�:��2^!PV��mb�
�����.+��@�;�O�:Uh�I����}������a]�#�P*�
���������^{t��^R��:������
���
i�r��T��j���;�~�)f2������s�}�o�������]������ ;AYx��<r[�Ph���N`���
R3TZ��W���+�qK^�����XAKI@��f��#����B*p*��?@�>�$���&]��6$6�_��/�`_�gJ�������$�X���b�����/�������L+f�Y��T������a�p�����9�
(��V>�����DHY\����n����,��
O��7�:8���|:���;��k��,��/B%�t]��$�W�h���~������?������K��g!�1��|��*
cI��^<tE�r��rpX���Q
5�Q��	�	�"LQ����pZ~^�)���Q�����,��qb ��f{��MQO��*N����g�)�����i �f��Z�����,�A+������Os���
_�_����
L�#~,�A2��u��o���/W����������v{
�|�	q�XZ=I+��c�����I�(bDb]N���&�8�"�q�
q��E�l�w5N~���<�J�
�8�~�k`�O�V����hI5Y8�FS�6�k���������@�k
�����%4F3�,�p��s�A�b�yi�x}���1#d���z%�|�zw�N�'(����S�\�2��,y���I+���G�D.���$#�d�m�f`�c�mI��{FfQ{R'�3�^��P,�.�O�k�=jEUU��B�B�,����XD���?'��*6Z��y!�+V�Z4���=����`�G�+�B�c_����N3�`�p[��_[C���a�O�������W��+�hYJ������T �+v�����+�%n���+Jy�q'G@����!�����C���K���W;:�9m��;g����:��0���s8}5��2_�.����n��8`���>d�M��`���Gdl�����a<@�v0|&����xI���g��%�C�B%i��Y�Qg&e�+�2�����w�ZC�j�4���9��*����2���������=��	�����H"%d&"�i�]X��b���&FdQ��P�� 1W���HG�r>5oe�x6�p��T�-e�X�����jd'�1��k�8G���5l���b(�w;�ER�=*aT{Wh���u���"�\��/���8��9v��_���X_����VS�l�3��,��;�������M@.h��i_�`�5�4��To�l����)j��3���M�i���-)���L:+}2�]���V����ZZ]j��q|j�&�K��a�"�S~89o�������(Zz�?{�[V������y-�V�a�_��bBWW�Pa��Q���GWK�(h�IuJ���4��-���k����\zy�Z�c=��+������
���"+�]�;��J�8�R}��QE�j*xi���$Ry��j����hZx����C����+n�f�+���*�E���u��B���
5q���+�	24^�����w+9�4*2��+��s-j�,���v�c7�'������#��J5�Jy:����\�1GPd����[B`���
�����Qc&ps��>c��@_��Um"���#�%�7�JY'�#�/�	(�xD��r!mf�c�D����L��2zm�<�  P�`~A@`����P�U�:q��Hj*��?��E��{?������|�UV��w��g�����x�#����j��Z6��bC�BJY������0���G��L3�bV��i%���jz��F��)�$�Z�����KJ��U.���x��������J���Z.��YZ������29,*���.���#�$���}���)���.����b���O�C�d��#�9��8� T���=6C������R��<�Ko���RAS��,A���S�t[�R>��c
�HZ�5�`%�AwiC����8:K��
�F��#?Ds�T��fP�HY���]���=�r��%��q�	�NIj1�5~Y��d���T�c'G-Z�1���:^C{"�C?��QthS(�8��RrH�]�<i�zK�7c!t�
cN���&������9�:j����H���V�s��7��^-.��n6�']2�U[�G��?��v�;�N�4�_}#%�It�������q�tw�O�W���H��Y���y=5�?�D���ft��K�}N 0�;}��t4H����`������H�$<5��O��9�J�i�����d|��Mn�������Z�i�M������a�PvPX� ��9Y���,r�s����r�j2��F4��sx}9I�����:�������l��k���s����d��OGG���F����n��}G!��Y�2��Nb�M��(��F�����'}�����������T����~�>\��)�"��}��cM����SK���!%]u`J���������8M2�!E�{ANnG�|��!���.]m�ha��)-K���_���
���xtH�P�����EZ�1��'4?iF�%���	I�s��s�����f�mB��;��:��.O�F���Q�'����"y��x��n-9��mF��������b1����Yo�
_���w��	_�R�3��n����L�	�&��6o�kx\�	��8���k��k��1t�'�T-���o��_X�~�����z���t0�y����a�~�S,&f�@C���L��1�\��7���!=e�|����kQ����z�8�*j�i�Y�T�GJjx0J���=��%� ��G��
�/�C&�r��CP�u��	��A��f�x�g���]N�,O���1���l��H�~�����,E�?�55��1�Y<�-8L;��b��~����p�#�	�W���6[QqC��T����C
[�f09�u�#�{�R@�|������C6�����3�x�A��|��3�l��l~�����,(�N\
����L//�Q~�������p����NJ�T�3���3��gK��2�\	=�����(&9�
��Q ���1��s$��42��O�*u��
<�WWP�A{B�,��V�ilC�C��(p���^�+�x�����R�{�_�J{�@*�[���'�������]�����P<i��w�,8h�H+�_�P����]K��V�V
�:U��]�����Q���3�F4��&B�3p6��g�k�e�{�JPN����]A$@�>0��|M�m�{�)�t2��x<��q6��A@s!���2`�-��f���kI��jg������|TI2��#�g�MR�P��6U�G��u=�q��' i=��p������N���@L����PTa��27������nn4�ae4G��^��56|26���Q�#�F��Im0Q���Xa26#��0��%kH��g�n�����V��E
L���C�"7��������O��
�]�T�>��3V�C&�W����Nd�������t{�
�^X�������1�2CJ�t5�9;����s��.ItgD=�e���w�Qkp@s���~V�����9��;f�/-���W��T�z�*v����]!@����4���������K�
��ub�l�aX/��#����Y���K�m��Qzu��	�>��}��}�2�'���h�����.����D�
�d�;��>���H�	e��{3��)���z�A�:H����6��������K$R�������W��������w�T��:�*����zt��7��4�t0EN�1� I�G7��>$���7s�*�5#�D�6D���fl3J���]�P�Vs2���/��_�^j���"�RP+��:��K]��GjY���S>��$v�C���_r[4�+���X���,�/	:�K��&5~���)�$7�(����|a���jU~]If�=�cpe�>�7'����Bic��j<��;4��"���|�Q�Y��u,�����f���Ik3l��(�/����@A�����.��bWf��U���B�:���V�%�Ah��E�s5S���3y�i4/�������E�Z.�U#�`K��/\�71�����q1c��O$�L8�@s������y�Bb��_-
�J�>��7B��^�����^�k9������;��F�/QC�%�R����q�a�m����N�����ZE�����	;��O!�o=�������?Tww�
��@^�������;l�<������b[�-�"���G�e����D���!������������h������BSQkB!,:���������*�~��b/�PVf�X��l����b��&���kN�Uw
�@5����������>@e�6o�Q�����+�
��U���8�������?��d���-#�
�����I16
e���-��y����=��������j�����������q�N���q�~�?6��������?�"�M��yf%<�?S%I�6'�U������^�!)�S��n���:=6�y�W�5=wc���'��������*�iN�[�QX}G;�]����>Rv�D�"���1��gU�����KH0�g�*UBY�(n��o�nI�	'9(�f���`sL��M���r�V�E�d����k����q��3flj�������1����3w�6�=�U{5��|�bZfCP��/}���������x��O�����r�<���g�Wx�������(�s���q�"��@�,���������k���k�����������J���|}F�������^�o���������������������vxo����1Lv���)��^3Z�j{ecm�U���O.�����G�Uo�x6����{��6��n�o�S��ZAJ��������d�%J!��y��PC`HNb�@������u������l.����������s��Wu�������w���lC?3�������(�2��Zc���W]%,���l<]�M0��7;W�jH8z�X�����,��	J�'�^~Bu���=��s������h�D��6�*�g���@���~f��������e���FEt����_�Qb���?`�C�����w��/'#kQ<v����[5��a.F/P�����=�]�k�r���?Z�PM�1�o���	��G$ho2)�GM@�i
:��u�4�N��`������"����q
� ��%�/�p.���rT�>�^&E�����J��7�5Z��P�z��&Anz
���.�}�^�����1V�e��}{o�M�j-�����.2!��G#J�y��Vp�AL�M��{�
B���?z���-��X��n�H+#*����T
��4��� <����I�� k��Pp'Y@�N�Xi��$������h���`87��]}���z��";���������\��������A�0?j�G�N��!x�4u�izf�tu��<��4<�J��8Y������>��#�f��X�zzxr6B����W�'W����tD��9[rj[�<�#�F,R������au$�����?�{��Z������9v�ip�C���y;�?`
����G����g��X�?t�#~GQ�����Y���}�@�4���`l�z��\���,<Ax�|o��;��������4a���u3_h�Tcw- ���K�v[p�0>���/���"	��-$V��^,�����/7hD>b�n�G�<�`�X�U5-���z�����	q����� ���=�9x��R�Q����:I�V]k�SO|����� ��W�|S��D����"�k�`�Yb�����,1�g{Z<�����-�f�GC2��++q�|q+5�>�&'�:����h��H�K[�1L�����
v]U��W�9�P��#Z������U������:�w��`��OD����pIH�����! 0���3���f��L|�m���"�D����Z~����t4!yI��ng�wW���q��U���6h9�S��������K���E�S���lEne����|�}?����IPfHR-AL":%HjT��)e��/��Q�`C
\|C\K���:��?')��|�?6�k��C{�Q��,^O����D(L�Ek��Dym,��b�Z�w�A�7$�G�"�m�,��.�r!zC�L��d���VM�
�$��	�E���H-����E����}g�����OG��Lx��5���z��zV��
��C���a+�z
H�H����4�!�"7SZ��;��}��q��h-�:c�r��Ux9j,�A���D������p���k@J�f���R��������/�oxs���n�,^��,���	@.�^2�.�@��J��Dn4��nCU8dG�
�%N�7����^qb���(�ho�m��m@�d��x�6�ao-���y8}_����4����]E^2��	R��l�.����`��5�'U����No����k�0r��CVj����l02���.�����3)�[�h�P�������+����������#i�KE]�;Io��	/���y�����o����63|��ehA���
}��P�#�{�C����kv���gx��+x�L��������2{�P�q����
���6/���~����]���[k:�����������i=����Y[E����Z��-(4�-�lj�w���#z���'F�z_Oa��4Fj���Qb�0�$Z4Fyz�Q����F�LO_�6��>�����R	���t��:��0���R�T����[����Jw?�����u�������|kw]���v[�~�wE���v
p��X�3��p� 
������*������2�����N_`l�.G��\�����1�
�/�]~�)�����~�p5���`��������
	aa2�����,�.�A<�'����
w�-wT���$��� w�d���$D�������q���������2����}�t�.��Iq��n��
����?�����_~�.;���43���,����@�|vz%��!{�2;x{|pxr|j�A�55s�>����<T��Ou��A�O�_����_�@��s��1���Q�	<(����ag��(���#fh[(q��_�j�wS�K�7�����EN	����g�' �>D�m}�{����"��H����hn>��|yq}Y���2:�^��.���W�����s�f����T�FM3��U��Q����4b�_E��q(1{4��s��B����A�k����{)2?�#��3G��V�&��g����&'2��p���]�\�>�(,���k��N��!�-���U��Bz"���(�����n����|b����U,�I��]7��5*Z�R�e���v��p��q�ms�C���"!�
��2���q�M��(��s���� d�����yu�Iy���Q�he�/o���G/���t����������7���'o�_{?c���?�jw�3?m��80��b�;���9�o�3�R.����
oi	�"{�G?�����;Id��hO�$�������<JNh�[8C��9)�������M��6������f�a��IL�
���	r]#�#�>�]�E�D�M���
�D{x]M9�W*�@Z��_��?�f�tW�4RP\0A+!���p���j
`M���a���>�y�`BfY��Iyv�+3X��-�3z�P�Y���wea��:"8�ji)@={�d���X�n4��-�[���b���
���j���u:�/w�t)�i�8��eV�j��j��Dk�,�)BT���}�!��H{	��@G�
d�Q�]3R\H����������5?��a�V�`d������+e�h?�2KD�U���2�4���#p���l����f
Qm�����������?����������
�jzS�a>o5���7H��w��r��n9�2�A�����F���Y8�a��n,���0�!���hH��d�!���'��Ib{/sl�2�-��!J����l�f�2R��G�=:)�-i��l���7�n���KD�;�>20#c�����2,���q������&p.4�5/��'��0ye��>��u2�{3P�?_���_SDK2D�F%6?��)}��#c�j�~U�4��K��I������d2�o�UeD���D
:����g>�6�l�s���M$&
�pD�u����%()@�dD����
!B��F���Z�Z�s�7Q������P����]�N�n^��q$N3��*�|�%|l�+%_���f����.Q�������z%G;�6w�|�u]�H���q��:8���zM�(�"��$2aaW����9V�^O�s��k##�^F��u����M�c|��U9
��4�\���x��d�C����xK�8S�"X�/X�j�ev�LO�f����0l�s�%�����9H}i�+��U��4���n5����yK�lu'�f�k���T�(���Z!$1ih�HtK��.'}{�%���\�����(�����S�'\���y�n
F�����C��b.����U�w����"d)����#��$�&E^��xZ�#��$-��r2�b�%x��������4L*��3�#U����v�����={��J(�{�@������	r�*�����$C$�b�'f�Go���������4Z#[��L�����i�K���gB���[�H���[V� 2����[�;����,��Z'�B�7��PW��"x�%��d��+YB���{p�S�_���*!fm}�Ki�FV�/wT��������atF�W�A"�C��5�]���T�v���rE$/�Qt*����gN��}��p~3���EV��C��4]���ooh�*U�����`;X0"���D�;���,����%�L��^�$��E�*�?�#�z���W����@��|��Oi��:���M�Eey��w����n� ��v��g��W��5�=�`�OBY�)\��Qu�@s�r5b�S��K��[���R�*����s�m{
g��Q�'��e��~��Z�����K���bNI�����I��&�I8mS{���XJ���,��,����S��	��1�h$���N��f��
�y�>��i�U������h<U�h���N=2"���&~��Wy<�~>�E1��x��?}�e���b�Q�������o�U}c�o?�P;	~v7���<��_F=;y�2s*+�~:����{jRD�u1��
}��
6�~�^��!.o�����E�UcD}yk����s��r,���]��N�z�-���\7�����k �=T�H���b_
��[	Fch�_�J�-��OW���7g�	.5��A]��4
��Uz �>�h�2��GW��!���M��Yu���0'��2sl���1�W�<��!Du/�I�-=xv�6� �}WF���Jw�u�\h��X�(��z�U���>"f������zj�Q���M�C�I�?�K�M
uF#f0��
�����Ft�N��n������y��b�@}
+�C� ������6��"���
����
I�La�0�XgyT4,���E.!�Qt�a�E�
P�n����h����M��u�rr���;�a�71K����@_������b��9�,E��f;
Cr�N*�:�(�m�j(\�"iU<�`~H��p��C;�C��� ��?�,T���e��D���H!����� \O/��m���y������	�h�z�1�azG��r����j(��6/$m���P"�j5�b7_��qg��"��*j���F*?@����x4�����A����w����=A�"g>��:�%,?�(0=�L����f�%�%��
q��|xQ,�@\S%Ex�NKo�,�GK�(�[��RD�`I�	���p3�4s���q.\�jY�%�X	�&�m ���>�_���W"k�?P��������]Q�+��{���'��$w�k��euh|��Y�����I6�o@��lx�H�� k��O���j�������}����/��Cj�q�������D�}D�'3.������$�����AN�u����gfSl�Y3��@�����#/��6�l���W���"���iF����@I5n3� ��F�{�w�i+X1li3{���0b%�@Z�01��o����������I%�
�����]f{{.;�o�
,e��g�2�[P�[��������>��n��^��(����"����t�c�m
A���'t���q��k��8�������+������������������Q�W*)]��X�����4���`�Gv��y7��0�,�H����_
=`��#�Y�
��"jF����	� ��X2�� �"���l��7���J��F�B����Z�Iw����c4��s;=k7���C���N^��v�1=|n�2�/���_g�@i�Y�X�[{z:�M3���r!*����Q#r�4;�{3H�LG��=	B�@O�-��(D�ee���D�0; �����P���]z��������	�#��>�>�{ �]��J*���ca�5���x�����v��{5zz��l��t�����^I�v���J�7XbK�^���O��#���Y<��vO���[�J��2�������HcR�~ �l9DJ�f��������#����f��h���������6������D����,D�#p�/�NR������P�#��g����nL�h+�Q�Z��.�=6��r*
~�h(�,��+0S`!<Qa�D4�r��jrE�Q�m��6��T�5&���3g`LF�t�.���>@f�=��s�wV��T��{���qG�hO��^�{`g��j{wHV,�xtu���C�T��_\�	��*��Tt�yU9:G�Q
���������5J�����	kJ����mP?
���O�}�X-4~��Yt�
�X�w6�����-���FJY��p�Sm��|
����M<���|���k�d2}�o@�au-�`�n����$A[��.� �=����?�L,hq�~%�IG�u��.)���4Z;Yi��kq���������+��~�c����@S�.�1
t��9-��V�^��;r��p�>�~�s��j�r~Z���j��A��S>n��a�%��UxXE;
d��]-�)���"��:qz�-J�r��Eb�9�X���:j����@�I.,�K���QS��+���v�r5����`�����)��E�FQZ
j��+K��h<2}������������2m��b"������������mB�l��~���/��l�v�:b��0�Pgl	��LK>��nG
�_��G3��n�h�@��a�� H�$�#��b�<>���K��}��t��Ga�J[h�pUY�%��l������u����M��_�>5���������b.Woznf��z���v��>����C����TMK�^0����U��&T�5nf�{��"�'\��������H'X:�����^����)'�}��]f����U������V�1�K�Dge]�@��5���x��5J��H �����5�m�����$������I,�Z����z����G����r����<'F��oc��]�����!��o�H�S{kYWc�\s�w��W�~�E����
�M��?��{��
aT3�j#���_�J�P��r
$��X�u~�nD4R#�w��-�h\DpON�}�a{VR�~����!~_�.�������9DvJ-���a�p�=
�mouo/��D�<l�B����vB�)����}
U����������l���V�p�\������I.��kx��b�����#�"�t��Xg�:����W(C0������#��]E��L���2�ZbX�J�K���O�J�i��z������%@�3�|��x��8����*����-�����	��h�|f<��pA�e��u'�9��N2��<oZ^�6^���Iv��,Hz�
U(�A�OB3���1�U��<�.���f{���u�*gj���=�c/��0�!i|��)Z��U�
a���r��V�bx��u��)�A�������)$"s��u���_�>������ ��$��>��/%��wh~5ep�;��.�
z��k.X��������N���9�g����nz���t�@����������:),���$.u�|���TP�S)���+�6�c������$����D�uK8���`��|
����.y�1d�U��Wi���w���vQ��~,
��r
�g���?�C��h
#^,��0�j? )�$��-*u=��T���Q�?�T���<�3I��h7���=�iiv��=s;4X)����RpW��,���%��
�>���q�����j���*R�����n~Q�����j�o�v�[,�����m�u�����q�����{��)�o����a�����^~��f���|f��(AD��S��1L����h��[e������bIx�^I;�,��+q,�!�|!����
���M|;Ivz��0b��W��,����x8�Ew��r
na��A��}����	����g3vT3����
�1�of����zD���j�k�k\�������l\(*�����16\�l���wHSF���o�h`����-�e����l�,}�#��d���U(�{M��f����\��6�r1m��$4���s9//���������'�F0r=E0_�5�*�HN|we����'�Lb�]���#�ZP6y�l�gJS�S�!��h�Z��6���!���4C�[6�)�����b�N�!@��I��2�1:K)��v;���X��EaE���t�~G����rjM�Jr�%.�rDS�U�x��S&�!\�%DeN��%A��C{S��!��<z�����-�i����#��5u��8=���V�U��:�Y�k�s~��}���@�Sx��0C��%j�����b|P)�4�7L'��s�Fh�s������N&Z�
�Jn��TH�C��,(���:�x8��w�G�)�a�Z8?a)��#����r��'C��FC����,`�Q��y�������l�� ��3�%9	������A�ifp��R�$��T1\7~��k({r�De�p$�;��k�$G �%��c1�����|�a?H95�����������
�Q�Hd��w�Y�:o���*����i�=��Y3;���m����B��"��c����(+
o��h���B� 3���d�fq������a>��6�(������#w0�R��`l9-p�������d�em	O��03�5���&�7�%�T���G�kn��������L���)��'�F��6*��@�x�|t�e=��.KC�o&��|zYR���%��}}n���������Lvyo���B��������c�����p�[*O=�bk-����|�|h&�|����<�&�z��/���p�U5]�P��F�S<����O�����?
M,Fq�/��W������a���Wu������C��]:�)�hZBT�@��A>-
����k]���UA�oW��<}��r�9�&R�;@����_���������e�����'�T������|i��&xl���<z<��
��zL��A�k\����R��&��r>nyb���Gh 
�����L�E%��6�wE����U�b�UIM��~]����Eu����.O���>j�������bFRyp�&pQ#���7�;R��lEZ��Tv�K�g���o��j:����+�SwN����_��p�����UC��-��6hL�%:9�����^�)2$}lg�J�p�_���[�9�bO�p�����z��l8���$'����7?'��MK��^�	����+�A^6�Yq���U9g��f�����i#U�*J-��l����O�-���.������r�R5S�z~���x�YH8�
7WXQ�K���-�*���*�����B.^{UD����v^�@�*�x8���\JL�������P�u�1�f�h���g������M�}�JX�@J@�7%�����3+�>�D}8���~T;���?Sg�4��Q40������mW,��Xx�hu����(�
��\&(���+3�z�W���/�A`=8��v�s��������T<��3]�/��b��~R��	3�
�<��kg$�IP��f�4�a����A�t�I��A����s�\ln:�`�p�(d}���s/af/;9|�����������pd�~�|x|6P��l
]�t.����Gx�)�8��qn��@�Xl�����
qk~3��n64��h;:1����������fb���FG�Gg����$~ ~7��Z*�O\�t��h���F���,����w��G�{����P���v�}32(+����E�-�{��e����go����o0wx�~�28�0!����%�����,b���V��]���g��S���L��4�����M�����+��
A�7X��OKC�F����A��2�O�~ZQS�����{�"����Y#&E���x����e��'K�d�m�E}����_��u�
�3�t��@/���_P.r"���~��R6����bz3)%�D��!s�}����=�S7��8�?��i67�1�����:5Gu|uV��9��es=I�s
���s<�{V����d� �[����g�>�M}�I��?�D�������z����S
oRl^�\t��;���-�����5����^�����8_6W���0����!v	��@MV1��,�L{i�rD�?�
��5��\Fm*���ZU��@�%��[�k����	��2f�U������������bGAF�_^T6���XJe�J���
�:���a���V�J���Vi�U^V5����/G�m�Z���8T|rj4��I������|��5_��K������?�W�\��|�BS=��r�{�V�J�y��8V�>��~lEI/b��b��*'<�if�Q'�.�[oI%���#z+�5�M��'���u[�i��LYO_J{���d�f�KN���d����=�����9�������=�X�{�Xc��X��+�$����P�A��>Nm��M5r�H�mCD�/Kv�:�q�2��u$a{mp�p��I�4��3��j!�����\�
���4�b�0Z
A��8P)O�G�Q?b4:�PB�d��)��Y��U�p&
�*�bz�x��6��x���K.��(,�Sl��<��,�\�j����V�Zt���7:��;i�{M��������l���g�|��,�Q����=�M"�]��u��}�9	?�7�;4$1�v+�?���_�l,��
���t$���j,"�`9?2�Yl���9$?tI�c�)���������6�6����Mn:��������$��!yAZ8j��3S�
��Z����cA�tBcL0+U��X������v�C���Z�����H���i��XXE��!���y�T��h�������60��DW����po���W����`�d��2�R#)��?��m]���4c�}A#|�4�SR�4�k�]Ap�p9�\
A��O��$�;#~�N4R��,jt���Zf��8��_�*�_�M�:��-�C���f��_���V���Wr��;���vUip��W����NU��h�~Y_���G���G�g�f;���0��i(��w������`�T�;���h1�1Y����r�������)�p(���\�|�-'k��v[m�Z��(�V�iY�!��1H��h���uD�����[}��"�{i��lX�.%��	���_c�	�����b�_6�_��h=����W�c�����D{�����_��2[_��2g@p��G\�I�3F&}
*\6���RK��8���X|Z��19��7�lM}H%�z���M4�
�YYJ��o�	m-+�P��b
��2�9�G��3��Z�M����-���!_[[~��z�����a[��N".����2��m�(������
1��2�����K�\����Sp�����V6W�~H�S#�uD���r�S�!�l��a��y�K�,�/�w�����������������.n���<�?�jO����C.1��e�P9x�k�))")^S,G���+�3)�2-c��pT5�h4��Aa�f'-�0D
L���@�@tXK��<j.��<�hd�����,a��/Mc�}$!%�����L���b��rmw�${��R�-���N(�����(�I��`��naX��|������(�IE�u!!�� K���J/���I3�L�������J��k�0J����m�5�������#"�f�E�^��{i`/4����#,9�j���q�����a�l����d����)�9��bU�LZ�<���#����O�]�]I�\s��{�l�"��Z�3D����^��%
�o���k������i�L��(+��
hC\<��O�"o�^��rF������$J�����?�q�n:+(mE_,)H�$���:�-�\�Y������8!e��!��o=W�++\��2�peD�;����>�����[bl,?�c	�~h��5������g��n�
��R�wx�������fO�]�
��y�i�u[g=����6H�EyQi�o��?s�s���0�L.�y��VdO?����$�0��_�6$��c7$1��B�N�#�L�ZT�9C����]:��8Q��=I t�-a���N��Y�&Z��Mr!74��e�#�VC_T�JY�|��Z�a�5�X0g-w[z��NF�����|���u~y�1�V��B{
AE���4uR��-�����RW}�or��B�(.�o������&�(�OC}�k�����{�	{�!Q����,v���!{#��8j�%������*�YX���&X��Is�������U��&N�> ~�z�c�i�}��,^8X��6m���l�-%�W���A�����$o�U���O�ys/���E��qL�c�f&�3^g�]��iO��6T$yq�us5���'��G��&q���|�3H�r�=�1B�4{������Vq	�V�d����8�,���J�9!x��`����\,���$S�f�%��1D,��W����8<�!��2��s}S[�1_�\R���g�
��M_p5*x�#�w�iQ�(5���6�n���l��\;CbZ�����"g�'��������Y��	������p�����"�R_��/|��������+�d�.����Y����������1���
�+�:��Zu0*����-'�bZ��}C@�<J9����.,v���x��5�4��j��zj��SZ���YC��v�������������u�����2�M��z
�dizb��s�����K��O�[w�B�6F_�����-���eD%+}���#v�oN1��B�
��QD]�=c��*��H��
�)�
���5�Q=r����g���
G����I��E5"G�h'-��h����]m�}Y^�^1�&������hbG9b�I����{>��!(Y�����
'l{����V�7�m �J	I���@Z���[X)�+;Jo��T���Cx���r����x�J��ee���)����E�F a���A�A!�(���t�b7J������m��
O�|Y�k�h�M��b
�+�~������,�s���7�����	������9��� �#s�^�V����>������7W�r�8D��@7�G��
Q�r��`&��P�n(��:a�(8���bD�P#Y2���b ���	V��Q:�Gn��r������)6'���R}+�����������K-��y���%��6^vB;l+��rmw��$���l���GD��lY���K�f��l���"*��o���Cg(�g�6��%~Y�[��8��������g~.�����(��eY�&<�6�zoO���h�J2<P����.�	�5�&���2z
Uy��F����C�+�<R���%|��H;B�t������|��s��b-=����6�Kt��f8thb~DF�����x�D����4.����E]�Td��3�"��-Y���Y)�x��f�O�r�EG$9�{+�yws�
��+a��r�j��;7������e�3H�6;P4�X����jv�=@t7^F~� !fqG�>���)���`�H��Q����[�T,N�.�������\��������"�@�>����i�e�����?����'����4R���T6�ow�f�y"?�w�^���%	]��	��<h9K��<���!��9�����b;�7��o��%�'�x���K�W����Z�x�g���*�����r��
2ox�['�j��B�U��M�,���G�5�9~�A�<�|{C
��U���ug������1�����fb;������$�`&�A������L`!������&���,���&!�M*F�*)s�P�@~CT9J�5�w�
u���?��4W)
M�b�`�~nYk�N�^���7���@�����fq��L���F�S�P�$�:�K�\�@w��0"�5*� ���(k��!��+��y��� q.r��
fA���(4+�JI��!����z"������1��BT0�	�]����4���Q��6�lQ-��si�cD���B@>��D��LK���<e�L��%���)��~K��r�	���,��Z#��_+��,g�p��%%�Ah2�n�P��6;'<��r$�����/b���bD:|?���%L��������}�
��r���B�E�W>���s����������l��~�����Q�T��%� "^����G�sj5���k���E�u�[6(e�V"54��q�B��a���O���=e�,����H�F�Ce���MC�
z*�J�eN��!��nH��	I�TH3�nzFs�3��8���@��~*p����>:������N���Tm��0m����}���������@�b>��4R��t~]��W�z������W_���z������u��F�	����I���PRb?�����w�Y'�;�h)�� em7������#6�b�w~�jm����e����re����k2��-$?Bp�>m��#/�u�n}���ulvd�C��E_W�^�*���U�U���a�7Nm��,��Gg��E:��o9�?As�pw��%-Nb��%2[FL!l����,������|4R���4�-�_H%��I����\x�/R���C�����b?p����SA�05��k��A����H��x"�U���k��z=�Y"L0�=����(����:��DDt�Rum;s������5P��y��A4j��A8�y1q]�:���<�������7"F�fr�n��tsp�d1�������S���t�
��%�����@�3d5\_#4��p�@E`.���O��r���h���|�h�g��WI�o�g��j�>�N�)�@�d���3���|O�Ju�J�b����K[*�y�$r����EfO�>����Z�V��i:�R�N�l�d|�}��
�@�i�Ve�"�]d��V��bb�����7Y�j�}���K�+��������F�%�29�^;\Y'}����)x.�������������P&O�^=F(�Q���Y�>W�Kv��Z%�����\7�T����z��tZ������b+��7E#������H���N�.?m)g��R�k�w��|�0C)�5��rm����f�����2���p���\���y�Q�uR]�^Z��j�C9J�[�|2����xQEb��M��z�Z�r��hI}�+���up�`�>�3fh�;�+|j�hYM�-#2�S�=���t3]*�lRpP�k]�)?a,�1����=x�W�{�+��!=����H����kM/if�����-q�(�7��Z�����T��h��uX'u
E��H������#
�s��Irt��������Z_�V��K�/���������\��L �|@�f�Z�f'�-l��+h[R�gk|j9��3L���F,`�,����z��N�j�d����;��4v�q
>X������F6��M<�u�=�c�-�	����K"4�my���q���
���P�N�
�>�R�d������7�/�"N����is.9�	
�B�R7�K�A$�G�x
��l��x����@v�n��f�w�+��7���FB�����Jq��4�hCJ���*���
�M
��y���B��/��j�����;(�To:�>��mF�nD�H!'���B�-��X���H����d.���)��~�P0�����p�����b6��q��h���|L�]�J�0��VN�y
t@b�z���c)���l�����������5x+���r�r��?yr���mhgw�
�n���D-��iv�t��������O���F/���������-��������>��b����U��0�);$�p	5�&��X8>�T-F��m��d��W3@g���;�S�:f�K��H#����������1��P,_d'ol�G/�-���R0���8E��;�!awO�ae��cLG������K����7t�[�E
���e�CP���'0���T[*�P@=3*3k����&�:E�H���!1���
��J���f�����0���>&0�������C��x����� b�hKk
��ye�x��~x����[�L�-:�0X.7��F�);�W��u�#3���88]�����:�{�?��(*b���}=z]�����B�Z����Vo��QV�`��K��k:��E��w`o��
C��L��97��M�^h
��"Kr	<~�O�
����i1|sn�Nv#;A�l�M�ap����P�{	
h&!�������_����@���HQ�54����+r��C�Z�_�u����_b\����HTP�:��j��a�(��K��<`7
qv��La��r���K}����	�����O�����]�~d����$Vk]{
�laX[��dn;�?�^�,���?������j��kL�����%�oh����}�hC���%��9q��\KB �g�����#�>��;�����Au�I���R%'�T�#e�
s�b�E��T��b�<���pv����q]5�6f�C�n���>�#�%���	8�7l �U��q���������0�������*���"7���������p�]�G'���h���L�����M-�9��:\G{�8n��O�~���:�3QL4k�[�����I�,B?���N3!����(s���A�$&u���J����r������O��B�g0��67��}�I=K��������fn�k2d&`��}8+&��:Y�k/�t;���m�d�i�������?���^���������\�x��(��$����KuK���o=rw������O������F^xV������o�=���1Wr�����n����	���O�6�G)a�t�|r� ���I���J�=�O����� 	[ZBEg[��H��(���U��]��N�\����9��{y�r�����~�'���#A���W��8b�Cm�\J��u�I`�B�G�.S�Pp�����F$!� ��s(��E;��Pe�Q��VGY�5{��i���*�b�V�^W���~2�]>H�E����
����c>������1���W���\l�C�f,L�����q��������'��?N|�"W��{6�J��>�~9F�_@�2��#3/&.�8����>��@����)�#�����h�1��y�����t�F�&[����D?3�c����h~�J�5�����B��3����������emt�	_}��X�#�&�6*e�D;�i
UZ�e�"��6�"G�$�v����=M��Lb�H���:�v�7k���]>q������K�8��B(��r���X������:��J����_��ds�6��t�����S�{\�m�i��Q=?yw�y�_f��5�	���3#T�x#�>v��|��%�U�vE�\�^,�2T\���;s�	dy >��l�1���+�@!��O9���06�M��(�
$�j�?���&�=��{��~��,��|�7:|�C����y_h{���Z�E0}9��j��<�����f]/H���7��?�����������[.�EP�Z]C�	�-���R�_�AB|���G
O��F�d�O�������jMQ�H���h�)�d�t����J
��+����������=t���]��k��k.q�_��V���%�N�0��"V��%~�B�2���rz+�A�qf���5��M��X��Z�{�"�����
a�*	��Y��n��z��t?5���V�bu�����������P�x��^�N����~����L�o�X�r�:��O�XAd8�G���'��E���I�<�u��P�df Q6)�W+q N�����~G�r%�����.��Yuje3al��/9��
S��tWu����Cm��i���V�>+�Wd7�T�����}��*��{��}RAoi�G�s��I�5�r~|��M�]��>:��|��v����]��������TM�R.>�%N�;�`� �|�CoI��A>4�>�������=�������iYX�r���9�����B3Y��e���u�\���d��M"	�N�����!���c!�A�@tq�����,IKl^Mlq|&���V�A�*r��N�~=�Mr9�d9N�<���nI�>j�Ek�kh�l"H)��]�}�5f���#&@k�'��*��V��D�e8^�H��5Y(A;3T��e<����%����R\���������Q�^6���P�
}��P��^���	��+&��c��QX�TZ�h���������F�+��/��;�8� M�p�T!������y}z,X��?���������E�"I��Q{U@65>j�� ���
�N���E9�e�`"�_�����~6uy���?��b~Q6n����XIU�
�&��c���a<������]N�pD�/g���?���*V�$mn*���KqL$]� i~���������q�0fP��������2wM?���.:��!�^p���WfX�����fF��4�{�G����dw��nM��RG�&F�%�h�w]������WBE����2]��M�e��?8w� �wA����%���O�� ��z�[`����L���^���\QY</)8�
;��.'jbCI4��g�T�T��G�8\�,��Y����;��������!\m�����hJ�$�w��
@<��O,�6[��cA����ss�U�$(����l,��~�H��?-[����KJV�|�b�&x0���8�'^�[�G!_^d��R<�<�!���
�l-|�t8�!� #\DNx�|t`�8�Q]-d�u�{^���h��U�aDu	_��03,{�t�@�Nk����;�a\C�L��a�@s�B�L�IF�@����
#�>�v�u	p��k��J����x�t���!�_��~�.�6.��&��Jb;�W�X}p:��*���F���X�����x(��E.g �c!����wbnn��>b>�Yc���X
U\b�\����Hu�M��2����� H|LSL�����V����5g��������@����_�9>��4�&iC��������7��zg��y-)��W���k^���T��<->SW���5�Jh�1.��r�X^3�����h�E6#��$��0��A��+����A���u���l-�]+7�$�a6��?�#�[��g����1�}�s���$#�t�_�T����[�r���Z�������� O���u�Y�SQ?�M��:�Fm'��}y� 6��Z�q-��+�jz�;���	�xH��NaM2B���R2��A*K(}$���kA8���r.��)W���;�j�i:�;�v�{�~������p��p�x�P��M��A���x
���V��~��a��mj8�V1L�ju���nO���15'C���t��+�'�@����..��^�l�j���K���h�Pqm�����d��u�9"�J���o������c�u���Eb	dQR�����b@T���pU]WY��G3����Z[yvYUF�9y-S�h�Hk?�f���4�nln BHs�s��9�/�v���x��������{�, b��^�y9�%��"(�R�h�������CJ�m��a31b �.�&C6kqqQ�K#�S����'k��P�5�*�D���'����������
��{B�.��=�T&1���c)A M�-�c�nHU�R��Q�kAAS^�[^K�x(��D��F�d&��{R\�N��7ms��%X��dU�Y�c�z��'��B���n7)�L����w/��/�K6���p�w%��!�eP�3sA�������X�}��r�r:/����	l����Q"��[����ih�nU��_Nle��R���GE��	Yi�����?����� #���������oQD���=w���=x����9�45���H'g�������!�U���#�����5�*N��5��O�#����c�r�{�����@��wx�"�j�-�d�L��J[GxO�m�9�f��P���-��]j3aQ��9;dtE������2�E���%���CRhlHc�t�d+�q�=���	)�yT]pHx5�0:�1����������gU�X�������^.,�U�{h>�������)�b�Td^cMe P?�b��@�V
d��t��D�������a{8�1&����l"~.���^�#�o}������u~���q���k��%$���0*���+�������t�{��(��~{�"���O1]�&����lQN��_�Y���B���k���0&Tw6���7�a&���HC�����`r��7L���"���lp��0B�SL��Ty<��Q����o�b��-����iy	10h����������C���n�<`���KI�c�:�fK�%S:��y����=�5��Et�����-a8E��p��������f$'bL�(>�I��������Q�����S�lK
�a��x"� y7�X�������V>��������_��%`/>���[��"|�T������a�1Y�dkO.���8Q�[�P]�l�^���?���%��7�Q���Q	�OxC�����a��D�=3$����&���SZd]7�>��^G������+y�x�A]���b�x3��|����"FR��2T��T��t�&�L���b���-��:I%�e�6;T���Xw�Q�
d#��Q��b)��{��tr���7/����L$A���#`,�cq��A*}��VJ=)�/
o;F�S@��e$�sUNn`='���������h��/��g���}��5��]�c$����Vu�����z������.�8�z�HU�Mp����W��%%���lcY2n�U��_d��`��z�Jz� �jw+��`l}l(��W�Hj�z)~>�2�����2.[����[�L8�[�����w�b�V#x(b�#1��9&�p����de�(��c�k#\��d�0C�I�o����f:,�oo���G/���t��M�([��A��|P��
������p�����Q)�����J)"���`y�3�?���\~?���FL��u��=�|o^��e�~Q���e�������bQ?�8	n/�w�������D����r�����*������,N&C��.�M]�������s����
��X���?�q�z-�R��ZP�2FX"'
A���F���@�p�����A����3���G��8�k�3��c��E�'}D�	����I�AKt���w��/�q������}�PZ��T���2BC��A@�������ywT��!���&��s/��4�����
��;���h�����x������1���>��������s�;w�g��d�r�[^��{�8�_��R����bP����S�3���3�CgSP�N�b���UA����&�-K��c�������1Y��u�AR��3���O�]%�@)c_��q������A��e���:�d�wt l�8X���x^�6�o��Iu�d��0<bC����J���b��U�f�ZXwE�������	kE9� s�L����Jj�Hrh�h_�B��o���V��n�j^��	�c0|��1��;^�������0�)�6I�o*��	Vp�=���A�+:v,����h@��b�������	:wa��F��uL�
��T&f�3�_��DS�C��M^����2����:��'���q���h��P���������3���Ds�S���'��E
9F5B���quU��d[�_2h~�R���aA| �z�Nu��L��d����-KG�7�����gl����%��q��M�5g���/nI��������;_�swJy���H���!��L�8������GXpw�n� `��9(��rlD7��9[�8q��}�
5��V���,�n ���Y��h]�����g���%H^������5�`����S0���{:����z>��;.��6m�G`��F:C��p���������o.����f�|�&��z�(<^����oM�u�M���\U�F��2�t�B\����F��O���\���#tP]��|�����}9�������@eX���X������q%*c���C�D�em����=����2P�A��7s�m0�IkDn��4���r�wh/��Z`����<�1���w"T�4��������5��k�"�N�h��gcm�t��W4�7��������.$�oRUN�L}#����43JO��qSin���@���m�����r�>��5/�����{�M�}#9	lS%��]��)��qR��k3�E���`�w���gb��
oW�v���PJ?�;�))����a�U�p�����9��bi�V���u/)��E������Xv�������i1H[nR�#�Rf�a�/��k��)����d>@I������7E4������t��Bej�'y�[��j��a�Z
�4|�]��� #
��8������a�_��w��i4+	�Q�F���0���=�e��3���2}�VT��J� n8H����.8���9L$��)ZK�|�[�����b���N%��
O�g>�5�D+Y����v��'�!uZ[��,��iy�N�Z���n�F�ww��9k;QO���8RO��z�:R�c���I��
��R.@T������V����3tz���r�9{�>l�X��GF�2�*�5�1a���������o}�6)=5���{�����v��}���owm4�9���_����<K��~Jn<�G{������+o����p�zv]����M1J'�Y1���O���������[:1��6�����r�v6��(��/�PRz���b6�����������S`[*8��,��
�]�g`�eq��� ��+��=�<�W>��P���R�8
z���������/`���U��t���M$��e�c=����4�q�1��$0�@�'HA^K$��[�t �@����y��z��V-;�q������/��H_�����o��B���CE7��O���OU4��<st�=�q������Cq;���������kl`.�s���c@q�|�7<=xka�����3yqx�z��0����1�T�o9)�����[���:-�PEUS���:|C�U�$�sY�b�=�0����kAv����(�|A�0�8����)�������O�/�r���������H�`���:����1=�=
*X��F����ll�.f��l~�9��x��xG`��+;a�y�?��m������#�8���X4��
@�/���NO�1!��������1x��S��7
�rMx����^4�IX
I�f!�f?�[��H����!�Q��c�W��(.���BN��S�_R�f�>$$K��>��������k�0��A.�M~��9�y)������Rr�zx+� `�[Z�`Z�#$��-0�tQ]"���k���ml�f:��/�)������a��y|��Q��.�bMSK���w�xG��'>�!�0��B���.�bxA�����[�j�}�H�`>��E�t��E+z�-�����INZ��oe�x�����E�s�����(��FY�ky{��L��5��L����+eM� xR6IN�/`�7������@Z��#�!2���#9�S��N�x-����3%��;�N����5j5������G%�P�;��O:�3�y����0i�����C�!K>���Q��QaW8	���V��������M<0�����&G,LIp@fHb�OF	������?�"���}f��b]C/���WM�L�����[�
^k��cw�8�@9�i-8�F�$(Y�p����ly����)����-�����v-�zm�-��Jn}8^e'�Zn�N/0��,5n��}�i)t��'�Ib^y�B��rf����*������7Xo0��W+���7�<��7�~d����1�
]a
3�D�A�v��q$�&����?������sk�c
r=���m�c�Xr�X�������b���% �`����0�^@�To�E���)�M��G2O��d�!�����H%����t~�i*�3��M��=���KN�����wn�	��yW�b,��dv�/m�H�8���${�f5}D�����5�	��N�CF�j!Y�f�8�V'��B����'�:UF��y��A�qz�iX�����*v���R����-��N��HW]Z��
cPQ�|[X�n���KNk�]�_�:��'��.�D��wue���.g�p�����|��^�0t(��d�S�c-]���
���~�/@��vJl��F�f���U���RC=��)A�^���&Hn��eS\	���)8�O^�+*�GC��@�����0�3(�/!x�e����h�����r�������E$��������
�����W����.��B@A9�#*u�#���/%*�`3�c��ks���f�3�/�=Xs���8�\�t��shK��B)�RX�{Bgb��]�QE���$���=)�`Z(�[����mk$��]�o�
�]R��-�����I~�8�T�����|g����L}��8�$nKL<={�"+�������3�,����5��l��Z`_D�u���.,��EU��}�-������eB�U�`O�5�����W�FL#��WJ����y�R�1���@ST$����~6�3�|iZ��*e�'���0�]Y��^
6��J��r�����%:N�jW�d7�E�y�|2|�d��k���A{�D'C������8Y�������q]��
����9��/Y ��T�E�\�E���������R�)_$��].��|0/��#Q��NIz���;����>����	75L��\D5C�I��IyQ��x�5x���x������N�'H=��)����Q*�������
��@�>���5x@.T�X�f�{j
�7�5���%�@&M�i��S_��D�pO����[���K��;s��k(�mOd��a
+W�$[��`��L�|+,����������h8����^UN&"�v��
���KK�Y_�9�����|�x�|��u����/���\e�%�|������
\}�f���L���������!�e�R#���t]�^>���7W���AY�'���<-0��c�4��?�h/K:�L4k&�gg�%�4~�8��o����Sy,����`@V/�+7������is[N��|�0������a�G]��'�.��<�����}��j�MUtL�R�@96�E���P6��8�Rrk�k�A��q�&x4�>�����e���o�w!����4<��r:������'���44I1V����M~�XW;,�Y� !����#�g4����E99����5�6��cW)f9�5�:l��4Q�^!v!=�d�0;�g�51��W�#�q�����Jk����Tp�ko�y���o���\bHR�9���G)�T��`����>F�����dNB�$�W����N�},� [����v�����w��0����N$���]�i�"	D,�{�i�o����8��)���k�:]0�iHy,M���]j�P$�����v��%l����m�aq$��������C��v����xZ�e��U�A��W��������%x��B�sP�f��|��b?V7�b?�yC.��-<�Z*d�����FBo�kzs�K��^�1R������}F�hG�:fu����T|2j����s�^�%�}���5 /p�6��Nd��������j�S*������������U{�o8��&�g�#W�D"�����r��7'����������F�3��E��������������"TK��H��!'��Q�f��U;������a&g�V|����0B��X��m�V-��pn%}lTB�[m�  ��4�L���X%�H�t�N����f�a�LC�n`����)k�q�Y??��w�wVU�K�I���x�/@�*72m���9e�u��%��
^���4>1����.��b
��P������7��bU��'s���S�7b���G������J���d����=�|�i�W��8w!x�r�Q7?9zpr3Z�R,C%���;D�!���������w�-������"
�]��jf�y� I��|�gw�\lB����i��bP����i�������	�\�eg�Y�*����,��U��I%��2�5,Z� g
�CSr�,f0��_|��
Jahx�����0/CFR��n���@JF�hX�3����\�l����`�*G
J<Y�� ���-������58�
�P����pA����yv]@�O�\����j�*]:]��p)��4�!�b:��v��[����"�i�g�E�!�P&8"����$��\�Jq<&����.A-!A���Y�1�6��z�X��.����9�;E`A
�P�$�IAU���1mp�kH�'ts��G�b�T��Pc��4���V�Q3w���7.,@�:�T.F��i#J\��a�D��� .���"� dh))����4�g{���qS�s���.�:��ai�X�����8;�S�
�%^����S����?������������i2�����|r�O���gF������'�����'�'�����������m����*�m���aepsV��T�D1L2	��mow�!��U��Q-.��������~VCFE��P^� Y���������w�5��o��]�����\�0|���E��Y���5��`���\j{OImH0�t����9F`j6;��Y)��5�a@2����J].|��a�;��.9���c�61[v�1�w�
  �-q}�Rb���E$)�=��,`���,,.,������-h���M>5�������\�VDD;��v�qE2�DNQ�D����wa�;� 7.F�����VNgsDT�9�3�"�����Yh�AD�'6*����_i���X �"��m�8��<Qd��Lg��"ZF�]�8�s��LM1�L����U/-�;�0�\�^����>�����G���O ������������������QR$&�&�z�����������^���_��g��O���;;��]�1�><��}�`��{�8�o�����e~h�d�9�W�������l�q���Fo�OB�h�Mt�u;��p�������]}�13������?w�r��������IJ��t|BA	��M ^��;k����tr�9Y����w����W���v,��\����b�@�C�8��:�u]��
��o|���������
���>���f9��t>��[W�ql�p�[�����3�n��/"<�{:����TOXu�H����Brp	������I_c63T��oD:�����d,(�e�2��D�L!�
�u��7-S���E�U�Bg��p��"���Y��%�M�67��
�f+8Zr`>CK�dMn��>�����d+^X=I���}u���E��ct���l����P�L����k���9y��y��O���N����3����������]^�3�K�^+��:"�?���v�^{G��*o�X���/�H*���'#����Y#��b�mh����I�l�n�(�\��2��v��������U5�*Y�}�S�u��$�uY��A������O����a<x��8s�K�RT���RF)v#K�Ee�h9-(�:��1�����La�������,f�0aO�-��Ppa�n��K�7�07��q�y���*v��Q����o&J���&�$RQl��]s����)+p#Z���[�}.ICn���vqNc��(���Y����p/����f}��9"(,!�����Q��:�o;F�&��JpF����������U4W�/2�3|(��)M�I}����2��d�
�yy���i�h�uy��X(b��fCVZ���i!�9W�^����Y_��%����JLY�}��)��Z8�D�r������k��SqX�H�D%�I�f���wB��5k3V�q��TK
��(�dAb�4��Z{5���Q��q��~mXP�Q������4�����iW��_���oD������}��OC�-i���:{5���i������_��r����3#9� ����/Hj��4�9+���D�2��[���F�*�X�I�S����r�nk���}�`���2��As*r������I
�E�����kp�N��Y�;,�u����K��Z��5K����[�4W��N��-��<>_���aT>~��u��(`�|������M���t�A���[e��
=�d�=���5T���A"�t}��(K��������]�-�o?7�S<|�zn�]���:�J�bA��a� �z������a}mT����b���H�#R��YS$]���lL�t��#��:_^�s}YM2��5^�!*zR�&k��x�B"���wd�����$��o9�����d
�^z���\�n�}�C���W����.�z���Y�w����������Vt������b���l������9��`
�����|�l���#��6�����D5�.�U������+b���a�Q����c��������:?�=�Y~ni��3��~��c@�S^��2[�G����^���
&����?���Nyo�pg�������fO���ps�Ya?H�B#s�v���[�]�������S����9��t���tPM���)��3����
���&A��zd�>WR��9�S�
,�T��g���2L/v��J����B�p�d��y������������h^z����_�%��I��_�(�58�g����~u�#�u^Pd���K��Q��9�����-|p{+0�Rrs�q�=�H��
xc�;�g��1�S�h��g^���r&s����`��]�HQ���6��P�;N����0-J��.(i�t`� �����yu?"_����W����Ep^H�8�e�<o$������6����nr4Q�O
���L��F�Q��6��6y0u��zc�k:S�B���?ao��o�1���B�:�@�%ll���)������!���Q����@�,����=�B>�k���+�g�=��<��}�r3����Y���X(K�aC��5�
e�Yf8_�;�A������U}vm�����76@Q����'��F�+#/!��t0��B��R��������)W[��wr�����q����U.*f<�Qr!����	s.�����?L�P�Jm��+����eI9|^"Y���C���{�����y�?j�b�!�h�xP�ihI�����a&xk��SX����'/�2h�q�0�u5^|�~n���%g�B�2���,-u�!P�5��U6���%(=,E�8u2�K#�BJf@�]�y}Y��p��,����������������R�r[w[���d��K���������������*L����������R���*K�v
w��OF	����Ix�k��@A��71�G�q�G�\��]�9���]G���t��MF��8��M��;F.(���
��-�DW��n�l�wJs+qr��p�\�
�(Z�ic����1l�E�'�����D�-��� �HA{�%-z;v����#,�^����b��w����=-���!H�M�`Y���X�l�$c�����#�����EM��GK�6�F\�����QG��y�^��P�����w�u<u�*`�AW-�j=b	����B�\�BO�\2����Oh�5���@��]%���S�W	�tYS8���	$���������<<v$���/�e���~)��t���n0�,��|,�i(s-^}��X�8�Q_Q�0�S$w�1:����LG�+��J�
����R���$�ND5�Y�D8�'��e����Bm)��7��D�w��^o�yY��d�m]��Zjoe��Gv4��.�0��]���`���
$�ob��&�����o���-�S�����~���(s�]�����sv�����_�~g3�$6"���1��^j7�P���r�a=�l�*n�?_E��i�l�������?p�����J&[%�v���X�?A���gp��Z�@a���P��a�����B���@�i��Ofp�	W��\`i���\Tq��X�b�2��J�>�~��I�@�:
'�����(������.��X��t(�#�0�X��DEMv�<�&a��������A��
��Lm(#�Mt�I���6�����%$l�
O�D �L��"�n3?t��+Y�SOD�l�bp�x�%������b��rt��o`���3�����w������j�v�����*"�b:�OF3���>������#��iR�C%�!ns+_��A�Q�z����w�'�G�g���#�������3��M�vS�<��Oz�v��Zg�g��sL������F'���e���f���|���3��Y��y����Y����aK�:IvXs�^�X�Sim;��8��{���;?��%��F*���P�$���3�t���z�5��F��0����_=f�Jl���K���w���_ �tL��tIj8�i>�<`����7g�i�o����v�O��?z�`A�h��:��
�E�l)Dy���m�+�����&����eg6���������Y�r�������op�Q�n�3JJM4���������F������&��&t�Q�Q����c&t��r�rL.�]j�����&�/H��E����K(�qP��!������$�8�9�j���<9{��������~,O���fT8���s�����9����X��GA*+Q��U�{�j{Y^�`���~���[w{�e�sJ��G	�[i�K�*�K���m��g����8y
�F0���X�T;�,hH=f�g�$h�Iy�\M�:���B��'l�;B��f������r��w "�.��}��X�FwF�@�I�]���O�����\���O����� ����y��m���c'���n���n�
�����[�R���=��9y��Qwu]W�Z�.�5��;
�CM��
	8��6���������"!��� �:�>$�0J�����2@��&&�Q�r�3��Q�=��,LyZ&b���fec������"5pL��I�<���1�������N������_�$}g�B�(��
'��N��w�Z��w������s���PV�Dl|H��*ci	�>@�����j�LJ�eV���1��������m_u���?:���P��G���
P��X��!��w�;@o~��r�!`�W������,������AG�0�G����8�1p����<�2�X�A@���
��n������R���1�i�h�2R��m���������d@�q�6N6���g;��l��-����m��pl�/u5���!�=<�T4s
{��=g�a�����nX�Y�Mu_[�?�Jx��=�����'�["�����^�������g�
hU!����9qQ�-�M��iQ��{�;	�G`St��f�E�~�����`�=��B���9��"OK":���x(�:�:3!�����wH�NTH<��������B!!�%$�^�}@�"��V��'0����i�a1�c�
x�+Pj�tI����	g���l�5�ji���UH;=����������
�����>0p4��
MA���<����m
-�������Rt�yo��l��!H��or��N���p�I%�>2�t�YJx�[�t$����L�2�$���� �#X*g��e-��BXK"��2_�-��R�e��F�4J��OF��f���?AP#�h��9��%�uu��$;��j�q��W2{4��{=J1�Km��9�SWOu8W1w�5�_y^�;�&��o��L^���jA$R]RI��
'��TBP1���1���]c>�.��%eX
4F��jt-�)����?���Q�K�����4�V����y���J�09���������N��k_�qJ�$������_'��;
H��S%���L�)��=qF��7��(�8���B��2Z����0b,��T^@��?���@����!K�aHy� ���1p����W�u	a�K�K_&k@.������.�$�e{T���(YJ��k���E�� �������e�7N���BwjK�@'@0��^@��e$���c�^�N�h����M> ��J&#O�
$�3���F�LX:�����LQ�J���{�������c&>9H8w�	�D��W2�����U�2!J5�,��S��]���2����e�KP���]����;�]
�����/�2Y"f���w��Z1��E���-��N�R�����N9���$��rV�&R!����TB���j[ccs��A��#^��4�lw?�������������)]K	~��Aj���=��,�7���M�-F���n��*o�8�E�A�?%�����Z��7BV�b��H���u��!~F6����'ik���k<��������)��Y�N-�_I
6|Z��~g.^Y���|�9I9���;����� +Ua4�y���
!u�J�c�����fB����'&��5q��	b3�;��h�D�N��������2�������B�p=P��)aN������4G�N�/�p���T���lt���������h�����?��g����E<�&f����D���&������~
gy��%d�d!�C��H?t@cR%�x��m!I�\��%�b�;S�.�cd���6��8��:sa��l���/��32��,yM6�
�1�`�P��S�F�zv�3�����!��$�����N�C������M���	_�i[��T�����I���
�����F������M_�#�
%q*����Y_������f��'�X�������g`c��"&�m='?�)�����"
M�G����mY:"��M�� ��
�u�����XQG0M"i�W!�����:T�/���,�����o�w�2-�`EN�b��Ey-#KY����O\�"��l� ���$�������B�-wv+�2�k��X����[7��pM3�o��s��m��
p��Lf-�Kr).RV@�j��i��#�pQ_���eJ^{c9�\(������<TH�`�o"�|��$�������!��!1n����Ov5h�����9-�T���_J��5��g���};^�����'����+
��0.�|�S�.��Q�h:9�Lk�q��4���cO�`���mn+3��_���'�cg�m��������PLB_���6���`f����\���������������?@W��J������.^��#hb
�W,3���_%��H�b�+TY_���Sq6u6�+��c��tR����.��A�

�Ok84���t�������P�
���`���L��d�;�>g3r��z	wo�1���5���������ONN���V�}\������q��x3g�+&�LQ��F���z����GH��_��:����
�(��		�U���Y���@���~�j�����S� (G��������C���|T���13U�8�����`s,�����'y'$����Z�
U��������Mg}q��:��������t1S�8�����<�g�
�������A���#�kg�+�4�>�����wX���^}z���u�-����-�pxXm;���@	O��_0��Fw���^���\��/�57����?�~	�u���o��+8����~\m�����h��@�����`9�{"�+(�Dl��$U�L�-;��C5{�/p����F��go�_��a�B
����k��Cr)������h�n���|'�k���@I4�6L�
��N1;��.#��A��������(
���_������!�9�s	~�?=�j�N8��Q*��J�R����uMM��919��GT��i�/�`�sW�e�J+����V��1g�P�����d���4V�/�[�TX��F7�n�����Ao�
���d�W�x���}��#'/.��A��i���G�v���(1[W
��T]��-:��h�g������i�z�i����8<8�-�8l������D�^��h�Mq�~���8�
/%�>���T�{Y12��jx[�+��T�2m� T	&����a�lRH)8���d$	%�R��Tri����-Y<2�:o.��d���A���<N�xm%o2y��$�A���'~���BW�sS��
�%�8"�����
�KS2m�����Z��ZF��j5����8�h������U�5�����Qc\|���=|�J��j���m4`	9Cc�#1!`��^�Dpi����0Y����|���
	�f�>Q)��=t�&�F���Y2s��l�J �fJ4P��b���tT����s�J(u=����[M(�JZ�m�qv�@<�f��'�V�[�W������	X����'��2 X�3.�s��xt��U<��"[G�]<
�^��E���x���g��Q���+�,����ZW��Z��%����������jn��k���_����#���8T�|�����b�`��I�~���I�v����q��n3������(�'��	���������`�eS�fu��Lm�q�V/����u���-��-\����������Gx�0bD����Z!���k� _
��wy�)�K���aD�����r@%_�d$�~x�d�������L��U�s� v~�+Np��Um4����?��VS#�����������0��WFae	���4���]F���."/`9�.(+sf��B���L���c��v���������2��{Pu~@��F���;//�~��N������%�^��G��_�����7������lMIo{��.K)L&�������{���\<$���o���:rz� �U��������@	��l��'Q���3���K�n���&5���,_��)�@/�����'*���^4=�����e���[2v��A���a�� �%q���k����/���C���4��h.G_���SD��O3'Am������;p@�!�h�&1]{���J��{�>n�P�M�����:�&�<�*�>��HiG'x�� �]P!����7�FF�r�e�V��	�����������7
������~�����{-.]�7��W�~����`��bT�*��P���'�yRZ������T���xh����bzY5.�����U)����W�xc��X�o��.����)(%/>���������3i4����vsZLE�e)��$�82M�����?�� Yz��pBK�b��T���oyR���7Q�����!����UH����X��D��1�� H���/��Yef�H���h�n<w\T��E�����<�^����^YT�"_Rz�1�[�U���2:�N��<���?D���C���M!�gC�{��@���
�|A�G���V��������Ux7�m���@q��u�1���1]�R�R�s���:�J0���������}������#5��S[���:1E����//G���G�W������H\����	O�D��Hk��&���#6��\�W: 9����Z��28�1�F�3�[��������W�t	�4���qZ/���n\��=�@�6����D�����{{J�@�2N&���%���6��48��?����B�n��i��O���{xr
�s�Bo���^�*Uq�C�:n��*�
WA
Et�W>]u����cI"�j>��+�Rp������9����W�����,��:��b=�	[
;��l������[U�sgSf�<P�'�`�p!�xDjiz���X�9l/���������G�����
G�.���Q���<qO<�V�O[n��9>��6��xeQwu�C��t&^o`3��c��u��������_�����/aA��\����{��;��+�<����L,���l��`1����0%�-rx���j������]o�l�D��b4
!�5Al6����]���NY����hq+
E���'m�.T�zi��3�/n��*���N�2�KdU!��IvBo����]$V+�t�9��#~*&-D6�}����c61�����>�dlY�c����Ew`���
������{6z�9���w
��
��c�����$#-����<z���IB�Q�5���|F��v���m�k3�.�g�����U�w�����;:9���Z��v��i��������7�]�n����9�L�W#�~I�A�.�k���������$W8{��t�������ss#��Gbj;7nqr��e��f>��G�;gC�����:����7�H2����w���DF�B���/�OT�=���aG\�1
�����g$�[=�{>��ac���.��w�5���%��g��� �����Y���E�r�V���Q��^������%VE�K��h2mO�'��z_����GQ��r+"��������ymC*P)7�%T���/�u�v�l�E��m������P���.v�\�|de����O �;�>�i�����t�G��+�\?;��4 �C6T����UFn������j�k������x�#s~]��`����H�gq�����2Z� 
��q����n��D}���b��U���W����4Q�M�!����4�O#��nDT��0����6���L����z/g��y������l��~�>>�B����0�Xf��
��]�G<�{!�s�-1��6#��(�����	��T��V��3�^
z�W#�d��`I�m��P�pi>�\N����#��{/��H�b_�D��W���0�@���\�d�R�(�K�
�u�[��`2m�xk�y5mX�D+"���$���AS	�s����X}~Rb�U�/)/�]����.����
v�E���j��9���.0���^�����H�VlU�]������x��* Fa��������H�����R#���U�:�[-=�6��gF1$��m�� ����H
b��'F�5����*���=�jYR��Q�R>Q�E�O}��0
�gF10��E�o��}����2���QP�*�hd7�FT)M*�y���\S�"?K����6N�1R��)-�
������K�RQ�!�����e����u�����y�VbyD���<9h+1�������i��s'�r�����4���������%L�&�ma����m�t��{9�6�*����������@i����a���j���~r�<p�M�����q?\����Zr�W����P��`-�<U�#�I����O���]tCQq,�E�W3_��HP���1���A`R�)�/r�%�(�G5����P���B��BX8/����Ub�'��*��Q4=�F�hV_�!����	�n����S��5��}�Aa	�KT<{���p�C0(0����Q�V �R��Q`���\Q*G~���D}-b6(O*N������7N������s!n���:�V�
5�`�����zhOj�3�M�FWu(���v���PQ������}�)�������R��c���q��0�
^l)i�����?�<n�-V3��#��,�8i�3��X3N�QL��Z}yM��[��	Bf�~,��b?D
aT�{C/�+��,�Pv+U�^��F�����x���a}�fv���������EW*fe!�xc<!�����."��e��{�t�R�28�����QU���w���������Q�q��a�i2/hv��}���Cr���c�l��3��2���������b?��2�E�]�Sr�Cd���F���T��5)<O��i�3bw����YO���u��M�(X��M��@��4�y���}����@U�YaU��N"���S ���	'#vS����=H����xE��7��G0��jxj�X��MU��OK�<�e�/���S[��?�<���e��2���;`�E�<�v��f5�sM�TO;�Va��&6�1d��j��4!��u]na�Na�rs+u..����������b�>]��H����Ic�Nj�����Q���D�'y�����Q������H��+�V�z��m�
EC�;�v�0MI�G0����6�)������%@P�����3>P��M{���p3!!	~�$g���S7��L!��c'9�ILzH@�������(���E������h���M��X9�;��|���s^g��������\��d ��b������6L��st;���
�-���ym���{�.���5��)���.�Y��L)�N��>��B>���!��95�rq�p:^vzW�f�E;C�(��%�3�����>M44
���w;0G�"���S�_H`^p���q�d����t!��R5�v�>CZc6]������>RM��`�nh�-������<M �s9�2�����q���%R���K�)]��,�]�#�R�8����5G0���p�7,�e��2�2*y��)�
�����9�du?e��4M'ZG@�����_V������@����Fp��������*�{�+���O2�`m@�H?���D�r6p�2�1�����_���t��"�fy0�A`���W���V1���agtv���������s�@:!�@������]^�cU��V���k����:E�j��C$%��;F�7,� �=���M�����l�w������#Ie����3D1��4L���b���
�#�@E��W1��kWB��b�	Ia��!���Y��':Rq;���<z�Y��`�����W��r�����}���g�y�	�A�|0�$��Es��'�G
iEP�(���F���G�S�)�"�u�E�J�����1�r!�3��%�Fq�f��fx���6V"*~5��}��WKSG$��T�8���7�2������p�MLKCFf�BD2Yc����S��lMVW������TY��z��_]���M���{�������5K�1V� �G���{u�e�C�#D�|��/b� v�zu���1�����yN\Jk�>A����y��A�F.u���O��ge�m i�����C��k�����W���@[2��7Tn)E�#�|�)���$����^v������0�!���z�BYu�[�����)-�3�Z�,C��Q��]��^�^��:�?Y����;u8����C��8��$���5$�%�s��-�������)��������)�G�W�X�1����U���N�R��m����dj��L�Z�|Y3���d��
^��BH3��9w�&%S��<h���:���B������4��C,��LSv����`��!a�@9�|A2���L������0���Y�r�D�v��z��~zn���pg���[k�
"J�,o�^�g@#
L�����N&k��	�2s���&�Q��
��b�l�
�9��"!C�3�f3.����&F#�XY�������pQ��<�������k{iq�yv���T���d�����K����+cv_R��)���Ie�����jR<S�R�]L��:�Yqb�I�B���B\5�0��X��&��\@
��jd���2� �F|��b]��u"�n�i{�sc��J$��u�`dI S�+$����[&�-�t.���g�X�-e:�2��L*����L84g���&x�s����OGS���;���a2ac|��W���9&�R���V^&���2�9[^lt����;1 �,�����HN�S�����W<vA�$eK����&o#�-g
��
��zD���A|<��G���.��0�5�{,����px��!����o4������"R��w>�(����
Q�	*46@�	L����#�d���b��7��G�l`��������.9�T_D��R�m����NX|�!_�<�y=&��"y���Q��@������g��i�G�A����d�9�a�>�T�
B������P���[����4	�Z%�`��G��'�e��-���V,=���e+e����1doY-!5P��|���M<K�<�l�f���X�����k�s+8���P@S��7 �oG9DI�z�`*=��)ba!
g������5�HH��AA6��i�q�V^w��u"1y��$�;	�[l��h���z_1U�t�������B%31h��tv`�j�;<��[,�TS���������C���e �W���P c~%����it[������A��)2�[����{�l1
���n�x4%5l��,���L���E��$�����?�L�q���������L�~}�&��5��F)�Rj �R�0�����c�6�7�>1L���1�A�t���eX��E����pLmq;��f����RA�	EP�j�_���0���{���7��`���d�\����^���O����P�f������F��bU������:�A��"�ha�c�|��]8�5\f�~MIs<v����V}R�5���n�Z�0$�����J����aC�?M�%"�{��a� *��v����s_Qpp��n����Mg�5���M�@���3d�	e��! ��E>�m2]1���t�~���]
�A����5S>S�o(I�#��^o�[��q�9o���vU+o��J��#�[;��m[!Z�U�����p�,J��]]���\�W���{����Uw��7^�����[�`��a0�gA��	��?�$��c�38B���^�*#C�^c<���Z���m4������K��*��r�������N8C������9��+$@�#���*��	�^%�k�<N���7��=f	H�-]��$C�k������������������|��]��e���5���R�ts�z��s
�
/(�(Z&��] ��a�#�,��2G��'�j�N�%�d�en��6��s���X4fnOg��j!�R�aT?8�:�v����~ ���x����e=d;adKz�c[^T��w����t����8U���>��!�q�&wU��r�=��|�n
�����:Y�I������1`��R���E-]p�������uHYFz0�U�%��&��J�:X���.�s�
��yW�&����4������uE�LB��ai���n��w���^R����������k���j���������&�kTS�v�"�����@�����Z:��^���}�y1�3�r�q(N�c\��%0��~
���{X#�����00ED����/������*r�]��������8��	�7�dwI�w��n!F�l�]%�X�:�Q�(� ��8�&����i�`�L�p����hu;������_:go�^�����$;����r}3�]_
h��� _�$���Ed����+Jms)��k�l�U�,
C���9�1
�*���s?��.��:��I�MDh�$'g��A�<���Z�5Fp&J���M���4IU�V�@���l�*�|x��m'Mr;U��������t�UkrI��Zg��aN�I���@����k;�	�����o�Uk���y;t���z{=���K���'>e�y��L	9�8t7���~��5��,C�h�x6oq.�e����	�`B�&�+�0�Y�z��"�iN6qP�_`Rq�/���BR����F�1��i��;���5�����tA��"40�#��f�4��&}Y��)F(��*�Te~�)�G5("�%��nb�8���I�����+Ov7Qm��U+$���N����%��h��2���R�0�D$��#	���o�@p$�`q����X0��A��H m��}���V���N��TcuS"���,���^��b-6�R\����]��'�abl���'G���Q����or<]'�[U)�����E����}������������O����sY�7>j��)g*�;F��*x���)Th#J��UA��l��������|��`8'�g���)m9���qf���#�CD]�9s�.$3Y�9])�5,'��^���KP��4!�n�p��y���~��y�p!���g���`{)��#����DC�=�ngd�1nQ��gA����st�x�>Tst����������F��/4FO�S4@�y]�bp�1%t	u��N+^kn����.F�a����q%��q����8���e��:���_���B����a�-�<#��*�.����b6��|VP�h�TW�����E'�ueK�$�6N�,��"k.���
���sZ{r��������)\�D�**7=�&���C�P\�>�*S�O�r��L&f�%�������B�W�N}�����|�H��K��<�+��j9}�2��)��c����p�����/��Y�����1��������fCK�b��Lr��9�CY��f}�#L<�E� -�S*+��n�VxeZ0�/U-���6�ZB������
��$O$���'
CQ'-�1���������s�`nU��
��>l�-z�Y�7�8[�4Ne��o���{����@�$Z�8�g����������T���z�u�KT��l��R���{�]i��'��^��2�$��@��2Y5�G�1�!BC�/)`��\(~�jZ�+P>�Q���!�hE>/H��h%�������oX�n6�f�?>�j�#��7��,�952u�[�MH���������{����B�m|����D��+=<�+�z���'����&�N!(�S��k�A
�o�)���"����=������3��%���)��y|��L[M�����V��o7&����\W����J�J��\�X�����E��b�Qa��QN��!e�W���0�o�y����O.?��O����J�Lx�W����jH�EZ|}t�����U��2j�|�{;)H�'�$Cc7&|LE��f�����K�F�yE�$�������l���������<��f� l/��6��`U�Q���8td����reT/!��Fb���db�I�����RQ�W�3�CK������F�������nU�p�kU��o<%������L3Tu���n�n����Rq(��	vA�GO7�g����H�j|��,��&�f_�Y?��7���l�r��yW5!V�i��nU{~�*6�������e�!���d�,�XLn�R����):�k7q������CY� '�
�@��)@>�5.��z����hY�x`����"��3��3&_	��]���)�� ��������^_����_\�l>��
�j�����X����2s��>�����V;������q;������|�)�W�C��4
~t��d���]��D���hi���/���Y�4�/�����4��"lv	�d�"-����1��>M���O�
>Y���Q"N�hY7�l�����L�dS��D�zG�l��""�����;3�I-��L�����M�G����u�z�����-k���]A9$�:]�������60�f�q�~j	������3*��LLr���A���a�+�-.����w���$>���G��G@uy����>��p����J,J7����/�_���9���'��f!����=l�<w�}���v"Ca������b�e���Q\B�8������f)gl�<�����8�@{�f8�uL9��!ue�,q�k�K�p3�w/(�������.�s9����I����B#����>(��2���p	���@#���q��XY4���DF,��R�9��b���:��%��R����5�$A���$��V�$�L������E�&���x�A	
���|�rb�����u�]j�z�����N�T�������D�E�Z�6����l'3����~	cErh��i�Y8@���F�� ����a��U����_��kT<%��Y��l
lY�����9�:���x9����6L��b56����WlK���<�	?������P)���������t�C�Xz�qF	��sX	�W�4Ud
GOp.`wO�iRc�>�6N�e�G�����7�;N�j�n�=�����;;�Y�v�T��l3M�v����ms��m!�$�d��o��.�\e��������������@�L$
��zA���9e�2���x�Gg�ng(z'���G�o�A��V3���EF7+���1sif4H!/$#�V*@��{�d4�"��!����X��*b�/�E%�=�U��5Xy���p��K�	���%>�g��t�����8��h8M>>�4�2(��;� 0�S����������!T,��i������a7���I���9n�^�6m������$�
���!7SU�x�j��?�������1Xd��I����cr���`]�3s�"�O�L�q�z��|-n�s�w�P��UJ
<}����ZT'3�
��s7���$�EE�;��;��P��8���$��������'����E��Z����gr#�����-����n�V;�[��I���>WPK�2%2������~�V"�'���X:�!���;���4���LMF���Io��K�[i�:r�����Vs���i�p�N6����pjd���4��^l��3��#!����\���^8��g��x��#����#����GQC���(B2��P�T2O���+HF�����F�R>����J=���_�[�#lX�Esa�������v�����wrT�Ff$�#��C���8[o$(�=�.����� \j|E��.Q�������j��Nk�@���E@�
__�R�
�`|����J�-��MA}���@M�����F�VFt�Bk>���M�qFO����Q8��D���x������;!�b�'$]��	����3R���������j��=r7��
F/���+2�PI�8��� 9��<D��fZ�qkH�?�GL�T�s&�FJa�D���Cr���;@T$,P��cjJ�2����S����V��5{	$���6Z���E����_�G�t��f�lPem���h��4��h����(���R�1�xl��V``qRK��#r�.�`�0�@�i��W ����OVK[���5����������v��W��K�_����	h
1��Q���n�+��,bS��Rv��
�8%h�C���L�H4��p�Bo�llD4�*�����\)�`���H����;,�K3�]��ET�-k!0��?�8�N����Ar�S�����e��#��mO7�,���Y(�E��8m��&����V���~���o���E"��n�+���L�Vd��}�Q��m���|��V�U��n���r�#	+�E�z0R�*�e��M��f3�A�>[o��*x7�B_P��z�����:nW�_7o��_/�8Es�,�&hV�6���Pi����22�T���+�E#Oz��i�����Ju��N��M�J�}9h�Jw�����cq����E�y��S	���e<�3�(&$h14�;lT�f������1G��!(��	=����F0R�,vwer+����,x�]I�p;�N�|�~��������Y�#on�c:�����L���=M��r���.���s��8�tp~K�;�����%���u%��b��V���uv��
"�-i.!4#�a�3R,�r82��p�����M��q��l5�u��)vJ0}``�u:.�a�c��@Z��Y�C>����\��o�������D��#��I��������q6LNNZ���v�X�����P��g�4�4�Z�E�o��*#'�������KA����nw*��y}y����	������g���XEY9O�AJ�{�\Ky�{@�E�R"����g�B�%��E�`|<q��=q��O��G���i�T��V)
S?�0�C���5����������l���B�kA�*E1�$�����@�[!�i����������tw#���N���GM���)��)����A��o�j�q��L��;��i�)�c_g��6X����*A����3�E.�����#�O�O�'��au����4���*e�(�nT�(�f�!���b��:��a��q���N�nL<�q,d��J�cv$#�2�2���fc4H����S���QB�]0�H��W���g�@�L�K:�i�ireo�MD�5�.?��6��q�9=n�~�8�|n�F���V�H_�R50�t��:`��K������T�I��</�h��S��4���Y��Ea��b��l�rs����KtW�'���N@���!=`L�<��7(�&��=�F�<d
���|�B|�t�mZ��2�����?H���P:"G����y��=:�8� l2��#x�U�Q)VT�g�N�9|7���wyF�*���|j�&�bw��.��O���8J
�"��;`h\!R��I�'��9BR���19EH����P!5��a��L���%�����HkC�(t���r�$
�g��J��g���g�����q�@�3�Vb���0����Z��I{��"m%��Bc��<����l[>�\k�X��V�x"�������q����u�*J�D�`M����U��B����x���qM@#��X��X6�g��7����A��VK�������}#������Cn_A:��mh'�.J�����f���|h}���mJ/�W�O� ����H��:�����3�2OF�@v�W�51�C��-����<��hl�2�<>%�C�`U��a�������N���B-N��_&'W� _,Zt%�T�sAM���������>?�b�7G=�������_�\�[Sg0��X��������Z'��y��>��L��_����xWu�3�Y(.
����&�MS���2~�8�i�Sp���L���G����r��',F�����
,;<�5�[����c�Jn��!��`W�	�u�<R���>8����{,�	�7R_l%?@�[iwp��y�X��u'�����kUYq�.��.%��}~��3�����2g�s!R���&u"n<�3�a��*�iUP���5'�WU��*�eq���@��[�bB�]VjWA���U�L<��Z0�C�K�Z�������#�e���U
5Al�q�����%�!�~�K�au�v	���R��|!�a���z�		uf�Mr�����:a:Q�5���������������t�Q��*"�y�U�fB��B�����a����r�����A,�p=/F�Id;e��/f�/'���/+��kq�����SPn�p>|�"k�5����Vf��FVkYmhde4�����i�6�������s^IZ.X��\��XN5��8X��SV���d���2+wC�S0��G")M������%� 1 BM\#��	Z��ID0�e���P/�CH��Rj�d���y-���G��&�I�l���`N�u����Ji���O������
p�c�]��ux�������nq�8�N�G�SRM��<����h�{|����0m��BU}��CC
��3X��p��S����O(A@b�e�"��OIA�`�PnbO@�.��*6X��7R��y���d=�i�(P��Uq�p�������~���Xwjp�A�ZRG�{�j�,�C�p����D@"��'?
t,��fYl�;w�W�U�KHY	���I��	�;�;��C��;y
���s	�v�#�H���;�2����-/�����G��D�������n*qO����HfCI*#�)+�
�S����0(�:�kP��-���:����t+Y~*.�V*�VP�d����g1�#/�-o�
�Z����C��i�>������]�|�#��c*�%��$b/��HXD$ p�#/lK��,�����dNF��)[�CvbSD�3k��[F{`�x�
�1��3�d�k�@�������u`��#u�1�=��l�#WU"2/
������N�o�������4_����&;�G��0���Y�i�����	����J�4��>v'���._|Q����j_f7&���(�2�N1K�yB�f��R-R�0�J�"��	����O�QR�X���"���x2�����m���1%�Z[�y �Z���u��B�����E_�z�����|Xy����eY 	O��AB�C�	�4Kd@%����C�}R~7>���q��Z�����r
a�j��T$��������������
��P{*x���Cm��9���T�vy�Mw��7�*��H��x��W_^����^�U���Vc�P��C�rj��u���dq�TR�
�}�LV�}\����R
K��d9���^�m:j�w�h.���g���B	�������>b�����3B�����C�tc_��#u�O�@s%��$hZ!�bR��BA },���:�����w1��9����z����xW��S�����Ls����#/I�����xm�+�t?��'F�=�.\����xb'�^�iN��j^Mia-��&��A�d�P����q�����c�E���������8w0r��p0z�}����,5��r��0��3��#���C�+��2�Z	J~�c1L�i����t�?�0�����Ktj�0���Mnqa�@_% w��D�9*�p��k���9*Ro9���m&�&�u�0�zQO&���5uL��=	�X6�c��lb�e��
I�
rY�bE���lg�X�0�%����r=�%�����?-%��TuA���V�~5���^�#gq���&����C��T��t��w���y$9&:�9�{�r���4�
���Q���Cz��k5�h"�����?*c�z��d���d�f��s�fA����w)_rLwB~�������v��jRWn)	=1a�tQ����\���E������j�
�G��������`��������������u�`-���c`Nu>�P�Fq���-���&������� i��]�P�^�#���z#���S}����r�������~���	�G\_�v��|A�R�� ��k�~�S}2=88���c�V�x����;>.����R8�V�}����3��t	��y�5?���1N��O��3������!������������sg��U��j��:w�P�/@�I	#�������(	�� �d5���,/n����c.�8�(��#1^W�t)2/$�������9{S����������h�?95p�#�-� ��w���}"������lp���T��].����4f$j���~r������(�.�{���c���F���������+��G����Q�����q���j���u�=�U�#q�Q��G����8�����l��I�&3KH�Y�����z�v��O&��_�[S���J��F��e�1���$V;Yz�����G�m�Ge����-��r�)UGh�
3Y��"��k�CS2�j�N����j�	 ���	�c�?,S��t@�����C����h����t��/��|3�(�
���*�����rl�(�a��j��k�tL��#eR;�<'���������{������ �U~�7i��I�Kh��&��F�Y���AX����|����Y�3$��zX�b�n���q�j��'v�_��R&w!e������B���E�7R��\qW��y������������*NFi����E4��������(5�N�������k�"]���v�s~>:{���������./b�c���V���[����,�W�������d��%����@o:���E�.S��@���_8�������^����X�#�f��
��-��g���Z2J�����{%�y�����rt�-��e���v�������`B�
�p�ZI	$7���������SL�t|H:��O��6�?���<���d1s����S���#���Sp�`,,D������Y���#����0�8�z$�&�������<����|�K��r��W���Kg���1������!������"������f���O�����.���dP���w�~�:d���M;�{�������c��0=����1%���T*~�>�k^SpC�,��<2s*q�E��%�*���5$��4�GH���'������H���E3Fs�
����c���6]�m��2��K����w<q[�F����y������QO�E�(�A�������X����$��Oko ��������?��C=8=d��~-d�����h����|��?��� 
��
A!mL��o
������YsTC���(��?��A�R$���E
Lu��$V���pr�v[�Z�oL���{r2�Rw~����
&fJ������N����������@����9�0s*�)��o�N�������u��D��Y���*?\�y��rZ�[|z/�	��������V;i����f��I�U\�Z"�2����C���?m���'XG{�x���������o���P�g�W���!������(�*�R���T�����o���s6�:�$|P��%���\5���j��c�b��Z��s�������V{�f��fa�ga�ga��Y8�
���]jD���5�o��~o[��������Xy����f������m�o/�=�����S�9*�7�������6��^\vn���?�t;@�pvq�5�lM3�����?K��sw����1��_�6����iRo|w��C�&WN��j��_1�>F���]W�uU5?����~�I��2�����/�M��r��k���-(c<�������3x�cr�;��G�	~���g���U����+��u<�6�V�E��/]'�e�C=Q��'yM
���d��%K�Z�T�%K��d�iP>2Z�lR�&�?1��]�{>��.�X�b���
���{�#~�p������+^�������������u�_@x�Z)��d[ke_e+>����
G��s�G&����h�H���f�����o�\\�{����b�w�k�]E6��Q �be��dg���
��^u�=>��L����$A������oB����8{g������qI���Qv��'�X����`�w��X���o)�7�����&���R\��c���=D���~g����o-�����C�)n�[�@�[�F�Pq#�R�h�X���T���*2�k�_ >���L�Z���/V�!�o'b�9~~Wqk���^k(������W�d�=W%w����������Z�m��&���-R����8�����;��j���F����������*�tT�6�����H%J(<� Fy�F<b�����������}/���S�����;�-� T�@e��b�N��4��I8�D�d�>,���
�3~8���z��T���g����wd3����d5s1�b�}�'�mq!H?~�8r>u��>�C����X3K	�����hA	���60TG���I����K��C8%���Sz�N�UQ����P6e:���`�������C�.jF^|y�HCl�5:��'�oO����G2��LZ�AM<�{�$U8��K����p/qG�. ������o;�]�&����+�zp���X�D:�gH�y&&�#��J�^���:��

�h�������5���R�H�H_L��^�c��#�D�����rd}��9`�)�������}���q�qt�����V�v�:8<>�6Z��OF=��(C.��QHf(�X-��������}t�_����|�������(�	l��G��;Ae}�]k|�\���NBAT��+�63-��x`���/v�|1������K���`c�}�_�S��RFg���j����s�HI��Sd�����rv��pu4f�����/6�a���1�����<>l{��W��_���������$z������b��$���R�n��R!�0�������������:�a�#��o���������(Wu�x���7��}Bm&�6!l(���{���}K�&���s���]���<��m�'����=��Vx�Da�K%[7��*�U���F������A������������S����2vt��>��5?����S����i�[��m��w#�'7F0�2[-Nn�zI��"���*�mW�g-9�CA�����Y�
v,�����k/�g��^�fU�Hs�<��~cU� ����*{1�����������e�Q���0X�q�we���|��M����N>�%�&���k &��.�c�1<i
A�v����L��n�����Z����pc�F�
��e����l�:���{��������y�� }�g9�^�����$p�Q�s.�@^�bM�w��/$��="�*�ub��mj�����&T�>v#&��$[W�-�� ��A]P�o�i���n]HD�LM�`�m�j/cbMV1X���o����A���Aj�����4�P|x�/a�G�\��e�G=h�~�K~��P�*
���N�C|P���5IF�d�U�WFk�V�����U���N����s���M�0�Xg<�oj�D���P31�6�]?�(�"�@}��o7n0�^�uG��e��{57�jN��W��_w���F�{�}�4��~v���r���nBY@|/���(��V����:\�������|]��S��
]J?F���t2��*��X5�i>��
��=�SC�b.`� L��)�G\"v�%q@4���U���T��Y}U�e��$�����)����JTK��T"f~��yX*��_��f�C�?U�B�>��Fuj��3%H���)[�/�<�p��%Sq.����W���������LL����]��3�"`����$����^���|F�
�Bf���GI^�f,����rWc������l=F��&��>'��U�������J��X!�E�j��9�����QJKl�i
rDqL���=�D��ZZ�{3��U�%�'(�`��]�=i������$�t�S�URr?���n����#0��@��=pE��6'(o�7u���A2R@������
��X���
�E���pP�+.���*!�	-�]�=���&_b-��K��GH	}�1�W<��4�W�Bx��V��/����	'�G�k	n{�!�<LG�Jw��WB�CQ���H0��w|q#n��f����b��1�������8���+`{P��a���v�~FT��$�������_(Zs$iZ �+��<1��e-C�4+6���G��_�� "��h�����|xIU�����%���=(����8?Bf�Q�<�gV?LY�*�
�v�m����l!��'�����S\���R7���"4#������Y����*`4l�d��$�$'�eLD��AtrZ�KS�� ���e6�wOU�6�����@0h$���S."����R���a���Y*"����8���q�~bN7��J��~�~�*K������Q1�]l����[�=|	B>�m�&��1��mt/�gj�hx}DD����j���lG�)�����?�
�0���|(	�A���}��}A�Fp�'�{�_�"4�17ch4U��R) 7$����WiL8�R��@���`^�h�yh��,�����$��Ku���QO�����|z2���(�k��tCo/���1��.�,�I�� w�WZ��M���WS�^#�^�z�s1�(���q����v�A�����O�fW�Jb��6�zDkf8�c_\��(����.�m���:!o�GO��7�O�0���4���L��,��_�,E�U�����������v���xT�T�j�h��	����r��?�Ddw.)������1/�Y�9��_0�2B����MN���O��_�����h6�4�G{��������|s-�a��w���\vG����7�F;g
b�\D0��p��.������'�a ���(N����QN�	���AlJ����!������
1_���$,Jb��K��V4�1��1������g����&q��I�@b5T��� G�U
����X���s:����Q.U���4�0�$��j�I��^19����_���F�kV��B����F>U�&Z�����j��r��xJN��XD�b���Y��B=���,�&:OH;� �C��l�z�b�j'I����Z9�Z���`���zQ)�?|�[��TT�%�	���}�����/����YO�R�^�iA�1�gn4w����}vx�
��b��8{6B���\�������s�$)�t�}n�t��	�<���h7R(� ����F�s��:7�:rm*�����Yt�e��;�"���x�K7�UH{iIX;����]/�����1���zSY��s��E��&vH��?�g�4_Ua�A�mG���L����9�q����(uAF"r���&IJL
������A�F��K&��*5h^��)JTQD��yJ�9�J��\\�����V��	X9��`�l-����?����V1uk�0�&?�`�MR>��*�Z`�q}$�D�gT��*H�z��8Z�'����'7���
�t��sldt�=��lG�����	1�!::�4�Bxv",Q45TI������xN��a��r�&��p��F�d�K)����<��[�/d}V��	)��>���h���Jo���d)s�cJ��_%��I!��L�����S�Dza@���g;B	��m|e�A�q�*C�c���{�t��/�����g�M�	�]��l�6��x�����v�|��|�2���Rh�qU��S:�6��	�����|����h�'\����Z�WyJ��{�=B��M��_!��\���]��2����+���s������������� {��>��^�F-4�������E�!�W�
^�Q�������������8�1�)���������0����0�X4$�H�\��cX�OU������2����r��@�:��y�)�,T���������"OW�Zxx�DP���?��1|��'y����I���rI�B\u�x�N0���B�"w%]�X��zV�6���:���R���Z�dgu�����������8�Fi������-J� ��j"�\H�.�gl���$�<��8q�S�D�O��F!K�PNB(�uAfaUe���w0I����S��1$&���$Z�7r�T
jbe
����$�1���J��Y��l�_N�/�tT^m�d�U�\Go��d��S�iAE���(�D,.N?@�[��C��M ����"<*���>}�jq�P�'�)�}*&��M�B�L,6w�l���
��V�c���+�Uva4��3�C����r	���<H�,Eg�����k�:zT������k�a0^qg���o���b%�A(��Q�F����l����6h�-5*��e�w�y��-cA(+������m��:.��Y�cj_�#���������V�b�}3fD�Og��`b��������	�{/���8���/�:A��_an��:&���A`G����Ls�2M:}l��HT�N��"g	b� ",��`��^^��E����T�Y��.�3t&{d�[��JS���|�#�f3�H�:�[�E(�w!�q��<�n{���x�5�t�!�����0�U��	q&�w��#t��M������bv�Z�nb��
vJYA�(99.yq.I���d9:�>dV���@�x 1N��a*�#F�[��	��3qxq.RK�5x#�����{y<cg�����e�j8�����vF������Z����Z�1�^�+/�Uq%z�8e����?��\`�����V@�QW������0^	��y��B��S�^���iWM���,�����E����?�&nDc?���9
�����xS)�m��� �<�A�l���4�*�	��6-BY$�Y}�[-;E����H
���v$tL�������&#��y����~�wd�Y��:|��`����C�+V��[�yz*��E9�UnLe�
>�[�J*��c7Ja�xN���A�&o'H:Yx��(�����O�m6����C��Aq�y/����D^����������~�\=�M���[��'�>��wO<[V1
"ETL@���A�O6��\b�r{/���_�����j��te[}���x�1����-;����W��}��L���6`�Z�v����E�?���"�g��$�������f@*wLz������FlN���*�T���Q�\_)�	���I�:C��i6z���B�G�DSkb��L����KP�&�>����A��e�.| =RMf6����|*�Jr'OU�BM��e!�����������w�F(�O9Z#��q���tY�=�� hL%�W%;6)�������"�d���M�^tb��E�y��'��m��jJ��Ny��/��8�����;�����
'���<��jD4:��(��.y96�����:3�Y?���fI5U�L�`Mi:�����<3���{�c2�%8��(Y��L�m:|W*���,o�h��,G\���x�}�I`��]��(/�L����->N�q���Q2B��I��]���f��0TS'cO���p�WdqN��_L�������uxd����tKm�������'6�(����(�u��R�a�u������������we�����`bctgU��������|���C]�����{�Tk#S������{����N������a0G�w�l�����Z5����cK���+L�D��t�In�����+ �L�����`����?��L����z���yop���C�FQ�a����T��,*Dy���P��.�X��I���2G��i����:�M\|>g�%N"�F�������Yt|#�hZ�|_����+���O+�-��3�c�!����q�@�HT+g�.�M3�4�k����y�LDRw=Rsz���B���QN���lc�b���9�;-_u��%5*�s+�%��)�K�x��M�S�P@���j�'G��r�9���g����9��V{q���1�ds�����.c�(36)�q^]_��`�U���e��7& &d=@��VHP ���~��rW����-�QU`,0�o���w���`���[f���k�,������:+j(���Iy��S��I,N9u�_`5%�4�1�!���}(S�X�G�9���N���[G�N['u|�~�|�m�M���B�p������o:A)a`���[f��Q�b���v���1j=(�)`I5W:�!�6�5����m��i���Q�����X�IG�W��.WFP8��Q��TB�����'���4�g��&�$w��+��C'�9���N��t%�W<�\����U����G|W'���F$<�����`�=����L��c+�������'�
�,��p�Y���EA(.2��
��"����d�acYqZ�k2'�f��a���I@�dDp�Rn��c�B���
�����a���r�Gk<�Oq;d�_��*��R=���`�B�g�aO����,��
���;5���*�����kFD�RyX��g�
�$��F�J�I�Zp{`����_:�s�'�$6
(���.H�I�g�vfI(�I�R�����-�zkn��W8���>�DJ��(�{p>L�5i����+��-�gTh��q,��[�U_B����:�)�*�+��)S� �zmx�Nj*�+����$ds��y*)���US ig���)���uj�S>Wb?Y��������E�B����Nw�F��5!Y�Q6C��f�J�)��M�eV%VW��%�SP�8�DCP6��(��*�}q�x�0N.6
����2RR����U�"��7��#�f�����(�YA0���L���C8��*����|�_�]��T��}_�������IM�4'��4�  a�;�@���K���&���a}{�7�H>����+y^.�Q��5;��h��Umm���������FHm�������}��@9����
&���D�<�������������������sI�A��h�E6��vS[���y���8���H���������x:��/����,��A�quXJ�(�yN���^9��9���`�ur��2�,h�f���75����s�����	A���(\D85$�������R�/h�ZprX����gi��t��J5@��QJP���S���m��n���O�G)A�
^����@�A2�N�I0�4��68�A�����Y�������ZR[O��v��zR�����u��{n��5��@_��_�����8Qq��-�N���_C��~�=M����4�ug���,J�{��7/�v��RE=(j���������!�#��A�;$���,�����;RJ�c���)���������{
�	`.��$@�kf[&Rtn�$���s���00,SB��l��"E%�6�7d���Y.�Fv]�r\��-9@k���s�`3�R�o �s$��jJA�b,(|�m}����+L�@�7���G7��*�j����m�q� p�8�(��@gI�qn>b�1�4��O�X��������1*�}g����C����$�����H�p��wco$�\o0�^�>��� ~��c����b`B��w��ax�����=�����q���������_��Ip3���.���M���e�@\�<�r�6��:�_E��rJ/�Jp��	h��-j�K�[���BKy�WR����?3�@ ���0u���T�m������'�����1��i�n�g�L7��i���7�������0+�/r�,BP�a�����;Sv�}QR,|�u��BFKK<m��F��T����C�MGX'�����[#!f�[��=��b<����B`&����i`��
�n 1S`>
������m�akP����t����+Q����*VtE�� ��)f{D�!w,h�����w��Ha��L��p���0w u����5&�<q��|�n_��!��JJy��W�'�������Y�Z^��SdK���?{���F�����Sd�=m#!����X��vt{{v�O�Tk�
,k�}>��[fe���gv���iU��������_w�hAl�!F�*.���jG���#���{Ksb"�l�9��8*v}p�&�������J����`��U������ �C���/�
DFuS�!#el2Rt�3l������;g>���� M����_gi���E	7�d���av������<�����	�C����vn��t����^�������o:��EA����\��	�(m1T�#"���	*3PY��yg�.��2�T2X�/  v?��o:��o�L�W����27:#��M�3zXTa�i�R
��k�@+�VT�P�ZY
�4�P]�}��IR�8��=�E�'g0Em-:&��}{`�z�F��.��&g@{gl������J��� �5
�& 5�����U�[J1����o����]��/��$�`���{��VO�z��&*�th��G���s6YesR_�#M�>�]]��R�������������c���~��\:&v��� ��Y�����3���ypZ�|�!��V�_���N�����70"M�3>�� ;����iZ�?"�����"��(�����G=���0OO+�������!U����C8�������ex�����),��Ft���>�6S�aB��������:����7������I>rF���O���a�,�V�<�&�C��_���FtN�m��Yb��+ni����*�\�o�x;����������H�C���rs�X�eEdG�h�X�Q������~�<s
���p$zfb&����8
z)+�������?@�,B�D"G��=:��0���=���M���Y���>C�v�R�k�%�7��f�>(D+O�y�OE���0"�2��j8��M4-�>1��n���?�f� �r�8��mO.G�pQ��n{�?�{�7�)	:gg��w����I*i4�)SA�ZV)���������s�E4R�*���CM
�o.:o)E����w���
.;�?����$D�D��,;���1�������uo�V����B������'��7���ZB�����cp�����]�X8�`��������`���P�tL\�D6����M�2�R��O�WA�LB(c�p��e�J����ii���n��3z����Wj��&�H&*�������#��}d"��|�U���� ���t��$/��S�nrR$�����}w�)�#G�sQ�N�/��gola��{�����c�����x��`��*�����:������
�P!-T�"gG���E���)��;���
��d\�F���)��bE�ML<�����@��H�����������h�:��o�E-
������][u����%(�x�4�'h�R�i�euq����Fh��E��#�WB�u�K��i����
erLJ���B�(0-�{X(�W�c�#���N���\���6�'�,���~�I�M����d)Nc�>���f��N�����������T&�	^�����{�z��2��9,L��#�'��xkuc�����y��e��kOA}�����]�E��v8��zz����S�M��_�Pwo�2�)�����8�t'd�'���������J���K/<{�].w@f�
f5�\�C�
�
���M2Gd��Z����!�o�Bf"^�p�����c��Y0�����	Lo�5�8���M���n*\g��������(���8�j�A�0B��a�?y� dL}IT�MQ'�n@��k�t�o��[�t��+BF��)j�z�W"y/+y>��z��WQ���DO�:��^2�|��7V��	����w)rG�
Y
~'��r�6i���S,���Fc�r&Fc�M���4�3�z����&*f���������'�ls��=�[�qy�i|LG��3[�$�F���rk_-�
-��Jbn`KV��)��w�x��u������3uD�����Z��*�s���C��@@���1�=.����1��ne�o�r`�
�ht�y
��)k0�B
��]a����d %�cu��;����8�JX��Vi����8K��~�����u%�u��^8�+��nOv�������D=K?�8q��(��E�����z��?�T�p��T���Da�X���7f�|�he������
��ft:���u	 ��!��Y���o�E�Qks�K�����>��N��A|A����
4;
0q��7j]�z��N��x�h:�v&����d�hHS�C�:R���r'�'��Y��L�z��.(����HdY��jG�|A��	1;i
�����>F��
{����a8n��ImXm�z�`�a�~�V��z�����d��A����Y�5�0���3~�`�����*���7Jm:D�E����~���c�R���b�@���%����#(��E��1X������b���L�ZI@��J��#kU�[��M�H%��X%�#�)F���"�J
o��'p�;���`fN����R����{������\�,���+�����2��M�{C��|2[���U����8���0]��D�7���������X.V6A'���d������v(f�k�^�t����]�6���2���ZL�3�"�M�����%��n��n)23��gZ�w�9����%f�b��X�����w�N5�a����H=!p��CB�YO"� bv��f�,8�4�	�0�K����"�H�������a��-a�U�X�b�<��bL>h^��y����D�����}���]�3
u�
^$��E�6,}O�{�#:m��i��M$BbuX��s2i���f�Y�9�q���	��#c	I7�&�H[�F�d��~���s�J�����$����4$%����5$�8��r�8�\��T�?P�$m�v�x;s�O�J#�����j;���U��G�c%j����%�C���1�`����jO��D�~C_��?����E����:��uc4nW�f�5,�v�����N|m(mdj����E��"f��/�
����������o�o_�3�C�{38��;d���fA�P�A�t�>|D:mo���L�\�[J�������%Q������B�R)l5�5�U�����x�X��(�c���-2����rK�`\*��Z����6{�t
��5����Yo������5�n��[J���"��4�#�1%^��E�0�P��Go��7��)/h8G���X���� w�����!�,��J��p`��5F����Lt�(�����"������U���_������z�c��/p	]���#���B��i�e��1�X��n��d�o����xq7@�lt��1eJ
���[��'NmR,�k���\�56�~�QJN��T����*N�R!��(H�tWd�s��z����f�|�|����7����||^������L�E���U��D������
��p����F��q�[����k yD$�x��L3����}���~�C�n":_:)m��t!��}Tl����}F���j�����%j� �j�Jd�0�vF�y��P%ts�W
3��P�@i��H����%�j��G+C��
^�R�1���4��������A�0�t�����\����W{$������tx����":V��<�`�o�5:R�H�jdX0%����
4D(���~��g�6u�8��d7%���g�}!7a�x��\�&�����a5;����"M<`h�F�2�L�����^������//?����������e�N9�&��Ti��V���%�G�J�U/7GS%w��Dt�V���?dOz��s�a�tFR>x�������|�����%B_�t����<("�c��0����v�?]��N;W$�[oI��}�����q�S3�K��td�Q��JNcTA�]mL���;����=���vZ����G�h�|o�P��?��� z:z9O��,��z<;f��[E��Q�[o���F��aS'���2a�����R��_�A�Q ����b���[���������T��5<a4'�z��4G���O�I@"	�U-���GDAP��l�t�=�w�������>}��s�=���^��/;7�����m���{1���t��b/A0%��p�����>J�a"	��|�����������,G�>.���i��[Gj�N�.e������1�����:lWG����:�zk8�>fQZ�M��Wi�D����,Ft���-�D���%��#���+�{��qQ���
��#�<1�����z�^��%x�����q##�����I���#1�	M���P�{UGA����]|����J����T��thH�uPhV�7��3��C�c6�^{���^N�S��=����+NU��	�J8FH
�G�(��H�c�o��N�����3�_��h6�-�U�8�bq�N�Qjn8#��d�I���Q��m��$������XT�l��HnHf@���N���'b{L)�&��8.<��f�)a��>�;�YPf��������R��=Bp��/�l\�����:�%��
� � >�M�5�����`���t#\���-x�����`2���[h�s��Fw�x���XH#[�:���N����T�Q5%����{���,����B�K\��uY�����X��+`+�I+H�+��v�,�������jm�l�l4n��ry���;�6�����T�;W��#���
-�@�a��%v=��w����
O�//���~�s���N���7�nC������-n��8�\W�W]�D�v�w��fu�`g��I��.�K��V���B,��z�w���-��J$
q�����%������SJg���Qp��D�g;3VE,E���Hj�)x�JE�?-" x)���)���j�?����T�_��5���u�IN�K����h��i0V�NDBu�����c?~wn(�;Z�+��F4�OyK5�X�hh��������|���Gog�;]�����^�A�����������S��TZ%2����'�^B�����0�O-p�7tlJ
����=�Q
����A�[S	I�+g�V��:IV9���f��t��r�f����#��ba�Q�WCx�_V��0���;w�<
o0#����,�gL8>F%�lr�7����������#�Dfh4�J)���EI�1���J�����=<�����Xx���mT�c)O/�R1�w�m��`�-S��C�N�QW�^y���n�<Xo���9�:�v����a�5n�Q+[��)$�O�-i������w�y�����!� ����[�������sZC�{�9������6r|A'��z����$��>������"|3MJ��w7O!j��D)������m����aJ��D�V��[��Z������j#����z�:�����'�z;sK��y�$�VP�ZiRv�a�Xl��j�^��7\�����a"RF����_6u�����������N�7���:�_��_�-�g���Sx��:�������K��\� �(�O/��W}�qu5���K�qq}�aU�����{q�9���M[��)��*�R_X���p<.��bq�l4'�I�U����HV���,.�/ ���PZ����D-�"�XL~�)c����,.�8�L���e�����f���/�
����'���"�yA�[�6D�yc���Y��7��w]�~��[�L�s���3��N`l���6��b�T�������`t�B(�O"��WJ�x��r5�z����s�t��s�����]���0����QaSTmL������)k���{��{����s���lL��K2�������������w�oSz�C����Bs���e���
��,dJ�PH=x}}���o;7��R�G�9����C��c�������r�k1�
wI%����a�\i����x��&��[K��6j!�nK�+Z��?�t��MN��@��_T����y���i�<R�/���^��jM��F�z���on�/SR�����]����
`gG]�g����T�s{�����LB��/_���S\�g�w?��[�������o����`�4��P����$�&b�Sd��:TF
c$�)�e@�)mJ�
�6���MI��p��1��nx����:�
�$����r�2g]
��G�S�Z
�k��Zj���KH10{s8�E��z�m����X���g@Q[I>~�w�R������������+���q�T��-���(z�w�O�()������X��`[%Z|�����	��6J���J�&���V�~�T�7U��t��@U�-�����b����{��=����k��p4n��ZkT�J���Yi�[��Sk��
�U��\���*� �^��U�p��T��������_���s����!Lm�����{ u�/�������
9O�Z/�����:,���z�_�
�T���O��R��\�Y[���Q��-���{{����(^����MB�.�
3���"(w9%�;
��&���*o�jWh�a=�V��m��|E>���=2f��?c���)������Y�vcmr4���Z-���g�S�5�hw�07��yd��(�@�p�B�0�!p���ZZa2��`��(����e��5�@�$�6���5Z��1;>*FP�r<.�\:�1N����^\�%R����������L�e���,�UBq�� ~�z�@-��*�G����vc��.�8�����M>�\+�������a��RP��O��,?D���\����/��9G{<Y%��X*�/�5k1�����:� (f\��Z�h��/���f:X�]�����mM�!�����G�^W�SDr�4�hI��'P��VM��9h02�!���>�D"R��>2��c����$#�82r��&���|+�T=cz�.
��LUt�Uh�p,$�H�z]:����e���S�X����f���&VDu���=.�7�+`�>��6��#W1������9�
T:�Ce�jD�B�Q�a�Z����Sh6���z �%��A��y4�[7���r���!4��~a�{ �L��B��wI�y�B�1�2]�	�Nl��`��^�>B�:����Lld����X~� u�^����(�^Nc`�)�#��?(�Yq�L�|����0
��	7G��~��j�0=�����%:ek����`���0�*���"�w+����az6�c�O�,�l��Q���g&�E�����e�B�wC�Z�F�_z'��d`�s�h��FW�*Q.��S��h��E�7s<a��fj���a��\��$s�j����Z������
8\�?{�G[7
sU�s�`��-����0��r��DoK����"�"�gjOvbC��y�FY�>6)*fRl���R�nY6�U
{$�h9Jg�2b���\�i2���NTQ��I�/�����\U*�R�c!��l�@�A�����;u������;�vt�.7�y7��^R��Z?�1�VH6J���;�b�Z���j�m�8��@0�I����n����n���N0	�,�l����WSO}�~E��J��,���+����������C�7�Q���!=�r%'�r�9Sj~��[o���P�iY�U�����,���(V$�T������`?���J��H�o(���Al����}������
�\�^v�j_
����]��A��m/Z`����aR���������g�i'�NCV������_�vA�|��>Zh��d<D[�1�Iy-���D�ZH1��jur��*��������W�'t�`��}���F�8�W�c)"2E��U�c����`�r�"����d:m���+�a�Vn�N�Q��J�z���vz�kEJZr�j��{�>A��go����E���+�M
�P,��<��1����B��/�~4p�p
��	P����v/n����^�{uJ�Z��+�We6��17z``2
�FF=������	�z�t����KDa;`���0����n����H��5G�wK��CI�{�TM8�[4�G��-E}$�#�0L�<�v���L:�f�Je8�6�`��;n����$�Lc��LlR��B�]a?�Q�"�$�S�G�J�|
��d������r�/��=#� ]��D�U��K'��h���Q~��)�&NKK�#�	u&��<�������ePh��A.�c�
�	e���)�'��i�D�
��U,�~�5�1��F���
���nJZ��({W
�Mv��G"b�_��
6���<��=�Iib�gtk���'8�Pi��g�����F-�U5������JZ#a����uOr~)�}bc�GlY>MK�A�s)c
�Gh� �(�J�X������I��Jz����WjuU�Ky�J�����e���5��=T�M'��r����=M�vY���CBG&������>D�9��Zz��v]�Dm�q��4�;}Xj�&n�<!��9tP�y�Z[�;I��]�Z%�4�$�����@�s(����IP7(A�p����M�Y^E���	�������d�<b�����������U��w��������(Y�5$�������:-�5�!Y�]!�3�l�g�1�"}�����r�@����)���C��"g�� �S�NH��sj��=E����	n��S:Y��g�����eT�}��.�V������19�@{@7��su~*}"
��{�\���u�O��1��xL� \.e���~%1NW���Dw@��.����>)�z�qHUM��s��Qw����n�?������0�n�c�S�`�	T��S����o�(~�s������/wkLd��0�[���.7iX��V����?5�$crB���I�Yux���9��)^��"h��I��Lo�9>�g�e�W
P�KTg�t��4�+z���Qk�?���n{MG>��K�k)U�
�x�����c���u����N��I%Jr���9�������/3p����Q��Y���$�8�#��+8�=`Kw��\�
�r�}��_j
��Vr��C����al=$��I���m<��>z`�1}���:�"�|
DX_�RX�.�����3����(�����0�~<�x�;�c�����B�������7����U�=�c�aGv�!�9�XG���]��c���x��lw_*
^��m
]����-.6�r*��������4�^�d��g(AjXA��"S��x�I[�$��9bM�]1BOM�d�Xz�W�*�G�%S%�Hm�^n�J�q�QF�{�9t�������T�LMFL�S\;5���p�M�co	S�X<���3h��+o�:�b4�"o�������$0�n���g����xFQ�I��S'��q�[����:�����3f� �R�*������2*c�I��68�fn�iK`���a"<������Q�����{2z
mn���Q ��
|2�3�f����!�`���"w���R�'B2�2��6��~����|�c�]"f��I�5�����������Y�c6L��1��onQ�~�'2����6�K_�<4����l�!��@E�P����7�FA�Ym?C�U�<(�R��W��<���,|)�Y}�^��p}��������{9���v{=;!����x}���9op+Q:�>�%%���3_Q���
����~P������{Ub� 5�6{J���K��g�Hd�
���B+�Gy�Qn6t���ws�M�J���e�,�P1t����z)4����K���@�=LKi��F6�"*���Xj����i.�vr'�4��}v��dR9eo~�;���������3��7����<A������&F�,�4X�aX���jr4[|���(�S&�{$�B���cA�B��Kt�f�I��@�H}���w>����h���]#F������r
���o8E�w�KO�"���N��Bhi������z��i����\k���K����;���_�
���>�cf�����������3NZ�i����K��f T�^����d����N;�gY_�:7���+���E`��+8�i������3{��t���;�1�-(�
���k��}�E���O�>�P%
�+U�{Pd��8'�E� ��F-B�S���mB�$��������q��\W����G�-��b����'L��f�_Vb��{�Z�rb�\rr3g��~>�XYa�x�O}Q�x����)�OI�������P�K��|�[�T})�3�Z��{{�yy'��]��������!����8Z�\��R�����<�q���K�gx�A5h�zz��0��\�i��hS�U�>k��=+(��P��D��J"CH�/�	�_7�-D�zg��;#�V�^�a�9�h^�ih
����lA�����'�\<��;r?�������A�o�	�3${�=�]w�!���Ib����UV�)������GC~q�A�z�pF���/��gj)_
r�Z����c��=6������M��Y�4�0��W��F�=~���gx����;���L6j~3�������?���E�~��������#sv�;}5��W�G�s����f_����3��#I6y�?��O��}���tm�t��MF����ze-�K�%iOt���G�]((������]^
�^_��y�u/��o��;��7�z�9�c����&tc�:�s?<�c�ow�8>|!-��#�t�����!0q�he�����`�Jz��f����?�s�Q��0�+�_�!��U<OZ�����u�+M]��n)�H~��f���x���'{7����w����B�,�@�:KX��<� q��w�R��������|�T6��L�k(��T%K
c���H3FP �������iC�}�\�������_�'������~<���7���n����j*��+�:��s~u`��P�g��e1�P��
���_w��=6��w���|R�����Q,��J���Z)H
�P��=��f�a��o�?<�u\�uE�t�b���������������<C���1�������s
�$�����F$�'�3��'��1T��G�.W!��l.~j?����+|9���#v
Xj"���G�C����|��w�������;(�-����������f�M���Ij;�����_�������3m����2�#8���WS���P�7��U�S��z*X������30�������K
4�';�9+����a�Df�l�Y}����g89zZ"D���
�,�6)9�`�"��r����uU��}�SJO��sV�]j�����4���\�^�Jr}�ws�Y)W�h)�6��@(a?��;����2�B)�L:�p25������x���!<�7�2�4��2�z��E�y��A.E�NM��2�H��9jFl$NF,�g���Q{G-���;�����5@�s�(4S�j�=�C`�
v�Wh�*�1<r�e���k�T<x�{0���7�����6���>'"!g�������*�R�f~�l�Eg:��T`�<e����/y�n�TIj���`�����L�Ig�/������O�(���f�K/�!0	4��*T�;����<V"M#����-���x���y�����'�����������D ;�U��@�x�0�rx*�����t�#������5W��8��zp\��h�y�&)P��;c�=t��sE�E�;%�QV[8G�!�n�}�[e��#gM6'����j�_#�m���B��i�7������N�v%��q��d`MJx�F�f��7��R�����5�,i �������D�Je�Q��Z�E����E��y�����Fs5��;�r ���G9��pq�K��&������1sN��$(X���B������:�5����q�b����h��hS(��Q��
�`q���.+h�<~`�[���5z����f\G�	��4�8��a"�&v�`�(6�!`�@�N���L^�n����d����jDy=��w�y�������{{{}k�X��+��� K���]���jg�xl�4^�W�k�Ex�����9�hg����%���U��ZRx�;G�p�Bb!����y�����^M�	�j+�m�%
D���Yc�fd���/�<�]�%Gz0ep��(:�L�+�`��^�m�!u��k	���j��v�;����#b��������H�Y?I��fU�{%�J�D��I��4�zk��'��N��rOT�
�	�f[��C�tf����*k�p�,��
��X/�Kzc���M�����Mr�+��|�d��L;�X�'_��^l�!SP/�b��E:��C�>�Sk��]/W��5�/�KmN{�.��zA%G�@&�W�Hjo��s�*f����k^E�M(�u��
H>&*�����0��2c�MiM�G�r��6���a�a����*����R#�V��R����_�� R�jR��^0��;�,����3n'�	"�-����8���;o���P:�x�l�"�	�W�]2��|j����{���d�R�M�!Xv9�g�Y���;�p�dYV�z����SN��Kz���w�oPuQ��\�g��f�.#��z����@��u��k���c��Rm������>r"��[FNt��*�{ �������4d��c#�F2M�@���99����N���]�p]�������
�|����l�=�l1�x8�a'��Q5�L�����`\s�'r������n�F��k�#�-�������W���A��@-GgB��I`5�c���&*������7
��7Q��)4����������`O`�
0�6*����A��y3��F��
�v�)+�8�f�<���wF���{U-�M> +�+2u��������Z&�{�<�i����8� ���3'B�iI���i�o-%�����VhuAV�
�)�R�E��$�S�;�0%��8�p�������)��������si\����������S��$:�!<�����B
��?��e2����^����"���WW8
����uTL�����]���n|���)[���-)����5�r�D^���Y���a'����F)����l��X�uhtlu$���n���G���t�\Zj-�lA�������L/�t��FR)���lF��-	�;���$S�o�R������>F�*0�z��|h��H)�Xkb�g��t0	h�If;��R�t�5XuaspZ��o����.&�!���]1q��dkbW�����jPhIi?��.rV7����w�x�M�M�w�#��~>I�N���8R �b"U6���&���{gu�:<�j[:nZbX��,�tD��I�_/%�n��a�c1g&6�:��#�#q��C�8�!���L,�h<�U�1�$�Z��@�����`�P=O��N4ifG����o����4�a~|�e#�8�����9h/s�Q��$�����@z~�@�� ���v��O�7�kA=��.YO8���b��
	���������KJ�n_o�(&�T��W)O=�Q�mm��n���~9X-�G�Rdi��`�Q�[Q_�G)����'���N��LS�f���!f�n\a��0�����~�m��{��x����5����m�N�`Vt�`odt��3�!&`����n|pz���iP���T�~HB�o|;J��Gn����d����9@ 
D^���(��Q��[���Q���>i�����D$�8����n����#��~��S������:�1$�G�Oj*�:w����-�}��4V^���Z���S���x@"��2��D����5I�s���_��F���l%C-�%��/�+�~�!�8K�[��
�
;���^��l.'���L\L��^�Zf���90��%�`����nur�����z�Q�+�BX�����H��p!c5��z����X
VE�
K\P�SuCQ&}!�C>�jh=��[�
&J:x�S0:	67����m�v���a>��/�Wt<�S����	)"N�I���3��Z{�(�VME ���u���a4w�VG��Z���o�����g�����#�0���9u�w �L�������Gk��~
����S\uH��v��W����N���&��)92=����`�u�mu�����������0�����9�I�?���NF'N;:��&q4z���z����}j����n���as�U��?\���'ra�FH��y��<@�Y-(����V�l�%+�p��$��r2Y����~]H��a���K<�a�q{!�"_�so��8B��!|7S)�"�k�
V13���h������X^��i���'5���1P�a����JL�oBh7�R� ���������������C;&�u�K���BJ1v���;\u�
�����N;�Q��R�����<���U���i�#���
��"z�n���G��=:�r��A a��y�~���)����7oo�_��`��ow\��A	7{_A. v�������{�gy��m��M�[��]�%�JF�o�`��������Owl���LnP|���x�h;������'����
n"�m(��Y�r�Iv��/�w���<�uh���-�g�P/����I�����y���?1����Y��:�����|���H|���P4 p��������{��j��x:M%���o.:oi��"���z2���$t(g�5:�&5Y���`��+��P.�.�&c��Yk��E�V���rY`����������1�^�M�|�����Jqs���t~*�X��S���&��O+�M�\�\���Sn��	~n��f���m.Z�6�@+!�61�������������%V���r���
r48�;B?oD���!��� ���!����� j�,C��7�7�b��-`���l���`|�C���=��s �
V�
��'O����P�I�$�EFV�.g�nO�7d�&�S�&��=wi��Y�� ������L��&ngwK����E�0H�ql��Re
	��W���R���a%+�@(�����7`�g�k-����%_�{���������Kx��8�L(�l'�3!��0�&���������0�li��b����*}W	��h�%gYV^9j�������4(�>��#���c�T/Tk��G��vYb����|	��7���+���0RB�JTGo�`������BxQhyi�y�[BH�G)6X���d��g��kr�(9h(����
���G�����SG=��}F����j�eM��������	F���P���nQ'v:�����H�D�x���I�=����6�ap
'���?����5dh�!������9$����K^��H�����0�.���y^�Ygb��M^6����'G���f�f����o�������>�n��������r}X���Ve2v�j3{�O�I���4s�^��3�Ib5�6�������a>��GJ�Ux�z��3w/�9=��;J��j������/v%���������fN{lRX���`�����&>�	����Z�����Y#;q�l�������m���K��|�Q��l�0WqoO��(���d��vc���0��~��<vn�R�.

e�����x���@v�M&��
����,P�UWw���"LH���K�q!��E#=L�p{Y<F�R�\7�n�8.�����T�g/���Hz��.�Rq<��Bg�7�t����E`�
=^�5 �����]�;����0l�������r�K<�\����}���,��T:��E
�dA%��&�j�.��4sx�TzGqJ�R���S��q�<V@t��)"�6�E$% �h'��y9�����������,�/���f��+�Y�o�!��&U��b��1�!\L���$�|�����1�����b5������%s��e���RV�tT����oh�r��S=��8/.���d�A-
���z&���nK!W�7��S9�bq����IsR�d3\�$���"��e�b��i���{�R�/{4���
��|���J4��'
/&�O�������r���~��0�%��D�7a����\C�,�@��m
$<�5�I��6��Z�
�h�)�j��p����brD7�f�������5|p���V��#T3v��z��%L9��C�b����/hM/�[30��g��hx����'���NQ���@��@!�Kv������������>�t��#���t��cSd��aHM����v*%�#�b�<���z��aBo�����I����}H��$�2�r���n�N��HK�������H2�\n��
2<B����X�J�����.���'<G`�������+|+��|�hWPK�F���M�\�
�8����"Ko�`z/b����JBM��";��i�i��48�K�kv��N��#�N�x^i�.���S�D��"�����H�jJjy�[P��j�d���haw����RJA�\�����pT�utw����G�*��m-?��r��H������)��m����X����;�7���q��"�i�.����
�;e�
=���b
�5B�c+/�+����C���"t+.	UM��������m\�����+��y�7X�s|[��h���Ch��<Y���94e��j�����!.
����JBYxr+�5�����rM
��Kg�7�g�����Y�L%�*60��x������lO)���].O��q����i7[���v��Ek_�!51V�������m�����a<b��	��/$4��Y�^�G�{���/�}A����y�[�K���L�K�
���4����������y������>��&l^1ro��s%`+�h	����H�����d�Y���#�gj��pzttTk;��u��Rm��ZT!�y3h%�6#��?��0,���,����A�A�=��Gs�e�D��$�J<�Y&������u2�Yz����RD�pd����}�U�;�e��`
@�-��oa���?�Rj��[k$��bm��qk���������2V�c~��^z�����?\u�����$�[�7[^�o�T��)����|�R���f�n�p��o_�=��B����~�|�f��s��F1������������,BsjLG*BM���z��D���4���a�w���C����D�k�I{���,�r�g�n]�G�j�3��A�����:���D16�E=ex��F�8��������Ar�]�q��Uv���v�5����U��jf��-Ya�*�P��{�2gLV�M:���n[:�w�6�#����DE����I���o�sm������i�H!�e����ij�\�%��bc����Z�Y,K�R��vZ�����X���'d#����J5
P8�@%��CK�( $���, v�;����
�I;���!��;]�� ��FDi�|���4�P@�"��gR���9�'2j��=��KW�q�#��x� ���8'�/a���/�v�"�'�����%0���s���e>W���z�,�CO�BqL��@`���;cK-g�n����J���9����	3�_�
;=��C���u����K����� ��/xQ�ODF5�lId���JL���-�V�;��w�' kLu�-@����`����MHtp�75��b�Hl0���z9 >�l���6c���jx!�`�LYI��<�������G$6�FwI�C�k{A�'G0I�������ng�����������9c���R=7���6����������a5%,F6��U�JC�t���+)��c��k�b�X��=�>�D�����N������p�87$N� ~���z��`-��
���
_�s������
�����:6r��h�E�8c�L&��,�f�������I�U��Fn��A�6l��)�BI%�=5iX��`)��~��O�����p��
$M�Q4vi�FB��%���
>1f�0>4�6����sI�&�Ep �8��K��
	WI,U%�#�
|B���4�!�i���2��Yv�+�f�q��i���6z����j��2ddX!{Pu
m]T����z�\T�ZU�9���
o����L*qDj������E��5����yT�o�k�l��!(}�Y@BP_�GHYWM��������}6Y������)�'��>��>���ty�w(p����BJ��?���3Xe
_��������������D������7�5z�Z�W�����PI@u���6���qb`?�o��A�
�U�/��2C�*��E��V!�������7#*�)�@���)�*.��/�0vvU���2YH���?��Dkx�m|��p�?��'���_�����W�
uO�]�4����Z����5� �l_�xQmT+��Y�����������a���j+_z�����S�
��nhX��U�Oa�j�:P
�g��6�T�`|B� �31��N��f���;�����H��-�H�F#������v���^���'2�"�+���B������mWJrF\|���gD����V�\,�����T*7��"O�HR��%��a��#���"-��^���������H�\/�x~��� 2�J��ua|O{����I%�t#�S��������0�o�v�����3%�T��$���e�l?yu�m�������������y�^���)�,�����2:��}2����T��r�euny4l����
��<������]�q�;G���e���SD��b�Z��D����i�����{��3��(�����a�~Y��M����L���3�u���W)V���������[�
�����K�R��}������#�u�h�z����h�8B�'8��G�@[\�1ef%���f�l�_
��U+5����h������?�p�CU��������e>�E���MU�N���������G%��r��V�Z���viX���
8�^���s��TzI�W�=$�Ru�+��;|r�������mTtV?��9�^}TK�?�s���J�����rY���^��%�T�����T?.WF����lI�8��:��Ri`���frl������w��vpz�������+���ut�#����C���q{F��>��,�l}W����W�`P�={��j��2Y#�El��Q�8�]��K����@��8��XM�����Q��������%X���7Bd�
�RMC��W}�SL����z	?�|y�����!KH�P�����z�#~-q�,�y����*v{}�K_��86�jd��qSoD���g���'�����o����_��~�g��r=_����#�e	��Ys�B �(����~ (�Ku�Z-����|q[t����i��.f'�����e�y�^��_Z�^�U���\���o?.����[�������;�X������i�D{<�����H�����!�/�3�*�������e�����fH�I�ZPNv�*"V'�y���3��miJ�/���|<�_#m����M����~�2pL�.:QFs`S���"2<v���b��I��U���E��h�������d������I��U�������*S�=��ee�HU��W*
�@�QM������=^���M�v�-��s���cYX:��1{[��Rgm*QZJ���h���Z�q�Eg����m��� �R��BJM�H(���t�j{��L�A�������
�2�
l��=T���WZ�h������s�[�ks�-^�No6��W��xYT����d������}n%�.�S��w�1O������������V���AGw�������PQ�z&������b�8Y�n��F�N����99I�����������z�e
��l7�������a={�e#�;�ow9����s��f�#��C�c-:��"���/�EJ;��?�	jS����NiLk@����$�������u	�H	=49�&�l�B�`��
<����<S���������5�{��hw��aW�4�7>���:�2YM�'�����me�N�K�����]�&�`�����������Nw�2�2u�3�z��y��'<�~Y�����U���[�L�%�e$<�9�q^D�d��K��s�N�3!�b�>au]6q]�^*$�V(�9�$~�3��aj�y�3F�_�E�����Z�E�b':Lo����+A����Q�c���wN�I��;T4���*�r���Ji\+���h�����+�����0�o|C�$\�����)7c@�������}�}��d���GP|�t����o����J���Z5�u*N�TmO�s�](>�Vn������2�atu�W+(���5'RTt�(�x�������d����JW�����p���C,�%�V��Ks@�HL&�%�2[!V0�C������{:��2F�A��=xG$(������ ������Z��������R�C	~0��06���/U��@(uT>�H�QG�l<�KGu��e�aF������������I��f9��3S?2��^n�q�]�E/t���u�P�
�{	:)�{�4<Rh�y�������%?����H[�)��7�V�3FE��3��C�����7���~O�b�7��e���w�(���%� �9�l���[^�A��5�m�����W���s����7K�~l���x!Iv�{�,|�:y8�DT�:,^���bw(�i��c!�j��[KL��J��*��w^���z�C���$tyY�*�:/`���&�"x�I�����R�;�)����U��(G:Z�b����s�d����_��?�d��2����Z@	�d��J��r�T������pNcIr��b�������PHx~��C�s7��
��M�B�/F<t�3Q.����8������0b"��:ln��5��i1�$�J?�-��;w�����T���u����Ak��d25d�����k�������V�c��F�P������		��|���B���H�
���y�Z�����f��.�@��0�L�o���>�C��*�4����������Q���1�$��5y:;^�4�� }&R�����_�p����b$<V3[���F�gHNzX=*����$4d8. �m$�U��hq�qU^��J<��I�F���d���7���sI��"K1\�_D��3P����2��<��t&�3K����s#Qa��AR�h��Axm�c[f�W:A>ZK4��<�]�J�+f���g�u�������{���iw�/;W���z�L}�Z����B��D��h0L�����+��c{���z��Xce����Rz�b$
}X�pq��_T���������}���O�y������m&�*n�l�'n��U����z�}�#��Z���ir�s�����VRb5�����a�{��S����V� L%�&a�q�q�����)��S�<#�?q7�g������?Q�:5�`i�
	�Y����x,!�~��I���^�3:��CA�Tf!�����z��$�r�rSG���M p����L���l$I=�N�N��>l������xF��*I�x<��Y������3xM������]����(��C
����k^P/	~���k��M�	y����!8=�r��w&+z�F<e�el����a��Wg�|$:��s}{m���c��(laST�yB[c8B���&�6�m�y[v�~~��VtU!I��������
����D��F0}Iq,�Wf���`������U��s��6V�,��3�������������1N]�%�$op��Z��&�B-������!�	�c���v�Y�����o?O��WZ){'F�	�-�c2�|�Z�
xu�A7�Y}�t��n��X'M��C���
m���+Q{��}��������s,��������g�����Na�%*��4#tA'�G��n�c�R�����V�P��<���X
�}��:?-�}��%p$�
bdO�q�(�5_!f\���`�b�0�'�����!����0}���@%�L� ���T`+`��l�I�	����#��F�����%������!@	p����$�#
�1�3�BX�0��}("^��N%�5l'���8��_�;����.����Q��l���y���^�-1���]����l���E�����Hr[e8Z�6_��+f���hs�a��6J��X,7K�R��.�x�l��!�N$_�6I:�O����k�����Y��J������N��"�pky^�l�������t6xg� u���F���K�R�1j���x��G��&�����D�k�P��mb���x6���$�!�a�����QU�qhWd�,��Y�SE��Z��p/M���_�������$�����_8Mj1�Th��ZHrK
�	���pc�	n��Q�}��FFZw�XJ�@��'�f��m)�wd2��p�T���xR*�3�6����]�x�����/��B�
LT��^i{�$-A���w��J�������sw�=��V�'uF�	y*pO�^���j�J�R�����\���j�r6�L� +
cT��~~����*���V��_����/H�b�VP��Y`' �p�@ p���+���]y������
�}e�ld��v.���(��i��[jf,����*��k�n�Y��b_4�P���n!j�������#�[_)o��N���������c3�����
�PH(�,�t��[�������
�M��CQ��4X��,�����]K��C�F���0�K������A��
��;c7�G�f2��6�������-�����L�8����$�8Z�����:P����2VAt+
Q��&(�y�#&m�<<i����Em)���%����I+�!iK�3z���@�^,��l�Nb�; �bp�:RmF��NH������+m��6�����k��D]��+�q"D��Ubo{�������t�m�y�!������a�*I,��B��su�	����-Q�
���x�'��\BO�5���������z���[��w���p���*p"}�,���I�V��iWG����Ph1b��"I���8��C�8pw���u�R���=P���?��J_����Z�
	*���RNu�����<9x[+�ng����/�����B��������j�'!��2���>I�U���4���|��#:{
�b���
�
_�l�&�\���_�������%�?G�{�C7z����qc$���?9����:��|�k�?�p����?��Z��?���1r���xR*�F��;C�Wj�������0N�u������ ��>s[�([j)�bKEl����i���3��j��/_4�����P��h�m!������H�]����w���~a!P��$�2`R�y�/��:�@K U����>���S�?� @S:Ad�mD-(�<!J�h�FV������D���������y~Q�r#)"��r��d�lg5����2����������6N�A�$5�z~�nz���K�l��H�Zq����[9���
��HA ��Hp���#8/��� p`Z?�_�D�6eY�q2bj3RM��zq�TK@N�7!N�P�3`�,z(������Zd�;�
Y��Y�wn��i<��#����s�&����d�8��[�Q�U���|'5<0��rg���\[d�qoV�|@n�\���;%��d�<�p��6��%G
�&�)��:����=�W��U�+�,�':R��=�
�<�����7�����Y.��X�|q��� ��;�_bUH���_��U��
|6
u��S[qI���F������,o����9���h?�}"��sx�Cv�$��cmV����=�8��p7�\���L�#"ikp�p�4�F�������HX��s�Cd'&<X/�U%��t(]5�R:1�#���\����n(N�un���L���aw���;"q�-��/��H�f�.��9���Tc�L����������c
(v�6����W"��qa�9�^�����{��/�DN��WHhK_�j�h������^�����?�xd�G���Xm+�w���"��9���5�g������Mi�CH�t�p�\�b��a1��2x����Z��P(�4�^E�����~`��O�@����/�>���eDE=
e�B}G������[�MN�$Z��w7�(/tY�W%����������*���F����[�|@J����Z� K\{JOm��5E��tz�Rnq�@��P�?_�WbYhu]�������p�^I�����IR��@�5����vNV��.�_��;T����{�#W��U<��|Q�����tk*������5�)�G��Z�d���`r]E��G�����P���\����K^^�k����7Y#���=�c j�+d�!�:�,B��%�S���G��XRi���?��=^&�[-�`��jBJ����f�C_�e��
��@l��b`5��.��9�,����2N&H$L������	�v��F�q�����8t��<�"���5�%�.�>:�J���v�,��;C6���~��b��0��4O[.x��`�n'��`��g��$���+s(��p�����L�k��@�������J�������F�"�N7��(�����X��/UQ���i��{�@�����5���������{4>��\"��=j�:�v�^���{$o'c����<�����-�9��e���/ �H�*?NV�"���g4gd�p��R:�?�^���xuD�>3h��/�{tI<8��G_�J�1R���`a]�<,����'�hh��.�x�����,��P���c���E&"��\���h-�Q�`?��<���-�vS'4��X������z x���\�4���F�\��O�
��W��*���:pM����t����F������x���p��3�\�h����1�(�&�r��������@�Q��!����������k������N��~��e�7�W,�n8I5�a���!���1���G�
`?���[��\����%6�DC/Bsk���'
j����N�f1�����{Q��2O]M�
���#�r������F��6/�(��=��d����\��j��i��q���7}���`�F�+SU�����,�*��:�<��X���]���&C3���P���L7P����j�c���nL�]���m(��\���p��>uF�����~��+g���u�a��#P�L��$K�lE�13Y3dNS=i��c��N=E�^*���y��U"$
���]a���t�o��E��������5���_����4�w"n:ESh:���/istW��f��E��"��A�o��'*N���H����Kt�����G�E���Q���o�S�a������hU�8eO�'^k�4�!(*
p�Iwl�N,��
��V�#����K$,����sk�1l�Vk~�D���N�������a��
�6D�B�*�m_;���C�����j�����H��d�Y���	�nnQ��
I�x�:;�t�J�/N���O���?hm�����MC�����
lf"v�j��F�d�/������/W������+�(�?����Wi!CO>��'��Y���(���F����-��YAh��dLG��}���=��T��aO77�7(�+�+��x���?�l){yD�`)����x�5��W}������	&���)<�f3�!rCT��[�v�+��ubd�':!�K�-.9\�ZA�~��M�vpq}���cX�����3:-�QCA��3����=�2��1-QR�I�%�u�h�z��'�J
��c��\��4�(H�09IJ��LH��]�l���v�hD^�]o��X�Dz[����x�	�q2�D��
���)���,cLP�z��L`A}]s��� �0-��
��P����|t
�N�/CN����^Z*
�>��=�GQuQ.��
D������p��T/���v�#�&l�;l���<�t��s����d;?����5�I��;�����}48�������bT����:z�����K���D�uu0��K\�#�lfd���2�_KYu��V#��I�p0Yx�3'Yl�E
w��B������������JC��7�W��V�X�����b��u5t�f;�
����!�k���)y=X����h$]���+V�2�T#���	�������J��h-BMy6�${��9W����[{bL�FCj�D	;�E"����n����Ovb�3HI.�������jlV[���R��v,���D�cT���;TTK�F�<!0�(���c����CF�TS��:����.,��)��w�x{����3�C�e
�1�����Wc�4�%�C1��W0l��M	p�v��>@
"R�@�Z
F�"EMQ�#0L�
��(�P��x�Z�Pm-�H}����s�����*��8B����4k�/����6V���d�fc��u���������x��A9�o�L[��NX�b��7���j	D26j�U��%���=�Y&P��/�;����'�P�4.���C��J�4>K�����x���Zh������1�������2��[�^�BE4��i�
����.�X�'
���������� �_m�Q�
[r$�5��.�:�3����k���O�R�s��:�����A�>����4����[��W���aR`�I��T/��,�%M��h��$�5v ��^���X-���������0�TgL��(�[�����1��F�c�������'^Xf��CI�e.�oK<{3g�M�Io��T1&��s�I:i�	��dK
IHZG��������+2	������M���R�N^?2�(-ckI��������2��!K���'���k�'�����dFY�@\@��WJ6P@��mi$��i�1��f��bc�q?��I�|��
�b�[�I��������8�c,��g�_�!��#v�+��<��V�Tf�\=��v�����������cxm����-�8Rv��0�k�����:��7�+��,�Yq%^P`	HuG'ot���=2�������t)+������&���
pd�D��K[�Y���t��g�
�/��J.]�I�'kv��%� ����7j\4�_O`���57Y�q��6�vB��Ig�"IS��*>7#��OS�#��0����	��"������ 6�,$*(	��x����z��'�F�j��(��^�Y]?l�#�9��b��HZ��)���\��B��Vl��U�H,�'������$�=�]�9�'��^PBk%��{G��b��!�A��	tmJ���ui��6Mi�a����a��M1G��vz�o����s���4�4�:n9�������n%n���V�`$�C�~�cps�/���c5��	�5i$���v������3�Q���dEC��?��Z)��{Ke����7��������X1�z�r�n���L��3�6���a}�y+�M���	D0Hc�����y�:��g����o)F��R/�-W�����k�s����
�Qx�B�z�gR�%�����a�``7)L���B���v���$&��I��������Pe�h1���M����)�u��p4	<����N?[�2�-$����'>������N�/���xF�����vU�*��.<�o�5e�r��?[��� +����=[��:#>Z,��Y*�*�V��*K�������4��m�6Df�'%H�J�L�����\o�(�W��E��"?J��z�QK�c��t�Z�7w���zs��
���/�O�H������D3��,f�j���%�p����d^����1�p��&�����z;�%�s?oG�sz������omy��C��#:���'>���-r��j^��w��s��^���Rj�"�B�y.2�+�[�$d��c=@��O��{t�����k+��M�2R�Sd�������Y����_�N-��_����Z�9�ak�n�:5���	��"}l�#Ml��j���Y�����G
�x��`�Z*U��`��V�K�s������G�k�H����)�;`�ly
Z���yg�Oz���p�:�_�l�I�����"��O�|8��2�<P	����PH�
���7{�����`w?�mE%��>��Y		O�kO��^�|������]�Y
�n�y�	<O���{����k�.����7��������Cy2h�G4jZ���G��w���i�}�Q�mB+z�����,����I��l��~u�<��x
�\�����E	�n��Y�|��Fa�n������v�uV��tC`xX^���������Y�}�{��+����n\�B1�E�#
&��}��u��Uh#y���R^��/L�g�"]�����^������l��U!p���0'�~l^����|�A��D�L�8�
_yz�����Q�T��I{�����������>v?���[xZ�z��?g���I�����J�f@�D
����k��W@�h�U]t��z%�x��z��>�
����5JiW�K���sv�������0���Cs�v���I�����g��h��aG�������C�:W����V��eo�!
����Oc���y��"�c&$����K�r\�|�-���\-B����9����79kb)��	��Og\�~�6�����e#{5��dQ/���h��wa�����U�po��v+���_�xoq��w���������}H�qQP��J
a����rD����u�]���gv~�x������Q�k3��Wmc�~� ��E����[/Q��<�,�&���|�����xh��x�q�(�.�_���+�H1(A��m��c:[��C*�*VVe�S?��<o[���&����3�1��@�		���F�^[�rr9�
�H���c,~� �\v������1���x�R@���;Xp6��+Z+��D-P�"V�����hw��w�i�^^����'bN�m^�[5�Q����3XFx�#7��]��r7���P_iyVm�b����d�%"[��#�r6^�h����c�;�d�&a5��kB���{�0b������6�l{�}���V���W�L<�����2�_K��wZ���0 ��e�er9#{�W�,����xZ�mev���W)�cwMI9�bs
���G��c�C�s+��V��D��b���B�?(��v��c�u�����Fc<��.��s,N-�8�]^'�,�6�<:*�l��U�Mz���EB!�LZ��[�������"~�O�"�����4���)�R����
�U&_��v-/���i�P���j��� Ql���]OU�[���d�f<���`�3j���9�����z�����A�}�0�9/��l�V:�|��&���hs`�0���������U�^�0��.�U�kV��R���V���x�)Q�t��[�S��{�$��*���(���j��q&^�����q�bQ
���2�����d�]
wD� P�����D/������}����-��
?�>�.�F�l6������#���$�h���B��.�����O�����?`@To;��Ue�T�*��C���j3�mT��(�cIeR#���J��q�4)�M�_�m�ek3�������aO�e�nd���u("2�#�_��K!������� Pa�DU,�B�S�v�L�����S���_9V$+,t`�I���Lql����y�C��y���L�'E��������e�+�-'>�������>F�1
�-y�����Vp8���,�Q��6��Y� �ob_�f��xm������o�#��4�,�J��|����m5�x���]�TBoL��2��|����|����I�kx�P��o�����Qs+��'�I�En�g�y8���\��:���#���H1��:;�
�a[o��,*���1b���mg�a��T��X�C��c��$�Iv���X!�G
fjO8ri��=�Il(g08�d��*�
�\w
5~��0��,�K����"��D�~��wpSpN���R������7�Q_����'"W����������9I�#KLr��c&.���t�n�hs���Or�Q�sA6B"N8�s�`�7��JF`��,{����@m6,�+�(�"�D?�g��5����
u��F!�6L���~��-qW�
@�V�^Y�z	��r@�f�4��ca��������^���7KZz�'<b�2�vz�.�Q<��*��v�u+�=)�?�[<j���&�6P��,!����rUb�MX�?����V6?��B	1���>��0��G:���G��������yC1�s����x#��������'���'�Xl���OQ�{�J��\2���'q%<M"�G�_E��.A���b'K�Q7�=D���DyM \�����r���1���D�7�jg��]	�.|������M�_v	@�
��xH%�2il�r�O�Ju���v,��KU)�����R@�z�E��*I����q
�Dt��r�����}6�t�����I�������V��j1)�$(�
"���\���!{��!�.U�5���0��	�)qm����q)X�������	W;�����AQ��8.0}#��4_2��V��|r��'����T>o8�
G����tv�����@���[�SN�-����X����Y�vV���!>1_h�>�0E��>v�����--p!���n�R��kc��/DT�&����1d�]p���J���j4��X�k<%���z�s��l	N�3���xo�MG#���w�G#�����D���H �C�+��!���M��#�c�_���X���R�f��z�Y���|\��K���Shi��t2fMjg�J�6���X9P�
��S��*�C�$4������zg�)��Z|�!�{{9������=G�S��p����f<�-�u�#�Y-TP��������kO���zC���*�b���,l�����?_!�J1?O��1p@8����pi�0|��_�=��� ��v1�]�
C�����6�7Sr9\�[mzi���R?�J��m��0�F$M&�<�y��*�1f������F��L������b1L����aP�"�������sL>z�`]Uz� �c3\�<�
�^�>��`�6:��-���Fx�������`�t2�����'�>�{����\dJ�k*���
�XW�n�h��3�,ppH��h�1/�0`���: P���OW�������O�����F��,�$�Xj&�F�_��z�^�����%)��a0b([��@+7>2�b�)[�5���k����+��^��z�s~y�|�l��m
#!��B���Ya�X�fL�e�po�GC(����!��BhSF"����vi5���v���^^m=�2}��W�����
g������7�s�G���n�pR��bj�2�h�>M'��HR0�7o�p/�2y��]:�f���im�m���:����=��]��t�+g.��x���)���rx���Q0����8x�fc�V#����L��� �'s���J6�F���	VV��B�I�����vA`��L'���������.Y�8��S@�J���1��3���[��.I[�  �Y�bCt-�~���=i� �&��u�rymQ�"���l
�T���W3���bYw��8��0L�S����)b%����W2����,�?�������S���)	{�m^$`�_�d���m�?�?�?6M�o�l��] �6���u#� JwNw�81_B��4������<%�X'�^J��J����E�PPdf��Ly���dk�*-�������"Y�����X����n�jq�XW���@%)�����������=��"[����jW"�D�J�^%"�R�
���V���������O�6�f�6�,[���>!��t����u���P�D�|���$7����F���I�
b�W(�B�4~��;���
�V�����6��C����9�SB�qH����au�b�u��B�R��P+�[o�V����Z�[�'2��L����ls'��EO���r��L�E��fQG���I<r^�x5�z����!�������L#E� �u�01�`�,�l*��R�D�gZ�g��zI����Yu�..�������5��0���a�pz���y
^'�}-'b1\�����ni�K	F�I��-�m]L�#���������%����kS��f"���R�0Ba{��T�����x���=���h?���Y��Gz����iTL��Dp�H�����|�$�;��t���U4Y�(}����4�����w=�4���`K����DH>����o�-��Ei�y�V�&w>:����\i]t�:X
9$�B�.�N|�@��n,s#�=�1+�Y��n�����?��4�mza�d�����J���gZ��dt���)~V�
��G��a��W�m�\hUbyV������,���{ Zq0� ����g@�(�����n6 z�����-1���5NgR��=����cR��/�����b��S }�rVeta��XMA���[�o+��ch��	���j��s^���<�������v}Z�_R�6�v���nJb{�������� �[����D����z#�����n�
��\�;�r���D�7a����!�����Y�E����o[Q�QOl����=F&�}aQV��*�Qg�U�0��_����0����r���"a �7�>���Q\Ky�:�fa>�q)�V+���~���K�
'�d�r��9��l�$�TzX;����j��Ri�T������7��f�E�`O��~�
"`������	{s\����e���/D��a��r��d���	l���EJ��w�p��=H&����'����7�~�h��{PI�R��������s�[)��{&���'�<�Ho�xe�IW��|������.sL����=�����_:���u����	N|1O����l�����k�����	�CQ*REm�R��E��IGd���bg�����a�����e�"�S�\|0�}�.sS�G	�s���P�����a�?��)�}��O�!J��w���\��7:���y;S���
~�s�@��T�<e����+c�����#��"71�|q�����d ��aV�7d��A����f?1�a:%���zW6�%-���9N��D���c��XQ���y;X�z� ��u	���\���U�L ���1e��.����������WT2!;@���S�?b������|�����D����x��?ab������	�%3-J�������x�5Gwmj@���?N8��@tu������L��3?�p���Wc��c��Wq������>�$6Y��(P�4�\;P�L�CY:f�>���K�Tr�	=�u����%��"��F��5�%n����OBg`;��	�K�|
������������Q�Cw�v,!�cLb���!�`�;�Yp U�B�� $�W�`T�FC.:�8U2%�jc���%R4��r8�^z�����T��l�v�Ix;����s��$�<���^�t�����2��P|Wm�X���|����}	��{`��
�QV&W�jh�L�^F���a3����D��J�t)���Y�K=�+���=`+���@��k4�).�hx3�|���f��ohHOZ�_���?��b�4�XZ�Cv�g��������-5�		��SfEG�2,R����[=x�����#���6�����n�J�F�������Nq�)������1�B�-�����w�V��e)l�����R|#C�������8�y��e��FBk���,?�x�CAn'�)��+�t��]���3G$a��I'���6�v�V�f�0�1�����&�1i������<�����/Hap6�,�K������nt��v�u�9��q�}����e�%���{�y���m��x����}��{�O����|����c	�>w�9�&�r�2�-p
9b`&TC�����y���{���s��p
�H6�o=U��YQS��"8��+����%�n�IX-x5F����V���Wy�$C�`I)�.��'��L��
vG�iH]�Bo2����
Z]�n+�W�����I�&< �j�px�f�}1���x>sfV��IUgzq��.*�����
J���.($�Xq�����������_�j:�r�>R�a�?�6*G����P�!x�~���	G��A�R�
_8�\�����)�=@O��^���|0���rc���9��C��;|Ts���#�<����$���E-o �)�IE��q�C (i���-�X��~��l�	��t���-*QW���z���I�VL�v��g1�`{+"��|����\�b�*xi�������J��lmu���3L]oH��oml������R1���D���������*P��)0z{��"��	���p���Au Z5�d��B��:6�6$1q������:f�	v,+�4+�!"l�p���laEr�7Yg.Q��^J��ZEM	
3�IZ�����	��YD �/r���<G�-�H:����vv�+��$�
y��g�S�T�w^F&�����n�+��(DHu"�\H�w��L����9�VeX���n�>S���KL�9v
�D�I+BBj�~������������A�!�{9�>��s���������$�D�>�Z�����]��*�C��y��+5�o���R�N���rabx�b�
9@!|X�f�p(�3�\�&���c d#��.�2W�;p�8���l�a��Ao����X���R�_'�HVV����g%r8�`�H�f����0����cz��d��,a�<;O�v}��9�6�K�o��;qs�6]�'{�joi�mSp�bE;���F
� �����
c�&����H8.db�o�r�����_���#�S�_�c1>���������!�|J�
Ok\fm��c�M����E�?�]��P[9�A�w@��H�1
��^:~lD��e0�@����f��4��Yl�"V�m:�4T�����R�$�2 Mz�A��S��oy����N���9n�f�'K����� �N���y>y�L[��^�vc[Y�n������������G,��]�+�/��@��*4'N�_�[����;K�R�1���4���#��Dt^��GC%!��
�d�&&Z�S��'g��S���oC%Q�lw}&��($��7*z���N%	I�9��ryI���"�w�	��a-��DN
��7zh�l�X���Z#�i
m����e��G��\W���Wh���5iV�t�v��t�(�����d� �2"#(A��.���.�mU�z��:��������c��tqn9��(3���w���5V��^���1��k�P��\"� ��"�g�x*�~p��J�%��|LZ�����5�F��h�a��7���];��P�jT���������Zg��Lc���q�q�-�
�#i��
����m�m(���9�x��V���Z���)65�8�����r��c�b����z
����X'p�����,4�K��=����O>��N�p��d����nZ���$�-r����2�/t�!�Q�b�gi,`K-��������+��-^�s����^�(�{�"�DWf)Z2^�t�D�����8�_	0s��X3Ww4ar�(O���}%���\X��R�����%�g��$BnN�p��E�^?D�+�qC�VP���4����2�L:��9��MN�����^a�Ig�g1A�Z]��5&(�1<���xT}�JB+��p+#��/k�G3����}��.M	ej`���2c33M�����+�8�V�!v�&P���=���t������+[�3'��a���>���8��p�X�H`1]�~/nT���r
�7)rj�^�&Lu[��h%Y�V��*�:�mf���g�w���k���@
�`�P2�;�|G�5>���d|�IC����FE���E�Tf�����76��B��+���K:�C����[����TOq9�C�a4"m�����e0^%a����-���1�Ci`	:,��L��:��5D�>���8?�`u\�#��Q+p���r�W����H���ld/����Xk��3qG�?���q�� ��
gI�Q��JM	�P.���0�K�~��&I���
�o|���H��	�"()�HiA���k]�T�v9p����oCB�9P�x��.�KJz2�U-������%q���#�o�=A`B7bh�6���u�1	T������8�[0))N�9(h����_")<�e1eK�Y�9�l`VEy��[��S�
0�>�<�����$M�\����V%j���J�;m�VEK��>�2��d�`\9�����R�LV�g�IFLge��n�l�:�����j�Q���H����f�����E�y���7� j�������=Q�G��3F���gl��������	��}f_
j�"e��,9��m4���Q��|��z���8����N��iE�(������u�f����5��vj^b��X�~���E��u�Z�#	�����CW3r���k�%�9�]�7~!V���KS�%������
��t���yk��[k�.��"����
�e�V�AGs��
 ������A���c�	F�X�g���'�����[���S��}\V�������CFInX�c����Bh<$���4:�!J�DS��)��3�z������4GN+���-�ogR�E�&�������81+n
Hj�~m��xHzt��>��v!�U�7�$�_�5�w������;�mzO�z��q�!��Q����u-;����&b�=���^���o��_{f1;j������*��\�XyPk����EL����@�3(I�k�}�5��E�a���]��$9;m����I�N�s�lr(N�$�����
6j��k�
2R�h!#<���1�����D�Nb6���2�@VpoQ���b����r�D��Jl��-��X�XQ��n���DQ4�/�;����>9�O?)����;�����{9�&n�(+���N
@H�I����BF�U=��������<7qyy���f7���8�1�"�`���������7(����A���{~�gUI�fU*�>���V�^�?�����S�}��@A�fop&�j�p�C�w��#�|#��i� �`0{�F�x�I���Y��C?1���X��^l�
��~�
3�w
`wNPW1A�(R���`�b�
�#="�MT�JH��F#�"~� 7�e�����6F3��
_t
k�T�������T�
�����;$����T�AL=�": &n��Cb��3s���&�A(R.���:N����'�O^������T
�h������I��0P@M�y���D��2��aaj��%e���\XL|m�{!�6���e�$)Zj���M����VL�2�x���"�u'���!3�n.yi��BH�k�K������+v������dSp�C�?(��~%ne56Njy��3��(����l�$pQf�.��m�3���B`�MF�j:gd<a���=��y����b�\�<����M���;���3�~?�.gkJ�-��F�F���dH���{|r��`:�}�n���N(w
*cw���v�e��F�?-\+�CfmD�����d1l�\+�}B�A�f_JD<���L
�R�e�X��(�:�)-<���
�o���=��[��	~�i."z�����mEE�������B�����'�W/�,'�����[8W�bP��c�������k4�]6L��iO�f�B�'s2���� �@bEk��}1o.�
��*<n��	�Wk[�.G+�;~Bk��[CR�Z,Eq�O��U�A�����4�N�(��hR��e��	c��M�2��������������� ��b��-�NB���F<��f�h�����?a����!������n]i������fN�j�q�"�[)�-�
;{��c.&T�<�c �BD�#�h����OWN)f���������6�V��� M��]���c������ ����Os��<N��"�Gk���\9jF�bDB5s}��n/����������+��(�b� ���Ml��L�u�Oi����4����w�*>	�p9�E��L5��>
��-�i@�������u1��X�rO�B��<�5�R�|9�).a	7���I�j���;����A��+��G�Gq�FE�y��(G���#/�+D����F���*%�.�D)B�w��'�tDQeD?��vqy�5/�,������N.�\��t�!&�����AM���s����9+��������Rd����l��O�����Bb���~����	�)�0���1��%�2��g��o�L���e,c} ��ht����&�PFG��0���Qw��o!����k7�'�8cS�����nJ����$��Y�B������ �-����T9�=����Mi�h��D��V�G��N+�H�3�^������P���1�W@M��4Fx����/S�%F������R�k]�'��w��������m��(jY���^��j���3i�Xc�������n;�Q����������z;�R@��dD�Y��\D�L�ztz:�"��WR�&��4F�[���AROH�d�('�����#�m��~��n�������
dGR��,x�����g"�!�t������V�,e�fG%]�HEw�z�r'G���S��U��!g��9m���Ucy��9#���Q�3j�tS��Y��5Y(5Y(��Pj�By��}�-%�=LM|�DI ����Et;���;4?��|^
���
��zV�y�=���e�a�g���4+��iJ�(�+�<pg��m���S>)M�1xcX�"��|����j�V)U�����?�?8���+���/���A�C�S�W@���H����YO5�^���J3��]UG��i�V�*�_�9��*{^e���j�^��[����+�!��	M�Wv�?�c�{������y�jc��vPz{�������������?��{������+ �O'pJ�y���+��Ua:���W^}������n�?����������z%�f��G(�q�����ZN<�<�|��r����{������7 �W��W��������J�W�C	����H��r�)�4a�J[[����������m��x8�s��#��_��I�L�;�_����
t���W��OJ�0�k��5�W������!AK"����O��c���'���n,��t4H���6�j��#��-z�@"`0���!j�e	<�c����MT*��;�!�em�&�`��
����T_�)���`6��1�]p�-,F�^��h�!J���G"B�0K���v:�J�[���M}�	�a���U�y�|b�������rBZ��3\b�^$vM/9�"h��h��W-C�����&h�>2����X�8��P�������b9vc9:�QYq��z�W���j�d8RdO�Y�������\���v=�FO�p�e��/�6l�VB<�
(��iJ9�������lc��^5�V��r/!��&%Z��6HM�?�(����y�r�K��Ec0%����?}Z�d�@y5n�����*�x4��S��X�����7-:���� L&�!������,�����_#���l�>� [����;�>�r��~�C��]�<F���Og��;���;W�o
�l�������$�������1-����jqMW{k}~m�O[�W��f+��>l��imw��.MG;�-��H���4��?*T��P�Y2h'3p�	����+"���������0G3�9� ��S?�>��?'���l�V���Hn�eT��=��do(�*������|��y)(���:����#5g)����x���:��0X<���kqN*C]�q=DE�F�<SP_�%�P�a������%L!}08��*!�����:����G��D���Q�+����Q���K�~�\��A�W[��#e���H:R��H�R&���Cw�e�)QtW$Q�w�/t�0��M
���i��f4QzK���2�����A���R-�������_�U��nb��s���fz�F:�%>X��F�Wx{4����E��<�7�n�D,�Ii�Q�s��<����s~oL&�Js21�����L
|�����	�A#������P���]�Ff�rP>,U�*�R�?������� }V����Gr:��C���w����nK�)"B��b�-�(v'�&����4��0-�fK�j��s@b>��E���:,g����$�*I�{1�e���<�
�q�zf��p�~0�sm:O���n0RC��W�TI�����9
'�Y�[���������*�&&[�����K+����8��KX��"�9}�����>Ur3vzx{��poR{8�������}9���G389FZ�!c���`%�������E����N�6���'�I1���7��w6�.�le���#��z����x���u��_,���d�vfnH��a�]�c��-��pF:i�N������u�(���dW���]�|~y�@��xf��#�le�U����n���J�����x�{���0`��Z��z��!w'�������
�^�p���s���������d:�i�y"��N�R��S=�?
G�M��hdRYW-5)�b���.9!�I�#���3��H�T8���@����p*R���&��y��t��=�#�h���	��D@
���c��QD�#FW$������5C���n���^=Y2��3�L�mx�O$I{1�
�7J-��d2����V��7n���N�`5eRX�7�����3���rY������$/��'�~��r��/�o$�yzy��p�r�P��>iH�m���U]]6/�;��>������r��a�g��-�7�
�]��!)�A�����1�	�\�r&��8|%�����q���jx]\���-��a(.�!���=,%�+���k�{FK8����o�a8�Gb.����x��F������PK�`��}�B
�E�b+)��IpI�)F��>��F�kBY�hE��"��E��t]m�Z#^ e6������!��(�m�y	���Z������C�����%�@��z�����[���Q��p�M��
������}��M$�
W��EstM�Nw0�G��k�����&0�:�{m`���e
�X���%3>QV&Y(�0������=A�d�s�W��o�����t���m�rJ$��}�e��S�0�?�JA8��3X��>~� ���2<��3<bH8�8��G���u5��9�}��}��pr�-�F	�*��)X#S�J�&%��>�P
:JD�B�T���q�/��=O_��v%���|RG�I�����Q�Usgb(7�pc��)�a�������]����[��%���w �7�(��z�N5�%	Gf8����IV3����/(r��������JDC��2F���i�O�-HR��"e��� ����0����*$�������p9T����o{a����2��8:	�����6���y<mUS�	-�G���=�QR���<�P���.���t<�������8���oV��#��r��,��ztJ��|p�	����@%#����r��*��N�Lt�e�J���I��;C���+�)�A�d�h���)� ��H�zm�0 E��\�ic�hx-�	S������w����7/�#�H���u�FN�I�*�0e�l
�AT�`p����^�+�����o%:��B�~���x�D���d���K�N�hy�������-��kP�!��Q�[��(�5I��UL�o�o����zZP�
��08�Tv����������|���H� �X�r��Ox����'��Y��8�|�:�<�G�� ���=,*aO��?�$�����h,';����7��9����6��0��,��X]9%�p0���)�S��P�a$se��0&pT���p �&v�0�r)G�Qe1*y�N�x�bg�{6���Q�1���H����JItNG�B��n}4�l7�j����u�j\��Z�i �w 8BS������t���~D�?*m����wFvI-��A�UMQ�nt�t$R]��1_ ic:4A(�q���(�����B�����\������Q��H)�����R]��K����ZE��F[��D�nL�H[����%��|��e�aH��JQ��a~��=�=���m��7���P����CD)I�(D:T��	���������&��D��(��
��S���I�|S������t
n`lf#
)�g)�p&�A������g�0�1@����F�2�F��T�g}�\��rNIM!B5�&��.���P,!Yl
=�����s&�Mt�)�������e�24��N��N�z��X���Fe
j�0r
]������
,�������� |E��O�s��6�L�a�io��L����)Y����`~��BQ{BwX#�8��rh���[
�6� )�V��p������YK��<?o�6y�����������;-����|�F��z
���z<���z��o.���y�m�������C�\������g����1oC?\��y�����������=�s+�m�i���d~
O�����Z�����pk�O�J{�VlZ����f����!��5������iW�>���Z���1f�2!�Udo�lo����1���%<����jq{��v��~�:�`,.������(��;�[�;���]L��gyu(�W�1�/+���S��r��5��K�Kl��i�Q[���H�	������%3�^�.�B�}9�	��0���N��d���aDN�z>YQ�X����Z}����)��B����C����p&���p6���8���%@��=��F����d���p��H�K�xY�/���9,Tk^��`W��A�tP��k���AH���k*����Cb<�x���r�g����M��Q�)�0/��?��nQ���H�/j�U|�e[a�#�`r%d��$9.
#�1�g�������o����)"����>r9A�]�����(�Z�a=m�|� �{?�se0 [���za�
��
%�����K�|�`BR��/��/�4��m)J��u�S.x��)��=����Ku���,G(�Ng����r3-yT������ld�������{����jd�R)W��n�4l_B�|K�eN���z��Gro�}Bs���>�gM�"�.dx��M����B���&`6�����:��hI���
�F8[q��G���
W���G�W��vN�C�|� ��}��x���{���g:o����u�S��ft���n���"�OIK���tPD>^�KX_e#�Emb(�`#�����&j#�u��Y�CMG��}92�Q����J'n���J�o�a�]K�E�FV-������g$�G(�`Q�}S�iJWx����j�t���RR�F%�M��
K�W���t�UP��;�Iw8�RR�o���)M�o���������k�g�EJ�Dz�;�
��~�DH��=���2L����Hdmj�d�������R����9bx?��1�W��O�d7�O}�AD����������@���{F�+x�:��F��d'���9o�91���k
'�~�N�JJ���M�s���QH��PF�|W���;�UkSc�m5��sB4������]c�f����������*��D��u�����aLY������i�������y�8�(A*��z*���-�6�zx���s��t��K������o4��������W�z~�I�?e���a�7����'�V�y0n�,���Cd��4Oe��Rw�.�=8!�O������5O����x�V&e���4����I�o	FE��+����}C�y�)�*��d���;4)���qr�o���pp��gz� �9f�)�@�����-�H~��?:k4�'l&L��9������,:a0P������'�"�iz'�O��Fs�i�1�yG���$���|��U��|VO6�����QW�L����b�S���W�������9W���������v�e���S�I�+C�>�6v�XR@s����e9�y�Ap}�����j^b���	�fIY�Y������~����g���0�j��]�C�����y��Q�<^���TV^����?18T~A������&�_ #%���{�Il��s�P�:x�����m2J	��e���7�W(���[�V���=�s\AFo�vy�����S�=�OCK>/
C����P:��#/_�����V�^6�BY5~���6~
�i��I��[W0)	R/`�<��b��6�����op�!8��e,.ZF����/D�����
sgb��rM"���=X����Z}�!�*����%��.th�s���K$Ftr�wT�"���+�EZ�(��I��z���]��g��:�%:�������v�1����IJt��Xi�=�=
�C�=j�wH'���5#�����g�r�[���ARvN��|������h_I
���U"1�%����E����F"MA8�p�
o:c�^�dv�[
��u�~b%+�
�V��)��s0��#Qu����4aB�����&��!��U%A:��)W"�r�{T�m�~6�k�;4	}���*��l������r�5xg�:@�#�X�[�����X�W���H��?E{�a�U`B��v��95�?���D����!�|%�B�C�&bl�����j�(W������/A��N9��(�#P0'�)4����i8�1cUa��ia4�}6���G.���/(3�0�K�� \���[6���~F{b��@U|:C�J�kD�,s�`l{d��W���HS����9��='��Fv���9.e��_���
�cb��:��g���AM�?	�����\=UD��L���o9���(|Q4��7v�8	����u�������!<�X�U�7�H�V����j7����U��y��qq�W���
5g���Xos+�\z�c���RqTC
6�I����u�u
I���v�P���&���m��)��!\����UCQ���]�C�Mq�H�)w��W�	$��G}w	d��N��KFV�9���!���at �d=|��X����N��a��Lx3�,,��������`U�]pk�bS@��!;��>�����q3�dN����T�n�s���m�2�v��_|���h@�����*#W���B�BT��iv�����u��m�X6��0��DE��R��WpS|��6,���"�<�`m�T�5�Yu���VjxM������(����"���y�>�L=U���Za���I0��^d $�$��=t{��J ��?�8��XZ�K�nG����U�Q�80W���	O�#�
����Ng���a�������OE	N)%tl=D����8�G�L�b��N(���A	d{01
�D8��;>�Xm7?���cM��1�@��QA�'���;���0�w%��t����Z�*�>����Ao��2���(�iP��1�&i?N~AG^%�pS��p�P��V3*�~'�9�k���H���BIJ��(�0��K���[Q�D�����p
�5	T$��J������J%��{ttt��l���k�!��S�Qs���F���TuC�
��}����A���cR(�/�tb��P�9�rVEqa��XMA����_�C���ctW���9�U�j���Z!��`w�����r�A��ihg="Xa�=.@�D�H������)��?�H�G=^b�o9%�O��D%�O�`�(������9�����L>PE��*�#WAWUA	h��G�t��=T'<T=3T=3T��C�Q�*�Ho�J`!%�=�H�H��u���z�0`N���g���
�_�Q��K�\�p���1r����i��Q�Qt��4B�Ti�s���c*n5���W\+H�������?���W&_6�i���$2�^����Itk���`ow��� !�@z9�L�J#qY��<���,{U��'��Q��E�/�&���4&�_9�����h�Zo�9��	��q���;3�������8�st����7K���V}�9��>�/�����R��b�%i��*���g�_�sq(
��bET���	�P'��;�,���)��*��^��Z�u��|zIq��+dEe�T�����j ��Ej�����{�x��4����QsO�p9c|sx�&�N\M9����J+bT��h��?�+����{�����'8������|���)i�	oT�<'pv�`�X0�������p$����K�ldz�oO.������,��W"�&�J�����!����L��)��#��R�/�����_�g�/��F�Nw6��Xsp���1����?a,��09

��@"�[��9����^�?����JGG�A�Z9�����n�U$1_
�v�\8�#��Z��b��S��0�).��!6��+eC�I�XHRMN���XJ��g�(U�}�0���vH�>��j9A��)!1�C������p�;���S�B����8��Ie-�Wa�ou]4�Z����NlXHV����I?tE&B���4/!���v�*��6�.��e��>E�;[�����b���]��(p�������X���b��/.�pQ�j9
��c�X�s���1������ww��h��jy��������Uz��A�:8������np��fw��?��������������?YzH��%���:W�rk�R���^~M0P�M�M�J�����$S��%���`����>�����N�x��P����MA�n�9�����rR�n���@hh��]Gco�B`�����6a�L����r���>��q?,-��}�i����!�6����va��-}����Kz:�%�8���l���M���B PM~}>�rN�Nu�X]�T��T�i��va��]���5��(����+^ZN�[�X)�MV�����%w���'��G57�tj�#��D?�Z-���[ 	G�5��G�5�m�y�)~L^����L�����H	�)(�0�\R��E0������d����,�LH�?4�����d���N�����:�T��?��g���#T��=B[��KTA3���[�����V�$��To��oE�ps�Y'��b8���g�����r���>:(&��%���/��h��Q��?G7�,1u��)k���wp����J%�zt�[����/��2�bL~O,��TT|Q������j{��_�F����<���8OFa�v:�:O��qg�wx�������-������n:��;�l�q���H�	�L>6�X8:�����?H�X��6�f�Y�Y���bR~+~y�����o�~�N���|�_������s���E��j���]��q�h�`�zw>�|�"�3<h��6Q. �v����E��x��p5���6���x���_
�?����Om�=?��x�|�g7=sx��j{�^C/����?gu����s�V<�������X��`O+f`+���:x��sr���?�l;U�(BM]�]���!	%%��[����Gh(������$E�����~P���Z�1�/uY�O���F�V�����z
�@�k.`����0N������>����@��;��3��'������?,�y��V>Og��.'^{�+!)3N���-�C�pEg+��[�{����
Z� �im1'�724��?Y���i:��B��`����w"�+�Z���<������q��di)X1q�#L(*��
�������K/����,�|o��cF�e QhI8��mA�K[�p���*�"{�W><"�'�����8�����
�����}��XQ��\vi����a�N�c���S�f0Ytf�gB��2W��y��W���_���?Z�\2��l��Nf�9=����]���h]{���K.3K�r��������N�������v�3�
C�&�
�CO���.^K��5cg�P����,�����(��8k�\��W.[Xe���r/����7���p�2]�z�u]{0�-�����6�gR�/ �E��-i�::�@!��^�R��E�9)�J\XP�#- ����f�EX���29T�-�\R��������<�����d%�8%c���F���U��p�E�w�*G�E(���X��x��')�����A'2&hk-�\�]��{X���)}��W�=7j�`J���l^��� �4/��a��sn���9D���y������ia*S��|�\^
�j%��rh���u��#'���2F��[J�[��Az���>��������igv���"�������m
����8-D\_B-!�a���P*��e`�\p�Kt]�����[%G���/(��B�X)��-m����<$��y���T��;9����x|o��^,�c�@2�_�Tv�S!��!m��=�Ai7�DE9h7TKPK!>��Q
E+�]���[C����Hv���S����<G"�s��oU���������������&����Kp���.:$�&�(����6\�����#�>FpM���Y�+�������T���Er�]dO���S������?H�����R��/�=�=��f������C��S��k��|�M=8����Z����M=�n��bW�!g� ~������aL������c�b3H^P�Qb���'�����)Ev�^-kl3���1�h0K�/�j�����]��,������h�%{���B���<�������\�\���C����~��C�}�;9��.^.DU�|	u$�>��|��h]3����W�f`3��~��{�S�`<[�w��9��<A�<��gC.�v����'s1�Q������P�!*Z/�C]?�RR���*U�����N�Cv���n�k�����`c8p��g��������8)!��?F���hP���2�\�2d�����/rdI{9�o���4$+�*;��$h+�����\�6�U
����r1�/�.X`��q0�2�'� ��v��v�Y�C���}GX�y�}���5���Awy�W�bU��A�\��
p+���m�Wsbb�.�@d�K94Y�6^W��4���5�.Z�4>�2�w�mg^�.�� ��"���n�+��2�2&����l�K�MLx�j\�[��IM��d��4;DSB	P�S �4j�4/�]v~�d��t"��'��v9�����yg�{e�3��?
%������������A��*���H��`E'j�l���Y+L��0���$|�Z�d\
�'�����#�{<{
?1�A>�si������z:{��c5��n
"$v�}7�S��4����.e�^��3p�[`0��`g.�D�9#�O�11-���C<�\O���DJ�_L)Q�N4c=�aG������<�
*k���7.���t&A��ITk�e<J����������dQ�����H�MYR+�3�iT9�
����m��/���zz��?r{�.�@��`?f�3%3�O���5F��0*��}4c��,�G����S"'��9,_�r������'��}��}��]���l���l�LU�-�X[f�����9��5]��C�;���^�x+���������F�E�\i2�����Q��"j����D�+�u��?0.P�s ���>T�L����/D#h�sR"k$@T��A�]Jjd��e�,��<K[���>������B��0Yv���b�5�Nt�0_��E�}����V��K8��*q�"�7���%�������:��#��<|�%����j&sG����.����ZF�h�d�����O��6�+��}%�P��WO�>��o&h.�`=&�N�Z������v���f�q�yXg���[�V�����GA����-�����
�vHJP��FAX�c��#�C����O�h�p�wpC�L�������'�����q�IC17�����a�o//����I����	��u���z�i�;�� ������f����S�	�p������/B���g���1T44zF�����n��6�e�6=K�1���2�$��3��x���T!�&Lc��h4�e�0�$g���"UR��@p��*�DO]������;V��T)�bzz0�o��;�,
s��Uj��������g^e�������Cj���.��*
_N`��T
�	���k�����k��|z�h��R+
����^3����O9���o&�~Pb�Qr��1����)��C.��,
E���J�Tc�[��H�5=v
<��h�UA�wI�U�3��@8�F
�F��EcR�\��c !,�/��vU�S	0��[����Kr�5 ��r�����#�����#����7�{x����j��C��&oks6�f��^Q�IOa�"���%s���0b��!������2P3U,�
�V��:��Qr��NHg��6����l*�[�-,}J��V>@B:�4Q��U������Jm{W&51'o6�?��8M���������4h�3R����p�,2�(��q9F'PS7��bp_�R�X����[S�
����E����Ad��g������&_��!��0	)�����7����bb���~D��:�m�|�Y��;H����N��z����4v���"T�)�1����j�����J�CA��c���!�ij����t�0��������uH�����~���k5�3���S���4\x��B�m�;W��,��:"&8j�j	9�05�+��2�akp6�����������-��I�n��L|����R�o��.p�OZ��s�I�ZM��[d�l�C�9��Q�%	G��S~�j��wS8H��O�t,�+��GO�B�2�������bN��C��z0���.0�L(0�3�#:#U�7L#�y5��N������j�yP�7�=��3#������h������b�����@�%~�'04���i8���_c�����Q�W�M�1a���-X�(t��A��z\r�)Q�'�l9oD�����4|�8
E!�����'��
L�C/.�`�;S����?(�pg*U[7�l7!��}���4Yw����!�)��v*�2���)"i��?�����6`�v�j�y#a>�o�������uTlLn}�0�E�0ET�;G�4r����������a��(�We��o��?����zG��J�po�Z�v�~���Z����Q��Vj���?�_��j���)�d�jC��!���kr`e�"���N�w���,�I��@K9d[����wd�����U��	xKq��
�;F	�90JL���vW��-��zP������J��^�?���.@��rx�[��[(~3�i���b��_�(�)���MNBZ����0��c��Y��e6�]<��.�35�1�`�	�j�|����Q�\eU�\$�!E7
*A$\gHq�g�h�%_5�^H��+e���F#h�B!V+l�F��]�:��6N���yq]�f�F�GtG#�>p2�J�|��
a�Q3�x�yK�����~ %����ni$�W�J1\�)	u�Y7�'��t8O������i:����]���f+��g��
u1�����$GW�g(��ACd�$�r����klW��NEg'���w�{�Ow��g<W=��?������z�~��n�:d���Z�U~��C`7��N�������rl���&$)��=_H"����,�D��t���:�n&��gi����G�J ����8��\@:�e)������d���y�y�yw���������f?h�=�\\���g�S�]�S�h��sD]��f�������i��y�S9�����G��Sn��xi��8������+�d�W7���%!��L�W2�TQG�}�P�[X������Y�DC���0i1Z7#L^�������%��n\Coah�wC������Y�P�D����3�`���g��P 5c�#���)����w.���Wx6)cG���	�ho&8~���]���S;*�����&e�����<����C��*J�8u��;r+�:t&�')&�C��F.F\AmZ/��P��9{M�e�V���}�2�5@����"��Y�����A���D����8z]��.�g�6Cq��Mv����W�%G��<Q�m������*r�:��77?W�����A��,��W�+�5{����/r��`��%a�I#���o[��e;.HQ�r��D5���c�bp�&�Z���(��X"<��d���<�0���6�[����\"7cM�[2��="O)=g�s�!�����H������$�f=��S�����Af��j�bD����v�����%��+R$���3=�6Mn|^������cV���Yi�_ad������,�	�xI\����Ee_��D�7�~��k����P]�A�:d�b����G����V2���D��o����eLq�_��*}]�����cYG�Y�B8V�|j
9�,�����:�%�XH�awj;u���h�Gr��vng�_9KNUD�1�wyx
hC1�������&M�/��Zp�l^#��p�w�IV�� ��s���.�����Nyc�~dv�|b�i��o��F��2��}B�GcqT%K$�����J�_�V98����������!?�����j����Q�������^7�U��Ay��vp�;�����*e��{�M�
�ok��b�4.j�@��$�v��1�
��������t
��������-���K�!��.�\�s�����!�0��Q/�����<�n8@8"�42��|���d����������'8'���*^kB	���@������@�V��������c>�rL�H})�}��H[)�@Z}N>����������2�_�����3����V�-���2�/A����/�
�����Db���=����O�a��1Z��?�l���bx3AL��L��������H�� ���L�m,��Q4��J�(h��P_uOO��<@��� ?:�R�	���P�_2$��D�`�	�@h��ux�K��>N�&����>�z5
���"� v7�����f�"��2�bTV�_<�������j}�����W�XK��eO"��%"p�)o���;���~��/�z���~��W;Z8"=-�a���a����0��-���Z.(�'*�+@����!�3��tj�����!����%����=����x��<���0��Qk9b�2��/n4�V������8��d2���~ew����RppX	���������y<��p�71�p1��@1�w�iN�	����]�~���V<'�G�W�)	s~���	�gSd�J��W�tR��P�z�����$���QY�
���i�BV�D|$�)�'�����Z� �\��`�tX�J�}�����Z�C?����b�����7���$�a�Bz������k�"xEK��/1���g�^���7?��6����d=K��f���d���?z�m#��.������+�JCA����~BV������p�U�y���������X�����e9�8��M<>%n;�v���$��d*y���6�x;�y+����u#r.K\�n��W.���^�T������Qp��G�J��tdoB����7�����b-	d�Ke��y����Zb	DIr��9�L?�E�I����Bm���������F��2�������=�:��W��?#(��0�����(��9	��|Q���q���g�J$*��6<cX'xL��"_E���2y�<���YO]r���zxT;�j�"e�����{����?N�0��*-�
�gc�$��b3�P�X����MYA���?�i��ci��t1M�N���O{������6�����������B ���Q(������|���PPN�P.����I�RSI)�������8�`���
�S�Fr�l��v�X���<��I�M���v� Ue�-�Nt��S�Ak��/��!��B������"���K����	����*�;���e�En��.��KW������E5�X��c�,0u�������*H�Yx ��Fd����SkT��b�X��Xg��yj�`�w��
�E�'�S��'��G�<�����pZ��GR
��Fd�����oh����.|"-a�@�
:����s�[.w��j�2�h��~���gb��lSr��U�������?A[���L6C�D,��%�B�d�T��1�7hu?�Wiuq}�WA�<��?eURc7��m�>�[����n�z��[�������e�H"�`����X��Y~�~F��4�TG������=�	���o������{��VKM6z�e*y�@�G��y*Dc���s��?
Y$�Y�z��^k�EG���z�QG���1^�'  DK����-uq���2��-���bS
c�J�����d@��ww;�x���_��PZH�Mx�_|����SX�g��R;�?���5�W
����#C���>_��y��hH�����H�L`\�����
��h9�x��N�#3�����l�V���d�7��sC2��^I���)&�
��\?��f����k����~#SR:���
VR �?�W��D^^�yo<�~c�{��Y��Z�M5����=.F�F���N^5�d����v:����N+{��@���cF�0��(	#Bhj��j��������'K,�l��S��K|
����(���F��l�h^���Y�sL�$;aD!��ak�05u�r�L���e���O4/�����*��:������p4
��xW�0]3g���d�����	dS�@+�����}�$���:J�+��Ls�� �������,�����{$�[%���n�<��N6/V��}�����l�H�b~�f�e6�f��8�^�>��X3�-E�#B��zx<���T����i�h�>��fk�.]���xW`U�j|E2��9W�V��U#���9��[�6�'ID�������}��o�%6{k �(N�Yx��5t��"��p���,&%�}�
�)�7m�2���a8�G����
�
U'���T��h�l>����QdZW�q�b�t9V���`�;�n^��,���:j��+�G�i��,���F�n:���v?}w���(%'��H���;�r��r}��%�r-Cs:���X���^�YC�����Wy��V��p\eg�^1���D���+j���&
�*����z�e�	v�h��u�hB$���&��a1#\�<��������u��WEp��l��W�F��>���L�%-$���r�t�q����CF�3-�7[$*/�6K�L�C0����R���88�"A��5:o���WMGPCLt�����Q�B��{��;�
�5-+�����%�YJ��VZ��8��B7��mTZ���f4z{��0�3����[�K:��h:�*��
��[��UF���{.������J1�����C���RN�`^3m�>����8��=�����������[y:����Z�F.����`���i��
n�����+@gRQ-d����i5��`u`P.Na�����|�h\��U��������l�8Z�IfQ�f���N�O"�(i�$^6�S=Q����Y�_x9F��l�d	�cJ�7K�Z�]���FQfn'j2�	���M�y�zXRz�QB=k_���������"����x�_5{�b�=n!��>�eT��
G�.�c+Hl��������{�� ����A������-���4�E��;��9�/6��5t9eTi�p4�������H��t����<�^=����E���^m4�M?�$1��R��Z�u���[�6I�Q��!d����;��i��j�^j�sO��D0
'��$"z����9%3'f��e2�s���H������8�tO8�[�zl�uQ�|J6�{��5��)��|���W���	�7��9
�P�
��>����d<l��������E�v:�Z������Fnc#�!���qM:X�.�X���+J�v�Za�c�>3G���0j��g���w�r���N��`���$x�`�����%Z�J�5���/��q�(A���[?P@8��MF�XU)H;��fx;_)������{��6��^8���b���H�6��$;�%Z�I�!������gH��R�}?�[U�s0�"e��v�*YY�������[WU�k������2��C����]���$U�����z��9T��]U3�*M�CWT�%�(���p�{�\�s�s�Di���p������?L\����^��b��d�Y���'S��WL�����g���!%N�����c4I�|3w����������R6�T�-M	���9i;��X�e�:n���y�E��KS=�/N2����2�����k!h.20�*���;NO��C.��������~(2[�0^<���	�j���m�6�5O��-����9��:�������������[>���*��a������Z�����������4���~
M�D�6��o��a!X��t��!�*
b���Q��{w~��E�}/����8�E^�����P�\'�u��H]I���H�+��~�&��a>����`}
�\N�-q6+H���}urz���
��'m���7G���W���������OX���G�Y�R��/+�6��x$�vZ�@*�����k�m53�mI��G����l���p:]�m�VPfOW��_�|����!�)�;,:h5�����}Lk�C�+���'K��������p�>���M�����V%�q�Q���������$G�8��:"q���!6/4[,��,��t��W$I����Og�!<���������������7�d�;^;S����O�PBIF�W�WWD����W������%X.�����{�_���H�
/����p�;�Y���w$���<^�N�A;}�O�[����'������L�?��v�G���a.&����ZS,�s M���� �:��;���j�h�Q��"-�������C�FN���(~�Nn��k��UFx?J�^�{�i�Ay�K��B;���x��v�+Z�<����
��5�eVz:���0�1g��af�`��F��f��;�Fa�8;�7_��A�,��VU
��N}����h����T~����@��6�-6�5��/�����/�B����U��d������^��/m��R���??|���x�7����&��pS*��������z`�h=��d��ee����!�~+:6�V�L����v�����'D�d��9��������Aa-[��5c�����}�FY������]
�0J��7��d�z?-;0���|s���NSx��)=��#E]��~���Tr�O�?��H�V���,�#U,��7D:���������hUh{9�.�oi)�p�s��9��q2��v�Rl�y>
���(���}���:u�����%���z�E��
z{5�%�r�K�C/�����G�e]��P�k��������Iy�i���A|����J�	��x�����8Vc8�m7���&�������3w=��N9�Zp4�P���7��36=_��9���R��������u��\�V�]u�i�M)�*�SF�����?]�s��x�������e=a�5o���v|Vc"����y1k"�����$���m�T���J�4
��~tJ�v���;x�o��su�����x]��
,2���G���/~(e
��O�V�&!��ru�A����mQ�����
m_�,1K�]��z��a�s�'C?��(���|?����e�j-������'��q�����%��Z�������w�������/����_�V��.X���vG�S�K�H��y��y������j��]��L����|C���i�=3FQ����+����l����,�
��oG���4s����@�<h�Lp�[��D
�]Dh����z�������$��~��>6���?�
1�����b.'�&!g�.�����t���j����/
5^j��'\��\�������Y�~4]���})��&�)N����YX��k*e*��M5��L��5�2��U�p���>��>L��x���Do/������|$�zy������vA.��F��W��O���s�9�	��?6���%�����(p�lw$�)���D+���)n�_�����^�?�?4���h��M!|�����G�=�	*�����9�kh�
���w�6��o�������|0��%�����sj�0�9��Z�M��xAS����1�t���>��������c��)��_���#[��~�p����'oI�t�p�~>>;?[V����F�99?~~��*������3��@��0���������������g�K�	49�u�#�*����N�G��>?y���7�2���s��������I��6��^=?w:�����V?yR�@���;�gY�;[_�����&�RB������S�o�������3�Ge������vL^��Qw�t���4����������m�����;��<~�l�qN��������	r]*!BzL;z�B>zj��i�h�����aM@o��VrU9����N� ��v^�LK�O�ea��3l����]��.��v����?�{���O���'�[����{��3%	?rl>���������L?}s��%��O25�s)��b�����E~�����"���!�(R��[[����x�q�1��
I,��6>���"k����a�4'�F���*��h�$��r���5
��~~�����W1<a�@�|��]�16�P~���8NO^�������*xo�>y,c\M������X�J�!q�/.q�~�K�|���>�'���:��f�i�d�<_Ue��W1��;����~v#���.�Sw�Mg����h���������!~6=d�AT��]5��S���ti��l� �L"�+Y[
y)%�*?�W0���7���{q�_\�~�����zv�%U��a����=�[��7�����W����#�/k^��o��WP��I8I�&/�1I7_g~0���v�:~0T����U����_����S�������sd����G_�<������REj�>����@	�Y>��#�����f���OGoO��������p����9?q��2�/)�<�!�d�:�to����$VQ�j~0�����pS>����������p�mY}���+����pv���v�D�`v��&_���0���L��y��:�-��3X���EQ=3XE�o�D����`�8o_V3���N�;��R����f&��|��p��R���<�E��������������\ ����3��{�
�4O��������7w�Jl')K!R�gNo$�wpw_������m.��:�-��PE��q��n;[��r�)@n��Pj}���
bjEZ��L�fS
�L��)}��2}�6N?V��O4�2��@[� P�
4��TJ��m(�F
B��d�F�Q��%
0v�fc���XJ�*}&�2}�F�����PA(M�>3��3e��� ��
q7�'10<}���|e�<_!j/P��RJ�Q�S�eJ�q�L_���(��K=�4Q���&v���]!���P|O!j�W��(��e��P!j?R������J��B�~��	~����Ex�W!�@S?m~��(D���DBQ�%V�:H�#��m�)K�
Q���~��"Wh��f5�CE�5���z��B�������'D�B���}�YM�Be(Q�LB+�%��G��dQ�,v�)dk�m(��
Q����X�Hq�l�8U��h�G��+���b�
0T��V�Da�I��m�)l<��T���@!j�nJ��"��D��F�����L3I�����
Qg���I�UJ�'�0Q����\�P���v�o+}�x==7��(k�p�H�D������C�#.�4��Cd����.6,<��&	���=_Q?<_1i���E��x���-� �����5YAX�x�b(z��"BP��t�'Ie�b�M�B�����B
�Fv�x�&`�"E�bW��$�q�q�ua���C��3���H��>��^b~K���dI�u�b��KC�KCe���"6��/s���2m=�P!�LQ?��a=�o;�}���C�Y!j�'���|<}�����,���
������}L`���[����x;��b(�A�� �2�P�l{?�0��CE~�a��gc��k��O�%
��"���(��lHe��c���+R�����H?Q�+���P�t?Q�[?u�N��OC�O����5���3S4?S�?�0Q����t������pC��Dq���(@��<��b�x(�b(~�M� p1Q��>"l@��OB5�q��b(a�M����3
�\	�K���
b5��X~�b(/�.� Q�
Aa=�W��*�� 
�N�#HC1�<�
H�U�	V?BWqX����
�u��X�=�P$e/Y�%��C�����+����C_!��XJ��!i5x�B5Il���0�zB*�+a�k�HiV3��S���$��2}��L|�-�I�,v�*d�*�"��
i���K���v�"e#e��g�zx����$0���0��<e=#��q��r���yv�'�nY������>i�3
,��0��.����0QGQ��r+�"�V�X��Q��GI��(Q�M"��`��3U�(��ji�3"N_�zX�]%�"&������6�C1�R���������PH�}J<X����PQ?�p#D	�������q�4����&j�`�\�M��3Q�Cq��+q`�������I�S!�,�zB�*�"1�I4��}
p�211B�zJ�5�V?�P$��]]I���0��G&��&�z����EV?�2�\I�D��J��I�a�I&���i��:��#�C���m�*�>�s�Y�B��SBOS/�������WE�}�����6
2L&)�x����Q�d��������!��i����D����`���J�i����76:0BY�Yj���dn��8iYXdZ�iFt��,�2_1���"�&uFJ>Ja���J�)mL���~d�b(fq�U�L;���Hg�(h$��j�m�>3m=3l(z��UX��5��@�%`n�vT�	����xzn�
E��$!K�'�A�;}h@��zd�A�& ��������I@o��D}x��Lg�Mi�-�D���@��1����e�i�C���n����S��j0B!V?�
E��\�" ����������8���6�D�%X����#���<O��#`��r��8���=��}�+��'S��d�a����d~@=��8����@b���
��x)CQ�o	�*�GF8FH	=�Xr`�2�O�H6`��f��0��!��	���$�C<�@��^���J|<�E�� �x^���S��n����
E/�}(�p����{���M��#��"2��}���(),�� ��@�����(s��F8��cuc�*�"S���)�G"`��gbE4�p���u���bC��8�
}(
|�?#R�q�!=%`��:��#���H��^^��|A&��FJ@q��%�>c��^�a�q���>��4yx=I��.	3���<�UML�q�@���k��}(N���8���&�8��	T��L^8�G����p�_���zJ�����q��b(&�'�f#��"\<�X�����z��D�(�C����H�4�[9
�sq��� ��8���)��sx��P1�PY�4��0�b2!��g�4S%I@B��PR�PL�o����M�l{�3�����^FJ
8^�������,��j�`6Nf?Y��f����P1�,�"�w],\	�@��!@��YP `���������aU��p1��8���&8���	$j�Mq|<1[l(���xw!����}>�cp�>&j���p(l��>������1F(�f���F(���dV������f��[��41	������\�������~a��C��D@l(�~A�������sFJ��Wh�8��3�0Q�J~���P$`]@~���t�~��+��BWpb�"�/H@l(�za��$��
S�~����$�q�)���r8����"���z�J1��-L�">�& =��<
0������O]*CIq1����e8>����KF*^�����~�cC����FJ1����D8��'i��`D�J�#J���1���D.f���*��X�bE�������	~bC���>+�lh�zX\�1�#����)&j���C����'���&.f�'J�1��>��8��OBu��&���OS�U�D)fC��'��a���S?u}L�`�L=l(�j��O��>����CIC+��p�R��!��C1M�?��(\���\�[2�0B��CO}�����������R�C~�b6�jb���8>��zJV�B�Y�����G}��W)fp�Z��
p�q�i��1$���p���U��M������M�����/��+W�@KF�u�y�~_���P��7����<P�hB��I�>.��J������x>_	|�	|�m�@%���8�4`#	�������XY2_)f��dB�qFL��F�4�x;�j�>i,x2Mc�Be��A�F��S2���\����������J1�������	��# �u�z���b@F-�Ac��5��X��ga�CO	���HQ?����b�8���m\�&�h�p�V��CO�(V�'��x=��A������0�t��1���S�J�D��~|@�
A�x0.0��:N���#���� q�~K"�h�"q|_���
����$��M�&1^2�1Q')�����P��k�R�&H��	l��>���i��� ���80B1��
���D�����U���COI�aB���z� ��H@��#lp1� ��~d1�'��P�����6��cpI(?C������l85n��
p}0�iq�i�F���e�f�q��2��M��.�>�� B�z��p��|��c�����_!E��\�
%���B/�����b|�+�c����|��W���|�������8�4�|��)��O��%�,$�5Y�P\����k�NE��=c�"|=�6����@)f�1yH�*.bt�>��00��c�	�
��t�����1Q����a����Z�q���C�[��<l(�0q>o��	#mr4�3���!�$����$q���M/����c_�����!=%���M���cp��2���hzp1"[E~&�7&>=%�B�I��� ��!�-�52��%�>Dv��d;@ml���tTl(�U��a��R�&L��Le|@����T�D�f�z�0s���f�����?DD������\#
�'�u�\�������e����u]�����?>������^G/�l���*0��i�����0Q!-
Q��>�����������C0�B��@��9!1e �����9>��` �2����P�1PCKSb9�@(�e?�%����@��0�8�a�����FF@��1j�T����'���@�������usB���
Q{��` �A"W���@���������(h�1P!j[��6%�5J@l�2P������Vw���5��o}�#  �.0P!j{4�}!���_"W��0PY�{�"W	# �P1P��{���i���@����/��{#W���@��$ ��{V	���{s	����db�3�qjDbOy�F����w�������m���H;�  >�  >��{95��iKD��2	J( ���q(K��(��,j�m�O�"��#��;��>�SB�=e�J(=5{E9
% >G�X"��)�l\*]a�)>/�k�6����C)>' >Q������GZ=5��j�b���l&j.�����xJTF��� �!�b(z.�>����}z8�%�r�!�'q|qdO��`����"O)M�q�����'��)�x��S2����F@WF@�q�(!qx0�3��wTEO	�' �$ �0�����Q���:���|E�B�1��$ ���H���q�)q�*�g
Q+7�D�Rz*�B)x(	����=���%8v8�Ru���
Q+��TE/���W��e8���8���zf8�<�]E��]�P���	���#.m��UR	��O����������d$V�b�J�q�~�
Q��,�8���l2>qL�+\3_��! ����^��f(r*J���"�����\*6j�%�q�V�+�_��(����l��W���X,����P�oIR�:U�?SE_�$ �;$ �X���:*�
���>gfF|&F�STX�#�d�F|&���(�!������
� ���g�`���|�6.l@J3q�3q�t�u50B���8Y���c���S�1�g' ����D19�7�(�� ��K+�g�����' �YQ?�L1��q�(tq*��U�����MD��	�/TR����B��  ��A@\��,p�
Qs�*J�����/�uR�����f���.�<b`�+�D�f(�1�^C@\��/T�/��v"��K���>������M*��)�/a�+ E�����';�!B�JO@�P���R�f������f}��T)�RI�a�ea��/PE�x�"mFJ���gA*�	�r?u��C1�q��(J~K�o�(���h$J�	i
Q+7�DQ��Q����}[��c��<W���h����wH@\)1��l��3P���W�$ �%I@%,q�J*�[�!��&<&�8�>�8V�H�a3Np=R�J�Q�����Pz*�J�Tl�H�V�3S�5c��~Ds����U����p	������C����L�������8/V?�PLB\����?!	qE�����EJ�i������	��\E7����W�& ��M@%�4�p=o*���XO�����Oz��y��&'�����>��N@\��4%�PL���T���J���Ga`����QsJ��$r�$���S)�@��e@@%�4���	\�7N��e.������J�\EH@|7�A	u���e�b(f���E�f!�1$�"|����,������x�g�b(f�m,T�?��C@EA�R%�4��}5���J���U�c�����	�o����X�CO�:�D���R�����$����������P1'�``��-�����&f�af����E����f^2�����O��P��xnV�" �A��H|�i�����j0B!V?b/��"��td��x0���&!�
��}������P�=��G@�G�;��0���p�i�+�Cb�%�i(?c����o����-�-�!����o��@�f��WJ��gE�����L<})��4���S��R�@�i���P$���J@|�>�Q���.Z�8�4���������b(���%���cf�����\K%����)f��r5V(W������P��9&��XD��B�����q`C1�����J�J*��r�FH�5�����8L�?���P���$J����0�7��\�"��8p�8�")7��O��m�q���0���,/lX���>>�������P�I���W)u�b���f���8RJ��]����>�r��_<�����8V�?	�`���C���8��#|�M@�P��8x�b��K0E�e�)=�����c�f�8qC1��� P�������W�m���0�D��8���&X�p�'��R���	V?����LzJa�N��!*�b��nS�~BLz���R���8����OB�x0����F��q�(F�tF�g���i�CO�TI�#:��L13��a �og����01� ���,��z�@��-�E2�1K�"\����l<��3K������8�6�2l(&.�A�&Z1���41������h�7� Q�Q7��|�T\N��}F��M�s��U�C���$�|H�����l���xJ)N�b6d`F�nI���>�4���xJi����x(!6�rRh�$^����A	�4�/���L0Q{��(��`C��	41����l�}�\! ��'�z��u���#%r�.t;��CI��
q�)�X!&�@�/K��P���D�.fC��I`�U�8���)&� ����$�P$ v�q�
&� 
��A����r &j*�C	=l(&R��b6I���M�">��hL�!��B`��y�P1I3Q�����,����� BR��/v����$
��H@��FJ1��o�(��&I�����0B��~D)6�(S�'�Y+���X�Jb���$\L�q��'$�i�E�>+�l�8���"�:6]q��B�	��A��O��0���F��O��O���v3J�(W�%4X�K�b6�M��3�p�&������M���W��>6�p�Jirf��g��[���$�L��R(IS�PL3�O  .f�d���f3�@$�5=%`���t,W���R�C��l��=�����$Kq�)o�D�e�>X���P$y��}���I��ZR7��)�����i ��F
6i�c�-���S�1����R���C��S����RZ1(W����V��e T?R/��H)�����)������w��
m#\�������_�U��P�����G��f�w�c���S_)f�!��L���(��hD�����>6����`�b6i*��b�0�G�>e=�f��/%�e�<;
�b6i����a*��)������3N��/Ed&X�0�b��<�@'}��r&�HQ?H��"q�I)�lRNy��FXA��PP���40Qs
��(��"��X�Mc���b���Bp�i�{#���)�a�>c���`��Sl�q��a��������/���j�"g�@��?.f���+������N��DM�������dM6�*�lR�"�PXF�>z��D�*7�q�S�?�\*�3��lh�{X����C����*�'�����H��\�/Y��I3������" =%�L!j��P����g�z8>�Lp\�&s�&��#������}��cd�r���w�\��M���>�� B�z�qQ����12�Rc��B@|���`���ap#e^�CO3�����W���|���q�J1��������Op�)�a����d��x(�@�/������� ��5� ����4�b|=F$�P��:��@(�l�x���l�C��S�����j������b(fa��?3��1Q��~p����%��ZdrMz�z��
n��PU��b6Y���(���3�Zh�����	�g��>�����.���b�!5A�����q�>J�xvc�'�P�8.Y���M~r��8�4K��!�~c=!������]#�d�b6dZ)�P������q�iF�&����c���
�,
q=�,U���5����,E�o����y�P���1����b�U6p���*���,������G$��:S�� A
E�����B��������/p��M�[vfE:8e1w��|2M.�����u*����~}��a$������O��F�]g:��e�������3���IQ���w����/f�b2t����,��b^���� z�{8[([T�/�
��~�n>[L�|7(���Vo��7�>y2������n9���.�y�V��������#�z4p��w�����[2�������3�7~��e!�B����/NO�:����:r��|*{,w���,���f�N��t^\]Og������fK����||v~��.E��99?~N;����q�l�����E)���0*�������l�<?=:<?r��^�=9=<��.����A>����y����y�_����}~����oi����9;��^-���,�h2���,��._�&��%�v�A�]��w�/K��&�>��E1�$��>������@g?�yu������aT�|2����wr�Z��$���W@v
yF����wA�J~P��? TgEY\��|<.����e1X������h2(>8��sA�����<+�(��I���J�1����������o���E����z��o������D�)Q%�� ���3�Y��dR��������3'Em1����r�[hM;-�������s������x�L{�G�EI
S�x��4�bV8#��xJ�5��|� ��r�C��sS<�w�{��x�9��cBdDz7s������MNKC��46��fr;��'KKkN(� x���I��>R�e1�;8����w�����������s�f���������8p�g���d���h�FsK���� V�<�����W��
��(�����J�� ���X�o!��/���P��A]t��1)nh�D�W�AM��J����~X�A�&!�Z�fa�;�b2tw]g����������)����)?����l��J�\"��5��/NV�����Y��L�����[����	-�f���{��>�/��G�|��D��B��O��/�����OH�;��i�:�u��_h�FL���i$�SYpCV���q^����
ib����[!5f��Y�0�N�
i!��n���K$k��)�����X>g�=����*�"��ViJ���'-���V�z+#�,�r�d���v@'�����W����~|������3�Ge�����3�<N��<}s��������W���4�W�o^�h����}tx������[;�������#�����������2�E+���;z�B>��]-��LY��Di�(&�,�]��}T\������)�>o��1>���P+w����{x^�5s�y���O��yI�?��.��..�a���:�����&�9>sH�t�����s��-S"������o����.���xzC�L������������y6�5��W8Q���+���[���N�l�&�������W@S��w�����%_�7S<��`���j?Q��l���g7������	\&IY�����g�1���]5�D�������D%��l�
�K��^3���6�������=�E������y�����4�{����>���v�z)=����[�����nt����)��Y�TY�{�+(2�dk��_��}0��f��x$��Om@�g!s�����������s���}4���yw�O����{�
�k��~������/���7�����3��f}5���������.���m���e��w�W��Hg��X��X��k����P���
�X���Vh��1�����7sz#	z������bd#z��Z�+���(��]	D�v.�F��]�W8
�~${��y���7V����e������e���p������=}Kd���6�]�&�_h��a��(�~��{��O�����������>/G�=����������eX����q����_���?�����K���%���������s���M���q��
�r�;����
g�J~�Hn���8��F��0�^9�?p�����0��(+�a���4����G�AZ�K����9+���}"��������p2��$v�8]���������{�l^����w/H.=q~bo�:���8Ly��>��'���
������������?:�w���Y�8cb!t��	���x���m��5���:!9�1�6������^a���		�>6����<\�/�3��^���z1�M7$2�C���<��}~��RH���������?!���3���zg~B�������}��������lP����[������kP�/B����g��������nf��5s���-���`@�<(���/F�RUx��������W��������h>"�hV,O�z�Kc^�b���j��?Y��X�x1(ZK���|�r�/��a��d<*�k��Nx�����S�_�_�Z���z6���(\��v��*^��h�D���gd%�P���b��'-�2�\g 	0��������K�fP��{o{�2�Z��m�O�F��]?X�
��K_��FG�+H�	��4��������(����A4L37���:��&E������_�c>�~�[�����:����������+��x����x�����s���/�O�1I}��f${�9#�_����sY�����7��S�Y����7�=zTe�������m��6m�����_����^%��c5�f�W����K��s����a\����E	L��zi%����<��iYm�����,��/y��_OK�)�����7��???�T	��m��O����r�3$������9v>esP_'���5��l�����������;��p����7��W��v��>Lm�l�8�8�����9Q����kj��<��^���)���s��sT��c�H��TD�Y!�v��$�����9�����pH$K��-&%	�vb�� �w&�S�t�L���/O������r�l���6����l����o��[K����E�����w�jo^O�9���_n.���f���
li]�0���D���9�)��<�qv��h��|1���sj��>���In'�]_dHHZ���0|WM������=���4����gl]�~K�y�s�]�Z�g�"$��x�\}�G������iL��yo��eB���)�-f%�z�)m^k��2B\~����x4X}�i�7�������f��bVp�|\�7��W;<GF��z��]��������������7{��� =(f��?����So�)�I���n8s��.f%�5�s�^W������l��x��o�-<
���:>���r��y�v��������h�t�lV�v�i��Q��o�O������^�S���~>��-����F���o�������b
2����u��_&�T���l��&�6�����C<m�.��S�����?����y=��G�
�W�bm��t����{g���
��N���l!D�WA�E["�����y>�:�^/���=���P���g��~89�]"�fG;/��s�qk3�!i���ik��j��4�/��
����������?\o����*n�nz]�����=�����&�6����tr�B�����OK��~5� �z;�=��"}Dr��j}5�:�����7�Q.e����?����
�5���dj)u��+�e]����#8;�C���pv+����7rHr-_U���3��~r3b���L�R
��`�*���'P��s=����'�9���:�F��?r�6���)���G����
�T�%J����7/W��Di��Y/��1*MC����N^������;�d��g#�yn�h)/����,��HI-�0���&��^���Cz���-��������.oCN�2������F�sk���3�i��d��*u1����7���6���CY�������5M�s�s�c��MJ#t��WLm��'��4�+����=z��FT���z�XT+mph���������� �y?=>wv$[�l:�
�w����Sn4Y��>2|���pH������6=T��������v��,�.�������m>��I{�?����f����R�$��D�`�6�(hs�:{��5e^:z��c�^�M?��w��Z�n�HQU1�����O��x���"��b~�f��U�-���by����������0��) ��x:��S����b�mr�V���Qg��!�'��R{�;���������28J������Z����%{bSZd�|���=m#�X�f��(w&�+��'���~w<%SnPo6�8+^��f	r���*���.��8��[�59?��`i�����������i��&M�O�8�1�)�R1���g6`�4TO��S���=�DB=<]7H�N�'p�2jF��VD��w��Ac�7�a���}r'�rv~j������A���k�6�c��
�'�\�+��d�p�(/[�f���[����,&Q#�'D�K�����uNj�:3�(�l��y-u��Mq~}=�^�F2Z2�E7���X��xS<�#�X����%��7��@n(�����mY������uI�N�����D��z�����	���j}_��>Z1�i9�j6����
C��������2����
g�R����;��f�L��X�DjJ����H�q��� �j�.��Y��WLp�O4����N����i�����xA[ci���vY��d�{����b��	�����o���v��Dk2���f ����j��N��V�&ZA��v�:�c}����������`8+�[Z���������1=��q����~~u��-�w������{���ok��O�eU^>f�J-�H	��i���zo�?������j������aN���Z����o����[�1�x����c���]1A+k��]}�f�X+��b5���Mm;���8]���ON(�{�������#��;m�n����|gGr3������$���v�xQ�����.�����
aw�L���c�P�m�U�.�'Jk��s�"I�����|�O�Y��yG1�[��gY�������9��'��=������v���L�O�T�
z�q��;��X���Q���G��U���w��&.Fc�p���"��b��q|�����Z���gN�?��l<����2c�7��;�:83�v�;J3z�����2���w���a��4��
�[����7�P�y�1�P�ab�l�v5�������U��������N��X�)S�zW�G�Gj���������6�����Y����?����U�jS��fm����]��J8^�>Z~�n��s�����td� 
�A�Vv����+�	#t���7����q?�]�1D8�����q��_��rQ���D��

�J
����U�rq���(�g���((�,�~�WNmr}��eS���YI��y��C���l�3c���`u��M��Q]fS�I��QON�_�!��I����������\w��"?�i�r�MV�����0v!��q�A�����+\�A�P�o���b��t�!l��h�E���nf��-��0��w;�D�~���N�&��E����2�$K�]�j[-�P�R�}!���v�^~��$��3h��i�����8�m���SCo�<����d������Ek����^rA�.�7weAH��B]f{�Y�����|�yB	{��e9'X�}'^;w��� �hq��;�������J:G�����_��q�7�M��f:�3����*&�^�"������D2H�4N�~�Q6��(Q���t����n��A����G[����K���=hk�������V���:Okj-��5Q���F+��v���_hW���~���yk�ik�^�=���4={u�����?�p��������$�D�#6�m3�
c���^���0�{�Gl%D��O�.M�)���L�'���GC�	=~�`�zI��8"�������5�.��S�y�H���a��~y�jtE�w���e�����������^�r�I�v������I�a�}��AV��:��m=�J4����m�����I��^���,������74nO�&�0�
�����a����[�z����y2!��x=��Y��<�$�[-6� 5��lYj�������|^���rn��+�U^���l/uKDv��������g������/
B���W��������z������4/uo��MS(����������u��vZ�U�4|���[���zG�����4r;v�������_^
�~���I�U�X��]kNr����[��jV��Xy���!�p�����}/���0��[w�jcpK�����OY���b����L��,���T80}�aU���:�:��1����~q9��"7�%�/�%�����7v��q~z�����t�������U�gW�	K���X����wyo:�C��������A����w��X�p)��&��O��0=�u}TO9�����T��4�My6����|�R�����z��L\���]�:DcUZ���r2dz^j���G�K��h\G���
���������s����Na~8<?|%����t��b+V���d�Hi9�������~%�m��p�~���>&�������-�=�^lH�����Czi�_����X4����*��N������|��]I���.��~:&A46����1��
�@�x�6��.R
��m�LC4�� ��u�\V5��Mor�Y�s`���6M�g!7/��N���;��pi�����0���&��SO�*������Z���j��T���
����[��m����}e�~������z��c���5���g����������v`�z���>��nu��4�WF1���/m��wp���9P�����:
���jg�oL�
I����0!bhM+�����I=+��}�����)��Y����o�fV��e��;��jjw��W,sO�����������0�tv���Mup�����!�v�{Ig!�qA*������`�	�������Ml��	��$�_I����D���K�|U����{������Q������
4�#���!��+��m��^���c�_���w�����o�G��xY��,��<������7����K��f�~1��/T����/D�NM��Y��d����,7�r�+?��p���!�����G������Q�7���.&���i������'�����1������(9��s9�_�OLp�~Q\<?<*�������rz������W�>�e8{�_n������m���=Og���O����C�^������M\��r����+J�bd������_�h��/�,\��wpzt������h�A%PR���Ayu1���������������l�������-�o���j�At���f���0����s{��%��/^�[�����u%
���~~���M�"���M^��$�H|���W�(l����+5b�Xm��
N���X�=2(����IR�������&�V	#%CD����s�Fr�mu�)�P4U�G��g�����}1�(mQ���o��O�2>�����b����y0��O���r:�(�'���}D���1��y/����;K��I���R���X�C��#�F�������'%u��$p�"l������U��2��eS�w[I;��RXvZ���-O_����T�y<NJ}Q�
|���Iv�����Uo���u����DVl0bz8�kB�VGLR.�bn�-���i3����Ok
��s+��2/7-�DC'����k[��"�-����t�RV�F�����q.�aD���u#^a�d�V��C�eM�����!�ag�"�J����9�q))!E��t�B������m�����U'(��p[|�}��:��)�o�=>`QA[���zu���\�'L�����ZC��u�:'��H�:2X3���M�&���:
:�&�'&��f���.gN1��{���l���sV�
�r���	��p0�|������5�RF7XH$x��jI��$^��/��/I���6e�YI��5�71wms���V���j���ZWe����9�3���\�\a\a��=?���\z�|^z�����Y�����Ve����$�X:���zM�m(����O\IN8_�]r�`+���;>���+F>�o�u1��^i~����<�Kj�!X��y�uyc��������7b�� ��Z�}%�3�41r����i�;
�ne��v�����k6��rWv�A��!�9Y�W�6O����M4�Oxl�������e��6U_��V�:�������'��Mn�s�6ui��6�ro�Q�2j����$w�f���$��&��
��ZM�Bc�?M]E����?#���M._���V7a������T�EQ5E*)��z!��@H���<�R�����������Q�%Q�\����!������*���E����fR8���w�Q��LAR�9 ~�Q2�����/d�5
�^nP��}{��������{�	�9���U������2�-UA�h����4*�r��7��/��L�i����hbY��k>���e��/�&h�0����e8w9�bVY�G��/|g��w�y{\[�h���Z�e#CW$u���R����$��I����,���#�H'��w��Q�l����xJ�ZlH�B�34�� h�]m�A��l�u��H��bl@��d�����[�K�VHoTd>��~�"qc�V7��������k.�Uj�X���(
����t�	����������"\N�&)W�l��@j��T���%��<�h�V!����!�Qi�,*a���A��r~�ly��o�fO�M����s��m�aLZ���!�����V���6�Y$3����VvIS��-�t;�j� ��(;�a�������*D���q���y\��0�-ZS�}u�f��#^-n�}��-�OiN*1��]��V�?6/Wb���h!lrLm�|��m�@^����
���Z��~H]���bj���n<^)���y��X���+�������d��
n<V�����,d�Ycs��I�e����@s_�A�(dM�m�l�k�\�� �&���D����P����r��GsS�W�	��g�4.fT����K��L��3U�F^���Ht�f:�vdcV%X=Y6�+/�P\=����VE'�
6��y�e�]�V��%R���2��p�_�����!������zQ�����efZ+�F�%�ol��N����|������o�l���hN��XT$jX��Zz���H}#���V4�����X�|.���\�L]���a5Y�y���1��������Rkk^^�1o�&���TZ���Y��Z~���(���c}1��`�doJ���]��c>s:�eKB���E�L"���U�T��T�Z����%����R~�����U��������$���>���p��B����\�[V:U���x*e�8�^��
�ee��\�Z�\���\��BlI���K���[�Yc�Vr��t��T���O�]�=V���[��K����"������7�T6���n���=��j����a�+F������3��&�sc=w���=&>ke�aVrV����W��$��l��6v�m�:��������N��O��W����ge	N$\����i��o[f2�Y�V��ezM�����x�������C@��"Y����iB�H}hn��\��V��g����{S.�UY�\
�R��N��Yq��_����i)�\\�Dq /��%�B��,������b���B=�1��j<������8j��xg��I%	Y�����>��C��Z�4^
�-K�\�4k�7����q�������{�����l����-2d����K���Z����X���	����O8uH�9�
��J�X�6�����8y��9��C�D�!�*�]O����
�����8~t&�g�6�\t��Ma���&@T�FC`U�����Dd�`d�oMz�<
$�+��x�k��$v����e�%�<�-E��X�3wVq���3kr�T�������&���H�r�5S�c���h�F����r��o�r����JR�=/������%�)U������5�
����e�����4;>	|����Wg
r8R-<����=�M��vR��\���X���-������a���h0 ��y�Ax��]��nb�D��*>�+��l���%�5�-�!M^���W�����������L��ea�*!T�l]�Z7n�QI
;��%DZ'F�hZu�0��v/+�	��4�����/J9
+������
���_�<s���]���
I'��b��S+�V���T}ZB�����cY��0�n�+������c���T1lcG|��������
���&�zw3y2���������Vw��(�2orX�?��L/mW���>��&�_.hW�j�J`����$4�Wv�ISf:j���n�!��V3�6�1h��V*��`�#1)n.���tQ7�cF��^�^�8Y������E3$�o�|&�ed�g���.�	W"%�x<�R�NV3��}^������pk����������m��`d/��Bu�~���4���2-:�yN����s����F�t�<h���K>��n8"�����3ea��;�u}��#�,z-[/9 �^A�@�7�R�"�5�xk����i3��q��Hef�
3�F���c�Ig�Dm��Z��
 �-�a��j�������������������Y�)��ji=���-����~/�<�=�Ds�.zi<�\�:��'��$`f��
X����$�f���{����@����1�7���I�%w�S�YQ���yY���-F��L����(��a�Np`�A��N%��z�������a��ck+��&�bB`mJ�OM{f~,*�LE�VI���iN)Xz������
�Mb�f�#���N^0T�`kc��)�I@cO�,�M�[Y�#��}j����t\i���SZ��K�����������������s��[���gC3E"~X��	R>�����p��1����?�h���	��_0`A���v�e�{Ps�i��(���c�Eu��p1.p=^Ce7�=y[m�HC�M0I�n�9�������5�-S����s������j���E������Z}�n`G������4���^�&J��z~4��
�T�[��i�6�Q�E�qd�V���%�Y��"�Z}^k��njw�M����p��[�3A�O�Q)�kc���1�A�����9�Y%��u��q�b��8��"��IE��K�F1��tf8r*b�6L��u�WuJ�L�
�4�q�I��0`���f� ���/����\g��w=�U�W6���u����/I�(f{�D�"!�S�Z4rh�\n`n;�T�>��������iE�B0�<�=�����Q�_��]��E���U<�.�%���/$���=�Y��|�J����*��+�#��eH�X�\���sQ]��W����w��������N��!�=9��:	��"���wK�=����T��WT�P�-_3iO*O�x
�Io�p$�����R=����
h���QA��e'1�q2M����v����p ')�iz_3�(���($����>� 
�l����:�zwp��{SS4���6��^�z���
��������hiU�y�+`o��5�����{AF|�k������-�H�?�N8��<�c7-���������������_"��D���#����l����"�$ l�i$2N��|dkl6���%�Q�
m��j�]9�e�������^+]@�Y���f��UB�����5W^\����b���t�g9U����W4�-Z��7����m	+�����a#����B�^m������<���>,���uwQ%7-M�7��p�.0
#9��3�����0���APU����a��;��{� ��(��ec6a�|�,��L<�7���������Te5e��pz��y|@��������|�E���.�?v~:>��C�z�a����:�x��n�z�7�o���w��l��;������RK1h��y�����m�y��r�q���n�E�A ��3���3h������{i�z�<���8S�N��Y^���(�����T��=�w�������i�YY����dT�4�;�=X��,P3DB��iTR�H,{q�Y�k*�&����F������:��&e�A��b��V�����jK�e�14+�
�U���������+�j�=
��k�[��]^r�S������+�<'�1��b4����h.�	��6�t1���x�%�e�T�t�L3���$�����a����
���3
|p��%�����g2u�0s;��-�����UF��|TJ0���y�0Y����m����|���s9��Z3�|M9�B	��w���d����6\s��W�$Z;�e�`Ov�����]�,��_���D�zx�KQ�ky���O��h#2F��L�*���7�U|O,v)���(Oniz�x���A�����!�U;�v�@����"kK+��������a,L�y���G'���&��i����f��gU��w�l�+@������,�2�����.��W���ha�Z3P�������'"c�������W���0�J&��
7]����*��U�7���`�sU8�J��J�wJo����<�{��`�"���w���������|{]���E>��y�XKb�lU	�����f����a/�v6��0�����6�����"�Le��/���-�pz���������u����*��I�����$��b�UM<�������8/��	�N�}��^�#��/�{��]I����n5�TZ��9��4�����0��3S�������g-h�:��E��T����OH�JM0K�XT^���ds������x���	�\���k����o�����k{@��2;�=RO�g���>��*�[��
#1wG��?[F���3��V�gOt?m���e��mn�B�,3�is�4Z���j�.�i�='�k�:��T�k�E?X�|g���L*�x�r�gU+Zi�7����,�,����(%+|������g��G�������#m�/��}��0�U�sD��:����v`��`&�o#mS�Z�����|ng'�O���d�������M�}���'��Jk�i/��8�������r}Z�y��������<��j���V��ik/#�%?��~:��,����aV"�L�
����9sVKlO*s���e\=�9;zu���������~'�m)O�~`\s�,���~����Q����)���)��m��M����M��9�yQr����u�}�qg��a��^'�3��#���5�5��z����A���z��'j�d�|fe�3#���t\~�T��gs�5���|:^\M�qI0z��_��	I��E��5l�U�����aO��������Wb�LGk�t�u\U��q���������X���M.�����#����&=i	�����#i�@�>�������@�0����Q������v6���x�}�K��j]�9�e��vq{��%h���m���vu��*i��V	�3���+L�q���9�?�d�������Z���
4~�!qf��F��'����!��?|���,dq�A���,1!���4���RI�S�V)����h��Y��O��[dE���*�`�����v5&������������>^�$�����GV���nl"��<'
���6��k2��Lf�ZNa��*$�e����q-Hk����,�	�Dj��#~�����j_�JT���y	�l���k��p����1N�v�6��L{S0�,�Z�������+6�rS��T�i���A�5��*6���^*!b�@�JS�:K}IS��V�����f�[�H{n{���l^�������+���m�1me������=��3n����6!�������}��=�Zi.���d��5�U���m(x��-7R�?c������{|s�-��*=��kRg�}�K�f����-Z~3�k�p��p6�������u�|\��1��
R���{��TT-}��$��l#�>Ok��s��1������7�Q���'i)��m�kYm�EYD�r(E�l5��o�9Z>�24U.�)K;��~l�I��T������9[/�[��
�o!nb�����vmbP�u��L>�I�3Y#�L����c�8����9t�����_P����T&}6v��O��n�����.f�&9����0��%'�="������}���m����1tmYi�H�pd�������U��j�8�,�u
5�`��l����Z/+��Y�^��-������*b(N��������$��%�P"����ZI��<s����x��S�71/�pY[CA��[
��o8��������8�*f��G6�i�������vx���'0A����-_^5������|[��w\�����;�>�i}X��;�.���t�������t|gA]_�;n����������X���hg�u�\K�����t�;��g�,����w��p{?�k<O�Ix��{{;�����]J"��������C��Z�R}/zH���1�5����� K��A���������s��$�������g�_IC$��O����w����G��i�|z���u����k���Tj"����T\���zX�q���lSr�RX���(��\;����]�F�;���i�)�N���g�J�{5�; ���rT���_
�g�p�:d",��zh�l>c9w��&�E�&���D ����w��"���Z-���e�]����_�L�&����+5��]���lo�������[�|�����F�����2M��8/I��9*�f9������UJI['Y�oY��E�V���'���om��_R�[��Qv��t,�����g�uB��G�3��",� s{k���_�V���`nj��#��@�-�)1�7�p1������6����7��h��#,K�����-��Hn�fg�'ze��s|"�[��m��o�0e�\F`�vOn�7tUV�^�Uk�x���*UN977]���'U(��*��5������r#mxE��e/g2-��W�����=��6(Rb
V����]�2������\�u
�zH��$H�����j.fw���
z�� ����Y�������~��/�
3Z���b�m���)�lCel���m�<�c���r�������G���F�j�YDW{5_iUG�k���*��|2��*%�$��8�J[�?������I{?��e�[��w��Z�o-'�		�M���
I ��g�T�wN8��8	�1���m�E1��HU�\.�)�r�u�E����T����gG�����D�����/���|��Z����Q�o1u�����GZ��y�{����� 2/�-[g��n#�����c���X��Z������$��i���u��+�����4zR��5<�����~[[��2mKY�=�S�xA��I���^���F��-��1aKp�(�I�@�!{)��+n4~�E��x�vQ�o��M	�A;�lbt��_6�����5Y��{���DE�}~��
�$����X����(,��hTU�4N����/_Oa���Y��<<������k�v���3����u1w*W�-)o�J@�=[�?��4~A1��-��a�r�d���w�������kdv?���_'���7����a��Ns�.HVMm8�;�\-]|��}�o3W��&���{���m��-����[k���!�|�
;��6����,�j���	��q&�7�X��8�^�4�����TQmf1�8���Jf�Kr���'�&M[��d�5����������NR�������4�V�1U<=9�����c�Q@���������}!�@,�>��D���8�,@��o�:K�K/[!,PG�47������c�4�^V�q �\��*{�\�#�'�FC-������7�x�M��b��,o
�n��]�n������LP7��S������(&��Yz�J�n�=��3�0�P����e
�.�r��XHt0��A>�I�q-���?8��`�$O��a���=��u�V�l�P�S�q�8��`m��>���7V�Vh	�2;\m������IG�a����>9���~&-:�#*q|�$���r~�����5F������Z)�����{�������Z���]� ��g���80��pw���X�$��$V��^�np�]�]j��~��U}��m���RK�:e;*����3w��@�%
�q�?lMU�w�7�a��AP���B9����T\�h�o�5���|�H��T�r��w�r��%����mez�_�\�;���sW���	$�����UN����Q���zO��N��,�d������U��
e����?�t�gN��s1��0$s��YI��>�Vl1��R�n��]M�+L?4�0����&�-�.�=SqAD3<{�W6��\�Ly	}��hX�Z�,�N�&�`]��x
�B�P(
�B�P(
�B�P(
�B�P(
�B����_����
#332Antonin Houska
ah@cybertec.at
In reply to: Antonin Houska (#331)
Re: POC: Cleaning up orphaned files using undo logs

Antonin Houska <ah@cybertec.at> wrote:

tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com> wrote:

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

Thanks.

I've added the patch to the upcoming CF [1]https://commitfest.postgresql.org/33/3228/, so it possibly gets more review
and makes some progress. I've marked myself as the author so it's clear who
will try to respond to the reviews. It's clear that other people did much more
work on the feature than I did so far - they are welcome to add themselves to
the author list.

[1]: https://commitfest.postgresql.org/33/3228/

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#333vignesh C
vignesh21@gmail.com
In reply to: Antonin Houska (#332)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 30, 2021 at 11:10 PM Antonin Houska <ah@cybertec.at> wrote:

Antonin Houska <ah@cybertec.at> wrote:

tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com> wrote:

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

Thanks.

I've added the patch to the upcoming CF [1], so it possibly gets more review
and makes some progress. I've marked myself as the author so it's clear who
will try to respond to the reviews. It's clear that other people did much more
work on the feature than I did so far - they are welcome to add themselves to
the author list.

[1] https://commitfest.postgresql.org/33/3228/

The patch does not apply on Head anymore, could you rebase and post a
patch. I'm changing the status to "Waiting for Author".

Regards,
Vignesh

#334Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Antonin Houska (#332)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Jun 30, 2021 at 07:41:16PM +0200, Antonin Houska wrote:
Antonin Houska <ah@cybertec.at> wrote:

tsunakawa.takay@fujitsu.com <tsunakawa.takay@fujitsu.com> wrote:

I'm crawling like a snail to read the patch set. Below are my first set of review comments, which are all minor.

Thanks.

I've added the patch to the upcoming CF [1], so it possibly gets more review
and makes some progress. I've marked myself as the author so it's clear who
will try to respond to the reviews. It's clear that other people did much more
work on the feature than I did so far - they are welcome to add themselves to
the author list.

[1] https://commitfest.postgresql.org/33/3228/

Hi,

I'm crawling through the patch set like even slower creature than a snail,
sorry for long absence. I'm reading the latest version posted here and,
although it's hard to give any high level design comments on it yet, I thought
it could be useful to post a few findings and questions in the meantime.

* One question about the functionality:

On Fri, Jan 29, 2021 at 06:30:15PM +0100, Antonin Houska wrote:
Attached is the next version. Changes done:

* Removed the progress tracking and implemented undo discarding in a simpler
way. Now, instead of maintaining the pointer to the last record applied,
only a boolean field in the chunk header is set when ROLLBACK is
done. This helps to determine whether the undo of a non-committed
transaction can be discarded.

Just to clarify, the whole feature was removed for the sake of
simplicity, right?

* By throwing at the patchset `make installcheck` I'm getting from time to time
and error on the restart:

TRAP: FailedAssertion("BufferIsValid(buffers[nbuffers].buffer)",
File: "undorecordset.c", Line: 1098, PID: 6055)

From what I see XLogReadBufferForRedoExtended finds an invalid buffer and
returns BLK_NOTFOUND. The commentary says:

If the block was not found, then it must be discarded later in
the WAL.

and continues with skip = false, but fails to get a page from an invalid
buffer few lines later. It seems that the skip flag is supposed to be used
this situation, should it also guard the BufferGetPage part?

* Another interesting issue I've found happened inside
DropUndoLogsInTablespace, when the process got SIGTERM. It seems processing
stuck on:

slist_foreach_modify(iter, &UndoLogShared->shared_free_lists[i])

iterating on the same element over and over. My guess is
clear_private_free_lists was called and caused such unexpected outcome,
should the access to shared_free_lists be somehow protected?

* I also wonder about the segments in base/undo, the commentary in pg_undodump
says:

Since the UNDO log is a continuous stream of changes, any hole
terminates processing.

It looks like it's relatively easy to end up with such holes, and pg_undodump
ends up with a message (found is added by me and contains a found offset
which do not match the expected value):

pg_undodump: error: segment 0000000000 missing in log 2, found 0000100000

This seems to be not causing any real issues, but it's not clear for me if
such situation with gaps is fine or is it a problem?

Other than that one more time thank you for this tremendous work, I find that
the topic is of extreme importance.

#335Antonin Houska
ah@cybertec.at
In reply to: Dmitry Dolgov (#334)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Hi,

I'm crawling through the patch set like even slower creature than a snail,
sorry for long absence. I'm reading the latest version posted here and,
although it's hard to give any high level design comments on it yet, I thought
it could be useful to post a few findings and questions in the meantime.

* One question about the functionality:

On Fri, Jan 29, 2021 at 06:30:15PM +0100, Antonin Houska wrote:
Attached is the next version. Changes done:

* Removed the progress tracking and implemented undo discarding in a simpler
way. Now, instead of maintaining the pointer to the last record applied,
only a boolean field in the chunk header is set when ROLLBACK is
done. This helps to determine whether the undo of a non-committed
transaction can be discarded.

Just to clarify, the whole feature was removed for the sake of
simplicity, right?

Amit Kapila told me that zheap can recognize that particular undo record was
already applied and I could eventually find the corresponding code. So I
removed the tracking from the undo log layer, although I still think it'd fit
there. However then I found out that at least a boolean flag in the chunk
header is needed to handle the discarding, so I implemented it.

* By throwing at the patchset `make installcheck` I'm getting from time to time
and error on the restart:

TRAP: FailedAssertion("BufferIsValid(buffers[nbuffers].buffer)",
File: "undorecordset.c", Line: 1098, PID: 6055)

From what I see XLogReadBufferForRedoExtended finds an invalid buffer and
returns BLK_NOTFOUND. The commentary says:

If the block was not found, then it must be discarded later in
the WAL.

and continues with skip = false, but fails to get a page from an invalid
buffer few lines later. It seems that the skip flag is supposed to be used
this situation, should it also guard the BufferGetPage part?

I could see this sometime too, but can't reproduce it now. It's also not clear
to me how XLogReadBufferForRedoExtended() can return BLK_NOTFOUND, as the
whole undo log segment is created at once, even if only part of it is needed -
see allocate_empty_undo_segment().

* Another interesting issue I've found happened inside
DropUndoLogsInTablespace, when the process got SIGTERM. It seems processing
stuck on:

slist_foreach_modify(iter, &UndoLogShared->shared_free_lists[i])

iterating on the same element over and over. My guess is
clear_private_free_lists was called and caused such unexpected outcome,
should the access to shared_free_lists be somehow protected?

Well, I could get this error on repeated test run too. Thanks for the report.

The list is protected by UndoLogLock. I found out that the problem was that
free_undo_log_slot() "freed" the slot but didn't remove it from the shared
freelist. Then some other backend thought it's free, picked it from the shared
slot array, used and pushed again to the shared freelist. If the same item is
already at the list head, slist_push_head() makes the initial node point to
itself.

I fixed it by removing the slot from the freelist before calling
free_undo_log_slot() from CheckPointUndoLogs(). (The other call site
DropUndoLogsInTablespace() was o.k.)

* I also wonder about the segments in base/undo, the commentary in pg_undodump
says:

Since the UNDO log is a continuous stream of changes, any hole
terminates processing.

It looks like it's relatively easy to end up with such holes, and pg_undodump
ends up with a message (found is added by me and contains a found offset
which do not match the expected value):

pg_undodump: error: segment 0000000000 missing in log 2, found 0000100000

This seems to be not causing any real issues, but it's not clear for me if
such situation with gaps is fine or is it a problem?

ok, I missed the point that the initial segment (or the initial sequence of
segments) of the log can be missing due to discarding and segments
recycling. I've fixed that, but if a segment is missing in the middle, it's
still considered an error.

Other than that one more time thank you for this tremendous work, I find that
the topic is of extreme importance.

I'm just trying to continue the tremendous work of others :-) Thanks for your
review!

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210831.tgzapplication/gzipDownload
�$.a��iw�F�0|�J��G3qH�������%������d2I��$��d�8�ok�+I��g����$�kuuuUu-������-�9h����k6��nW������4:�T���1�v���i�V�?D��N��*M�b�}^;r���������������?��Co?�o��Y��{��j:u����4CkS�A�=^o��4��v�m��-�l��=X���:s�o��_����������L;��kw���t:m7{����L��I�ow&�i���j������R}�l���D�s�y!��p���
>���������C�j�����f��7+�.��8\�
�<X����fO������j�w�
_��.o��980��@K�h)BO(��T ��sw�������4,1�|�p�wa��
�M�4m�
]�����������33�iP����>{{%L�v5w�,l9���P�6<t��b2���.�&�s����"w�,>��J<�:W�w������?���	UyF����bn�$n������"4?8"���g	C���(�����.��X������
"s��E�B�X������aSSL���{s���-� p'3��`�=��k�	����B��v<��Y�?�����P��w`��*�l�������x������G3��
k7X:�;u-��>�[��w�o���p������������U��j���W���a�<0g�?�L�����;�|���f �V��=vk��^��0\/���w�_g
��=�;A`�:��}0t:8:������wo��x�����;��x��Cx{o;Z�����������k��������:��������������0�E����!o�E��=��'!Z��Us"�o>L��,Z�q�ac9+��\��3�:D�lo��h4�U�?���F�0��P-�l���*���+j��Q&6��7[;����V3�~�!�&�����������o�3��w���0�fvV���,��:L|wA�g�mbU�a�T��Z��WkWB�3Y�3;���j���Z��2����������OP-�����c�H�sI�y.��P+����H���U#1�����
���\:��$�G~d&�)Z��Z��A�"���0�xv���?��g�"�5�]��7�9?�������R�����wsM��5���c,�a���������?!%�M���C��A��\Wu�y��bX
��p\[s;�!Y
�;��k��w�`�s��v� ���hR�L��*���`��i��A?9�.{�j�0�Mmi"Z[�pT��'7[j�zS�~b��o�n���j�<A�J�!�r0�s�3gk'�ie���S�la���r�{'�>��'��
�N�`i���w�D@Mu�`���|�}�����We.D~01���+/XXp|���"a5���W�=���#��<[����)��N�`[RS��lMD�Za5��ZX�����m�1p{��0S�Sd+�"b\����J�
j��F��Y���+��t�����A�����n<s?8
	D�4A�6[��0�����4�zp��3����
Nu������(�mI������A�mc
"��)-���HO������4����&q�I�xw�vAD���uA�;(&��va�;E����a4���mu����C�����w��z�������u�Lf��:�e�X��b���pv.����W!X�O$�W�Nh"	�����l|6�9�<|;�>�����j"N#���	Q�P�������f�������g�
����!*���x^q���r�����c�n�i�YA_��oq8c��+�I��[K������q]<�A�N����P��y>�K����I�2���oM�I���f��oM;�[�n7�[��%��,b]�@����s�A	�N���\Q1���F�!�9����"� ���O��V����9���h��{���u����s�
��~�y����0�\�Ocx�H^�j�`������rb��]�Y3/p����U*�'�J�]oNV�;���7���m9�����E-%����F�W�Z\���k�J�s���=���_oF��'�P2Yjw����0�8V2�<'�I/�����m�T��_`�>�3�wFY�&� �..��99���`�{)U�>aT�F��c."����{����;{�M��h���[PAbW�q�����N����M:�~����.E��Jt�����?��������Z������6�:�`1���OT:��X�P,C���s�[�n�����M���>t!�]S]�oF�6�?;��'�U�K�L!��ig�i�
7aC����
-��5�?O�����B����������>|3zI�������@Z\����pQE�LkcW
����i���5l������F\6��7Wg"o�W�PV\A��;�:����g����\�+�zp�5�(�h��y����ZT�T���A��v��QL���*u-os�:���],`�����j��SE��3^����<����M��_8xF�co�u(L���s�{@�	,����p�t������}��H�@�7���n�-)-�����'G��c�y�����xAG��pK2�L�kvzfbL�@��?X�f[�g�m�z�O��Vt�� &����������J�%5���������c��7�k��%���������W���7�����M8e�A�t���d��S&jw�������VY���~�!�t��J|�|4���.�����w�mgj�f�����6V��z������.��j��/�[�s_�i�N�>�Fe��x������'�`tc�!�Ty�������Sk+��������YOX���-V0�C+��`	�/��T\����9*���GX\=�����y3�::<z7��u=>;9��w�3XW�J
wk�g4�U�r�����������[�������{�I4�"�%|\:��1�pW�������
��$����:���Ev�S/�0>�^2���z�f?�Q�Q�Tg�����5�m;�j9sP3������>��1��(�8*������T;Q���G�4���`]T��?���*���S����6�
@��\���d@4IE�M�p>�i���#|��������j%F�]�����	C��1_�r�[�������l[�b[������F&[�y�����iA#�v���k������5���3��KVy��
u�!���8��!a�����[�zX�L������XL��W%�J�o;����W�V�(�v�7��=��;@����
?�����5�� >2�����4�m<2�n�����(<�x��<�)���������j@rD>m����K#)_I+�c��}����(n����A_	.�5o��������Z�r����0�����y���UZ=�}�*��K�K�So��j�������]�S������������ ���8uT���&��^�Q��,H/�b�9{0�������#�*j{�]���
�H���?il�����.�����86���R��M��'����e\����ZS��RKk���(���
h��G��U{�F�%��q����Q��G���YyB'G�Y�����t�������D-V\�Y�$�����[N(��#�}���f����89���&�������o�X��Q���(�6�/@r���t���A�����[�#E�����O�F0e�L1���/��J�w��O�����%��Xg�9YWB�3���6�C��l4��5���V'��X�+0���K�&2%��`���Mz�o�9p�w�vW=����[����&�QI�{3����I���f��o�IU�,���� ����!�
k�.�o���@&�A���RQ�������kR��UCkz��ss�89n���;��������a#�E������K�'�[�v����C�1|���!�d�Rg�C�2�M��5�V������L��&�1�nR��8��W���F!���w���rL:S���S�������~�d�J���~���|����D]������"+#����#��1o��:�z�������B���b��l^1���9�8�B�G&W�C�+Q�L4�����F���82�k��x��$�����t��I��F�uZ�`��MbB����[G�4�h!��+��N�~��?v�j�N�\p��:������A���w�d�a��y�<�m���2/Z��I��+l	S��U���P�fO������u�M����x�U\���^�}A��4J$p.e��;W��
lu�a�R�������H���S���c<��t�o
����l�q>"����J�����VFWWW cOM>9d�������2���B|�:���B�R_]hb��!��*�������B�%KI�����X����h�����1��X�j��S%�;�-����q;�4 ������'�7��H�F��j�-��������Vm������&i�FR1WMZ���P��;��px����?	��T	`��1c����vr-���������p�h����T��������&
)C�N�cM����h���e����ps�[OR�I[5l��%}H�C�z�V��U%+���4��Dq���k	�y����)Qy-yL��<����������[Lg����w�s�yx�"���-���t�m��+�Q,b2C�G@�}h�!����
�C(C�=���)"�����+jv�Z\���������������V	���I\,�jR�S� 4��'�I6����33V��d+5��)nY?��[��
��X����RT�!g1s��%�#t��[GD2q��9]JYk�;��������f���:��Z2�i���dJfmh��c�l�������sA.��L�WZ-���-���0e�S���/�xm2L���mMA����z�c��R��}Lo"�'�S�T+����|J<�f�M{��l�],���AO�1�J~0���
Q��?p?�A	�����8��J�1���P��iK��N����1Lr�L����A5`#�
�����Y��k	���6���>p�a��Ogk��B���3g�*��G�8��������z|q~�#v�������	y�i��
�R1R|�;�-@8s���6����	j��k����l/Oy�@��S;����"'��(�VdU��M:�gb)�Mk������v1����/�L���|�<Z~�r!%����f�����of�30z���'�ll��/���"�~���z#����@$��I�B�A�lC�E�����V�h��o����H�� !^&D?b�J���Tqi!�f���iG�B�������1��`�����1R�����!�1����G���m�{��-6����U����wdq1>9~��2�y�@��X�����|-��L5������5n���
������z��v���p0m
�1�f�9i����M�~��^�I�"���L���jQJ�reA.o{�!�I�JI�������Xdo?��~p���?��
=�^�����!N0�S(`�{�H�u�,���p���u�P����K~&���C)c��|�c�/��w��x|��+�m
����������	���Jp��w�m�Y����K���E�f<
��-p�d�E�2�	��������W�2����8w�~�����?�2!d�wxb_9!JA��	�����/Y��0�X�'?�"?�R���F�����\ze/����.9��5���R��&�����N�<��ZE���d
yS�M��v��<��~_~&����ep.�g����<l6�d��	(�W����lv�_Sz���+�]�lA�(��W�W�
��G�|�@w�PNg�m@v�u��w �s�R�*���?������(=><=r}��t4>:^�����3��A�W5�����.���./j�h�����l`4��B3��)������J4�s�s���,�<t�5�O[�������{�r2,����0����y��98-������j�5H��>�ty��gb�pl[�o�w�o� �~<���ZP�3x-�-��h9��1?��9#�(��NE,Kl�;s�G�����20'4{?�x46Ih��)?3p�P��~�
���({������g�4��	�a�7tZ����F<���DL�����Cf+�IB�c����),#IH��J��8:R�4���V�^��[��/��������
_]���b|tqu����v�Y�#��<��BBU��1v"�Z���j�n�n��0�^T�Z��ZU���DrzM�?�}����opG���Kh���4����g6��WL1�55��Hwb����	�V����ZE�J�h�r�H��FY������A�FA��]�c���}!d����z-��4T[���N���a*:�N(����)v��n����4cJ���(DAv��w��Er���� F�5u	��B?8'T��,6���T�<�B"K6(�.l��JF�,���r��zf���L�Fc�������7�8����M�,���~��`�U�O>�"�!�<����{����"V������J�h�5��m��-�,i]�"rG�
��{yo������
�5�o>0�����[]����7�Pnj`��zz�V���.<[o;���o#����d5�~�����q����pZ���kO'��d��}g2Yoe�n�p���"��>�=v���3��\�FQl����K4�u���N�F�#Lu���( ���2����0����c��#�����EbR�=��y��9�>76q,�`���X�P^�O\J�q�c
����i������:[��lm<�%��uH��|5	a��Z���!&e�$1C|������� i�$|��e\,���	W���7�'��
�`7����	r�f��b;����M��b��]�
}�I��5��0��zVs`5-�]�kS{��M��keJ�a#�����;(��k��zR�T���[��@]lx�@�
K���z$^mgy�A�q�m�/R�4@(i#mP��~c=P��`���Hm���N6��o���������q������0g�����}���-�PYG#r�����l9I',��v&�5@A��Od�wL�i��R������F}y��c�=s�����,��@H���2l����������S���y�y��&=�;�S���@����</��
O��h��&`tg�X����e�5`C=%!}	���?c���+�������{a�Ek����l�����O�������/�����C��������P��������L*�5�L�����-�l}���vw��v��'�:N8S������r]��t�n��7Z���6���#U�L���Q��rc�����J5����k���s��T�h@����l+(L�IB&�A������4�Y�������i���
����~^�qp�vU�5�H r�Op�<=w6b�s��6��kA���O�<+Y�Z����x���������������k6R2����pH����;gun�C�\��W���>�3�VVs���nh���6��o�������{�O��{���ot�R��������e{�/�m&����me�G0�p<M��;S��Z/������8�2�������f�i4��A{�����D��
O�T9"��?i��V��
�s�c���G ��i�"�A�y��F��X���^Q�]�	NQ%���f��G�3����������wfp�x|���pJ�T%z]:�:���m� (T�	�|��
�N
��+%RA<R+m�N�!�`|m��_y��V���JOk{N�~}C������V����3�
	�a�!;l=
�p$�����ED	k���}��-�^c�&��7������7�G�����N�����2�(��cW$������������E\�>�_\�������m�@���_�TQU��XE��{CF^�"���M"�b�U�CL�q_N�����Q;&�_����M�!�Z�!n ���4��@����n��m.��V1`*����F����_�^��A���Q�("�%�����:�~y%U����g6;���h��94:���;�-nt\$�~]��v%����V��o�^����O0�hc���d*��M���*���Fo_�3~.�Ri�f���aN��Rm'�����f>H5�u���]�[��Ng�?/{�I�=�@��I��
{��������s�P8u|�B���B*��&������2�z���[�8?�f~�����[��Vm7��'�=�-�����������&��WJ����+����G�og� )�[2�^��G���b���=<�~�e�� 9CU�>�?������J�N��/Ev#^
IG���2:q�9��*tD��M��e�_ ���SU0:�S���^��]�O>)(��S+�Y��8-�������������4K���
�!����[�,���(�BXm��\lw?1��N&a�'a���%�n���
����O����w&�B����(4������I����f�N!r��5/*�9 ���G���v�����D�-����ytf�p����"ck��uL�J�[���y��?�{�~����nS_4dhq�st���]��q�x	�k�e�m�t	��E-����K�I�K��K,~�L�'�V^�'�4Sm���U��|��-��:�Q�=�
��O>,'z�'�'8���|}zq�����l4���f���4���=	��E����k����c%�|3��r������M+L=���,.j�R�yf��JX�:L��rN�z!
���z��nL7����b|� ������A(eD��(���
���3�v��D�����&��6�?���E:oEWp��,b������j>/����)��tS�l�cy40<Z��N��R���9<?>�:~Y�3!�zy�{��k�/����K���,�v�������"]s������e�G1g���lL���vC
R��w'�7z��f��s���g-#��@�a�9��h���@��H�+��bn�O����B�+O������/y78����'��<���IP��B-q���s���U�cQ�)1l9��Cp�ri��c���Tb_,�|���^�.$ri��)T�0�<V����[�tl������z�'��s@1C�4 C�Gc���G.&t�(j�p�.4��������c>jO��p�y��<��:�Lh���_<�M�T%ME��6#��(o�X�Z��p���$,F�E���nF�
�]�\
��'�
�Xg�� o��y�������=���g�:������t�:����G#�6SX���9������'����G�_�[�U�P��H��}H��)�0KV��dR���O��������L�����A������X�T����UI�y`M��f�1��a����7��mq#�qT��ph��J���A_��5���[��H�Y����{�yY��3I���t1�K�!r�����z�Dt%������������RK����LAMN�3���M\��'������1qD�$��& =��GUT�(������w������������;�D�e��
+;
�Zy�E'����M����3T��>��6��[�@x(G�i}��C��G��j}�4�@')��F��H��e8���a�h�#��� �!���Q@B�?�w��VVUP���-}��%�uw?���_���`��/�/JA�m=�t�x����Z��T����b�2��k9w��1���DkjL1Hjt�38��zc�}�zGQD�@���� &��,�~�R�E/��G�Q��5�_�
:� ',Y�C��K����E��p�	���N�Is!����(�� �+^����%����"��!@��d*�t*�u(�v$���vQ\��^��,���U���iL��k�h4���������`��F'�*L
�.)��v��d�����h)�w��m/�t�K����>�3��g����1_� Fhv0�m���y��M�-P�g+������\��%���0/���]b���W��fH!�����OX-����n.���|��d�SvT5�i��?�r�����3�~Y3�\��6E�'���mtu1��Q���#�#^$��3fIr�1�f�������ziN&�v��]J�$�������o�8�9�W�H�$m��G�I���P,�7#��� ���^��1�t��HV�a$���Uw!�d�F
�X��I!<�	?��;������
(W��<u��gFh��z�T/Q�������Y%^���2��p.z�(V/S�;uvz�+��d#g����~�=�.��"e����wfl	D���1�C�O�5��',�����<������8�#E��2���!(�Hs�R.F�s��!�
�9��0pVX�+���ZG�M|. ��,��k�_D�hL(��#��)9�����!rT�r��u�7&�����v[��3�#C�&���6�_f����d,�H[���������'�3�F�%aU�������Q����O=
E5�7y�&�_���(}�ja�_��I�*�$��I��L�b�u+X��-�	��"����8���2uiF���W.�]�]g��i�1����?���b�#�NV���a!���t�NF�12%���yD���K��V���DJ���c�63������E��p�R��d�z��t��E�c�NNtS	(4Z2F�r|��G�!��'D��d���,����D"��/6���g('�i���b]�����Zp)%�QvKl������S��k�9NMS��a?$�����%q��}.���GV/�$,P�����_r��$z&�Q�_rjFp��	�0��?(��2��3��]�/d�Y7�#����(SR��_��\8�y�������J�*J}zi�(��~�%���	.���c�p���@[Xt&�4����Z��k"�"��;0G!�W�d~)/�#�2`�a�C��5������M��lLe��zd������)�j]eh�02�H)0�u"`��,vtJ��YU�g����G��	2D��W����:��z~tx3��������lO��\c
�T�&�O���
�����[���@�|T��"	&)-G.*,\�[L#��g��{�\��EQ��vV��WD���qz�c����ew�=����yM���y��0��".��CIa������#�>9���K�Q�Y�\��z�>7�9;�I�Y�����t���V�g������f�Y����������O<T��,HC�S�A�TG��1����~='-H��D�oP��������[�1l�qZ��4�Y��7H$���������5����A6�HS�#��|��8����I^��������
����lwu(3��������C�hK��v_�����w��q���/?Ot��D���)�������q\�D��gm27��lL������K��M�T
���L�Io���i���8�����7;����:����)�3!�h�.�@�<^�$c3BU����9�-�s�>� j������}X�E��O,>�3vd>_����y�W������J[X�|�|��C�>�~����@d��T�
M�������#��m;2����N~A�|��8�D��������=�f���-�--�Y00��t���0�������/-�6_��C�x��c����R*��e��m��F���zv���.j-�?�J�����2*�%��%��������!�nR�:hF� d�CB����}D�x��(IZ��So��/C�d��*(;�k�(�G2��j��������s;A��u��y���dF'��r��'�� Q@',MJ
Q�T`�^K�;I�&��D{���c�e	��2�I�o��(��|T�7���1���<
����F?�P�>���9|��*l�z�}nl�
+�:!6+kS�i�[9�h^6��>�P�P�f����-M?t	ndrqr�B�r��)�x	��n��!����mC`0����t���u��Mt'fpp�.��)��2��'C�}T>7�<��[��Q*�k�k���4����6�*u�_�����A�|��� �l����y��})���9_�P��_B�D�b�]<,��!C�H��I���6l�{��w�a�]9Sr�St���#!n���}���M�����������V%	r��'�|��F��~_����>eW�IlEs�[5H��_On�����-lm����a�B���N��Z�C���2ld��oj0RZq3�y��,T��8vf����	f9s�w+Z_���8&;�/L�_o��6r����_��;0�wX���nP{�?��V�UY��6H�E^.�X����%;�����������89���F��cu�y�����R��;�	��Bo���j���#����v�����p���Ul�|'���`i�{�;����V�+.��8���7|��:)K��WYUz;!����@�
iR�`J�����qT&� <���h����L�!�
$[F�?9l����G�]�!]2[b�k�p#�|�&���ke���
��l���AH,��KW�,�8�9�����CD���]�0'ea�/��0XGw�g����@���LE�p^��l��a ����I��<1�#I��"`/Ut����cK�OM�6>��kG������H�]';�U�.L'q"���W�Q-�x4���:<�//�o�^���r*������������&n;Of�^����,����Q$�����T�.h�7����o�m||uq9~������7�`B�.N�#�L\����6[���v��jl�S.x���?�����5�DE�`����c��%�ox�v��U�Q�[��Z4�]�x���x����������R�56�,�@;��2��dN�Z���� -b�B����x��M�,����'��.9 (Kk���Q#��v�����N����/�O���iw[]U�����*OM�(�Q�WP�i��$�����vO��|�
��%�4�>A.9G��V����
+��Ze5�'���&��<��rd6����>�]�g<g�MIA�F����F�8yM�W8�F�L�y�
�zT�
I�Y���=?����y�p���p�F�F
8|�����	������3����"��V\�����K)(�����C�D��)���i�k�!k�[R��.�O�`�H{D�/�J��}�o-��a,+��m*�@$�� ��"t$����g��%��gY
�H��D�4��5���7k8��8E
NQ��<�H�,b�����n�{�&���u7-q9y���L����1��F�fs��N6�k��[{���%#��$u��I+���X��B{"�o�(�)���k��b�����)@�����`5���#H����M��8�m:����t�(�}F_v���z��0��*��uURU%�J[��j��T����#qz��J��hG�2����^	��M���Q\�hTv"8�����'`/GhL��uq��"���4a���2�h��\1�Zj:�H��w���������,����5{�����mX�N��Hy���9�<�Z�Q-���Wo��5���LuC���3���O��@�mi������c��+�mk�p2�ab�jZzZ���a�d��7�g�d������xtuuqE(W�Z�U(��X���@����Nl�����>�~$�Q)lSm���2�Y��d���N��k4�`�1��Q������1*�A��������h�r��D�����Q&���2
�/K7d�Z���QQ4���6������	�'�nA�	y0��X��i��;q<z����q'��h&%A��)Y�WQe��B����r'��O��S���3���8���8���f8F�����i4L�2��~kZ��(�N1~�edJvN��X?BB<HEQ���g�N$�%��0�,;f��YE���*`��NKHA�5�JQ��_��!2NW�q(kP`�|1��zD���C(��D���p�I�'?��xnKq��QW��	�3{j�X�y�`�-���6�~����Rx�%!�{J���,�������/�>U�S�TZ=���v�s��i�p�k	N<k�u�<�X�(�I�pPfS��P���r����3�-e;���
���[*XgK������W��*���]�Z�c3��Z��V���X	x�St-3�4M!�V�+��=���="�C��[7}�v���cN�2E.���x��Z5�T��1]g�/�$:Er��Z}���.���`��Q�,Z�PQ/��\����q�_��R�j�r��&{{,�3������
�Af��a�SJ�A�b��@&�����Ng)`�����C���d)Z�R�}��/��O�f!�a� l6��w��`y;F�q��t��=�=����+-$w���4���Z��cu������c���6`yA�}��[�}��[Q��B��x�y��-�������p���k52r��@��U�$��Y.��S�=YL���7��9u����q�������9��c�����BE����AC�h��{����7���(���jq�����
��Yb��[
)�p@�Q@��U'������\���6a2��q>:��q��xt~s�������7��=>�q��a����+�&
��B�7�/O���;-�2,?����$$#	��s��WV�,
��$5p���t�?�}���B|�EBKv�u�
�y�hRI�R
.����|��]-V�2��()jQ���2��"9'�C�j�&����������m���Rx�N������+��b}�jfl�L�3�<L�b:�(<$����h=5BJy���]-����	�.�r���V���?M�� E�J�#���<���'|p��%���S+�X�X,8����R����Jq:5��hW5�����Vr@eT��PB�����
��L���<�8#�Ha8���~S~/EG��<��s�4����sZe����C<O�v�l4�&���c�I^��d���2t<�)
2~
^�1���C�e`,	�/���9A�����7��ZK�:���tsq|�B��Tv��� �g{"�.��8�Oy0�1S�.Ha&�����$�f�U`?8�z��pP<N�A�#%CwE�`���|�}M�h^�H?��$3�:���kb�T�5,����2x�[����|U�E�?���n����Zti|J����R�girUd1�Zuq�A��^�g�)]b������U��_��x6�9��t����fX�f����a� ��f�~Q�l�u6�~����n�����p���<-Y-}X�i���g���g�8�l��"m}dXJ��D��K���8��[(�Jt�d���'9�_�~�����1�<����K�a<]�\k71EG����)�c����Z?����������l�w�(���� P���p����8�N}��������4���mw�I�i5�N�,�Q��H���d��k�Z�>I#�����6}��@�!���z��1�����P����A���M���P������x2�W>�2��I�t�Q����Q{��������"l�:_���$��u�=����z����+�%e���������C3�8T"*��G��M12�/� �����
�����=�Y�s���n��|"�^��������'NA�o�
�9��:���o4������vz�ZH!g�-�.��E~�#U�b�v�`r�@�>�uC`�9ss�S�|�$�}\�k�gt���U����8d0��o�w����D�cr�yr,��t!Z���e���nP��@UN��(-��^�?�t�b��"���p��U��T�9����{������p�
_���)4'�H.0�PvK%���$�N��1Z����[=s�iw�"Q*�����*A������@_����e1 	@�O�a%j�_�]h�xtsx�N\^����\������H\]� NG��N����������!���7�+qs��t$>~�(���pd{��.'��QQl�J�zs�x�GPm����?Yo�Y�|
 ��q�l�5#+����tUL�� $�����8�8�<���?�����]��?�I]�_�\��pC�����??��{�������������K�G�#�#	����s*A�*�Yh���"ycl��zE)���m�v����q����S�O�O�Or�$�>I8�Uiq�K2���g�c�w�c�/+�K6�5{S
>.Q�E)*��x8
�OA�4jD������)C��q�le;*���\���������$;S�X��cO��e�f8�19.k)&=e����3������,/�"�dd�w{��V��7��(��eK��x�2e���t�J/��\��.j��V�J��Z��Dv�yn�j�<Y��L�1Z qb����������Xhb{L-�����I���Hs�^� (�#��t6��7(�V�8�#��
2��3=��'������
�m�vr`��V!gmr��qj�h#N��l5h�#�xQ'd�S�k��
��a-��9�AM�F)�oR'dq|�}`m�RK�j*�i���������X�cc��h���J@}�e2�R��n/�rr�����h�d���!29h��J�����j6�6�!�~wb�������Tz�D�H��lw��:C��i��;&���Hk��
H�1.9���vn��o��2sb��@��8x��c����m���W �$��a#9��
�������*��'��D"M$p�QRH�|�������[�J����lvCI�H#qbk�S��_��lOi��M��i�Bm�j����V�����^����$����g���)N��U�H�N^\�\�4gCq���.�
nv�I�n�Zl�V�m4�����W
��oU�MW9{������)B�(7��=ON����aj1ht�����}��j6E�$�&�k�}�^���7'	��%�����AGfNm�;dU��3�����31��3��k��b��g�|�y����n6�v����c<K%{��74�m�(ED"�Q�������@\-3�43��:�����������}L��f��.(%7v�=��i�R�q�����<+�u�ewwQI�"}RA�G�I�\�F<�B�
�h��|��]������B'�lz������ �DsV�L�a8����bT2I�D��-�}���v1#)�����1�+�">���R�<S�"��l�q��!:��h����t�����5��b�q�D(�D�k4������Kt���.�	Y��(�\�����m��Y�;��I�rii/b�2R+����SCCx����:�����j�A����u����c�wx�n1���M���o�c?m�?������]i~�fU��+*���?Q� =;|�����_l9}�%YF��.+��J�a��e�o����G�)��Zrc�x�� �r�5�?�C,~Q��G�S@oI�MYeIQ7h&����j
>�%���l[�����4*��e�zb�A76��h����_����'��'��g�hH����EP�&X��<?U<9n0������E����5����zJs7�Cp��g��CYp����cH����r��&��[���V�����j;��L�i���ms�@
�)=��a��������/����L��2�[��
F158�i�!���Wtg�lYb��
��o�`�d���<Mi��7
5���I�����#�7�87n$�|�F$�"pB�:f=�[���[D����@�Q���H�/��6qp���gD���k�@����'���D
���]���^�j��������O��Co�ZY����%^�_R F(D�b�;�eI�
]���9�Ve�	����1W7�i�M"e*���2u��Fpf�R%~����;N���H��~����������S	-�@�q��I���H6�2gE�7��7��[�7�L����k4Z����:�No�y�;m�����wA_9(�${��O^���Dp���
|��Y2���E��$9����A���l�F\�&#��o���3��$TC�t�D��7Y���M(�p��������4���7"wG���76�q��"��+<�������OE-�g�i12M�S���n�������2��LM���V��v��=���d|y-d�_�������Spk�_
c��ED*����#����8�;#�^WC��[%�'�K��^-�$�i?i����2gZd���&2v���c`�E�z���e���KyHk�N7��EF�K��G@���`n��7e`����#�S�H2"V����$����u-*�|�[�O��d�����){�Eu���A(w4�&<�
J:AHHV��W��CM�)��A� ���5���5�-�i;�f��7����:�vv�l�r����E�N��QQ�Op�N�bW��7Gs�M�H|F��V8��9���� �Q��6�C�����t�6a���T���\������Z�Px�=qq.L�����3������q�J�D��I���\�*�5�6������A����n�E�����&���T:�����������[\�n�� �����j$��G������_��U�r�����p�~��Q���OhXMEo���xt%^��7����9}��������c^�_�y33}8-�� S���r|���������mX�A��i_�=,)P#^
R���|7.G	�S��?�9L7�V��>pNo����F���?����e�
��l��PM�z����o�%��J�����Z�t	kwuqz�������e��u�h�G�?:�a�����~c4���������w<9���o&���������Qo�"���h��,M�T�W��:�~{g�t���N��s��I����di�cL�Ci��[������v��Tq"$5��<�,?���'�+��(����p�,B��pi��X���d]qA���0ut��@�KE��At�G:�YUF��SM�!"�5����2�P����::��ooN�oN��ql�F���%��s�%�f�6Z�hl)*�^&��� �W������7���i������q������  U�h6�O����~%�?	�={����3t�O�~��l���.���%��}����9{�Gt�rg�`h�cS_O^u�<����?�C���.L�W�3eE�_Z�d����
��n�1i��$eMi���)j[�������;���������jqp<d��j�_Y
!�r�O������kj��#������+0��8�G6�����6K8��������p��	������xq���r}�}�� +��+����Z���dg����1ss�������w�z
K	]�Y-�d)x�������~�B�_v�����uff+���V�m4������O��Z�:v�A�8h6���C��VK�������i�>����KtS���&�"�F�k���������c�N�]K4[-�l�C4�yG�VhC1��g�{�D}�7���b;��``9�A�2��)H(���6�vw�1'��v��iXq��v�����O�`9w���pz@:�;o|0�7��������%����&�h���(n�pu+�`�_�{/�=QkBc��+:�^��.o��:�_���hI��c�T����7^Tf	�G:����q\�(����������8[��q��d�s�\%�s������)m	�O�2�g�,G�,&��] ��&�+���v
��7*i]2"Z_�%����������%���N0�W�8���})P�@��r#�T�Aj ������� �pS���u�2I��SI�w�������Fj R��Zo��Q���D
vw
���r}C���J�����l||������c��K�w2v��&nW��=x�R8�D$��g����
��$��@Rg����R"#="�4�\}��[�F���P�,�-����o?�qq2��hT7�^�9�v
�G��*��
�,FeC�
���bbD����^Z��*R��@�0�k�@Eu��
W�>�Z�.ZC�������nq����lXP��m�V��k7���2Z�t�k���o�`6�oIa*���>�8�SL�s��en<���$�!D��{�OS
)Zl��o�y:�����cCq4�ac
��[+x3�^���AD�c���������F���*�s�u>�7��A�LBt����bG��8�B0\��G?��[$���~<��;\�Jd��g���tS
����w�N��_��1<y4(y~a���>�IkE����]�x�Z���^�<t������G5J@P��_���?���/�6���3�4u�*�W��V����l���t8�3�\����UQId�%�����8/�x~�<����M����>��"M�lo<����8]�%�M���E<�������z���6���	��(�-�II�(� ����:�����<<V}:u-}83�L���*���V@��Q[�G�I�c`oe�H��	��44�?�g�B�+L
�{@��a��`��[0��P!bDJs&���}O����CpQ��`>[2���s�������qb���c�&r��S}����I*	��8��~�����[�)�0g�p��& 
$'������3]��<�5���(��GM���G�B�����~rpA�&�����V��uj�3�6m���08 �1�=���#����s��JC>Qh��#��.��V��)��p�Nj3H��3b�(N8u>q��pU@:�3��%q	����<�#s����e�
$q2Q�l�e!	��=�F������]�����]��2�[n��"�XI(
�?T�][N��.�t�>k-9��)�<@)�R}r
�m���_��Q��:�JI��G��#���������vskA����A�~���!	<J���a�HW���^�����#l����J��B����z0�&�B��AN3�Lh�n���� ���T
�le	J�a��9�a�|����c]f�o|_`��)5����������S7���h8&M��a����!3��z��#������dq%�}��g
�r���PS�G���v3:������#O������������o��T,$��J���i-9��6"����R�g�DUi�F��
<��$IR}�$M�A�B��$ImK�e1C��~�'@Ap�9M�KA�d+_!�c���7�A��5+5�H�� 2�+�9�>}�/�����%����Kb�.D����$��u:s��NX�RI�d8_6��}z����Xz�����C^�a�n�����c��c����@oe����5�99��G7���WW��(F��U;�SRbnSEf�R��H�O��d��[`&:�\��D�o���W$���8���a���x��~7l�w�6o��2sG<���L�Y�����J�_,�E,�R��_��=���0s��d\���Y�:����duI����n�jGK�s�7�j�`r�,(�L@��(9}�x�J��!l���u�W7?���z}C�OF��O>�bR��)��������f|>��'���E[������������{�|vx�z
\������'V,F��Q�si3��j:h�>���(Q�*���3��d�'��,_�����v��7t{�S����%-3H@n��������vwJ4�|���������W|��C]/��,~>���j�����p�����{�������>�55����-�|]�M������ ��3�F��h7��8��FaHv	��s�= 3���.���|zx����@������b���
#������h @�PP��%��6�`������Kg���F�Z�$�	s3�c�7'�����XBV�T��L��EO@�$��"&>��m�
O� ����a��5�c�dVc��;@�_j���	����K����E������
E�����������B����y46�;sR��J �\��
�@89�!Q!��K!�1c�T�<����%��%�`��^�o*�P<���T��n�D�W����"�����.N_�;����c2SW����4��PC�-JUt�E
X��[p�	����	E�(��D8)d���<^L��vB���M��-"�f�#4��b�������J9BB�8E9�$�����N�X#���$_�����A`K�>���S[R��M^�`:La���p��w�!����UZ�!��|�����8.A�����<���d����&�;�#Pe�����Yk�b�,y�x��Q(&��Q$
\�IN�t<��`�bg��r�p�8dA�@r4~\���
�"�h��X���{;��>�f]�j����������Q\+,�>l����O9]��
�>�R"F;��xt�H>848y��e�R
����������!u���~�&U!���TH�����F�"#>��j�����O�����k������<e���Yz�"l�BG������d
Js�^V���f�AS�$���F��H��;�n0�P��oI���w��E�fG����c�t$�Gj��o�oI���[��E���]����!#c��4���8g�*�"'P@��w��&��$@���c�#�[�W���'*A�������;bxH��;�K����"�N����{��3��8��Mk�)�-r,�}��}	����*�5E0lO�\Y�J|O�c�<�P�F*��-O�=&�l0Z�'��g�:6��x.t�Y3���L3t��)Fn]�	Z�K��xrN#w[����+"
���#�J�	�m�7�J
�/�
��z��^���|�����XD��|����a����;S#�����x��)�)U���ln~l(p�m1M���78UL�L��������fVz��S�u��a6��)f��u;��9��RA�mo��V�.�M�d��.R-�T�:`��*�>���<�����U��E*u��swfRxOe?kB����h��g�����P�����Fy^Z��7�d�8�QSy�P~������)��Q�VwRS�9NW����8.�����|�-���	5$'i��
2s�H�A�_-WG5t�1;T��R}���M�e���;N��I��vX������.-�@�3r�9re,}qc��N����O@�{��V<amou{��&5�w$�j�8%��{��%B��a5�Y���_P�4����f�.����������;�~�������j[i�rPy�&-��fd�� }����RZN���D��43j�&-���j��������#Q���c�#�,`!c���>o����~_(?�L�����at��W��e4;���n4�-����S������)>/�2�d�E���6�
+mW'�����tUEi�.2��,�Sf�^�s��l"�&`�)<^��
��f�gi�4�*=B���MOo@���c��������S���0�b�~���=���S[��������eU���[m������M�]bM_���ni��14{����C��N�����3�K��,���F?�Rl�X��ubQ��b��]��<XB���p,����Xcs,�
�$d���D����%��]eQ:�$l����~Jg�������6�5�$_S�Z���tU6�"��&����e��TP)�����m�0o����q����-J�q8�)�����U�����6`sS����7o�F��� �i�i9nu��
V$�V�����H��=��F��K��$t�F=�����/��+��f6�P&B���}���I���8G�o4Z���g�;y�m�l��.�w�2�ok>����A�������6|���������)b4#�%7D)��}�����0����C�x/��F��*3���OhU����GZU�rP�o`�C��HU'NO�����.����F��S	@z�N\z���/:��h8��������tO����>�T�v
��j����b2?@����������n?�z��z�������`ylk��5v]��cn��a$af�r*�����M��T_�h�wR+�$�^z��d��:��`Pb��:��i*���L6��8T�'����]���s���F�����#f�}��'�jO:(�$����A/�Wj�>;�r�F�$���(����� ������z��	Ne|���f�\f{�0�9����P~5"�D��V�T��H
���g�>��X�� ���0���bf_/�ep��c�g��_"�_�B�h�B����Ah�m$9����1Z�<�Y���]r"��~I��['L>�Qy1A*���g�t�_{0��t�Q����]��`s��4���rf�,���h��0���'���'�N����C�m;��iBO"D���R�Q]���y}>�_K���B���J�v@y�r|���q��F������2K�-?%��p�Y�h�O��'
��P�O���]�F�-��$�n[y�����	x����\*��M��������:�-#>%���2��~��M�2�F������(�S�vz�=�v:����-�5��	�XX����u�������B����J��}l[

V6���8.U@����;����@�*�v;�!o��]DV��sY0�T�.Zm��w�<�L>�y��)~]9+�#6�6vr��n����������wM������7���T,~��dP0@B8��8{%"��8�����������	H�
� $����}������A���F�;���z��_y���`�Mr+���h���h����������#������3'4�	�������f�+�i`�[s��;s�d�4���nqB�q��I������t���8�����~��&�q��0���bu{��4v	7�B}�hd�(�	rg2b�:��:@���]�#����
���I3������"� g�]���(�9�����e�l9c����+U��W/�N�jw)�55-w��.4A�	
D�;����!G�D]>����#��8�����}oH+i���7��"
����Y��*9I2����=k�~�1�Q�4�D�]��
Vg�,
����}�J�c��R|����9kN]S\�����"Q]yH�;���{����%l�E�`�^9����?y|Aa�V4��.��������+Np0?8:9��W��������k���t���~g���a|���w��nFG�c{��!���W���tg�[��.�}vf~p(bS��me"��z> 3���6@��8aE�$��w?M�~����p�'"�E�u�F������^*��p]���f����m},��H��5�j�LU�	��T�P��+� �?�)�Z�:P5����q5�'D&b]�bXZ�&���7B�b+�{���w�_�M�o]]L�
�4^�V���	��a;c�#'��A�0�O�_��n�����&r�^.
��� ������T�~�%��r U2���,����jH���]>0W�������F6�!�A����X|�Zj�����z����:�t�u�)WI�Gm�YT�Qf���z��������^4�Z���)K)��$��M#�n2<�#$j�e)�,7�JN ����T�-��L-^�=�R������?y��D��^�_�{7|<x0�pLB:=oe����6��~����]=�7��v����Pn5
0g�J��K^8Q��s'J7ke��NwAs�'�#g��E��a�����<H�H����c\�^���'y ����C�����=��^j����"53�������������i;�80#���F��'E�mUc������Ts>:y�%2GB�Z�5w��W<��]N��A�2&�Z���v9W�&di��,s�@���������Y�~z�U��kT����j�w.@�v&p��sgB�&fW\�n�2u	��E��4����Qa���_6���!O��,�f;� ��A��n�����F�c����K��o�����s�3z�q����{��6�$m�3��,�[�0s����Avi
����ZZ��@��R��tw��~v<�/y����Y��5S��;w�k��	H������9'�����gy-�"^���W�'��N��W��X�����k�Wqr�HU���dU�����k#[����T�6W���W�.���Yz��c6�L�:F
�ff�W�>�+2*U/��Z
�������(��(��D�H�T,��o�����Ht�}�����Uk�����E$��,g�����(6���k,����Lg�=O$���5QT�	Mh�df^[��3t����fhi�}mg-Db��$�e��wv���o6�G�����_�0������������Y����+��Gu�l�V����W%���'�����������>�-��K�{�
_��#��8��o�I���D_Yg��)��������++�����x�bJ�u���z�BU(���CQ�����i��rWV����#�s�~�Et���\����K�~xs�>=�����(D�A��>f0��e�h����(I"��Q>D��!�3�J���b�Eb�fT���p����)���	�fF�V�|~p����u.5�������n���_l�mn^$���[��v��M9��3�9*��������|4g�.���A����e|���]������	]����4���N��3Y4*	�Fx�U����������n����`����/��9p%���r�6�L�����|�������Y���KH^��"��Hs��QZ-!hw'�H�~6�U��]��^��^^_]{�4-�1����0z��I��
��g�5|��M
���^
~�_(N�3���~jF:Hc�%����o��{���#����!���;@�2J4�cJ�%v�E��P�PX:�$/}@�h��	��=�JD��IFgH�~�<M�(�(}�Ws��q�]2>0Z^C�*��.���~�v�!m=���O������c0q�9e��{��9��ptpL�����[�������|v��q5*�-��`q���zS_0�Y��d������R���vx��T5>	���}��F������ �����F�x��	�����K��qf����k�^k�U��=}��r*"��i�f�����Y����L'���/����Y�;�p��
pD��������x�m"(z��(Tq�`�!�r�Y�3s�?��~U����4����������%
FN?_��������FU���m�#o���X����Z_Y���\�X������W��`���KW_����+��%
}3��S�.T<6�����C#<��L�����`N�XO:+!�I��LB._�����Q���{?B�H]Q@���@D�o��.�*��5$�!��.M�����
5U��]�����F~�r���(�eMbsM��M��O�M��k����E�����6^�����k�������t���2�&�.�/�"$��S�+�Yq&Pvi�/_�������R�������8�m�0�����^�N�%���{���Nweem������X��|U����^[:����A�DlV_Zy���8g����]��\��P������+��������W:�g����<��&�1��M�5���/S%>�/����b���Ej&��q��sl���n����=��U�,�`rS�d�'�4�WF������e��A��k)^�%�@�EwRj�b����
�/�������oWFpa`������=H��D�?D�2�Q\	�&c��~X�����s]#�=�KU(/eL����3$���2�/-v�%����)XB�|�W�6�	:�LG����z}$g�;RJ��q�e�F?b;�ti)��E�����rG�+��B8� D'y~����r/nl:z�s��f��gT�	��p��\;t���"���luI]�l?9��N����5��������HF��:�I�LL!�~f������~�XY�<8��������$�~�]{n�nk������M�#l�E�`�=��V�����QhAty��)k�G�RJ�����@�Z�|�g��U\t��E?�S��\�F+$h�2��7��U�����������.�_���]��-T���V��d��H�BH�9%�Q�����Eb�m3����
�aH��+l�Dv��K��c����	�)�l���q��/�Y4#7�E��/���k�;�5���7��6���������3���w�{�;�����N��>j(�a
��W�����I`�����fpWF.$�J����pWd����gg�0!�(���K=����>���9���\��@����n��:�;mE��	��H����@c����>�Dm��Onr�q��u�QI���{��\^!������������'\�R�������h�)��)�p�Q&�>&j��Of!���X�3��G���=q�@��>��`x?���?����}r'\?���E�\��B=�����1���_MW���$~s��^4�Wg?f���7�d��ta�K���D�
��_�;>�l�"�2��W�����K//���E�W�[Je"3jj�$���~|�7p�G����9>m�k1p��\Y��D2:�| ��6�����q|���a.��3�U`~��)w�	A�DeN+��d��RrE��K8�SB��Q;���^���Q���Xp��ry�����f@�U�^�����������l��k��
�~oN��=]()��r�6�:�-"��&k����R`����u�����BW�3��>�v����}*����S�
ps�\�0j�Qi��� X_D��r����������Q���Z4��&#;�~�@@��~M��{�{������Wo_!���O�����{Gg�&	AJs�
��0=-TI���������"9���D�G���R��S}���AIT��L'�b;I�+�0�O�	14�=�����+����{H.�5
��h	��k6
�eE�'�B����3C��{}cm��1��[��F|����w�YQ�B���c-�|Z"T��D39n+@�\
l��H|��oq�=������`������r��A6:���3UN��N�i����S�tSJrt������t��s�@�?�dW�0/�W�B��rO�g������.#_��;�VEU��=�0 h��+����%Z�Y
[��!�D0���2���T@uN��C��C��?��S�
��Q�/R����vv�p�����/�^������Y��f�X�r��/-^�������� ������ GQ�����=}�3�W���p��Ygo���� ��������y�����P�f6aI��T������������xee}�����v����t%}=h��v���.����6X���Uc����5�����h+�7���-
�z��[Qc�q�G�9P�wZo�>�9��}!����|��>�5��
 (��GP~���53r�en��>X�c}���@b��O�:O��E�S����z�B]�v�����u]Ka��T5�BcX�76�kf�76�E�'F�v�d����X��\A3��y��|�y�<�����-��0w}i+����*��b0��*d���S�u6����w�1oS
����c�!�j�*��!<���ru�e�!FW�n��vXx���*�����}2��l�U��p�"���7�xf�����FO�N��������[�����p�h��v����*'�V�N���N�<������ ic\��6.m�n`^;�����Y�
� ��f@��.���$�����K������9�#*�����-������z�_O���v �F>i�z�J�2,)��5��D��z��I_@R6b�h"!��Rk���o�5�[�?s=W����-�h/p_V3)R��$��b/@����UH������Y[�}b��3t#��d�.~��WOmHus����>t2��w`���Vk���j��F��E9��������� �J���;"��!�&Q.�{;������_h�h6��g�@^�W\��������]I��F����9#���>��n���R�!�V&��
��=���d��e��\�������#D��0���{n��wp��k�&AjU>�]rY
��b�q�(8�<
��6:���I������D9.X6M��TF2-
�VA�@�A�C���Sk�^]�s�`V�}�����9��JEdQ�@R[d���&�9_F���]h�t���[�Ji��-�O�L��Xh��&	u&�.��������Ts9�pzv���2�J���@�='=���z%j�l� Rt�3��kQ|����s\h����#[�QH��2�#E'� ���4���W���,7���B|����YM3O����ify��b�E�U�]��B�&:H0��
z��<��V>�0���JV������T2����	��%�\��y�����`��"����F���N�+wbT���#�|�n��n�K����(*J�}����p	���R��Gs�| u�����$��&V�<�4k2%?E������[���2��5T^X���$�~��JQ��_I���Cg<M�����O�X����dG��Tf��Y�[�,c	 ��^t��Aw�D�T�h �&�f8b��n����<�����.��LD���X:�w������k�|��jQ5��BZ���*���1_f1�Q���y������������F
##y�I�KBF?��o�B�&,0|\.}�+�R�M��<�3b(q:���(U�	9�F����m���N+}��Y�|��h�8%�K�Y������vX�Uv�(0_��7�U��*Z����}uO��=H�����q�=���Q�V �p0�D�cw9�=��\I3M2�4�9��`�Q����5S�Z��~����`���K�u����1�ov�QG���x�������-��&DC��z�_��3\�
w��sEM>�C� �!}��J����q�^��n:[�7��}��n{���\
y��	��k������+I #9�� �wI����AT�ux@�;��/
R%-T2ZY�PU��#:�<�Ai~iV���(yp8��L���t�7�m+�E�$B$�Hv@����k��"�����^)�o�L�6J/�_~<Rbs��E��yhHU��W.Y����������������~���6a;��7����22��x#�����m� A���*��g2���?PF�*Z���P~���|H�y�Y��UL�/�/7w�ir�h>����=v(������@o�O��|��f�9��}�Jw�z��$�����D<������r5
�}����w�pW(��|6q�����*����B[
�F	58��B-��!���o7k�m�n��o������b1
�A�H^���HT3,
�����)[dD��iRh�$R��� �����q������l$c�ZB,e$�P��B=��b����B���m�J�����&��f���:)�K1�FG6�=����CmFO��%{�"gD�������G�'�HD���*#��-v�����NC��'����x8������Y���_�P������J��#]r�KI����Ke%X�;�����Y�����!����#�a�p���e��A�����0g��lm *�q[V��w��7Ur��j��=-�Q(�������}T�4�}@�E�Jy�zh���_'�?:���Q_p8Ms��3C�1D�2��p���6\D� �N��B���:�zvvWy��8ZT�t~�}f���U'*�?�4��N�J@��I���XPTU�d���{n��Q?U�AWyF�O�K"0[��������TX�l~�����x
��x�1qG�@���E���f�6_�x}CIT\��U�J�"3GVf�����i�<��X>���$e���Q�����P�J�d�t���u:+�S� ��H����zG����q�v@�����r[b<1�c�>�^J����C;yU�JW�������`������M(3����a
~�	o��Qn�g�DP����[��A����[c��R���������hLv��U��$�rZ��,?����~v���wz�>z��J��+f����G���^��^���v�#�|^���D_�R���[[[��S�^��~�[�R��iB�RMjee�����l�HKh#���+��\x��)�dq�L��x>�15�,����I<^��icX�4����'Q��
�Z4��,��)�@N�4d����W��,x������j#���[J1 ��{��U����{L|O�a'��O�d�g#�p���zFf����TliD�+�?���!us��R�X3�4��;�0�]�����3����i�b`3�h>�,q�r�I5��������a9�d<����$��q�JKj$>�Y�J!y�gZ3G�������m����d��(�\"UG�H�d}c0:��-��y�x3�I*� ��O*��K���q�u���7�g�q�D����N#>D����� J'h�J}�w��t��9m�(y�M���ieP�\3r�5�_%L�����UZ�����������i�����VfHGC���?�3�P��0�VE9� ��E|X����iK��s6����;���}z�g$�2O=�����`�A�1GQ�M-�$6)������,)r��-T�I��!\��X6Q_q�
��%�s&�RL)��B�y���3��?	�M��;��?�/I�	e�b��|�s+*]�{��C4��zd%�E6{�(�P�D�`��Z�������v���3����K$t��Q�k����Yq)�G�JD�o�����$?_M�G�I�g��nu����mo����������GL��(��/OLNBif�O��sSk�9�Y<�m�����>�{���M��$nJ��oB|�D"U��e<����J�@�&_�w��t$�wth��7g����-�`��NO?���58j����X����d�^-t��ezp�i"=*�W���;Z�������iU��b�8����=�� I��@k����L#�#+U��.'d�,�w��j63���N�]����D��7Xl���!fy���4�:x^��g�����'A��m"y,�;HZ�Y��l~��*��prm������k�ogDg�����Wp�Ls$][�L��8��H�9��	t�&����~l4�>����/�QG�����D�\���_�!�uF-�=c{�����.����,��a�a{H��C����X�q�O�$�AcJq��@[���6nwU��������oY�(��[Nk��L�8������c�.�}n<��F��\}��j�sz���B�����1gAi��k����s�X�|�tgg�'���\{`�����6^J;�*EFk"S��_T����s�
�}��K
�����<W�pJ\$ABB�<���K���R���3q7����X	'%@���D&bz���go�""�)g��k�b��M���F0B��#����W9LAY�@����D����(]��h2�b�	+I�:$/��G��H��ui>����p���)��\����U�E��{��5�	�
�ip�k�8�g����  L�G�ML�?���U��������W�0�bu������{=
���+�HH��'��_�q��m�GZ=Y\2B�!>0�
8��������a��1M5����C��X#�i*�`'n�bl�m�������������#��uNZG����V���hV�9q�7��Vl9?)��a�vP�����b�0?r��#����Uc���_����6�t�K�^t�0����t��vP�.g��fY����(�S����3�^�g<���<]�WmKVm���P^5(�1�"�
�eR��+�o�<uU\���2ea�����t\
Ob����z;������"�;�"
VR��p)���@�����V����u�����s7��u�C�������~�����If�������u'+���������|�wx>;���`����U�U�0��i�-Z"�+L������xg�����*��P��]��}6��[���*���������A�b������o�����3Zg�IF��YgD'�"O�'5M����)G�L\��`����������^�;yK��,@��!N�?([�M�,Zmn��{s{gS`N3�2�
z�{IOUH���{�EM�v�m�Sn�2������{N�:2�����]IuP���[���ZJ��������4����E{�F����F�����76!l����}
'#�zf�=�c�{tsd�o��B�7,w32����T\�p���+��@$\��.s����q���6q�:j8w����J���Kt�v����0tGq~�.t��^$���S���xxW	B�'�hW��c�%�b�����0�~*���p9<$�P���.�T�2��d|LC�M\�}�W��YJ��Z�Y�f������a�h?��?��_/��!6��F��4���16������S����Z������&��\x���6IZ�py�_�������dJ���5���3����T��h���=����]@zq�Q2R3�s�R��1����MC�w
(\�SP���rVF4�]g������/��n����%��Lto�l_���3�A8sDv�$Ka�����hry"��.'%�T]�K��M*��[V�@I$��$!!%P���t�	� ��8���),w�M|4,�����\(2/'���_��xa�PN������V���Z�t����,�N�%��#��ho�����I�s��h
z����l9xUM�L��zBgkS�V�<�84i�@*U���HeU:�F�J���/�%�C��t��dd:�6f��60��V�(��cA�S����V
�%>7}������
�:b�/o���:N�+��U�U��0��l�k������[����Zr��������^8�G��My/��%�	��*��7i�����b��3~+���L���R������ ��6��X��)����K����F��F7Y�qp�RT4��
��p�|��C���d�������2�
�%i �-uY(�,������c��kC�����`I��6����w
s�!V��Y�y~(����v�~��3�z}��N�c����a�34%�~:�~8��������i�,���������:x�\�!�
A!���A�P�l�Cz���*���s����B���3��@O����TR��3C{�$��s�3�w��8�������.��/����]�*��15R�9��]	!e<�L%[z@�7Xf^l��6�����l���������[6�c��	�A�Z]��c���WY��7��[����P�/������fAG��z_hG%��2��]9&�S�B��D�s�u��J�P�*Y�E���a,�<y�>�+������u1M]�6�=��� "�Af�rI�������_�?���d��[tS�k�M,��z��t����_����KE�!�DKt��']
Ng�r��O��c}@azt��3��(������C*Va��N�b��2,�Yi!��v���)�M���Q�&���`�n�ctS,� E3
�6�:!hm�o����)x�\����P5L����l
�JSVB� �1|IG���4
�����(�[le����gB���z�pQ���dCA�r ��#�/������S�h��
��m�w &�Z��u2e��Q���"=L�9f�N�Q�|'�T�BGXW<�{"�2.�AP������V����b�sT.����T�b��c�o�<(c=GoEL��~����i^H���$U
o5mp1N��f�����p��f�����W/,L���r�����,7y��W�T��n{�������q�:^������
���i����m�����Vu��������r�m:6C`T�	�Xd��-��S@���;��AH��$�5�D�a�&S���4�)�.���7�������OFA}J�l ����Jd~o�`aD��]��$1��q7�,g$>��;i7���7�:���W��\����@��S7��uaE��~
��W\��W+��x2�A���'t�S������c}�UC�9a�����` ��[�����E���*��;��Y�4�<�_��������e��V�"�������Ecs�oCke�2b�K�*����rQQ:c@��a]����Z&&�:=��M�@OLB�V��� ���c�1��&��-n��������[�?R8���)_�^-�`�/�*Z[T��WdW�MW'Y�	<e�
��3��&�V�*H&�P6�
���SH?JnMG�',���"WF�!7�c�13��_�)��0W�h��C��u��x�O������.��A�f������HJ

���T��D�������/)-�T�^\O0�u�0��8�r�[w'��d��Tp�34s�Q*A�9�	��60�.�8�`=����5��6���4:��"�DA����f���j��Il����*�D�@ib�Nt�}BC�;8O�-����V@t�59��%B����#b�0@��/{-�������	���R�yh���=K"R���������i�.�w�X���m��a�Sqm$����1���\�"� ���)*�N�+�zE��u���T��v(.��J�����z;�����F\�F�7�Yq�PQ|s������9���|��jp|�HY���B����.{X+LU�'^]:���-�t���Ak��lI�+t�~�mA�/����&m����b5��no �n��x�~_���Cq���r��]�sG�Z+�o����>���w~�;�
�4�R��W�[�OL7�n+�S������L�����+J:��j��}x�D��jt�R�d'4��-��I��Qah���)�X����,�f�77%~�q��?#Z*T���Dd:dl��"DxC� ��.U�����N5<"������8�/��l����1�a�La�X�)Gq����H@g5�B��8PU�6g'J@�b�
L'��(=}&��j�8���������+�r������,U�-B�7���a���o.��,��|f������/.8��?)M���s����zH#fY�}�2a��Z��a���T ,E`d�YH@��55w�'Bn�v5N�w@���D���rn����]_��d�5��@��Q�<������<cuH�s�u�ss����h�#O�e���D~i�0����{g��7����*P�$�kEuCP�B��s����~����=���&��~Kau�m�������:�������)�
J�cVeR��+]���$���4XA��"�����X�
�������w�$�MkP�F��W��ss���i�;��v)���`��X�HY��~/���]�w�_���������.N�q�_�R3����^�GU����Mo�F�����,�a�u�C���op�/�@�MY�p���.9���������z������d�[��}��
��5g�����cV�_y�������1�<����5�\�5��7\�G��}����Ym�47���cV�(d�#��*[��L6�G�H^��a����	��L���'��^�����<~$��=6�5��a�;��i}2��Y�/-���O���z���GA�3�,��e��e���e�]��������YqZ��S��c�$l\��g�B�����#�������XY� B�6�{���/��gC��<M�{b��4��1����hy��0��y>�>��q��m���r��j��t�����3>��������F����b�����d=���VW�77����g������L�d���AP�|���q)��#��*Y�^�z��<��MO�JOa-�}B�}(bd2��.{���n;��FM�o�������hGp,�����qys]�M{p�9�R���,�Ra��ds�dD�R	�����~����FB���z���|;����`��
���d�` �(�.I.
����~6�_!b�qN[�.����d���������\_�[���s���0AB�W���������u@�-{T0W\��s��t��(H�K���H;�o���Q
��\
�b�YU@���-b�������r�y��g������h/3�.o�i��N��g����VG��X2KK���M����d�X��< ���(�X��S�
O7r�b�G�
sS�:����{>8Y�c!����	�m%j+�SE)rr��9r�q.U��	���	V?�	���sj��y��Pw��_��]���u�fg�!��j��q?�G�bB�&���]����c
;���������[�������m����R7e�Y�z2�����4�'�R{���:�z���� c���Bz�&�Fr e#��`E���/c�1GY�d4!�Sv�������t���,���7���&i f�
��;�rGI�5�QO��&��l�-g��P�vGmqF^l�,�%as���z�&���,����$o5aY�U
�N��89>;'���;o>�}�:��������q������1�}K�Iv�W�?�8�;���gP�x��p��1����*���@��~��w��
�
������D|uf����Gk������������;������#��LB�m
��E��K���Sb>I�7�4No�Fm�qn��S��,]�z������s��Qru1�\~=�_;�8�C���PJ��V��6� �	������CJ�8cb.�|�d1�+p����� �\��H>a�kE������u�T�}3^���'H�������P���������oS�Uo
��m��=1��;s;9��F�
���9�G�P�mn����b������_��7:�������}�����~>����z�����KC��WV�w��^o�<N)�g:���i����'.�81{�[�~M���ZC�O%��%�o�y�"��E`icGp�U�e���YD*[w����^���d6�����!�^�	9������>�!�6�L�4���2a�������/i7���cEZ��Gb�+yy�n���N����g���c�^�yG��WT�p���w����6�uW��|wkn��Ds����w��6�_�G'�������-�t�����@�3C~���������~3Z�������g�Q��z�(U���Q����\�k�j����=7B����9�T��}>��\p������)v#���sr�����P3����'�����Jt����bf;?Jnt���R�������X�$��`�����
k7B���e��2}�pL������NAVik��#S~�U�&Z�/4>H���qQtq����I5Y���� ����6��/���0��O�C=6d_t3�5�<�,l�Cd�H�s�T����M�)�mqu�v3}_��[Y�^��\]���^a�K�<L�5UG�����]m�FKk�u�50�w�������_�����e���vT��}�::o�m�C{�w����>��O.z)�f��=�4����I��q������K�h�O�o���A�\����O;~�g����S
CWf�J���l���ba����������L^��x8V3B;R�6y�������:���[�����d}��������|�;��8�����'E�b�k��E�^,�SIu������0s�,3DE��w~E��)UC�����*�!F�_	��Q���i��<���H>�**�#v4�#�2#��zF	�����(���IF!��.ke1Q���C�����wJ��&�7j�3\'�!U�7G2 ��(�_�P�H�� ��l�a'��d�B�Q��}��$BxA�)����.Fk/^l�t�E)�W��(��;\����6�!�
�#>��&1��d0�N���$�t|O��faH� ���8���w=��vr���
���	�K���������M�w��M:(��U0�?�=z>��tG���n�!i��J>���<H�u���7����\qP	��e"�\��g��;�;�7]�Pd�6��9C�
��$�u�����_��}:yw�w����K��r�|;�9������9GT!*����q�G�A<���#���+���0�c�����)��bp��������r��;�shJ�mD7�X�jFg��.��I^��G�y}������;��<&�PB�\�0H����9���m��-%�J�`�S6�v�c�����P�)����7R&��0#cv���N�K�P�y��"�U������v"�}�^U'��uv����5�(��x�K�.9�������4�.z���N)����&�v��(m�,�!�_I����S'�o�
��L���^�E�f��i�����%aa�k(h���{J$����_��;#�@�����ss���p��YZ�y�W�P.����O�h;}��t�'J��7����S���f�4��!�I1M5�i-������3$�4i4�4����R�DW�?}���q�s�[�SJ�O867�>:SKV#��#���!f[�f���,���G
e��;i��ux���+� }�!�LY����w!���]M�T��.�Q��Y�g��E"��������������s�������&��]s�B���%�v!:Q�"����->.-����z��O��G��a2�Q���]�.��T+�5,����1�m�Ty���8�e{P}t=���[�G�i��u�����/���:�]�
o����|�S����������d��!�[���1�s����[&p�8����'8�����W������a��B"D3�2D�d'���������}t���y|`t��i��Id����.)����@�4a����q�����������w�7�����Z,F��G��K��Y�uzz|*���F$�t�����;,"6�%z��W������+�������-�}���tP���yT3��dv�;a��,!��:������9��aB����������He�|�OL�n`�E:�&b��F5�D��N��C�N�����9�6EF��Q���������o�G�g��������y��b�X�
�b�4Pg�tX�8;eG%�=�i�[�S7����H�Z1�F�on���2������	��1��V����1��gX���&I&�u�\�rN�-{�����XJ8���"�����|<5t�������O)��,%��;��u�1%��^����A�5���>��WA��T
���	��tE������8q���F��#ya�^'� v�:Q��\��
��<����ml4d���{m��"�B�����E������UOTK�z��� [�O��7A�1���9�sI5�z#\����I����=��"�%�H���9e�0@���)�Q
���M13T����'p�*�
$�a�(�9��+��8����z����Egd��?~�]���8����"��37���]���0�����p�����Z�z�0;sG��V==*_X!xi.t`�PY�F��&�~�BumD�����H*��@;���(d�^�WF���+���������e��,F@���2��\��W�����b���(DkX��r�e1$C�����\1?)�<�gG�������1��P%R#i�>Y�_y�����Kv�>�
��a0�sa�.��W���E�5��9�)���|>�M&kJn�S!���?����T�K
����V���(_2�hp���������9�
K!��z�s�L ������z
�.�i���Lj�b�7�>x*�|l
|��n�g���E	�[�PH}�p�~�=�:HG���9{
��k�&�%'/,"J%@&�q{{9���8\�w�
�i8�k�N�=�"�,)1� 
��s/rO�c*�mX<��c��#�'��W�fR�<&��]���
�tU��W��-�H���b������p���~�]D���aF�OfE�_�:$� �(��m��(������g	�G������S�'�0��{��������f�s5E����������d�O��*G+I��*�d��R:�oT���h(AP�7�����;�1��v�rW�
:!z����$��^/�n�����b�oj�A�:��������I��G��Z,1e��U��P���^!��NM�_�tMi����=�.{�"�t��N�����~����A�z����]������Z������������������_�����LK����)#*�����;UV�}V���YwXkO��7������s=:���7���������z`o��[1��a�M?���#;s��t�����X��jo��E�?:���j������%������*i�R�l%/~�F�\K�3R������p���;�D4������{��'���X�R�����P�����cY���J^S	�e�`�R[��S�t�^t�������{t��m�J�+��#�~oL�`�:G3%�a��u�W_���?e�9�6��=,+'�bV�����-�v���B�/��j����]x�����#�v����L��	����5���6�T�1��_�����o�����2�b���AkOF����j���>��<��as�k��n����I1������6\���������xj��G���b�^��A�;��y�,)�(C�]F��iA�	.%[%�7�:����R/���@:�5�8Hbq&�m�\�6�C�P�����r�"����J�9�2��$Z������ +?!��Yn��{�c-��/o[��o[������v����i���+OE����X�]%W�G'����B��1p���Q�6z��e�YT����_��g�5�bN�pH���P
\��5��<~Bwu�2mDa��]�@�h$����+|��=|�������`�#�����Yq/�)9�a�J���m��0E��Y�����J�����oIB%l��NS�r|yIQN���7���B##�U5B���("y�8�|�^�����������Y��$�"6�I�p���
��U2�P��]��C��b��>!����A�$�����;���$B[!Y,eD�l��w��n����l�VC�,i�;��8���:Z�p�qtC��C	��D{uR{WS��s,��9�~E�������B�!�������^B\����_������Y��Vt����/�K��`�j��rJ��h*O���'��w�60�='��>�{��|m�l@h���G1��B(�
�i���?:H��Z�G%>�A\�`,Nk�D���{�H�����	vR�t���f�����r�e�����g_R��M�)Pvd���j�;�l,
�!���	������Fl����`*�Uaz���]���$����s+d����t%lV0�+����C>���E�Q����g�v�aa0���.��G�X��������kF�6H!���.��FPPt�\L�;ehMGD.Eq���Q���2ZZJ�J�BmG�I�se�'���K�cub;��B�Kk�_��i�����+�n��B���M�:sW�v4+���AU�����C��s����w���F�����!4�k������:�'�1����'��D�&��*Kh+��
�����K�OI�Q�u��)����ZF���	����NS����b	�����!I�$���!
�,�		b*��W�BX����l4"k��>�7��,UZh�����E(�����0�|K��s���<��e�PP
nWq@��H��:a�r���*�pS5x��C[I������PoBd@���<�y��a���b����� ���w�0��^��^B	g���,������zP��8]��Oh�2]�f�X��z�6���tV$��|��ag'�s�>�<���P�cpb������a?w~_���/���������<��M*j�
����/�u|�9h�����9�1�����������3xv�t�-�oq��D������b�B���^���{s���fV�"����UL0�f�		r����$�H����l�V����$]������+�%c�=�g���,d��U_d��?�����t���/I�1t=�z
�j��$�@�����Ag����9M��nZ�~um��0��lG2`�<S
������j���h��.�fi�(z*��O]������}4I���	c_e����0&����tg �����B������Ib�'�����(
M���Z��yP^�t��9�����?��Z����d�����U�1je)���^k����L!_��.J�������p�*�v��p
�
�%(0@Ho��B�+CC@M���F���,C�&��K�"��h��h�e�O��4����I�
_"�A�v�[b]?YU�����E1,_���/�vRc�C�`
*�_�p
~��CBxk�Px����{�S���M�$G�����q������V��r�n�7�O��(r������p~���l�P�+�'I����5��u�����6�HS�%6���<����D�LP?��GaT\�z9����`2���/i���dxN���N�EmI%��#�(������Z����_\~]�d���f�Cg������"Gr���!_��bt����bQ����P��u��F�,��Kn���J�}_����W�-�u���S��ut��
�`/��^���t���.��� �W�+G�=�ypz|��yRA�jl.	H6vo��n�f�����qODP���bYhi�e��{vx|�1���i�/{�����V�s�>;���yPI�| xx�?,U=���$\��L`���;d�N�kxpf0n�c|���ix�d��-��is�oM��5�)6*�@������@o3
�:�^������T:�>�H�6$���A���0��)�
�d�zL�-����j���S���{2Iq�G�q��F1�3����1�4R���������9��k/������~��$� ���B�n�Qz,L7��,�n��9����G����p-�g��`�����F��������WY���#.�E��Ay_���}�E��f��07]�m�+�B#����A�c32�-a@���S�1�9�f��BHu6�]
[���%1�:P2����Ja��YQ``����G��\����k���C�2�t�������*\q.r�`��U	Y�
aF��*R����rj2 ���������8���a������G�+���:\�d8}�	��r>];�_u�6	�'�\s����$�����'�[��[�.���9C�rN���q��� +�+`���!�b�s���:^���A��.�����:��5D�[���c�~��/�_l
V�#�
��%�	Y�����=���5Z���v�\	lg�r��6]g�s���7i�5B6�^��h)�i&��c^�4�d$�[� D��f.�<{���0�khHo2l��y��]���a_|L#d����9�A:�����|��x#\�z��.���IvCS�����pI
�/��]�w'*v�������7���~������lhu]2>O"l�"��x�l9��[�����Dp������\�W�B�O�Qep�����>]j�z�,������V"n���4:���*���0���$]]O����f%������t�_kVt-������Lw���?|�+6��>�����X���4��=���u`>��o�j��GO+��;��u��!Ap�s�bv��\��h>���	��%gz������^IY=^���Z=��i���*$w��0���L�j�yRDgQ�5�={�:��g����O�]��5����t�c�Q��V�Q`T�����*���5����F���~��-�+����jp "b�F "�[��I�4I���h�QBC7���<O��a���g>�e�P
'�<��HQ�X�Jr�e��f6�f�:I������+(`�_\��v��L>6�Xqz��ttJL9��!cv����$�w+#���J�H�T:���a�m�E@=h���)�r��y���X����,���7"_���8��+���k���Z�g>Q��3�+����(���,�/
�y���Q�to�.�E&*�*�O�w
;.
_����9Y���%��g�Ug��^x,�:�eyT��J���#8���!��"����+�OY���W2�9bE
�p���{����,*P��ML�c��U���
�w?�Hm�}�D�,�t����:��-P�X3��;�iZ�5�pA�v���)��!t����6�:���V�a�� �ae����y���6�@XD�$��b��U���vu�9P0+P����c�������$�c�������D$�'�
���A����;w�lX}�NZ���|�i
���'Z����{)X�^�	,KlT��2S����w���;�:�u�����*!!��e6�j&���{�0uk������]!�������-����'���v�T�#;�%j��:�������}�QS`<O�u����5�����P�R@��_h}����z�T�����'�w����a`�v�P����CKA��9�z"6�[`�/�E��!��	,&������5��q��Pp��&e���glz������Pp}#�@��w���6��EEr��er�q��E�-���;i-U�N��_Q�(:u�����yE($'����5����'cQW)�����I�;�@��t�9%�9#Xt�Z����skn�u�j��J�2Y� �80��(�RD����@�
?�!C�b�f��>�j�B'��V�C���Qb����/1�2�i����9��b�8��5-��d~gtPW-�>�	0�4���+�����NdGO�&T������ V���=/N���������>�{����~�q����+ ,�|�j���`���PM|�$&����:]?g��
���e���p.y�+�H�UNK�W���r���+(���u�V�E����gG�S�+�j���?�'��2�,V*Vg�"�%h�%M�Cb����G8j�"�1�h���9?o��!�h�J���^P!��� ����k|O
�3d'�QQ�=T����2i��NN|��j�)�	� &q�ve����)�%UR��7��gzO�Z��r��_#��������";���'��^�^������{�)	�����&;�����_���Q����,��2+���^��"B.k�����S�.-�/8��G�����9V���mM�B�����<�^&Q6	��)���^/�T���*�4?Rb�����6i���"��HGZ�U�z����(����"�����}!��N
Z���\�~|�z~G��D
��A)��+K^�a����~_zU�m��P3�:b���������R!"�����6��ZR������y|��u���_e������Yz�"|U�3&�o4���S�0z�J�6����
������a�E}���!S_j�
.��)hl��}���y�J�(���tFj��k���}�]J��]J&0�u��f�=B�^p�XVW�-!h���X�Q�����7�����H�����C�-�,j7���Y����p8s2EC����sU�9��F���m�8�#�o@a��i����5���$����	�������m*)`����~2Q�Q�	>����O�j����U6���tU���"�c���<���
Ur���y��+P�i��O�F>�0��I]����Bavz��a�������q��]�9�$P�%%��I���)��^����z���/��0�BMC�h*��A�
����"s�,��$���J��A0&r �]H�
x���` ��*���^�l�,��U����1Cf������$Lg,1_��[����m9�9�i��4Nl�����gV�:��,�lh�
�����lV
�aE(� K��x��
�����h�H�W���t ����@hFdM�QI����J���.f#�U5��gI��F��5P�8n$u��!$
7��Na9sl�]�f�H|�]�Y�oS�!�����r�D���C�9��s��i�/c�]�)��s����_�p�(�&�e����\m��O��Gz���'"�c�!��1��1��]�<Nn<6p��t	�R|;����x���%Z"�I@����(�f�� ���v�
4����uv���.��8�����/�e7�PzF%��;$]	O��O����������7���qq�W��S^�������H��P����h�a���;H{���}���7�x.�Z"��P�k��.D 7�'�e'�f��%d����bR2���9�2�fz5��%*�M�4�,:
�����R^�_]��w������l�H�?)���k�:�i��=�����1���-�Ek�
����E���Sc~#+�sA�p�3E���+�X�(��e�� ;���E'�H��1��g^���>b4������A��u^\+Koy�f���}�y�����
�l���%��g�}^��IO#a��b������������q�M�+���w�]���U��v5�r@^��
q32������24��A����R��5�����l���,�Ji��'M�v�������8h
���*���NQ��&)Z8�)\�sd��~}z���T8H�5l�s�������r,U)�R���e&�}
iNF>(Zl�	�f����W2��de�����!����y���F-��yr-�����TP�P�UL�
����k�����pI���An�rop�X�Z��I��jZ����0������@�f�������Tm�x��p��pI��QN��wa\a����lB��%�!�����.n{��j������[:Bl�D�0XF�;!�
1�������e�e�k*�\R��/��<���h�8����k#���/���gsQ�	%�i�&5`�VY���Q;��L��	,����pM��P�EDAZ���1YY����pU��k�����H4 �����A����,hJ��ZB�P��QA�\w���tJ���K(������fh�'��w 3�0��k�w��su��'��vaMu�qm*�������q��|#sr7�<�������TI�m���^D/{C�5w��O������!l�x�=A�M�$�Q2�oJO��>��>����/�R�T=\Z�6�����8*���L#dU��iO)"��s
��a7��;��g�1��sDFC��1��{�
��dM�)��s�bZ>h����|��T���������;0"����WN>_�YFp�CA����/�"/�L�IN�W|!���r��8�����>�\L|��Y?�R������h-?E�G���j����
��	>H��|���d�����y!4r������3�H�!�+������#�i�"����X���!MS��e��|(���@��M���+���_��RR�!�}�����S ��+<_�jL���	����6��������L���tx�B�(���5�8��ND�W�v��V�A��!Q�
��A�T�����B��������|D�8��:��E�{	���|`C�����4�hB1.l���?��m.���n��S ��f$�8�x�W�����j<�	��~8�6$��h��
�'�Mm��R���k�D���+���V,�����Bg�LiQ"�Ul��F�Z��>�|���Fk���s�./Y_�t�������=sT�����ZR�v�*
�h�r������������.����F#�"|s�����(�6^���9���=�e���-�w9V��.c,�|C��(��G��Y6��@.���������y��t5$v�
:l�r�BS��G���$�;�����6(@*�����c�W��"���,��^�3JnH`BW������]t�$��1v0<� 8��M�\�a]�h��Wp�

�r�&cZ13A��,�����q�@�2����+P�~���b���b��/���
�vJ`��x����lqZ��r�J��a�8��4��c��#��R5�r89����=>:�>�+��Z6�����M����c,���
���z��b��#�~��(�S{�����]oQx�,\)����
|��v=TZ��0=n�&|�|Hl�1�cE��L~�p�����D@����Y�9^�#c��2�9g6������m��{D3n��s/��rd)W�����GeHJA��6��]6E~{�����(���Z,Y���x0z7�����?�Zw��U��G}A��~Y(l�av�5R��(�.AO&j�f �����O�o,��������.*K����X��x��5N�������i����
	�����`"���E�E�;}
{m~�l���c:<�t���AYet'C�,��1�b !ct�N[�'�Pv�:�ou����I�"h�'��FyRzA������(�w���������;���c����2���������At���YZ�\���|������k�J�T�^��D�#��C
������8�"���'��r/Rw�B���+r�
���b�w��$�m��PN��%!��~`h�����\8���K/cn�$��U���\eWW	_)=�zy&1w�7��0���(�s!�(=�U�B�?w��?��(Xa!j�g=��`��~|_���I��orO����| =��P�
���/VT�
��Q�Wl����|�\���x��r��%j���=s����Gx��R��3$����L�
�6��y�w�uq��$���\a��{�(3�*z���U����
���g�K�������M��V��Kf.t��������~�� ���n��gZ�J��|��?�����We����V��Qd\ "���������Z�t�"�C�4���bI�d�Y^��=�~a[1#����$���Z����V9 S������Hd$��~��1���/�f��H4�?����i�99|y������[��e��WU�����Sz�*Jr���3
@��p�Hm\�Yb�����"�?|N������*����h�G�%3h��.W��?�$��VQ�	jN��Y����q�� <
���3����
f�L!C����/=	�*K[.cp�V���o����*AT4��-�	>
�p��Y�-�~;QC����#��k*L3�+�����E����!�k��j�����N���b�H�jD��|RJxE�����F���
WC�9�=?,[����Xq��nA�{�-/F�G���'���s����&��3�`�w-��=��1�@>���g6.���E�i
�j'����dh� ���X��7A���l<���>(����d�_�K��0��z�Ar"��g�&�����g��X��|G�������<=���o�N���p�[sK��_s� +����j���z@��L�#���Q�> R���8�����:_55c���zk�2Q���h��� �����)�E��"��T��G�����C��%#�0.XC���2�/8j��Ot�J
�Jo�'�Xk�o90��=���������a����l]-zK��J�3T-~^��}��F@)�w`����u��F=`���u�a����a����7d��{����K&�&\j����]|3$F	d-�c�!/�y��P����#w������p���4A��,��N�"���!�_�a��~Y�s�������+��01�s���C�������0�C���Q�b�U�tm�������K�eE9�E��0�/�PNj;N(�1�S:�V�ml������bT"��B�
���
��z	\w�Bj1k�\y����tSX���R��.��He����GbAT�������15b�&`n��:���Z�"0�J��g.3���/�tE��Y#	����N�F+)��x��E�Q��db-��A��S�����7$�6�~��:�Us���u�p�3ri�*G��4����0A�	��
�rZ{��e�-�}p:��fT�Z�=��Ubj���-�\A���Z2Z>U�d��NE
��V�*����#M�� �S�������5w�1�M���~P�}q��.�I� ����,q�h�����[t���WH���&�l8����I{�>�M`���Ry7�(���8���u�8���QO*�}����[3�_������8r�Eg�<�F�ia�f��6/u�c>��b)�"h5��=�{���W�H/����.�n�'��P��($)�R��'`0���"i�V�
6�W����u��g���Y��/��x��P�dh�-�<<a��a���}���D����@���j�����N�&�����B��^H�Ii�P3#-�SNt��>����F.^z�����:�d������7Z���QL��g��e�X�.:[�f3�S�Z� �����c����&���L<�9��	���XpE��~�� ���S^�����B{	�O�Ge�-st�/�A��!�-Ehw�U��"��R�-A����L���$�Uv�V6$A����#b��\2���e�G�SI��S^~,�zh�_�k��r����K����:Y��
���F����FE$�
�c��*�#4o�]C���A���BM����e����RA���r��(H�y������tP�a
K ��<��;�����{E(C2X��������;�z��������9#GS�_@�V�HZ!
u��)����&T&n�����->�&���sxwK���x:v8�
���?b#�Ab��Pt��9m��U�#�,T���Y�4#/�7	�}�f����+�U H<()��4�HLC�	���=�(zt��a;��O�:c'3u9��Y��hd(���7�������vEAp��� �\�`l�����������<�_V*Wjg]����U��� 4bY�������y��e�f��e��N�Q�d�K$��c�B�|��r���}���}��Q�3#�}���x��GT����|/����U�\�*��Dj�8�4�!l���j�7����r�f����xW��nf�b>�=�(� ��-0#]
��d���~�O}��Y�:����Q$�a���{������#&�^-^�M���t�;.�'���,,rO�s��e���K���q*��er����4%~��:�N.�2t|�{��GN$������8��x8$M�\L{��
��5LK��D��k��b��s���z��BV����X�'$6`pA�����S1��tD�G��N=������Y���	���xg���-�{�
b.���|�� �](I���/G��N$�Nktb�)��MA���k��
�a��I��P@nF�4���������{����%.V���q�L	"���aJx�f����]����*���"�<kDK���p���*�[��S1�����M�=c�M�*1x�\$�S��9����l�s,��b\�0AU�IwAG�}���/<���K����jm_���L�pBl�|���`�v��������kTy&Y�?���	��E��(fJ?d�>�Eq��$��J@�ZJP~���^d@A�"�1U+�`���r���~]w-��.���W���>���]Hpo�Q6$3�d@n6�9�C,���M4���$�I\���Pa	���S�J�x4���p���iL����J~�T�{u������O��o�r]��F���g��*��{���Vw��T��q�a/��e�f���Qt>�������������u'�m��2�V��Xw?S�Or'p����O6n�c+J"��m��ts���G-�4�E�*��M(�i�7�:�pR=��~�p���6J��+�L�U��P?h�T�����r�S{1"�����[�:�9�[f�#�w������r�p�������Nl"W��/����7StKXI�W���A0������] ��V���F���E�������tfxk��l��)�K�y���(|`�� �o0p��t��Mfg'c����XiFm�b�	����	�l,W�{����I���M0���]a�����D��m��l�K��)n����w��Z�%��_=vni����[��|d��d�J��|R+t�G����+��ywTj��T�`.���D�Q&x^P����Z���?D6�D1��H����%~��@�C������)��3�l����t������+�Cv����[B����P@�F?���n�\i09P��T�br���Q���3�����(>AiJy��Z�3���L�e��D�5�Y�<�~�r���x��t������{�oWu,E0��0#5�D=B�1�������v,giC����6��G\)�s����Ntf�5�f�������G��o"#_�dW	����O�I?�A�w?����q�Xw�[]D�1���G�X�b����>0�������1�Z��"q*����������Q���AtY=r�(�O ;�;o�^�c���hy�T���%��$d������m��C�x*| Yp�����V������;G�ur�:m��C��}����e�����D�������
�"�����y�>N�r	��R.>B��6��D�B��RQ�eS�e�e^�v�R/�Y���6�Z��%��<��2���&���nBM�����g�`�;
*�EZ������]kd+b������@�O�FI�hI��f7�"(x9�=T���z��cb;�=�H<J��~vM�dp5�va��9N�+u���f��������7���l�5���7q�Y���:��L���Rg���+<�g���Pt��z�����H��i��o�O+�BP��)�l9���s:�|��f�Q�l
��RW(�3����
��i�����v����������������0��D�x�EL6W0�&H�G�g	���d�S�
o}!7����R�a�������eo� �$Z�|H�u�#�>S��3d��]#�"Ea�;F;����%2-�>�]%jua��@}���A/���E��#{���m�?��+ld~,�q�8]&m�[VA���FS�TT<�y���T4��?��J=���.�J�(��!b��+��d����-��l��B�m5�9�W�^��54�WA{����D�K�$������<���Qd�n�4��	���F������iEDB��������d/-�-�� ����!���
�h����i����~98��[=l�J)�#���,Ek��!�^��0�f2fS&�VSDCL�.iTb��D�B8��v�.R#�RT{�F
���)��o�VD��Y0����WI�|9-���U�]���Y����z����H{UU��+'E�H���
���&����"���ps)�-�%8|J������`+��n������7{�!�e�^�������w�>������o��N;��Zn��/�Z'�H�f�������U?3����v��(��d+�e��e#����3�	�2�p(��~�{�'���������J`Z�����S��O�(�]��nF���������i*3g+�M�]�	�5eO��s2&&b��\������F�w�_��u&��%���W~�J�$?���V0�Ykb�m6qM��JW�r����n��c@������e.X��R����L�3ddR��3����b?s��F!X�U��.��M��"���������2���BUg������e��WVI
�n|m���f�����N+����+<|z�91������2�|G�,��U��
1��A���t����>D
��Sgt0��@NWd�l�%�p�X�:��MTM��tf
�_�'\����
J�!������������^+��^�����{���4�����)�l��y���O���8I8�����[Itq$_�N���~�[�U��T��@��y����U*�c������=��FE��4�����$�3�N#/�ka2<q����+Z���>)�fUPH��j
�?	v�P��h�5�D}aV���#��P�����H;��a�5QG������{-:k4����E��D!�gF�j�^ �:��M.	�&SL�.R<S��=������~��Bv�5�EW-�|&iK�(\\^M��Y���������9K�Ul�!���H:����YT5������U���
t�]8{��#�,2����^�]���<5�3d����
����cc��f0�NsHN%F��r�`�����������@A�z�q���@��D�0K�h��(j�=�m���"����YceG�o�73�Q�rhau��%��`����&k&T�����?����fx)��~���-s��q����_T��+uk���N6d�� ��.��$
-9�A��],��0�fQ������Q��`�����@$�i6�����62��oo.q/�%�)�:�-�VZo�[����R\5��jw�s/+���6����������uY�E���������q��T�{�M����T�<��+]�g
JTs(W�V|��qAu4���m��p?�5�d�(�p�c��S��-f�H~Rs����c�%
TT�{qx*�����B�����w.�|����W��	��O5a_��ys/�p� -����f��@-�����q�mU9)��i*%�3���6����[�g��`q�+��$��Q.Q'�)�7�������tG���D���Y�[�C���29��Y��h��Q�"D�S|�xF9o�SV�A�~���z���7���\[E����8J���z�f���$tD���K���PW
J�jl'�FC�P@X�.�����q�\!���8���S�[�H�A;����,��C��88D�umuY1A��0_UQX��yfu�}�]���
�|w3�u?��~
8��7M�Ld8�&���9��H������5��T6��As����aX��
k�@4��2�f-B���Y�����U�F�0� ����c���+��������������[�������=��&b$��2�{�����#@d�b��X	I8�3��qJ� �<d�f���A��6�N���.��������������2^P�V�s�Xp��cF����bPq�
l5�[�aO#x_��Jf�g`1Tze�
9��0�m_{O�)�?3�!�Z �;}�2,hs���"��
U����������������r�����������}=����VG�n�����y5����m<����|��@��P�����|�c]�[�����\������������-��������������d$�c���%���1�����%�\�&^'��xl�Yo�����-N-'kxr�0�������B��K���4����H2���;t��7z[����+$��������HP�/4�e6�(Pr���@H_6�M�q�l�~�%�b��kA��_�}e�
j��H8�'��6��Tv�TW����)_E�d��iN���"Ew��d<�l.��]!���W�
�����l��#�Q�k9�{������?4���MW���:[�L���2K���������FR�l5���`��j�����m��uA8sP�(n�	�.�Y&Q��&���i2{����?��y_�3TRTdqA��W������.�������W���u�����o�"����\<��������������b�-��rI��E7p���]��f�i���h�2�3�@�f&���{2�9�_-����uYO�K�H9�Gp<)�\���]\�`��<�k
�4S�frY�R���4�?��r�(xYZ8�j�l��`	58����D���a(���u��$I����E���R&��UX.x�=��A.��/����Wao����}�$omN)>[X�N9���;rH��x��b���P��K�7��J3kzb6�������(�?`���\IBJ{�/.��gF����VV��{=z��p�d���h���1����.Qd�;B#��;	>��h���q��5@��P�~qV&������<$gcx��5��,f��eV�{F���?��P�6#��#��	�`�@��5
���������5���P�T �J�+�QCP�����B��+I����IJ���d�^�z�$��_<����Y9�������~��?o9�'���9y���G����{{tb�)�8��K#�m`~��!g�xl��W���g`fs�
�%��8<�����M ��N$������@9I���s�t}C&Jf����>����=������>O�E�B*
�+�]K�F�|6/�>�"p8��f���,�����i����x�<`��[�����������[�k���&��iR���$y)��0�+����o��}g�,��;.���O��u>_��}�w��Op�Z�a61�Gf}�����b�|iFGSb1����z;��I��s`'��J��d�|�v���G��N�D0'���z��"P����I
$#���-d�+�-rE�������{=<������,T,����f�����o���Z<	���J��K	�������u�C�mf�_�;��)b�P�p��������b�j�q����|4��^���@L��TL��������BrX�1��'��9n}�M���y
��R~������)Q��*�-��|���h��0�s?��N��������}����c�*������b�]�m�C��J��j@���0	��K���Ql�A���}�����~�!y�G�����02������$��O������<Rg�7��/:(����7�����.r/������������s�t����[�tr�����|�����Y:�t�Y	�>'@,D?r'� ZO��,��x�v0��2��a��D�5�����)R/��k��Ea���"�;���5X[��(^�����,��3���K�����>��}n�
�oz��m�������� *���j���	�F���E��p7d�a�`/�Pl2�����q����7}oD'=�$T����)8.*�"�~e\����:7���z0Bj��`R�1�b��������lqP�U9�b����Yt���)Yl1���D�������J�d�E�B	��Da�9*�N��sVA��9p`���$����|f��Y�i�EA��@\�:��
#�w�O�B&14d��4�����>9���Q'~-i�g��UF����Q�l#��X�wXL�����M��"(�a�
��1��o5��u� ����*-a6I��������U����W�RG����H|��-������R���*�����r�Y�m�E3����
j�)4�(nK��wHP��m�A���������s��)`1J1���
]m�%�b���P�z����K�����eii�t�^% �F ��������T�o^N�-*��t�.T�2M<�m1�����F|�7dxg'�C�@��`�����"{�7�f���t���)f���$/�?@��T�%�A���Fv��������NB��`���q/���ou�����\{y)���'[B��T 69F\��X�8}�>�����W:�KN��)�9�W���S��+�� h���2}*K�c2�S���O���'�>MMpD�
�rN:D���:������61G��A�o[������*S��Woj��B����]�Xh���e��^p��C���4F^�/�l]�/��]���b�.�-	ev�8�]����e�S.��
:J����{#�����<��e����&�t���6+�t�Q��mk_�y��|Q/a�+XC3�.�j����P`�P��Og�]���q�.�D`
�r�)��r:�"�naJ���zU6�E���a��R�{m1%}�N���-~�f_t^�#]{�k�dt���j���U���P4>��=2� m	����
��q��A��E��z4,x��F����v��L\���5_
/��X���N��t��#�H=2��*�f���+J�}u����f}l6
v</52�������������������u+R���`>��M��y*��9h�b[��1Ez����ofY�P��;$k�#xUV��C����a�������"�������t�TR����*�TV�;��nKs��0|M�-���"�9��v�-�H�����=�����Ad��7��h-�y�r[����j����Gc��^2������	��+q�z�l��gq���R����I�Q���z�����@�Y.����?���E��Y��A��~:82�����7;�?��s$���r�`���8���������jq���5��$Gq��N��v����;�=x{�7zt��QIKw�6H����n�pWpBa�K��X����}��;+c��5�1nD�	����b7���/P�6��[�+����Yu�l�I}�o�mE�l
T���2W1����L����M�W��������@������Z�vW��t�G3i<g ���"�G~3~�*'I��|AL���dc��)��j���
����c�)���m���������,����?��k�����os��'����q|v�����<�dW������������_��'�'���������_�U������=������"��n9��0��?$O���?m|���_��E=?���;H^��K �r�����\������;G�T���/��b�q�(��F5�@��vX+�7������~����l��M�����?PC�����R5��Mg�dF��<����K��m��b��	��$/;��q�c�<H�BG��65>���)���3���,�N�5*"�5��_���;��}��A��������|1Y�����;U�����.��!�U�n|a���_#�K9���*�jj��}(���H�=Bc{��H�Cj�P��������k��R��ICZa�p�a	��dV��V�8�<-�����	B�7�'����q��x�Va$���:R��[�g������+��
X9�z����|Z�"�u��Zs�,G�����L�e������iZ��g�����h!��G�=e,��}e�b^7j���g��8U��3Ma�Z�[�g����Y#%��;��v��J+�6)T�e�F[GX��v�mIa�l�";���������\���R����A�0?j�G�N��!x�4u�izf!���<��4<�z��IZ������.�w���k��F,�9>�;:!����k�������t:����-:���<�#�FG������!b(���?����Z������9v5ip����y;�?`
����G����g��X�?t�#0B������Y���}bW�4���`����-��l�?Xx���8��*�/�!''Y??����	��f��r�6�1��P���/���M�����K��5I��� �7.��b��}�F#�>C�S?�����.�i����x\.
���r����.x	�l��%KqF=<�tM�$=[rt�]�N=��������|�����'�$���ql$�AK,`�R���@�mk��y�g7��i����\������q���orr�ci���V<���������@�;��Wey�@`��
e?<��
~H0���n�-�+��;����}"j�-�
G ��<�/����9�M��i�y�����m)��t�|��u�w[i>�OG���X�����%��u�c�� ��Zn�)F��]ru�������������D�"�2.K��d�����L�$�3$�����$5�����:b�������!���!.%s�>I��������[�	>���5��������	%�Poz$���5]f��6QP���-����k��R���B�G�O>��c��i2S�J�&~IE^��"Hj�[����^�"qGC������s��#nV&<b���ny9[HQA�d���!�P�����=$
l$'^�r���V��1-E}�G����]�&Z��� �,o^��jE���:�"&FCK���b�5 �R���i)|�����T���'�7�9@xF�h�\���q')�#�Ri]x	 2����!���8�nM�|dG�j
�5_�7+���^qbl��(�ho�m�m@�d��xW6�ao-���~8}_����4����]E^2��	���l�����`���'ec�q�����GC�$b��-���6W.x�`*d�	�]0��G�'R��~�"��
!���W��
Q��.�G�����
w��d�^L��y�����o����63|��ehA���
}��	P�#�;�C��4�d��9b���WI�����O�\�
e���R�����
���6/���~����]��Zk:��������D��i=����Y[E����Z��-(4�u�lj�w�7�=��S��
#s���0R�]#�X]elPbCa�I�h���*���������Dm��}~�c�%��&L[}�~Uu��aZu�|�R����Jyg9�)��~��
��P��������C����������9�����O�ym)�N���gJ;���A��[�N*�x��o�2�����N_`l�.G��\�����1�
�.�]~�)�����~�p5���`��8�����
	aa2�����G/�A<�'����
w�-�T���$���A���!{�-O���9��s\������W�;���nj�jx�������g�Iv��n��
����?������~�.9�a�81�?|{��$�� z>9��������}���=��;:<6� ��
����YUy���C���RF� �'�/[����Oo ���u��1���Q�	<(����ag��(���yeh[(q��_�r�gw��s�7�����YJ	w���g�' �>D�m}�{����<��H������|��������)$t��������������Ms���s�I�L���'����\�QEi������.�*Pb�h����*)���������
������0?�#��3G��V�&��g����&'2��p���]�8����Da��V��v�u2
�nQ�//��#�D�DY���w3�����]���l.M�����9��1P����/S���i&�C5��o�#R�m�	�o��������xh��DId���~ ��YF|����&����F%�������?�z{����7�^P���oF�o���x?c���?�jk�3?m��80��*�����9�{��T)�]�R����H�����p��p��$��N|��t�<,Pby��<JNh��;C�CsR�{R7���$�p�L=J��7������� ��� �5�>bQ��9]�Nx���#���^�SN��
=P��'��W�&���eR����(.���SBY�d�~����*�0d��q�4�1�
��,o��<;��,����B�"Tt���]YXl����o�0���E��R&bu���,���0oy����C�a����>�t6x_���R|��q�����������I����US������(CZ���#I�����L�f�����G+}�?�k~���t�����1|5�mxo��F��\O,�V��CrHD�TJ7����r�Bm$�
��0y����������	nPFH}��CW�P5�)�0��a�]J������?�&�,T!0�����u�(��0	�0�2��ecP`�&6d90
����,?$v��(]0Ill'�-X&��5D�><��Ph�����L�������%m���R��C��i��%"�^	�����x��QCq��������&p.4�/��G��0ze��>��u2�;3P,3���~E-���������g��1k5d?c�G�)����C��������L�[rU�����
����3Iq6�95�%&
��=�3�rq~a+MR��+��9�AB����+	E��(
��o("cC)�M��$����4"��<��H�f��� !J�,��W��)�?L���M]�L�%%��S�J�v�m��"�8$���6���L�!tp������7P �Elv���Y\Hz����X�w5��=�M�����	qr��s��6������i����s9�R�Q+�$��"��-��L��`y�`��y$����3I<	�I�+:��m��_I<G�ks���W���ri^Q=��j.�*�����6�N��H��Y�DQ&5�BHb��R��o/!\N���K4��������7Pxi]�A�$O ��k��f
F�����C��b.����U�w����"d)����#��(�FE^��xZ�#��$���|2�b�%x�������n5L*��3�#U(���v�4�)�=���J(�{�@������r�*�����$C$�l�'f�G�p�KUeXz�t�X#[��L�����i�K���gB����H���[V� 2�������`�"Iv��I>W��eA9�Ue��jN�����#�J�Pr�����W�*�L�YY�RGZ��%��m���`4aBrr��d��d���!���.��f,d;zJQ�"��(:���A��3������p~3���EV��C��4]���ooh�*U�����`;X0"���D�����,����%�L������"D����J=`���+��u\��^>�����dki�&p���������������{�F�3_��������h��'����i���c�9h��	1��~�9���o�{��*��Q�������S�\M.����������k����9E1����b�M0+�p���l[Y���p-XP�Y�/
�p_~�c&��H�����1��p�%�'�:}|U���3���h<U�h��>T�zdD��%M �xK��x����E1��x��?���J�j��(�Cp����������9�N���
�+3���w�����Q�N���������N������Qw����B�=�����Wr`��[�����b�@d��Q_^_�g���|]9�N���@|�z=����S���zY_��
���E���a����s��#�1����R�����'�+��������S���.@O��2>D��4n����U�z�A�����:�Nk�'��sl���1�W�<��!Du;�I�-=xv�6������N�Y�n���.�p��QS��EWe�����A�3/�^������k�LC�I��@�d���F�`D���m����j�7�<~
���p��������8TB��1(�
a�aNa-�*�� p�`�P0H"d��A�:����aY���R	qnD��e�u�&+�l���Z�hhJV�*G�>��s�z�X
�D�%��+I��
���f)"�?0�i���vR��D�m�8TA��aH��I0�C<��;5�d:����!^`���/����A��"�H(���1�����S@39���`!�_�6��\���
&p���7�<*��UC�y!iK�X��29T�Fw ����;�Dz���6��)����?���0����(mC1 �o�1�{�lE�|t9t�sX~��Q`z����E�^K^J�3�<��,�c������T��������>Q�1��u/��� �=�������fi:����\�n��K�$ �uzSC�;<}��H�;�D���Q������h�.��z�=}���GA�;�9l��:���c�,�����$�7 �r6<}�Xe�5���ye��G����B�>��I��j��r\���|;��`Q�I�e~a@�Y��BR�pZ���-���:�{
�b��:N^���G^(����L3E�2W�E.���
�������j&�fFATk������08V�b�2�f*��"�a�J$��pab���r���E~&O���R���=��{����Lv;^7��V'��%�#����e�&$j�g�}&�;������Q�C�E&�\�
!�`[g�N!F���M�����b��
b�pW���R�����'C<tg<-��/�D�\�D�{�2�RQ���>P�
f�>�S8~zd���w��	��"���!�|��j��
��~h��Q3
Ou�O�a�%�7%#*y�r-2]E�Q��{��M�y���HU����P�=�n�]�{��{n�g��_}zu���K���n;�����@� �AR@Y��L(-:+~k[O'�i��QW.D��1�7jD�f3yo	����p�'AB�����!�����Y�`2�Q6Lv����1!%.�
�^�����E��b��MF\|�=��.fC%�f^����
�R`<����R�w���?�{	i6�|p<z{x���+	�nZ�-I�kB�R�Wb��2��fo'O�<����/���Rl.M|k}�3����){?����"%B3u�}����k��y����z4��ze��j�x��a�L^,��?"&Q�����d����l,��H���a�b�����m�#7
PK����&�SNE�
�!K���XOT#M9�C5����Y�(�6H`���K�`Ls��30&�[:PG|D ���t���;K�[�����z���R�'oZ�
�=���}~��;$+�p<���HB�!]*���/.�@]��f,:�4�*��#�(��x�������l�mV��5����K�6��M&�Gb��S�?��,��H,�[L()`�hKv���G�t-��T�!;_�B-��w#�VL>b���l2���6 	���T��7u�^a��-rtf����AP�&�8a������fJ���`kc��,�����K��	�Xb�o_�%V}����aN�_G�)h��������n+x�V��-��bb8M{���9�\5o9?���|5p��|�)���a�%��UxXE;
d��]-�)���"��f:qzK-J�r��5��r8N�0����f?*���\X���
����CW0
b��,j4�1����P�f��@�(��Z�����v4�>b�zt_O�z��kt���IM"�N6���</��p��6!���5~���/��l�vot��Q7`4�����e��|(��n(���G3��n�h�@��7��A�:�H6G|!��y|��M�������~���J[h�pYY�%��l������u���-�M��_�>5��������~2��7=7�hD���p��[�oEy��	�{�_R��%�/�Aw���X*�7��=TR���zS���^DD�#,��{��Q��O��^����P�.3rBY��sq�AW�Se+�J�6�YY��9���+E��o��B)�"	DqRg��B���~����v��4=���Y��#0W^X/����h��6����x/��Q`��Xd~K=�$(|H��k:����Z����K����Y~��g?�<�N{���Y
�R�����0��Q��lr��t�M�	(�t1
_C,�2�A7"�d�;Z��A'j����rw����t?�[l��/v���o�W\��";��L�0f�����7�����|�F"[6g!{Y{K;!��v�D�����*���n�CK���V��oZ+@�r�I��';q�/�
����E�YA���
^�S��q��Dk���+�!�h�������-E��L��2�ZbX�J����O�J�i��z������%@�3�t��x��8����*����u�����	��h�|f<�PsA�e��u'W9��N2��<oZ^�6^���Qv��,Hz�U�����.�f��5!bF���<�.���f{���u�*gj���m�c/�W0�!i|��)Z��U�
a�Wi9�p��i1�v�:{Yg�+ev��:�k
��\jiU���������8���|`8������1����������Bu'��E�A�{}�����2�_@9U�I��2��������.!]4���G��<�)"�N2K��+�B�0����zn��T�o��
s�
���d5��}�<��{��!���%4��`��|
����.y�1d�U��Wi���w���vQ��~SVW�
"p��c[/~>��-��F
<[L�a��~@R�H<�[��z*��f��z~X�H�y$g����n��{�����3z�vh�T���Y��.�Y�6��K�qH}���Vkz�w���BPf�H��;S���Y�e��^[��|#x�uK�b��]�]l[�
`���?�q�����;��)�o����c����g���w�oFL�Ls$8F	� jw��}�aFg�mG;��n������>K��J�!8f�?�X�c���s�����-n��H��.�����,&Z�P�[W����-�����)���@���r����f��f���YYFc�_a�<��zD���j�K�k\��������](*�����16\/�/��wH�F���o�h`�R��-�e����l�,}�#��d�e�*����a�?s��E.�c	aQ@L00	��o�\N�������?~Y2���F�����"S)�������dY�I�s\a�e���f�pP�j���9+�p�E-�Z����
!��R��yNq����Cu���M�f������XJm������<�/
+B�,@= ���;�}��S��h�U�3,q��[ ����d�R0
���!*sZ�t���i�
�	�����K$���m�N�C���Y?�/�.���_����jJ�9��]���a� �*��@�S���0C��%������b|P)�4�7L'��sfFh�3������f"Z�
k����)�T�<�"(���:�x8����G�)�a�Z8?a)��#����R��'C��FC:��W�a�Q��y�������l�,!��3O%9	�����A�ifp��R�$��T1\7~��k({r�Di�p$�;��k�$G �%��*�l�Fk]��a?J95���U�O�bNw���($�^�]l�J��!�
��c:F3ZD�vO�lVa����
�3�cR��\�[vL��WeE�
Br�|}�Id"5��la�,��>`�6�G��&����Zw�v�P��� �-�.?��xQr�L��,���fF���@����Z������{$����,.�����n����,y�nd8o�����w;���X�CQ) �l�4d��a"kI���!��K[r���������������{�����]�R�?�����m�����Rx��[ka<L����&�C3���f�`���5������yWVt�CQ�
]O�� y����i��'O�24-����$�����K������r1[���F-;w��4�4�|�v��t�Z)��kU���UA��W��<}��r�9�&R�;@��������������e����5O���?'5����\Qu��\e�y�xv>���� ����>1BE{���5L������:���4x��~_3-��P���e�k��G�V%5A<��u��Z���
�rP�O
{}T�������bFRyp�&pQ#���7�;R��lEZ��Tv�K�g���o��r:����+�SwN�������p�����UC)+�~4����OvN�F�{;�C��I_��T��������F�c)���o��^��f���%9��;���7-��Rx&D��\�������7V�D�����~����TY�Fj�Of��'�|�m���0�(��I�� ,,�.Us1��������7�������q}�%��������(������*���Y��X:�T�k��XE���a� �K���*�qq�^N�����c�{�p�~�������
���(pS���N�:���O�������j'0��g�������a��
L�T�pP����qp�r���gk���)�fC�$�	
t�~������U@nuo�KjX����]�a>`�8?7�3�LW�K����.��Tbx�L`�#�8��	7#E&��E��m�=#)~T;�aR��xpvq���
�M�������l<�f�����w{G���'{��{#��7;�{�'�:�S��p�TWb��}��D ��P��9|Fx$�c��o��*�����F�������l��LZ�w�KD��N����o�����O��V20��q ���]c�Nh��>q�������/��x,F�`�O����?����{/�}��c��
��2�,j[D����\�X��|F�&�>���s�g�',��	�]�1+\R�KX�K�"&>j�K���i�{��;�������F3�5�
1
���?��"�4�u��47j4-�M�tS.����%5eMH��g({*�%�1)�`���hu��(��tr��'�l�.�#�4�� �U�.�3�9�]��9�9�\�D�D����=�lF/�2��fRJB��!C�4���k#{&��n�qL���q�����s/@\�������I�_�PG���$��5�v��po[���
�LCl��3������'H��,u�~���������%�Rx�b��"����aonla^-���a����-�m������g�
?L`�p{�d��N����(Gt!���p�X���a��[��U��t[�_�������Y�H���1������~(-,6j,vd$��ei��_mJ�\��: SaQ��9��h������6^�UY�hz��|d�&�u=Lx�C�'�F����z��3������r����%�eS���b��e!�'-4��-��Goa�`����waaH�c%�����V��"�r/6���p���&�uT�����T\-8��^�_t
x��8Y���f���D����7�n�I�o&�f��;�9~X�g����={?��?�����pobk��)��AW���j����V�6��&�9o�t��!��W9�[�8E\`�:���68R����?k�[��Y��z����d�C���,�,�D�VC�#9T��h����%��4��SJ���YY��I��J�����^7���s*y�����
�Es*�-����S�[
�����c������B�1v� Mq�	C��t�Yx>�M���L��S"��>
�\:���H�����;x��<'���f��$f�n�C�'��k������4���4��H�Y�o=Y�!���sH~��:��RDb<62z�&��8�{#�7s4�����������2�L�%i����LE*8�k��-*6����1��TQob�"Klo3�����>���-�F�X�N�4����
��O����D�-@�,
�m����&�2$]��{�F6�8�'��&�����I�D��g�(h��]$���w�������������\����Z���a�jB��l�� 	���D#@J�UV�K����c0K�����W���l
�I�l��XU6���2��7�]�������_t�{�bH�#w��.|�w�����������~T+�~$zq�n�3Q]3�����<x�Y��5O���.F�Aua�#����f���:��.W��a.7}gEy�C1����2���n9Y���j�bFE���H������A2h}F����#b
o�X�*��S�h���K�g��t))����������m|
!���y���
F��Oo_��m�����#���f��l}-�N������q)&��\l��)�pQ;�2Ji,
rO�`Bb�i^�F����_��5�!�$��Vf+� 6�fe
(�v�	'��,yC�k�h�]���?�~�y����n���DnI'�
��[_�������,��-�p"�p
Y
A�9 L�F��hQ�9y���+CI
O(�d��1��)���ly}a���<����(�z�Xns�3��mV?���"oy	�E�����1�i~�sf�&�w!4D�i��k���[��B�'O�C�!���2F���rJ��/�)��S���P��1�M8�^c"4��Aa�f'-%3D
L���@�@tXK��<j��k�<�hd���&)Y���1^��
�HBJ&�u��	�.���5����a��5��Jn���xB��@��G�L�����������
�J�����PeR��	��~����,����0��t^3���_��t�%F@�����f�U}��4~DB�������n�5����{I����h���W����v�X(��$J2�r�l
n���G*��!x�w��+����)�k�+��k��W|��_�W�bb��y�����A�����r���`z�q=u�^eE���m��������U��[��r��`����}J����
�����EFi+�b��@'���q�o���H�$�����	�(�6�
y��|��*^CX�d�b���>lZ�7�+�XR6��n=����lg�% ��~��<p�z��/������u,�RJ���M+W��6��{j�e��y����^�u��+@��o��\��F����3�9�:�3�$��|�V�iE��S�H�H�
����MM��=FpC�*�l&<�	�Eu�3y,��#K������B��&��t��El����(rCs�\�;�ou1�y97��U�;��uv�QP��s�r������d��?����)\��	�]a����TZ��MsP'e��P�YUB,u���&'�� ������F �A�+I����L�4�W>��l(����w�����bW�b��k!d�~]/Hg=]�DT�6�����e9�b�6H�;P�l���Z�^%�1hb��T�����>V�f�w N����uN�a`�0>�,��
�RyU^8R����%y;�Z�|R��{a=�.���c���6�0y��:��bEN{rO��"��{���y�w?���?�e7�cD����&{c�2ir�at������P�Z2Xm�L�M����S���@|0~IV@���P��o�����"���+���|[���u[�d�����-����i.)�J��3����/��M�;�4�
J�qw����[��$�97�����d��5��(���������>r8��4<�w|y.P�<v��S��cY�������v�����<�Kg��u5�%>i�������,rL",�s��������V��=3$~����V�o����#�R���t�����0��b�%M�������Z�;���xk�=���6�`e4��p2�����X0_��	��\�����#MOl�3�T�2wi����|��P������3�]�%8���,�(�de��C6�r�.��)f�	�A�:����g���c#�]B!�1Ra����6�G������[Y�������A��h^��Q%�I�v����e1�pkK�n_�������3 t4����T��{q5��|�:�0%	����/v�	�^o�f����qH�RB��F -^��
,��Wl*;Jo��T���Cx�������uz<�i%q��������5D�"�#�0|\R���a�w�d`�A�%��`as��e�'y��������I��|>�s��u��^r�9�M�p�B���Frs}���z\���j;�+�x�A�V��k�{����}9	|"�Q��#���(
��Hm�	RN(W	7��@��u�J�m	1"b��,����1�qm�+��(��#�Pf)XI�A�_g'���Ru#�����������K-��y���%��6^vB;l+��rmw��$���l66�GD��lY��%�f�$o���T�U�`gm��P
D��m�K��L�4�Yq8�5��[��|������(J��,�d#IT����O�
C4h%
�?���u��v��kg��^CU^�6����l�sE#��G�z���iG����~����%���x��U������X��)�DWNh�C�&�Gd`��9���'H4���L�]���U�M�@F�[Q1���0���j������7���w���-:"�)�[����l��_	S�����P'�Y�Q'o_��.�) ���@V�`M��bl���w��x������
�1rx���S��)��j'(������X�]BE)��I�����1����D���}2�q?6��B��R�m����)�Of���4R���T6Lo6�f�y"?�w�Z��-�%"	]��	��4h�p�F�%x�?�C}sR�K���v&o��o��%�'�x�����W��w�Z�x�g���*�����r�V
2ox���N@��i	&�2)O-J��Y�����k�s�v�yD=�����16����K�����1�����&b;������
$�`&�A~@������\�r��5M!�^\b�MB*�����S�.�0����r��k����9�Yi�R�`���d������|�,ko�8"�U����W5����i�8�����RI^u,3.��=�z���IfD�KT0\A��KQ�F	/Bx-�W(y��A�@���T���:��QhV���
C
7��Dp��s{�1b,��`�#8$Z�B�i�i,`�1|�r�m���\�S���������|$��'��:�Qy����K��-S
���#������+$=�F�W?�8VDWY���(�JJ>��d��b�H�mvNx��H~Cm�-^�J
���t�~%��K0��	&�M�+�����6���&q��"���|��,f����$_����|1�1���FG��AD&���)k����j
���t��l#��o���5[���J��w���&>u�O��=���KO#��E�IP����T�����C
v]������f]#���4g�q��@��~,p�����?������Ns���������s���e����$\%~��5�A��������+���6�d�;/_�������X�|��$=��7_W��n���`gy�����%%����\Ay8x���q2���3������t���y�T���Z��Z{V�l�p5[��2:n����i�o 8|7'���fkt�n}���ulvd	�C��4��j�tU�-&�D?��4�-�&�{Nm��,��G'��E:��o9�?Bs�pw��E-Nb��%2[FL!l����,�����\�JIK���/��W��kpx?�{�/R���C�����b?�_���SA�05��k��A�[��H��x#D�� �]�J��z^�H�`(*{(&
���P$�K�uv����|���v�.5��9k�%����h���4pJ�l��ru�58cy���KG�soD���8���O���0�bJ�iu��Y�
��M\��;�r�+rBU��$8g�j��Fh��"���\�����m*��	e����8����WI���g+2V5[����)�@e���3���|O�Ju�J�b����K[*�y�Dr����E&O�>����Z�V��%i:�R�N�l�d|�}��
�@�i�Ve�"�]d��V��bb#S��/��!���|�7��W��/^�cu���\Kderj�v��Nz_9%�����b?VDOr6�^\�B�<�z�����q��}�"�*����J�!�/j�n���Y;���;R��'����)�V>�o�������#���tw�AhK^X8H������9�����p���g�f�I�����9�G{;'���h����h�J�k�K��]-q(G�j�N&�c�4/��B���	wYOP�\.�-���
�e5X���������:ZV�f��L������z��.N6)8���.���0�������<���=i�+��!=66g"U�6�^�5��=����z���-�T���ki2:sSZd<�%��a��5D��"5�>��GQ���L��$'A�~������;]�+��_p��e�z��_|_p�����	�x��H�Y@��l6�B[h�
�����Z�{��~�d,`�,����z����j�d����[��i��0N�)�`�6�S�t��7��V
��|���m�O�h�x���]�an���������)/���v��P��1�Z �g5}l��=x	qJ��7��s�Mh@���a\R"����)lX�������:����I�B������_���_0	�v��+�u��p��
)���vg�*�6�),��}Oka���!~���`7�A)7�x���#����f�0�F4@��r�^���^������K�	���|���32EW���
��X�n���~��S�&�����m-�����ia�]����a�$H��@$��A��:�R���NF�v^��������X�l7p���{����'���v�vv��P�n�I����f�H������_�~��{7z�s��/�o���j/�c�_2�FlF�����Fy��h�d�l�9���,�b���S�����c��H^�����
1v��u�66��a�FPI%���@�u�}cN��
X�����n�_[����`���0F��;�!awO�ae��cLG������K�����t�[gY���e�CP��g�'0���T[*��A=3*3k����&�:EZK���!1��E��T%`���E3��(�:8a����>�dFqt|#������X�AC�miM�sz"��/:��6�z��	b�E����<�H0����enl����,N�m>���N����#0����1�C_�^���`�(������E�T
d�7���(+g����%��5	�������;��\f��nJ�����kq��S/��CQ�%�����_�Bvd�����97b'��!L��&�08�f�R(N�4��pd{�xO��g�I� TIS$�������8��
C��x������gy�3��o��W� �"���������p�����R�%���B�]�2SX�1�y��R�#�fa���{a�k�����j��Y�}+(����Z��D�9�B�V�#����l�W�A)K�1�r�v��Bmb~�r�)[�!p�$��-��~X�/m��:�~��>8'��kI�C�,�Z6pv�����a����<��6����C�����r�l�a�U,�H���
�XV���y8�����?������y(���1�����~���T�8��
��j?���'�!1�/~|�c�?�`U�AE�i����3�9|�����h�H�N�U��3��o�s7�$�X��p�i��	?�1,j�D6��=oI���O�d�e��8�q�^��2Gq���HbRW������� L��
9�$\.$�q��@o�p����4�g	��2R�B=�g��:Af�K���b����%��J���	�K����I.+;�*�u^$�F�gw��&�^����XeFI>%Q�\����}���'��~���5�:���O���zk�Ifx|���S��OWv��hGO��,�`�8J	�����@P�PNMj(Z�t����X���N
���%Tt��������8[%��-�$I�E�������`/OYnt�Z[�o��\�w$(���
�G�r���K� �n6	�u@(��f�
��=����$d��$p����h��~����5J��(k�fot2��YP!\������`�
`�����V�bQ�X�{�r�Ze�0f{��jp;����P����85�r\l�����(.���	b������@���}�F])Z�!"#�/�H�\�td������>�3�7(1P���L���������h�1��y�����t�6���b5�5�~f*��Jv�E�0�$�\i+�!:�X+gU
%<%^�������j������FM�mT���v
%����5� �E��m$�E�������}��4Z'2J��#�"zZ��d�=��=���w�4�D�v��_B����$��l\Nry��3�|V��X�Z1��0#�\�u9�]!�ij���x�&��mT�O���`����Y}q�p����U#�H���]�.��e��E��]Q-W����F�#���`Yh�X
��R��+�@!��O^X
V�
��~f���w5t�`�j����=m@�
�S��N������A_���/�=�fy���"����u5i	\
�N�}ws��$x�����_���������5#�~.�EP�Z]C�	�-���b�_�AB|���G
O��F�d���������jEQ�H��i�)�$M�dE�?�(>\1W(|������f�����k�
� "���=|�~Z".�;�f����.����|%��[�
"Hw�3�gd��l���"(7��������ff%T�]PIX^����-���V����q�[�����r[f���s�C�����v"o��^��We0�gg��R��yX��$�4���[����SQ�C�a4}�}6"�H�M��
F�d��%��R�\�k��;�J��V�Z�L[t�KNf����/�V]�m��P[�u�;�����=��
%�����d��v��3c7����>�����#������+9?��������i�>:��|��v7;-���
���a	����}����w��DA�i�CoI��A>4�>�������=����Og�i�Y�r���9�����B3���E���UZ_���d��M"	�������!�f��*��� b :�8�����Y�$��i^�lq�L<�����U�:�5���j���r��r�Jy,?':���}T6����3 �D�R8s���Z�f���#&@k�'��*�����H�e8^�H��5Y(A;3T��e<����%����R\�ws�
}����x/|MU(�V�>�)�S/\����e������(,I*-\uaa3�P�AZ���~�5QrG3��F�#�@h~����V���
�+���p������D�"q�$K���* ����z��@�^��y����2i0���/�`IO�z_L]�,'E�8���U����c8VR����I��+/�G��0qSs��i���C���}�4�����X�����M���r)�����$����T��@P���4.�����4�rr#R����A1�EGc4��K�Q3t����������;��&y�����W����f�[������QeQ|��-W�h%,k$��P�$+,M�3bSn�%�N&��]Pw)��{������8H~Uop����s��qq������+*i�K
�����?�������M����?M*Q�#F.Ks��,n^����P���?fNr��6WF��U4%N����
@s���X�m��	62���6U=�������IP��r9��X��82�$wZ�"i, �������M�`���q|O��1���B�<Kn��x.y2C�
���Z8��pCAF�h@\8i��������\�4�j� ����J$��G���z#�r�:\��a���C8���vZ�������`�|
+���5'E
�FFK��x����������+P�L}s�?��W#C*V�*1���<�����*�6/(Y�1�X^����t�U��e�p�f���
�#�P4�\!�*���������9������VR��#VC�)��9i�&R]��xP�fJ�Cy�|�yLcL�����V����5g���h��G��t|������8�&iC�����W��79�zs��y-1��W���k^���T��<�������Tr�Zv�K��\0���A-1�#/��t���H�59	&9Ls�Q��Jb��r�$jvc8[�m���)In�
b�����Fb����*���|p��!v�$�z���}�j��t�{CTP�q���x��]8A��FXT��MRdOE�H�6���@��t���-��l>k
��x���D����P&��!iJ�: �5��{��K����,���jV����cJ���J�\�����V�M�����s�;����pOS��������)��������t��7�p���30���i��pT��01���;��=�:��d0��-�B��6.����_�n����{q�Y�
wX��/�S��mC�����C��
�����+��B�1��
���Y�����%�EI9��o�Qa/�Umt]e���������jm��yYu`����L�E^ ���,�)��nln BHs�s�����v���x��������{�, �$�-���"r:KD#EPn�>���m��9�����,��|��fb�@�]�M�l���,�F��>Pq7O�`s��*8U��z5,O#A_��g���Yp	���]�{\�2Lb6;�R�@�R[,�:\�����*e��������*�>��P"�-�,V��L�]w�����o��$t�����1����>7��.W�x��2[�U���3�"�^���8��.Y��N�yX���J\��~D�A���qf�,���gca�)�A,��,��SG��r.n(F���n�f��q�U%|>����J��%�;'xd�17�X���br�����Pa�z=mn����AT,��s��
��G
/���OS��|p�4pt2�=�?�aXE����=�>���Z�����T��w��3�y�q��A��b����B�c�"��_FV������Id��i�������"�\a������%�6���CFW�j���K�-�.��%/A��� ���@cs@j{���$[��k�����u(HHA����C��b�� �pXx�0r/�7^B�fg
�UAba���z��8W����d&�Ft#�C�8b�	R�y�5��@@�L�����R 3_����( rp%-�g������1��U�e�sa�-�"�~��p~��6 ��6�����YZn.!���Q�\^9<��`|�.���K��H����[��C:�t��b��<��?��2M���������x�P����7�l��\��"
�fV�B��c<�&`�_����ve�C��"�lb����9����[�{o�U�N�s��AC��,5T?���`�4u�����'\J��+�a4[$�-������[-�:�����,�+^Dn%o	�)2����e6��7��0 :3`D�iLb�L�����l�:%
�d[R�
��5'"��w��5�qX�����As�(LJ���kY��������������O������Z$��r)����7%��E��
�dSg�
���8��\�oN�@o�����&��-�����{fH��Mz��SZd]7�>��^G������ky�x�A]���b�x3��|����"FR��2T��T��t�:�L���l���
��:I%�e�6;T���Xw�Q�
d#��Q��|!��{��tr���7�9��e'�H��7
G�
X�9���	�T�2���z�M_�v��������`K0�Ua8�����/xhc�z����ac�hB.Jl���R���]�cC4�+�X�!:3g��5��JGD����P�]"U=����Ut�H�.)�,��6g��q�/�:�"��
����]*���p�+���0��}����D�K\�FR#�K���&��6^��,�����
���������{X����P��jE�}$�a�>����&Y�%j���B������9�P�`��[8a?��K���������G?�<j�-h� D�a>(�yR������p��i��I������lH��v@��<�����eQ.��r�O����:�k�6���F3aY��U�e}��?�a� ��X��"N�����&/��z�w�����v��TP��T�Pw�yZ3������4w	�&�.�{���j�9�R��K�t�@�-�rN���P�q)Z��c-(v#,�
�����BP#d�[ M���F����`�������#g|��5�l�1g�"��>��fQx������:��������q������}QZ��T���2BC��A@�������yw��C�_�M
58��^r�i����|}H����h�����x������1���>��������s�;w�g��d�r�[^��{�8�_��R����bP������6�CgSP�N�b���UA�]��&�-���c��mO��c�����"��	gz�'�x�J��R��(���g?����x��U���)6��@�,Dq�~I���\m��dw��*����ax������i��O�9>�
�����-8
��u����RvA�d�f���Bj�Hrh�h_�B��o�P�V��n�r�����1>L��C�/����JF[���I�����M�+��������s
;��AfD4��Y�Q�_��P����;3M�9#��:�L	�5N*�3����o��C�
���!	]��
�e����q�����:��'���q���h��P��[������3���Hs�S��3���t]�r�j������4~��*�03�dP����a���4�@��h��x��6��)@���2��,I�4k��'��i���-#��6����r6�!5ZLfTS�vp[���|���)�]R/��t��!�gb���v��^�<����tM��&�A���+`3 ��-��j�1���h���P�.h	(���"I�����kx�u����\r��(�|�Xc
h���9�a@�N��S�(�/gS�8��h��|��m��1�G��jL�Y���	;��*6l=0���5���G����w�~k"�2mz�X����0��H0��q��G~�:>Mt�����<B���l�g�2�k��>���
T���|1���-��KH�Q2�2X]h>��]@�XTF`(&�X���������
����uh���KZ#r������V��H�C{���#�T����<�����!�W~�/LG�`��8���8�MK���a���^��hf��?������� ��p�F�	��if��t�������
�J���#��|p���_�-�������Fr��r���*+�S
�����f�=�~��^�No�O����w
(�-y ���3@)����|�<.#�l����lG�T�!,K��W����tenb����S�H��.h_���A�r��63[��x?��rl#��VZ�;D��%1cC���_�4
=���E*S>��_�ai��?&��������ZSdD���~��v5,�5�yK�G����m4_��0����xf7rS�/����R��M)"`]����4��"���g�5E�a.��u+��"��A��-�F	���S���%C�.�JV)���\i���f���V�&1�vZ������������]�]:a��N�S�bE��Sw������&���%Ft	p
q) ��}O[�}��|��
:��EI9����t6�b���#��[�����E�����W�����J������=����Ag[���]���[6
����_T�/�eNm�E�l<%7�#��xe�������cm8L=����L�������`�'�Y��c����yu��N�4�����$(i������
d$�_���<�����BVL�9j��.�1!@ ���
�;���ib��yY\-���~��Q�e�+]L(�Jg)@�JA��Gkt��0W~��	f�b�Lc�:}�2���uof�p�b��@�y#	L=��	R��I���-H3��3�t�t�����%�?����������{�w��f��,�_��)�Q-~��+?U�D��������z������� �������l`.�S���b@q�t�7<�}ka��4/�����=�9�~{�?�F�[L���4?}�o���EK1TQG��E��P�o��*��u
 �S,�'&xp}|-���f�%����F���7�b��~������y�RN�B0�������V'P�<��A�GFK��!��h[6����
��l����1:����,`�re'�5C#O��������ZLL:��C*)�E�9�T��bj����B���m����M��J;
yq��*��������E����Q�t`�`���h�����B�	[0�~%Y���"(.���c8���9m��CB��������|�����
C��r]�7��C��2^���� %g��������%	F��9B�L��L��9�l���������kv��;��z�����f?���6��=uq84YL���]��A�q3�P<>��3�y-t�Y���H �1�i1��O�!�e�F)���D	��I��uFw*]���������
����&`J�V��'X���_�<H�$E�������������^#0������P�Q��
� e��T���Cn�j
��E�>"��}.�>�S:5z������|}�9SRyp�c�����F��	�����Db�y'��	�B'}�4o�P�&m�q��y�1d�'��?j�;*�
'a������m���:s#��_A�$G�����$8 3$1�'�b�f���3�"���}f��b]C/����u�L�b|��-��5���;C�Y������J�l���C8[���
������E3�d5�����������U���%2^������_�m��F�Y�������1-y��6��8I�+�\�{�C��P��T%��6v�K�
&\�j�R��
5������A��x��BWX��=�~��&j������Oh�f����oZ�\��i�Gk��:��)�9!m�4�)��hs	�1��!)5��(��j�"9v�a�;����b/�`�g�	���:��O9M�{���NQ�'�x��X�S:���0��8��J�����n�����d��_��A��n�P�G���\\����of�d4.��Uk��iu��,�I�]�Zz2�ce����������\��b��1Uk�����,���t��[��0e�G������~��������u��~�o����W�6)[�r��Np
=���Q@�Yu%��C��'K��� k�"T��Q�&�|]�Sbf4Z�0{���rUo����L	*�bF}�A@r��,��J�L���u��|����h^��x4�{�B���,���i�A����Q��m����SO��D=LW~�_
�6H�I��=������^��j��Xka�D�O���Z��7=��8�����,���n�v0����_h{����q��:���#���23��R.�������"�������$��I�s�{R���PL�iUc3�fc[���U�����!<��
_�t��W�cNe-i�I��Qp���*�����SO�F�����(���J�J��>�Q��?	�[SN����ETX�F�������j}�����V����L���*���F��0�*��i�U�R�7���y^�TaL+�=������}�����D���m��q�2�TD������?����,�J��X%Rl�������L�jW�d7���2���h�,��r�Wr3��P�&N���kW�p��������qU��
����)��/Y ��X�E�\�E���������R�)_$��]����|0���HG���������;����>��m^nj��)��j���\���,w��k�,]��S��E�O�z��S��5��Xd�G��s��K��}����kp�\�����Tgho�kp�V9�@&M�i��S_��D�pO��y�r.�G��y���d�=����),M\�b�l��
�3����TyZ_<OJb�V��x���z]:���c�7�b�"<,-'�����/���+���#P�+@�@�G}f���"�,��
�r6pa���5{�q��g8�V����H���K����u^yE���k�\�7�2e������i�!.��)���������I��A3�f���vv]"H�����
�fk^I9��R���k�j�y�&}��{�6M`n��<��O&�`4b��5�����+�������~� "#6c�O�R-�)+��	}@j(��v ������9�QLnmym`5H�:N��'��\�_��0�
�.�y��W�g�ZL�^��@�<�D��r��#)�J:s:�Noj�j�%4k$dxp�������'���e>�f��o�zq��*�,�\�-��&
�+�.���f��� F��@q��11���\�a��z0�
tM��i-o;��
��KI
1G���(�#"����R�����s0���Hh�$��PQ
�)����Y�����yi��M���<{W���m�o6�V<�8�W �@�����f������!>���C������������eA�)yiMj��sP�
4��-�u�����b�b������;�i.��rl�WeeDk\����_���s����(��p�rg����~(�!(�~��\�[x�5W�\���	k���H'�&��,�FI=c�$8�=s;"z{��4���u���;0�)�d��q�%�=Ks���5�k@^�Xm/���������+������39�YM�=_�o���Ssr59L��G���Dp-"
{9l�t7oN��7�b��[q�(��S"2l0��7���N�P.�bh-a����fFe���V�4�����a"g�F|����0B��X��m�V-��pn%}l�C�[e�  ��4m�F�e�J��v�������D����<��i�&O��\�5�a&�h����YU�V,}`'a���3�q����YZ�s���(K���5��i|b���]J���m�

{��;�!n(��:�MO�R���S�7b������{L}��fP2M�p���
��4��`�;�<T������=8�-f)���Y���!���������w�-������"
�]�������A��)�^������r����&9���>�<��-h��/6�R�fC���(;[�V�u�d~9+�:��H*�v�8�a���9S8 ��Cf�0���\��K~X���g��a
�2d$e� �����dT��E8��l�5�������;U9RP���"qZNn(���F(�X�����R���b
��8�T�M���|������Vk������
�zK�b*��Jq�+�S�����GM�=�-���%�#��A�K�@����V��1�fwj	��f����q���������mmd�����ST��-!�����i� 9�I'WOIU�:�TJ�dL:��wM{�Anw����y�U����^�o������,�;�R�"���9!U=��)lq�j��'|s��G��	��/������	���A9&w~��*f�f��D��3o���8I����U� �d5��3���'�D&+$]H���_�����[���4lI�-F��J�?�M���������?A]�O�OAc{�����!������V�0�A�����[A�yk�������6�_��mn����F��8�����m~���cU�6�X�(38��)V��L2~���G�P>��c"��dz��<E1��V�2��h�?D�	������/O���l#��Q�6������BN ������fI��,���L�*�w�Hj5�1���d6�1BU3��3O�H��������>�=��t�5����	��d��2�yzF��������������`�i��QB���S��D�D=9�B���ZM�~��mz���G hL��!���9-E�!�C�n{�d$CN�C|�s� N�y��v'D��K���2�����	��B�F5Oe����&�@�q[{Ec��[�W����3�Q��J�uk�S'J$:��Qxv-#�@�	�T�a��~�������5�;�0�\��;��������{o�>�O+:U��	�z�����#r�S�*��dOL~����
���J9�����w��1��_~f��]-�u���w���T���ap���t�����z�t��O�SX��Wt^���G�S����C�k������)�jS����k��ot��.��3#;���5����{y|z�Q���Z���x4aA	,1�@~��Ya�g�PIw�`�:���x�������(C��%�8
�3������	/L�8����	���,�~�.��Z|���}L���,�<S�|�+�����@�s=�����-����_�`����\#��$�My�d�{K8�]|�9;����UkY���rj�\2������V�!X��5����(��U�r��M�$N?YL�
k�����Q��z�I�q��fK(Za�\�V���Ya�E����E�l��iQ������I�lA�
���f�W�B�2�?�����g�������?|��i5d������M��Zd�r�O`�(�Va����a�tE�m��Q�����
��M��"�k�g%�m���g�rwVJ�T-HR6Q��"y���&�^9Ruw�\��l���@���T)��;4|2
���'�X����M���8��b7x>	�T���sA�p�:��
Q����RJa�5��T����0������Q0���`�c��P_�'�m8!��@,�]�����u�5{0�/k���),���)��L�������8z��}(�.�S��H�`K�D�MVM5�J*�C0�},)i�,�?�*N��I�sbMqv��s4���B�iF�
<�#B��JoQH�� '~�P�� ��+��t�W?����U{UI]m�(@��*�X�'�0}�d�O��y��E��1���4�{���0$Q_R��� ����b��B~��qz6�S�O���*���cB�
�=>��V����A�5eS�~F��e��WA9���("5�:D���2//���:����8�pJ�\�szA^�Ib@��#���:RVj�6����@*h��Ku��P��f����u�����������Y������n��i�:>�����?�N��xJ�m��Kj��y�9�N�'�_�h������$��������D,Q#�+�V�D���o)_�r�eq���7�}F�f����?��S����)�]�I������F��c4��h�y��:�Q������G�\���h:(y����Nq��A�L���h4B�����N�X��#�������M��@�e|���,[yI����3v����l�Yn�7r���Q��4u���[}��mSvC������9��g���h�>��O�'�)%d��\����]����
&c�������#��$R�5a����CBWS����u�pU��H��69<�fM$^��=���+_��2e7���c�:��P1^r���v?�,S����;^G��M�+�s�����p5WY=������Y�vZ��������i��~�jE� ,j�Jn��5�AjS}B��pN��@�:��+���4�>�=�ocvk*��S^
�y�����<;�$�GOl8�V��/�ja�t�$�0��
{�e�_K
��_�0���P���|b��"�#�v����R
�9����������zE��J�|SK�$������c��Ye�]'1���]��Wg��,�G����(�:3��O�.e�HqO'�h�O���{�2�����h� �`U�^��gR��4g�x����:�����������!IE|�u�$�y�����,:�{Z0{k�gu�4�G��B_��/Y���-��&���9�6����G��ug�����m���R����Xbvi���M|�����#�kg����0~�!�gD;�	���|0����T�^��+����Yb��)�:*�P�
0j��`��l�J�-�
9h*�]����fh�I�j]��{��^��T[[A?T���<c������*����t<����9��7!�hW�����<�8�W}�D<Xt�m3��
������ ���\C��	j^8
���%�J���p�_�����T���MI%8z�9��p��\��U[g���t6�+/K��!1r�b����5C���c�r�����i��e��7)�!�������]�Hfv�.u2�6���#�����4����.���
B����(�^���/��t��|���r���v�	C#U��B�)�N ]���X����Bx\=�����
���'6ab3jl|������U�Yw��weE�s�HV���1�XBE}�=��f�,�����,�e1���v=�j��(c���/�;6�^-�<eB���?��W]\�g_6_���H��$�Y��S*i$K�;�+P�
���V4�@�%F(=J��8W2H"`I e5 P�����V���	�2D��6)H;\�X�}�-�ClA��s(���n�[�8Hu	�!�9��"���<���ga�; {��wu���`����sX�_@D����sx��H�z +B4�-�G���Qp���NQ����:����dG��`�I�Z��t�c��*�^B��T`s>��T�n�hmw*�V��%W_��������y�f��e�:Ct~0[�"�;�������o!�
�PFB^��X�YoCL'��`�zl�+X�E���������HY\vF�ub��qi�3�%��E�����?����C{��-���/�i+R�<���.q�V���%3u�<kS[xW����U��*�H���RH��Sh%��y����)���qYBp����QK�����"W�C��C�O�t��;��p7Hv�����I��8<1$}�����a���K1����!73?����a�k���O�a'��+�>L����T�y�����L����g���d�~�[R�fN�h�FZ)��=I�kPu�H��W�N���]�H+��n-����k2-zB���n]�Ek���U�:R�����0���i�d2�m����1@�E�=�����g�q)��du	Y������s���������	�9���,�0 g/�/t����������+�h7|�\y��<�JK��_��UQ�����&�kq���M�d�U����i"G�/�x����11}}R��$����.����
�J��Aj	�*�G�9jD��e���\p����r��#�-��!��PpA����g���>
��J �a����1X�45��1�w��G|�����R���(�n�'����U��x��9��w���0R��Q]v�*%��L12~0���S��?A��I]�WN1E�,/M���l���"?K�bd�>��	��	xa5�hl#
Lql�Z���Iw.���);}.�NvhY�����J��6�������8G�>�7<+�8Zq����+C����f�j*�06$�����g�������X{��:��u��s���/O�=�}q|�����E�UM�����(�������SS+J{k�����r%��?YDlJ��Qv��B���aBn�����O
tRX!b��}�>]�RUZW�$��Q��^^��w-64'Z�=+}2�b��Y$t����Z�D����D!p�����6j�:�U)����~����w*<�/�1��h�b8������0��|S��v��G�X6=w�R&����J(��26iQ9��H�Y��EyC��M�+���6<��MR���/����x��z�W]�������(B#��cg�L�KM��Kq��j��B`�#=0��r���Z3:U��P���B�)���<|��!�D���4Z`|������7��2�2
Rb�mB�����8��Ss�:X�?����7f�
�5s������d��	'|��tE��������M��He�J,���e�{cj{�"c���V�2['����b��h��;J�����[E�7��eM�����g�g���<;P�\|�f;�(h=�gjq�R�ei39
t;|���a-�N\ Nl�{�4���>H-�,
GCw�M)�}fI)bB�������t��Z=<��h2��B��.�����HT6��b�YL������S����	b,`��t�q(lb ���:#_��#��
���G�e��r��l�m�=]p�<���{Q����?k�p9�1{��k��\���_�$}g0'�(���7��������p��.:
�RJ�����LY]}�a�!s�j������-��LZ�l&k��e�_�c"�{+%�=�	��o�������F�������H	Vhx���|���\. ���j�2����'���3�3�(<I�^�p���#_.��X+9�i��\�����]ccY��!;�3���AV���-� 1
�p�x`�[Z�8b ��l�f����qeF+����	����m�4�/H�����3�TB)�i�#�������e����BIu�X�?��x��=�������[!���~i/@9o�q���Vm�<���jl)i����H�������M��O�}�u�U��rgC�����s+%���|3�	,P�E5|gbT�������TL<:O�S]eE)�P�R�_�}@�b��U�,�`�����0Gb�$��d�:fW�����yC;��<����ki��P����u0z�6��a�%�67K�����>8p2��
OA���<��D�v����!|q��>���lN��*��VK��r���@����J�|TH�|�xf~��n��1r�N2����T�g=��|I`=���P�Z���:��Y��x�L[J
����Mi���4��
4��D��J�F����s*UK�����I9vT����:#�W*{4��{3
�1�3c�+��3����k������x_�'_�6��o�@�D�G�:�\�R����I�|:!(t�x�/�1�� ���3,E#�v5��g��"�� "�*�(�������5�V'� r�E���<B7N�j�g�%�=}������LgP�0�w�>�G"NJ������M�sT09G����:��o�8�z�
5��x�Y��F��`	B�@*��?U�>T o������k�a�y�0�^��`�Ik�a��e��5��b��������b����_�3��wEl��?M
����@y9[E|��h�)r�vT|D�qD�<\��B�����z<4u���q��]~��������Br�����K��v��)N��H��c�oC�0x�2f���w���O���p��*1)�\��lD�F�EZ��]b+G���\������Duld����v������<>�����%�`��me����8�,q�|oq����j!w�[.��p�oN��V����U�`��>�ExY������'sQ��a��E�zV����t�����i_��3�:�4R���'J+F�w_������f(�hJn1����v�'Py��	-zxr�)���6��Zg�E�fH�_���S�E�����$V����?�Z�W�\���Fh�F�mOnyJ�B�������\������;'x�uo��Y�$�l����.�B@�>p[�
������D����X�bS0��p�)#��*��(��$l�O	����D��t*��f���g	����51�3Z����n	{����'6�A8��4��7/�eZT_!�D������q��M�wy��u�___�t��9j��f
p�����IUk	�\�Bm �gx���<�Q&�cV$�t�tB��/hJ
�����z��Nj�Cog�����8�@�r����lX�p1VuN`M<�l���/�]02GbV�&q��|�Vh��C�FPzv8�3Nv��!O�R�":i
:��L��C�����M��� D���|����4���`�d2p*Y����r���E���Y.}�7����8i�����������f��'�X@�������h���&�m�^qRS��<3�i<�a���P�rtD���
4/CY�
<K
{{:��P�D�d�'r��+�PYZ��w���{�s����
��xX�;��T��BB���+�Yn\���7�\�ZA����K�� ��l;��Njei�������Z��4��qM��o��K��j��%p��\e-6Kr�����wcZV��)\�%:<�L�co���v~��t�'�j��\�mC$���7�$6�}�<cE?};&�-�v{�d��F�k8����O����B�[�!�y�G��Z7������p'T��`PXQ��G!p9����p9�[����`X�����q0<�
ssssy[�E��r�ypX;�6�����n�q�9��$�(�:t���H��l�;�g�1:2W�x�>���Z�����R�V�"ap�b������`Ol���RxFq7����V�"���|v5Y����P�������Iq�'�����x��p�_����S���R����A��m�D�:�l8`��`nA���^���}�B�Y��J�T������a+��o4��;�Vko�.�����7s�(��Fso���m������������{W��E�jd$�77W�t�Ep�zoo�/�^�}�>�z��r�t��x|�G|?��i��z�_�	����s�����x����>���`�N!�r�t���d�<;��=�E�r���^�!�:��������JBC,�����qh=�O����i�����Ae�^���n�k	�+*#��p����O������5w���`��U�E
��������
�/��'^0A`Gv��[/x����������E��#"��Dv����m��=���k�6q�Ukn�8M�)Eup*����8���k������~<�;	+]=�;pN`��z��'��;gi�1z*l��L�������fb�����xP	�T�3oU���h�m�<��9RdvL5p]V�������%�z�l�q7�R�<�6W���\�]d�xz@��[���JS��n����z�L���}��#�(�2�34�����Jy[�����������9������b��U���YW�����.{�7������u���^�(����������[�zi�����V��&@���}]s��R$���-{��<I��w���{f4&>����Fv?�!{�}<B����pE��(����|�+�9U�c���2�Iq�*���X����Z�l�6r��dgX
k�G'����������A)�)4��W���6J�N���R�W�}���K�BEY��b�h�]{��s��s`5I��d�3���4�L��&W�!6��U!��h&����w����5�Qs�mB
�@���^D������`>�n��Rr���w����?��{�����o����e��o���x��{�&J�o����
SL4�������|�	Pif���I:u��Mt���n�F��
�R:Q��q�$3��R�l�"��l�b��uk�gAc��a���\w�����T%p��'W���]�����zH���h���d���c������4���'w�(�������H���*������} �(F�B�"`�+����E�����9%s�_����pg���?������-�������o^�UVs�h�e�R~�J�����c����|��/e���������`;�~s���j5�	Liuy
SZ����F��p��Alp
�\��[hmG�,��zB�~��s�!�
��Jz7��qIX��6%��M��!��Mb�^�Tu�s��Q(.��k�!�p����9�~�
zl(����/�T�EO�������.��]��p��DTL#�	���	5�4����Z�	3����5����#��&��o���B�(��UX��%���y�gq�|�A�T����TZkf!�qLe����1w`�b�F�]X(>�G������hU�-��������M��F[�vC������^����#�kR'�D=���3u�f��M�OW��WJs��w���I)z��_7�Z_���(Fl��sU��S��\��*8OP�����=<���������"2+����2�� ����#>z��E��'�Ng�����5�T���hl6��[�7<�s�u���
UZs(��F$
�C��+��h
��|5
q���~���j�<@F�
(���K'[T������vs{�^�
?<���V�������H"$�C���Q��hr{��ol��d^�ke�M�O2h�\�N�..���n�wv��~q��>��9{BW����e����o��h�dc\�*
��N��T�r��I���MHX�P&C�a����%��;�qk�������::i��X�tT0#��&�=RRJ���Np8����F���h���GYm��QV7\��'����O�I�����+����Z�Vz�l&�o�[�R,6�-d���}���X���=T�����M���Ka��W�HzB�b��gM(��v��U���>����N�1�����(�_�%&*D��"��SW�����������p�Kt��/�������^Bn�-�("��
���H2����H]����
��"�m�/�$@oW��s,���X��H@��K#����*`��lct�c�"�}�6o��vTbc�	��c�D ����7D���$3��S[U���)}0T7�/zm�\��.�w�2�&1�|c��D�Q�/C�k��]���
&�����7m2rN�NY���V�2d(G2!g������q�ts-�%d��DF���^��T��,�'��[Z.��:�5N��]���G�"Li&��f;��3�F(��&�J�����b�����"%3
��'G��;O�`dW�����������{�
��i�;��
t��X!�;HI*�����rC���9�*��D[�.�2}�����M��m��y���<Iw�:�����6tq�W�Z����&��e�A���!�4�B���������R��x�H1��<A��S��#naJ|���{��S7qko�y�A��=�������7�U����*K
�C$-��h�z	�Yu?f��U����h
��N�o�;���d��9��6������4L�sX�w�^Y�/����nHS@)���p������7<����w�,TT��B����	�K������=����X�����_��{�Z�c���N;���i��
���b&g�M+�����I�	���.��5~$�n���J	�a�u���c�4&�~q�����b�c�[v��,�
���!b2����I������@��k����$��z8�2������G���$�$�A;�a�A�g�w�+���6r�5��#]z��2r�Z������f�^o��}�`w����8���C��c���J��%��G����(�
�k���]�����&^8y��]�����{���0_/���_�	����)g�z-�Y�������������n�w~���`d�:L�������S���E��N�������0�4�sgz;1���@�}~�._U�[���y�*�����g:4���{FN�+SX��Q��<��/��K����j
����y�W�����ww�����U���Lqr��AG4�/y��uL��fB�'���=2���H�/�/��+��v<�"O��?=������MU��z�1������pv�hd��g�^Xr��`�� g���bHG[<#N��;l��Z���V�~������ �/_���Up���A7+[<uo��2��=L�QR���g�[2����z��8��S;�1��\�&����?������i�_��@��	�1-����V�ih�����j��A0�������V?ll��VPi�z��.����o�Rs����`B�\�r�h����bbiZ���G.(okp�t������P��HA�C�8�@��3�/-�������q��^����J�~����������4�V	%
�����Gk�vk{�p������4�v)�;v���������o�A���8a�>*�����.
�dpW���D���}@���N�����A�G�����z�N2�lR�U�f������Z�a��(d�������rg�D��� �]�i�d
���y���gl[E�3�m��m������X
b��'V	������|2�&��"���e��+V-3�E�I812�*��Y��g�����I��d�A���VA�9t1�G6
��.e��F�V1Knv�#�*?��>;*����9e��F������	�T����:>��wq������������V&y�9|��5h'i��� ����5�/�����o%�nOrjp��}��U.���6�i8��p��X�y�Q����m���L�^EWEP���t��z�1���B[�1�m��z}'8�
�AcH�z+?oM������p;l��a3�vw��Br��� 1��6	;>�
��?���]
=��B�#%�>�6Oa��#�����Ctb�0L�S$�j:9���S��/j�Qu��L��V���2�R��zxE�>�>�`mUCT��.i?�C �)e�H�!���a�����Go:�(�VL$# �<$*du�nU�u����N��P����O��CB��A�k�6������6�,��b�[<���M��tIM�x�o�t�B��O"�g5�	8������V��6!���pI�������e�mq�(Q#m��2}���-Q�2�b��X���2�pl������|��:bhC��4H�>�q���j����{��WR4�����/[3�����N�j��U+*$[@W�pVyZ��5o��r��%��o��K��:������?�'�w����m��U��������
��tG�TT���2@q�24��D��QU����O�
B�"HH�@f:�w����SZ��`���9q�����Y$��C��P�LXy��Q�D�"�8L���v	����1��	Q���8�\Ed[��&
�t;
$%���~���E��
�q�V�Z�t1����4��
����5��R���EU)4�M��`�~�p�<89��Il�;�eb��'s^E���?�	�8X��#+;������|��������d1��*�d��<�C�����s�%�Y�,��6�^��J����nN�Rl�z��a����a-�!�i ����H�Ei���
yA�n���������N�o���w�����"�IY$�L1��d����!�z��4�(��_�$�X����S+�G�JQoI�YU�P6d�V�E�T��������F{�*��1�DY���9�F�'���kOr�hBJ@o�`�YGV�������`psv�dV�"g���,Y�;�kl�XR� L�2�R�m����Z2��}����E�ayt�U�c���R�d����>������L�L�
�t;��JB^u��ya��X�r����-�Q����.�]#��Z<��}���?4���td'���v�8>�T�oO����u���x�j�����8�s]���	b�����n���H��*��C�r	���E8�1��Z��zg��aS"^J��>rM�/8����j�r}���D�j�T��7K�6�'��s�O�q�TQKJ���]-�!F���+����p�p;�f��e���u�G�R�6]���No���)_]�i�q�2nPt�� ����5���I�5��1H���/Y�� ��i�V��T��K�X�X/R�	�.{��i{�:V�����Ohv [��y�������b9��Q_��C������W�>=��N�nn�_"���9z����{������./��.�s����D-����"$�>��0���t+�H~���������[����n����d���:�O�0��38Q/`�Y�F�����������~L*a@m8u�)l�#�
G���D3�F�:?��`�V9-X2-�	B�����+B�����0C��� /%��R]?'u~�.s����b��1&����z{e��.R\��N��"G�O���K���T
.�����o.�S����
��7S/>'��p>�uD��E����H
���9�g�����b�pZ��-�d��-T���W������c8�B�tY��~QL�����SM���|���6������c������������Q�Z�&-`,�A��xud@�)c.p��D�>g�NT�0�Bt���J�`�g+
;���t��7���~�����byM[$W�|��;h
����yK��Q4=R������I���,����������w�(������P���T?P�{gsK����`�,}���~��h����g��7�c%��Oz�m��P��tcb�����q�/�0�/c��3/X(���:x�~L�1���j�kD]�A��Yq��&"0���DW4��2;Q��0�e���Hf�����G�53��^^���I��<c4����&QT*��J���u�S����/+\M�1�?`!�T���`�����bD��R�Z�8%Eg�]q\�tF@4�rR0&�������K�"�5E's��^Y��-aQ2e�`�Mn����g�%q��K8
;��P"���`���|T�)��XD:[��SM�����HL.�u�!�+�������H��j���M(A�?�
QG�)p_�j]��B�V�W�g�X9��V��,����Z|�B�7"2��%�����<��Z����&C3*����L��t��oBa�%v�6�@	�1�op@���,p��D����(dIp|��jQt�s#�i7{�K�q��J�Ncq�"dh��������]6�oZ��T��[�]��Z�L4
MTH�����2A\iI���&:�K����O{Ct�F��\����m��0�g��8��L�kED~Ze}d���q�)XQ�1�BXL������b�*���(�L�fC$k^��EF��-��#8�#�V�
���$?�+ra��������;]��)n���K]�v=9ac�V���}|Ua�DF�����.���I�&��=Jt`O~"�����V��[�&+x��
�V�����"�������iVfk;�:��]�����=GF��If��_��X��A�k9�Z=i��c�(O��qS+?�=r���9�,�
���jD3�%c�qn�[�(Op���$Ptl���lu%9z�B����9�k�a��=��]q�}��#���r���W�dCs���o4w����D��Q��|m�w���(�S��w�R�o�tI	���CY���f6�l����l�7W7o�]�S��j�B���e�98����x��vr �HB�E�&;)�N��
��MW~����D����#^V�g�F�Ko���.S^���\K-C����{6���v�-�}	�$*# �A�:���|�L]�{tU
��6��F�B+Sd3���
qM�>�b4�(�$gi�kX�29L�M��;�C�[�������/��7�`���������������
NVM���R�� ��Fi���3�2-�~�}B��#S�H���E���W����Dmz��G����R��)X���_�?��Q\�E��u�����Sf�"��R5J���'�$HeC�FS��~�9-s������)�2�vy�
wNb���: 
��h������u2���%�p?l�����z�ppx8<�6D�.�1����:���4)����I���Q\������+���!�,�������q�m�,���!w���
m�Zg��>�"�H��VLR�������Wg�]r���<n�H�O4AE����������V�~\������Gu����PY��
��-�{�V��,��*$��GT�h�����`�~8>��/O��t�.��g�����N���dB��?A����Q4	�EH�d��-��b�38����Z�)������nk����Fc�}��,.(���>d�1";��fE�,�GJ�o������
[J��B,hx�H'��:����Hvd�>���K`.p����$�(^32��L�aILW�)����2eAMl���lX��!��T��t�W
��[�w�\bP�W$���x��J�^.�~H������h�
�9����3�IS��Q�@$n�;2�����j���^GB�B�e��tL�$c�3�d?�}yf���,������q�R=����?�A�;����}����8�I��>&�11r��w5�s���_>7�a�`Y�����]��r��C��Rm���f>:L������tD��YV�z4Z�Y�aO���6Z���.�s���@�Y����A��2�&���Q��u%�L�i�qi����.��w�u;�)��JBi����Ak�w�s��4�$�\5ed�*Bg��`K�*�cX�N(��i8�`�r/:Q�G��c
%I�,A8[��V������5r���9�bP�4�X��R�����"���%�����d�2��?$p�������2���m�Ch��&T&�P��b�Qo���:��7H�-��s��0}Fkl�����X�
e?~��������L%�z�u�����6��\]w��.;|j�O�. ��j���y����KNs5v2�PT:�'����)�1�H�z���#kv�W'���D��9������Pr��(��YA�e�����8��L7#���[�����
^ B���#������[�y�oOz�;��N�[s&�u.�u������V�>�.	�):z��.�0��z���@�zg������<������m2yQ���~�S�����P3�A3������z��9�FQ���P�&��yOsa/�U�]���K&�r�H��:���2G10��2
���SJb~�R�D������"&��y���p���Jij� I���������w�(��'����/0��s��d9�O�5�Zz��Q���$���u2�q=iE��$�}����":�M29�j�9��4	=�5���:�?�x��8��JZ��Wx����I�Oa4���?%--&���a�<;�����0'X��%a������7'�!��;L�0_�����9�b����`��N�i�RQ������`���G����0�
VU�3�H��e\G�������M�L�0=�o�}�,�I=W�~���isf��}������|��	CE6�l�\dq��mr.�L�6>�������	(��?��^P�>Pd�$DV�|�|��������TRV�y��$�e��$|��n��X�p���.�?g��}��������VZ?�H�Bs��&h��I����7��'�d�	3�y4G{�wZ{z��(>1;�5�`��+�A��gM�8`�zS��si0%L	-�������!�r���#�Xy�WZ�i��M2�IE�P��7������)Tvw����1OX���K�*��
n����:����d;o�M�B]E�#�*3��C=�������x�Nz��9�gj���F	���)
DP��J�C�������R����`���'"�q���h��q�I�Q�zs��E��7d��������
J����P����P�'���M<d�n��W~���?��9�I���$�
�7�Fa�u�)��P�p%>��8��oY����	�F�=r��O��!;U�A�4s������S����������g��H��u���s��c8P�����9��p�mh�;u�+�[�%��Q����}���<����_�7W��{����D��4�'��
x���*�������RT����yet�3R��l�h�j$�=<�<e����Yz�e�I"����De���<��
�����(r� ��i�W�|���f/aL���$d4!+A����#���h��v�5�������o4�Vs]P��S�%V���)F7�������9Qb���1��Z,���'���(�|���0��6t��(cz��x8����������7D���0�X:�MK�.���^$���Y)�R�g�Rf�����^���J���� ��E�����.����=$����%�M��^������1*l���+(�� �ly ������i����Qt;	��5�����r)�-��p0��C���P�}�}�L�AnU�0!�M�����$T}R��K
>��\A;%�����~���jtXf �yDNFAN=����������>l����7�)�)��b�j2z��#G���8���%f?��H
"��u�l��7)\8��V�V�L��R�?7_=N|�M�V�Fcf�m6��������
�P�{V�ZJV���fT���3���-���4��������Z�O��o���2���	�Gq.��e%g�|nV�k�p����T{q�:6�����_�Y�����d�L�TL�������.��	�
�����N<N��y�u��<
�)�K�-������5�G�nH�,��:G<�Rn�	�5~8��i*u.���N��szv��_!�����_�l?���
�|<����X�����O�[��^����{���`�y��.�r����\		����!��g?�PO����9ZL`x���3��$z�N
h�B���xZ�t�����gh�K��Ue�.������i�,f}�Y��|}��p[�3��McD���;l����QN5e$%V�{���B|�m2����#�wv��Q	�v������`�������?(�p%u��]I9�|�2y f�9�>`�8i�xD��#�1���a��p��`J����uMfn1j�vN������+�x�.y �$�'b�5�����.O�$[�MHx'R������EiO��������%~�F�6�IF�[���aIH�d��	����@��������j�e��p\A�x �uzK�v~��0�
�F�s]�==���d�S�Q�-� �@���W�6"��Xb��a��{�QA�N������,�J�y���2Y'C����n,<@���@�YMP�uLl�����������`��*b���f��~(�9�����d}t#���q�8Zc�-�Q�������j��k��$\�tX�u,a>���^A,�	XT�����a`���G�w��:"�M���OdNL[���Kc���w����V5��/�H	
 :�G6�"������8���Gu|o������]��h��y[�G?Mn������^v�i��%�n��D�Mk5���c���]LX
�?�����gY�Rf��.�*�N���PG������R�e��|��4W��Lp:���V�[M ��8�V�N<�V��q��Z��.��]������o����O{j*WO����n����ms��cG��rs@�7W���S�����O��+n�`4�%q"�rI��/8V���Un��U���}����`p���U:(T��bn���(�f����!�\�R()�����;J�V�*bJ��������>��"��
������0Tg�J���"p�E��z
��^+|��j%��*A�I<������<������\B� ���@OuG���������P9�{<���O���s�n��0�����f��������vs{�5(Q��S�����*��+a�h�7�����41i1��,3D?��%�����0��|�s2�$"�O�J�q��ws����1��b�0�F�sjt�����(�'���O?Q>��Q������8��y���?)�3�������y��N��
e;a>�M� ��N,~���w��V?��A��	�v���v��\I-�=�+��Elg>��DLO�]���*L2Dy������5p��e�&�_t�dI��28{����!f�������F��Q�����Qhf(~���$���'%{����	�<TN�g�a�=
�k��hG���#���3�0�IU��m�0T����'�������f%�G���TNFs��rO�G����&���ohnR��F�k����?�U�n���A��DxdQ:���Y<�����QJ��#����	�{�����^<-�GI����6���zx��,���/UE|���W���-	����)��V�6%��/��55N#A��aZ�4��|������M�}(���ZM�H��3g���	�[>!�z�O��!���)�>�p�W�m��=�4d�
V/h{�!VU�5��@~C����1�(��R���*����i
2����)E��sd@�����Q��LP���M�yX{g���B������
�o��8���^��F���@-��}u��K���V�]Y�����������<�5R�nt�]��D��� ��5\��wW�)DX@ �4��+�~	�YH�-W�iD�����;���I�u��������c'�<����*��]�����,p��d��A��?O���=^I0��6V$ry�(�;'���IeTYG�7�+��c�Z;��o�ua����e�����0�����LhG�h~�����3�o���F�Q"�����,g�X��~��Av���[;�{��;�(wvw{��!��r��)F1t���1B�)S~���^y�p����i�����������bO����#VzZ_e��m���i4�����o��w�x�n|���:y�������o���5y��^fq��(�%M��V�bB]4������62V�\���s8"@����B�y�z7��+�:{�^���``_v�kkw����Hbq�`���={�LA"e�"M�:��	�����!�a��5B~^x��W~�0�$�z����z���L76Tr+����xoY]I�p;�N���q���P����3�i���Lg�������l����R��"���E0/?y;���/��w��H���G�6]�|6����f�\�������6�j�(p��� -S��b��������D����U�d����g�)����pR�����������>iYj"Zdp�}���\��o�����K
��K�d
�.
~'�E�p0�n���-������F�r)SG�5!�9��8[���m8["9��0�*�o���Sj����x�^���������U(C����8>���#�����a�^�4|���������-��Y��v	Y��V�������z������v�_�NM�������=\�co�B�-�m���]�dc��9a`�n�9�1I)g�����gXR��q������3j�z&S�#`��GM���)�S�Y{�po�:�����`wow���)�[i��DA�`
��n�V��X,������v���}Fs���I�8����m�E�����Y���vk;���C��4���hc����\��SQ�'	i��Q�O�2��\�k���.c}?6��!=)��<��^%v�E���2�E�
|�D@��\����nW�oX�������w�f�?h5����p�<�|a�V����m+���P����`q���"V�7*�&0T���N���E��{�0�����`2��^�f4/�!�g4��a&=y2#�d���L��)�I���e�I���l&*�g�j_�������X�g3OY�a�f�$L�|]����!�'(�(�N��?������fhS�C��
]����E'�6xe��w	�+�K�����5���#g�(�0��}9�}&A&GVr@��M<S�������8�����>2��MB)9����G|�}��U�������VY�nXAE�a�_)�D�yf_j�q~����H����|�f�>e7�O7_���|���P�'�(G�n�C����T�#}Y�*z"���9���y�@�S���d��O ����d���R�k�/R�$A�<�E��N�wr`����&��Q�oX��T6�'W�������	���l���-CF��{�
@��9�(���|��SP'����C���w�����VY(���i�D�	BTmE������f�J\p��x
��S�I�-���(�����D�1��@������(pJU1�����o]�����O;�h~eCx����"�����:����q�������>Ou1������}���+F4i�����XW�S,s�pas�8e�I�*��]��oy����w����y��|��`�1O6�����#-��2�����\�6�������Et|X�H�E@btO������<��_���e^z�����AO�����[��C8-o���'8��8�r
10���@��(���*K�?��~"��I-nr:��V���9�*��l��n)���P����������`�c`���b{w�<������*��vP���.,�*��+Cf����3dg��	L(���JP{����
�I�X�`0x�P����?��ud�l{8��A�(��G��������e��h'k����9�k(Q3T�p�8��E6�up��Zt���j����i��Y+#s)�E���qD�+b��TU�a"��T!����n����Q����R��Q���� !�I";
�OG�8Czg����P�����3�hP>z���.i��������F��/idn5�����Q�1R����Wg����\����3XN=�I?�!��W����e���4kDES(��g�Rf	��V8�A20f
F t4Q����1K"(	E���5��-��N5��X�������~���5)��99�Iw.�lY^)!������x�;!t���95�/��
��{�g�SL�D�%�����M��Q���'Tx��
�J�Z�1x5��}6�;C��-��,�
���
k�bqW7�v5�
�������G�S��_�Fn�W����S��_���� ��s��=*�`�f��S�t�����#�
���;*�,�sR��d
��j��2#�" �$�}K������R���l��
dV��Pu���
&�,�~��a(>�������n;
	g$%N��O1��k+���b*p�	�8���{*��[����)�P:��l�I��5�c�A}j�[|3AT�������%u��E_G�Q^8 G����d%�Y��
���.6�,&}�3��Q�������(_���2�:t�dE���P������8��YH�J��� H�#/K��m����e`�������c�E�;k>����h�x�����s�e�������+�e�wp��cw�C�#�����VU"r/J������N�o��������_,���&7H��4H�Y�i�0����<!���J�4��?n'>��]����.8��-��0�n�*.���?pf��"�V���B�����Z�:��{�BE��Z;#������q*�3Hf�Oe2k��g�m�>�1�[X�}!�Z���u��b����}��X�X�����������]`O��Q��!�Da����<�f����a�>��[��U�X�M������+�!�P��t�z��_�o�����H	����:���v�=Z����TV7����Y-\����Y�]���<m�W����G��7g�eW��*s�+��T����c9=��.h�:�JE�2^w6�2^������LK���)���Y�U:j
�w�h!���gG��)�'(���X?&����]g������!T��~�2��:2d10���Z�DM+�^*2U�H#��C��
/G~�x�������0������&]��m����H4�-���[�L%9Qkv������T�7F�<�.\��l	��I�W��Sd����I�,�kkyA�"�<�SRA�let�|'<�zl���fV��Z8d���cN�w�-e��*<n�#��20+�9A-�<��bqO�S-���7����c���'��N��s��,i
�$�t����&	��,Q�"V���IQG����X���YR�^x��1&k3)6�,������dP�S���N�D�b���KLdX���y�uF*d�*�e�\tlB7�k�AHc-a�K�5,�J=����:�_f
�K��0�p�*��Zj!�������=��Ib��a�����nu�n�����D���{�R��R��A<�u\�X�����@l���'mZ����;
=Y|X�\rX��h��]*���P_���,�f,���������8�'6p��.��IUf�|�#��?"��:���I��^��r������A�����f�6��r��zr��e$�����
B�J��!_�o����o��?7�C�~�����
�����P��YO`
�'������%7XHH��v��x�{��A
�8�>�V��p���l�z}�������(YkI-��D�7v��:}�B�����A2�MhM�}f���5��O�}�)�O������x/^z��on�.���S����!��Bb���M7^@I�,����>��7GQz���O<{Y����,��`6�(�:	TT�H
o�H��6����{�><��A;�����x�Y�	V���SV����$N��������v��b�jI�����	�x��>����,��_>�c��==�����G�-4j������g��fAV<������b��x�)5�����s��o��6g���H�8������K�u���.!:hm�;�a��^��=�����r���Tz��Rt�)����i�t�?�j��������|��l����B�^A*U+�j��X�\^r���������������s��]�&�Av
vC���I�3�`���5�<���{�pb�>��!���d���@1�+��k�z����q������!y.��^��I�3����OS�N��c��6_���������'���Ai��W�k��6_ED7@�y���(P����(�HT�����Si���q���'��_��R�{aeT@���EY�{ �"�B�G>��O��7�"�1BW����yY�'iObo��������������vP��������Z�V/��h���������w�/.��~���4	?G�<�7�|R�Z����,��,y��������vLSbq{�
���m��������3>��Gsv�i�tK��/+,v�}��LQi���������^���2����^�y^a��R�sL���'���N��9��U��`��uf(vM�SB���z���8R�$vH��n��r�Wc�YQ�{���V]j�Q:�Ld+�)gT%������.��8���2Y�#���ID�������GkV���~z���g��������da�yUn2�|Wnds�]��)]r7��RpYdx����q+��w�L~�5k����@�����-:��i]���;��hLtfU��ZSb,ZO��������57d���9�#7�
	���0T���oF�����O{��#���sr���11��x�hnI����_ql�q	F�2q@K�%��y����������7���`o��w�e{��RA�*���+��'�Y9�u�I��_J��/`������� ma^=�=T���������)���h�f���I8�� �?
'�`"]���_&���=�=%�����<�����?��A�v�����d��S������^����ac��m������.�������������t�p�g�����,|�������Q��w�3���o@���p�����,_��L<��}�*�}��*���2�J�Y.->��
0�;h�{;{�z�pww��h����,�R���^���
�i	���x0�~<�3���s_��H����yjB=�QZ^����A?�y���Q�U\��
���Z)�����[��I�����a��KU�?�j��j�$�S�&�P-��R-��j������Y8�Y�Y�Y<fN_��A���K
T��R3������uw_�w�Od��?T�6�_����U[�x�=3y�:)Y����I3�on���jc7����kn�&]��u��xwI�7 ��g������Gm�1��d�/�s�/�<���M�k_7��w�?mB����)��o��WL���v�j7�K�V�V����u��-[��,	q��_��s�����Y�$�z���<+�q����O�pL����1���1��|�o�.������h"��/��:�/w��>)j��Um�3j,�����n.�9���\����"���5�i�Y�1��M����?�&�_���W�����z��`�7'�*�����M�/w(��	>)�z7j��n�D���m�8ke�^�+>���8�������M�����,U�]�-�]��:��>��9�[�&��D���U"c��jR�(r������e��L.��F��KmA�Kv��U��z��O��N��/:rA����#�C���1N�f�p���E���l����'�R��wcW���>�Ky����)o��`i�[�������7��`��lOys��Z��wb�����F������F~W�iQ���i���n���6~�j��~,����we�����`��lXy��.����
��q�]��?��Pq���U�[���d�e�;�Z'��0�����z�����?����j�L5��;S����Z��m�?�G�Sw��Q���G���
�����H�3v
|�q]�����%H�0��;DeE��`v(�.���q��~&G�#W�v��B���(�Y�c��X��G>e]L�q���}B�->�'�RO��	��p�'���9�kf�`�p�C�T���?0��@uL��;���es&@;����K�O����0�'%:z�neS%HN��zO�
�)�=���P���w�5�.Z����|����[�qOeW����TP���f:��
��sa����~:�#?�{�V��1v��+��-�
 ��3�5Vh���s[�{���w�@/p�K��r��
��9����H����PWH�K�>Lg���c�����?F�������j3DcvO����|��*-xs?l����^o��A����/��Rf�a�B�(d3T����kv�=��}|�������x���T,I�}A�>#��9�.$�_Uc�Z_x~��8� *��UP����8p��h�iO���t���2�O0��PW��,]�>;��I���1r�"���dy6����-���L�7�I���e{��x�����
����<�k�fP
�\\�����7���z�'�!��Gl$LG���t��8��H.�r�����(,�5��< �
C���IDX3��x���a�L"_w�|���w��-Fmf�6`6�QV�������h��[�9�R��Fp+��������sIP�$`�]�;(�����Y�%��*]��2�4["9!v�#��!bo9m�;�R8��,]������l��Gn�b�w:�\�V!����da%��K<�1�IU�kir'��f�)�y]��������9K.�P�� ��DC����D���L�1���]��9U������? ��p%��������U�kk����0�(�*mX$�����XiP=z�����\gN��2t��&udS�����&���F��x{�]�s|���3����o�t�	c�a���2z���6\�������E�ro����N�&P�
��r���Gi������@��������SX��G�"'6l������~T��N�'n�����j�T]���u�>��Q���!;X��s�h������
N-�e�@��*:�p��6����	����y��1�"F��M��(��d	�������J��Q%Z��h^������Z�&s�R������5�����VQ�[�a�Z�T=�N�^z�~W���u~6E��c��:�����x}�,��I������EA1��-��q�n����������.�y�

��<�z��}
�E�	���|���+�O��_j������Ai7���Ye�X��v������9���G�k��}L8?G�d�U�6T��n0�|a�Kb�}b����:\��!�"���#.���p
���j>!�h� 0������~��3B�����k
���G!�Y[cb�~��y����#J|A��kk8�S�2����zTG��<��	�
�2�o
���&2�T��d����
kys������V(��M���.����c-G	��%����]�BX���3��XhmB��|z�(�����L�^��j|�4������	;��a�w���A��d,�[Y��h��8��]�!'�HR0x�y0���$�&f w��������i������[P��q�y>�>a9��� ��T�I�����3s����z�1C�J�/�G���!���]V���*rE|8�B�x���?�F��"���R6�^��P�2�Li�.����(B�I�zU������ia����s"�k��GH3}�1�W2��<�7W�<tmU+�����*�D����7��,k���;�90��H����^�S�4�)��=_�iH��C��d��i�f�_A��n�b�>�[9V���������;e�hC��i�q��F��������Yq�x����9�k��
H���v�����>�����J��%�����=h����Z8?����M�s�6/��
�����,�p�&�-�Js���=3w�V}���g��:u"qi�����,g�"mf�����a�� 0��g>�,cpq���l��.jQ��MQ~k�.�=�{�����0	�@8h��)��gRD1,T��:{E���)X*��������8�������]	w�/�/Ne��
���k�
U������a�����bw_#��E�KO�Mgh��F7z�����'DT5
���������N
V��k����g3D��$xTW��6f��!���� �����D��c~���x����R@HN��k��� pd��w���a^i��}� h��,�����iL�f�4������HbQ�~��z�Q:��iO��x����D"��gqL0�O���^�p[�.��_�7��������p����9�jkW�6<��!�}����E�6B�z�k�f4���(N��}�"!����e3������e$�gY���d^�m8�Y�+�dZ%�3��O������C��V�F����S�	bN�.;�J�Z<DDTw.8������5/xX�>�6_"0�
B����V����/�"_��1���li�Gw�����k��|}��{sv��<�h�����wk�V�$��\B��k�
��#�%(��&���4�I�8�s�
O|b1K\�'8�����%2y?_���V��t.�x&��`��GA�V�c#����lz[��T�#[��j�=PX�Ua�5*�vu��P�=�;�cA��>.�U�53�&y�H��L��dlfod;^�M���Q���U�!p����}#����^�S��}GX�e������|Z��"F[S�Z4N�����f	?5y����G�9�Pe���W��4��X]�$CDkR4�}8� ^���^�Z�|
+�����F�����I��WJ�Z���I�E+u���������4Zx�����z�<�����*3��M��t/
U��5�exS��4c�N���h��rB���������(������������\�J� 9na�'eYwIF�SD3��7��2��Q��������jL!���*x�4�~M�]X2;��/_z�,�x48-�e�������bU����s��2�,L����^�������9�':�JE���MY������%��k]+Je�t��'I&%�)&�IZVw{���@&��\U=����.I�@$���$uaF"v����&INL�`*G��� r�8��)��NE��W�nB�<���{����N����N/����t�V�"�=Jc���Z[���H�h�R�F��YQ��!e����3��@�����Dl���N"�2.��2n���d������/�b=�I����������G�������c<�!gg��SDJH��:
��KMq�����j�c�m������lH��������h�(���%�bQ�5-��&#��fZr�;�r���b6.?Z����r�"F����zp��gEJ�c��[b�0����A����
�6���
q�0������1�I.��?��;��%	������#����i|��&���|2��gy�lr>y�n��gif�q^���>06���x�)	0�;��K�h&\��O������>���{D��M��w�H�$�_u�/�N2�����x���'�9Q%����i15loQ�k0:���E�[�>��>
b����+�F������c��������~��{o���t�c��������Q.P��c��
jH�����@��R&������������v�@�@�:H��H#g��5&���3B�$����\0o`�|�*UG��1zI����A��Y�B��sI�B\7^x�F��K�EEiJ���~��3br/yy�H�S�f��.���aU�	B�b�������wf>^����g�4��-*����)����X����!���R����&)���?#c)�Y�B���/�"���LW>�������H����lt!	���M;v��XE��#2��y������J�����b�m6X�V:.��<9 a�*����
��J|q�1-�(���H���Ct�9_8�Y��(�!����O�q�&�ZZ7�F����`���R�iWp�����)�fs<mR����:�Ubs�}U\�]����$�;�`Aq���Dx������g�sxm��������?�k^�
C����~�������B�&���{0����z�^f���l�a�e-}�������(���h�,�!������~Z$����92�� nZ��cB����J�l�oP��0
���8j�3mSzo��e������W�xC��:A��^qlr�uJ��`����O�#��Nd�d��=�"�1�8q�O�%�I���8(g���^����-��XJd����'w�	���	f��%j���)�AQ���f�)f$���
�����"�;���u!�)�����x�5�t�a���i��n��H6:&�$�(;8�k��[m.�.O���=>e'���0^M��c�N9+(1�$�e/�%�_TI��Sh`Va���e�G��;���<b��E���H8��Kr�Z����;�����L���wN;g������.;��3����'��e������b��]n9����q���������)�5����j:r�=�`����/��������s|
����w��N��#����-�����i�������>3����K�(>�J���Sk�z��Y`�|fM4b��Uj]���:6-bYb�i~>X�#;E��V�H1�����:%Y����Q_�=�����V��ud�Y�M�^o�D�a�1��(v���:9����<����|Lg��|��7GD��y��(���9�@��Y�X�NP����'��*-\��1��g1[��_��z$����=f�6���`�AJJQ@�m��v���b��?�����������;�`*a�t���
w�����l\���u^P�3��u�N-�����W����A}�?}��DK����+G�>]C7 6`%��� ���G��8����Y*,I��I!�E&���*
S�'j������i��jo�J���(
���2���v��gl�9�F�\��4y$m@<�F'�
P�I��r	)Sg�C��D���BPPjfE�Y�TP��M��9���iI������A0wYL�~d�5������w.F(��9ZC������uY�=�� dLe�W';69<������#�T���M�_��8C��]�6�'s�m��|B��������B���#����Y�=��U�����W�8w^U��F��%(��L��r�h,�s��~Z�_M�|��J�`
i2���t=O���Qj{�c2_)p�uQ������d��*���^^.�a��d���v�����2���%K��H�������rr5�%�x�GI�����w<���[a���N�������pN��5&AeguM$�C���o���,���\Q��)����(�u�%��L���FM����Z������������,�/�4/!�:2Y���_�������Z�[��H*���)Rf�������f�U~b�������Q0����%�b 0���/[bt��`�)#����CM�y���
���8���N�KDMP���d����eN�����	�)e����9?&J����B������*E��&
J��/��S�T�������[�.�$Jkg����Hc���]�q.U?o������k>��o1��)���e���\��l�$�r*�"�4RZ��f�l�����D�t�}-0'���+8�M�2s�����?f E;��c���U��XBT�r8��^�:%���9e{��E��E&{p���z��l:Scg�O���2Z��X��1�ls������b�836+�i}�_\\��
����0,x$L@!L�z�+v�P�@�C���sW�����,�Q�a,0�v��ZD<��dG�����/���
�Y�X�F+5��vZ��|D�\��WJ%���r��M��ruLqHUU�9�Q��d9����8�~��q��*�J�)��|��mJ��ZE���)����M(!�`��������|U,���3cG� 5�������	9��6�5��^�mr�q�K}c/��qbA'_
:��A=��o�G)LV	��/O��D�.�I0�xO?��It�v5�������Ig^8G��R��`�J�h������P�/wu�w1���D�G��H���q���M5[Qv���O��PVnP������<�E!(.6�O"��EK/���
�J�"MS950�J��%J�==�s�s���[�WD��,L�t������1�����?���"�$�U�5e:�)5�4���aW���4��
�!������W	/W��5=�l��-�]F����D���1���M7��5�v���G�����������:��e��p��3�;�h��IJ��cH<N,��[sQ:_QO���)���)v�,|��k�8��2d�D%[�O�g��!�S_L/��U�b��e�P4�SU~��"S��b�+���]9�=��Y����l;O�(%#[��j$����3%�kH^��t8�seD��R�Q�qt�A��p�89=��
����&$�3�f�^NW�1�v��l�y��5��	5p\���ft��`�����W�]'������ZEJ��w�m���#��W�!qk&=���F��!�5�Y���HG!fg�M�f
���_e4;��;���K�3+�o��X���+s�J�V�cut{9���4�$F���w`��'�����,��Mk�k���R\��]�"S|�3�[5����<�S�n���V�����6�K�#\�L������7[��	q���Iu��rJ�A\�d�%��v�������y%���^���U���<�(���O�(P�E�%W1��i������R�F����5�r-�K��������y��jg�'y�y~�G)�}zi\��9 �C�8�sA����=b����x��g|�vr
X��e�����_����x�<I	���uJP~���/���A�T~�T�a�KWW�|���c8�N�4��6<�Q�����\��KKw�����z��N�$�'U��l�]�(u�s�ZT���}�-�t-���>'
�����-�����w��DW����G[7�������Z�?�H{/u��d}���������c�� ��f��W�1(����g�~�����R��b6�M������w���8�g�c9��H��}��������� ���0��9	=�2�b:���_$�x�|���E0u�h����3'E����1�-$��������x���}`k�����XP���Hk�g�;A03�l�X'_��V�fP�H����d�P�6��d08�b�E)�9<�������|�rc�
hd���
��/#}1�/cR
���)8U�2
�n���{�E#)����_��0��3�%�C���k�=V�G�(z�!4o��{����d��\?�C�`�����I�������y��'-%��fpi�2�����\}�{1�������,��8�MP��[P+S�$�n��R�YK�[��W/�3�f;)Lz��$]h[y�U����Zo<M�?dZ����Y*�?��i�v��7�WI�!2z���f�p�Y��r�-�=��n������^AyDH����	n-b,���OY^�R�G^��vo>*���8YGl
D�1#��<; 8-�c@�>g3Y7UL�o�=� 0��`X�E�"�m�*l
i\_��{�����9�yq�"O��9_&b��8�l�>���ML+�yG	�1�����'�������N��/H|x����w-����e��Z����d���������v5W���eK���*[{bX�h�@�Er�~t����n��;��mx���C���hNF����gK27��sqN#���3�������$�C�c"_����:�!�db2J�U�3|��/N.��l4e�� M�������������
�\j�0;�tD�%���<��g;C?JO�!	'��\;8�;0p�j�<t��8�ns�������l5���vF(E�������LP�����;�`�����V|���{�����\u�<��;�le��"��.�#,�0���h(Q�5c�
+�g�a�*)��K �D��!f�$�n�K�\�����+�Oimm::��i=�Z�D��F��Y�; ��3?l��>��^G����|�T���d���|B�����
_���w�wS��.����(I.�e6����Ig��-[�9�=��k=g�UvW!�E��i������w��>��f���#`q3���S�������8XpG#g+�:K�+2�LS���4�
S,=�����1~���f2b|_^����O@PDhx�3Qv:�`�d
��X�������D��.q��������M�'���������r�J�}�v7p<�8�3�
���\��Bki��w���h=�-�,�m{���=m[m��fJ�?�d��;���d�m��FiP6��w)�~�����9�m#������������U�B�o�hrk'�I'��-#d�(���>�q�<��)�pY�v������
��~E�j�Q3�H���Lh���8

z!;���y�]1��`Y�t�D�T{��Han�]#zl��CS�����>�v�R�������1\��?�(D+O�y�OE�2�aD<������Ft�4"���W2��'�!>Sy���q��zb��EiN��1���M�,9
�ON��n���E�.�)��L5��|��x�=�u����"j��Jv������w���T�Ztv�C�G�c��v���|�4	��x�H;����~��{�?���>�\=�1]������GQ���|���j	��;5���("���m�X��`�������`���P��TN\�D>�
���M�2�P�t�� m��
!�1}����c)JC,��T�/� a����J�q��5kZb�d�AL*
��8����t�P^���Qv8$p!O2]@�$*i/�ix�9���y4S�����4�~c�(
.���)\��"�2��0�l����ED�&���{�Ye&� n��%6f���$TH�����a^8�eF���BJ(��#/�`�v2�@'�Q��fJ(�����-��8���u�_s���1�y�
Te0���]�����/]��5U7)�tlA�1����A@�/���UC��N�����	�����\LL9/[Q6T]�1)�����Y`Z����� LQI���$�^�i��E#�m�H'Xr,��H��������Ac�H�U�O� (�U.JQ�fr@`*���$�0���Dn2�L�Kd����-6�[��(��t9��`��*RP]��C$-4Wj����|�Z�Q��rs�b���p���}����LF���8Wd���pDe4��=�[%�_��6�]�.�����������p���fq��aM�C��A+Y0�;�~��+f&�%wlw���bL���q�G
����*�Oc%>q*���nF�3�L����cf��$��@��G���*q�6��A���H��CQ�AD��{�Yh��[F�t��+bF��)j�z�W"y/�x.���2\-m�/� Y�uH�^�V�tfk���M�������"�6�N�1�Ju\o��#�P���������.���g�i�=*fJ�|����6P1�iX��1�x���}�U�[���\���*�����i�Ql|�T��WJ�|���17���9�Gw=�����4��L	:Y!��������k��j�{`}x;�zq'\Lnl0����h�7o.����������e_w��(ti��H��5�����8���"'pTC`}l���@!_�>�SP-W�u���:q�g/���������p�9X&q������%�us����:��O�X�1������=2.V��q{��h��V����`�:$�d1ZE5/9�Fw��U�D��\��&xJ����a��U@��
���bv~7-b�j���V�������<�=&�1W�6�������_��b����(����rf�t�[�=����:�C�:����j��Y��fS�h5�U��}�_��,�53P��G��X��I��@ns��ex�Y���59F��<�*�mM�7L`����`|�_�j�n�o:@��%��eX�g.oFa��ug�pA�����*���J�:D�e�����~o2S�`� !��N��HEK9���WP>�:)��-b��`.Z%�3_�
C���|7�%��d�� 4G�2j�d��KE*���*�!O1H�TJLf�y��$	���f�&���]H�r����s���o���K����
J�t���y���#2�����������2
��_rZ���?r�7�\,��6A��l�|r�Z�[��[|f�~/�:Z�O{�Q��6�c��VUc�	1��!�����T��Yb���.-�efv���VNa��vxIx���.Fh�j}�5���#�,vb;���'.�~H�?��
"nW�X�M!�g�F<���d	���P�	^�
�V�$5l`2�%.���K\��'�W�����:�����@�R�6`���@�K	���N���4��a���4�!>b����f
�v!+����q�V(4��Z�����fMH�X��(H���FZJ7�$�@_w�����$������ ���'�!)��������Dg�������{	7�����<}�6�
�g������J!����j���U��G�cO�p��EJ4Y��K�b�t!���<�3,�R��~!x�?�r�w�����U	���p����VsP(�:�[U����ZSZ����C���I�`^
_�
���'�y�9�5g_u���y��c�s�?i_��
�-��$h�.���}��t��{t����������@�:Qx�H}w������B�
�+�5�u�U��4���h����0�c���)2RMs�%}0n��I-��
[�U�=M������h��5��BqTn
������-4��l�K[������/�"R(���d���R��������Y�	�z�����P���9D���m����.��&�^��[�=��=�^F�3��V��J7��j�|	��y�����;�K�BW������#fq��2>�V�Dd,uW�[�>6vs��j~���l�N0���,�A�Xi���U(��V�9lU���E���^�Rt����.��L�%0PPh,�g���T�Q����������t9�G��{��"����-o3X\�/�B�B/2������T��������%�����t_]�"9���z����K�q�S�if1��w�e}�����Q[�����M�Oh�S�'�x�����6��$a��8���T��I����6f���%�����h�����������9�E9�1���z�w�H����A�0�t�����\U=���o�L`�� �����y#+_ET�>��x��'�o�L��&����5-����OH��"at{?c�3*��5�).���,�EW��%�������{��jF�3Z��z��z4��l$(��$���O����p�M�������}8���[���V�+i���K�b��Kp-���fc0���&����(DV�b9_�����I/���sX,�����g�hH(��)>�*$n�0V�&���/�
H���J��jw��?�����$�oI��y{�~��?����R~,Y+�����1
�V���Vk�@Ju���+�V	e�}�Qw\|/oQ���P��I�8
�m���}������Y���H�q�%�z��D$4�g�:���u�u�� ����U ��LP�@++������'_����q�V�x�h�[�V�o���"���T���E������&p���{xtG���G��>���}�9�_wO�v����y��e��}z�9��//;�'�� ���%P�b���i�w�p\H�6����vlz��(~���a��`CO@�8S�`��P��3��*�KP��*�VeX(j��?�5kv7��������(�[j���C�RH���d�1�%k��/�����G��sQ���H�"�<1��7���'����_�D���E��x�1=��`?\�af!4��8�j�dW�=���#���m�n�D}OC�N�RC���R���y9�����18b���$���)zcz�jx��6���y8�&J�G%�#���ZO���p
���7FCM����w����/eI4�f�,��F�0���_/6�x�%�d�)���^���r�$������XT����HnH�O�Gb�$�	��SJ��z*�M��S�6�}�P3��H#����b�� �=B���X��Qax��
&��t'�A�A|���k\;w?�C�>��+�m���pUA�����m,���Q���D���f�>�#��4��Fj��p'�6���_P=%����3��QZ
������y�K���uI�����X�	�`*�I+H�#��f�,����2��Zq8��B��\m����D��6 ����q$FJ�G�=��������}~r
G����Y����'����^w��x����^S��s��s���U�#U����Cs�j�V�������l
�;�[�V�R��������Tk�����A#:��B\������/W	!������Bp4
���h�l�dc����h�;KRs��������3WF@$��82s�%���r>��au)</r����@����S�R���Mu6
�*��H����`&m��2��E��y+D���3T��e���i+����?�g��������d`��������}2�i���rx��&x����A��?������qe��~p�/�XG0���#������A�[�)�+W�R��:I79����#��[���%2X����ZK��G�>�
�=���o��W�����G��Joo��T��a����	*��!�=�f�U
?���X8)�� 2C�IVr��7V���y�=�b>����D��b�u>sV`�QY��_y�GCn���H�G����*�O�����r2�^���7����5����5�B�>�6G�A0lf��&���i�%�r���?�h&w��sV?��9�y��:�?�s.+`���3��Y��?UN�/�������p�I3�n���~n����&��hr;s5�Dl�D8�n�idm�5M�w4-Mtc������M�6��
M/��Q�/�w}�������-���q]@�t����f����z�6����sZ��q!RFT�������7]����������^����{}q�=����/NO;���o�/~n����EV��K����i�s~
5���X�s&_�^�Y�l>����^�O���p��pyr���2���6�F�`X(���9n6K�FX�]]���%����eq����e�b1�{���7��{��%���)���H���_.������cG���T^��I��S���n" Q�4����R�����R|���G����Spa��*,�rcP�
�Jk���F�e��U�y����*I������u��3������=4����^G�uPa������u��Q�V�����//��7em�m�p[�E��d���No����>d�u����i�s��Z����i��n6�?�J��^\\���������G�;��Ep�`a����`�F�+��1�h�M)���b���-�Z�a0�Z��&j1�n*�\Zn1������I�������SA���:r��A�����_-C������p5[R�wWg�������U����"8�Q���^��O^�}u�����MB����>�������/{{�������{����.��%TD��=�BU-����S��2?�ux%����B���b�\���r<ZWF���y��gkKPx������^[�`��e<�{�8���W!?��������6���1��J���Oh�N����g�Y4'�71��PPDR�����-*�Biw������S�!c�R�Y)�*�����?^����p|@E����1/�u'��Q������F�d���j��({�j�T��K�R�+�+�z����?e0���o����l���������h���V�����r�:��0g���Y��A�/�f����3���R�+���2��.�9���e8�{��p}��?�w�>|�^ �f�!��w�`I���^�z�y0����a��"����	r���������X}U���[&l��sS.�����n�W�P�i8C�{���/R���4�!BJ�`1%@�g�.7�|�@���>P���$�v���n�.�e=��G�����}��oS����&��c�9�)�F��rA�!#���c��H������pBN��dx+NcW��=8���b0�V�����a��Y��e����D8����cHI�v������!&��������DJT�PTa��d�}������O?�)4�P�%:(���Q�9�[�h����e��_��~����������U!�udh��9z	"&w%����M&k~�a�!j���GtB��:(�T3�!�����X���<��
����]!M��e�1.�o	?i~�h�3�F[fZ,x�`B}��V�Dr������?��)��u^B$����f:%B�bl��9R��xEh��Q8�Q~$BP�`�����
B����K���Y�e��"r,U�N���7��P��0����r��[�X�).���p���3��g�D��P��fnqP��|	��=t?�G8*Q����8�T^L4b�`F�$D��A��a������F��$D��)t�������QUq}�t�G���|��/D{�cM ��p��]��e��yY!nN��D��J�/������/��rp��(M�o���.\�����C�A����}�:'o�e��S��
��]����~����]���-Z�	"�FHTr4,=���_��wW|��E��j-om-����<�C�J�
����C��Eo�]
�����9 �D5���1�%C�L�>���l�����?1��&P)V,��=�i:}���Z1|���
�p�k��OE0'��������U����s����&�y2k��&� ]K;�����m�Z�d-�H���e�|4U���V�]��_�4-��0�Z�R�(&�D��jF�E<�S��� ��^�!�_�K��������V5�3�f��98Dw��)������kj�@�d�R+�D�"T'���&:t��Z�X�*���
bL�T�����>���7
�?8���k��p�'��z�M�g���[%mP/��~k��
�J�6*���pX��`����0���5���O�)`���u�Q�=[N'���#�	������ct�2������G!���`K���bz|�JM��|���\U7��*��{�e����HH����Z�O�e�`?�JK����IY���J����C�E�#I��w�=��W��.�����3��j�i�\^��W�{��+��
bZ��E�Wx��8���,���� v�Y�<�	��.0��	��(�9�:�d LZ�x�6cjm�>�P�B�;ZL1�
|��k��1-Nn�O.>��y?a�	�i>�a2)��3l�������a�:�|�����m�.b�s���)l���\�*�a�PDG�V��:����e����V�c�	�%<{7�M�;1��LW�����b���&��x��,��������������@�44�U���s����;���v�>G���.G;�M���M����b��<<����F+.��./�R����V2��F?#���	�R	������w_�m�?V����Bp� ��H�.]�X��k�I�R^Z�����y�����R�G���F&�%)l������?���,��ST+�l�����4^�fd�A����`�U����N$�\o?.������g>N��d�rX�U0Qg��U�q�����{�-��_� ��M���)���)ec=R��n����2o$-��+�Gk�Uk�Zm}�R
�3O�X�d��x!"�����c�QE8�����G�,�����Z`��k\W��X���!9R�W�j�9jk8V#��!�W'����P�����:Rx�$��g�Y������;�9��[4.fJ��?�y��88���K�nN4#�P�;�����\�+��:}�nQ���bFA0����B���i��������q���	��C��6AB:5���I��1D����J�������Ds�v��2���������f�P'7�zPn��	��������Vm�yM?I�{�K���9�,��F�%JP��0\���������F������/�H��!N�^�lB����~�RZ���+�������g�x�P%K��@!����hM�W�����(U�~�8������������>��s�R��O�����I������������=�1��e�.�9��(��N7r���0yy1
6���������#�'����]����2&����	3c)[�N�QcxNkE��Z5_*fO���>�<�K8
�Z� �9r�T��f�i?��8��&C�����[:z$�9>�(�e�JO�h��]LL�� �J�h"� �q�rZ��n4�-C1�1��_�L��-q�s�R���������I�s�[I���O��^���k���aF��"rDp����J��3|"B�6����g��@�Qgi���(�.��b��'�����|�/�Ebx��T��e������v�h�K
R�M^zs�"'oy���N�����w�!N��9�x����eK���82��]���}Keu*�h�G(L�����Z�)�,����&���h�AMI�)S�����3��8�c�l;z08X��B����KR��������
�CZ�9�">�{���O��7�����v�|�����__y�*�b�����E��py`��@��*[����@�����'�V������q�}K��(��<�kh�K����e��/ZCo��s<J�V�~�l���#��S�1��`��[l.F8�)���	6���i�)![E4Yv�Q�^B�{�1��A�Y�f�����b�4+�=�y��LG���J��J���F�rf�=��"Y��y������m��`�����K�W�#�Q�M�s�ANZr�{���l	
QV��F�2�!�����V��$"�YF�@ut�>��-�n�4Z=L�yd�b��a�+��Y���#�����Q��%{QH�q��L9s��vO�F��8�l��-a*`������f�'�
b���
"P�0"f��IcX�
�V4�����,���@�����{c�[nQ�~��*���T?i�/
^�v��������c*	5��0�pK�.�A�Ym��H��sx���yJ��s�'�g�����i�_{o����c����p�9����;��Y���}�U�����=�%�K��������-��T:,��cA�s������y>f�g{�����U��32����J6?.�I+�gLyUQ,j�J��Y����s����{�K�_��b��_����=�P|��{�k�����aZ��.ld�O�2Z������0������kJ�jg���8�@{yUN���u����r�����
h��pqL�� ^��hwZ��V6qZ
�����>f?A59z.��9��g�"�a��h�;�-v
<��rV���T����R�6$oC���M���0��({�d;��[�nh�5�����g��WtR��c�H����l��U�8y���\k���Kg�]��{g�v|->���>���\�\^�r���
8n���'=��4���Y@9��A��������L��s����N�����/1p���
3I�6���F��^==���cN���!rG.��y(�	�<w+X�O�(>
��8�D	��	bE&�a�w����i�y�{Jk>2���u�2��p������!��-�0 {�B�.~R��9�i�����R�����x��\��m'���3��Mq�F�7^��0����lMy
���S�<����$:4�2�;��h��KT}y�fT�v�P������v��V�7{<�����<|s{K��$��������`�c�n��36������k/.���T��]D����k'��7P�������QD���_��Al-D�z��w((ZN�FN��a��9��_�eh8S��lA�Q8�#a��bDr?G������GDt�	��H��;��n�B�����[�������$����]}�%��I�~��j�(�n��}�P0R:.�P�X�����ctmZ����a��;J;�W���q��_���Slo�`rP��.��_��'����s?U�����c�����V�F�g���:9M�{(�L�K����"�
4����1�a�Y�	���o�!#����jil���%������c>��i�
u�Ch@����y�:�G�}V��9}�M���������M��	wN�,�o��\>3���89l��!:i�W���X��X�2L�b�J0G�=��������O^����L������k|�m�Pqq��:��"��Sh[��[��?��q<�{���y�����4��~�-@/��7j�<9� q�<��������f�'��S�0)�2�o�4|�
)��C�d�u���O���;{��&�����)�I�{�k��'���F��|�^�/���g��N���YU7��#N$���=�3wCh�s���SzU������o��
�A���DI�>���%(V/�r�T���v��$����di�_&�� u-�����������
eDS�gbi�g}y�Z
qcH�A?�I��8�)���o_.4#V�X��'_�,����o�w1� 3�������J{��.�~����*�����Q[��0N��3�����b�����-(Y�=F�R2LpO�v���1\����fA����_����������`���}r�#���cTc�w���~�+�)��~��Z7W=���W`�-������|���;�*_������Df6|�Y}���jd�8[b�3��Y��Kr��������������cJ�)���u�:��
�����.��5v'E�����w�Ke�@P��39L<����V_r�T!�B.�x9�j����A�c`���j_)�t���/PG���"��	24�bx���GE�p�5Cv''
���������.ZD�?X����t��*�Q?�<�C7Ej��>D��`v�~��.dB��,���w����	����x<N�b��>����'"!�o�v�~�2{~�OZ-p�hO�KE��S�o�,������E��F�o�
K/�d-:c����y�������A}V����@�K�|��E�_����J�i�6b��>;�	���c^i;���^K)2��E-��q�@v&�<T��2L������V�+�		E�<D�""���A�M��8S�zp]�Jk1x�&�������H����"��H�(�
�\����+&|�|�2/��?�.���X�&�5��7ZM��n�i���"��;��*��`%�q�/�`MJ|�F�z�$�RKB��J�4P�vS8����9S����e}�R�y�6�@'�T��������/���jn��q ����\�H�x�M��.R�g������\�IP04D�d�`7��;��k���G�n��O���g-N�Y�Z�&;H��
�J���;�3(�B�\�v���)h&u�Xnp��}�E4�pJE�di��x]��E��
M�mAV��a��I����q�~�������46����+#���_���Y��f�:�Ev;C�c
��6)���ov��C�\����6C2����(�h��������8�D+BP/2���P�
���XQ^B��K�f�C����H3�q^�y0���H�F�1��FdO>S|����c>�^�Q~�#u|�+	���j�V����j ��������*��>)��f4�G%�J�k/!�H����{�[S&mJ�T��D�U��@�q��M$�0`%{^�U�c��peI�V�-�{A^2���m�8��`�ob�#\�d��vh e��'F�0�����a3�	Y�jCwW��/@��n1����RYbX�������4��<����,����F?�����A�S�-�xp�CS4YB��
7 � ��l�Z227@��2c�u�&>�c9BG�f�����{q����&�4��b	���k�b&�&��:_'���A=���B�'Q����s��)��`<N8D<[�s�qJ�3R6ou��u����Y!�)$#P�,�d����jc���w�J�d2��z��,��3i�,M��@v������_8z�����{���~����T]T����Y�|�9�K�x�^@),��
���O~��J����6�����N�\�w�9���
''�?��������5	��Q
9z��������C�7r0�V�C� �)�0pH�K�*�y\y�ug� `9�.*���t�_v\js��Pg��d�G��}� �;��l�&���P�pGnr1|���^� w@�z���"w'����a�;-T��!mm��0
�7���((4������B���p
p$�K��c���[��[r�=[�Q3�D�_���[�����e���(6d��l�b��=�2��"S�o����N	�2>��y(q5�,���$��O�)��O�H��9=��O�xk���z��hl�^�E�a0z��8��pK�c���Gi.B5���5�&��0��{�B:g�����7�Q{*z���<��Y���@H�z�_b�L�!vP�����	,������m�
#�p�m1E�{w��	�G��EG����GJ�� ���Vp��JyiZ<3�Pm�NF�1�-�R�[������|�����HdO���pZ��n���R���������� �u��s���-^9I9�Y���bWy&O$�>I(��'�*��K�Y3x��*��z���o��H)��k��	��`����f=�$������`W�dl����)&�#�Y�C1���b�7��H�3�a�@����W�.rW�������x�O�u��Y�Jr��S:AT��3���H��E�g��(����Z�O�������8�� �7�sv�����8�����+qg&6�6�����8P�C�8x ���\,g�=�)D�Xh-f� eiV�i0}�����u7������V�<��|X_�c��.�(�*�s��\�l���y:P�Z0�d�o�������S���ZP�����.��zbM�xmnnr�3��O�%������4���"���*��'>*|�
9���/G�E�HG�lM�3��
}/��}�*��d:���y�i�m�R>�`���o
�i�0+��8�Z����6��	�_�/8l�6���i0(���10�g�)�`��7@?-<����;��D�A��#|R�����u����Hn���
�.���`��\ ��/�HU���������mDA�������w�=I�����1��f^����9~J��1Y{6��9��iGM�s�]���K��W�����	�^���G�
��$�aA))�O�N�>n���I��=����<`����+ka
�c!Q�+
�,�r86Ck�������%����f�\����'.��:gi}a�c#��������y���
T�Du�x**�Y7����pI����u�|I���c�
�X
�P���uCv<�2�4X�Z�����%����N��M��Q����3�6�F����{I�3�E�C���P�
�O���d�Wz��D;���)rM��X')�t'n�s��Z�C�V��>[�E�dr	�&�kN��-�.��0�����?�+���;2;3������'%�l�G�s;x�����_�>%W�'��Pt�N����>"}���P}��|�����r��Q�]W'����Fw�T-��r�7M�F�+��\}v�ZZ���v,�<��>�!�B���;s�vdo4���{�=��B�������d������u�]!1�Wi��x �����B�
D~�����`"�bf�IY�d�������Ms3����k�Xj�+>?�	0
	zR��#k}-e9fi����d#B�K��	$��p&��e�,��7[D��h{�~�1���u9��TbV���)��D�I�;n��B���._��<���]���!i���^�BT��^|�)����	�q9��� �0�,b?�lB���pC�����w7���#�k����a����WP�]u�o��M�^�$'z�
C�.t�_r�7�Q���l�0�|��C�����:<�����5����_(���qm\�*�&�����]�`��_�w����:��as9��^x/����IN:��U�}������s�{�����G��~�������9*��N�>9�_��w��Qu����S'�^���������,�X�h'Ow�q@9��1 ����
y#��c��`:gqh���?��&�Y�����_*	�}g�v��<{&�����d�v���	pR\?�N���;�&F����?�4�n}9o��B\H���X��Rk���j�2�7F~���Z4��_�FAZweL���?��(F�0�#�aj���z�4�	����p	�;H�.�����J���@�&�i�#��u�p�l.��'�P
�n
O�y'��^�0��-@D��v0zc2���O��+�AH�q���]�Q���{e�d F�=����a��
���	����?*����q�1n���8����0�H	L}�9�K���H�o�K+�e�T���\	�^�%N�����0F���/t�_l���PB�&Zh=,��&w���b �����A���+WX-����{�������e�3[W��Am7�Jt"����5���\�����aW��8#��.b�e$�D-�pn�L����/�1��X�v��|�A..�����q�<U��z.���fwn����y�H#~���_����,�
�P����9l���l��$1��Nf�YI�=��[���a�?����<?1:�UO�O!��	\\��!\�|k)�\)��f��|��0�.����������&�G��$�U:^��i:v\fb���^9��K���Mg���p���U���:�j�j�Ph6���_�V��g�N��L��d.yM?I^����@li0����+�y��������������n� ��	AF�#��%l-8��y���+���Q���oU�=X?'A4���Oa���l�<�$��Q%l��DI������ �]1H5���#�_h�T�w:�I_�i�rb�����+�`b��d��7�6#����3rBH8�n�?�XXb"DOQ�� ��z�e���"jy�����?&������bz���;�`s�<����;E�U(T��A�5k��nZ�
�].g�'@�g�)~�/��0u�_��3
��`��W��F���xiac���E1J@>����YDh�r,3n��r)���+@c�K<Am^^��y/�L��u�HVv���1K��#�������~���U��Z9h�I;�Em-[X%��q�����9"d����}���v�����j�q��~(* ��d����!�I�����%�������i<��SV��r�s&y����n+�,�]4|����R[��}�O.��Ntzw8_8%b{Ql�t�M ��L�se�l*!7h�Z#����
��Q���b6��(���U�J$J~�����P�!$(�s�O|��E��,���
�h:%^{h�C ��S2�6&���_^rb���Zt��	�0������#�W�3��3�.K�`��z�h�����4it8�TJ�u��������u�9T��QoM^����o�e����9�V�`��<�8�K��B��>���^�!��aE���~���~D���}@G'	:���:aM��u��d`�(�cdg�����x��`�9�e�N7c6]{Y�v�5
����b�/}��
����k��x�)f���4)�Z�������H���9,[=��q"W����P��
�?�Z��C�@�yy��(W.�C�=��^�j�	L�j�.��)>1S���x���V������aB�DC�,>�x���d��U�,b,o><I��N�:��q���Tp��T����%������^pe��j$r�&]�(����wS���I���k/�v�����+��4��Z�������/��pP�}t{�e��)=�*�-%@&�r��H������)5������,"����g���a}b�	�h�Uc2�b�
����Z�>�=�n&:O�[��V^�� �~�q94��#"�-
�SU����Z��_L��������s�����������*&���p�{�'m
G����d��e����oh��KM�K(�O�`G��J�O<�QnI���2�����}}s����n$R�����,�=g�+�K��2n�J�Vs����2�[�f�����-(�����������zI�|�&s�	?��T�.i�E��d���W�=������=���{e�a*97`��x��6�;�N6��3������T�6��J�{�����{c�!� ���R��i�t����OL1��1&c��O���,�.�4�~�4h5�a�P��F��.h���Rl�Q�H�.w������+��P�G������9���M���7�i�>���l�E��H��`n���<P�9~��~\�V�b��$�L�J���.�Mx�"B��|�e�M^Y�:��M�~�<l�6����p
}���1q�
���Ix� }�i��L������==04�>�zA:��.��S���S�P���	�}�x�^W�$����z\�&jHDh�/U�[�	��A`U��(���2��uv��ZXE�����p<=�?���������R,�)D7��p0�Y-����f����@&�5�2��(4Y��*���mXS����({^�%��o�;�b1U
��^��S/��4���yu�\(�sGE��C��r9��@(z�{���|���A��i$4��������W�,�a������Fk��!-GP?����x��������`:��3�@.�Ft�O��� "��J���eF^[��%�=��c�
���^��fO���%j�����W�X�=X�wh��&X�Z����i����P��b��Gl���P'���h�K�mX�@
N�d����'�gv�F�L>bvU�E���6�Y���X%�����& oMGT2����_h�6��+�K���@�>Vnt2<,�]�������s3s���h@��$#�&AF+y7��d�M����S�o��6���kN%��Y7"������~���������I����J�A���|9�\Ps�A7%O������~������Fz�+��3�����ur��g�W`��f���4�������E8j�cL�����_�cZ�>�����) WQ���(�W�.�F�Lx������{�m7j��������m����o�!�+����S��W�/c�����D�c���\B�y�&"�*�o=�q�o���_fxU��|q���_
��w��n�t��2KI)f�&�P�����{p����	>z�.�?�F������1��I�)�Z��-7�A@(LO"����r�����n
���������P3y0��%��2�8���}J�����w 
�D������T�)�NBV�*n�c$	�&,J�?~���n�iB�@7�)����P���c��]����~2���l��(q�(�b4;Tq��G�������cu']������&>C�����:�aI���A�k���	����z}��uM�EV�.��������w������0uS]!U���5��������e�
�c��vxU������� ����t�3��������q����G���v���@�CL$8��~���v7������k�y��K�P�����D�i����F�i[����:���������a����?m���w����|��O�������������=��=m#��Fh��"���vC���-q�i[�&�n_L��1h:4�����O�k��S6���m�����"�[��2�m����m�L|��k
��]<�7X�R��?��E�e����������W�+]]'���|����wH�z���N����)�U�E�8����(V���vqK���sS�m��GE��PV[�Z)
+d8z5
��B��a��!�O�E�K�EC�u�{�c��8�UxW$8��o�D.a^�|�;��`�rCt:h���_�����"���S�����	
���}�W�
��y�*�E��P��	-��#>9�{6�u6�u��d)���+������(�?���}�	��/L�Y��^`�b������t�<ZS��f���Y��������J��r9��QmX�7
�A�U,�[~��6�n ��p�9���G��Jk����9,�NK��9��%�vcP�c8]����4�0��,��h���w�I	��b�~M����IIjX
~"����)�)
,t�����]��+�y�I@X��iD�]���@��9�$�$=i�k���82�Q�;�����j�/'J�BId�����ip�a��&g�Z(X�K��d�|���{��0Sd�C�"��@��	0��2���w^:�0��,���
T)��[&��-dCd�_�:��%�B��}.MNf�m*��/1���{5{�)[���`���?����20@b�fX�_%_E�[�c�e;$�6�9�D�����"���F����\ksC]�����R��>m��w����oG�1���Q��g���{��?j�quPi���ZAyP�K�j�N�.m��ps2l����YM1*jad�_G��K-^vi�$J�s@c���ym�������#�=(��j��*���?r�7b�/	�>P����~z/���b[���39�*��(W���2�6K��0( �V�Z���NRi�p#�(2T��!��Zg�~����n�2�t@�+�T���(7����&�`�Y��!��{V�6�"���5��;c���^�C�
���h�fV�F���S��������^��
�5�����5�	P�mwez�����"rLE��B��TUB��/���a�R�
�F�Q+7���:Jk�H�b��Z�FR��
��<v��^��^�N�������|B�Q�y$��x�������7_B�P�r�/��"X2^=<ev�� ����^@�*�O�����cI4������*�w~�L����0�w�]�[���-�r��a~��YUql�����W�����?�����0���A��o>@oF�!��(O�����K�~�Z���D0s�����k4��fL�H=0+Y��y��a>��U���3Y�C��6 �&�p����9a��r�6�`~���z^A��]��J#X�R�Vx����4(���7�FQ ��:�[!��c0rv�=z�~��������{$
L��]�0�'�	���!jV5����� �:T�d|Fdg�3���AX���1�[��W�P��dC#��Q�F���5��1"��#���W�DW��bC+p(<����l�G,�������B������
�b�Z��� [�IIK;�t���c�p]�.�.��N��w���I�\��.{��r 2^2J���`HYO{\C�c�.�s(Bd��
�g~��^Nf��k��L	�)V���X���� "���y��|rz�=C��~�����Rj�9���fN�}�����5����?*5���>
�?k�y�Q�t�
���&� �a������SD�����_b@�U����9�}�s~��$���0m�/��[G�5�d*\�a�;��n�P)J������5�rRlVJ���b��&
��9��`t�������C9X��N��J?T�c�o�7����,5j%�'���Ke��WK�
�Z*��b�Z����+�S�h���~���=w��?�E��5~�<�T�r������j��Q��h���h4��	��l��`��^�xH���a:w�����-����U���������q,����/����|t�]���W)y������?�������n�3ez�l_���W��/����l�)�$<ddK�
bKOe�*��b%��3n�O.����W���N��c	�Y��@b9�&��z�a���gsF�;����32w,���D�2�IP�J�qv�����z0D���H�GD�j��H8S��[N�9���UD�.�S��������%�>�j���\7������yFfuT����2�`@����jI��P�Z8���j���-�c��N���$ruq����$��r�����>���!%-�N�fpO�,��i��^-��p�]x��V�E�qa�1b�J	�!H��%f���w����/���r�z���B��u���������������.f���?-�oo+_oF_�������O�(��_����n����L��_������9�jQ;�����~+�����m��/P+T��>�sW$��5�g�Y!�a-
k�w�kp��/�(�`�S7�k�*-�K�_j5h�h�~�:W����f&�I�/�@����(�M�����~n��Y#�Ds~�����@�+�u�6��O�+����'3�7�A�JEfh}-���JTk�+�d3e�Q�+�[�a�pm���Q]������]����L��
�u�x�UX��<0������]�*�0v��b�Y��~���[�a�G�F�l'�L�lgi����B-���2�;������P'���vGM�|{��=
�M����������q}�
^�Ao4j��2�5�RU,��x�o?���cn�!������Nt/�g0��������6���Az2�U���{�x;;�!p���rT�w4^AN����n�c |����.�IGt~���ys%�x/�N�wys��CI���������oCi������>G�,f�������=�O�hS�P�Juf���~����.x��p}us~Ls"�����6����t�����p�(���s�d���'��fttO�y���O�W������Y��Gn�qs���S��3�0������z���������� ���[jP���g��p�j�<���F�m9�B@
>J����i@�K�AC/o��4j�S��+�]���>�zPX��B�<2��3��7��[ �]��Ht9�Y��N���(
��o�%����d�ad�������x�Zr�����������U�[F����^����9B�������|����@o�h'q9p�Pvo3���
�#���Zj����1A+�TL�#<&:a�������!~Bp6l�|"3x�s���tAI�<n�������O������R1h6� ���Z�����m(>!=���Z�aZ�'�����u,�7������C������G�=�GA���3:C'��]��w��5	0����3�`�������l �o&'L_����0�/i����lx��c�:���1}��Z���%�Qv�jW�o��'N�S(��u�Sm
]`W�aR��I���HB3I��<�)]M�rpN��A���}�������w�z�_�#�����'�v����+���3V=J�K)`0���V�������d�����x�����^J>��K�S���=���7%�Z��g���r�5F�F�^���p4����j�&�E��#�E��T�T��b����^N����*�u:}�D��d:Y>����G[�~����x�z���d;\�.�i���g�]$��|zJ0X��z�Tr���q�Z`|v��D��1"��~�U���y1A����}�[�$ykD��[�2fT�m4L���2�j	%&�����������`=%��������o��ql����\���&^:��(�xk�F�qI���a����.Tj�]O��[I��*gV��'�{��:���9r��n|���>>|�+�;�����O�����ZA��~�V�W����6xGer��$����+����@��,���:��Gb���za�C>~c�:��3����o��mi�k�ec|8_	���I.�>��
I��p��Yhxy
������qL-~�E5zd����DZs��F%��r���^G������a��Fu��Ev�4������@�\�����_���I�k�%���U�/Y�6;�r�8�wb�WW|��,�k����,�}L�@(9��ss9Zr=��A1����+��I^1��R�a!!���:I ���o��, ������B��-2<G
p!����
�=���J�����b�mn!��+�!0��9�����Q�e��=����sFv]��8�u8���UtQ���F�S�K��g����wzk1���-ea�J�0EP�Co0^H��yqj:#��\���� ���}�%�sEo�jZ������^v�z��u�����������k��s�T�v�s��`�A�w�h�#�q���+�a�:CP6+�Z���ce����6R��������4�����z�����fd�^�@��y��=Ul��yo��ke�|������O��*�h��_,����S:�f�$��&�)�T�TV�d@q�����4�y:��������K���q�yn�#��E����H�T�����X|�M�\Y��cyC��O)�b��*�
��n_]C��u������XMY~1��YH=~�y�bGcV�B~�n �)J�����
m�O}�����@�8����*������9���$���,�y�p��1��eu�I��r	���w%�������ra�:J��D�$�+��_������W&*^6N
	�������V(����l���Q.np����G��Q=�*#���=�5����blHzI�B�QX��	A*hi5E`�c9��5�7{W!I���������l�S��/���X���~<
Vso���"v
��U��l����d�c���N����X�*G�{���64)�_���i����q�hi��$�J����\�b�.9s�g���qv"�s,n!]	�����>�����d�����f�7����KB��bp�z�m8b����D���s�����A��S-}�~��g��|a��S8b��J�m��c����f�uk�T,��BYB@�Je�U�����}m����W|�
{b$��$�&x6�����$9���h,��Z2f'>��]���E����g<����j��uyv�b��������Z5_��D�H��c��
6Y:sl��/5cf(&�>�8����MT��a��g#�T��:@����`�����7m�)k��[7i�p�{���9���oO���0��.H�q���!#�s�.��
X��&����W�I#���`H�C$~��_I~(���!��J�s����������R��o�
{�.;�
_�=�O���p���[���0s�K�e4�Us�t6w{�Z�����m����$��kNJk�[�70�g���������_g��:�5���W�����G���v����o��t~����mg�{��is���olw�k���^7ht���9sR�dd�I�!g�=LR���e�Mh1�	qA�k�<b�"��"�"S����a��
�T�?a-o���i���x�+��|L2���B����l�J��Z(�,��3��8��p�nF�j�ge{���%�18�F��I�ugQ�$dy�f@��N����[��y�Z�xy�W����:����q�a���D����OH�a5�iy��h6����Foo��H��>v6��5����H@���on�4���.��mlm4�w72����Er�p�82�[`w�h�0Zk�*d������� m��]EI`k����x@N�Y�`K��L�O�U�ie����u��\!�/�Z9�Q��%����k��Q3c3t �����,�L�i�����&�H^����
qQ���	#�;?������9���������>um����4b����8�~J;���Z�GB�:D�shj�����%�pc������p��V�on����������&g��&��K�A.�<G�fF�!q����'_� �q�~��wo�Pj����P�@�xE��B���gq����m������0�v	n����C��R�a	(�G��U	�$���.�\"-�+��$�r�|���2S�X��!X��T��r�Q�UO�i�(%��A�x6&\l|0�#��]n�I����s��w�[���0��FJ;��2*l��L���E�W����6��/�J�jhs�y�����tL]9�	'Ad�b�g�z*X��o|�����3e�GDqE��n�s!i�OBP/�g����)�'KaU�D���
\H+4����~cs�"��F�S�#ZQ��l1/��t)����x��3��}�[��jSus/����B�S���Z���@�n�)C�%62K�b�o7�(�$I����8D���r�Yi�d�I��B�����d:�s�1��'|����}�h�����#�Mw$���1+�5�;���?����V����������v��6�F�)��U�j���T��_���h��S������So&���������g+6v����������������~gos;��j�����o�A�?���sM�<D��'K-7:d��,WV���}0������0h�� F������Z�(�SL�))/I��u"�Q��7MI������~S[0�i���28�?����2tQ5,	:L��'��\��H@�������"�=����81�O��-8�oj{��
��S��.
�B����W�E�=����D�&��A���P�0M����mf-t��<��8�,mox�I���N�:_4�[��S��#��ux�N��z4�����C
�[2Pu�'�n.,m�jGe����I<��yp0�%����D���x��W�~��m�'Y���&�C�d2��F�(�Z���������h�Eoo�]��BN@��jAX������Y���>TI�x$qhXn�����M8�����y��^�T~�_Uy	:�W��7���r0���a���W�d<$z�x���`�@rb�%v��s:^t�9��w�Mti��+[d�B	/��_��=���WoZ������wW���^������K��G��U
Qs��H
� �J����;��jq��������|4�R��A��P�&����	+���Jl�����g��M]�>�����@�����p��su��7'�PC�����"��S-�vz8�flj��
n�q;F�gt�shN�gN(�xmQ�
���)W|�=V���5��2gAq�������;��0�#���^3�������������p��i.J�%�x�
O���N���fS�>�X?���l�jA������������O���f��Q)O~�d�'>�>])�&�6:��Yq,^���Z�6��j	e��������>i�-�5����Z������
SF��gPC	��7i��/�����������|�f�6�ch��)o~Z�6�i-��S3���y��Eg�K��,���SML"����D����3�rXO�Bz/���w1�F��T]�� ����0Y���u�9�f��m"]��<������NN���)�_����a��E_;
Hj���fe](60�k����N'��(��j���z�g7�\��.�b���}���~S�#�FA9����2{��� ����9�x6 ������AZ�*k0��XI�#�8Q}I�Ej
$���{��mc�����ldZ
XBr��8v;�v�$��pN��H���Y����v����=U&���"a�d��BA�m %�����
{6^P�L����j`*`�p?�������l��L��j7���!v]L�p�O�Gg���'�`]��i'"��Q��|;U�;w9�=���a#��B)�K�	�zLfq�F���z��
�I[t����|���sx�_��nN��SbE�A�}�EeF���b���b���C��sc��������z�h\���p���+�H�-&�Y��<p��������s��(��$NU��V`�EI~�����[8<s�����*Y�sG��0�F��\s��dm����y��W������`
�u�������m�A8��U>�3a������i��W"U]soH��%^���6^���������O`#7�!�)����w��!��������u*3{�4�����2����R|KT�&��}�6�_f��������n�<��T���������;�f2GE�RdcS^�����<P�����!y�)���	2������l	f;@�x�����v�j����K �'��BV��0�������v0������>"�6��,�p��*9M�w���0��x��+r���R�[8�����6`�k4
���u�Gd�63C���Lg���� ��p�wE��Dt��X�����~{�P<C����+�������h6�*	b��>+���z�%rd���13��6!93
T�@s�V��E�U���e�9�~�M�������.���a����p�1����~h��
tr����4I��j.a���%N��i��x��N�)��C�~~���p	[��:�B�v�����a�"5t���:i�YC���1.���^���D��L������#x]e23��)�1�o<�Q�����@%��w	�9Pi�D�>H��W��u����2^��m�m"[��u���q�&������8�J%L���#_���rS�����
u1����~8rNCF��rH��#
�'p'�W3A5S���w J-���S���i-th�8t������,�E��4KjF�un��q<�95��$S<+H=K�t@J������������#
���L�'.�!��+p������|��%_JgS��^�m�_\�=<K�"�����+�g�,=����dg!�S�����m~�[;h�w��6�'�n���h�9������L7�sc�����IH�N����-�,�
���m����[�`�W��H��!�Pk��?��j�e0��j�D|����]$��c3���OM�=K�-���t��^�{����}vq�>��&VA����nz�PP��|f�Z����E&LB�K�r��{���%VG������E����z��H
���
j@=�@E���MR�\�;!uy��v���?s.����So�+8|"{,v��6�3�['�I4�(�g%MQ+�<�9k�
��pjr�jsM)*��lY���&�6����Q�Z3��h/C�y�p\e/�����/��&��W��X�x������[�N���'j	:�n��K�lq���[k�cG��3Go�zL�~�7;�L'����.Op��Go//Z��+��B�:\��
��_��Ui�S�����[���$R�{]r���*�(e���FZ)�R�`1{�;7Yb�y��7��w�c�g�s���Z�5$-xS��a\FZ1k5�&�}�g���0���S������@��
��b-�/?;�d���k�:l=$��z��	�Ov�cu�QYT���j��	&=���9��pO���	--����vDL"�����r�
D@j�s���DG[��Hw���Ym�
7J�������=�Q��g��AQ/yE�����������=�KF�T�����Y���e��[����'����XC�E�8:�����{��h%�K1����<�����M�>@	����]����B��!�x���v���
��\OX�l��2�4��7�����W(s������������������Lz��.�M3��kB����+*��
>�!^0]��!q����"v�%��� �NrX��a[(��L��['�d�vd��Hot�m���F�eZ��J�4>��ioET�����Zd=�tLH��}��Y�]e7�!K�B�F;��ht2��i�n.##���C����9H/h�M	"��J�rT��#!��L������	D�|R��}�.�>��i0 W��6����%�&K�TQqP��eR�S��#�^(�isB�&-bp�Ht�	�P2�M�~�N%�)�h} @l��ca��)����w'�x���h�K��#cO^���C�#H(�H�����z<������zagF�K"��a0&I'K4�|�l�H!)iC+�(n����S���\B
6���C_��c�*?�M��#G��2���D*�{P~0�j��W��b�o�YM�PoL�9D��dF�q .����+-d*~����D�,��_��mm{�~5���0>�\��5�X�L��'Y��?���KL�1���,f
qt	#�����^x�.����{Z�)�B�i���a��;M�7�	�wj��r'n���*l�H����D>�����<�<'P�GG p:��=�����o��t�S���~�����Bj�+���p~�V���b��r�;�W�� ��[WE��	��*:q�Ur]��h�3���q?��~��.I�57Y����6�vB�����������.���\����o�I�^�a��$��`T|����BE���9Ydu��^���j�(Y
P�����\R�����y+6n��M���q���	����/\m$N��>��!w���5:��@<YS��Xo%���������%�1S@d6���\�v��RZ�8n��a8e�P,��X�N��u���~}
{���#��Nz��u7.s�[I:*b��i�����C����u���X�^`5��+h�
d�P	���}z~z�}=sz�CV4t������H+�'W�F�����n�&6�(g�#���^�/����`\&�����eh���
sS4��
<H�D�������W�3W�bY�k�Qd�R���r%��M$7�M��L(E�I��>�b�fZ�|�f���t�I-�����4����$D[����G�n�	U����L8��0���o���|��IXV?JNL�:��T�+laX���mzC���I��(�3" xN�S���xF��h��qm�uC]�l_�h�9����#�_����p,�	��o�I��(�1�6����v���Zmw�������%2&���(0Y����
B(�9��
\e�U�����D~�b�Ys��	?`0��~
G���ug0��������'?`��f�cu�Z�(Zi�9������)���Xh)�R�J��2WY�����[���5����kn���5F����R��'5���92�P7QQC����+�����+��o�k���NC*;���AMd0Y���>����f�{��[��AP6���#��f?�!�|�Vk4{���Fc+4����y5��t������W����E���
CY�����~�0�zO��;�����B4H���f��N��b�3Ao�_��:H���N���8p����[�on����i����f8� AM�V4�tC��!y�.T�b
�]L�2����_.����_>��6�w{����Q�L������/#�����������w	��z!�s���V�S$��������G�@�M��c��(����4�1���e4��JY�����)�`u_k7�����	d2��G��������U=s~}r~��%,���8��������.:���������b*��)	�2����okQ�O�Dm%jB>��jK���<���|�yu"��=1PV����w���3���>:*��y��O�~9;����'����W���?�������i���U/d-`�Jd�����1�jjF7h����N����S���/PqL#8=o]��vyu����;t��Z�S&�xh�O���@�������,9����u��_�[���N�������aX����Ow�����*=�U&�LHD��Dep9�e�W��q[L��:���O`����3�������5����Y��nl7���{�������{Q��������,�|�c�6��
�"�/�*���O��O_�����Oj	<�����p�!�L�.��w:��n_��^��Rf�1�B�vz���*��'���r�e"Oz@�d�|��!���<������
v4��|-�m"k�8~v�
^����d��o�{}���o�! 	2s��{���\,���q��9C�	�1� ��,L����B'`;7��=��������q����KTxT����P�"YQQ�+����M���
�N���fSR��>�N�`���"�N�>V����|8�bc�QNk�t�����$�2v��%��!;���� �y`�&l�%a5��kb��hK�0��4�i������o�b���6�������/.���\������!�{RI��?�.��z��%^���8V�7Ha?K��}�
ad}��]�R���Az�/�h��k
qb?1v��u�Cab%n2^w���^�i2�&]c��<���f��3����
�������F�{����J�i0�����UH �@����&�eX�_��w�5�������������`�,Y��6J��&�R��
�^AA��c-���������t�a��d�+qD6��B��H��x,��������9��b&��� �|���H����w��e��U?,����Q��U�i�V���8Z����L�N��n! �H����%�T�D�yuJ��^�D��A=�C��q�F�k1�����s�7�'��<��s�@���7��@����.]�~/*�j���M/j�[��� �?�B�m2�/��bT���#*���dT���w�,[f%W�S�i�e�}��z��?0!z�mVn��S�m��w�K��e:��G�Y���I���2N)�2�c����sV�hd�3V���qi��p�YqC��rF	���a��/8���RM/�c(pr���n!�����Zi�������_xs�Q���pBq�N\Qy�sz�����c�6��<�:^?��XZ�N,�,6��~�NF��0������j�[�D�i7��	�aq�%������1�� ��)=�"�i�N���v��6a���]XZ���k�S��V�����6��#�q�l2�����|����I=��~�{��m���q���c@��$S���q�����8x�d�������@�Ug
�@ek��L���$.�.��g6_����A'�b
y�{'��")�g�E�z�bIO��;���q�2xsc�z���@�`p
)(*@
�^����!�jP.���]Z�`L�"H�t���x7S�r���s�G���Kv��_���w�����K5����H8��IK�FJ��S���,������r��{Kv���j�A"�8�s�p����ZF`/�"f`�N_6,�� ��!<�y`Ow��."�s�^����l��a���[��o���N3;K;��`#�]�r�)��1��u�/�q�"�Q�:n�`r3��Gc�+oS��f���h%�	Z&^����n��EQ�=�� �������	QO�����}�cz��T��H�]6z��#�q����1����?�C� ~00)�
�$��
>	kH��������=���F�f@�~@*0�Wx��������u<�q���t�>��+)>r�����oTd-{������Bb���	�J��:8�Q��~e(s`��[��b�]����bp�U�r��r����1� b�PE5 1�(����F����n@QU���0����L~<G���BI��#wP	'�k���yPQ���������A6L����j 3h:aQ�����j��S-��U���`��dY�Q�~���!/�j
!j��(jY����C	\B�d}��M_�?�lE�%�;L���7Y���-���n>W)L���A�P �z����a�'�R�SC��Z
7�[@�p�^�8
%k^*N���fv0M���f<�`���O�d�*�%��e��(�zr�Kn��\�\}N�cZp��
�N�E��� ���V����+������@���o�Y�7kN�=��_h�4�� ����t������	~*�j�����gh��a��3���u+.����(��}�������Ati��N���+����l��k���p�x�uoD��9�}HV�e�N&$�!�U*��ty��lm�������(�����V���w�"�g����Vk��V�8k��L1fM��`�uBy���;�^����KzS����&��J��3:�����p��P]bVg#�{��I����Oe��'������z���C
�����~���J���{K�����C�����S��m�,&l���X�R��#�h9�D���#\�E��%�����It����Z,>K�>�����F����"�Ag�R��&��}�[�C�l�E-R�/iR��_��+��g�T.��91Gl�Bt2��>+Z8c.��"M	��XY�wi�0�E��}�Q�\�9&s|�j�����`7��KU���N<P��+1@�C���\l�o��]��0��k�����y �>1w����ikH���]�X��'n�h���_,p�I�	P1*�V��r�������o.��w��rzQ���e9���`�h�����n�N��n��g�2��C�E@��c��i���w�C�%������S����N���7�o/�O_��\�UJ@!��J���Y��8��vN�e�D
/	I�gC(���s �V�Ch�cB��4���y��S�l����^��x�~���x?��{(�����e.�*�����tn�����*���=��tF��$����}�e{&����X�WR<�O��T�S�@�:M B2�9���A�KO,p�,%�F)a���������'j8<�Ok���/�l��j��u����u��Ef��x�d�p��k	��v2�U��0��dEn��h��Dg��Li3���9�8B�B[A�)���3��?�h-�M�z��A@0�`���^<8�(���4���(utu���P�&�/���)�kl�^�4,�'�e��Wa�h�t��:K�J�~�������"t0��jK�s��c�������=p/���9|*tKn�����M���*;{�V�
��^[�4������
f�*E�b]SL��9�LAQ�����r�{]��~����k��;�.�J�edqo����s�c��+����
Zj�!������"<�1���q��n,���dg[	~�������Q�Qrw��T����S��A�W����=&�+�e�uGc���G�Fs��k���l��hST[�z8��Y��������^�b����B�4�������^���M\���Q�%k�������P]������u��J�V���*����']������������������������}���l8��H��	��N�?]�x�T�x5�z��el�$�z�!���c��Yl��k��h�
��(y���=3+��
���
�Mg��6X�]M�������+L�21B6��E�7��7�.�^��X���.��&�;��Z�O~�my�t[��b�����������Er�z]���L�/��_�F�jWp��wk���}7�r���Sw8���|�,�s&�����iUL�����>d��?���=��w�
����t�4��(}���dq��������w��4���`E����DH��������M��y���f>9����|i]t�&�
9�dX!����b���i���	��>��#�8���?�n����Z���7�&���v}}xz���d?W$7�zN���}�X��^����m5���tq5{���`Ndw:�=^@NH&�'��hd>X�6�c=-z�d�������>i��}sU��H�F�HU�h���KO����qE���� �P�f*����U��3�Q�])g���Wo�o��O���da=���!�����	�]��������v'l��H��%�f3��n�&������[Mc�����`�����g99�m�z��p#'�ldBc������N�|�kd(h�(���*Z�ov��O�>��-(�g�=����i�j!)���hH�����'358�)�� ��G�����n}���w�[��N�Qz�N����I~m),��jg�b�o���}L����\�b��$������
�d�/�~�$�s��h���������_�����[z�7Mn�]L���b6z�����N#wSfV���o��n��~� �l�^�BOnh,������$����3$���}^�{
}�0��@t�^_���=��
z���T��u�!:�I���9n��h�A���t����Jk#�*(t�-Gs���n�f�N��e?�c5?D�C�#�Bp���kf�s���'	�2X�C�P��m{�DA����@��
5O�y{z��<|s�>�>y�*4W�l��qp��(Q
���]=�jw>L����C�.fz�-���]����L�����������#��
�K���wu;*k�'��(��Ffz��!7��IofgN�a
�c�${�P4����A�z ����������#�����{4~��Wn�� k@��!9S4c�������3���MK#^�!�4b�Ez�@)$�\�'�1mJN�����r&�%���Z@�p�G�8m��@TS��v���L��� �����3<c�P`n	L�p�^�9��!T$.Hx�P�4��:P�X��B]&�����(���R�V
/]����}���8�s��N���xAf�m���4[�c�1�mn\��Rv\�\4�n�>�~��&��v��1Tc������f�y�Pf����Z����^��y-��\��� �<�1�y��|H��{'\U"��y�Q.^B�����	��d4I�������t�8��=��@g6[P"T��WO� �>>�����d+�30�Pg��k#�$i�������&�'T�K�����Z^O$���u��s7�T�8�3`��3�����F%pnF&�-����������x�������x<j��������@a{������-|�h�l�\�I�;:�Y��v���c(���6�[E*���w��EU�S��������R�dI+
97��u�����s	��JO��e�JD�3�c��W"��?��"�H�D���w��W�[!�o$4�%x�F-��s��<�sr2�9������t��I��/�	���-�;����5
����1���}�Z`�>z�2O�,b��
R�MG���Y�d)��N������u���������Yq��7sl�O:��������^�uu�Fl����A&��~��}4�~R�SFN��9,H��h�K$�����f��6n����(m��P�)]�6$�%`��\���c�d���e��,��Qu9/���}��y�&�1XS"��YP���c�3�B<��e�7�(�C���L�f����x��P�$�^�y7c����3n>��c;���cV��Q{��fs��.*t���jJ��V����sS�����b�g*�W��!��/��9�H��(-�\X ��pA{$�����b9U���T
5���I���dRDHG�OU��]-G��5+�I��rc���������?|U��;�!0;����$���U�n��)�M�`�qZ@ �i�C�8l����D6u�}�C@~&Wk�J�E�a`�����������g|3�����I����x�)=�b��^�1/������
[�_6���S?����;W[���{p��Li�=2!c�d��#���4���u
����?�H�gD��\��cP_��M-9 �P���eV��&l,���3���I�iC�eG�f�?@|�9�+��C�����2��r���zIP�����f@vn(U��x{����w����*A� z`�*����H�%�0o����������L,�)rZu���N�RMQ���3D8����]
3�rj��p�i�X�s���E�����U�� �W������M���K����w��I�)�_���{N�t&s������4+T�\�o8E���3���
�������:j�(���6�5)f���%��/kj���Wp�2��5���/��X�@���|��{�q����\6Zm^1�,8�u�(l5����X}�T�U���
$D�T�]	��[�����,�W����e��r������~�93��"~'Q]�����b��B�-m�UB��O�����^��=���vH0����E�u����b���V�E����'�S����,�k�����Y����}Kp/
_����������/�A%`C�]���8���|�A$��H���{�A&�jB�6u0�@�c��e�X6���l�*�Auz�4�p�p��Rj!1'�J�`����=�T�~����6�|����%a�7iLo�_|x~���������|���l�f�W1s��m�������3O�o|L�B�_]#\�0�8K�?H�BU���'K�r�e�5m4s��#��D:��@K�9�����L�9������?J�S�����J������b
�UH.))nTU����J2�V�r��<�&�5��_D���\	���l�?'n
��7yiXn�8����#�e�]����m��O���+�<�s�b:��4+I����/�c4q^�CrFg��,^!d�\j�eW��Xdq��b_�>^�v���F����D����:��n�B�Y-�df;W�h���Ci�r�4��i��)>��C@�n��5�,9p�G)8���G
.m���0A�V��	C��\���vG:G��QYpA���C�D�!�)����M��3���qjj�H�p�,A��l�cC�%���F9�Z���M"~NI���ibM ���������o$W�.�=�r�_S���o�&�Y���0����>�e\�����_[Z��9X����-�����g���F\�8�9�������=��}1�rCp���Ka]c�����E}_������Y�_��l,�����it��2���uN�
s�g�����&(�_�������-��T�0���t��Y��}Lo�����
J=���iWi/������[)Zr�n�8��
����~j���X���^�3n��W�/uB���'�e���y���e�����~:�'�N�����\f}�R�i�39:�p��p��+:����::��2���<j�w��9�/������BF|�o�u][�d*�)$(�>��J;�`!���t�
eVJ�����	����0FI���r>M����I�_��_kx�ZS<����JVw�������)�#���2#i��JPgZ�K����hz���E_h{��(��#�5��Z/�Uy8��A��!i�7	G���tQ�,������h����7�`��
:������d���B"g�
K�����u7���O�#�@y�W��������F������%���8�X�3���sq�V`:k:��uF)"T?t0b$��8/E��O�@�,����c>z�`�c��MrA���W��Z�������}�<�]kH�1J /��Y�8bcMY�A�������"�[��&!z�y��
��!����]_�X��@�
��\�����PKqr�A���.�t�I�A�%��=����X���������c���uzZ9�Y���T!k��Sw�;��3Kk�J��MGm%N��6RtRy���R*Y��b6�e>�r�gt�-����z;���3��,��D�a�@��H���ph���z�M��*�����qr��H{���+����r���=���OKA���,���F���:
��%rCw�@��^��)1=���C:���J�q�	�D��6�0L����������:��,}7�����h�&�!�y�!��y��fd��)6�K��C�b`�B�D<�-��B���I5(P�!v�������m�V� �4�}�eEf��[B�>�����U������0�A�
��}�	&������O�M�2+���]#N��!R����%���������b�F�o�	��xJ(4Sktt@����b�R2/oB�B�Y+{����Q��'[�Z2WR�Eh"�������0s:n+���y����xIzr��9��w!�y���d�������4�,�wj6�n���������!��P����c�I]nS|�1����y*�Wo��!�e7��V;���U�~�2�:����E)A�}��9��&)o��1�0�Oj��	[�A�9;o���	�Y�.�g_�_hN�3F�t�
���5[�,�1Z���|L7.�#�u����r8����+T�����������x	����3�S�,�.V�#s��w9SM�K���w�7���G��`SNxggx3�t/���eE�7H�	7K�[�P�w�F�h5} f�9�LB^�5R��2���������n���g�V:�^��������_���/��6����*���k�I'��sE�(F�;����]+�9c����[B�$�p�����Z�+���YY��
A��a�F��mM������]�Bu�1�;qJ�_~�Y��;�%[�uM4��D����'���b6�b��J>!i���
��v1�oby3J1�w�;[��ng�V��{a�t�SVMir�*��h�46+���?lr0L"~���A�fX�SmTG��e�p?FC8B������T����!=�q�I8�3K���S�7�iL���0�%{���W��������*��yBP���euX�0�5D��v? 4+~�7����ZZsd�!g�K��X� ��:�
��o~��C!����3O�k�M�^zj�oQO����wJ�s�	iz�X�?H�� ��>"����� ��R�] �b�\4�N�o��/^���9��7z%~%�E�_��+�P���� |��1�*����9����4�����*T�WH�'�<�U�4p�;�K�5����������+��T�������Y�Ek��HH�!�.\/=O�3������r�#.���(��A�_6�F>���f\|����6h�D��sxy��mJ�d��R
�0X��7c��x��������:!��b��{{��f���(�����N0T���a4&��7�h6^P�&��������w���O�����h2L�����5�*��n_^�N�\����R�����(,��6���B/�]�u���1�AE���d��q�7���c������`a���C=�?�aX�
h�h�3�uF@~^��Yp�}Qx��������*�j�f]O[�K0�.������xf�m{�����e0��sX��Lid=g&E=�%l:U�[-�Y�^\�
��Q���"�N��RvT�X�!'s~D{L�����K��D������y����i��74JI(*9&oY=��:�_��.-s�6s��[t��H�	�D:C,1�%LhP��9I��MU�i<FL6��=rj�J�_v�6��CJL',>�=�HK������0�����y�p5�NL����S>6N���r��.s�	����i)ZW-�)���1!�U�������+6����Hm�.��x�5�.;E6 �����r��T���j�
{:�����\�	D���
9�9��F�|�(���7�-��&Be�?��>���'�A'���@��;����y<���_��!�&*��E��rNI]L�(��p�X�������N����	���Tg��"om7;[����1���z�����V@-U����*�r�F�����w��J��?�:Q�F
�0Q���.�����CT�}[;��V����W6��_���x��(nnd���\����E�����k�kV���o����Rd����b]M�{����P�i�>�V���3�STa`��b~���^�l�\lJw�
���ik�A���p��)(�Ox7F�K����_������@w(�:9;9�����+�#\��'�!�IQ�)%����1�����j�a��h��`YB��-�a[���h�!\?���FS��i;~�s����f�D^5���$�2�)!�:�M#�%���(B�J���x�Q��*3����!���lo��S�������H3���x�i?���6(�~.��P��j��@	�|<�Q�@n��+�H�V3;]�x���i��F#������A�OH�����h��swG���(Q"x��D�������
d�S����U�����~@�0����������mZ�LEC�Suu��K��;R��gl�Y`a�O�������_Q���(S����^� �fj�rfM6��l�M�(�F�z=���J�{X���I�@���B�
 ���{����%�IM\}��UT���������$�IQ�)
�c���Y�K�PC[{�9��'�:l�S�Y�Qm������4A�SH���XXi�6����|�����w7��z}��.� �W��SuU)�Ru6������U���6���ym��0S�ocg����v��
��l466��F������N�?T�������AW��C���X������"^�n�����[[���Fc��G��V�W76�6:��p���jn����rc��Q���O5a9W���p4������f��@}��W��c��[�/W���F����:��(�<X��/�;�\��VZ�����^�/���W�������]�H����H�j6V�,mN����i_�6LP0�N��6Dwr����%�����:�:N���_�N0,��X��yR��`7_+����uyxt���=�D��~�`P&��;L������,*lL�I����^�Z�
��D{�t'���L�C��%�.�/
G9*I��(�����1^
&����?�
w�gL%*�&t���K'�x<`�	��s����f�ATo�X#�����Pp��J���m4I��+�Q�U��N6���=���	��Z������/��`w�%��?x�i��px�LA��4�@g��N[�:����^�d�3�*������H����\]I ]�D}��F�L��/0�|��`W1�<��N�j��b�TQ��������T����������JF��%���lI�������^c3�5�f3��w3�[���I��Di�w�Tj
N�r�����K%�We����}��z��9�{w5�s��Jc�a�;��K�]�?�n�H�%o'X�B�@.�=�I�>YU����E�)���>
���K+��=��7�N���L��b��>'�?�x�>F����:ao\�>
�<��0M��s��F�
`�BW<>%s���������}|z��|�z�u^����K��4�����D,[��
��Wi�Xb�Y=��Q�C%������ B �U�}u�X���Gf�97i��'|����Iv�������`�:�,�Z+�V�H2*]527���lI�4�v�\��x�$�?������@]���0�Z���AK0����X�QQ�%e+���J���������_7%�TqxT�9�1��Q��v�2�;���l��[��V�u��^}g�����'��e��r�Y� ���0��d�m2_��a���$��SDs����0����Z����e��V4S�����������Nww���������N��m.\��s�8�4�����/����x�p����u�f�+r�k"R��P����<����s���sf@r�s����2�(��D�7������P���Y\FV�����m����j�~?�pks7U��J�Gv9Z�]<���F={��NgpR2�F�~���l8�F�k"��.?�hz�e&�`	�lFfd����4�"y�{��4������$�&I�[)���o��<�
�?�bf��p�^��[j>O��N8�S��Ui4I��N����G�Y�_��{�
����Y��^+�_W���@]���8��%wq���{���+���8�S�<������_�3������v�}6	���en��^)dL�7��}l��B���tg;+$&�K��T>�F���H��$�F�X��p���F�����h�F_��:��;��
�H�c�H��a{6+����a<&5,ZP�<�m���Z������������8>A��xg6�#X��i������~N�W]�q�g�������Z��y��G�ht�������*�n1���=�v���!<<��C�f�O�k��f������0�	M����WzjR\�>���[sFi[�gx���^3�!�z�|�2�*�����kH�G��p2�C������g:�SLpyF�C����""_1:2!��&dh�K�M<�u�bf��3D��cq0�5>�"���U��"�a���F�������}�������BKR}�W��C|�$M���uM�r�,��Q<��oP{��1!0�"�V��i��|_��*��FsK��v6*���M	��������������]�����YI���sb��PB*����h<�h�� �I������$�%��Zx�U������WS�2��'��wW'������45�!��Et��$�'y"|g�p���5jP���`(N��l�7�{ �4���(�EQ|�:o�D]r- ���K��F�a�5�E�	���� �%�:��
���E������>6�~�B�i�o����!6���]]x
���Z���9�������=%B��|���h�]�������e1���������&��!R|�
1���1x�m��R$����_����!�{-`���}��@KvU|�+�P3�MQx0��� (cd� �����@E���^G�n�/�D��|��9��9����Y@��1�Y��O|�������U<c(x���kE�%u5��%����u}����{����S���X#S�.����$�0��P�$�����#���H��u�R^v(�����R�G9=E�#lY�����p���#�<����z���]�:<�6���������m���v�^3<$�	�Gf8��.�I�0������|�������
"�!ZF�)�j���O��H
8+��%�D�d��A�&g?]I���w�����.�9_7#���i#�#o����"�����>���E�-��s��Z��i9I-���M�-G�w�A���xt�1���9 �����sI1���r�������I�G���G�9�c1S��
r���#G�������R�m�K�R�I��;K���
'z+i��@e�m�����![}���O�0!������PW�{����m����=���j�������J�����R�k�h������Fjv��q*=�[�.s�i�xw;�,�}*%������W"Y_z+gw��l���:T~Ld����
��S�p5�f��
��QFT'{5a0T3B�Z��.������ 3������R�c���o�����<9:=<k_����]���Q�(?�~��C#,���'�d�q~�Fq��p��;�YAy���w��B��XCQ��/K\�0zQ25��>����e���F���ZsW�I�
��x{+�z�ST3����7i�� pw}`)�]�����UR��i$5�=9
1����N8
.G���WN�J�)uH�����]y���\B��g����������%�����!���A�H�2���>��k� T��V�nUJ�d�Ha@0�A4�z�zo�yV�=)�6�_���ci�P���UdJi��L4�����^{�Z�<Lf�6�A�5���w ��a�R��'�y�����R��bn 
�u+����B�C������)�3�7�M��1�\<��maVu��&�Sxs3R:F�cH%��Oz�U�������0�����#���Z������F���t*D�'��1�s��� $�"�]=a�c��	x�D�IN:�h�u�j=5Z������d�;��i��5*kP��Yg�[w$�X��E!������E��E�Mk~"��^���:S�h�`��"%+cC{3wNn�q,J@bOkD�' YN�~����a��"�1��\��NU<=2���h9O��=9>�#�U~^\%�pxz]�8]��})��������U=����=����7W���2��_����
�z��G�og_�{���y[������=��u=�ug0m���[�l;�_��m=_������?�]r���n��i�iO`��M�9�����sj�N��`�����i_�>��hwF���!���L�y�G�+�;_�I�?���@	�kG�|�V����'~u��G��kEr�_�#X��������{���}�����hh�\'����W`�i��5���i�9~�pI~��k��	��)����9����eF���A-���7��+�	���(���ES��9���f�Y����l��o��K�%b�B������W������Zr5,���8�PG��%����F����h�������XK�yY�/�3{�l�V�����)�����H��X��u�aaL����*�5��Cb�V<��M�S�&E���(Q�.�0����� 'M^��#��tZ�>�3������3��t^����������S�z�����Wfd�[�l��z����Mb�vEWk���.�U��XO8��C����`��H�T*�.Tq�@HR��d/e>�n�I�����������R���K��S�(�zJc�~O`�p��D�+�w��!J��x��X�F�'�n��������Fn.
����Ks�
�l}�<_�;M����~
�&Bh����p|zU��a�H��8&t��4��~6��-��B��,�> 3!�)��L��\�WL�&F�3"MWp4���G���W�I�����n<y��{��K:��hK����!���;C���+]v�h������0;�A�v������7�-b^5�W��W�����|d��ee2|d~0\;�$�`DT�!�<w�h�z����h&]Z.+��?�4��Z�.�<������Z�	/����J2�G(��P�k)[��/<.c�q������.--U�>����<�R������\���Y6�T-9���rNW�K��<�#�G��g%����H�F��cCZ���x���=���vL0�����j�d�����@�)����t4�������A��p4#�!s��4����>�e[S�9�y`���z�>?|{r��t�#e3�����8�=����w�g�C7M-%������B1	3�:W���Ho�	����=�L�k�����^���a0�����_1�FQ^*Q��!��a_O�1�{"/w���L�O�.�O����O^����� U�e���kVK2����P^>*S�]��]|�����<��K-|y����|��sW������d���)��[����v����N�p>�����>�M�*�F��X�����}������1��e~D73
/������$M�}�c�;�QU5~����i�wB��*����Y�R�]*Q��<y��wQo�h�3�i��/�����;���k�*���O����	��e�g��3����i;�}��:�~�0����z�?����V�t�1���#*��$���~��U��%T��J�����Q�������)9^V��$����f���o���!��������e�<;����&C�>di�(<��Q�2��� ���3j37z�K>;�>�"�n)��E)��������x��Lx�+���im|�t����a
�������2���C���Jq�i+��A�MB��G6FJ�mW�Il�	�kY(x�,|�uBCx����������L�\�����c�w�-Q�O�����	2�yW����\^GV{���f|_��U�����{����*76�6�@���v�<�
e��%1�l���i6�d��LN�\LV�&(�������i�w�q�8���e�jx��BT�8�6UX������D4/�;�`-�sj��C�:A���%��!t��k���k$Ft����4�Nv���Z(��I2������R��X�����2���AX�srL'���I�E��R�4I����!5/:��Q���!���\3���-�]�� ��dBo��
j��gr��4]�&�K��������7��4qt��QH>,|�����">��
{Y���`X��[���qn���`I��D�*���IO�������Y��x��D�^}�����Y���lh�����U&w����x�b�=���5	�g�:���8�+����DP�����H(�?E{�a�]`����5��?q�G���L��#�r%�B�K�!b��������(W������/y{�N9��0�u#P0'(D������m��q����vZ8u�"�f}w��E.���/(3�0�K��0�
�	�f+���pxFgb���L|;C�J�kD�,��`n������I$RG$��	:g0���d�	�t�]��_�����i�b�����s����&���M'<��z������I������(cO2�3�\��h��~]���B��;�JM������:9�<�j���O��T������_�]�L����)S����^�5��������lP=3i������k(rz�n�jZQ�d����A(��:Y��:��7�TMY��&{\��D*���f�J3H�Gc��Vy��ldU^33	����u''j6S������h��y7�c��&���q�q���A&��U�{l
$�����_����]�nf��_���O5�?���q���]|�]���6&k%�r{�A�J�fc�Yi8����������z7X���g��+LT$	�(��{7��)�����{%��#�Vj�YQ#X����R�kR\T���������3��]�l�47�D=��Fes����7��@H�Y�ww)�z��J ��?�8��XZLK��@���[UQ�80����G�/����_�O�I��a�������_��3SZ�Xy�0YG��p����b5��Y�P$�!}���#��8�(��Xn�U��b}��������� i���C��0�Q���=��X��j\:@�GT�h�G����;�I���a_�4���:h���&���#��[��HG�\(z����5D���������q��}Q$��|�$��V�~�p��%��o��R��9����:
h��Fw���jvk5�gsooo����mNE|m~!d���j/���gX#��P��#�����|���I/�w�P����QV��Fy����qE��� �P�f*��~����Nu���^g����hl�n����7��z�����'�W=]�N�=���1�DI��D��?�>������4S���/�
2�x5]s�J4V?���G�������5cv�j����a����M��������G^t��#3UG<U];U];U��S�Q���Jo�Z`!%}<W�L�D��E���v�0���1����Z�GB����65�������/O!�����O-1�ETl��=��-M���U��S�:9{{x�����6C��px��=���_�m)�	����Y��
�������^U7j���F����kn�Z���)Ei��i�R�RW�Fe������s3�>���?��������J����1�8����\95pq!r���{�����6e���K>[��L�ak�q��1����[�xAM��4u�C�I�3�;�$�X��`8�^������ ������	�!�������o��[m����Wr8g���������?eSs!%���:�>!��ME�~ZJ�_�� ��N|������
i?���$e��I��E���i�L��DB����
��o�������N����l4{�z�l3�����&i����.������Rc�1�CU(�?���PX/��F��q��	�4��hyk�������t,B�SB�]JjP�4�i�z�R� r}������U��c,q�����q8u�:��_eK��x�v4���]t��.�_�������#3+����Fa����qn�\��S���Aq��m���O'�I�_2�����h���j�+&p��?������^��I�� ���a�>�vB�_����3?���F������������G��������V7����V'���w�����N��4������V��������@�
�R1Y� i@B�++�P"N���5�P%a�<h�fA�/���(D�7���h�;��]�q�A��#)�!���Z@gPA)����x6�8���; 4t�����Pbq����7�2}��\�A@���	czK��T��S�P7�@�B53���l���l�&R�?�g��u^�x�zt�����6�>�_������>�)/�����'^s��\�VK��HK��B��g����5����a�+xb[�n��/����F�n?�1n*�]���V��]so��:������h:����M��F=��uso;��u���K,�S��e~�������2)�)e��w�/���dq�����&���l�?4q��$��~b���?O��|~�����uV�N���ln��k��F��g�F��'W���!�-�g�J�W�i����-f���g��5��������"L�_��m}:����'L�/�
�������gf'���@����=X��Z-h��n6z�0�%���.G�{���������Wp�n�����XW����d�o��C�=�o��w����y��.7���,����h�!����������2�s�,�����z�����U�.����������A	W�����N���
�|��V�����|Z����?��o4�NZd�x}zvr|�:�<��zTY�&O��3N<HMx������,�#tvJ>��|{u����5��Qs��@o����rtq���
�P���
>~�R/`��m��OES�D3���������q8�kR d�
������b|���\��s�vK��"���8,!Y�PQ:b����U.]��E�������IRd�%�����p��Q
�K[��*�lPu|s�����jXS�SX%����-��G�J�=���#�G�8B����8���^�q�T���������2�uog��:������x�b�<V@Q����
u�Ju@��PY�b.�t�	�����!�����i(����G#�
��X����$��H����Zy�v��19�T���-����F�"jW�8�~�M��F3�`
��cTD����w�z#�lm�'�nt?D*���z���Q$?�.��4�\����~�Qv�:O�s�w��&���,��h���&��1�@ z�����W�o����r�7t\�gaU��l��������rz�:��V���\g�>+����;i��Z4
�`��k �������0%���c^1�e�o�Y\�1��:@����^��9=�*['g'G�����?6��FaWrll+e�{��������
4Rl�Z�t$��6��e&r�D�?Y]���%;�������<eyyIaQ�IZ��;q���
�Z�A���5����t��dq�.�u�����2�q�����mb>�/IQJ��S1���?�zF�2]�n0*b��-U"��G�|��������Qf�#ohnd���	�f�>��!j��7���+*�_#�UF���o�$��/N�u��<��o��I��ku��5D�d�=A
������o�/Le���_�+�i����h��
V��"'���2f0�c]c}P�wrNrDwx-b�Y���0
W���������9r��������X���oUqN�)W=�p'8>()$��r^5Q����/d��F1�bm	7	��C�k��*j���]+	m����������2^_�T���5���aV@m7�Hg��4�[�H1'���Y�E/�N��]�[C����Hv��	�������#Q��fH�z�G�����������?�R!�M���/A����Hhx�|��mSX(=f+��\����{A#�}�����#�n7��Y'��K����������}M	��2��2����o�o���//�=�}e��aE����c��c���5G]����n��F����C=�m�j�)��O3iK���a"'���Wqw2S��&v1���%&�q��8�R�)��F�
`u����X}�M�������L�����s2��l�Y\��:o��#�j�C���:=�^)����i.��G��5uvx�����u��D[�]���d�R�zf{������}���7��8-��x�;����"�$�m�����d�`�#���$v;�;����/���������-��%�v)����FIS����7�K��0v����������k�3��GN����N����F�z���@�;t)��Y�x.Ss��K��7a�[��9o�'Nf���~$~�������H2�e6�u�����.��df^}�����p��w9@�=��.H��������oN�1y�D�`�vf7l2�"t���>�Gh	���]���9q2��9�(�N+�6�E���NPH�Ad�`����
B������	��|���^XF��vH�e:���z{���
_�V�������+B)d���o<-���]Q(�<DYN��3�}z�����F1�H�)"���r����sx��%��6�������='�@��|�����	�����d�������������^@�3�odX�9�q�-P�������O� ��;��C���{
>2��w0�Z�	�o��u4~
���7���L"�&�u?�[�uT���	��H�oI��,p��b��pm"p�	�G��8hT���+C�g�|"�Y8bJI:�Y����X�A�p&�(�����8�(���rRA���Tg�e>1�����_/N���h�M��k��"�6gK�����Q�(���s��_��u�6�| ���[����+t[���>3J�~{�2�����A]{������$r�]�d?G���R�F~��9�yz���kW���Xg�Y�n!��|�{��x	f�5����N��"�������@��w�q
!Os�sF����4WE7��G$������`$����k����5P�`a���N�y0{��m:o
�F�+��Q�M�����t��cqk$'o.	�������D^�������x�O�EK��E�	�����%v�2�H>N�twV%F,��16=�������2���|xm�������u��,n��#K=���K�u�mD�/�H��]���������R-��PBi���\]��Dr���q��G��Pq��SBH:P��3o%�m��w���&�@��J9	4j&����O4�7���(e�C��=�
�(U7Z�/���W.�$�F�|
w0�4�{������Y���Y��M�������(�d_]\��%[
��#f���e��u���~}
�����Y�u)y�a�.���� p����1">%�s����LT}?�����-��B58R3LX��k9���reL����:Ipn�Q�6<�jh��r3���m8��EA�J)Y!B?��fT)n�b4��{y�B�O�7�+:L�Lf��8c���Z�KaIZe���y��{x�eQ`��MX�-�:���C�"{�����QL����i>��{����-���:�;�c�h���&�XD�c�y�~��Oo6���/�?���-���w��O�!�f@}��+g1B2�t��I����40G�����&>��T����=��Y`)�����k�u{�2��m����^]��:���h�@$^��=��(�SP�R�,�����'R�|�����
�
tW,[;Cc�xB^e��f�1�;�����T�R�CN�7��1��`��(JB���[�D��\?�$�\�����U�_s�(��x3K{a���L���7���nL��r��x��nX�Hk3�Z)Rve�+�#r���Tl�}y�,=��4�lG��>6��e�e��;�VM�5�����;1
�/Nx���h�L�mk���=e���V��������kN�J]�}
�U��^���d]�?OKqt���Z��hv9��4�����=lQ���BF
�X$1]�c*0/�[7 �l��~�/@+[��j������8Fa@=���g�M.����_�5��7���l ~#�J�J�V�|S����	��q:�����]��a���iz����~>;�a�x�d�����vDQ~�!H�aL����j�YYS����g������$��N�(�(H�^�L����'G�g������9�;{~��wv@Xc�T-�
�BX�p�%��b��Q����2�}� ��4�J��a����/S��,�qF�A��l�����Lk-�y�M���� ��k|S��26B��>k�,����{�H9�����k�����G��Rh����Zm�i�s�x7&E��d|4��\���Z�*,#��,������X�Yn��^�&����\� /!H�����Vc�����b����91��9�-w�,
�KM������)�3y�������-����u�U-;B�m�60�W0�����#��$l���aG�A�4l���+=g���a>�(���i9�0����#�������\Lm[>38)�"e,L�����I�D{qYD����4
���Gvvw�E���S�7QPSZ�G������V7��3�I7f��#g�&��[�s�U�a8�(��q�:��a$��}99�����~E������eJ���E�e�MsD�9*m�s\��F���?�����C�Y�������?�?�����Y�<�B7�����a��Q/�yvj�a�^b�a���?%���DK�Y=GKk������jR�[W���i��@��_[��$P*��k�<������g��r��%��r��p�*��H���SE������w�S�{����:=�����h����1�NG2v��hQ��%���t�Yy�i�E��Fy��'-I��;��i�p�Yxs��9������&�A�`��q�-J�U{��������������X���3�/,x��2���)M�D_���V.�98G�9��M�]�S������
#�����&xX�+�m������n�_�������[��k�&I��s:+�}u�v������e�����2>�	��Yy�(+�����t$�4�7K�K�jU�e3~�{�l�&u������}�����������Z��
��9U'�Aq�hH�}E}�|m�2]�����Nt���������t�����>f�?��?;{�s��r�t��L<�����5]~5c����G��^�W��9��){^�N�*���:�]��k]p�c-L���;��]�j�.o.����6�9��Y��?�h�����y������ZE��F�����C.��������2�����_���9�?-c����mK&'�e�������:M���L������NN�F����~����p�X��9������\8|�XJ������"{"��Y��z'�T5Te����$H���
��h�GcS��(���rk�"�M������q�wJ����?{��X��6�;uA@�5S'��+W�U��f��.5(C�*�L�x{:u��f����S�+���o.)�-�Q��L�H|�OMbbO���x�HR0�+�bG����"�~�^��,GH{�������������*�&{g^$)&�s�����P��i=F^(��5x�"��e:��
-���m���2�2�r�u��eyo�$��k/^���������T.�v��	��s�����M����]���9�1r��$/�������e���"��8��Vu"�N/�07���\�0Y���K��MD���trc.ZX�3
�X7�r���0n(���O/cs����!�����"YKN�cr���y,��r�������9��Vjf����koj	zZ��73������Q<���aR#��*���y�#��b_C�<N��j�#{��::�7u���%�?wT�X����hm>���I������[*u%�)�}��f��V�������?��������e�_~�?�:a�>�����&�5������}P�hg��R������Y��a���GB�=y�G�$�����7�_����o
al�!�o��x�����E����Ne�>8�l��������I��~���W����?	�]jt%(���
7s*71b�<t!���V8_�����c��1��y���<Y/��S�Wv,��H��|�������|�7x�����������9�{�����*���"�G������yN��?����~���e�8q\���I;I���
�,��^�q����~�y��]�'^_G�2����N3c�H$����'h���"�v�/�����Lx�I�-h���%4�Yj�vMz�87^���Y�os�i�{�3����>��<�d4��I�%(M\b���(zL2
��@owo���]��.��\k�����_K��S&M_|��E���w^T�v_������3�d�?�Wv�0VI%��k|��4���A�h���������WGF�.��n���$+.E����Zv��2�"���w�|�q���;|������tp1�����x���-��a.|��F~�w�M�\�.L������������F�L2���y�J���F��%CK;D�H���g9���� ����8}O��y
sF�PP�]���l���r�
-���6�J ~������O�����h]W��|�~UD����w��
�n/(���@����y���S�������v��Kn-���Q��+�@R�Uk����2�:[���.iT"W��u1������_��b����SN�X�+������%�^��a�g�W���W�)�u�K
��G�����^f������]�>ZW~��b��n��~7K���,���w��"��5�|����o8��[�[���E�l9�cyb
��w��S��q������g�Q[&����[��B~����w�������N�EL#���T�t����&���]+��B���Jo^�#A�^�I5��@�=��|����o��
������_��hX����+��Vv�8"+?b�wr�����7��\A�1g�^������V��U���YqV����(Y#|y��Xk��*������)�i��!�-����,��w��N�g����^������g�[g��e6z;�YE��F�C�����U�3Xr?;XX��h�k�s)�E��mcA/k=��w�2$=��������}���};��[i�B[�F,�'�&B}C}[|I�'~(�E��� 1�����:��IKK��"AI{��3	�qR�Vo��XLQE��Z���bX�hY@��K�g����ArW���]��;Y���8��"*e_��E�jY���2����,?���'&m��Tj�(c���$��1�M�o��B�D�L~�<�f���
m��y��8����������F"��#���v
c�b�Nz�� )�:[��tK��/���Q��ZQ,&���i�
[�skR����|�^��U1���7��yFLz�J"C���"�7"H������BA-��������\�4e(jg�Q��������\����%�)aJ&����1�v��"c���Nr��\����y;r>,��e?	�)�>A��1IU�f,kG�p�m	��9�GR�K&+�q.9^,u���y\/5R��"{��J�����Yd���_Z�y^��bt�u�������b�������j�|��F����.~&
�Y�B�n�"�d:<�V��)��
��t�q���EV�o���T���E�A��%2����7��j��	��9���X#;s'�i9����r�a+��$y�Y(����n�Z�$;aq0���f����t'�mv,�h��}=��������\����_��u8KG����Sl�I��!L#q�< ���cd�U��;���})���}�Z�_A�?�T�`�D�O���;�w�M����v��J�|���b�I����aV-le��}��G�������wF�%8�����N$�c)�V1{�����jD�����J�/S�o���,/�Yb�R��F&���=��������l�{V��VYB
��)��J��{w���!�J�_K�b4�����Ds��{�����j�}[%�}8~_8d�+���X���g�����
rK���I�����Y=�@����jz�/�����L
i���d�|����������I��A}d��[��h]$	�s[���pv5�����1^Y��jw��u��f�������I��:�k5������7F�)����q�������6�b���o�J**����N:��g�����I�������������)��&���m�)�����L3m,���	�(��r/�����z�Im�|�*co���`5Hu����<%��QaSe�n��i2������;�/�X��aXx�6�_5�%�e��W�K�����id���^O����LF��7���� ��E�3�����"5;R��t���Q`���~�{��|�SU��R	�s>�����@�[�
>��`�3�@���s-\Rl����_���f�������
gSy%-c'V����>���"'�]��'�t�k��;=��<��m)��e��^��F�!*���;���,�q��2�`���#�q����~������L������4���X7�INm�3����������[YW��H>��
n�4���\�B2Z��������P���/MZS3:�	�j8�/	9;�����A�d����m�58�P���%g��47F��<."�U��j9�J������T�9�.��A��4�����]�[�	�#�[O���7:rD����������H8�2b���4�\dS�4���N�<D4�f�)��
����8m�{]��^]�9�9������W��RN�^f\�o�t�Q�(��W�=cW�)g����E`�U����
i����
���T)Gu����gy�~����.
q��^��EA��~��|��uK��_m���7����Mv��>�bD� OS��*o����z�6WE�R,��E����oT�J%����<�-���L�-���b<V
��+�w=m5^��[BE$M!tE��%
XI�E�M#Kv!A�<��r����j�!(��D�)�����w9��H-���Rk�r�������,K�����z��^���5�����/I�������2h��������r)�Y=�Y���������_6�2��q�V�����4Y�aj��������J��xmp��P� 0;[Y�����wk���a��[������`B,�)j�8r����������#�������d�^�������E�Lva�9�{A�uF�r���������{{�8�j�.��Gr{�p�.��c��U�L�}�Z��?I�b��N2�����(���n9������E��+T������GPF^���_UeC�
�b[P�87���������%����|q�MG
��j%�&�Z�x�_+��j2����KQ�2�~�E�}&j+IliQe���2�m.���B�����m+���Y>���"5��"����7������P�"�2XU�?^M�����UI�>r�Vq����������W���*B�qa���&l���%zZs.G`�z>�����L�&o������������=;����m�%�a>�� �w{��y.��5�9G[YY��,N��m����O���h�-�y�Z��]��k�s�}���r7-Z�<Pq���j�p�����8Jjhe
��~����Q��M���w����y����+Ml�u��`��j�K�Z��p�U����_��k�+�q��`�bCQ���|����`k��c�G�z�6mj�U�W�
�.2��������x���r������M)��d�O����a�V�
d���f;W��Bd�`>��z�ea�E�.�g�n���d�U!��azSQ�6��(f���(�..��
�(���P�{�\���s;G���Cw������?�_���$^�j�6�d�U�?�{��Mz�1IW�����r���89-*>��1J�����[R�"��m�t���(e�s�v81��Z1n)���8�,��j#c���z�$����R��_8���	�2�C�)O�0������������=_����Ej�W����o�P����N�o��������2��/m��}�{us{�if+-�����j��z��&6��'����l�S�l��Bk4�=�_�� ��N�
���_���i��+s�*� ���������Z�R'��^/�ooa�8������e�Q_���� ��������;����1���U$)M��qV��;�;�{upt��b��X��:�������������<�l>�1Q���;kx$[�.Y>���ms�/��F�k�M�4I�{C�����`���*>���\�����dpa�e\A��]\��^����^?����'���y�����Wd�C��M�7^�~�i�/��������������{�W�]kU�V��~�G��B#9�����Pk@$�u�>����~�$Q�����,����������^����^�h*����OI���z�P�,[�������������n�k�k��[C�`���/�q���\�O,�9q9����������#q����:��?������Z�.��QF������?��v�����S����m���2Z'2��_�$;@kS�^������ "B_�������u�D����$�O��m��N�2���Q���^�m�}/�r��[pG;�-����rF�"���6����4+]^E���3?�!G��~�'=���G����wx����Y��/��`�i�O��"I�sy����`����X����@��V�-�Ej�������[�B��q�+�*������z��o���F�$�����<1���B�zI�A{�&���U~Q�������}$��6G0�X�IB���c�i��������m.t���H�l99��Pt�P������6�%e�����q�Ai�vn�sW�����������OM!�����`Vk�x��)=��#Y���z���d�v��7I������$	�HK:��HA'���z!����Z��������|v��0�������@��L%��n:��'�AZ
��u,����{l��s��s���?,;[������Aj��N ��9������q���2&��]��A�v��,�<�Y���|�qe��`��	��x�
n�-�16,�p��f�����G=>g��g*=N�V>���7�`�9�o\O'�z��$�s8����R��$��Z�Ra4{��j.v�Uf��B.?e�l�h����5�\��,��j.^��j�f�����j$��H,���GWz��L������*~T��yC���\���C�����>o�C������/+^%�.����{c1�}��y.{P����o��I��r��A��W�����(i���r���� �<1����uo{����I�d��v�����y$A�������2�n{Kl2N�;��������X|@{�N.�������G���?���?y�)��\��Q|4�:c��W$������;�q���������a$�o�����N3���AQ��#�z��Dl��rG6x�P����8�0N��Q�$H���	p+���!t�h��gST�>h��"����w������w��e"F��yu�M��C������}�d.U�SN�}��S
S������r�������z���g�-so��0�)`��
���-E�,E?�����������-E�,�|�*�2i�&��s��)�m��+(z{:��G�|�+9�I���;�J����p�WV��E.P�?�_��-��+:�����\m�EI�H�$�H$�C.����m������=L=�����Q^��K�g*���3�
1UzG��[������"���eq����s��K�$�����{�Vs��K�Uq����j��y:�h�j#���q8�����i{����>�)C<�v���k	.���Tu����+K�5�����������hu�M84���tw�I� sm��5�?���|�a�������Lb�xo�t��g�K�	41�U�#�*;�{��#�@w�^�<4�TN�N������{&8��8�;��=�Z�����V=yR�@A�w�"�PW7�.������R\��Be�G��vv��2��5Iyv�M�T�oX&.����?q�-�������f����x�du��X��~���!�
�:��?����g�#v��2XB���&�w�L>�� ��>�&O�I�(���R���P93���W+��FL'�}�%��G����6Sk����nB3i�:���O��<�G����Y����{��3&	=��xS���kf��������bI�fRN��E�������;�$���O,��b �N���!��������x�H"	|����^xu@���%�XhN��<�9�#�E�'�q�O� ��Y�( U{������/bzBV+�(d���4Gl��W�p�8>:8�~g��/b������Gs����+ ���/�t��q'���_��� ��LZ�}���*O��1��l��RU�+}S�d�m���On$~0��������;�="M���)i��EL���!��r����My(Ts��"�k�l^��H,%Yky.)�J;�0�O�7g���bp?;���eYD����s����7��y8�����SQ�/�����W�X��?
/�KF�(���,���m��|9����:��A������\-
��������������=�!#v?�9����LZe�.�,R�����;��kO�mQh�������7;.;?��:�?<%"4��������;,�����i<���4>'ss��8�%�����J�}(�X��c��ig������474���W�;����_.�\����������{G���;�2���!�R,��g���m��@
>3�������������A�z��ob����y%���f
�����}�����!�?�>���V�����=�F�����������.��L ~���S��{v��N5�����������k�����%�	��;����1�O�����'}Y��fg\*���"��
�b}��G����k@�O������P�����PFZ��6�D���(}���|�6O7T����%J������4�i�be*�����,�(��-4�|�� R���4jH��T"[�3����4���c����X�3��:Q�����;�S!���$����2 W�2�U����u�X������D�����g�+�)H�D��wbGP)}&
R�����2 �q�T\GAj�U���)�t=e�\_Aj7P������J����n��	n����x�<[AjO?<�|zn���)H��������D�J� �)����[/Q�����E��]���)�jH�����k�����#��.�-�ENloG�Z��<��T�@Y� T�/���be��D���Q�$����SP3���8��r��H9�a��3���H�W"W!5Dn���7����"��F���Q���XScM�=���)}�
��#�E�R'���MQ$	/_�+H���'�WP��<I����f��c+��c�X�cy[�3����1DYR�[F�Fj�n���(�Qq�YE"��O���b��VIH��H��������H�U/�_�����te@��UL�1R�����)���+�!B�I6�}WV�16��)H8XN *��m`��CbP��CNh+��:�3��,~8��(:a�M]q^�g�a���|LoIqU�,���m,~8��(:���3V�CD����$6��N��g�+H�(�7,'p�#��k����UD�����#q�����uE�u�-sy�!��1�����uzKb�v��SE���dV��)���L0\_����+�����������S	<o��q7�0 R�2"�x@����	���a�Y�)��mGx*Q��)���
R���������y\��
�U��(��(���$�=���I2
D=[a=���<�Dq�$�(@E���="�x*��(zn�U��1R�����@y�bO�|#5�q���(z~�UL���3�0_��K< �>��FjR�1��BEQ$Z�M^��(�r��3V�}^�)H+��+���8�����#,~��b��m�~��Fj�����;��H�$�2��0R����B/�W|W������>�0<OQ}�j���6Fj�����~���W�W��������(���L%��E#e�"[Y�H�Z��P���V�$VE�	����?q����AJ����v�<[?�9`�A�aR8�~��f]�/p�����0���
�/�E1�y(��g�{�]~�](�	�`���� TEB/,
�Bo�H�&".A���$�+@_�g�(�A�`�.��g@&\��v� ���EH�	DR���Q�������<��S!I��)�`�b�}E���=< :H�OM���`z�CS*�}a`�&	�:d�<#�>������a)��(���=�Nb,'D��(1���H�?���,"�U\O��a�&���G�)��U��+�{B��X�����(P\O#�x@A���L�W�0R�����(��G%�����ga��E��EQ�e�x�����n��P�(�����E�]/_�*�"��p�b�~{	F��h�T\Oc
�cRW�TBEQ�C�8T��q�{M0��c��4��b`��+x@I�Ij���$v��8IY�$��iBx��z�]%��(&��Y$-F���|<?�=��S:�M�HBEQL��<�v��D�2�X��)c�.!��}&�~&XQtl��\�"5��@�%`n�N�	�����|:��E�P%!M�'�At:]�@��:��A�& ����(���I@�o���}x��D�g�Ui�-�H���@���p1�q�������C�	�*�[��������X�  V'��	����qB��+b�S�������|���;�b�s����	?��b�S�u�v��������~�>���������b�S�
<
��O:*X�s�LE��%`�,)�x@�����(���xF0�N0jz��|<���@�zJ<��������x��I����]�1y�B|��xv=%`��������XQt�D����;8|����3:B/�"}GAj���[�����|,
�J�
�59�2.~�]O?t0R�!��;��(0����x$��~&>D��Z?0R�_! V��}�0P�����3 ���c�S&��@?�+�����$`��m�84��:a�3���'������:��b�I����Xh27c< �g����6"hqx���BE
#�|�1v=%`��:L�E1"�N��F��>\��U�m������b�����(��	������O%T�'l��D�1RG�����(�&���X�tl�l�(�]�zJ�/�9�|��(���lY�|�1��r���3���$ .�x*��(�I��`b��&T�=��	F�`�S'!�=���q���|��$��ja2Nj?fI��&�x@1��%��(&IY�k���0�l�%D���(�(@�"5}��bE����<��d6��y*�!����)#���c�x"�XQ$ a]��B �1��\���f]��.�TX1�}�f��Ec<��A.K.x@v=%���H�47���t��I@���x`��K?x���z��^����q<����AM�bc��!x*�_�ib�S�8�H�*�e�gcE��	4�����o���A��Z
M]|����p� ��H��Mz��d6���AG�����T�����T|+�lW���+�l�ac�0���>�������(����C.u�L%��l��+��`���o�`x��g���E7�z(�l\N���`��>�-P��A�E����&Hp27�]����������F�O�OpC+�T��PIfCG���*�?	�����M1R�$�J�E7��6rp27R����U���n�����7! V]ZZ,�EJ2&x?�.
Hq=uc��H�V�����"�&X�����C1q:8������
�/����"��(�1�'������mczK*P�`�S��5F�����\�'�)�!7	p251�LB��&v=%�LA�$���<���>	��}��$����
m�g{8��������"�g8��g+��gG�~K@����I�������3A�-#`��s���s\�(z|O��U��pV(~xt!���&.�M���������9	�_�\'��\�m�@%������c%	�������x@��e����#���xF0��5=
�ie�q���}�\�d����cO
1&^�]OI�S���p�[bG
ad���+�lc2�;8�����Z�����+�)��Ez~��+���l��c���	v=%�BoE��+�^�b�q2/�=��X@# v=��P�����b�(z��?��6NfC��-;�`��]O�+#u����q+�Xxa���8�F�0�j���<���z���[b9���)���}^��d6�B�W��7���x�H�H������E	���b%��;!�X��}�v=�b��^(�;p�����#�#uL�"nV�?�%6v=%>��	^���^�aE����G���l�$�����8��G�C��b"�����*������	���ph(<�����`��b�S�����(�FXb�Qe*J2��m�|�]�9��)_��&�	������B��|�9��J����N�]O}���x�U�c+����x$�U���|���
������w#|���1.�������_�������S��Hl��5Y��,R����\�P+��~.��$��=���'ZQ:`��D����BbE�'�v>l��~���������\��l��V�>+�4M��k�l�@;��
�����O*	F� ��q�(�$�C���4F��U�m�84���]O��b��qy�P�c�-NfCh������������T�:�py�������M#>�(<�H��H����th���������1HF��"i8�+�l�X�oIU��|����
F�8��1�����O��g�b�!	�WHN��8G�O�	F�D)�������$���h�6�a���,@�vxk9ZX0s^�����U���%4�ia �(B\ �bj}��#@m@��2�sB
�@H[�!=c ���4����2P�'���������'B��@m@���aB�@���l���
BI��P�a;��� ��%2bY��P
d����<eVBi��XNf�6O,�3J�T������Jv*H�hL���EA-��
R�X3d �)	��Qb=����WJK2j�'���
�u�������@�]l�  ��0P�2l	l�r�@e?=l-
l�u���B�@o=l# ��P?<�|*��@�cD@lql%������>J@lY%�Fo}l�% �[�	�m����!��-��h�3��yb�>�
P�}�������������rj��-I��"(���wC��lY����o����!�=�P�o�����o	��y*�����6����}�����q�t�������)d\1=P��b|/N@|��	��' ���|j����{+p����n)Xpq���S�)@�	B@EQtl�}�.��t��K�����T���9��=�]�M8Jj���+<�>{M�[��G@EQt�0b�2b����D)�^p��>}�yG@EQtWzb?Ab���+@��H@
��I�%��
��&��''@R�}�}M	��TY<S�Z��8J������D���+{`�����p������;�V<�	�(�N�������`�nb_pj��`�������EQ��' ��8�nV	-$�r>]�G"�kJ����[���*��T���)H�z8�$������YH��q0��xHpJL0\%j��8�G�{q�����x*�k
8&/_�c�X�U�,��[��D~n�(�n���W�����-�1��#�"��8� u��n�(��=H@wH@�xJ�:*�
���>=Gf\�QDX.���D�\]E�����H[\��������#s���R�\�+P��L@�L@9p^
<�Gk���TR�1�S�������G�^�(������c�������3V�}^������^�(���M��8C��8���o+�[���&����+�KTLz���jg�  ��A�b��=��MO�SE������<)T��~�s�ZV��}10��d_S�g�! �{�����p�������8�qf *�"I�
j&�����H���`�r���q8�@q�'��(Jv)������p��r�G�&���0�23���)�"Q�e�v>%CY�����@�O!��F@EQB��-"�����>�p�:���oB����Je� HE1Hp6?����:TR�s�l,����P��|���v�V�8;#y(�pFH�\�T��B��$�"~�V��d�d��H8�g��C�2�b��GJ@��4��6��JO@E_	������~&��f���h
�����98.�3ry�>������8_0'���G�)����P�'D>��p�I<�@q=�B����8s4E��h�f5�P�����	���F	��M@E� =�	Z�q��)@g<����>������	�3����(��Rm0�5�m�d�8��i�!5{ �����H,A"��<�T��p-*��q��'p�EL�8�(�q�*�+�R����6�A	#u�T�OQW� A@a���+�I�k�Pq=M\���c�����(�X��&1�C@E@Kb��4Ip���m%�qh+�1B�����}B����)k�C���SR��>}\-���@���mqm��V�B��������bH��!���>c\q��?���j�2������'1l�(��<7��WP#%�^�$m�4�_
�����	��H@\���Z�F@\�k0)RR�����
bE1t��i���a���`���U���\%N�i�?C�����d���,r�L< �Jf�U21P���Jjr����(��!W�����������K
=��*��H���J@|�z.�(K@\���.v=
��H��VcE1��j���`����&n6�x!��S�q=c�7�d�SJc��R�%�m��J���Hft�`b��
R�.���������������W�����R5����x�G�O@�(��KO%��H�����!'n��X�e�U(��C��S���}(�e���$�+|3��/u��BE�B�(�$oC�+�<F![rp��CS(�r��=Fj�0_!��3t�}(��OF�2�P���4}���gPQ9)^���L���H9�a�]O9�'F�P��F��(F\=��������\��F��)#�����M�j��
'z��*y�	a��P��(����AF�H�"��(����6V�'�$�(�*��	�]OI���"��#��(�q�l��������6���i+A|E��C�(�b�V2��M��Q�=!&&��bR�x�ObH��%��H*8&�I��������'��&��:b�x@1��
�+��M?��HKf�&�����]-_d{Dj�����kZ@�%���g���D��Y�>D@�zJ|��HM@�O�+�����$ NfC
f���Il�cI�����&�����cE���B�$rB����X
���91v=%`���Q*����EFP�$e'��H��|������i����|�I:mh2  66D����b�S��|���_q8 ���	�d7����1?"�������)c��^��iK��H@l�cG��^�A���9��$ v=eG(���T��;XQ�$S3*�l"_��M�"��fo��>��B`��y#_QI2Q��$�����������$/:������Q�aE��
�
�d6\�� ��&Qb������bE�b�(FA��O��V�1&o��?�J��(�l�����'Dli�
y(T��DabvE�=ft���fc��N��a�"�a Nf��F���<��	M��#�V%���u��"%�
����%�_�ko�-��)p@����D��������������������4��#u����XQ�����D	����DII{�]O	c�&���'�)�!�d6Q�\��|������7a�N�,�m�(?����V���Z����p�CL�?4�6Q������E:��~�:D��fGF��+����e�D��qq���v�b{|�%�a���	q<R�N7p�8m
P�����rB���~��NfC�XiV�/�,hx*���Ep�_3���x@J���U���7F7�����0jzRsV8���"[���<��&�|���B�	�(�g����TF��(P���c_If����8!f��z�Z����q>������r�E�~��+����2t���)G@a���D0�(������&��g�l�4�	E;.�����P��1VI��"l�8�
��-;b��]Oc>�x@>���3��`\L#uc5(�����4�l,��\\6�)��UL��8�
����)(��tL� Fj�f0�$A�'K���XIf���<��a����F�X��9O���t���'��3�`�#Q���R���'..�'V9���DIf'�
��'E@�zJZ���t����p�gb;�?�Tp��&�=l&��#������}��1[�aE(��[If�8v����+8 Gq=M8�����	������_�pLx�
��I��i�����Jy��u�������$�I\�Y	�!��K���r
[���� O��C�xJ|Y�98�M��	�}z���x>v=%�� ����aE1!����$�!�_��f��"OB�	FjVfp�������LHR�H������1�Kbv�H�L:�<gh�7sH(�'Z2�$��g�|�	--TI�pb����c$XQLB�g'���������PqhJ�cN%��	��1_	#�(&���,T��$��?�_]�z�D��[���XQLHJ���p2R�y(Jp�x"���4!�#u����8`E1�}�#��d6�
c�6!M�[��4aA(�qy�$q���p�
����C$�c}%I\�!��p��1R'JybdPQ�:�NK	������wv� ?{5Mn��d���o�������h0������c���O���y����AX��K�7��L;�x��������f��{k4�Z�,�g��w�����&�l4�X�I�_f��.�V���gm��P�/����^v6��F=�
�}������F�'O�iQ>�������"�Vo����?��Y�������
�����lp���m������-/3��P��s�����u���������r�R�o���j�63Gt|NO����x�Nn�I�=k�����{�rz�l���v�N�w��Z��;����w���8���g9�����������������u������������m���ijuo�Yj�l8���98�;FY�{t����%-���Z'�����a:��������V��+��(����0���<���}'�*�����'�����_�G��X���������{���Q6���VM����l�3�\�0����Gp~nmm]���>�'��LC�dy��:���a6<�{�YF����K��Q?�`E��%��������~�q� ��R[��wilss�n���Y[A'�6��c[��_����H4����!�U7�����G��7�Gy!JYS�f#y������������z�������/��a�w�
���.����Ij�$�����d=jv��~FR�����{�}����g�H��cf
��h ������?��OikH:�������&��Icci�i�2	>�$kR{�j<���"'}�|0����z�,�aJ�=:��E+�{{=&��[n�����'�9���������k��$�L��������������	�A]����-��e�6�#���-���m��>�mz��m�����4oB��q?+1�`�]�lo�Y���a��|L���I����
��mm:������'����e����/��%�z/�F��������������!���f�B��"lbDK�Y������O���7��?��>Q'������K9�j��<�V`���;�U���n�H9��F�~��5�
7h%{N#N�^��!M����l�ZH5�}Y73�N���F��Oo�/�|,!_L������c��)� �;�,��RRj��>�i�G���Z�[��e6$�uN��P+~\L���:�{u���g=}�{�thM�A��e��xB���������������vN�i6;�/^�a^_�9�9Y����
=�������=���xF[CG�o]���}D��;|&��\5�^�,:p"�\d�l��)�X?��HA����Y����ST�OV}������5h_^�|I�g������������}:�������lw���T��OZ<k��"Q�:|}p`�rkML$t���]�������	��1��|��)��?��f��.��N�,������SK���}���������%}���E
�Em�/��c��4�.��y���5������H|��c�������?�'�����Q6�@��|�EE�1��tE�\���9eDg���"6�����F�=�A������(y�����$�{]���>���q�r1=���k�,��/�����u(����X��{:+�3�xk�.[n�x0��f��X$��o��O��-"���������S���}4���u�N�����y��k��~������o���7������4�
f5��������.�����m��O�u��'�"Xy����/��y�������y����Y;�`	d�if�l��X��8�qC���w�=(<z�5;��� ����8�oX��{��.�+\�9�~({��y���O�V�����������f����p��������=}�g���6�K�&��k��n��)����l;�������k��{�c�S��}�z[�U�5>��^f[��!�[Dr���M6��Ni��q��-N������_��O\���q<��[���z����e���O��)��P�[fr�L����O��|2����n�f\<r������ t�'<�EI�O��z9Y'���D�m?���\�`��yb���c����Y�6��3����M7�L��v:�n���'��,c����]X�y��O���Y�65�z2������_����`9�c���%hY�	a��EI�����{����c89��xD|�}��k��R�8w�B>��p�c�������r<a���U�[/g��xU<��5�`���^���_�o3�9n�����Z�������}BK��	?��m�zq��>i�imgru1�gy��?���n������!��c�n��t��W��|���L2qkf���D�����>ms?��_F�IT��������7���x������t@��$k.�r���������p��ju����}���T{��	w2���O`'<ms�$T������)
�'u=����5��lksq\|.h7D\�N'$���V��&�D2�e:��[=/����^���
�St�������Zj>5������|#��,�5�n��r0�]
Z�(���	�^�%��^?8������4��y�z�����������;�}RD�Z�����W�b�����lH�ek��q7�Z���������0�<�������/��������k�Lv��N��*��4;y�/�[+++e�	oI���wCS#��������/<��,in��4���z{�N7�z�.4����*�c�S�����h���T��p����������8���K��W���w>����g{�����ww8�����i���8���|�5(����&8|86?�pP_G���j������o�3rDC��o��^���s��GD�X������M�k�(��5�J�Sz}M���4��g��rX�aj�S���P��th����dG�IP^/=����2��4�2��l����M2�I'�D���u�������j<���gj����
�tj�][�^o�Oo��z2�����/w{�z�O9�l�r�	Xb+k-P&_*�&����@X����P��z���j��_W�����t0��a�Rk��w�d���b�,EB��,��i�g���������x�N�vy��tr�����t����^��=;� V�}���
�a ����?��y�9�jMD��0!��&9�z���x����<	1������A��y+��xh5���M2f��$���a��Y:�]�n��f����2��l��������iH�����0�G�����J��=���oL��%����u6���Q��Xy�]�'7�������3��V��I]���b�����+�����;t6�MZCV��s�_3K�R�����)���
����)�qv������+�e�}����Z��Y_��X�L!d�hy���������z�#��F,�D�k��M�y��bx�����}n��s��x��`�<K��c��m����4>Z<`��4L�Nf�$���k�A����L{������u }4�/������'�g���:
���h�E6�5��!q�������~����t+;����_{������Z�~d�V�y���X��+�S�-O����&������g��/��z8���~s0� �z5��s6_���v����~����(����\�7�q�/�
?E(<����oYXa�=�d�Vj��u,�<�����I�A9V{F7�'�,�p[���EAfZ�o]���|;�oX�����-�����1o��������#M����o���zz#��K���W��/��Dh��\0b�����7G/������w!�?���^
����tL��Grj��xb�G[5X:���+#����%?W3b�|9H������4u5���*n�Zi��� =�E�����n��a�����^?�M}5��!��_�B���S��d�����lD�6tR�LC�������m$�������zf�N�1���]������#�u?�?=;��#a�����:��e��Sn4Y��92t����9�������P�Gg4]_7,s���lE�v�������s�|Fg��"\���%�3�J��F���^������@�)������50t�xF4p2�Y���-��#�DU� m���?�������"��bz�j��E�u���R�&u�	_I-��!fS���s��R�oE��j����gZ$w��A�z�=�.�Wvm9,|g=�M��S��sI�pYR��lD;|}y����2i?L�	i��1���mv
�rgr�RN!q4�
r���������W������1�����'����#W[���e(p���x�,��u;k��5Z���p�IqR���g`>o�Zv��&��{���jx�&n��A!d���y0��D6�s�����/�C�O�D[NN��bF���������M��"j��D.�+��x��p�_fE����:�B���VJU�c�(j��������N��Q�$-��N,�W\G��������d �%%^����X7)���G��a�3Y�s�E|�FSa�s��JC��������uN��������_g!�zc�U�B#�C��>;�]YP�i;��tLwm~6��.3�2w�dHLBr~[$��RXPR�:wVZ�L[��
�X�����V)�F�n�D��AU]>��������i�{�G'{���S����hA]b�/�����A��5������
#�	G�Y��:m�y \��,��O�X��z%�<j�B+���}p-���S��������0�$��o���=^Yam�1�}zP����o��X�?{�s��].�����VJE���"�|"�b[�3%`������
�Z����/������4���SR��"���9��%��\c��4����W��W������6P���\�Y,���q���~�;�t���j��|t�@��[����^���l������^l�w6$�`�'��_Z�3zX��/�z�{�	��2�����V��[�0m0����e�������{�,���w�t{������������������W�N�������T�������B��B\��O�N����tp��7WJ_��A���gZ�+�V�yq�~:��~w���0����w�l��vI�;�F�So���\h/����y���O������uGnFO5V�)\�������V�MsI:���}�/k_�Y#BL=�t�8k�m�.	a��y!��K ���>?�I&���
�)KX{W����T��K�s�+���'kwe���W���\�������u���o�B\�Z�Q���h��u�v����}/���w�uo%�m�����B[�-�p�Z:���S�epC�r�M��[�������4`>-�p�T'���_-v.�(��q�|{��z��%����M.o����cl626;i����;D
^M��2F__������%�e������x���!i�O��{�{�����9'.�E~�9lT��7Y�9�#���	�������th�X�b���|�X^;�����(������4���&��t�����&�gj��w
��B'I�8	{��	����oA��F��	*����G�i[�/��.I�?c-�3-�.?���%�N�L6|���-T�5��=��lY��O��uL.��~�7�:�!�me�.����
��Gkm�f=!����V���(�������S1��0��X���U����l��t�����O;��x��q��G[&��|P����b�B���������#'#��D��� ��v��(lg	AZ��eY7��q%���������.!`���q]�����^H��[�����:�9�X'7����v=g{������A���u��#���3�������������F�n�9)�C�}��^�B�8�.�-�""��@����8B�p��*q��ltAp����eyt������Mz����s$��r~S�a�.��Q
����D�d�OB���V���=�n�^	�.q�����6�La���^K�	�D��;�<~���M����b+�������J�sQ�h2 D��z8�?dgx����Z�KA��sd�����������rJ�����^���:d��-!���������B�{I��^d4�Bp��/�1��a���d����ru_k&u1M��_�����v�Y��������Nun:��0�������b:l2<��i1�Z�s9���|v��Re��K�I���q�\$j=/����2���a���x���������q�1x$_�s���h�_Q[X0c���fWM�v�^�X@�m����"����r<�:��aI�=���<�p�h������������zt��S���8���Xv�a���;�T��p��>�b����mkw����|k��{�3��/�����V#���{��WC�'�YM�=���eGuY�J�mjq�F�*'���9�s��������1���,��o���ri	��{x�s�s .��#����)��W}:S2���FZ�[��7
��f�I�]7���"�������������-#=�qlH����!u�^�W�z|g=	�j0�J?����Y>��J'7g91�G~��xH�hh>��3���p��S4�N.��@�LC4�����c�'M/'����z���;�k�5���I-���'_�����	w��M���,�ay6�~0��fP�����ZX��*@c)��Q�pO�����/�WX�-^�o���EV�9A�u�|Y�6�����������F���N�[FW{^]��et�+|���2����q��m����5*���W�q����"\��$1����!CmaXH����T���n�,KoL��;NA�����mW��i���Q��M
O���JA���n�~�8��a�<������{[S-���5&������m�,�8�Hd`�z�]<�@�6q���/����1��"D���W���3e_�f#+4�������R�wk���*�cd����7�*���S���9����	�5����
C�]�������/������:Q����	���y��N��'A������u���B;�L�_\��BhiUhi5��*�Yy�h�j��n~C�����D	�
0oLG��|7����zz<���/������7O��1O��!M��g��7�9���u9�^�O?6�Y������^v}��������������?���.x{>��������,|����{����7��9?��_��U:n_���ShT�y&)����1�@����L���M������5�~���v�����N��L2 �L������I��?,��$6�����!�	��!v�"$��!������>����zkw���~:�s�H�z'
�+6�[��\��}?u�i���O��<5�/�n��e&�D|�UM\����)%t9k�6A�-��8|DFd]etF����V=I8~�#���Y�n:eP��D����,r��@����N-�<�����p�Y��n2�g��D*�"���Z�cq�'K��TR��1W'��y0������r<eO�"O���6J���1�����DEL�_���	���R�4>�O�s��k�0��9����=�Q�s��M����_D�M?��w���m�f[�����N-0�-��"V��Va �#O_
h$�5�\|�|�����D����l�������oIU�*�HVh$;�0>���5
�vG�h�T�����f�D]���$�]��&-7-UHC#$J(	?}�h������(��
J�-�$e'�AVZS�)I��H4��h��w�h2e��!�&��Dp	���a��Yj���=
�f��s	;�����g�-���]�(X�xX��*�w���
��{���q��)\I��x�Y���������)�'D�����`��|t�*��I�Q3����g�&���:M<�&�G&R�V���.Gg�����9�3�u�

�r���q��3^|����9�Rf����y�R��^-z���Kb�=���8+�x��B�F��68qHp�:+��2�NA��W����>B�~M�r��hy���'�\�J�3L����{�����s�VF���$�hN�?]���&�6���L'�$���}��X�����?�j�%!�o����2��[�5�.���;S	�5k(8��JolN��]�A�F���Qs�f��0�F�M���)�A�--
���bA��N�Wv��Sn�q������/>�Y}O�_:En(Z�'<�oL��y�u�� ��2��U���z����ij�Qfr�����y^�����b����H5�L�����H�^3�ILu� U���`��05�z%^��G���oR�JV�>���Bl�8�rP�(B� ���Hd �[�;��9M��	N�C$u(]����s55�x�$��
1���#h������i�N��8<�r�(��c�#AT}0Q�0v���HtY��)�9�����&��
-7C��}c��v1�b��L�1M���RIH
�s{�S/�\����/���Fdj��l��x��I��b#i�>��
����O�{�w����B,�0����e(w>�bR�����3bW��`y[���H�2��T���.NHr0��%g1/�P7�q�i.\���s��5��YTd����pL�ZtH�B�f4����-J:T�r��'�2���){�Hw����R)���N&$_3K\���
&6���1�����9�FAS����t<��������kC�5H�'.W�l�Ab��d���%�/h���@dTZ>��E��2G(���{�`d����3k�9�~C${�nbh���:fO��A8a�$Mxr�X� *]p^�9m�L��&��m�To�2�N��Rui�j�F��������
�1�R��5�(7"�!����gzpr����7�oC���4%��IS�Z����K6�lX�V9�EP���^[���{{5�[�U��4���U;/����N�t��������b�W�L�'��F[��0�U0/+1�$������h�$�~s\MB)'���02�<�7����%K����
q�K^"�f�K��XN>����I�+��,4V����6W���,��+U�F����@d��r���
Y�`�����V��j�e��jY�$��H�������Zm��p��c�l��!�63
.�-M�����U�o��
�l�J6�-|�{�zZIe���YE_��]�\m.9�k�Q�a66��i��&���?/�G����\$��N�tX�!�����9�A�/�X���(
LU��X��~�v4	��>�����#��o����6��s�+p�pl�HJu�lF[�����$�Z����)�%T(����u~������ )E�sjW|�E����m�����h��gi]:�	e+���/F!�
�M�x��q��a�(���u	%�����������Iy��T�����J�h)�:�A�����rSZy�0��Y1u����M@�&���jrK�oL��i��MH�g�X�ee���2j}$��Z.��
u,0�1�������Sr�:0���Q1�R���m��Z��)>��E���>�M��& ���2z(������[3Bx��#�d���&��������O�l��Y�?�~|]c��$E��p��^���pZ�G����p���Or�J[��f	�88$�?���t�]e&y�%��@�b���8y�}�P���?
���]TW
/��� ~l���!-��T��Z��VuC
	e`�w
�x���S�.��2�����������2��5I�ak������z��?��,dM4�p����sD�6�3���w&8�C�3U1I�����>�d���56���81���-+~��4������0.�:�����~����m��H��D~+z0q���I��.+�-�m�@��!=��9n����������������{rQ��CVU������\.� �
�k^��S��$e�wq�I�aDV9��U�"�w��0�V�w�i 1n,�{v��7�X]���.q�X�����I�*�~�P����K��Z�bc_���Hn���Te�P�\�<�v�}�+���A%)2�Y�T9�G���&#c����M������6,��c�;�����"l���)���- ���Q���4a��^���u8��U���Oh]�9L\��1-
����]bw�M�#"Yce�Gf����_0��@h�{	�h��8�vd������+6�^��l�0�J������w���W�P�����:������0w��)L�4���s�%��uV�QX�{&���Y���gDD��w�g������<�*��
�M&�RQG��������`:7�5���y):&�_������>���j�V���@�/.\�nwcy��^an��{q����C���3V�L7�<���f�x�$*�H��$9V����Y\�v����D��V���z,�c��*���(g���*c--�X������0��;Y���|IQh�����g�]rE��J�9�)�������4�^���#L���|�
�ie�2�d�ZS��8~����s������rv�L3
�����MY,�F��]gO���O�����������E��kg��.H�&o6�j��F�A�*[�~�fP(�+�,"��������	��E�*G���������\�1��1B���a���`A�����14�Z��a�)��dt2�t������Y�����l���Vc��R�������(�#u�(7�� p����g����1(�o��QA�-�u�����aZvzi��]1�w���YXx
wl�1OXT�t������L��q�6����`J+~)����8Z�%�RF`C��0�%���lB�EgJW���VG�3����
����������X�@mb���CZ�R'uF��@��m�����	�I���A��G3^��1�e�t������'c	��hErZ����):7����I4�[[[�(c�$b�����luX-�>�Q���m*�Z@e�����IP�@A��b{�B�Lj�@f�E!��K+>Fm��2�O��P�&���zcP e%�`�n��%��:+��]s��������IinSfvN�x��y�,����=�����0Z<���V����V�%2���[Z��WVV�v�;�������,�N������CP����6��;G��,-I�O1FT1�j�]aC@����1qsZf&��:p~6B���g�/l�K��X2t��b��`8|*�am���B�����k��8'��`�*]^:A�����2�z���I���-��o�QX��|EJGVv�De �$������E}��F#�����/���y��b�T��ib�5���W�b��H!�7����� A�M�g�g��K���~���Z�H�<��.�>`�H>g��Zh�]�\=h��A}q��zM8�1Y>�d,� �h����"��lk/�D�_f�P�M�'�K����Nz���p�!�M,|�
~�P��O���������A"[.1�Mz����$%$�'�5�������B�/X��@�u���|la5Y����{��qU+q����?��4����>��s	&�EGC�E�Ww+����)��^����)���|8��d�?��N�'�iv���i��������E:��'�/GQ������! P~Z�[�S\�����H\7i�h�>F�e|�H>1� ���2��<i+9�������=5�=z���Zi�OTe������S�'�eGB�������A�hv�g�������)��>	c��+2�rka�w�3!>�l{�LGY8��WX�SD���������������,��c�%2�����K}4��F���d����av��d\�q\����k/3�@�,���1`<�7��-1O�w�������}2r��$:����DNW��%�������z�R:��^JB���e��f��L��s������I>�v�r9QE|&2O�a6�v5�\�J��;G����o�>������������k�i�m�O�`^����S�B���$���5B����{�����������6UC�+�}�������� ?D���*�)�)g����qa�B>3�������P|��5�L������dC�2
P�E��X���h����I��20����5'>��+�kc�a�.xN�����(��-�^Z�G(��-v9�b\d�����2KL����Q��y��q�����b�a�8�2�����A����gV$!��Y���~
[Yi^qHBm�qjn��^1(m,�����>���D�w+�>+���.{�C���g�j�f[�������}����>F�6GV��Q<�����c�clA >��&�����\P��9�;?����FA�J���u�b��5�k��������y@������j���2��n��L��yX�z�E��(h���Jv��Z2�h�s�M�
h6��+�g!����;�L����r��fr8P�e�I�Op��(�N��f�nd*$_I"�,�4��q���4k��oC�k]8����I�F���1��v��8��p��w��w�����o�{U���j���4��Y�^+�+����U;;=J�_�g=��fg�g'/�������L��V��Q���2D�m�I�g�$9��?�����N���������������5�(-w_F	E2p���F���C"7W�2�C�z�l���[?��������} ����9�������~p�Ijx�iE������=I{��i��E���[V�����B��������D��c!]x�K������[�1�^�~{�#������c�� ���L"��]��S������}-������z
�B:[I�h>����62jC:���������nu����9�/-�rx|t�h9���I��%y���io��UZe����|^)��){��T��%
R�QIM�)nKd?;�k����foG\�������m���%RZ�����o{����lt���|N�������~o��G�����DO-�������sX��2����a�Bcf]w���n�����md�q
/���^��dA:_C����s��B�e�H�����P�8���"��������������fH��q��,b��]�������m9w�5��+���jS��	�`=�r(�%�ex��gUWX����������P�_Q��{7 {�������j�d?��~ �(�����9���a��)�����U�5����w���N����"@Wz�&�<���i����-?���O��S�c5��zgX#���4�#~ ���>Zr���	0�����!�M��OR�z��}x8��	�}�����m��L�-�Z��r��a|oq�l��<)�A�*���Z-xv{��)���6	>{1�H�E������1D��G�����I�w�_VF.*���
H������|�"9�=>{���3�/����;wv#���O�C_d���r���ZgW�Ip.����lL�R��������=F�y��*CZ�4N���mcb����>`$�����%��6I���Q�������(�H��}�`���_�d�Lf��0���P��7)��������GBn�����e���/��*L���	����6��� pVr~�����7Bv�%�����1��6����wDP
�&@Y����e�>;�T��R��wqS
�@57��Y��i���k��,7�Q�n�>������h	�T|��k�D�������>S�xA<�G�������{�jG�q-6���p*�����{�/n$[<`����}�CT�2;�C�����������;�)T.���Z{
�s^�9
�������I:�jm���"�BL��&VXK���=�m��G�����c�P�9Fv���t�&as/-%����b�E�YX�"�"���nQ��m|,%k�����D����$oS��@�I������n���q�����ZSmM�[GG$Y-�'�#zG�!:�Y�8�]w��������$���Q������������CV�r�St���]r�bz�Nk��t|l��m��07��$ZYiU1��.�[���:���>I#
^��`�M2���7o�_�
���l�Z
un��J�����F�~�{�?M�N����Wj�Z����z����b�������*��_���L�n+���O�}�-�_�����f8����.>��1�}�kC��]���`����~��������N���9�d�Z\~���8f������$�)ixN/5=�u������
����������=�yY��U4���t�;VJ89��'g�����',���q����:����G���(�����0g ;�UY�=�z���I������$���+Q�~T��}��������8�U�N�bxa�?�
�T�TLg����`���\��������g/p_}������r���=����������x)����A�3�2��f���W�Hz�z�vHtMFS�D��{����_D���Wy�5��o&\��A&B<N�Q�lZB�����H�A@>�R�;���E�y�Z�~�i,/���Q*�9�7��-�������u:�K���u[��������%xn�:�D>O�����Q��� ����L)	u��y��I��"!��MI�����d�G�|����y6]�5�@�=��c9����aF2��X]|M��P���Bu�r��*�&���\!�"_�������z+�E3f�XV���}�:�a��� t��!��3y���3Z@<�$�v�(g.��2�W�j� d��*�Y����b�\��P���C9����r�n+{��8�)��c��5wo�{z����H�5�����M�K�u{ �=Hp}W*��!���+�-����YA_�����2��>���c�_�3Z��
KE�mn+�*���
�<�����h��|7���0=��t��"��XDS{���(v��R�hL����EQ��$1$���xn�@�?��>��\&�g��v��NB		��H�		={F�z7y��'q�1����^ \f�F���������#���K{:�`��o��a{�a_��)�/�(��R���	3�W��������ao29<���t|�?�����m���f#'�C�w��~!V7��>����D������~+����^Or������Ei��V|��lQV�p�5Kp��O���4�w��x��/��7�,j��`�uI�q��,Bx)�Wly?�r�4�h�x|�R��8L��|��E��Hw�y�|V�l� �0p�m�M��:����RR\�qSh>�����4~�1��w�u2|����������'};+_���.
�^`�+�EA���Vc��N8��U���bwd7F`7<u�8��������[�����Em(F�a��(X\���p�H���As������2<���NG��1��&F�V
����
����\�c	?�Y,*]N�%�����Ro��wk����^%��aU�������
��B6�0���=��������V+5B$���_����.4����
Eai��
��1(�}��T�g�m�r'���#2�]�0�|�I�/3�1F�J\5�)��@����T^�"hR���R��\d�����=���
�KB�t��0�KR�z_G�
5m�Y0��r5�����UZ1���Cu��PZZ�Kk�)�.�h9�2�zt����Jx�����zh�!:l����<������@<@N�C��)���,�rT�
(�R����c�g+]]f��f�hE��U'-�	>�+�6�:�0+���t�%�8~����Bo}���0��E��+�)+������u�2����aZ�N�pW�����@��%0w����qt�R��=����+��r�A�5���T?�w���T��d��_��n$����P�����tj���Y�9�a�� (>dUR����4��&��X���5\��/@������h`�R��g�{��:���{F6(������j�Z5��1�.�8�����*9|�x������Y�d�*������4�Z3������Ls���+(��!9���bE�����-[Y(P���;r�q�0�[K��JV�G��prd��{Vb�6�}gQ%�vf��\E��p�r^Xs����D�������O��������y�<}�>O��������y�<}�>�_>�Fl8[�
#336Antonin Houska
ah@cybertec.at
In reply to: Antonin Houska (#335)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

The cfbot complained that the patch series no longer applies, so I've rebased
it and also tried to make sure that the other flags become green.

One particular problem was that pg_upgrade complained that "live undo data"
remains in the old cluster. I found out that the temporary undo log causes the
problem, so I've adjusted the query in check_for_undo_data() accordingly until
the problem gets fixed properly.

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1]https://github.com/EnterpriseDB/zheap/tree/master, which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation. However zundo.c in
[1]: https://github.com/EnterpriseDB/zheap/tree/master
execution, so we might have a problem.

Or do I miss something? UndoDiscard() in zheap seems to ignore temporary
undo:

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

2. Do not load the temporary undo into local buffers. If it's always in the
shared buffers, we should never see incomplete data when trying to discard
undo. In this case, persistence levels UNDOPERSISTENCE_UNLOGGED and
UNDOPERSISTENCE_TEMP could be merged into a single level.

3. Implement the discarding in another way, but I don't have new idea right
now.

Suggestions are welcome.

[1]: https://github.com/EnterpriseDB/zheap/tree/master

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210909.tgzapplication/gzipDownload
��:a�}�{�F���*�=�7eR��v�Z�mmt�('������1IpP�&���_�@�$);�f�H}VWWWU��^��a��4����_~��F����4z?F�M���0���m�^�-������>�I�[���P��/kG�#��/�o�X\���m����}z�|�V���\��o���������Z�BZ��z������h6:�v3^�fK4�M����u�����������{��[��=�,�2�N[v�?�-x��[Vo��u�h����cg%��h4^��	������e�-������O�������8u����f�f������n]jO�����FOT�������?]
o�?582Z?�J�X)BO(���K�����'��#����[b��b�<�O���7��Fi���0#ho57��7��}gn�����u1>-L�v�p��,l9���P�6<tg�b:��O�>�&�s����"w�,>[�J<�W�w�������?�j��xF����ba�$n�����k"4?9"��gC���(f����>��X�����
"��D�B�X�����_aOS����{s����� p�s��`�g8�~&,o�^q�����g��t>��2`B�c�������e��S������0�ha����`�X���DB�Pn)��#�u�K��W`�����.VsW�W���sh�_/q��/��%�����b0%�&��<��[��8_/}o��
�u�Sz)��p�<:zxx���[�	�6�{����	��9t����������w�{'��b������?�~o���?������������'?xP�����c��0�y���Z�b���p��}wz8�-���`�����U���j���tt�������j�z=]%�����7�0yY��r�a��i����:�Z-e�g�Jy���K���`Y���=����C�o_��u����b��.�QF����J����>2-�����w��g�y��&�'vN�[��g�p�Vq� �?��;���m����uy)s����������n�����c�H�sE�E.��P+����h���U#ekA�|��wC�k��u9
}���9�����^jC��a\-���k�h��#MA6�a\s��}s����.�^/5Z���x3W�V��Hv�8l�\q�� ����~�u-�4��Qz~�U�V���/I�_�]ka9� ������cG&8O�l����u��9���x>�)����;��G������&"���gd��{r�����7�'�'�&��[�,���rG;w9s�v�Z�P�P8����z�2���;	"���=�s{�Xi_s�3�a�*����(\�v�]�*����r[ek"D���xS�ka�^<GUO����i�*�vsNE\�LwiDK�
j�C��Y����W�����B����c�D�;���V���������O��a�z{�6	�[qBIVl�v��><�u��?*�/����[�����v
�0�z���3��Y�!G��kI���^e=��O���
j���s�v��*|�\K�M�X8{W�hc'<�s@8[/N�$�UNh�~A����|r>�^
��&o����<x5q����Z}��(�V���8�8���������y~C5hgoO�
,�/^����_;�����D"|Zy^������_"%�?�����vPb�����������);	��^�cP��1�TB��9�[����d�=��t����OE-%���bWq�[ f]����x-����!<����������J&K��o���������$���~UV�o{�BT~���H��e�T���z���lt2�(��$�C H U{>�+P@�
����.�.���a�.�g�m��h���[PAbW�q:v���L�����N��^��
��.E��JD�jp��?�i�����T�!��h��K���X���u`.,����P���Y��<_A���MT�zf)�]S_��7�x���8��'>�x�~�B�g�5�{��)��u���������}X����lx��#!q,����������^��#����<�R	�p�,]��1�Q�W���t�CZS5�f��F�7���B���<y��re��
�s:�s��48�qHO�����xV��G����?������::h�I�HC?��`Il����`1��c��5���`S+w��=7��'j�6��j�xY�w�i��Jf�O<6���EO��7�x0Q	{�@��>SX�{���8I5�~����}��H�@�w/�o�-)-����g����H�4�����80������r�L��d�T�������'��������G�w[��@��A��i%�Iu��tb2<�]k����������xu�[��R|�����T�����qK���r��V��0��	���o�Ng6�N�p�D����Q>c:�*�-T��2uA���7�gs�.�~ ��f��vf�zN�kc%��o���/���#���������p�����S���CY�%*Rp��&��	+�}���*/W�����`fMa�f��v�]�	+���
Fuh;53�/��4���d���?�WX\=`�'o?�{7�>M��O�O/&p����7��R������MEU1���������hr>|�X�T��ZWT�/	z�B����+� ���_�`�k��=>�P�[�A�j���#�]�PdO;��������5�Ak���%0Ju�L����(�����X�����8H �������y�X�cO���o�f$��EO���<V��5Q��Ag��0j����3)��
�M���B �A��.����L��3<�w�/o
�j<�fbT�����*�04���-������9-|����+���:��_kd���'�yl��D1��6<�����<�^^���`���d�wY?Q7�����/6K�������5��/[N0��$Q�~]r�`�]G��<����"�����F~��P��7����o�Uyv�2�GF���:1�N�L���3s�#
O�I��Z�^�]�>�>����$G�����?�?�#���4J��!�������mTsZ�"�k��`��m=>��t15Y�����B���5�6�z��J�G�o^e{�u�{i�kM`���n�9�q5P�~��,�W��n��^�����r����{�� ���k0j�����#�e���|@ZZ�
�������~�v5z8H��7���?�!���O��$��SP����J��J4���.C�[X�q�@����0�W�X���%F��h�n@�L<�f��Z�6�0/9���'&���?��V��:9���&'���?��D�n;j������I�XN�����R�0;"��o=o^��<����>i"���Mo�F��m��v�H��h��
$G�N_N��,T�<MN��#<R�NNLkp��iS6�?jo����}'\�d�(�h�#P��M�i�M%�:���m�3�[�z��Z�v���6����+06��K�2%��`���M�7�9�;��WO}�~�W�'���wTR���y�L��L��M���7�b���NHAf�������-��@�������L|�z�7RQ�������kR��UC�^��s	�8=����;��������a=�E������k�'�[�v����C�1|���9�t�Rg[�2Z
�����f������H��6�1�nS��8��W�������w��jB:S����w{M�/cb�_0Y8�m�y�`�W>�'�@�.�pu�I�Y����>���R����}
U=��������b�ODz���S]�
4��������P�J���qe���x)����h���.�"�Bd`9m�k�����w����mD�dS���,�h0����?�������S�La������%ug�9�15`iP���]8�Zz5AF�/|�$(����*C�>
�E�m,*��@�p$�����8v��T��AMx�Y��5>^�����N/����R��PG��$��v��`��#�Bt��D\uOrG�X�����p�C|S�x�&&�e�����_�VZ����2����{f���?]7\~g�!�t����#�O*!�.���&��R���~��S6��,%�zj�v�o5�z�����}�tz[`u���N�D�n����}�� �����JV���o�Q���4�VRT��
504=�^��R������&i*ER1WM���P�\8��0<��B���IU�0l��1j�x�{�6URk��vY�U8O4�A��J���������&
)C�v�mM��Q�7��e5{��`{��LR�I[5h�)}H�C�z����5U%+����%�B(9�@B|^���cJT^K�yu�%���4�{�<����k�?���xi��;�Sd����N�����b�b=z}b=�C�4��n�B*���O��$�`3�\Q����F���L�����xl���k�p�H���R�&E8�B#�x"�d�	x-�=S0ce��I6S�������sZ�1�����������5S� @�Y��%�h���Ik�6��[O�~N�R���v�3��60�F���vfN����dZ+�#���������%�	r��k-C�B���9��V�(�a��S �@�Z�T���K�B���Gar[3�%�����,����Q9o�������6�
*�9*������^x7_w�KGj�q���Ew}���r�=uCl��O7CP|��;�3�1��t�2�3�{��(K����<���I�����;�L`D��a�u�6��`-Acy�����Gn <L���|#�P���}�,�B��xt��������xryq�g����=�%'Sr4���[��b>��Hw�;�p��d�]�9���
q��%!`a!�^��
����v<]	�EN��Q���&�h4������7��z1�������b'�&3)�?��\�����e\g����Z��=�N���knV���|���$�6I)��s'��H�b �jpR��jh�5�3����y4N�7Ii����tlZ�/��y%B�^������~�vH���#H�i`5��F����p0��k���)����������j������X�m�{y���h�L����p���,.&�'�"�UF8��3+������������"���=� �
R�������Q�n������^�c`���f�9�T1�'���6�}�'R:x��n�����-W���7�IL���PJ�-M����r ��O���
F#�b�����p����c�������d1_�2X���`&��N����gj|>�2F|�G����R������c|\�lS�u�H��*[
���8���G\~��b��Z����k��p���NgZ�s���-v��X��=�W�2��y������d�����������	!S���S��	Q
��O�-����|�&~����?��y8�"����6j�n,����#�p�LS�C���L����J���H�o,���Ry�&�*��9�����������#���L��=���\
�/8=��Ay�h��,?P^�������N�,�6��_pW�����B����u�>�Q8�-��] ���y�]u�z����������m���a�&�(JO�gg��O��o�F�����ztr �?g�tyU��	���b������,H���F�N(R�k��+�9J��E�	0>W	��2� �C�X��i��[���t�RN�%�py#frY8P9���{A�wy��T-�i�����L��m��
���-D��g�4^�|���9-#��w�'86�d�6���e��r�n�H�:w���R�	�f���&	M:?�gn���������+l�����`�����4A<���)Z���]�C��m�D�8�5IA�A��29�X���h\JK`�
��z����vbo|~�Gu�x�9�	�%�"2FU�+��y9B�l�(xIiM�S��G������v�B8��7E9r�y?SR	g�`K%A2\��$�)|�w�^�;��������-�io�����!Y��H�|G���j0s!��'S����
i���yaB&E�APf�uP���r
3�B����*K�Y��hO�
YW{yo�������%�o>0�$;��[M�^�o��<Kz�������{�T:X����W
������M��m�������B��a���v�����~����n�@��
wO� �Y��q�e�k1
�0����h�TzZ��D;���dcD(��T����Qm�)�h�|P�a0��X,��������w��v���JQ)��@N����M����3y{$�q:7���h�i[�
�>���h�A������cY�HY��S�����0�����bR��A3�g	��RO�&L��`]������p%8ywu*�	$��8(5;�$�U�M�+4����)L,@�]��3e��[n��m��nX��1M���}�a9�������=h��&��i2%���@T����a�"@k�
	�U4���x�[[���a�^��R�������=h;����xj`�|���6��^������������D��N$w:���x2���E��L���u�-��9���T��������\���F��`����r�NXV�5hO
���\���N�f����
)ENY�io��GJ�`�3GL]�Wd��Bq���a�~�8��$���>����{�����Yjl��O@�u������J�Y<���
t�F3�Hg��;e}�g3*%!}
�l�`����������{a�Ek����|�����_�/$����������C��?�����P��������L��
�L�����M�l=��v���t��'�&N8S����Y�2,��t�i���W�O�6���#U�\����Q��r2�����J���N��RI��\[�(�L����#�90?�VP���,LN�pg�WU�i��V�1s�s�q�9H}����%��q��VU�U�H r�Op��<�p�b�s]l����kA���O�*Y�ZFH��h��K�_����������g>R2��x�p�����;gun�M�\��W���>���N6M���n���67�o����������K��{���ot�R���D��E�e{�/�m'��[*�dB
G0�p<M�"�6����l��RnrO�d��c�=������k{�����\���S.U��y����a�I�N�uWg����V=�?<��������v
�r"u2�zM�tq8$p��L����.m��G��$�k>>��p���-���38�`08�D�)��]� ��)��	�|��
�I
��+%R.���vi'r�O7]�E-G��).����������0`-v6��y��9�u�g���mf����G���q��Y�A��PXk��H�2&1���+�pCJ�([A�|��%L�����(�/��"s8vE.Z�X+\ZM��zLY����������>&g��c��?$�^����"��:�L����=����n�3�21�)|E8��k��F��~e��7���9�����.�L�?���}��Z�Z���`���5�����K�;�F�2
�EDA��1�N�C��/�dtP�f��k�pP�Z��Wks����:.j��y�/Q�lf���.�6��~���s�6&��7���|���#�?Y�z����w��j�6jV9,��.�[u��JH	�Ha\��Q�[w����V�B�v:s�y��O�UiwN��$h�����6����w
��/-�	\�^H���7�=��[�U�}��cK��'����}�#�p�o�vS�y���������\*o���l�jR�D`��1�f��/|d�&p��
���$S�����PL���a�����L����*��_�xon�}%n'.�&��}�������92:q�9����-������@@�K��`���v)d}�0����|RP�w�V\��Q4�M���}y���+����$+���u�.���y���C��R����u�C?�&FC����$��$,S>���2t�a�W���T7�.n���3�9c�0U3	�_xo&��'��X��Hhi)����fu/�-^�8�V������H��k�	N	D� �2����R����P�/�������X��u��!C��]�#�5�2�K�K�\;/�o�B0AW��\��H�\���Db����<h��
>���j+���������iq��q$����mp�z�i5�(�>�������g���M�����zt���g3��^��$���Brr`8����V���������k���7�0�<��,Y\T_����
���u4�J�����B*]��4��n����"0| �F2���A(eD��(���
����{�tl"Eh��Ft�
�m��h�"]k�+���cB�p�#Fd5��SE��.-��l�Z1�<
�*�h/BO)��o�'���WeQ��������f��*H;���^���[,E�`��;��t� ��B?���������h�"�|8�����4�l���t�hQG"�b����`M��D:�X1V�|
@��Zqx��\����~����|��=y��i�M��2�������[^�D����N�a��A|�3�K�v+����J`�����v)�K3L������NN=�B��`�6��X��s��+�C�4 [CV����M>Bp9�CDQ��;t������h�'��'|��� 3����y>�7=:t���?�?3x��F�J��l%RmF�mQ�:���*�F�/IX�v�NKL��I��u�K��KaR�$V�,���m�y�(�x��P:U�k� ��Pg5N�L�(L��SJZ�z4��h
�wC�2�"p�2����=��c~qL�?���7�
(�D���C�5M��Y�*v'C�'~J�o�W$�ej�����%���\������Fo�J*�k�k;F�0����iZ��l�!Mn�[���������@
�'p��N��g����u\$��f�������n��<��0]���J{��G���<��^(���;^�9��~xF���:���d��<#�X����=�QZ�~�h�W!>Ib�h���|t�B��<������hx2���;��Tp���2���FaeO�Z+/�������_isS	�`��>%������M���&���FZ������Q��Z5�"�I
!A���(�]�O����eP3Z����/�|�%Ez��/�GP��U�!{{K�`x�{�?��������7�|����Rb[/"� ���`�E
@#>5*���8jZF������%��S�����Q���V���J ��t_��o��H��"��d���(���/^�K��,
�!�����/P���o0��r�vB������G�gS�z��XIj�JWvg�I� /b��u����:�S;��E�R�(>��^��<���U2��iL��k�>����u[���`��V'�*L
�)��v��d����h)�w��m/F�[H����>�1��g����1_� Fhv0�m���y�TL�-P�g+����RP\
/N�)(����I�.1E����3��Bj�����'.5��8Z���)E�V��|��d�SvT5�i�^<�r�����3c2Ys�\�W�D�������r�?�	��x5A�G�L��g����c8F�X;�)*�	3���L�il_���1H�������#�8�%�W�H�$m�^D�I�Dl(�����Qu��R_/	H��;�/$+�0�|�����Aq��@�f��������k�I[b�����s�����3#��_�U��(�6\�q���/��[YxQ8�pI��R��:����u��
�s=���HG?�J�+g�2�		b��;s���Z����v����S���eSH�SGw���C���b�qd@o��M�9G)c��AX����N8+,��IRJ���&��q��o�5��/�k4&�x������x���9*�R�����[�pD�Ed��\�����Y���t��3�w�Y2�s�-�<�Dv&/�����rc��0����AIL����C�G����"��R���Z�1
�\y�>�3i0�����$x�w����}�<	���V�lO[0=�WE���u+q0�i�db����y�\�;�Y���z}`��~��m�D"����5�eXH�lm��Q~�LI�\6�H�T���*\Z�H)�tsL��&q���X�����A*��l\�8�.��H�b����iH*�FK��L{����>��H~�C$�'o&#Xx�G�D$5/^l$c��P���
�����P3�E��RJ��(����5u����7��s����S�~H���wK�.o97XP������
)�RU��P��Z�7�)|��E=��%�f���`�J�\
��
��j�9+rMx���@���"��dLIHML5p�+�r����f3X����U�����Q���K�Y!\ ��'��F�	����Li�s������D�E��w`�B���%��R^�G�e�.*� mi7�1I������
�,E/����%{S ��m���`d��R`��D��XY�����f�H�j�����Cd0�.%�_�e5uxs���xx3:��]9Hi��l��`�����:�=��?�G�
��d�H���VELRZ �\TX���������c�����#J�l-���1��<����tM�`����um�Sv��5�s��S�t�����CIa������#�>9��?K�Q�Y�]��z�>7�9?�I�Y���*9I����F�h����3Z�f�$,Pi���/-�A���r�d	�Y��.g\������s(^X�g�zNZ$�z)�1����N!�a�E���)������o�A�Ks�m�>�H������Q��6j�3E���HS�#�u|��8����	-�!������06�[��::����ZP��g���l���Z��C��3���8r����'%�||"������f��ca�_<f��S�{c�N����_�,os��j���^�L��6�^
��7�^Y@�mZ��(N����^�����P�����<��d����U�p3|+3D[�=�Tj�A�4�E7:v���:�p{�X|�g��|��^0Y���R�u3i���������g��}����#+���[����;K�u�G�%��vd�=������_;���7Q��<�#��nlO����qcrK�o�v7]�q���@n��}��KK����M�:�##~�3�����+S��uZ�30���=kv���/�$Y�Zv��k��eTKF]K���8�%^��!�nR�:hF� d�!��8��o�/��$J����[/�����1��
�]�V7���L1DAFe���Dm��pi����{n'�Q��W����������rZN\���$
�$b���AI!Ja	��[�t'����WhZwl�,��Y�	)=�-��������%%}L�u+�DCsI��z��&���,n��Q\�m]������e%R'�bem*c1My+'��~��AT��+�� �����.��L.NOT���A�8]���/A�@�m\;�������F����5�n ��X������������v�^���{2�;D�p#@��n��*������O�J�����6]�����s���/��o���������6o���d�������kBH�HSL�������sd�i9�@>�"l�
[�����{�m����8����H�$k!yfS�jG�{�$v�n�r��UI��o�I5����n��S���7�O�l�[�\�V
�&)����-!�l~;���>m���7�6E�Sv��!g�\62C��5)�����<CN�*�\;3��j������-��rsD�����?����7XN9VQl��|V�=�,GF7�����P�����,�}����"/^�U��N����|��'�F��]]�^�L.F�������	h�|�i%�����~t�7	`a�����KjNL���a�;p1�_�$_��i�>9XY��N���k�����w20���_��%�N��$�Uv ���|��
z	���4�g0%���[KF�8*�z����m��sS�����-��������H�#�.��.	�-1��^��}>q�F����S~��Zvu
�� $�����qz	����O��!�S����@����8FL��!����s�c�@ y�^&�"q8�6V�0���V��$�k����$Y@�W*:Q�I�U��E?5y��������;��R#Iw���v�wa:���D��2�j)�������x4��������L�wg�����&n;Of�^����,����Q$�����T�.i�7����o�mrr}y5y���������������"���W��E�i�����*[���9g�O���sy�2QQ1XE�'$�X$�wI���A�p�n��f�)�
r�"�'w9^h�A��8�@������y�
-�(�N���p7���s3�A>I����w��=^���R9~�a�I{��J��4"c��o���;�V���J����+�����l�?�UW�/�����SS�!�tT�Te�)<Iy}8�@���7;_0�`w	 ��O�K������4o��JEr�VY��	�<�	E�<�@92��EO�.��%��9H&hJ
�4���6����k������A���T��������GU��d�E���!&�/������+UZ7j�����<��H(�VD�|0�����+i�����7^IAQvG='bL)GP��M�_�Y����tu�~kG�#R~AT���k|k�_��cY�nS!"��]RV���D���/�92{���Qd)x"����,��<���l�l��58EiP��0"���%Z*����Y��t�7�����]K�=mu:}�l��Mk`��Ac6���Eko�m�V�t�������&�H7_D�b
&�������f��������{ZggD����w��\j���t e��S6!v�����<bz`������}}�	:���V�`"�$�UIU�\+
l�����RaGW`z������+AR�A]���3_{%�;7
\z�Fq��Q���|�S;�7J�@���1)V���c��V������<�I�r���R�E�f���e���$��Udy`��F���<W��m���K�E��,&�����j�h�?�
x5�X#�3�1m����;f?������
*;Ww�~���������I�i�iYS��Y�-\����3��#����������P�����PN��`���H��%��F?�}�~?��(���)����b��,VR2�U�t�����}��o�f�(�UQ��V��� �}d���I�e4I9�%	\"�S���,����z�������-M���(�hAf�l|DzNZ����V� ��<�rkl�Q�4iO��8�������g4�� @��,������e�RWDa���'���_R��E�I-�B���KL3�i�L�����i�v�9�7cZ�N1~�edJvN��X?BB<HEQ��9�g�N$�%��0�,;f��YE����TU���4�.k)��|	�P'(C>d����P�����bLi���mh�Pr����j�zO~������L%���u�����������[$G/lt���8����KB���.��CY�����Y_*}�X����z�M��=�&_�"����x��8�y8��Q��@����N+��������A{
[�v����[�-�T���N/�������uh{K�*�b'fH��nI���G�����Zf�i�B���$�{*"�	�?D*��U�n���F����e�\|���o�j��N�c���_�it�����z	W]��8l��	)��Y�J�02>�^E��
1�A����JT��r��&{{"�3�����
�Af��a�SJ�A�b��@&�����Ng)`����Y@���d)Z��R�}��/��O�f&�a� l6��w��hu;���CG+��d���*��5>=�k��Vh��hy�Xa;��+,C������M�?�>ld)�U�e>�S���D����_ �]5qoT���[7�'�/��AE����q��Y7��'��I|�����<���s��o�^5�^��N8�GJ'�����0������J����������0�����m`,:�9(����2x�[��-��ka�O������������rK�`�,�T�\&q�+r��d�e�k�\=���bT�^��r��YW;����Y��f+�=5$�27$���5�|15c�U��s
�L.�/N��e�7�ZN���~t���a�f�����Of��$~������hoh 
�����T�q2Y!v���J��3N������^?E"a�l
�'��8���o���Z
�w�i��e���u���_�����o�j�hv�;YJc�S�l�D��9`��5_��
��%���^I	y���o��j��������s���Z���R$LpV%������Yt����=�}�����#�D5[���de�6�UIv��d���kF<��cu���V!�	c���	���G�JXc�����5�(�,����Yw�i��ZP&�+��
�����H]�Ia6F�+[dqc*�g��1�T*>�/�M�N����
��6�.���6��8�V�1=��5pB�@�'+�\+F*?�\��H����-��MY:����|��7�r��X�T��x5z/Qy$�;�aY�}����K+����^C�f�� ���E�����lw��D%g�*���v���[x��z��m�:��VgH�R��"�4����3
vfH�c�����v����cpaL��s��`����c|�@��:���1q��dV�T32D'Q���y��I���E����H��q��l�_$��<�f����J?�z����
l��8��&���F6N��l��m��kVk���F��[�;F��Ir��/���W�
ao�lz�<����
���Kv�, 7����-��lZ������h��V\5T��U!���L)r{nk�E������<_���aE����Q6��=�e���"��s�x016�����cS��ty�^���������sD����q�e]�������6���V����������=���6�w|��`{����PV��74�m�(ED�{�����(�Sv���-�L�mJn5i��@4);�5l�P�����J���j�,s���d�j4�^/�`Sk��]T��H�����l�&�/��y�fh�fv��sw���
�,t���H��q�|��a/|J�9s?s�|L�����#��]�.F?G�|<�����P��c
���9�Id����sh��2~��3�B
jF�����Pj���0��#=�(���i�	Y���e:�hhC�6s�'��=@,�����U���-���W/}�n�Sp[��:���lZ�XG�B��QI�n�c���Ko1|������o�;m�����:���l$���}Q�?�=�Q�|��Lh5,C������)J����TV$vAM�a��6g{�l
V�ZS�����>�{�PA e$j�>f�O��$��R���������n�L�+Q)���L�w�m�V���k�����A�85_?���cL4��d��g0�i�d��i���s���dn�h�5*��S��3���Z�
\D��969�`s�K0����fZ��bR�<Z��m��FVl���������:?��6��[���V��g��l9��L�atg�mrn�
�)=��a��H��M�o����UD�_Ks���*#v�#��d(�k���,$1C7���I6&O���ob��������I:q�y��#��%�8�n$�|�z���&�|��i�mX�L�"���B��(�PQ��Wic��/i���������A���i�	��:��#��e���7y"$��"S����0���U���^����rA�,V�3Y� ���n�,��*��H��4����m+oB�P��n4�s��*��?pr���CjN�m|����5�}���|�q��I���H69�������A�������Mg�i�����n��t�n����
6��A|������l�_�=v�'�b��P"�q+�d:H�d����p�!
9����A���l�J\�&#�yh���s�� T���Q��MVG���r�.�0�a2O�O�z��F�na����@S�Mq���,��8N��N�a���>=��iuU���n�������
�����a�V�>��{6hY9���Z�b���
;(�m+)���F�G�z���T�i��G�i��I$�vF��r$^�K��rx1�����"-AR9��	�N�.�rV6A��0�	(��l��h-C��L&<�x�2c�rnT;�N"���VO�MG@���`n��3e����#�y��C�*�	Vy�'
��l�9o)?�vA&�)kJYeZT����r^�k�cJ$u(����Q7��������������4G�F�y8���`�Z�d{$��v��)���j��L�@���A;_�����'�k����0���m�^�%��a��E4~�yG��x�C17�g�{���>���{�����J��h�,��3lX��c���i���^���
q$a������K�O4��>6�R���t����>��[��O�#�t�c����>�\����[����A�F�e�����m�����:V�R�t5�9��pd�~��Bb%9�X�|����7^Tfd�C
���q����{�S�--��fyK����Io,��e�oC��1���.�H$�?���g�-G�!�~{��	S
3���4(G��C��l�K&T�[@�-����lx����������WYG-s8�.�p#D��T
�YW�598�����
�.V����wK�A�rtx�p�
u���cc�<J'��e���p��Q"��>�����O�|h��=�������'������`�*�j�m_]<x�5�( ��S�a}�Aoa�|���}�M�e��_��z�*�?��
�	K���L���=�c��Q��z��e��.��X�U�E6]��
 [�g�)1"gR�_/�zh)Hw�W7���E$1z�*��R��9������� �t���;�e[�^�����F��l�m���o�`�I��0G����*�W�@��t})H@,�F��><�E�Z�
f0:�]�O�7��;8�>^�.ni��U��(�|"����}]�]X��}"�t�	�n(�RW���f<���V���}
��G
J�_�#�<D��Z�'�fW�N���i�^���0{J
�/[��/C���������>%�G���^����l6���M}��������&��/.������pi+�cc+RCj{��w�hm��ZM�m�p,l�a�������;h��H
�;M�O������_l���7u��!��EX����(���Q��e/|'�qk48�-���:�FE���e�Dr�B�����=P���p|����W�K& �
 D���$[�?��
2�4��)����������-�
�������q��>b�i�$��HE�
�	�0Fk����]���t��
t8aa�@l����������j q�a�����Q���J!iId�u?=��h.&""m
�>28&`��t��m�aap@�c* 
uuG��!�A��|��0��G��L'B��*���c�8W'�$b��	1�A��:�:3��* �u����M��0���\�.x	��")�	D�L��FYHB+�GO�KQZKX|�_h��m�a����W�]!	E����@McN�l��BaP5�w8��)�u?@)�R}ru�m��.���(A�A�$Z�c�(���P���s�&�2{t�/	�A�~���!	<�z��1�/[�b�d�s�������l"�<8����$;7Y���p���`B�%p� �$�A��9FI
�lu�E�f	��[�����0�Sz��x��9��r9�3��)$���|9�&�~l�ID���M
��)��ft~���7=��x���Pi����0���T,$��?��nh�O�.�i#2�`�-�xKt �������c�$I��/K��'I�����l9G��~�'@�"��y���-[��:i�,E���P�Y����H7+D�q�3�������h�y���
EA3�%�6!HY���]�:�;��V���0��Y<��7�O��$-E���Wn����e�8?�o���7	��[�}}z1�������&�������5�s��������N������T������)�����{Ev}~�Bi��7�Y�H�+���Z���0jC��Dg���;���wC��#���RF��q�M�.O%?.W�2e�L�����K�i���o*�M��@bm��Y5����H�kdFR;�X���\e�Q>	��2�0���}����;�-~e��������������X�O���x���������_�E[������x�~t|�*�����9A����_X�=G�7x����������J�U�	�#f��:r%��|��t��W�-�@��A,�����T���`��S]9abX����3Wp���C��ZS5Qt@�\]U�F<u��0?����o`:b�=����b_�'&�
�m��]Scv��h	���nb�������������:n��q�{��MHv	��s�= 3���.���yx��Q�%�v	U=?�*�
���{K�����@��Qf�j�Zj;�@} ��J��t`���o��e\��+���q��j��gdr�����7�����I��/�T	�����+�d�����	k&)uX57�S5/FwU&*�!�Dq��D4�MQ��@!z�o���$���$!e���%��%�97�����
�#	�H5���F.W�����C�D�J���8�a2�0���(���M����hkB
A�(U��5`�6n���vCb&Q
c{q�	������#�vB���O��-"�����^��&���ke�	�������dG���;yb��{��K}sF^`�@���O`������@q���.�S��K��}p��w�!���a���dP�c�Gt:c�7\��[[wuy�1�����������Mb���,�5M1I��^�pYg�i6fz�p�k;����g��s�.�q�����DdA�7����=p���w��T��>���X������YW��z�7I^�mp����(�IS=�� �X���`������~�h';�N���C����X�!���
�(�FT���!u��>����JQg�
������I�d�	�Te��NE�����TF����k������<e���yz�Rt}�������u~C�`�9L/+r}B3���E�����t#/RRs��3���l��I/���tH��g�Y�%��7�'�T�%�R|�xC�a�*�.B�r�^!����Qb�L�/����h��@)�n�yzu;1�aB&<r&�g����[M��DT�Z���	 ���bxH��3�K��6�"�^���0��3��8��Mk�Q�-���}��C	����*�
E�;1r�,m%���1r+M�Z%���'�S���'���`�3p���]dV���%��]E�z����YC��|�9�O�YdU�Ls[}M$@!���b�T)!U��2Q��B�KjCb����c&w��/��"�O�;�~�kT��gf�c��'N�r�R������G���D}o�S����zB�~�DOqr�z�����s`oW?�������]��G��{d�����6G%�u)r�s=WO�PW���p1c$��J�8X�%�"AcO>�u���,��,4���ZT��$�9���a #`[�6�/�X^G�Z�IQ��*<[������Ph��s�����0�G&|���p�u�S7Pd�\�Z��?Z��j�]�T��r�D��<���0��$��C-4i/��������/n��7*����OA�{��V<amo}{W?H0�g�9����?��^"4�V��zal���I�\<	�i����	�/C�<���p��_~?���v��� �ob�R,jFF��EP�����r:r�&�e���Q��,�9K��Q[C-6�G���K�'DGrY�B�y;}���Q�X@��'p���W�G����;M���3s��;���69#�J������%PQ��[�tu��]dy�\	Q
D:�L�&�l����8&_�v�L�����`R�(�c���Ia���������	%��2�;��[�?|��g��~f���)��<�f���V��$g�0�]����Y�C�
�ns0��W�N���
�(f��c��N,��X>��w��� NyS��+��@l���0�7��[�.}�o���n��=���`�(m��t�Eo9+�i�h3Y���J5��K6����&����e����,�������
�s�9�7l����������m�7����M�X��7�������a�� �i�i9nu��
�$�V�����H��=��F�_J��$�pC~��{����D�����Z1������`z�z�3�u�V��I��u�u�X���q���z	fP�
i�p�el����05M������N��r�
Q�j��4��u��(���4�P1>�o�M��TM�'�*h�C��
��rP�o�X��E�:qzz�w�w(�$6"��H�+����[��~��Wq��z��N���t����j���G��R��(&F���
�'��X�������YmoO�~����)#3�[5������ck��������H����TZ3���T�����l��Z�I�������WuF7(f���^�uZ-�T���lN�1p�������M��}���r��Xs����y�|~�6�d_�I���d�����&�I���*�������z�������%�^Oy6���O1������m�*'���Fd,,��jb:��ax�{���N41<������	B����/�Up���g��_"�_jB�hN�������4�6����]��{q�;R;�k������%�W��0��RD���|^��-����H��G�R�v�&��~�]�[�6��M�$�@�����^�AH���F"�&�yas�����	=�HL{GIGu],z&���<~59���
I�^RH(uL��I���������^��;�F�,	+���j�>{dq��z=��(�g"�=
��wQ�wW�H�k��ww^P�'���H�L�����s��P*Yv����W��hu{�^/�����������G���f8���#������wfF������[�e�]��O��V�	��3)���m((XO�/��@T��?��JEW����(�t�Q'wY���yd�4S9V�h�IF�'�|2���e�����Y;�������v�&$��~�&|�oJ��l$����'�Bb�ao�"�� T�C_���Q�%��C9����������h
R����+I���b�|���7�P�@F����toS��9�)�����\�[�t�E�M6�@��Z@�jFK�I�y��������8o��I��d��!l�9c�,<��U���r��	Y��m��I������t���8�����~��&f����%�-��w��H}H���KF#SE9�=u&#1l����Dxj���?0Z�D�!O ka�f@A#X�M}E�A���: Q�s��}C�P�r&h3C�W�z^�\8������������y'4��</d�[� 3���<���i~��2���y ���Ww����4tB�[g�.0��$���:��u����G��DqtBt�N 7@X���4(wJ��+�N`�+��za���9sM16��������O|0M��C�D|g�`+,��r���eu����K
�����wa�
^�� \q�����p��l�4G�����O>����������6��09���{���oF�}�sk��!����������f]�����P���?���E�J���1	w���	+�&Q���iB���T�+?�,��S4JU/|�Vw@�R����}6�0�_�?��l�c�eG��m������&��8P-B�V�p������jE�@��r&���Z���D�KW�K�������!U�������;�LU�7T��Mu1���xe��f2��lg"q���2�#f��+��N��-�����DN��E���P���� �
�����z"�HU��6F�;������!��t��\�[g�C������gbe���U�H�eg���m��������O�Jb>j������0�V�sW���m5@��a��d�@�KYJ�%$9�m���p���I!Q�,H��`�TrY&��n!fj���A�rD����/'B(�1W9�L7����N��pr5�M�m�_��gdW��-��8�]g�(�=�[
����9��i��N��������X�����]������`b��j���A�Gz�55�z�z�:�?�1�]��P�@���E�uS������N���g��n���M������T5H.<)l��s��O�-]�������.�9������'��9��r�
E�z�*����j�����j�lW� ���t�Dg�<��g��mX�^QT��k0;i��j��.��V&����g"�&fW\�.�2u	1;E�����V�������/�Xc����n�T��Y��[�YR
iC��Z#�����+�����J��Tk9��^I5���g�.����t�fw�j�R��% ��1�1��z��N�,����������jR���aE���!����=�g��N�u���z�v����r��N�w_�f��Y��-����W3DAo�����,PrSn�������C���^a�����eg�����m��JTM���W.3H���^���d��i�lnwE�C��=:�����R��r2Q���~'��at����F�H�oW�Ld�������������T������s-V��\�"O��B�^W�?vj ��l�V ����
K�r��f&b�t>�XA����b�a��1�V#b�Tm�i�'��YR�9MD���fg�h�L��7����N����k�f�1-��N�R�"*A.h���V�[M�kwn�.z��G���Pyr�OE/r@�����soj��"p������M]����.u�}�o!)���c�Ov9
}���`E��B����{�����fB��9�I�{��1%��&�+'A�X|����+�\8g�)��~�FG�>���v�v{�����m�p��E�����������au0�#��:�����>jO��<XY����~>r�8���mY�������%�h�?���V����z{������p�G�s�\���FH&�Cp6j���&�D�����/
;���J.�8 aG������W�9����<�8�V�>���X�����1�A	�Nt��q&��n'����Y���VZj�a�-]�{�%�q���u5���sF�tM5,��x2��9}wz<�9���S�~9d3_u����|���3t�_�O�g�/�QD
�k�z����C	�I*�eQ�JL��/�L�A�V�b��S��2k�{'�,�YT�%��W�����T9�\���0C��K
��n�����^S������K�<�
oFQ0������<3_|���*9�0`��T�v6��+4�gP|��?����t�����T�(Q8<�
�7>i��?���#��3U�Y�/g3`=���E���$������n�?^�^e���[�v���z`�i��b[����r<�|�������h�J��x����Bp�mQ[C+O!1^O�mT��H��
� �KX�M>�'�d�4��O���Jd���G[TV���V����41r|���pr����m����so�-#O�N�10f�i�Y�w����hu����6&9�c'w�8�/=63z���.k����T���K`�>oM����{M�E��wB�����r�+�R�8y����&��E���q��j�qi���5��2(��&���iM':#*����������q���5��;�W�c���m�b��W5�iW[���b�TGP%�uj"�&T=�*
�)~����_2�q]����x�2�LS8H�8����B��t��NODU?�f�%-�[�g[oQ���t������������Y������^��+�zR������E,�71r]?���VV��<�]O��	_]�\M��W$V����������J�_������T����j��C�����bT2s�*���s|w���:(fRr2NxuCF1���+,bhO��[�Zps[��Hn�x��l�-�K)�su=���t��X%���n�h���	Zr�HWC�I�G����%���q���K��PP(�C$�T���We��\�.��
��>
��*�������,(ML��G;�^F���(�������Z0��^����95L<�rK������C���B��W���r
�ZM�j�\���k9-�Q
�C�$Y��"�e���xlj(X��>�~����{ R�Yf��0����>�qpXQ�h�+X������
i�\�������#���.kr��ab*���@b�h^����������C�����O���;�w�v��F�s�D�E�����j����'�D�'�1����_�D<F����h8��OD�C9�����=��Y��l�������Q�|z��s���qM�K��8|��������*}���7<�N(��1�xoT�n�~0�:@ok"��^J�A.�(f�d���1�&�����L����G�JW�gQ�,b��X���DPGK{;�V�G��������&p}��zx>9;=?���~<��!*�F(X��gp8���a$�8u��ap��b�K�����di�������a$I�����HcRskN������Ku7BR������jx=|c4K$�S�����8������uq�v��"`�NzG:*�}��4����@.o��{���n(�|���sY�i��z�|_�)�p����1M��g�P��T`��!����S�����a��~���c�:�u����L�����`p�Y�9Gtph��A�
�\��:�Z��=�}�k�Z.p������A
�"U��������Z)T�;���:|�I4�/�1�e��z�2jA���g#���������mP!<�8>�;�������;��[VRE$�y����S��*����������1c��)�l|!U�Kv#���[	W�+N���Zd��Lvn�����=*_%��?"�%�����.H�0�/a@i�� ��	w����^h5�|o�H�9`n4{�P2Y#%����W���t�,�wM�����]�R��B�������s@k�l���41
�&+t�G����gL>-=�����T�|72�
���<�'Q5����fgr����B��~1x�\���?
��`I�;���gw%�G��w��(������]��@f���^�I�$���L��������\��go���	��'�!i�/������%�?�<1��e�Q��#@#�GL;��u~Dq��Y���9v>����s5����8���T�D��1l2?�J(!�q�L��������n��.�b��~��X��NC������^f+��#�(c(;�Id�S���L�C�sJ����[Xya����G"�FF��g
�|>#���F��;}�lKw-��s[��������{�����':u�~}D��Dj����ZJ���[�h�����C�qE��3\a�61��	r&��j:~I@�:�0~�-���r0���6@I�Rj������1f5%����[�G�}/3����A�}��`�W!^�g�W�9�w�6����,�t��b��/�;wn�D:������3���?bgtv�_w��������|<�0<>���u�}�~?�H\[+��{]���H�U����mc`�����k5��=�Zq%������h��\X���|uU��R������-4U����uX_���4QtT���HF���xD�������������6G1���������:z���(x�������>�~2d��\�e�A�>-��E�C�T��I�bF�����/�A�R�������e<"�E%��*�*y<U�t��V�0�Z]���A�Z/����:S��x%���M+D�R*�[��VD�%�D�F�.�W���p���W�n89�9����a����5^O����Kmt�����I����b0����)�<�����U7�#�������`���P����Zw�x�X�����E;	���i�E�
�j�{����#n��tr�C��nrN�\�������r���xk�r.���
���Q��j���A�Nm�D��<�X	"HL�F3H�������{���F��	�_�f�l����.�]���4�v��SK+���P�(%�]�ow<�/yWu�Y3��te�����qy�	�)oZ9o
�6��]��<�rp�X��"y�����g��t�����h���(�g�G�K�5E#�����h2���J���p ���F�2
M$�:UiM/�5Q�n"���'�����%�%n�Z&EI������UlL��R��kk��g�����_y���K�rn��{�g�H�v����>�2��_�S�����
L/��<l�^��l�6Y16W�q���D�1��I��G���A��Z��k����`k�G����3����XveP`\��g.�y�������s��kH(��A�2)�M�hHK�X�I��$w%
�����W\�x��KqA���v{/,\��A{
lP'���'��G�aBzTl!.,��g�w��@+tXt���A�Q����hi����%���H����!:�?��&wi����[�3���������	o���i#����Zk���5a�\)J��uM�M���u���V]��$����������f������5�~��4G��ON�XP�\��L���%�?�P�F���$���&�es)�/��{��S�~a8���3�xS���h��1W�f'�F*y��&;�F�W�$�0�i��3�g �,L\L��������;T��D	n��a����U��K��c�@����C��^�yJe6���l�`x_33�u�'|rA�s�T-�|`��;'��[1*c��1>�6�I77�d�]{U(�;��R��D�Q��G��� J�C��.;��[�
f��1�����p�Id��5Yr-���uTkX���"�~��>�0������v��s'�C�f���-K��G���lr,�J:���`V0��t.z��"=y"�i��4��&���>����<��i23��r�-D�bi��y�{el�[���}�T�����F7��rp�GTE,�Lx3�k'X���%�#M��^����O���7�12�g��&��S���-�L�S���h�r�1��1��G�%N'R���5!�����Z����1�j�W�8o�����9�dq�?���y0���x�+
�W����"oq���t��yuW��3L'���q�=���Q�V��p�0�!���O�ELj.a��:U:��~������5]��z��^�Q��Y}6��aA$�; �9F
5vz�)|�������DY2�������vx�0��9e�rg+DW���64�������_ ;j�v����%|�(�7���7�^�����`��a������9��*�I��#@�\�H/�TT��3��h�h�e�*���U�@%��&i�X����|���y]G\���a8UY�;�|gx�Vx��I>�8l�>���DV�"\)����kD�'�S�Q�QyQ~����
���a.�D�C�T��x%�%�a���.,������s�O
0]'�5���Y^FGv>Oc�s�^sC�#HP���r)��|	7F����fm�lR(Y�zy>"p�rjn����^|�\5���<=�%:Ci$��F�&��%���;�L�<F�`X_��R!���)*#�Y Zw�Fa ���Fa ��9#���J#��8�Y���lOl!Y�J���>Z�w����D������V�F�����9(�����a��D-��r8�8+��EG4�;u
��9�mD1�H�@��$�p�*n��d�R�JF:
���_+.�*���*
�m^�����R�nN�������X56���=���j+zL7A@Z�g!qFRg��T�r��Xo��0e�1�����!YV�ih���8�����X�jbpc�v��B3�30���V�\rB���eE��<�����i�����h��}�����s������B��q���`O'��@U�������O��E����Z��P;�Q���Z?8�����g����;��P�����?�P��az�]���O�����qB�3\������������p�@���B/���y�r����|G��������|����D%�C�m�T����������2"�P�c�t�����:�s��/��l�Jr��Sa�g���/Ng���Py��F�U,I�Z3:��hyc����������$�k�<(�>Cg����UW}��4|}����7�����Em�zxG��������~{?V����Ha\Z�����J�����f@>��Y���t��P�I�����zC��h�6��*�$�"Y�!�=6���		�>�?��`w�
[��Ny+�r�"kGr���5��������?����E��n������a7�&��G!#U���p���B�[2�.�w�:Z�R��e����.��[b|�u��9��~g�����o��|�������ix�~��8���2���i��RKjuu����l�%�ui���[.VA�#���<���L�D�]:�FI<Y��ibX�4����G�U��
��h�&i��)y@N�4b�AXk�\J�~l(Fdd�Q�9�-��@��	Eh�7���� �I���9����T�x������
h����?H���]������[�k��fW=��:�	�,=��L�V,��c�
s���X�;�&2�/d8�^�1�y2��A�#�8���%3��x�����3���i:�r�j��xkY��nX.���U"
C���������d�<Ng3��6T^!(�*��s����,��6��_�gGL�.�p��E�E6N�Np�+f������v�������w����m�N=q�Q����U���r���Fc��ekkk���L�������hp��������-0{dw��k5`��q&�A�����������6���n)6�\�{���8B
=<u��n���cFQ�N-`[�q>i�}0�K
 B�:?��8�>9Bh���M4VE\r���	��)�S�C]�:/� �w�
�'��	�>r#W��%�C9�<�al��'�(����9v�}�BP*�GV#l���$`���$:3�����~������h�Z[�;�l�F����V'���G�J��'N
W���s
���/��s����
x;���~�{�����<����,�{t��(��/wLVB�g�Ne�}So�Y�^�1mCU�e�/{P����F��&^sR7�rR��oA}�WD#U��y<L�G�^ ����@��.
�����@�-�S����10N�����NO���4���gmmm�J����:=N�Y*=+��W��[/���������YT�#��1����#q� I�pA��~�G,#�%�U��.�d�$��k�
u����`
'�n<��BG�76Ym��e!fy�g�(�:�^T��k�����+A���E�X6^ i���K�����r����vyX�@�	wNtA��N��� r��4C��K�	g>I@2'V;!�n�t��������e6�6��s�"\� I���W�,�����3�����}��I��G�^����1u�?������v�(N���@D��L)��Ky����{�&�e��).-�[V�F(����3K>���}wA�@�|���Du��;���9`���X���B��Y���^�3�,��Lz�5:�9����7H_�����Xo������MA`����N������Ud��!U�,�lt8H��9}��K��z%�O�+J8�S$ABB�����K���2�Zd3q3�
��[�D���)1"�0=��A��;
B�m���m���m������u��`�w�C|;�a��3�c��
1��"�K��+jj<rUF�J��~���b�Qj-��iC����)T<1
��$n�!���5{�-�X�bm��s0p�����K�SHW��X/��\k����ai>N�b
���>������
#fw^c�����L�]�����+�BG��p#%I��Tkk�^�T��H��F��`�&�C9����
9���������a��1�4_���EBi���,]�7!16��6j|���g��r8�{9�1m�����9���]���\V�9q�7F�VL9_)��bkv�=rj�.c����3Kdsc�y�����N^�� �q?���m�Dg����q��A���|zW��B��N�%���k��-�b]����"<j�e����
�Q�1� �h6$�:H]	~���Q�sXf������{�;��jx����7	��4�r���@}��H�E��d@;�R>�n�����[i�7�6�o�7��[���?�����w>�wv;�^��e"!��������yg�bK�k��{�g�����+3��pr�Kf�7t��[���u�+���So���7F�����>�a��a�M����������-���8��~1E�����7�KL��m��IN�����V6!��A'5K�����G�t\�����������Ws���7��DQY�*�`�N��nU���j���=�[�/���8c/���/���T�T���x��k��mq�
�U���:GZ�����G�Y�=��#��J�7v12K�@���Y7�<�f�^���c�����i�o���,Dc�v�������h:6�7h&:�16�EC1#�}�GR�a�[�����I�%��
$�M��*��VwY��7C�&�`���j����M��~����s4�~�1���p��q~�!t���%���S���|x	B�%<hG��c�'�2�������T���h9�In��UI]B��,�����2����\{W��YJ�`-y�m����"8��?�U�9��9��z��Y0�F7�'d���B4�`K �K�k�UE>����������dR���W>�Y��A����P�g��<�-��/^<�{����O������c�+���Z��@���G�I9����C�r��~�^m������8��!���|r�]om,U�xIbt��f�����to8lJ��$�Y"/�%Ka��X��Y����<�V��l��K��M*��[V�@	�|O����dKF�M{��D�������������9���[�fQz.T����Q��/�]<�k$�9�;��`�]�|������Y=�9K��{�!��V�Cs�"���h�����l%xT]�&KK`#���SX��&����4�C*n������t����"_�%����	���������]��|z���k�N�]�i%P/�s�?:���sr�m����2�U�S| >X� 5*
�F�����zq���s+�;��`��mm�����}v�����Y��Rq�B�x�V��1Y(�:��R��irF����k�u���.d����~�hv9%|���d.��(��(5z�����HQ��C�PF������:��KRi)Se:0�%m �
uY)�.�������ci�+C�.�T���
Wq�\P����|�Ui�bV<�����c�;��E����H����W��y����l�}7��������2k@=���/��=G���������U�����������*~��QTF�������/B=�MsSAa������4#�E����k�(z���ep�X��TC+�t���b���H��Fw5����3����=z��3�j�%Y���|�f����P�<h�<���=f
�T�������}�����F[��������j�cCvZ��#F;��4���9�{W���p(]��*sn��P��������b���}�9�M��_m�h���a ���N�`!Pa�2K�Kf�_�F�0F��q����o{`�MQ�E'�H*�}Te�%p�E��,��*�
�'Z��=�r�:���V%yz=�����V�f2+(����/Z�L�X��n�
�y�uX.��Cl���b���6��Q�&���h��F4S,� %3��6�:%jm�n���3�����=������)��������1~M����4J���A��-��Y��M���1�=^.\Ti�/[(�1��z���k��.?�����y�N���$1���Nf�4W��e<c�i5���I�,4P���'~K��B�e��d�a=?���#jX������l0�T#,���3�Z��2�s�^t��h/1���|D]M�@z1��'�Rx�e��X1<���K����5��U�K�l��0B��;�������_Y�+��]��Mlh�����3b���i����~�8y�;�7��D�9��6���U������{����N'���|�.}y��* S����gPR�W	s�(Qd���T���
z������t��Q���i�3Z��qP�R,&�4"������O�,�A����YM�a0���e93�����v����laU�6���hW��+3�?����f^��(R��!1���=�je�x4<�G���'t�U�@��#��T���r�GK�M�@d����N��g�U��g�4+�r����+l�W>�.�`��JQf��Gg�g�.�}}z�(��K_sSy�R��K��+���)
��
�0����o��Zb*��]D���J4]>�jR=��6y��������%�#��8�����j��|�7�zS}O�>����N��x�".<D)g"��M�Z�f�p � #�*;��O!� �6
�_�������^eF����	cf��A��c��a����m��k�3���,
�rP�\#Y6�n�������'�����m"���q��5e����y�W%�m]',��]O�k�'��t��Tp�3�if1�T�8Vs>'�]��4;�,����*W�P>��P<�����'����PF-�U�W��Oj�V'�T�&�J��D[�4$������	PP�jD�_�/��t��2�3!�HX�]���a�=��(���G���t��<2z���5��lI������tI�i!�.�w�X��i���t���h�`K�' �Bx�rI���(���N@9�����lfz�1�~ XfJ�2��C	a,T��D����4O,5u�3*�2��e���[�[���8;��(���N��3�B�v��s����|��
W���G�AGB<����k��-����v�/���=B��{��f�����n������������=E�a�W���T�B����:�to�\k�=f���;��?��h�j��R��W�kF����|�u��e����c�wjQ��%K^���>�U��^�])^�m��X�$���0����)�Y����<�f���?{���$�m���TWt":26�h#�]�f��.U�������4< ����p�d9�)n�'8
��J[�N!�I�f�#u�����4$u��rm�A�@2�T�V��1z,�f�$��ciqd��+T���`�ef�+�-R���������O�S+�U�)��^�g��x=x�t�a��JiI=�p'o���e�<!��U.l�UM+��>,�RF��G=]�I�p�|� ����Q��c"��
�HT�(
�������mJ&ZG�2�,���_��1�,O�����L���H�7�4?P��<k��$~i���GwN�dj���Oj�P��:���@�-!�	�Bp&o�)�w����z����F��!���v���!�u���C_#St��Y�II��\��V%M7g��*J���T�7���V��&U�p�$��YFhZ��r6���)�����G�V��1�m����MobqD�0��[1����>�	
�����(�����i��n����������17/�KU����-����k��,�a�m�CH��Op�/oA�NY�r���!9�����H��=��Q����b�[��c���Jv�1g�<���}F�����O���1�<��~�o��?�7�{/��}�g-���6g�Kg�>�
N����G	�-~F.�7��P1���uvD�}"\Z��P/��������?A���A�������6��I��m����M�v����KA�3����a��a���a{�q�a�:.;��f�j�.�X��]��Mp�W�8�[�`��_��9���w������%
���;������7�l������wO-�;�/����y��ra��Y>�=�gq��m���r��j�������Yd~���o�?�2Y]�>������h}mm{kkqeee��-.//��N�a:�������$Z�G��o�d�r��*7XsQ67]].]����
}`�EQ#�I�{���b��b_3���x��L���i�^��qd��%��	�7'E�al:�c�aU@H��Kn`=�
�^%���'YK%��.��M�6&G�)�����(���K�)�g�a��'=�����$�4�������v���K�9n���f�.���P����Kku�U~|x<�	S�2f��O���=���P�lq�\���#� �����6�����1
�\
�b�[UH�%�-b������r�y��g������h?3�.o�n��O:'����vW��,�������b����1�8V>�WBl�(�af����,��3a�3�������N���!$V���-q��F�w�(EN��4G�6�R�n��3��`�����xN��1�3�K��/�^m��]�mvb����6i����&���<�J�0�[�	"�}�4����E���0�/�<Tl����g��);�z����\cG��Ar.���nm�A��h~9)2fLn	�c'>�=)+�O��O�e����\O�5'�c�����#��x[�D�>����@�*�;�4~>�����k>����M�{������XB���5�j�%�W[��f����$o8�`��!�[-x��UCA����ON�����w���owwj�����sp�i�
~��]+�Iw�W��.��IX�3�M���p��1��o����!��ve�w���
������D�u�Q��NU�67�W�[[��������^�m&�k�R�l�s)I�nm���t���ee���O�������q��8W�z�+�A�.��I����;�s��qrq6=_y;�_��8@���PJ��Vzd�e�����_Kn�q�)%�������X*Y��
�����1T�3
�+,�V�m�����X\?��"rl������1>=7u�����i�=4���g�c*���/�[����Fw�`v'������u��Y����	������V�Y���-��M��OG�������t��y�����~c}#9�������29�'�����Z���\�^o�BZ��e��S���=��^�%��L��Z����7�������G`y���f�]����HT�o���O�uVo�����p 
r�L(�F�7�xn����E��g�97t��A���A\����������[y�LB+K�~�(�����o��w���>>��8����*/���w>�&���mE^���0�{�
SR�=��`�/�O7�������-;tMJE�I ��?��.=�h������x6�9�yM��s���O-1J�ND�(ZRp<W�zN��������,����H*���L��,�e8�j����?�fnbj:�����<��v�D��X>�*fv����A��+%�������=[�t�(Yq|ftss��v���hPY�w�g�����c�U����t���|>�������O��Mm������&ku�{B���q����4x��u�Eb�]j�!���a���afi���$���<��g�"5�e�0u�[Gd7��5��VW����������!�������S
M�I��5�h�Zk��1�	k`������L[��"}~��0����_%�w������]X���������/.���~����w/.O�Q�l��|���3���e=h�w������^�dg��^v�����&���j8�2�O:��_8����
�TGW�o�����=���[��/�nS�)�^}�o������a�E���ll��:����"��xK|ssk����q�(F�F�iQ���J8�T�wY[�@�L3'�2S��=&����T
��0B�M|#����9c(�.��+�
�.2�������h�G�e#��z�����(�b��
c��cN����8�&���[��~��N)��4������`DU���L��c��Qj�CO�.�\N'����o�A���X2V���5��W���N�������qb����]�G��l�p�s���hi���d8�A�O��4�trK�������a���ci�m��b����#�6�[����/�����-V��W������a�w��Q������E�7�mn�
��5R��y��B:�{$��J�d������
���I�r)��-k�<�\�tU���m��s�&TaF�����;?}8�9��W�%'F9G>�����a^���p�U��z��t��o����e/�L����ty���}�o�������b��a����r��7�qlJmD3OY�jE'd��n'I^|�g�y}��f�����4Ba(�l�l��h���s�������t%0�)��(;��1����k(��Fs��'R��(#gv���N�KnP�y��"�U�
������v"�}�^�T'��ev���� JyA}.�%'|�1=c4/M����9��!�)����mo����vx��>�z��I.	�
qu��0�p��k��a�a�a��9-8��\�#��
��tpK�D@Z��4p'$�O?]��^X <2{��_fh%��G���~?5���y�D�(���/'s@�[6��H���'E7����d��&������������i�e�+eI�4������8��9���)%������/�������F���'��������'jm��R�B!�F�e]�F��	�-���)K:r_�6�`����?U!�K;��x����E!�FQH��%zl��E;����'�Se���t��}���)xtA�\U�Pmd�DI��_��N���i-�bK�KK��*����Un���g�:�*DI�.g^jK�����i�9C$U�G+�:�i�.T?]B���>�������#�G�s�������16�%.��w���fV��m�.�&���E��Y��8��2��`��tE\?�B�}����������*����-���������������V������������ +N�j�����N�
�Wv�����#:��}�9�9���b�~`��B�e�����c��d<&�k�]���e���(��*�h,����L��-}������wWK�H?��t��Z�an� 3��)�e����=�����7��Po�'t ���`�a���#R	�@��i�}v�~RXe�Q-������P�S���V�XV�M�����Hny�����sP�}f�h�G�R+�-��T����R,����Kg���d8���r� u���]P��V+������0�����t���b}��}������}zy�c ��QF�A
�I����1W��3b��%�%��W�D:�H����<����94��s���t{��d��v���2��p�+���������^���mTP5Bd��&���\��G6C��y~;�����Q{���e��d)	�����:������L6>�t���b
�/�M�������U/TK�zj�� [�W`���;1f��|x��j�5tG.�B�U�p�9{27\Ex5% �����2XjX���$�x@f���VA����+��*Oh��>I`�
�-Vj�~����G3���aa�����YV�b��o��7����>h�w������d�"����;���=w���g���+//d�.��]*k�(��$�/0�.�j7u�P�@5h��|��l����\~�Y4�T��T���:��	�]��:�Ph���*����dX2g,
��#�D,9���!r���T����L����Ndy��x��bf�����E_,���<B�y���u��C|j���k���W���E�7�/�9�)��x}1�&�7%�i�)��1����+h�	U;�P����7�Uje��Kn V
��QWL��p��x��
��H�22�bDN�+?�k)t�p�igx$=���5�<�B��p��+�������r�5To�C%���)�yw��j/�s�9:�h*CG�br�R(������vr�}�p8Oo�
�m<� k�Ln�^y�����q�!��q:�����'��p��s����`��]��A���d{rh�P�Aa!���?�JY�����ErG(�S?G��hf�3xwNv;�"K�����A24#����%UX�������> �������(d�J�i]���6��`6"#�^SW�
���r�j���5��b������ ~���V��IU���a�v���P=�P@P{�w�>l�/�g8c�������4B4������DVz���v����1���hu�{K�����	f����xb���A��P���^�q�N]�1:�%�4���q�U��~�
5[}��f����_}��A����k�oi�G����{�����Nw������i{�^��_�C����MO]+"-S���V�Z���T�D��Z}X��n�����oX���;�m>�h�����^�����?����Cl��\���n���.-��1X3�I�{�Eb���olo�'[�{#�\
��
�_l��h,In �*���ECm��<��E�\K<3V����t�p����;�T�(�/Z��I��7H����*���|�*bE��^O,e	ngW��
������J�rD��K��A'���Y�����~nq+I�*"�T�	�1��c��fJ�������_���/�_��}�������r�)fE���c��|3����t�\�m1H��.�u�K�v
����L��	������{��=�;��c<J���g�����m����s��OF����j���^��<#�����1��qoP�����m����dF���
��x�z��9��|<!�4�e\��(6�Jw�J�FYR�Q�9���eI��.%_%�������y���G�����X��i���F�-�'v��@�~�9@��
JI�q��^iN-�u��y��/
��U{-7}���1�������;���������t?�?�k��+wEPY\C,�.���#I�S<�fz`�'�����(5=
~��2�<�=�Hp^��g�-�bV�hD�~�H
l��%at�?���w��6�0��.��	��k$����#���=����������#=�����Yq.�*9�azK���}��"^x[�����H�����OIB%|��NK?����PN��'<DQ�&��U��:O�q6B�h�;������6�w���E4����\:��qH����{*����Kh?;Jy�<��E��}B����L��J���z��N���Ei��E`)�2d��{�u��W���e-H!��1���:�t�qtE��#��R��:��������G�~���
����I?!���~�@��:�(���2{�;�������������}^b���V?.x�SZ�EWyJ��1����s4��9d�����v�i�U����ow2���W����;�#63�Z��Jb�%����,v�I*c�����$�b�6�?�
:E�Y�������2�2�y���������A(;�B��`�����l,7.�* ���1������Fm����0r�*��s�?�P��d|d�~n�l�_�����*���Y1d���U5yG������	����
�w������g�3����kEk���MGj#*(ZS�V��%!��8���Q���:Z^N�K�JMG�q�ue�'���M���^�~n��������Rs���k.��ts�����d�3��j�f�x���J`O�h�3�P�����j��C�����4�U
<��pE
�j/��c�Op��
���	7��4PYC[-U�`�����������U����ZF�����u�
h��i
(jI(��1LJ%9�7i'wHS�G��rD�����c���x�3D�h�Z���~��V����j�b����p�_�{�����]�Qt�*u*��YJP�)��l�U���(0%M%y��_	��������%�	x���1�D�f�f?���A`q�Za�k�xBz	%�cY��=������q�z�9��m@�t7�"�����x���^��"��c���agO���;E�$�B}���>��g.��{���X�
�Q��w�g��KE��Aw���3�ft�:����C�68X����x<�"�?CdI�@��jM�o82�4Q�V"��g����'��Q�@9��@��	>�U�B��4�O�X��a�Q�+Jz{�6`�I�x���1��3@"�����m�j��er��^z��!�0F��Y_H�PBmr�$
��H:��gD��j�nm�������������Ic$f�s�����W���Q�-E�@����X��p��'�|��%��^��
����`k�O���V���ds�����>cq�t����Y��4f��y~:��~����v]+^�?��H��C��CB��YC+z{����?�32J�"FwY	k������)����C�0w!�<�0�.0E��2��B����@�M"[Hs=������ ��P
��A�7���}��RwVM4M�,�I;�f�W�7)^�k��o���X�OF�����}U���i�W�:��pBZ�����\����>$���H�������<����Ird+N�l�e�����������tm��~�C��-�"�O�I���@�O����8W:=N�d3HG�!��q����-�iX�[��P�(��N��O�jT�3�A}�J�=PuMW��o���H��_�8�$����
�zG%���%��l�����n�����&�g�~s�ma����!~����
�J����)C�"����lC��b���P��������0�%7��CT%h�L��������2��j��~���;�C������0�$���+%H��gz�����`�yj�TP����M����*��}��*��}�% ��(��[,w�zYb����v�:yt����i�������wNN�c��&l^\��/�����$\��,`���3��M�KDp�pn|`~���ix�d��5��9���&����G�o*�@�����J���
;�n������T:�6�H�6"���A���4��	�
�b�FL�,����j��`S���}�I	�K�?8��0j|wb���1�����
<�y/,�'��_����+���yF�����g�v+��D�a:�yp!xEp#���C\�?��$\�@�k�=+�� ��E]4���F�W�����_��,!P�}������5�Q��x�rU�WF�Fn���A�c�e�[��@m�'6c�sbMoQ���l.���ZQ����������_/(�(zjU��9Gv?{���\��}pxr�����%�3C.g+=�p����0�I.�V%tv���3��,4��������G�#g>����x�W��m��Y���P9�s����u����l����i�� }p��[����d<f=�����-w���.��N8C���������)QV>*p���lq��q����,r]T�oKt���o��{�
Q����}�>�7�����U����Gm�qB�5�i!j��r�V�E���;�R����-��M����g���U:`���<ZC������������*�|��`@�(k�S��5����F��OG��R\��4���=j��s�Wi��:�A:����}���#\����6��d%�O������pH
��x�=]�o&*f������\�7�������=�~Yhu]2^O*l����=e��L������~��Xy_�j,3�sEQ9K	?e�GQ�Y��������Y��\����5D���)it�����������vil=Y����J�a�����P���h:ZAP���mLg�z����+&��>�����X��p7��c�!t`
Lw>0V�%H���Jc���k��0CH�p�V�N�Y�kp�Gf��>�\��,������[��+)������Vk����l8Q��f��5U��PM8O��,j�&�'�0g]�L��U���4���f�x����k}��8	��t�t=W�B��f�������?;K�J�:��CBD��"�������I�dNDK�:�Y�e(�p�<���E
���yb)��(v_����T����5��,K�I�$F�T�=a�������b0y��e�I�!xU��L����1X�za���8=��3R.�s�3"�g��<����:@N��+�/rl��$`~kA������_�������_�7~��]��r0��9PI=�����b��a��7���,�n�j�E(2Q9U��Z�S�q�hx������	��J>���W�
�}��`����Q��+Q2Tr2���`0[�E��2�����6NY��hW2����p����V�%N����o	51��a�GV3�
(oX��t�D�2y�i]�SYIt`�[PxY+"��4�85����������R���Av�����{�� H�_Z]�2?����7�	T,bd���b�|�J�5���s``V��k��-������$�����[{I����!<�F��e�n#o���p�9iu+����)\N��hl�H����p�{x���l�e.����#����M��:����r�!}��*��5����}��A)�2���uW�mn�8�:|+(��������(���~��v��?c"�<�x�3j�	�������q��q���SJ���+�O2t�\O��X����r��^2��"=<�
e����sh(�1QO�NzK,�5��1���h�����;���-��SCW)k^�>s����vdv�mB��F ������~�67��?���	�
�2��8�J���������-W�N�f_Q �(u����U�yD���F��w��������RHCJ�I�;Q��@���JsJrsF��$3�p��gf�\9����:�+Y�+�I��c��F�l4N����!@�
?�!S������)b8�j�C'�������-c��)��obie^��\fS�*���>���*���,%�;���j����N�9�����4?w�;�=��Pe�Z�[4G�X�Mv�y8�2�-|&P���=M;����?'��)���%���^!a��{5kJ����*��� �IL�I{V���1��)�%-�q�+��#8�<�Z$�*g%����%���
	p?����*�hX��u��~j~�R#BM����g�8�Dq[f��K��l�\@.i��)ZnFa(�b�1e�hz������z��G|����*����!@�,
����U#�����`VT=*�!��,�-�����bZ
8�=%C���\���Z�"o��$��|w�����w�=Z^/��U�?'�6�����yr�����e��q��[JB�v�����R>�!���������*�5��'z)"����Q��y�>U������
\����~b�h���cE���d�b����@?��M��eJ���K,�	�*0�����3�Ni���cR�a�!���*V�d��(�Aa'm�H��d�[_I��S���j8��o�C��$���a�2���Ze��Or<�������D|�jT#&�/HH+-�Z}�
y��'�e� ?�I5}����[�������M@�*��
��c��i���U5�������:���DH�+]��<_����30�U��a�E{���S_k�
.��.h���}���y�F��<��Fj��!�/o�j�R_i�R>�:��m�?B��,�G]E������:`�[��_XY������=#7��N��m���f*���D��y�����F�U��*:�!s�n�b���
!p�K��������q*���I��C�o������TXd��>�������:O�	}IG~2U#o����K��2T{+�j`Ns�*�����P%�Y'��u�J9
�������8��c�8R*�nz5
p����Q��|}:��O�a�N�A)��II�|��hu
]�!@~P;���?��f��!0��uo����`�o7�\"�� �6��r��1�!�Bo��'���WU���V{kuIV��?�?��0S7�UAL�a:g�y���z��7�l�i�YL��>g��FI��L�a�qn�lS9U�����@���O�����6��\�!��X��|����9}��v�4�x��"�J���l���V�D�T�l;�T`���1jUJ��p�i��zY�������8�$��j��)<����l����nf�������
�MT���':P+L2�1��d@�|����"~�D-��%V�D97�3"�n�j�~��<��Ou>���9�tK�P �cJ� =x�\y��)��#|;�����,�7K�B����O�'Qz5��5A�k��&*����ev���wz��ac-������gVx�C�U��t��$��_�y�&��y����7bU�?��o�Z����>������L����l:��T�7���W�:|����%��������rK[2Sv���#�=��T`LJ���(GY&��L/������������@�v%L���������^���Q" n�S.����u|����0Nl��c��{�����9�_�� ����F^F�P�X{���Wh0XQ$����AvZ1I�VxQ�}cB�7��>��s�l��������I��8VV��`�u�
,��P�Y��=T���M��K��g�}�����/a��r�����������q�&��>~���]��#z�$�_����<4�B������X��0�y��M��n~)��_r��j@p���q�M��}��R�pt���u����i]Y'�'h
���*���AQs�MS���O9��@���7p���d���A��96�WY?=�m���*c�J������1nSDs2�I�Z8&[PjV��5x��r��+�>���q�����j����O������x�
��*��	�A|�q���_�4\���Q���9��6�`�����5��#|�J.�
�o^)�/���jE��SV���M��^�q�1�	q�ew�_�)�7��G�5�oG�M���U��c���[�TP8L�GD�N�"��,!^sV�����M��s���:�S�>����#�}rsi����p<wl.�0p���#-�����,��v��sw)���
������O����tZI�Yd����o���������_�#������tH�Y0o#Y9L���%M��q�%���U82�5���L��:<�qh����|��	��;��9���k�w��u���������1��������{J�E��e��I��X����*�5�Z$1��U� ��Q��w��Qw�k����H,���N����1�oy+�|%�a���t�Q���=*9k��? �J����Y�/���G��1��U��iO	E�9��J�M�����s
��p�:"'������-[������S��R1
/������\��A�a�]C�m	��	�k(�+���;Y���@����/� /�L��$',�E�X����\19�kt�w��O*S���Q6H{����$5V��Q��:�����V��C��?�}��q�#2Ka����C}�I�p>�~��Vd����h��3�H(A�����t)�1.0BL�Tga�NN^t2Q���zt�7��k�[JF6�����9;��k���Sc��\�6����G��?��$e��lf��@�8_���y�8v"����;����(�I�+m�&
"�/,���_U���c��1)��� /�������}"�[/:�X�	E\�$��?��m6���n���S <������;�\e<����q�C�������A��E�&U�h��n�
-�*���\Da�����e%�"H�N�-u&��%�o�`kuH6*�Z����������|�%nS��;?g{��iJ�#n{Z��|��'i���%jG�����/GL�q�,8���!n2;�]�P}����	#<#��)��Bxk�Q���S�qF���b;�%��1�"�7T�pp\>BB�2I��!����?��+ikv�l�p�R��G��$��9��Q~
���po��������u�x�����Cr�k��)Lh��E4:��]�c�0�>�4N�d�(W��KMs�����_o��tB#f:�y��Z\<��G�(��O�����Sp�[�Q���M>�z�
z�������j��qZ��r�J��!x�cS����5��'�T��M��xv�^���������]:����o��3��K�jd�<4��+��'�������N�#�&n{[Du}�t��k�W
|^�f=4Z��0�\�,/6�y��9�wQ�$R-9ht��q5�0�tdl<���4���Fr|����)����k�C�����,Y��v0�E����(�[�&������oW����7!j}��f�(v���w��T^z���L���3u�w�-�����T�g�Z#�
�����3%�
���X�6�_�>A�Xf=�^����]T)����X��x���d��������i��PQ���LAG�QC�2	�d	��
{i~�n���Z<�t��a�dt+C�,E���1������1����v�]���;X����t�(OK/��V�����V�w�RT#�L�n~_�|�g(���Z�^�dw�x���X�`$�V��x)"_�#zc��Z�g������r$?�H��B4�2��J]D�y������]�T?���f�tvR��dp��=�Y�
'W�H|�/%�w�t(��e����[.	����"�:W��E�[J�n�i�xSaJ�������b��C�����X����`�Q"�q�7&�
��B�T�H����[�/H/NY��YwA	+��7�ZQR(l�{���`��%0d��K�:\U�E�cx�(�.��d�5n������e�J�jg���nL��$��
�}�=���8�H�H�c��s��=�L=��g&OU����P�?z&>�����nJ��^2s�	7���E
���r�0E?�T��g��G�y��UK��3�Js1�����+?����g�������"�2�bX�;�p��}x���_�W����c�7�P+T���*��*������P������u3���;�5Q�A���G�B���iN�a�~��ti��Opd��WU�����Sz�*Jq���3
H��r�H-.���lLj�^��|����*=ff=Y��e�����U��/f���<�B�_��E�Z��k6�!��Da^a�D
�F�n���N.�g��~��!�S����U��-�1�kUZ�7�s�W� )������N8����#��N�P)+&�����
�������jy'��)R�6dr-,����x�heM<",v�T�F��]{NJ	��ior�H>���j�6g�����-�]�����D�/����b�}+���y9=-y<������|w����'w?'
�����$�����n��N*���P���a<�/��xc���Y<��1bP����e6�\�J��L]�^�#9G�3
�����|�O��b������X�<U������Nnz�h�[s+����TPV�������;������Gpb��b�HIp�����F����MH3�{{���+UZ�a�F� MGV��M�)���i���B�?K����w��q�z�������q�B<�
#5�*�
R��b�Y����e,T�M��=�~��y�&��u������f0��Z�8��E�3j��R�������r��oz����q�0����C������7���!.���
���L.
'L��6E����jD%�I�t������C�o��G�F���Y�p���4A��,P��L�D"�B����0�X?���\*)�(���J�5��L���>���� ��*�nAL��t�1*_��J�nA��3T�a�v�}���(�t��(��B9�8! *Rc���k�]�7���6q*��1�l��`[M�$I������b���$2�t�8q��0�n�)�r�m=#�He��?����(W��.�����J�H�����NG��*����&m�.���������)&�E�S���F����2�YQEn� ,�X{�����P����n����$�6�~b�
������f]'���\��Q*4��4�cA�	��
J=i��/�������W����UH���]%�f�O����(�SKF���'Y)�SY������
�����cdi��AJR��U3�p���#C\uSR0���-.x�#>�e�7��%�������w���k	(�w�O�d{w�1�?f�Y&���wC��~�?�_��?�%=����'v[n�,�~�����c���0	���g���%������y;��bL���t[����3N^]"�(�f�g
�P�����B��D���K�O�`X���E����U"l"��qe[�6�4����Y��/��y���`:���l��'�0����=��E���� ��[�gG�/���&��:�8�B��^H�N�h�P��-NRNt��>����N.z8��!���K�p��qb�t�;��H�(���3@����X�.6[�n3�S�X}/��'��c��w�����M=�Y��	���XrE�tK� <��<l3���v������[��
/3A+��-Eiw@�*����Hc��@����4������_e&�`eG,<~=��?�M�<�����,��~��E��&�U��~(CL��.���Su���
����F����NU$�
�c��"�%�h�]C���A �B�&n�����S��Qi XMPO�$�<XLn�UU:(aX���<���JuB}.�^�P����VP�~U��v��j���[4�`�h��l�N��P���B|�_7�2q�7??G|��5A�����[�B�������Nk�F�3���m!t-��Ym|(��G�Y���Y�4����o�����m���D	���A"e��"��5������DA���������|�����i.�2�D�C�#��zzR����(��8��';���o���-�t�'��J�������r�cp�
�.�
"E#��-��k-Y�Ipy[|&�\�hF����J+cQ�xCV�i
����+�9'�2v�],F����� E��T��T����|����U�����R���I�9as V�O����o\ Gd>�w���e�T�G��/%���%�b��at��q<h��p=�UG�{�0��[���[��K?8�c��$���k��<��~�e��/��2�$}.�5�^�=@{$�@�����	�iJ<���:�NV�����d�#'� x'�;��N<# �$����PU�����R�=Ij������x�)�����O)����,��6drA�����3�1��:"�#�Qg��)@�"��f�
�zB7�2��\Cvn��������S�7P���T��|�rj�D@tZ��Nq�t]�+�v��P���T
�V�\S�
�8�cK������%.V���	SO�"���~J|������]{8Wxj���\�"H��Q-����{%U����������6���6ak�~��m�s�"dVi:�<(6��m|f���(Tf�q��dBU�#��
���A�V8�D��h�/��k��}���YNa��!�&/c���x~�'�v��Q��dk�~M����6�!�W��)}���6��`o��*		�c!)A����{�	]n���T���MFr4��v�>��n[��wf$��������v!����lDn������sb����7U�s�IB�b�Ra	wti���r�h�%*-ay��SL����F~�L�[
������'���I���DT�����s�c��8<]�h��_^.y�o�O���@�G��0�yT��N��bZUD(��ce�z��a~��$BL�������/T���	\�q:��[�������l���:��5�@����&�S�2�	ep���=�N�;v6������F�8�����J�B?�{�N�\���*��y:������z��CI����8�����'�U�f	h�K�G�MIc��S�(����ud��d��h	+��ZY��I��\�Z�_i�*Wl�]���Yl,~�H�������
�b�����'�j���{�*!B�
&6�������d��O��a��V��<���`~�8O�ewh����(�/F&�]�6����v��#B�3S�!"F����.��0�1|^�����Vkk�z*�����=�v�|d��t�
�G^���%����Jxx���+j���~E����� �<T�td���4&����9QN��-�!fq��)� ������d�CK|���8nE���-�(���#�mq�����*�z0���q��q�PZNG�J5.&��Qn����}c��7�G(M)OM��=�>���)�,�8�
B��2��'1�P�B���h$�9t&3��������N�f�f����G7��>
�I�~�	��!�kEOZO(".��9���I|#6�y�������.��GG��O�/_�dW�����'&����G�w?���q��vAX]T�1����!����G�sg��K�o;*�&�Khi��I��zKK($Dv�s��+��"�e��9�L>�v��^���J�}�����x��#!J��$d�/����m��Cpy&} yp�����v����i��v�*���}���O���i~��%�����<pL��cI��+4{������z99�%��*�x1��2�A	�
A[������'���<��4�+�^h����m"��KT�y��2�:��'�dE7�M����^d�h��J*�!��q��M|e��u����z���=��"r��Q��h��zX��
Q=*��i@=����;�9%$�W��8D� ^L.�Q,�g�j�aQ3��{G��]��7	���L�^���x�G���8��L���
�3l	��+<�k���Qt�B=�p�Zi�6cn|�_kO*�BP��	�l��n�Q�9�>W����� s>�L�+��9���V%������Q��~��i��t��~��hg�H���pE�|�EN6W0�:H�G�g����dmP�
o}�0��sz)�p���@&�����B��U>��u�#g>��92{�]"�"E!a�;�:&d�@��;�:�_$�ua���|f���������w��lz�]���U����X��Ty�L,��7l�>�y�S�RT"�y�~i�Lh.�J��zc6].��Q`C"���+��d���=�.Ecv�^M�&Q[�r��J��z���*j�^�K$��K���5���'Z<=���'�Y�q��`���^�>��H��^w�����1����B7?6�>zX�w�+��Z�$��_����R
v�6���z�_����:��M'����jB1�$���S�%@�0��6��R��ST{���ZOmS
�_������b~E��/�b�r"�����ZsV?�:��#�:�=�����y[N�����<QK-Ly�Q"����s)�-��5|�J��O8��X���j
�{K�������P���T�JiP:����u�v�w>�O��]�����`�+�6�*��������{1��<����.�4�q���EBb�L��l�v\�{�\�]Fn�����N���vv�=�a��1	��?�w��=���6���sw�"1�P�D{���i*3g+�M{=V���5�H��sr&�C����d%��q�o��.���1K��^���6DI����`:����l���;P�0�2��9"D�%8/���c~������i�W��Wjt���?GF&��2b�"���-H���>�T�+�T5���������$���~�e�TAU���+0��x����J#��A�����o�]Fl#��sZi�LE_qsp���#�'&-�_�"]��h��4XP�����olQd�MZJW���1)�B����h��Wd�|�L%8K,^5GH&��rM63A�[E: �����B�:DU�����mq6�kuR2�KV�^�x��R@���/dI��oAp���1w�0��VL�g	X���-r�{JL�c�2$j5�?)wG@P0��f��-dEk>:�|���;�t����YS)��}�������j)����"�$1$������Y���:�M��fWL��R�)��_D����t��SEq�Z���;���-��p����k�\�����i��9O�7Ul�,B@���t�p������;�"`��V���-W��"�g~E.�vgA�v�_��������k�p#������i6�c�KHI%ce6)��U?��p�������j�����y���	��h�p�3�Z<AOr�BEmH"�����Q������G)�+��'�,!d��%^'N���Lx-x����������,���q��2������}�M�g�n���5S�}%*H �f�}�"�-���vX(
�L3�9�e�\��(�^r_��� � �p�V}�w2��/oS)��JJ�]8k���Z�=!�QRjq�tL^���pd��X�\�G�	<��o�gC�{��G�;[aq���o���@y��,noW�F�
J�'T�,���
$��My����vf�Dd�	XF���q�<��,��o��%RF"������B��W������2);�zUh�r-����/_8����4�oH��:Z��oi�
n|�e�16�H�h j�f@��c�mU9-��4���E�X�t���{�gy�b��+��$���\�� �q����P��Kd�e:.��pg�a��I�Fw����E�x-�eT�����)�������0�������L*�7��A���c�|�5��Rh�#����U�	�l�,%���TW�J���N���PbX�����8c��5N�������5�`���|(�,8���.������u�3A��0��P�k�nu���Y��w,�����?��a
���7��L�8������i�$�r��3���5H��l��������a�N"�5�M��g��E����<#����d�������(��v|�=�
T�z��"$����4��sB<�n���'��@"�C:(cJ���b�#����a���GIK<�#�Z�G�<�5u*��2�d�f����:���9�ayh���v�IY]P�^�sC��o�=��~1�j�
M5�-��R���A1U%�?		�:*�2
���[����GO�+O^�O	c����Fp����h!pr�*{��R8��j��`}wb1M�o��`�5�}�����������u��V8��>��F�e+o�]1/�.5}@>�|��%��0���)?��7�Op����F-}�?�.�.~��+=�;S;����L��30TD=��
t5Gg����ZjEA#N�yM��p;5[�ZN���Q`���x������N8��CX��#>���N���������E�/gA%���o^ A9�P��YJ\b�
�(s����!�&n�������_��;X@�����@�S��f�j9���8U@"����C���R9�k2�� ���
�k"F8D~�W
�6U�xpW('9�  ��H[%��\���x<
5��ZN��\�q����.�`�U���-�Bu��LQX��Lo��8�o�����������-��_�?�������#���z���g$y�gU�\�E=��,��3������K���%EU.��� ��G���*��^����g����p��SzX<�K�]<���|@���k���a�[:w��T��O����_t�,I#,(�F(�9qT�0�O{�����9�rey���	�������{��I��>�����Ydv�'[�T�,i��fJJY�����YJl��r�xI<t���l#���������D������S���k��I��Z%�E��I���%c����'{?8�c���av�����p?F���)��gS�Q")�9H�!��}�S����:`-\�������Yf��@�O `����j���'�J��JZ���=�Z\lO��0Z]�q�������i����s���O���7Rb�DG�F���>�*?�Iw�p�$z�H����U��g�S�A����Q����{t���	5B��/<���^�����7��?R�����O�5�I?"�]�W��v��8���&7��F���?-���(�]Mte5�J�J.�T{,��-s��'9u_��9{	�^Y�E\<����9����?�G�|G~nE�p6�O>z��9�t�F��������S�Oi���/�r�����c����_A�@�]��M�(�q����b'}B����������G*'����{�\�����<
�
�O����SK������:t)��H��ZDkt��&���\��MnG�|��!���.]m�ha�[�cY�8�O�r��mh����C�+���%D/��q4���I�R�-a��NH"���������4�n�P���N��I��4k��u{R��.�.��)>2j����X^�w�N��8�-�Hq��7��v��I_,x�I��U(E�`;�����n�\#��`��?����
���H�os���N�_>Y����[��H�@�������/���W,4�3�5��w��ox��d@�hH���	F/f�/:�&�[2��Z��L8��1vJCp�p�p��C}B�j1��j�I�/>K�h����?�����\L����!�@��������o���d��o��Y6���uzf���(��,~~L�b�:�D��W��%B{�C3?K��jM
~jL}m���u�����%�:8������������f+*nH=�
�B�t�a�&g�nr�u���O
��/��w�2�?��_�aF?h{�r�b4��Y��/~�AQ�F�����V8�^�����y��{9�u��N67��I�O;#}�=��{I<�t�QF�>eB,�����S"5K����d�_�_�Y���F[<�+`��+�c{l\]A�M$U]��
�C�@�W0C�{����5>��K!�~-D�*����o��w��w�7�Vw��j�CT�]S�-�� ��"i�p~yC��*w-QZZ)pUu�~t�7Rl��E)���0�@��H�	H��q/=����X�.��s�P�v2�0���
"	@H�#Ha"�� �M����������?����k[�����{l3e���`_k�-���.\X�}N��$
s�!��<b�J�Y%�%rh�P��~�Y����|���\���-M
18�8�A����jE���!s��_�m�����F���V�����������O�F�7*H��7�
&j��b��6E��A��6l���s�Z*��~�<|i��������/?t.rSY��Q�+QR�6���E[�'�r�*�RH��*����Lz�Y$�q���U]����X�;�[fHY��K��X~55|�|}��|tF\�Z��[~�_m�`1��J�Pq=���������c�e�����mx�i�����[bW�M���+ ���+�#���r3��j�L��i['F�6�U�� 9��
�����T ��`�WWI����[�gO��,s���)2��q?">����JKv�(�a��bw�����`G����J���wLy����!���A^���(�)�P]f�#.CC=��8��_.WO��k{��S�
0���QA��A����]#��
�d\��"'��g����F�����7s�*�5#�r�6D���fl3�=��]mbA�U�T��&/g���L1�;D\��V�%����(Xh�������|*�'aJ�
�}5�\�ua�	�;������YT_tv�8 Mj��7+R�Kn�Q�!�?�����������{�7�p}�oN�]����
d��@c{wh�UEg�*������Xtc��]�04�1#�����+�3_\�i����5�a]N7���}�"��6<u�k���K�����0�j��^"��o�h^l���������\�k�J���O�sW��}M�b*�(H�0H[���ir�>�	���|qm�0�B�M2D���"���B;jm&)�`�Z�w��D�����o�h�%j��dV��t� �7���T��z�5p[����~y=ag��)�����g����?������._!����6�����>��3����l�x���L���?�#��Yt�}b������|��lL�������K<l�z�^V���5�q���T���Um�RW�g(+�M,_R�T��K�FYuP�5'�*�]C����c`u��ApW��U�7���VN���{I�����t�w{D�}G��I�R�'@w���FI�g
����R��O�����nM��kvz�5dv��M�L~Itk��A'��z�8��~�?6��������on������>���H���l&3�,gYvT�e�$o�7O����
	p��v����y�>�h����;�w��b�h4������;��g?��s$���r�`���8���������jq���5��$Gq���O�v����;;|���h���,������8m�^E����������~�*�"��������q�����	&�6R��1�$�[�@������������S�67��1�{�q!��	�>�[�3�����Y�S�����6�n�($���qU ���������p�����}�_^&��W`y\W��lxLE�-� �mg�\��h�v�b� ��'O����
�0|b�����WO���/�7�����I��1�H6������_��l?>I�v����?%���������kS_����9:��m�9��� �������|������WUv������������*��=}quW�q���w�Q3�>.���(�F�/��k�u���u�����b<]�M0���;��jH8z�X���z�,O�	�'�N~B5�3��][L���y��� [4*�YK��HC�'p�&�~���?�DTS|���E���F������_��]g��?`G���
�w��-'#k�Qu����[5��S*�	����
�O��5r��s��9�^������e��a�#����T)�&Q��I:����>l*A�4�F�����k@�\�/iu��s���B�QIz{� �|��.8�w���hF� ������x��=�z�b?
X��Z�������*9�d���Z�g9�V/�]d�3�F<.�(������X����F+-w?r��c���+��r�Q#��0�J��.x�i
��B��?3N|�(� ��(���I���4VZ.�I!f,k05���*������oK#���a��:��1�%y%���y����#"��a~���&�\�C�pi��<�������y�#&"ix�u�*2��=��Km�}��K#�&��8{pvvtz>B������'W��(�tD��9[tj[�y�G�����[g�C�O����?����ZS�k����v5ip�����Y;�?`�y��G�������X�?t�#�A������i���}�P�4������-��l�?Xx���8��:w,�!'Y�<������f��r�6�(��PA��/��m�e���K���
��{� ��6.��b��Y�A��1C�S?���U���i����x\.��Er����,T	vh��%KqF=<��L�$=]qt�>O���
w�k��S�\��u^����m�E�Z�8����%�/B)[Id O��xu���[|��o��V�>�5����8l��79������H+�Gl_��JBn� d�����1��ZC��hA���L�p�aaF0_k����b�3��16�g�����t�v�|�g��G0��F��b@{��"?�'+[��V������5Vt;i~w�zG�Xu8n���s��rt�\}:si���,!��vb8��������9�/��G9>6	��I�%�=��I�j��?���%�,*~l����o��d|< �<�qo�*K=��c�F>�����;��D�M����Y����L���"������=�������b���������/Do���3��������_
����>����������H�����|�������	���~X��K)h��:=��IP����������+/NCP��*�2���o���7����iQ�<�������^Q���>A'R��hh	��],.�Bj�z?-����`�*���f��7����+��4�$��~�?*���/DT3<��y9��W�:��2|����E��xeT�	��T���ODhP{�#d��$������{+/���3��c�����8�������-N_p)��C��f|m���v�fF[��.%����(�*�T����Z;���6�I��[���b��R��L6��f����\J��0ZD�Y!�!�t���9tsC`D�B�K����������7�b��w����F|z���r`gC�>�v34�t����������$�m�2lr���tg���+����E�����s�f�m�a�����g6�]7W�]��f��������h�w�$���D8��L���E���*
��5�����nL��lygSk�{7u�S�<�8�;�x��tG��Ky��k�
Jl��<��Q��C{T��j
��+��h�z���v�>E�T��i�/���Y�1�j���O�/�����.�-8�������������}Q���%a���m9~K����Q	�S�������v��I�� ���z��=�.����`7�N,#���8�������ws��>p�qG8�@M���t�I����{����N�|d�Q��o���-2+X�V�� ���]
Kt��U�;jA�~Jj�� wn��=��'H�����=�����A�k}�Ioe�75Z7��rw�/����$�s�7H}����*�;�����M�8>K��O��'��S��O����;�*$o^$�oN�NO��1H��Ba.hFrVU^�������<��#�Jh�V"yq��kH
1�v����Gb:
s�1�<D��f�����
`�VJ��������*'�q�5�8'�B����^��~q�hl|})]��v[���u<E�G��7��sd`?7}|���]U<% ��C���Y�U�����~�cYQ���G�iTC�����8��5�()��QQ4;D�J'L��_�\�����������ZA�wQ����xc��t�	��*1&�����������.7���zG[+,���o��N��!�-J��U��Bz$���(#����n��}��`�����-�I��[7C�5*Z�R�e���v��p��q�ms�C���"!�
��2���q�M��(��S����6d�����~y�)v�@�Q�he�/��OF/���x�������������7��^y?c��?�jo��8m�8�����;���9�/�3�(�����ni	!��G?�����;Id���M�$�,Pb�d]%'��&X��`�iN���'u3�j$��a��nr�l������$�uP�����K��.�"w"�&"u���n�i�*���+�vn�[��r
���I�r�2�haJB�+��bA������0d��Q�4�1�
s
��m��<;��,��F�B$"�s���]YXl���`�0@�����R&bu���,���0oy����C�a�U��>��;x_���R|��q����������I�A/��8M�|�Q���#�G8��
6�!D�v�Hq!�f*V
��n��d���ZUw�
c0j<��,2��������X"b���������n��f����5D�a���/�NG���H��2BZ���#�"5���M����3��RZ�F��������5l�f� 	]6J><L�!��@vc�~��
d�DC�|�)����?JL���c�	m�_
Q�;���
��Q�H
P�����Ii��6"����p�,�&-<�'�u��Gfd��6c�S�PB:n���>��	�
g��Bl����<�^��O�|���������[SD�2�f���2�O�rd�Z
���H��k���>��D�#z��v'��\UF41��NCAg������F���C�p	����[���\^]������+�A�y���Gr#�DBIb-��9����X���dS(+	�@�-h'�Mc8/��8���h�D>H��U�u�]���H�0�v�vt�	0���v\//l+����K����0���G����3�G������k��@���M$?fq-IC�
�b���D>��7�62��'��]w����$?6O�*��������Z�$y��oir�T���U�#A����_�I�L�^	a#������p��6�/�p%�l+���������������>o��m�����t-���Je�Pc+�$&
-��n�����o��DC�X���{}����tJ�i��0�?N�(�q�p��T�eA���j���8�T�Z$C&�^x�x��%����O��$b�
�Y2�'�)F9P����[����N�tQj=C:Re��i�0h�.�����J�����:C�i�>���!g�b�@+XA�1���&~�6z�
��T���G�M��5��G���1�j��K��1�>{� �>���a�$�(���`��"�W��Uq�����Y�H��\�/�~s�O�zU�+�����r���.%�+�g9E�U���b��G���md��rA%.0M����q/F���!x�0?����u�����RT���R�N%2`Pr��i��o�,��L�[a���#�P��0y|����Z�J��cE�"�0����q6��N�6��x�dI9)��g�tc�Q�����R��"��s|���O��	 Y�Z��	��� ��.��:������������=���g��I(�0�k�?��hZ�iBDw�Fu�7t��^j���qT������3��(������u?�y-{F|��%�7��bNQ�����I��&�I8mS{���XJ���,��,V���S��	��1�h$���N�[�c'�p�%�'�:}|]�
��3����x���f�c�!���<{+�@���^��t�Y��bhgx��?}�E���b�Q�n�������U}a�o?�P;	~v7���<��%�	_�<;yk�2sV+�~:����{jRD�U6��}��
6�~_��!.o�����E(��*�<����F�R����r,���]��N�z�-��]���z����
���E���a����s��#�1����R�����'�+��������S���.@O4�2>���4n����Uz�A�����:�Nk�'��sl���1�W�<��!Du?�I�-=xv�6������N�Y����
�.�p�"QS��EWe�����A�3����r����k�.���F�.�65������+G�����:�o�y�����L�/�/�Oa�p����cP��F�\�Z�U��A�2)2�L���D�&CiVM�����TB��1�f�6@�E���K�m��7��Ua�����]������$���Q|	t�Jrd��s�@�x��v�2���TBu0Qh�&UP��E��x������N��v����2�AFi�X��i����iD���#e"JcE��z�(�fn��M.��
X�7���F+���ix� �;R���������
�����[,�C���U�;��|	W�����Dz���6��)�
�
����`tWyQ��b@�c�+�����r2���������s	������
��f4D6��e��rou���,�
���-}�Lcli�XlHI@�}�%�3����fi:����\�n��KJ�$L�Mz[C�;�>�_���W"k��`l�u��{h�6��
z��>Ar��� ���eY�_x���Flko�|�
�o9�>R�2��y���_X��Q��=���y���>���+o?�O�,�#
>)�h/h`7��^H
Nk;������BzOaSl�Y3��@���ya����w�a2�L����a����f��`&f�N5n3� ��B<|�w�i+X�ki3{���0b%��Z��0��o����:�����I)�
�����]f{{.;�o�
�e���O�[P�[��	������>��n��^��(����"���.�t�c���3������4n���q��k��8�������+h������������3��et��(�+���e�`V���8���`�Gv��y7��0�,�H��O�_
=`��#�Y�
���hF��f��L�^�ES2�B �"�U�l��7���J�j�T�<=�/
������h��vz�n����Rw���==��cz��Nd
�a��?������������tB�f�uEDT(c|�F�0jv�wf� ���{6�*����[��Q����&�e����1RB�"�v��o~��QrDN(���d���q��)�b6TRi��4�a-�c
L�-�{���������f��g�7'��=�J����tOR��������8��8������1��o�������;+�Z���|khl@��$ �-�H��L]`��~}�Z|d�=������s�^Yz�Z;�&tv���H��!<Q���K��d�����,T�Hn��!�b
����m�#7
PK�����&�.�������R��3���HD�F��AMn�a�"��
����R�����b�����������'v.��J���~{��:����;��@�s��l�_m���%��*�xH�B�U��������E�\�P���s��0�oa�����]��M�����tQz��������H�����B��z��E���ex�	%L�m�.w5R�������j;d�kP���=�n�a���GL�A��M&���$�V�����.�+�A�E���r�3?���/���'�WB�t�\�L��RlmL�����|����.�+����������81����4�r�@W����m�����#QL��#��p8����-�������/��qkJ�XZ_P��U��@���������m-��E�g���B��(7P�XC� ���7��i���T8���I51;jj:te��!V�Q���FS���M�����(�(J�A�{`ei��G��X�����^9�@��m~R����]<�W%�.��&���V���OQ���-�N����4��9�[B�,�����
�����h��-
���F�� R�s���/��>������p{;]��P����ZG9\U,�}�04�gF8�~IB���{&D�����OM�����4�����M��,Q�9�.���;Q^xh�������i����s�]��8��
�F��ltZ��������=������q����CH����P�.3r\�q�AW�Se+y�V9l���.�+ ���*J�#�&i�R�E<�����i�f[w��0%	#�4)iz����G`����^4������imB����^������d��z�IP����7t$���������\q�v��W�~�E�8����f5�Hq�R��fF�F������7%&8����"4H|
��Yz�nD4R#�w�,�5�N�."�''�>��=+��~����!~_�.�������9�FvJ�B�����@!��
���� �������Y�^���N�6��0�����
������`4����;�
��kR����O������G/(v�tV�}xd�W��nw�$Q��qx�
eF5=wu4hrO�o ED"�����R�"����[I2-R\:�U���(qf�N���G#��SvB��������t3u�����j�m�����N��:G��I�����M����+��7�.<�I�_�
5}��������&D�h���=������^5ey���J������q_�����LtH�_2h���z{C��UZ@N=�*`Z�����l�D�A�����B"2�ZZU)y��5��(��8���0�BNb��}���Rboy��WQfG��z�"����������a@P���
��*���l��zf{�^�M_��.������]��R'�%^����N��w�A=��
�x*�7�~����`wl�����c�D�=����`��B�
@��g>�[B�K�<��2��������v�;��^�(��)
��r
�g���?�C��h
#^.��0��? )V$��-�ui��T���Q�?�����<�3I��h7���=�iiv��=u;4X)����RpW��,���%��
�>���q�5=��`�^#(�U�P��)�����2�W���j�<��#l�����.�-�0N����?�������=������z�g�1��\�������7#&���	�Q�8��]�x_c���j��A��[%�����bIx�^I;�,���q,�!�t!�������M|;Ivz���0b����D�t�j<\�������[�:e��b(s=vBn�������E6���a4��f��	���!FV[���
p!
+3K��v��8OF,6���p�����:7���_K�� J�=[&�e;/�.X��G�+��p��+�{M��f����\��6����6``m���\�WI���~|V2���F�����"S)�������dY�I�ks��\�&o��A��4�
0��rYR
+ �Z���i9�B(A=��e�����=k/,����]�t�*������"�h�-��y�^V�nY�z@@7�w��*/�f����$gX��+�@4�Y5��>�`"�U�CT��,���=��f�A����Hn�o�z������1�~�������_����jJ�:���@����U�����N�V�����&����CP4�4�7L'��s�Fh�s������N"Z�
kT���)�T�<�"(���:�x8����G�)�a�Z8?a)��#����R��'C)�FC���W/�xR%����.H�g{f	1E8h�y*�I������H3�#����%�����������_C���'J��#�X��<\�] 9�0,��=���n�����7��f�2}��S�����&�3�����.6Q���_��1�9-�b�XA����	�����1)�l.�-;&)t�5�7�A4��u&a���pz�T+]�������S�2���;R�����J�R�D��4����/J�����%<����h�8R(�PZ��pXS^�c��W�����e����-�}S<�%O���mT2#�x������z(*�=��L�8Ld-���4�x=����}}a��������y!;���N	~p���!eI@�C���L����L8��-��{�����?w��>4@Z`�h6���^���8<��eE�=�������o��z��'��M,Fq����������9�a����U�������Q���dF4�!
*_�]�0���V�<��ZW.���U���0G>�\GgA�I��T�P���g�<#E����a"cY���~�b*��IMm�4WT<6WYj=�_���cv=&H����'F�h�T���I�a>�<1BG�#4��q���k����c�����`���
b�UuM��~]����Eu���*�����>���Vj�kw1#�<�a����d�����^o�8�tF*;���3�`���u9��[L���;�wF��/_B�������!������)�D�g������������!Ef��/�mQ��
�������G�cU������=�n��s+��Krr!w$��12doZj���
L�����r��y=*��o�Hj���S�	�o��1�@�k�K\��O�q�����*LP
�T�;�k(����gvt��^/�������-3�Y33a��G5�=Jon�$��K�^�����j^{���F{��5����Fo��6~��.�^C�\.�:k�5K'���>�m���!ue�4H�R���J�\�@��-r�8$�h{�L������&nEo%,!eY ��7<it�k!~�>����~T;�a��rSe�%�,�4���Ik�cR4�!����M�!2��:��h�����2��S�#�;���������{^R���sv��*��&������x����_?�� �q��y��5�������(�D���z5(8h*���7�����I�.��S�!�����	u�����g^^�~rz����������������������@����D!<3�{������3/�q(���>#������7��������	F��������h���OZ�w�K��O�^����p|8:>9>�7Z��H����Hv�����f���j�k��U�no���6��??z�v���/_=
�z���o6������m�z#�b��c�2��������Q�;<s?a�M�T�lf��,�rQB�2����Z��-h�3����fG&f�5Z�nhV���l`����rp���������P��mb���r���`�-�)+\z��=C�S�wV��^Fc�����OAY3O'�st�������_��u�J�~:�������9��Qw?~s)i�����4�����P�t�����ca������F8�_�m67�������:3Gu|}^��9��%�=�f�
���3<��V������ �[����g�<�U}�I���?����6;ro�z����S
oRl^�\���;���-��+��5L���^�����8_����������C�n����bR�Ip�����.D�����A���CCT'���l��r�(!��ynZ�,z��V-s�����"`$eq��O��l�`J ��T��E��fV���Fjj���h\��#�sI7�j"�>����,\������(��@(�C�y.���0I�{^��+s�4�u
��(.��v�H(����VTO��r���x4�Ez52o��&<������Uu<��j�g���=[c�<���F���,�Cz����p>�OZ(���[������Ry#�B�7LP�=�����E��^.�[C%��k����Q���[RI(�`��
xM�)�Q��Bk����0e=Q:����[����I�Y��^>��/���zz~����_�;;���uob�p`�)�"����GU��g�c+���a���7��B[���������:���������fk��z������G�&tM6<4�e�d�$b$�@�q�R����~�Ht(���:��_�B����j�l;�U��#0���"��M	�j��+:��G���
`��w������V�%����E�l�O~���S��b���,<�&��y��%*��R�����1�j�����{�I������E��[����v��fm-Xi�W�%qK3s��h���&p�&������!�����H#���?lT���ort/�F{�hr�!�$�7�/D9���s2G�l�X�C��v���b;a�Q��e;.]��n?i)���q_=1��rQ}�k�	 c�e�6�����u]�s�&�QD�,
�jyM���~�M����"�|��S6oj$�)����������g����/#H����UJ���r
��#�~.�������I�p0|g4_����
]�}��"��*�S�@�W�J�WfS�N�g�<���l�����y����z��t������CqC��S����������~e���G���G�g�g��!���e�)��w���C���b� +r�#����f���>��.�����y����
����w�,[�;n9Y����j�,&����IZV�/��7%�-w`�#���5���XO,��S�� ���K{���t�z����r��Xb�����5�`���Wu��>X�G?�z��|������i�.����Pf�k�~�,).����K_�t�b��~@A���aK��QJci�{O:l��*5"�"�X�
?;VRIj_ke�"M���w��k���B���7T����
�u��j�����Z-��&[�I��t������-?������r������'}���d�`��o�(������
���2���T�/Y�A�qA��F63[^_�z-!Om@�#���0����1D�����Hg^d�,xa|�&��-n}�����?��"p��-���G'P]����i�&����*�1y9<YC��D��-�!d�	��
�L��i(U
���d��0N��N�"��G���z :��V��5gf��VU�9�@�a��,a�@$/Lc�5%!<�r�(�DW�������0I��K%�hKg<��k�^���&UGB�}���b����t�OB�MJ���2	a�8
Yz?�ZzY�Mb���	����?p��
#�t�����[3���nj?"!�n�^��k7��^��
������K<�Z���C��b;�
,L��%�R9{6�9�Tj�[�I����MP�w�
����>������]u�^��]-d���T�D/����w�X@�;�J���8�e"�p��z���!.�~@d#��^�Y^��-��%P�*�Xns�%�8V�-2J�K�xK�=�T8w��@�$q,dL��G���n����K�U��8� ��D��a����������t�A���gc,��
�C������~���l���c!��m18����+�gj��=5��l�������m]��
P#�� >�N�8����L�7X�!b&�$Z]-�*5���~�@Ijb~9���I���nHb�AE���G��7!���+��+�%��oI)��92{�@�R��)��N#�M����B�'.�\�;�ou1�E�0��U�;��uv�QP����������2��/S������
�:�����9����n���*!�����~S��� ����F �A�+I����L�4�W���l(�����w�����bW�b���!D�q]/Ig=]�DT�v�������`�k������]s�P��t$41`�.��l�C+z3�>'fM���"�6�i�!]�`C��1�+o
��s7���<)W��W>������jEB�1���U�E�<�y��t�"�=���P����������@B��������O -�M�0�e���O��NY�%D`����[f�l�ll�*
������K@�r��N���~�L�|����6^�/<�g��d���d���$s��uLm��|MsIqT�N�"��7}���8�a����EVP*��m$�:�%����v���1��
FeD��N�#n�Q�-�#��HO�����)�0��c'Y:��8�7]�|�_o;:o�����t{�gQ����{�].��"�$�R8��x�_j����sC���x�i���
|=�(���N�����s��.�^��{^�}����Oi��wf
�c�qJ�"
VFC+'�
�����e>�p�����,>���9��`Q+s��L�����pu���;S��-���eDm%+}���C��oN1��R�
PPD]�=c�+��Z�
�*�
���5�Q=r����g���
G���7T��G�rD�*�NZ�k��6 .�!�{{�v�"��d8S���)/
���r�|WS)P��|�CP��M?��b7|����o&[�����%$a^jD��_���H9�����&�IUkh9����k��]�x�P��J���N)�^C�,��\2���%5j
�GygH��Q�0�F6Wl}]�x����\�Y=�x�d�/S��1�KZ���%ga�C���,� M�@'7'�mM������������<dh���v|]��������!b�E��l���_���F;�� ����pC��	[G��d��#"�
a�R�I���� 7�N�� ��9?R�����]���ls(Hp)U��I�8���@��-����RK�m��a�������[�@�Jx�\����3I�x6��m���?Z����M�E����z
*��o���Cg(�g�6��%�Y�[��8��������g~�
����M��M�d#IT����O�
C4h%
�?���u��v
��kg��^CU��6���*����F/��=�j	�)����-��������x��U�����X�"*0�$'���Co�#20J��e���T*F�q�.������b #�-d��ia�lIw)Zp1����G����
�[tD�Sz�2�w7w��k��H�!�+��N��p���<�mrP@������=����V�[����2���C	)0�;b����W�$�S�!"OPF1�;b��S�89��
^��VSs}'(3��#��@	dl�~l��������`������H�;����dz�3P4����9|���%��/I������L@A���4�.����,�,0'����Xlg�:/�6\\xB�G1���x|%���%X�����WE����\N�Z�A�
o��	�R5-��P&��E����9�QpMz����������b� ���b��������|��2�������>.5n����	�����L`!������G��X/gX����&%�����L�W �!��9�{��:BN�_FV����:X1k0Y?��5c'_/K���2��g�{��T
g�8}Z&�uG����m�T��W����%��^�;p�f�
�+�6q���(��Eg�
%o�1�+`+�����YP��3������aH��?���vn/<Fj��%�xP�DkWX8�8�,1�oT.��;[�Kt�\@��+ �p! IF��I���lT��m��g�dp���ho��HF��cc
�P����3��U�3t8�i���� 4�c�\(Rc��Cs9��P[l@�1�RC~9"�F	��&u��gS�����}�
��r���B�E�W>�N�s�/��������l��~�����Q�Z��E� "^����G�cl5���k���E�u��lP����jh�������Ns�:��{�Y�������P�"�$�P����T�����C(
v�������pf� ��4g�q�?!��:��X��C7}|5�~}���*ZSyc�W�i��V�7���wsoI�J�|�� ���A#oI�W���}���?�=x�������WIzo������?���0i�}JJ�'#S��4qp�)��d|�-f44CY���(�'���
�0By�jm���������r����q;��h�M[H~����q8��t�^�;/p��U�h�c�#+�,����uP����b�h1A,�Q�X� ��l6�|��6���xt��Z�C/����#4w�\��$!6^"�Ua���+P�<�BO�	���A��{#U)iI�����T�j�t
���'�� U=`1t��:,(��5���=kS 
�&��g��,�7B�����u���^��5k����������"R$�K�uv����|��Z��.5��Bl�%x������4�R�l��ru�58cy�7�KG�oD�f�����O���0�bJ�i5#��tN��"��5<w��W�.���I����0�#���0yA@��c8zy����@��q�}��N���wq����v�*�MU��9��+�/��
:�^��<�j���4��P���
#�7�2����o���L����&}�"�i�	7�n@��hP5=#��y6�E�H�	"��P�P�3��@�T�XR��#��+�_��
�������2����24"�c�`��D/>�����_�7L��tuz1�zO���-U�w"��V��Q�v*�{�����V���P_R*T�l�{��}���L�i��V-�b�]dC���E�F����_e=��	���o�b�_f��G��a���5A�
 K�3�h�6�����q
�I	:�FtQ���uY�{"�m�Q�

cg![���U�S��{�V�L�������7�X����z*4��ZQ�����b���7���p
�������9����>�����]�;N��7J�g E�hg�f�I�����y��G��H�h��K�h�N:q�K�3�-q(g�zm�-�yMZ��p,�m����'��._������M�������G}�
{Gp��R-k�`��L���rK��z������������~R^�~�'�{�P����������|��m��{������_�}���2/w\K�����*���AC�����!j�5��Z�C�=���BhHr������=�������	�:_��7_���	7�=����@��,�����n�N�/���6���u�������7�az=N��V�_[y�W����������y��qn���P�	s� Vi�~{�����a
m��x��������������%?������[^���[�x'���ZD�������7��4��d�c)�q�99�	q�������9P'{^ea{���u��O��;���Xm�l���p_��I���H8����x4.���;d�vH;A�e]��;�Z���Oa�H�{Z[��Ex��0�f���"�j[3�_H-��a�H�`�.Xw#���9M��}�E@�y��P��4�����|���7�GW���

�V��6���~�_�������]@C�����b�]�*���$��@}����:�R����G/^���-&6�i�=p�('�)����+O�S��@��!�����aK>���������{1����������|T@Lc�����������P6&���Vy�V�#���P7m������J�����)�$9VY�������@Y��ns���R"�:| H	�P����C�|�����?�|��H�,����y$����]jN�92�1�b��n4<GO7e��j_K`!�g���5�sp__����.NzSm�G5���	��~P] ��R(i-�4���o%{:�G+���`��(�:e8)��	���dF��|#����BY�AC���iM3tz"��/:����zD�IxGK��A����67V�
�`Rf��5� �r�h�hEWuJ�]T**���}=z]�����B���lV�Ri��O�GY9�?,���I��-Z�� �
��b��V�:�[,���_�p_dI.���o���<�����"-�Hb����nd�E.��4�c��4�/	�$$�^1�����~�:��U����3��&��6�C���������y�3��o��W�0�"������<����#����%�����]D8SX�[>��!���G|���E��B�'���	i��O?z��VP�����=Q	���0���Ga2���
���R��c��8|z��Z����sSF.8C�pIt!����T�`_ ���u1��(��8�\��!0��pk���k"�4���=B)�IF��

���J�C�5�����"Idb*���4#�:a��W���*�zQ	��
���[@ ���%m��IN�7l ���'�DH��������c<�O��^a	��qnh���5:8y�1���h�H.T�U��3��
o�s7�$?Y���3�i�p?�1,��D6��=!J���O���e��8���^����Gq���Hb�\������� �N�h��$\�Z�q���@��(DH��4#���e���zZ�Mwu�����g��_['K~m�&Y�m�%c���$_���	G�:/�#���VtX/pQ�Y�2�$_����.�-�����������?�N����y p�''o�_�1�$3<��\}3@�+�Us���'���?�PQ	�}����	 ��H(�5�V��a�~,�_e�������*�6���Rt���U\n�ND�����9����r��F����VGO,�yG�B�i�P�q�,��^��r��f��^�2���]������#�)�HB&<�A"�
P�C�vL����tL�����h��$dT��i��u%���G����'����XT0^2�w�����V$��l����b�`4J6c!d�%�_P����]��E8�8A��8�Q�\�����+E�Dd���	F�����<���f���|��%
`������9�T�{
?��3�9������F��V����!���LE�Xk�n�(�����kMb�9D�k����������r|Y]��W��;��H����J�0�N�D�B�"HD��2����Q2��]{c�/4�F��z��>D/����@�M�cz�yWdO�I�i�%,+s�m��a�u�6���x�U����*��:���7��i����i��+MSs���8��5�l&�.FH%,5����c4����M22��8l�p����Y_x��LI���rW{���2qe$8BCM	{���i��[
��8R>C��>�/ ���UY?�]��/������(o5�r�+������)�r'������X�Oct��������y�J��������(O'����W�������������0���6c��l�����:�6[[ ^-�L��e�(oki���1��������[%F�-EA�H�"-;���)4��/�����k����q�y������^�����X�5��5���*�O+����^����-�>���K���e_���V�@ ���Ik2�A��J)����T�Y,vs�**�.��y��A�6f��(��A5[��������������B�+�	��]2�������p��!&��9���y�?�!��� �6kp�q��)��x+�ZI#|�^I4�����B#������ZA�Ck�\��T�;������Z��D����;�D�r;u�N����*@�D
�����K�TgV�&��p(�(���.�|�l'�8�s������B��N�H��'�B���h7�������k8*���9$�p��NA������~����u~|��GK�M���Ta�QS��L	�y YJ����y12�&u=�Qg+I~�rat&�w�i7���&� +%nP��h<K*!R����C6^Rp�TH ���R�:*�������W%P��d$�:�w�-+8Q$e�]/_����f�#hg+����'�$����
�q[N�=�����-��?o��G��� �/2�
�`�����`��lb���l��p�|�{#M/m�r1�������'��F�hFu�hr�����
�o<p�������Wg' �	�:~9�n�#5��H  ��>��@���������8P���}������\��!:��B�S��Y@��.��Uf�x���4����8�K�
�+�����j�yf�P|ka_�Z���~4)��2$i�1)s�\���z��@�+�7��3�U�|�=
��Aj�f?M�������j�a�H���a��w���R��4�"�xD�43��(�G��=2���� ����UG�8u$+bU��}�UFZ �y%6B.��+���<�[v	V���(_���N�l�0 I����������pY\����8����J���*&an��O��r�&�1�D�p{�OE�J���K�$4"�; ��r�8>z<�����!����jsM��>~ms�������'c�-s������QO���9��e��w�\o6@��7���;Z�������Sv���c4��i_���=�j��rA
N�2�-������7�@fk1�sd�s�!���������'��F�sq�(����.|�+��s�lz<��9�����p&�e/��@'�k�i-���J2�3��x��!4�)��jN2�����������r��'^k�@U��������^��X����s���j3��
���� b�6�l~_��t���^��0��tm���BH�X9��
B��g�^ss3g�a�Yp�Kj#�`w�j�����'m�DJo
b�~&d8�3�
$�ccRf��e����Om�9;U�C�G?
���~������!%�Hb�>$������9`~@�;��5>�8Zx_�
��y�.5[��4{�M]Y��WP�e����K~by���20��Y���1	��'a/�y�5�}^�A�AN�D��cHCb��Y�1%�
��1��J�8��e����+�+}��	����C�B��u9����T�h%��B
N�����FXT�MrB@D�HH.���@��to�-��l>j
��x���D����P�$��!iJ�:�5��{��S���,���jV����J�n�Z�\�w���V�M�����s�;���������WG'����f*�d�S��V��l����``nk�mS�Q�j���V��I`����X��Pk2�8I����z�
�����"J���f�6�aQ�>eO���
�O��I�*���(��zY��k^�����V����H�$Eg����Z����.�FM�f�l�����,�:0�R�Z���" ��~ZW�~SP761����9���gX��__<�����M��p�=Rk�
M��^��q:GD#�N}��_�jfB��R��o�����v�7�7���|����@��,<Y���:��T1�#��tp��!�}
X� M.����
�Jk{B�.��-��f��b@�R�,��}9��R���*�_��BW4����*�>��P"��,��d&��{R\�N��7ms��EX��UT�Y�L��KN<��r��+nR��ReI��g^^�_��m�(DH��y�K~%�C�;B���g���*���������� �l��s��#`h9WL�DZU�
P�������>��r�C�
C���������z,��A1��e��(l��D��6����}�� *�~���0�9<jxY���|�����c����������������I����=�>���Z�����TN�w��3�y�q��A��b���B�c�"��N�GV���P��Id�Vi��g�����"�\a�Y]����`�	���O��!�+R5���%��BW��:� ^H�b�H���9 �=�A��-���\l��T9��Qy��e1�aV6,<���	�/!C�s�F�0���
�nC���\H�+��t2�-����(��,��j����g�-6k(�Dq�|�N�[`�A����z�}0lo�8��B0B$P�L������|$��/��y20����o��52��eie���D�F4riy��P{��S���3]_���E�������?�SL��I��� �E>>)ge��=H��Y��GB�@�d���o����oD����
��#��M��2��������x#D:��K���9����[�{o����N�+��AC���4T?���b|1u��!���'\J����a4[$�-������;-������,���Cn%o	�)2���� TvV�7��0@>:3`D�aLb�L����l�:%���d[R-��5'"�tu��5��G�����As�(�j���{Y��������������@����
�E�ol����B:���"����+�
M��+��7�P�_r��9�ByE�J�v��S+�����Gr��!�\<7��4����"��Q�x�:�E�0OW.'�X^"��
p���C����������1�
E��
#'~���@2�g�l���-�:I%W�e�2;T���Xw�Q�
d#��Qm��b)��{��@0r���7�9��e'�H��7
G�
X�9���	�2�2�-
z�M_�v�`�]���`K0�U������/xhc�z�p���ac�hB.Jl���R�+�}���hjW���Ctf�maF�����u����D���������N�.)�,���`������:�"��
���rU^�J����������H{,q5I�Y/�]@F�xq��n���'7��������n��S������H��0��	:,i}M�2K���cj#\��Od�0C�I�o�s��f:,�o��OF/���x��u��9����������U5}prtr����'��Rv�#�!�RD����(gw���u�n�Q>1�����E{4����4�	�*���f�U��������/?�8	n/�w�|�����S�B��ppz�����*������I�L�6O���H�6Gr��V����z\\S�+zcU�s*�P��
�8LG�)���!O1T��(dp��!��"G���6��F��o����9c��t��`��9�"��e'0��k&�M�P6��/��������������Sf�Y���NO��<�&������lR���=�����<����@���Q���k8�l���Q�mL��s �o��3.��m���9G�s{��A�r+W���U|������,
�^�_m.5iPp�� �j;��7t6���)V��]��-�mR��(�� 0H���t�;&+[�-<(B��p��������(e���>NYq6�3`?�Q�nU�_����b����B���������Mv7������g@l^�^����O�9>�

����-8
��u����RvA�d�f���Z��Hrh�h_(Q�@��|�f��K�$�<+�	�R0|8Wi}=���B]�M�d���XM��I�|]�L����F�
�_����c��cFD��g�5��.
u��M��3�4_0=�� ��i_������!j����:�� ����uo��Z��\!���h��(\p�}���=�<���U���Z��(�)�0���4����\��Tr����Fh��?�.���<L��K�/\�V<����Z�f�WVi����-cl����M��{��vz�z��i��``�Y�f��-��b2�"������t���`�N)��z����c�_1=k�����z��]���6qJ��\���my�V3�!�5D�>�A`:+0k\�X7xGb����h]�����g���%H^������5�`E��21�`8��)�t%�l>��;���6��G`��F:C��p�����f�N80T�m`�=�@q����8
�����[i�i�#��2Wl�4E��.e��<�����ibO=�l��:,g�%��Y^��/'8<|0�uUd�z��b�
k[Z���8�!�dBu���b���������PLj���c�Y�)��}3���3�����&�M���,��|��2�F�����x�q'BHC���p�7���_qX�=p:D��<k���������>�_|�w��}Cpr�e�A$`>��Qz�u��Js#'6*mooS.&����}��~.��>~�slJ��I`��������O)L�������(�M�{;�#?e?��5����a��fN�H@����Ce�q��nZ$��j�:R1��>*��*����%���(s3;6k�N5#�g��}U7.i�MLvd�@�0l�.�E�i��c!���"�#��(��_����&Z�^��tV_��7j�'y�k3,���'���6���Xk���(�O���"n%�?��7��PT����������`�����Vn���[RUR����� Ed��?������C-)r�Hz	^S��r�Z����!B-4��2;a��+<U��\2��"�d��������8l��imEl��z�eW���p�����F?��f�	�v�v��9R��H�����&��?$Ftp
q) ��}�-���}�z}F�N�wQR�"g/����������_�V���>&�q���2����o��&�]�������8�l/���+��r�FA�w���
�E�����H��]r��?B��6��^i}�,0�����YV]eb7�(�@f���k?�.�3tvT,�[�tb�Im$�'A
g���l #Q�u_�������b��Q�%uy�	RFE�m����<��6 va������r|/���������s%C���A���G�
��Y
GA�RP}:�O��F����+?`��C1]����y��>h��X��77G8M1�p ������p~�5�%���-[:�f��G`���H��UK�~�==zq�3m+��������������H��j�����SM�?O]y�~8�����0�-�$}��?�}w�
�K�����P���
���AXn2�&��_���8>��r��-'Y�`�_<v��s�@������j�"pm��7�\J����)���<�>�dGM�EI��p��������u���~>~�����y�RN�$.�������V'P�<��A�GF:��!��h[6���
��l����1:/���,`�re#�5C#O����+�m\���t�TC��rZ�����\��	?&������� o�v
����e�	�usH��f;	�&��,$���'z��IsQ39�	[0�~%Y���"(.��\`8���9m��CB�������<������O
r�n�[����K/��]�D��3���A����C��!�M�h�I���
I6��p\kD�nc��5���|Y=HD�������W6��=uq84YL���C��A�q3�P<>��3�y-t�Y���H �1�i1��O�!K�e�F)���D	��I��uFw*]���������
����`J�R��'Xp���_�<H�$E�������������^#0������P�Q��
� e��T���Cn�j
��E�>"��}.�>�S:5z������|k�9SRyp�c��?kZ�V���H�{T"1�����P�>s��x�
�6�8L�2����Z���v��0������{x[��������w�#�V�h
S������Q1a��k8�rUX��>�v�X��������:G&	���>d��w�������!�,PNpZN�Q6	J�? ���C��[�o���O�������J�
��V�6���x%�>oc#�r:���$�2a�������
��ssWc������*0�:���
���m��":,?~���e�i��%�e�ben+4+_���a�����m����*��|�qY�u��/��x�]��Iq�W��p� o��V���D�>��e/l�B,V�XL��JE��j[~AO�P����8]�(��i�}?k;��8"�"��������n��5��F��g�U�6��w"yb,gC��Ev[�7��O3��RROk? P�@�"9s��
<'\C�b-+���A�M�����@-8u��\pj�wp2!: E��|�@��R08^�P�s�6��@J���0�U�������,P��n�P�G�=�+�-g���;����d�%��a�'yh�����GF�X%A��m�����ay�6������F��Bd��;
��{uu��#(�AE��la%�a�/2z8��zme�@���;��#���V�MD��)���DEO������z/ �C�w@�I�B���Eh��A]�?��!�R����hQ���6��u#��P���h03,��ga�}��).���Xg��(��_���(�GC��}�9���0�3(��PK�<{O[������S�99St���W�B��CR�wO���i�����Xrx]���p��)c���	�:@+��W	HP�����������f3��r*m��6`�U0W'�|��Rs����6���j�:`|#�w���*�d�b&��
�I1[�C1��%�M�;�mmozW�[���z�T-q�*|y�-k�_1v;��5&�����GVY_�>���z7�%&��<D��WW
Z����,
w�	H@��r"��H-�/���6��.,��EU��}�-�������B��U�`��5\� ��W�G�Al�(�z�����K��T���}P^^>d�bKp��&(���JE�)��|�~m���H+�lTd�H����cK�w3a�]��TT#��S��!�(&�M^��I�C%�2�����%(���!/(^O�c�������>.���|��h
qa��L:�:-��4����fe�/U����q��E��=�s��4R�(RG�a�;�?��������'^�R��k�M
Y%�R��u�Y9�/sW@�����>5x\�I���^i[,<�Esyt}�,���g�}`=%�m����pOu�6V���*G��@�I���`������|�wk��~p)=rg��r
%���L�54ce�.�dl���o�������a��
��������D$���Xi�@�ai	8i��`�)�,_1!�`-r���q0����d��f�b��#g�M��Y�G-W2~�c�E���u(�m��T����!]��� k��:0W����h�'��jx�`H���"��_:|O8��$�`S���,�`����E�E����T������R����8n.���ZHE^��F\%��-�&0��t������p)1��Vnqp�z��Yn���$�B�����T�l�
�rB����+���4���!x��I�[[^X
R��s7��I�!��W-;L~���m����/����u?@w.?������H�����No��������P�
 |Fc�z�����r6����-P/ND��������%��D!c������B����e�^�t0�!����k} ��S�����	�:��m/pE3�C�B�U=d>J����j�C��xo�!��Yu's&��::U�u
��}�,��@Bxi�M���<oX�-��\�4�V<�K8��A$�(
z�������	�%9�6|@1T�3�q
)���I;U���J��S�R�������hj+b\�|$I��<UC1��W������4�T96��2�2"�5����=.�V�D������rg���~(o �~s�\�[x�6Whd�9��Iz��I'�&��,�FI�c�$8�=s;"z{��4���u��#Z0�+�`��q15��Ls�ug=�k@^��t/��������l6��f�v��NzV������U�=�9��&���#W�D"�����
40oN�����"U�J��8ge�R*Q(�����������E(�P%���NCNh3���m�vI���W�D�������#�����y�T}^:�0�J��(�t���A���i�0���*`� #��;�u_����3
y�Q�3Lv��\����&�h� �0��*q�X� V�z}��M/7��9L1�l��x�"�;�����E�{=�l������zO���mB%��)�����	6B~�K��{�\�s������r�C�����#�v�u�@}��MLqwx���J�
s��s���"�.Q���Oen9��-U�6@`F��Yn0��Y�\|�l���4�J4
;W3�� .��-����n���8�C�lP0�9�p+���2�i2� �&�g���rf��U�r
�WJRb\i�C����Q������[��3[�M�5�������J����q��$��2Z)|������
da��@�M��5dV��^��c�M>�<���	32e���D����_D/=0���d$Wa��u�V�p(�)VPz�bz!���T��F�w��hv�F�����C�y��5[���>J=B�|����Y�2�47�]�����S����Inx�������?�6�	��1t���+<[�����8��S�
0`]����)�}������ ��'�;;_�~3�L�_�n@R��I��qa�������~��M���$[�����$�+�����?{���6z	��6gu�{���l� ���v��K�uNJ���^����g5�`T�V��������oN��N�G���������s����s�(�8��1�cE� ��r&S)�=!��!���m��&��}���fb����Qg�;MH��#�u]�!����{o��D�2�����M��i��m��3�J�8�fG�(B6q�'pod����`X��#5V�l��_�n���n�~q��Y���B� ������K�$r���#"�
���1���1��t�5�9C��8��t�H�\,�M�B�B>���0f�t�DF3����
q�<h;�a��&d"������U��B	.���15�</)��b}������0r��y~tvtz|�����^�|��?O���p����/.����>�fjhGI���@�A;����r;�B[��3~D`y��Y��?�'����cHO�|���c�Q]����T�y�3�n.�C�%�����Mw?%]
�g�%6z}p�#E[o�k���G��Nm���;N�x��0i�c�(�?�t��)����M�'T���������g����N�;'K�Y���k����Qg�]�AK$1��4�\ b��=NR�����JU��������>������>�{7���M��#t\�u�DX6����v�g��n���_�wO����a�	�n��VH.3:~�y`6�k��`�^��H��\����u������_&���)Za��s��e���q�}�E���/�.�S��uw�n�~����8h��
�����#Y��E��1��G�8��VORq�h_,�u�,�]d�>Z�--����N��5ZW����5�������;i��CJ���W�������]R�f��������&yd���,�VV�k������H��.@A�2VK��8GjA4��g��`6i/��C.�J)�`�����b�lq]N���1��@�����Aa��1��L)c#�{�0��25�Y�U������d�n��U^��$�(#�ndi�zK�0���:�H1t����La�����k���
t�O��tNp;E��[�mQ��=�����Y�/�`�����0�������D�FXZ'��)�-Z�+�s�+enD+�Sp�}��_����.�i�U����az�48\���[�z���f��K���%�f�
v�"M��I���7?���_U��h�V_d�d�*P,$�R><�:��#%-�Y�%8��:��BV����k�P�1������a�Bs�\�y1���g�v��&+1�;��S+K�p������7	k,����mv��~�JL�f���$��"_i{�Z���y<���!�����i� ��	�C���������8L�V�T�h��4 (��
���-+��C��W�Z��l��o������7C�-ig��*y9-/�i��,���_V�r�������ud��"G��I�0���<�<]iN�/������n`�b��1��?��$�-��&�F�O�����/E:
��S�����+mNR�.�t��� �e2���wX�Y
����+
-Z��5K�8��[���3s����F��_,���0*(F�:QkTZ�GT����&�W��:� FE���jB�@2�����b�d�� �R^�Q�F�h�lH�ly�
���������);�=��=���:�j�b���a\�z������a53���f�X9m����P��E�+�r
��lM�l��c@��X^�3���'	t�:��U=���oes�B2��872i@L���Z�������R?�/(���0��yG�������:����z���Y�w����������Ztx�����b��gl������9��`
L�6#N���W�{��}L�47�k8[^��$&&����W/|o��a��S�89�w��+5f�t~8y�����`�g����	��&�����������;�����7�G���6�w����?{�\����E0{
�}h���5S�LO��Gl
��C�,� 9Qb�|��� �cO��I�	 `$��]#�v\�(;����^���W�K�]�S���w�����`���r��5�#f��i:�����>C�N�2�Io����`�E�`�����N�)�,��V��@ �TNW�4�a:W���o�!��y�0�z����P�K�-�>�ubB��8I����K�9z����(j_��G��������0|�Cs�z��d9��O�����sI�U� ��@!"7��-�@)� Q����C3�Q�SC�+�Q5)a���R'������q`��6��x������L�h��	�����������{��fH���\��E&!���	��"�%k�C���6e���snrlc���E?����T;��(I�{
6?/v�����D.�3DKlp_p_v&J�n�50����B��*�A��%�mv��)�������w0�Q����@[h����=KB���k�`*���a{�Az�j�g��[��/#���$#sDGk��m�!��p�>_r�v�����������Gq�m8��yR�O�EFh�J#"�X�0��R��R!����ez��Z���krJQZ��8G1�Zf��E,���J�A�l������*��T*�_��oI���/�*>�%��y�Zm�V��
-$�
.t�����@V4d�K��\�d7��n
�v������f�9��&�����������@�t�"_(��!�t��%@<�
�h���@���������%�H��N�Un�A����i�4��25��|��\����H�;���SV����(!,��5uL&h�5��J�2��U���yf���o2<u�*N��
�Rx�VY��k�3K?�Yw�'[$��V��Z29�����A��a�����`������>�P�9���:���!nJ�P22:�_���%��k��q[.��������8C��na���$5���tN(�i��G��qc4<��"������W�ob���je��CT��g�dd������{&�?%I�_g��"�P��q5�q/7�����K"��U5�An"�=OA��k�d�$�� ��)��\[��J�`y^�pU��>]��@lf�F�>GM��nXr�|uvBb&T	����������]�,�+j��2��K�*v�:pm=yL2BI���+��~ZF��9EIz,�5:�O�*�J8������6�O �$��(��{�w�%��#����69#]�@K!���T���a~iEur)MF�!���������k�o�;K��W������t$��<��t�p�:)e�b��!�-f�U�j�3����-����ok���5'&y���5�%�����.H�Py6�/���VVzq|dG��`�R01��9�1dA{|�l�����M���D�b���M���eu*���_tv'�HFe�$m,��k����i"��)��f��ab3Jqi��J���%e���������`��+�LfP�mv8���r����K�S���S��n��W`����4:2H�~2���H8_�4�������4 �,�bd�� �z+%N��|8]��&Et@�%2��h������jH�s��4�Wy��5���������Y'W�#+�8
-�O�J�$����5H��zFK�]WQz��L�L)�[g}�X)�[��De �K����,�f~�:WW����<�{���c`p`�0���D��������U�*�* �$e������m������@t���(��V+�\Z�e�hI�����$@	�$��eM&��v-�a!)��w����S@��������_��9?��gC����6����!���&�8��C�O���c�G&�����IL�}��2�����vj%'��)x�@X��]SmM��^P����P���U��s=@3�(Rl������gb5�	���J"a���Hyh�L�U���X���CP�1*>5
�n7����c?�Hn��vq�9��RYZU�����mD�_���l��/����@|d�t��J����t6�����\L���>������;��]4��6AEIc�� ���.��N�B���'~N�tx���5��-]=V��%��U�|f�g��i ��ZQ�(R�E����aj�bH�{
q
<�v	;H��P=���_�~���������8o��K�6`�"��E>��Do)bS,i�X.��G&��9�=�V�k��T:!E
��^��cd<�;�.��G����/�t�9�4h
��o�
aO^	!��M�J���%�(<Y_��y0���������I8}�L}������.������]��F|F H(9\������1�MJ��������A�B5���i2|����M�����qI�������H��.��6IV�� �r�h@��O�w������
��k2��t~���R������	�������� �Z��,:A�b�)���b�����z��L&�8a��p��O��]������`P`Ax���Uu]E�t%0�+�&f��LF��/��\k����� .+�R�j(�L���/�M��
����cb1Lr���g�Ql��^�t:��M����F����u��6����������z5Z�c*��MEs���j�t�oSk���VB����EKq�7/%D��2��eX�k����v�'�Xh�o����_���<(h�Ia�1�T$�Sp}��{� )?b�=�#!x�:���������QNc���t��������l��u�RC<`��I V�'���B�pq�p�!�y�r���P���2�)^�������p2�4�^P Ed����M����33��Hl��L[�!l��K]�v
��
r7O?d���&���b3u���T7�s�4�����������#	2�O��:��������u�M7��0vB�OA�IQ��M�����������#.�������J���l65��`014��2-�h#&����\oh��`;�`���!�6�Y!��2��o�(
����<~�f{��DB�v���lW������<�h�o@9���|�R�g,*	�C[��~��aLK�N��B���l&x
m_-(��[@�
3������.-D��a|y��O�%�5���K���(���
�y*�!������+�B���'K0z�H�e0o)}P��h��r/W2��p�D�gC�f|�Fh�5��%D]�xcRA=��RmI���)L5��Q�$4t2X�d9e3 ��	b=�_��)i�<���PH-�����F ^������'�J.�E\���:z����yD,J����?y�5�lx�d�(V��\9���� ��[��%2�JH*zSN�9b���
�sA��
�g:,]G}��V������(�&�Q��J�h3��Ax����y5n �j�g�&�|��S���Gd\�����#A��;��$�z��au��dz�QqN��H:02�>	�E����T��*$��X�@f��Ry)�����a,���7G0�D�����(�L�*	|��Z���e��2�����Hw�� y-_CEK�R������A5���'Z	����M��Z�#q���][&������2.#�Q
T�'�2�9��@��GDz_�d�����iO�s"���)�NM�4���(�eF@�3������>c&>�-8� 	�D����d�#�f���eF�f�@�rS�%�qDtB��86��E/�[g������{=�����C<�+;�(6K����P�������L�ux�R��jwdNy���$�rV�&V!��.�T"��j�bcK��F�G��y���������C��m�s��uR�:�4���������{�9R`m�
��^�[E���o���n������J&�+������O�l�b��H ��ml�!yF�������%���k����3&e��]� ��Y���HZ�����;s������4�$���r��.�A��C��T����1��!!*���+U�1@v(k�	"�)��(���!4�&Y
�T���,��p������Ey��Q���	��������Itik%��4G���/}g���Tv�bt��i��:����������7��g��=�E2�f���'Y��K�M�-������6��'3J�6f)B���+�h�F�~R%gx��o�I-]���b�:S>.�cd������b"���5f��S��2�9#�N�R�dC�������?��ig+sA�����*AQ	d�����1�XH������Q���q���������!�����m�(�������~^dR)}����S!iO���#���`�)Pj�x���vL�VA�{�b��S�j�j�g�tP��`^�<
M4Li��=mY6"�'N��;��P�q
R���UB�iP�0�u(-�]���y�����s��ze^w<�Iks��S���w��9�)@5�&T��\�ay��8����|��d9����O�_��@�h*e������������Fp�V�l����JG/�K	���C�.���V���d�;1�x�G���|�	��0|+C�S��&�=�x�^�x�Z�>�������������]�Ve^)���o�T�������g=&�A��D�o`�	���BU�sw����le��m��`�s���k�Z�{���2��X��#xb�k�YSa�c�n�L�L�R�Wh(�Y.n&�fl%|=�lYO����P�1����i��>W�(xWY������0?H���T�C��K��������L��z�%����'���iK��1L��Q���j������Q�;Y������9S8��<8�����������}y��\CJ��j"����\9��h��.z�����\v�N9�
Q��X>�C�!�W���(C�G��T�r��A{!<fXh����8F��r-T��c8&M����$V���An���:���M����y�%ECt@DK�v_�z�?�s������|���J^!�K���@�����ntr���oR�5����Q}��Kr��a��r��CgG�xc������H�\��9����E��[s�yYx�������R���G>x�#�����f�y�1�t(��3�G&����J$�/�C���X�b�<P$���KDf�|?�|8�|����A���#�����(?Sc:���q���p�!�:T�'*��|v!������u��N&Ua
T�������u���7�Q���l��]��wo�}$�|>y�
�!��KZQ�}������^MK���<��#���<���Jr��E3w�++��
�
�k��f�P�����d����'7��[��t����N����n�;��!�2�c<��.�������H-��4Qt�D)q��o+Ol])�P������1n�t�s���S`Nb�����������,���~����h�������l�#��*������t��aT�����K����bd�1���R.��mR��t�P&d�~E_����C!�� v_-��d�\N�rV���/g�VaZ���<ZH�
�F[x�$Y�]-E����\�<�q����fh���B�
��B���(������+�{7���KY��{�IT�����r}r���,������5jy���{<��N���c�m��E�ew�������&�:��F��P6t1�G9
�Rz��PL��&EI>���@l�(N���P�:p'�#.���~�$b!�"q�LHw.h�Y,���El�t�Xw�0��7���\���:5�J���-���$mq�}�;�V����2�Gb��*�c�	����;�!o��}0��^R�iw�e��8�GB��Y<��,�U���!!�����3N�{�����1����3,�W�Yzn�5���5�ES�%3����O�&���h�e�T~�L���������<#_������'���W���9r���q���Q,`
��J������4�'����'��7B�M����\fn3��C�z!�����[f����	 U�#8�.��A���Q�K�7�z�`,���|m4����=<��#����!]�-gf��V*�b(����Lp�Tc�0x�,Wl�`"������#J���}B�������a���8��V��X�B�&.�o�k�,�0
�QX]K�K�%�twQ`}����X�����V%=��$S8ci��=�m��cE��XQ|M�=�:�#PA����9�/�~���v������l�\�;�~�o�S�k4'B=�{���[S��.�6��&�IrsNH\�b�R�������$�T�6�=S������@I�5�r��(���3�����N�H�������Y�+��&��C�D|��������z8_�<��%cU��*+\1�RM�w����+rA�
��%ipz�R����Q��2����N���o�q�m����C���<L4�r�I��G�u�h����Zm�6\�d<>�6�x2U��|2����N�)�A>��B�95�n)������yHK��&�/4zW������`8���/�����M�{!]�7��N�{��w�c���_T�*
P���g0�R�������
���xh���bzY5�����U����������Xat��(�����)(��q��N&����I�v������������QT�Bl��'�S���a�,���+����Y�4:��"�o�[�4,6��CP���G���e������*����],��D����h<o�!@*A�������=���x�h����!n�(���gp���2��
 ��<�#���}�t��y��1���x"��Mo�v	���m�#�P��C�������b�(�d�S�z���X"P��#���o��:/���#y\���#��tc��c�"e}�6m�odTcE�;$�8!��W=b��$E�b�V���6C��'������:����5y�B0�?�8g��`��(Q�!����!n��G
��f�4���]b�s2�R�eQ(�#��3�[�T����t���(k��-#Y����nv#�$]��������D��w��{wJ�@~1��������7����ep��O�wZ���{g^�!(^`(8<9��H�9�����b-H���Z��u�/\M|�YPC]�{,��; I7���$�t~�F��%]Up�Q�t��f����7���}Q�T��b�����V|���
�C����f����	�j������8�$2"55�s�U,��F��?��v����vaL���}X��;���~��=�<px8�w�����;�Q����(��-;��h��1�)?���Q��[�(	!��*_"wVW�%"���%���8>������s?;������Z��c����!u��Fk�:iN&��Z�=����Z�9�����B�{M�����W\�%t8l�B��A������m��^Z	����;��J7�o�qg�t[/���9�0s8=g�a����}���h�/���61����A��k���>�16+��!�Ew
��{�]������9�n�������ji���hTC��I!��'L8��/�,����+���M ��[������3�!��n�B�
Y�7}��\���2��������AsT���#�x�p�/7���g��39���:���;�������B�F��_���7�f/�C������m�����\����^/H�m��1�mM��;m�����~h�
�.��{�t�����) ��~n�����Rh�b-��n�Dx�����8����������[=��>����^Y�]�M��c���KH���g�HA��:���2���&�V�i�I���������)VE���G���X��Zm\����7�+����tq��i���/���
bH�)�f<���8�����o���������,NzBZG3(������e0������� �8�<������
���#��S����P� �	h�)b�������������Y�[�C�ky�h�d�u��]��#��H��M���"�!Y��q������V������b��X�3���%h����C<�)�V��F�����Z5az�Eam�qCJ����z/)���I�>q�7G~�>:�����YC],�=:���#��u��V���6�����"YA��Ge*�o�b�t�����!`���I���Jp�i(���r����M
��}5�����BM�K~�~��y���1k��KV�%�� 496O&'.\���'G�
�O��5��hSd���VFM��y3�
^`�����R������������wxI�o����7�����.N�pz��$j�.���L
0��a����������{�5
��`����L���l�Q����!��������r�A��QD>3�!�E�o��������,@6�>1�p�Q��E��4?�E��U��"W�Z2���c�CX�QH>3��5�Y�6^?��= |�.!S5s�b�GvrxD������*z�5��e�����qD��'�:R�)y�
7.96u��h@�p8�S!q/������������8L�$��9\��yr�VB3���}�Wd6~��dx&x�Ym��]��%8�`�~r������q0�����Y����n�zM����P�P���rY����v�����v]��Vky�����kLPR�y���p9�_(j	��^�f�����BEqi��"� j�.��?]2�v�
E��t�=^N	�S��	�)x.<�<����#G���"�T���b-
u���*�s(����2N:�W%�|B���BES����va�����l����&��j�<%��������!d&�{������}�M�+L#�dTu@�(�	�T��}�0W��1�L��bz���c�q��q�i���6����97�)��C�[4��\��r���}X�w�t"����B��
�L,����U�+���5�7�E4/���T�������n�)�!����R}c���J�"�b���P�I+3��O��9��K�\ ��4��q{��F�5#������w��/O�`�w��?C`M���(�����!�*p�(��Bt%����n����2&l��"B)��F�T`�����l��}}~��F[Y���n J����b��2e��J����F�QG����*FT�{�1��
�����D��� �1@5���4{	��_R
I���"����/����l��`�?�"������v���`����/��(��5x�*j�������)��������gb\�[��4�E�.S���Z������\c�*�gaUtS"�-'�g�4��LF���K����	�g��f�w(A���kxj���QU����<�U�!O�!�s[��?�=����uU��e�<!w��~}4���mB���d�)6S=�������H
���j����`�p��U)s��
tA������9�w�������:}�S���D�"U�/|R�O��}�5�%�����7?����*#|��([��-�o6�9
��@���<$=c�3B����2#I�VX_G��cg|�5�����p1!��y�,g����S����������W��$�:dIB|^��m���v��0����"4���O>����;��|(��s^g�<������\�b���[.�}N>�7p�[C�#�t;5����)���ye���|�.�*�G�#����.�^��L)�L��>���B?�X�!&�95SGq��;^��������:C�):��h��(V����]������F��y2����u}p��[� ��s�&����A~���
���S�bF�R��j�]�WqC��i~�`=��j����Q��������v;�J������E��rJ���+(�H�\��
���rmf>-9������r�`��]��HMV�C��L��q�e`P���*o��e��:����8t~�\�9`�a��>�HP3������BI1^� �VF\�#
��:�������e�����2�v���nf�Z���o�����"��7
>o����^��m.������)	�����(#�����BD[9g
g�Nj���S6����j��������_Z��qN�dA��u���+�,�_kIn�1��=$
�^���s7��hpb�L��c\	a8�U'4�]�n����c+���Z t�?�@�V�,XC�����Ti��8���50�L����x�h����x�h�O�_�����U���P����[�S�(�"�u�AZ��P�!��S4�B)g>��sP����������oh���Xz�:��x��i#�\�k$D"��M���y�~�Q8����!#�e�"��1#����Y@�&�+�X�Sy�,wL���o.������d��t��:rv����6����������1��dz��D�n;t�:�8���Uh��C'.�^�e��?�m^f
�����tx�38��Q�yH�~�	�^!��5u#���W�C~K���,n�0��MO��mf��S�(�-D��r��p{q��e�D��>�v���� ���{S[�{��4X��������]_w����9	��^:u�����C@����4���
d�% ������[��w�3RT_
{
�kS��.����i7V�:)N j������K�H�&�����G�5S�+N������yD1$$g��r�L`*�vE3c�����+i��
f�xL�b"�dR
�7H�
�Q�p�n#��p���T�t�Jw���r�3&�#��W����F�i
W��%���Y��������=F<��E�YjJ��dr\
�0��,3s	�������"�X�6[�����^L����DS���n��l&�K"1	����%0�$����7y6�K�k�������tv�-G�h����?i����X(~�G���T	4�1��Iu�e��S��d�B�4���u~�Z�c�N�J���B]5�aD	��p�m�osV�N�Px���H�������U������]��s_"�k��9N���!� �@&�!OH�����(��\��,k���b����O��2���y�C���p:k��;7)w>�t8�l0��6
ff#P�1�7�)���s�_�S �d��a������$��`�+��0_���	Q�Z*����4L���\H]q�E��-�
�G��������j,���B����H\�?@������Xb�����
�A�?�_h�3�Wy�M��}taZ�%�V� oQh,��#P��y���l�����X7��G�m`��������.��j�/"�Y)���C�[',���/K�z��S�H/�|�0ny-�;����46/�Qr�cb@��#�{X�N ���P%b6��/1�8=���c��Cc���rK��������u�� ������`�l%���9�4��-k%�*y�����)����;����0X[$�3�",r���~px��
,��8��0J`@�L��5�,� �O��a�77���@s����lRp���l!�C��1�n�q���&#r���N��`������tA�)�*����S�2��f&
���6Z�w�I�?��ZK5%�q��z6��Xvh�����	JZc,�?(>���|�TB|=F�}?�� G�2�L�NK!�������F7N���6�L�RcJ������=�����u		��6}o��?��j�����q�u��A��Q�
wz�b-5k�Q���L��L������U��-� ���I^3���<�eg@	�6����E(�P�`��,~�|���]��|��z��1T��YqP�F��x����th��hr��/�ejt'��<�X��m��,o���q���>��`�,�`�t
�_QRb�N����dttT���ON&'�IcE����`Ui���c\�OC����X8�������{�<T^�q����}��w�KsB�����]�o�B]`mR�H�"�4��O�go;g?��t�� Cyy��A(�)�
�7���	Us���-��������
Ku����PQ��
�	��$�9e�V�7��*D��'T�����`W����������+��U��{����7��2A�|��\<f�4����G&���k$8�#:�(��22�x4>�?<j���a�1����U��u��t9�c�P���(�������%�P�CX�a#+��-�	�f%�k=N���7��?f	H?-!_�*IF*�|�D�g:�-���-V���������&��S����"(�<l������;����cP�Wx@E�"Y��e=o#�i��9�
�L�s��0���&�s��`��vx�M}��13Q:}EPQ�R���1��t]��I�#�������X�SVc���������4����&�;d���������~�	r��nr_��0��U�w���t
�['+�2�974��`�!�T�����.8L���.r�����E�3�����T"L2AfK6:���Z����B��5h�L��.A;f����tD�1�h�I(�0L
�x`�uR�����CJ�5��f��:����V�6�NF��o�	%SM��5���" �#���JBk�s��S�Q,��� �{�����@�A8]�$���j��9����)cP4�4���/������*z�}������YB������N�	2�$�;	����~�{6�2	�J,D
�(����Hlq���s~Z�}0��8��KH�����
K��������w��HJ���U���
mPe��to���j�OP.��l��������5����5v6
��t��O�����x�z����cf�7gm�n�d��bR��!���q~� @&/����_b���~2�	V|�f�*��
�y}P���w�m�Yn���^�
��;�~gP��K6��<CB����T@>�-	�)*z����0���xw��[�������/G=������W^X��t?�)#�%2%$%�/�t6�	�~��7��4A��!X���wHsZoo;=�K. &p��:�\�F0�d��P�9f
��"9�*$1������oL�h�<�����
d���
ej� �r�Rq���FS���p����\��i#��xV�2������woc�$���I�����+[;����6����H�Rg�pJ���v����B�]��\3,(�������$	�h��o����9��5�:���� !X��h#a��o��N5�7e�A��{H��qjw�Wc�X�5���$t�y�JyE��~rx<>���m���x���
�����X1UU�(k�<}�����������P�[��g�M��,�o5���3�����e.x���+TxG�N���,���&%Je�����=�a���A3�������j�{�l}Pu�������dE�t�����,G�+�.!}����l�M�oqW������v�9$~�a�a�����H�n
!4	�����qz���La���<��2`��@����c/�%z//
|O��>%��p��K��e�ZcJ����o$+k�n������BaZ����q%��q����8���e��:��!�_�����z�j�[�yF�U�]��S%�lPK���������'< �y�v��&�Vk�q����D-���-��?��]9`to�lp��D�v���B��u�1A�.�/K	����\�-���5���LB���Y"<,x��������|�I��C����J��fI>N5�����1��v_��XE��b�,EO�`�������;OS���eO	1JCf������Rd~�#�%<�F�->�s*��j��xd�3�/U����1�V������
���O$���$
CQ'M�1������6��>V@[�"���Pf���=�]�;�8��i������	oL��(	xYI���>p0?{��E��j��T���z�m�4�"��h)kD�=X�D�b�|�M�T� �
����Uq"4dn��l������*^���/��^ ����� �c�-�7����}����8t�z�p��u��h5��a�r�S��S��EU�q�Ew���J�n�9zA����!���,���'��H�|<��8�����;�D^����&HJoFT9��kPC��j
��%�H�D��u�������tF~��������5�q�5>���Zm���|�`��9mMm���*�&�<��?0��9z#��<	+$,�K�]�2�9�3��*O�7����G����=
�B�\~�"������R�� �#��a���S������5H�r&�Q�p�sb�qA�?�'�6��S*��4�A�^� �o�+�&e��7�Fd������:����S>lay�7�UI6� U8}d��#�$�'�+�z	�� 4�8q]&U+�Mr'N�g��R�J�	nZ��=��7������`y�h�����L�LF��}\&
U�g�g��cEo�t:U�I9!.(;����k��yZ��������h����2-�Xq�3�8�Mr��3C61�f��3	U���j��Z�fQ}�S�s�(7���om�������jC��T9�gx`l�d�o}u��PF=HZ��5�i�Oq���~��'^V"���<	�����s�����{�jJs.������?�����W c+pA�/�4W
�o9���d��h�[�'��G�A�mMj��>9>h���L-� S�������'7�N���%��������������>\���y�s5�����_�~�]�7����e�}��3>�-�d����-���e���/�Z,�2u)'��'%2�;�J���*b2�_<�S�w������A����Q����'����;.f���2lWP�]&����uN�4���8��������:K�ggT�g�������pa�6����[\h}���%�I|"�XC�s����<����|�;���
����N��L>\�\���1�N;'�wf!L�C'}!28������wc
+~���.���M��2r����o����1�R���y����m������o���!2��Nu��+�>��Xb��!���Q^DN����nq� �1�����x;��_�Sh��3�1�UFV�/�0h�v�a?�+���e���E�Rj6�P�4\����V��-����(ZcC�(
H�y�_*�Mu�z�1S5<�F�X��5(�>���0'�
	�*D�BP��3���G�w�: �����'L�yk���7��a;�M���S0�%���!c@�q���V�d�4�`1������#��������a ���U`k`�B������y�y�N���?�>���:X���8WS�_�,=`�b�R��K$�o��x�e�J�_;���M����c�5d�%���`&h]��T�5M�$���������}�m�~�~'�4+�}Won;����>���o�������iG��p�d�i���L�}j�L8/�$} CD�����k�����o~0pD���FCX+-CHT����b�iq�^e���xn�g�N{ z'���G�o�A��V3t��"���CNg���D
R������;�-���(i�&l�8D����y�@	mO,�
�j
V��h�f�����y����w9�t�����8�}�h8������ ��(��{4��h�p�<z+�BG���q���zF�M�gEw\o����~���&�'�f�u8.0������f��)W����{����*(�ib�`�YfP~&U�K>J��%w~�u��H�$�xDF�"���b�������;�?�Av�*%�<Vq�[����������|�|_�"�A�Bq��qv����gP���w5��J�s@<��"NX��b��3����p�}�~8�\q�i����q���>WPK�2%2������|�V"�'���X:"���;���5b�����~���^$�����;��N'����y��7Gn^���J
I# e0~�Y�x�!�O�&���>�Cry(����$r�	�����S�od��x�N�D
����X���ECer�<�\�� BVw:B�J�l��;T�������2�a���[4}����ni%����*|7'GEohF"<�� 	q2��@�z� A���u���>�p�I�y��DGp���
z6�!l:�����*||uJ�3���-L��+Ut��77�O��55JF�BZ�i�Z�Q���+Nw��CiS6kIH>YS�(���I[O�t��'�N�G�H�'���G�����WC��`��K�"�t�	P�
�#��CQ,N����D�#}�4L%P�0gBa�&V��1:$g�~�DE�2:96�����/R�*�6���
Yn/��Xh 1���G�X�6
~hP4j�j��Mo�s�Yh�W�f���FyK�F�^��G�F����A��m������Z���{wyC�R�If_Q��������J1.��D���(���	�������;���=z��Ge��"*��0�O����z��?^.|������������\v���m��M��
uh��c��F�1h~�����������9��s�&�J,z?^H2���O�P��,��x��/�i:��`I�������B�z���}@c���=��#����kY����a-�v���1����=)H������C���P���������4:�@?�4��T��G�����zK(����~����x��fUU��Y�0��CV�����
����d
�����O{��ap�[K�����P����(F8��=��E��O�bL�)� }�������E��� _���	���W��?F�R���/���"%��	�����xWT^=��A]���T����b�1K�������u����}�]��+dea������}pj�=�Xa������<{�LB6�d��L�:�V��16*�����;��c����)��� fBT���������L����>t�{FW�X�@'������Q������4��7�v�1��n`S/���yOss�\&C��]~pZ���/�[�_����	P�)����\��y�t�.�GMv�L,[��.!H#R�qUh��4�C�e 5!����i]2������u�=*VJ0yd�b�u:.�u����@_�'Y@>����\��o �����%5P��{(E�����q2��:�`�ON���O���T�����������y��e�@�����sNI���1���v��w��^|uy�(�w)����/�O��{d5S�)���K�����B��3��)�?�&�,���:l���}��Vk6Z����������pR�RX@���-�{����F#p��!3�1�7����D�2]�
����B�����!�q�y��S6X�:��uT1�O"��Z�K��[?:9�Oj��hr|���j>������
�
� �!�N,�TC����d���G��Op]~j~��N�{bK���s��%h/Tn��-���
�#���5��=:�
`��Q���v�5O<�q,Mz��J�cv}#_8�2�����>Ix��O�>0�O)�F	�g���#��u����a�5�.i������/�1�M�������#��<���W�{��7���<���J
�L ;�1Y��!
@<�p������mg�L��Ul;l�A����{
K9�rX&�U��,�'����e����IYH�%�}�L�'t�������wpR�'�N���n��]8�pX7����y6�u�On8�y+��wp!��re�)�q�h���=�O���+�"r	j��`�����8b@��"�oyqvCQ��cGL�m���x��4X�L����dtn/ ����o_X���W*B
��!�x8���g@��J�o�l��8�Ygw*�*\���"�Y�����������������A_<}�!��1���T��e��{�|��w��-G/��������^��������5��m�C�&��YL������Mo ���l�?���]��nK&q���%I����s4�'�#{��i/\B��"����u��W[�����P. ��}JOD�5�C�!!B���8=��������7N��U��t.��H�������E��h���N��\6a�$�'j��a2���+��pZ��@�	����n��B&������(1�9����m�xeP$a(��{F� ���6d8c>[8�-GPs�~b$&�%�^2S&6G�������$:��!	�.�fGU�eS�60#�/_�lM�a��(T������'��T.e1�L!��JJ�.��9���3WAt��+�)�~��?�������&���4������>*0��J|�����������\��#��s�.���zDNt�����HO09�����t�x���/�(��v�f{F�m$��5*U�e���	'��������c�Qv�D������6������	q;�W`�[�2����v�scG��3�9�������������8������Z�3�-�*��~�������~2����`���:��S�/�r�A?�?�}h~6��M&JO�b#&� H*�����u�����E���$R�#z0���"<�����Q������ZD��'���r�U�z��p�O����3
�im}�!�\c�|i�h�8����L��|0{�LyYA����*�|����}E�^�zkjC���cK�GTX�GVY{����Df�36=*}�J�M-#���s�"'*�U��w�����U
oZ������e	<G�NL�sa��B���������	�R=9uvv����������1&-79��d�f��R�)���n���'9H8��j��:���D,4?�Xy
������R��v�;����k��#]��]J^���
�g"��f���"W������h���2t�	��Dj0�s��������	���32�GP��
�2g�����e,��f�J��H,�Gp�l@�X��B-���skH�]�:�����#�e3`�-�
�F,�Q���4��:zP��G#��y����	���y�-�����zB�b�<�g���o��YJa�9v>�@2Fcf��||�%a`:�H^��*p��5x.`�����suktw���3D�A,�p=/F_�ce�R�8>�!=��J1�[����l�.`rD)�K-[���t���M�Y�hd���������g+��7�p�=������x�����T��G�_�*�D�@`R�{��-��s�D,�X]�#���&���c��4�0fbf��`%�&��[���y�����
���_�Q� �R��D�v�tY9��.�UYSc��^��;�NYm9����F�#����IhqsUt�+���m�A���pI�����U��|��Nj��3X�iz������76�Eb�e8��G�\��aolb.�������b�E
HjU4���?}$R~Z�s��'�8�>����$�hQ��~K��jpfD�ZPG�
{�"�����Z��Y ����cyfSET+���c���b��[�auR�f�N���q��v�K���"��K��S��T����PE��wy��`��<�'B�E!�p�h�tS��roU,��T=T�2j�R�r��P�[x�o��c|
Lu�7������������&H����(AS�1��Z����]b������{fb�/>T��j��x�4����;���g�
�����i�6��P�*���w����%8���C�-�^B�r���v�Nu+�h@�g�_L_G�7[.��:	$���oT�w���i���UyY�yQ��
������:i����bu'%���J���pJ�f��
C)S�*����*�d�#(�N���]0���.X����p�<&;D-�)�=F���J��#�y���S-������*#��	e��dz�Q�	Y��N�i���L�*�k�Z5�~L�W�cn��aaLU���U��7��Q���X}�+j���9���I������1�e�B2`��z��Z��`��P�q��=0�,28;N�'J=���f)�'��$�v���
(��Yz���T��@�mCi3T#�KF�Z<D�)���&�4��fJu�
5fC��Nb�2��$�>lj%���)��&�O+��=��Z)������e��Mk4�������	���hf�j�1,5�[r
��ON�<�o��E:��*�r�;cN��+�)����SN�h\I�hTZ�K�)�uHB�B3^�aI����;���IG��)��h�i�Q�R�����<k:P�v;]�T����8Z������:�S���7a7]�w�or.���"�'�������C���)�o���F��Mn�*s����q3���E��e��	S.����v�5�unlozCI��m��nSla�Q��l�a�%v#�����z���T�|�*���c��
s�_�4��9��BV<���%�q(.��}oP��T������� �4�����y!Q��U��'�1�+�JXz�<�E���1��t|C�Z�:���}�g���s���D�S.g
��+�>�0x*Pq���}-�R��H����N�a����7Q���U����B��d7�����uS�VI��L�[&����I���7��o�K��;E��xCi�l���{��>��u�xt�������7�^1�\N=��2�(p���'���%���'� ~�c��Q�M�����t�R�!z@�a���.�C���|��p���r.��9�����b�����8��D1�"��I}2i�Z^}|��jco4x��y�j)$�i�hQ@�B^�#S���A<�����"E7*g�P-P�!b�����e����x������\:����e��pJ����N��/DI�,�B%��%w��h~^n[�9�bov��"�5� �XY�d�U����}��sv'������������h����5����M������w����r����o���:�����)Y7��G���C
�X����B}����������|[&����C��?T��|"B���/�!��/�����.�h��/;\�^��sd�<��Hcq����?^�]��z�/2�/h�����N��I�������������pQ[�pac�j��>9���,<��u���2�B�YE�
�s����x����"<�+�CS2��~��:�T�Q���� X�]�a��1A�}J�F5T
k���S?�&�@��|)�'�u�L[�XWP5�Tqv�7Jf;� &&�1���K[��cB��;�sH'�{�?�	s�9�E�>�}��_�w��WD�����q�I��N���}���� �'@t��X���<s��P�Z,�MV9N5Bm�"����9��JtC"*@��l������^p#����K���w���#	��S�ZI�,/�d�����!*!�Y�������rl����$eM~�^��z�����������w�?�W�Owy���h�P(�l���vx�n�r���|De�_�%V���@o�E�"_��zc�����/M����
�B/�t��C:��
��-��g�7��J2J0mqf���t����k)�]~��d���~���u��CE��������BS�
O����':������9��44�(�[���M�<H�S���lY>�4��#��:�pC�5�$�h���/���sl�X9��G��d	R��������<��u��Nmt�0�L\�+wt��]��	nr=R��,R�?F�n��8V6�.�t����}on����o_���s�m3|���S�5���AU��
����Si�7���xM.��j�|F���T�WU/S~���RM�N�=B^<lqt��$..�"%�vM}��]3�46[v\l��8�N���^��4����?j�j��������?���2`�=9-��p�+�M2����$�
�/���3�$���C�OYn1�Q+3N.�MF�$U-�%�����h��i����jC��~#�U?w���5����\�Ee^�1��F��L�=���r�IL���������j~c2�o�''9���uh��O`����FQ�#�����{-���������9@9�1ut�s'��
��g8?�g�������B���3�@���C��U�O��&�����u0��N���������cR\�J&�2(���!��?����GX��(�����+������:��S�W���!R����6��(�*�R�$�T��_a��b��>P�F�K�Z��U�O�~nZ5���>�@Q-��Z�I���M�=ST8#*�5��
��P���RU����_��~S��{���/��gLcu�'��;������M[�zw9��������Q.��7������^���}K-����;�~�����?��cc����_�U�
���U��N�9�3��io;��&5��w�MQ=dor�������c�q����u�\�Q�Q��*~=�u�E_/bh�r���*���v���1�3)��Y�����<&7��1����'�	~�	^e+��^S���� �Z�U
�t���=�D���5����6�,Tk�B5�,���E�A��h��e��w~b>�]����U2���,~��
���{�#~�p������+��������������y�_�x��)��l[se_e+>������>�sD!$ ���p�H���f����\n�^�����z�|�g��yD16�R�e��g���
_t�;�.ox������dA������oB���S������U�7�K����4�Qv��%�Y����`�w��Z������7�����&���R\��Uc���3T����d����������?C�)n�k�@�[�J�Pq#�V�h�X���T����2�j���>�X_M�Z����V�`���b��~�Tuk����^+8��T����w�d�=W�w���6�������Z�m��:�gp.2����r�9��w29��F�����I��]k����t4�6�����H%�����!hu�V<b������!��?
F{^R=uO/�"n����LT�����;�(a�(2�]�|�E��q>��o����)�����\��0z��<����p[6#D�;O�SS�&��w|B�@����g�#��8o(��n�v1F�� ��B������yC�R2l��w�!u���Vv���q�\�)#��!lp������:��-`���-8��%���%
[���/�Y�md@�O��r~�rJ^2�������dh�|7G�
������&�K����	$���R��m��[���{��p-�||�%���<�B�sA�O-x�c����kt���	�����S���iO1��&����� �y_�cHX%�D��A5�r��s�SBc��E%~����Y~��#��O��Z���x�f����_F=��(C.��QH�P�g��t^�[n�s�-�������Q�)'iQ�3��SZ�����Z�G(����l'[��rj3������ ��b5���:*}����1=�?=>�R�t���j����s�H���S�}����rv�wpt4(��tBo��x�Pa����m
���oz��W��_���_g$����?���pc���)�D��bT��mS�1��qrr��$T�5�W!0��������2����(Wu��x\���k���I�M(�RV��F����h��[�9�c�Bf�A������`y%`�H���(I��s��]Q�*�Mih�=>9t�A��	 0Ym���C���>��������Gv8c�w*#��:�Bbw����-`������|S&�F��B���)B�����fpV���3<�}��s�)Q�`�R��=�	�vc�AL��hVu�<'��x�7V�_�9�H���>z��x=����}qy�x�6{��SE,7(�v�`l�{��!C	��}��(�	%�L`[c�G��Bh<���.%�	��M�3�O����@7&p,�=]�������#��`��N����w���;�|�#����?�eEHs1�H[��\!/,�y��G���;�o�����~������n���d�j�� C�%C}PT#c��Cw��[��<SA��	�Z������U���8���t�g�Hg��}0�w H
�� �J�O���),u���e�\�T��V����g���� ^*�;��h�Z�l$Y&]Eyi�!je�VSq�~X��y�d�]��:�i��^n�w
6o,o����������pc�6���$�t��:C���}����w5����������x��{�P�/�����N��TN��B\M(��e�V��,;�'_��u���_��tx��r�Z�U�R���1��)�B�zP�P����L����Q��m�N��m��q�(���q�X����0� ��!�GS��U����h������*��*a
5$�R������a����F��R	���}�����2�,�B� �.%E���@&���$ K&b_6R
B-o��:/�[��H��OK�� �]��=�`���#5�	���C��$\�
�Bf���'i^df,T����zWc���������EM�IfN��E
j&�`���,[��@���1/�0	����FKTl�I
sTq�:
x�@�Db�ZX��;����^u\���rP
6����\��6��U3�����zJ1�K��^��6?�[��_��ee ���"UD��6������`�()`s�r-j��E,T����qvP0���I�����.��ee����K����������+�;���<LL+����.�rB���������S�>b��X�K�����I)$R �;�8��4O���@?�V1��b%<���Kha�����+p����!O�����B�I��������R��H�j����F�������Ul�w.����:�0 �����{�V�����6��p�T�3LI��p�Rk���U�&����S����8�
�#�2�B�bnU��(.H�k��f)����U���N��K�exA}~@��9n��e��	�B�'����8�=�_trV�9kS�%�`���5��@U�2��� �� 42v���
�G�o!�^�0mr���5� 6s}f��������%=��_������7 u��7hT�Fr'�C��d�����%����S����4���3�V4����d�N5�O���X�b�@e��X��b>�p
�� ���!�b���@#8�*���/����eh4Q���( $%J��W�,>�R��@���A�~dZ�_���*Kx��x1���N����x,��'����BZ��z��d�Y^�(t�gaLb`��}��m�\h�~5'�6R�E��;�E�
c_��/l���
�lx��}�hv��$V_��{S�[�h�>)9�!�e���E��ai�N�[��������YlFx��t�	7�%.�P)*/�y��dg�?�W�6k���Z�U�F��%P	8�����J>P���\������AX,Q�_�/
P!A��LV��C���/��_�����x6��	�G�N ���v�~��t��^v��W����b���8������QL~-���?���5�a�j�I���>*��67dx��U��}���jw�A�������������G`51HI�6�1����\V���k&���0��WM�A�*��9��j�5��s��t!S�\�B��I*DVI
J����3{��x+v_|Q�U��Y�
�~<��oUI��hN:�+��S��y3+ ��������V%[��	�z<�������V��,��S��>������$	�B�k�xhMR�u��J�����
����J-��q4/�]���~�`�UN���,��{x��������e~j~�����Wh<�d�)���E�*����/����$I�I'�����6.��(m �m�v#J�x�|��H4;'�iZ�		W�m�K����u�(�+��x!E@��-/�`Z!���%a���
SH��Ye�2\��~��t2�Nd��/��&u4�Z@��%�{�"���
K"o;���i����0g���wG�R����F���t����J/=_DnT��d������o7E�*���=O��2;[��9���Q�:����+��k�������'1�2}'��a�f3�XcL�/C����B��Vg�'�I�{F����`78���|�

yQ�vr����a�m��9�F������4����Q�.?���$x�<��� *�YBX�h�TI>MV��>+�	�=���S����n�gLu)�����G�t������fZ�B
<i�n"�q�z���B:&W����c�b�T����%)��m����[N��5\2��	�m�[�=�
�ELr����t��KRW'L]�JN�v!��y���M���y���]��&�.�R~�Y
�9���fj�B�B��;��S��}'�"b��l�i�K�q��U}��$��F0�T�������.�����M���
����iJ<�.����������
�o��g��M�R/n���G���*����E��h��C����H���}���n���u�c��0Vm�5�)87$@rV	>��7b�X��SU�7�Bu��T�x ��U.��ih��4�<���&V_0qU�?W������s���x�TS���C<�/������1iT��S�b0���sZh�	��H.�P����	�_������4��r��%�����K�3���bU�	�b%����|R���V������8��>H� ��j�8�a]�X.9�i@y&:q�$B�	��#
C�,��rR��x��2+�j+S_�"�J�H�p�cHLf�I4�o���ae
8�����1���J��	�rQ�������%���� ��������������q
1-h(��E������zs:p�Y�
����X@�G�3~�FF<Vjq�P�'�)�u*�H���B�N,w�l�����T�c���+�Uva4��s�C����r���,H`/F
g�����k�:z\������<a0^qg���o��:b&�F(��Q�F���������w�:]fTpX�������S��Pn&��U`����u\�O��9��*���M�?$4�u[	���
Lg%>�y��}�!�2&y���)���X�/9+��/�9An��^�6ej�f��D�����GoO�=c�&�?�B^$}�W�qq�T��$����l�f�./���y�jP*�(��.�3�'{d�K�JIS8�l|�-�����:�{� ��\��r��\�y�P���2K�)�t�M��jhd��/@�.�M��0���,�{m.�.ME����N��P�&�����&��������/�H��]h�CZY��Vvz���y^0��S�~'�N����)-KW��P�;o�������w.;W���p���N��E���*Fs�.�sk=�X{���U��������vG~z����F�������<���g7�t`�<������8����^w������E�����e����7�7$����z�l��VI��������iV0��Z� �������tU6`k}�e�M�Q�qw9O�D�:X�#�4L���Pl��j��Sq�M������7-�<o�����u�j��}=��Xl�����4)���y9;UnL%�
>
�[�J*��c7Ja��O< � ����4	�-:u�(�#����m6���>�C}��:���FLfmb�f�ARKaU@�m��V[;i����/� ���>{����i)�b�w�5��|����2�K������l��Vx�n���>�x_LA��?�~��e	h8-�A
��k	u�V��M���]>-��sZ���e��T2{dB����1�q.W:��[�8�:X���3��TAs~�B�y�s��e���U^o
�<�w@DZc+(R>.�^���0y�����e
h�d(#s�#��j2��Y������&gH�TU�L)9�O-�f�P��,G�s���T�������oD�,��QV�L%�We069������"�d���M�_tb����y�6�'��m��jJ��N|�^/��)8�<������,��-�����<��jD4:��(�0y:������:3��@��/�I5U�L�`�4����{��q�4#����=	�*JV�9z����
#x���8w>�!��<9w�p�0|ob��@&nuu��W�8�P��(�����z�����L���I���x9��Y������ ���M$�Cm����3�R�@,��vz~�k��3�IN<�X�j(EH�o��>��/�����+(��\�����;�rH��L����6Oo�����0��S��L�2G�n���4�#�1�`�Zv�l�����V5������w�|(�2���P���ui�W@��!��c���ad(7�3���zA��n���;�2��2����3����YT��B����T�.��������e�E��T!�q��������$�����w��0��3�W-)���l����}�qxO��n�/{�p�E=�U$��3A�:J)��
R����o���]���&�yV���e!����d3����r�wZ���KfT
�V�K�S��T1����H�������8Rg�����t�<�����A�e�����F�)�9`VGy�.c�()5�q}^����`�e���E���& &d=@��VHP ���~��s�����o)�����\B0��2N���d=�������z��W,Y(���PW}�U5_����G����SN���X�E	9�oLuH~*q���&��z�!��q����G����4[���7����4�G�r�-��TX��U��H��VT�����WF���C��6$�[��1�����%�.txCm���z��)�'5'�F�Jw%��N:*�Z4t��6�z��@���dB���?	]&�?�xO7�� ���]z�:Y���4t�9���+������T�������W|=��:�]�4"��$�E����)@\�ue��/[QV�W'7f<�o�f����(R[r�����>HT ��0'�
��d�����
�A��-&����J�IW��
���M4�2_��b��M�����`?����~����kJ�@S��HB�g�aW����,���KC�w��/_*�Z�0�+FD�RyX��)m�xcn#�`%����kM�=0���O�����9�	�������`$�$%3D;�$��$��R�����%�zk.��W8���>3DJ��(�{p>L�5Y����+����gTx�P�q,��;�U_C����:�)�*�-���Sw!�7z}�.��U�W����I�����TZ2�%���@��0�[9S�s
�������~��.�5
2�J0�
G��{�Q�&�
���3�?o��� �o�"�*��B�^�?�_��������_���s��,���aE3KK����(��&s�i�M�k���I�����~�4�MR���dw��Iv�W(���w��	���(�~FGY}��t��v.�r�P{JF�whm����e2.q+=�����@0z�T����\8���;-5�f���.6�kw���L�gf�[��q2��uO�*�+��aL���t�Vt%����/-�m-�w&�x,��/�����4�7sn������������y��6� ���V�9�������G)v������a��6Qr�y�C���DnGW�;�=�N��,"�+�b��K�d`s}Yb=w�ghW
����u6����4���)�{$U��"��w]�";���/���������� �Dk����_S���L�3J��e�\_����^Z�����E%
�y�,u
j�W�S�����4���r��u��a��~Wm���D#o0��>2/�2Pu����R��K�5�����5.�'����E?{���*���f���]�*�HZ�����t�?��	ay�^����a#z��ML��+�?���\Q\,���T��]�&�^�TY5�*��<_�����0���=��c�����b�J?~T]>�>��7!I����~TG�Q������c��bh\���l�Y���$��t����A�Z:���^���"�%��V�����?=.���$�k������(�qz������#�[3��&y�a������Px|��X�M�������Hb����p�y<D��8����3��|�Y����� ��� f|&gT��-��x=��{[��t%���^�����_+a��|6:���0Y�����������/Ik,}\@�����^?:Pt�W*�X>8fX�d[Z���Y�l]@��kt��BH�v��/V�,��O�I9JJ�M���N�`F�.U]6�{9���Gc��Y-��8P
P5cP#C�$�����-3���n���=�f3)�lz��$�hSy�U4K��(?O�
��q5~g�,*�_�����������:�wOn����!g1�C����QR,�������C�]%����Q��Un���;//T����U�]"���"`h�
I �X���k�)�%vR��V�#<�W1�.����s�e�]�0H���s����^^��7/�D��#�`H'���G0&^x�F��U� � �P:�����+'��#����K^��<p��1�W��$.n�{#�l'��]��J��S�Nk�]��*K+����:�����������(:-r0ft,��{oai�;�������>x|	����Eu!v-��^pP���}9~*#m�N�B�z>�~���H����Ty]�s(����K���'|� N��8��,m���(����F,��&<�����x '�������c���Qo���������d���]t`������t��jg9���nJ'��O}�`2ZA&��@ev���?���s�`-��Dx������szv{}��y�������]��5aF��&�-- �����@	����%���x�/��5#M{�k'*/&SI�Q��i�d����Fp|vS���Q]MT-����=�������1�V��A�x�;*���
f����4$��T��B��8R��)��7����xwvw�g8�����%jM���Z=�0�aM��th��G���s6��krRv���H{�/�������'��y����s�1�n��K�����:k��X���)>cx��������{J['~a�E,�:�f2bt�^��4��A�DH�Xc��0��%X��2�1�P.���OJ�W�:s�q�({�'���qz����6�H�}�r�u����,���^����B+i�g�����j�L(�"���t4uPg:�4�J_9��<�zHN�k}��)7UQ��~c�j�G���p��+�o$�T����%c�Wq7r5���p���>�.���H������j��C���#qs�J��DD9�h�H��b�#��TL�G�=�D+M�4���:�;���
(�N*�*�v����H�{D%I����v�'��7��i�,�@�����R��`m�d��;����(���R���hb�|���z��p8G�0��H�%r�	�1�1�����v����(L����.���C���#A���w���������A�`�����H1-�������
'�]�b�@k���_�I�s��Y������}���Q�������=�%��t��g�dx��M�]]��������j�����W=�.c� �f��\��I�axd��r�?��'B|�����L��lV�W#�4 
y���IK�6Xc"r�[Q��p#���:�!��>�cd�#�cZ�`����������`EmV���T��T�-�e���&2!�gZ�(*
8���,�A�
1�&�$���\���y�j&GaA��n����5����x��y�y<����,�q�Q�1�����M�(d��F����:�Q���&�� -T�"gy���C�����%��;���
�d|@C���{�'bE�ML<C����@��X������.�����(�y��6����uP|�b=���I��c	�13��E����/-`%��"��������<�	g��mM:c�31mW����B�IIv���B��p����*yCx�<���N��u/Z�63z����}��G�4����-L�pV����{4�w��|���
k%�2Q�R%��W ���M����aQ����8��&[�F��&G�c���W{(�c�p�����J/R,��-����gn�B������{;�	�pH�d��k@a�<S������������H��:H/<���,7Ff��f5�\��C�
N���M2Gl���V�������"f"^�0����Q+R��Y0�����]��l�5�9���Mg�KcP��{Sv��5���C��y�V�B��u�sY��!���<o�:�0�C�T\#��;��"�'�4?�X1��LQ��s0��{Y�sY/��`����,�d2�!���q���te��':�j��&En��A!"Ka��DS��F�V�2����p�h�Z��hl����~��F��b�\�7��KN���it��BA��o�PHg������<�4�����{�%�ML���rk_-��-��J��cK{3V�n+��w����j�v$
����cU�yn�R-k�������A�B�/L�H��xs*�'
7��7��=��������"	���gA�'�]��+#��;�!�7V�k�#�/`��!�U��:{�J�8H�������G��C0����t��T��qZ)�zu}����<��O&�H���������7.���Qg�����F���3�:$���jE9���t�je�7�L��6hC�x��a���O�|�
��|z|3-b�Z���^�1���'�ypwD��9b�6������-�q[��c0��B��e<���kgR�1�!OW���Q�����m�;}=�E��6e
�}�ua>��W�E*��G�A;��+z�L��Ik����M�1:������W�a���j�jk�+�����g�
���5HW�7� ��%�;d���\���|�j��
�fn���}[��$o����$�]�BAQ)��x�#�JAB&�-���K!�V9��EGP������1H�?����b���L�
ZI@�P��#kUgLQ�W�]#Mk�9B-b�,(���O��j���	���f�$;��K�d<�q��K������K����RZ�t9��q���~2����'�!�������+~��y��/�{���e��b�D�}�6_����u�
�
�F���M���Q��v��{fP[}����:kd1�"f,1D�-M����%2'\o���)",���>�J��+�/��K���6w�Iu��/�`�4�b����!`��CB�YOb�#bv��Yg�.8�4��0�K����"�H�Z����'�a��0Q�U�X�hp9��bL>h^F�y����T�����{y���_��
u�
^���U�V,}��{��xM�I��O$Bb�_��s4j���f�Y�y�a�[�	I�#	I7�&�H[�F�d�������*Ib���oR��f����XCRrkHpq�=\m!�lm�^�I5d�]eN��[a���^�	^i$�;�Q mW����`PX��t�D
�tt� �y72&��R_M�I��F�o��R������[���S_7�v�k�[�b�o7�����l����J�6��Z�
-b�P��o�P)�i<���p����7�o{<9��wn:d���fA�P�A�X�zZ ���
>b%���6X����H�C#
�K��]*3d���7(�R�j�Gjj�(b����:d��O��(~
�����r��-��q�Kja�M�B���a�)�r�����f�U+K�J{�(���Fla(�d��������&�xi����j�}W���Tv ��e-��Y�	�z~gk��aT"����tI�}�d]To����n���.~�B�L��+hP��Wo��^ _~�.by��.��qq��*�N�*/��H���Vw��%�=i0&�n���zr��v�))dK���Q�V�b�^.��{y����E)=	\�h�/7Zu���B8'�Q�h�/�f�����a��=������r������7z�������L�E��y�U��D������
~V��`�Z�V��������%�,�)��W�1�:��s�^��v�U�s�eu�����Q]�}{���U����s�;�X�E���5+�A��Q����C��G�G.q(�p2�<�C�)K#�&�~�L�	L-e�+x�rc�=�I�����<���A�0�t�����\�=���W{$������tX�����"����<F���x�5>R���
K56,��a�,��ES�Or��:G�cKvS��E?��3�	���4��4�^?j�������^�5=Cs��,{+��zA#���!]^^~�;���7' �h|����q$M'�b{�V���J��m����j���59�w&G"�E+U�U��m��^��)c����������B�GF���S!1K��2�0��go�[D��$"ka���st������{��SX������p�B\�h��"_��w�9�!%[��
9����t�CF���a�������7��V
(.�a��2x�R�Uk�����7����������!�W8P�2��{��p���Px��?�9��a�6n@�I/,�<�f���L��G���vd	
�N�Dl^��R�>0HA�{
h��:]Ba����7�G�S�|-��F�z�����9j��
�9hdw|�LzRIH�k��>bZ�x���N7��{�tB��}@������;�'�����7�7���|��U��svvr��\]�\'^�0Lv3W@�����N�7(uG�$DM������8�����~��M��`�]
@X;Rs���x��X=Y�jcX����b�_o��~����8��������4Q
��j�t�� c�bE�5��OL6O
��r!�:�K��,@X2x��+5���@���0�`���?p�$;�~�$�5�B�+�\��8��t��)���P���"���~KE�nN��3a�q����3�=�R���r�����s��*\�� ��F]S��4O�^�1Bj0<�F��(n��S�'��:ib��N<7g�~)S����V��5��Q�?��F���\� �=$N�FW0����I��w�ki��o������L���������L(� ��86�����='�����g����kg��g�����������=���o0��7��	�pp�	
"��������a�����]c����h��crx�
C`7������� �s�����V���p�S����&T(�v������`��n:z�w��u^��k�ntR�����4�����v��m��y�^j�v4��#��/��+���Yn��LdosR7��\�{�D�C*�b���e��� ���w.��`+<�<??���t��7��g7��_�}�=�Y�������5n��8�\�'4��m���1���(��P��F�]n����FR�B,+�z�w���-��J$Jx�]'�_�W���������(8&�����+K&����b��3�_�W4�w\���ho��BO�/���m�kR�~���t�RW�&99L����������J�T�bG{���}��L.p������>���D��cv��a�Z�����s����t��:|w'�x!��'��fs�/����R�Jd�Wf�,�]B�����@��-p�Wt���:�������tJ�mP"����$���@�����X��!v��������,��'R�%Bf�p����,�;1���}���W�(#dx��%��S��NP�?��3b�P]�s��i���1
�����J�0����$�w�r��9~�����,z/����b�����Do8�;#����y�_��[.��p�n�>Xo���9�z�v����~�5l��A+[��)��O�-i�������tzw{��%]���.��D~&o�9��7�=�����������r�
�s�4C�pS]��x�o�iG�O]�>]5���5U�sT-Mtm������:���H�6�-g����7��pMzv��3��?��M}���J����[�bs�U�������Ah�G�HQC����O�r�sG�8r�tn�����������wy���������������:G����D�������q������r\\�0��9�L�8�<����~zu}rv�9^���M����qQ)=\����pX�����h�Z�V����
���5	HY\&�C�p��PZ���^���b�_L�1R����fqk��e�����>���Ws_��9m�}���79����m:]	�����p��f�b��*�;A�ZI�V��=��A���1�?�z�J�V�����=l
Jo-��e�I,�J	�!~��I���������pN�|��z���%��h��_�F+|�>�9�)k���{�����W����KY�d2\�t�%/3����g����o=��s�=�@�������TN��������������{s��� g%���8/���ry��2��?_�Z�>N�MR��k��J^uPi�0�
�a��v\}��1������6�)|��"Y���O��+���	�
� ���
���?�ko�`��<���
��tA)�^_�;R���O�OT.������<�	Q��?�\�������i�/_���y��0Xz=�(�~��U��jM�G���x}x�2�9�..�:���I�M�:��2?�����0iU"�hiU26hZ�B;�J#�P�����`ge
r�]�~6��+���v3��9�t�ut�k7#�N���+���j�oCf,!E�����i���mx|Z��3bx1��Em�$��L��J�Z.�������$�+����~�T����hQ�_	�Q���pd81~�5��(���������O�+�J�JS�k�r�Vn4+UU�T������?������i���|C������[�j���Q�1��j�~�:�{�~�����zD���=6GuLU���rS�J����c��d^��tLA�~,�O���w����>�x8x��A�_���e^��2�*7^�j�K ����vw��O�_��\un���Rm�\�IW&.�Q���������*�D5S��I0��'6��#0^�`fbtlA�r:�|B�{v��Nrp��V�_;e����)�n�S�(�v_o��Cg���]N@�X|�{�5~8��&[;�$���L �Z�i���6�v��r��������U�X��"DN<�~K+LF.��6$����7���X�5�\�Z$�h�'�X�V����\��p�=q��.�)��BR
BG����xz���6C�� �*��Mt�m9���oG-����.�v���.��@������V�6�k���%7%���G��5>���Q�����&Q>�h���$�K����&�/�5-<����r1�RT��GCp}�g�G4��"�"�V|duo�hB
�e|���D�S��2�#�#��TKbE<z��&x��d�����f$�	b���A�#�
��ft�|�y*�"�d�WG�D�D��dv��sE���L/�F!&B�TE�`��!�p2�����3��h{tQ��h.0�q�5���i�A�jbETr�]�XsvSl�f!�J  _h��M9�������Y���8�W�<A�*4$���5\w����U���p�8?�,8��s���l9�/���bM �aT�6�2Sz<�����Cri�����������v��F+1�k��&O���rq�S�X.��C�A����N�����=P��-���N����~��'�m2 ������C�
4�w�pP(�����=w6f�`Tt.��V{+s�b.gQ�U���E��V�U)�=w6�}�dOV.�l��Q���g&�E����f����?���_-�������4���_�%;Wos��M\>��B��r������9�x��xs����d���&x ���\��}�u�\�d.V��z�y�x��s�(W���_�#���\e���D��lV9�?z�{�
1H��6����YQ1�bM��X6��3J���b��{��Hd�\����f2W����f�����H�P#�sU��JM����f�a���=�55�'>?(�n��m^��z��w5l�5�,��
���o���k�a�X-�~��76��T�$�+�!�1�9�"C
�!�O\C���d��_<��?F�G�}x�J"����@�g&}Hg����=Wr�0�3���:��&�yN��%Ze�q��k����
�"��Bh�_����;��NV��T
|C��Yr�H~$�������F������s�v�]������u��us�aF������������Hg��z�N���v2V'����
&��^������K�C�6S������br$J��"�9�a��C�z�/]���_~��U?��[*���0��r�pR��A2��.N>�\���].B�s����b����r��h���Q{8�4J5�b����V8��WVC���$Kx�v<��r�9��Sm�;���'4q�<le]���2.��o�62�$�'gW'�������9q�w.��\���f�!��)c�i��(�����<���pI8�����%_* �.���L�q6�c��S�<�#@�K:.]�9��k��I��c�j�Y����=wm.�#����� i��c%3�T�P���[~���=�V�7��W.;\	�Q[��&%y.��yv���!�.��r�J�\4E� 9�����)]x�}W����
4��u��N$\o?�������#�'v.�X�0�+a"�|'$�X����a���%��[�&�Nh�N��FrAhs��H�&�Uo"���+@��`���Q���j}����V�x��U#m���	�����3���C��z�<�������gtk����8�Pk���%�-������B��C8/�F��1��=5��U�p�I����e9���-o9�
���BtSH��D�GW[`��D��^�9�v6C�����.s�#�0X���<���e��3�%����K���'��#�������#�20��	LD����E}��1�&����3m����`���i�cV��
����Xl�~����q`5��������^-�"}�d�& �y3�Y�(z�$�+���)a��tJ�&�,�lwH����%Y;������	A.���t����P�xE�����'��8� +J�t�b-��+�\��\b���u�B�Oq��2�^�O��r�|�F�����)���C��"g�� ��R�������1�%3�-�Vh�#�2O�t%��]^'/������(Z��so?�+bz���n�U���H�D��3s1�q,���U�����1m���R�����	/q����%��x��w�Heoo�I�����C�j�������G�z���<?6���n0��u�#�B�0H��'��{��Gx�D��^
l6^�-�\c"s{��`8,
G���]��x������l�	��'=g���r�s�#�9 ���`�I���L��)>�g�e�W
P�sTg�t�`�4�z���Qk��H���{IG>��K�Y���rU8^P��t���o�i]w��\���uR������n���7������E�Q+���,�N[�E������z/c�_�;W�B��<D�D��;������B+9U8F��I��zH%�f����<��>�|`�1}���:��}
EX_�RX�n�����3����(���:��;�xtv�=��	-s[+
-��z�Vg^9�6���9zR'�����C�r.�X���]��}���x���X�R������NQ����shK%{����X������lq��%^+h�P�Lc��������?E�+�,F�	�g�1�������I��Kb`��Ri�n�Q��j�=��;��W�������i���:�^9���9L�bq_����?9;�.�Iy�G^�|��v����`�L������/�w�?7���=nv��70�#T�Z�Y��Lt�MJ����Z�NB�XK@m�����-�m	
�=L�'y7��y�&W"J��`ywOVO��
�e=��=Ra@�q��L�r���@?-8� ��
�*�"R�&�;A6��3n(�)���%bV4�4���B9������x�x�2 pC9L��1��onQ�~��2���;�K_�<4m��������m�QY����7�FA�Ym���@��x�W�<��F9���Y�R��z=�P�y�����'�������^�������z�'��7w\��z�0��3|�Q�)�>��H�'��Ad����w>��\�#���u��vO���:������aC�����heP�@%���3�������n,��������������w�:���]z)4$���K���@�=LKi��F6#*��Yj��
����R��x��������������{{�r�����n��`~D�� ^�pWZ.Vq�
�����A59�-��=2�g�$�e�.4��=�-2<GkV�Dwu����{��!jNa�&}���0������	2r-��K\
��S�x{[�*�������G�[������q�b��r�5zZ.��tp@���-Zm�����M.��8|����M7]������������f�(�uB����6r���i��s}�����s�q[e�W��|h�i1
2� ��~z<���P,'_����P��uR�Z�~�zQ8|��pT��
K����}��� ��F�"�S���mC?'������7E�W��������-�0 {�b�.�I�����l��e!6���x�@sUNL��������N'�+���I��RO[[�B6��)�^�pR[�u��-��@@W�
U_�������=��������.x��l�f�����t��g��"�`k)SJ��i~x����3<����zz�(�vh�i��xS�U�>k'���*(��P��P��J"CH�/�	�_?�-D�z��w((�L��P"���q^�����p��������`vOP�x&E
�~�1�r���@��
����pv���d�o&��w��UYu&$��l��-��m�I�~����(���M�|)�j�6'OLW���'z:��4]�M��
��u�u������p�������l_�d��7}�\>�__��?�_4���z��-�XzD<6�a'��W#jg1�;�
1iv����?�>�d�����}���z<>	;A�M���dD�0[.��}���$�������%WZ=C��������E�&�[���W��sz<O&c��s�]��U��w�4�N��X������N�_H���;��4�M�iDkLG�,Z�w�h&����Q�������GuJNJAp���;����I��W�#�>�x�����-E�-y����#/������
it��,�T�O����|ok���'#$����_���4��1#>����IA��|������dIa��;h&�`�$>�sw���>m��g�3���uOn�/�[ry+?N>���./zW������{;��D�^����������B�^��������*l���}�LW������%R4�v�Y��*�b�����W86��Z{�LM�?U���R��:.��bK�z��s�����h��LnZ�YO��VC�o��z,Cvt��$�����F$�'�3���$��:T��~��dz6G�Ty�u��b��(G�Q�T}�8�`
�������#�w�/7��d6w%SI7�9����Vw����vB����_��TK����iC�����!�l����S>C����V�J�M�p��vbH�^wu���F��w?�S��xh�Q�Y��-�����(2�eS��}�=������m�W�g1�I�Q�(8����$��}��
�)���[V�_j����CF���\�o�J�}��Bs�Y)W�h)�'�6��Y@��c��AH!d������X,X8�>�N����1��I�Q�]�����<\Xt-���#�p):wj�y��PD
�/QC0`#q2b!G[8�
�|���;:h=.�����t�<���~�*O��"U�aCko���@�W��1&�Y�ZA��DM��}��r4�X�\Xs�pd���7Ywtw�v>�-�I���$�R�e��i�.{8�)�vR���FK���2S�2�&����K���?��|��\��\,�*�
Y�W*��W����<V"M#����-��Y�9�o�Z���?J*��/�A-��q�@v&�<T���,��a���T�+�		M�"@|""�%��o�
Eq�����$"�����MX��;c������E�E�;!�QV[8G�!��y2�,��<���lN:rS5&��`����r��t{J�a�XE��gJ3T9!��@>���-�5)��	����t�sH-	-��k*Y�@ZMa'>-gL5����*+�dS�6� 'pW��������/j��j��w��@�e�9��pq�I��&������2���IP�4D_e�`3��;��kf�'n��}������g��MJ�F�&+H��h��/o��n����t��F�~�[[�L�(:���������'�h�`�
�&4,��������m�����VC��9�Z�8�G5��4�<�Z?���\__^[>����$�$��n6k��-��Y*[0M��@s��/�+�s�v����X����_��!@�%EJ��D
�.$F�z�1T6�Z���P��MF��*�[q�(�:f�5��aBk�L�`4v���A���Q|���!V���!���#�����v�%����#���R��7�����3�u�����d�OR��Y��^I��:T	�ERD�4����2��-�S�����}��j5��M9$J7`������������Y���d�g1
���p������$G���������|�i�������k��D#d
�Q�]�HO���G�bjM��z�!>�Y}I\j{p��w���*=��2���GZ{{<��T1f�\��*�nB��/n@�A�QY�dd/��;������h����m�%����w�lY��U<N����:C����|�N>�2k8�2^�b���&���7{�w�^����;q���=A����7p��1C}���>J'��v�T�p�L ����q�Sk���>��}(�$��z`z�����>����0N��Jp�O�P7D��<��(�b�!�����.���Mln�����L�~�g��q�v;�]f{�||�oP7R��bB�zd��ncF�d��y�|C�1������f����A]��O�Bdu������{���%c"dT	|��A�=�
��J����D�C(w���<d	���p��D�>O��mk�V�D���t
3�@�sls�]�l9��P�i*1
�S���3}���;����U�����B���c���^����{�������8k��Y����z&H!���;���i%�c�L_��������W��NC<�>j�/���6I�sl���|�a#�� ��W���Y ���x��3�����~��!���:a5y��p�������6�k���
H��)dq �%goJ�	��i���� m��-%��
��Vh�Af�
�5��G��$/���)���Q����>��tB|�1�GP@�������a
�;�����N�s���"b3)�o��s��i/���q�r���[|^]��A��q��r�>Xo�m���t���l[����0h�A6-�n-a%�#��4����|�
;Y5���4J�f%�r�,�$��G�c�;�=u{�*^$J:���~i�����*��~�������@[a9*Y�v��c[�*&w$9h>K���&k��+c���+n`~��,�|�BH���#��$6��c��$�)'��|6J����
������I�&��]L0*"KK;�1�z����b�
��rt������[��#��H2q{3���$j�@�ey������B��l;d?�pXK��&S�z�/^�h�g�R^��$zZ�Qc�r������0a��n4< ��'6pO��|x6���`9�x�<�������Zo�$z�yy��=!����}����J����UB����!%0�7��_���?��������7���z�Ok�����l��������	���bt�&�9�(��a1�cB�)j>�e�No���D��Ua�"$���6$��f�rI�=X~����\�r�W�G�"�,Ob1��
r��z���*<\��'��d����j���[j��2�r����c�2����,,�J�"�W<�&���:;���k�ab�u������l����gy����;N�kL0�
1y#�UTM�p�s�X�c�+���T��I�S�/r�
��m�Y�@�q����]�+G��(���Ws�J`="�m�2��{���?����7��)P��#�x�����Bd~J��1Yg:���x�i�R��s[��=�o�����z�������8�x�"���2��DA����J��)��Ce�
1:�
@#���Lf������m��[	�k��u�l�-�
������������!���q1�h�]
�Uv���y����[�J�����f�>q��Gi
����n��%�B�a����V�,�"��9.��GZ�������A�H���&57E%����[���Q��o�6OQo1�����:��y\l_d��?����.����k96�C��+�g�q�>#��z�7p�C�X�K [!F�oc�^ScX5'��d�QNt�@����`����"�Ffg&��"�$�;��z�3r���~�/`����3]����(��Z��5NI�y$����t�=Z���C��Lztb�!���:!h�DO���*�om-�"V�q�zly��9!D����� ,����d.�����$k������J�m\e%����X\N&s��<�����FdM;�#��r�1m��%����h5�+��n(u�����^����a��G+���.���H��Os[���y��N�����l��HVb�*$�D�I��	$�p$e�e�,�����p�����.i���)���/�����%��Hg>4NS&��`���(L	na����"r�rv7MR�k�E%Rcs�����1�E�h���������	Yr�������|��}��*C����{r������[wxz�5���X�z�_g<������nyQ��������/�U ���
4Q{8m�X�����\�/4��3`z�h� ��_�$;�1�{���������z�^����tO�O;g����u?��^��������D+>n�{F�x�*�!�w��{W�w'���:��s��p��������V�,����'�i��"/y����lg��%����[�c�f��2&���	*9��k���+���d���u={�#�j�x�r���pR\��N���wM���m����4�l~Zq|��B�H�bfa�Um5�b�k5+�js�jm6-b�g����:��5�����kE��D�QUs��]��N6�]n��Y���*����1(>���Q&]N*�����6��j>�7�2��X?*w�~�P#�JC��g�V�4�?zN}�i>CL)1�bq�0Z^
�/N����=+��*"y4������V�B��i��7�1�x�ia��m�v>�M�|��(0^�BH)K���u�]#�d,[��H�����������Wn�]��b#�r�N��BL�^`�5�������o��i����3�f�d��bl���]��J?��O��z��#�3���UC$xa�<���A��<��@����<���@b�PkC��Zm�[����,
���t����v��88�0�Bd����8W<;�E�p����'^�u�u����)�o3�<gT�����_�^/��ZP��k�b�����^�Vmf/�i:�5;����0,8}���
,��&�J0>�G�G)
oQ������xJ�iMF� D��c�`����I\`��$����pK�R�
��ua�����>�9:�A��-2���&�:�g+_�>�������A�Y+h��?���K�:*~�N;3���lOF=l�_>��Lt�OsQ�f^ D��rrUV�qo��MB��[J���0�|%�b�������"
8;="D*�X�YB6��e/�����id����:��v�XmT��v��8��i�w�<,�N�Ie�r��Q�=q�I��(-�q�����B��K��aM���C��{�����,�%�E�7�x�����C[�x�W�'�r�k��!1�R�d9X��dE��;�0E�2�k���:(���76�U�Y�V�E,%!�c�S���"Ny��������"��_��z�
G)�P*��Tg��2�!G�c0��%>|����v�>q��3��
51�����O��u�����,�n�����/���A>�&X�q���	�����W��z&��"��������^��`����7j4��l�sQJs�+9h���>H�A<�hS3.x�x���K��\�q}�W=�D��Hq��^
Qu�F��3t0HTN^�%
�9�����	����h���m�*������#�*�m��:t������r����Z-��({$WQL������$^d-�������E����9�*:��y�9��S����9�}�>�\�����	�z��7��F�l�7���Mt����D�hz?�&,N�SD=y ;+,'o�,�t4Fon:���o�l���*��k�)�
*��W)yp)�Q����[�*�Yl�N-z���Z�:I���9����v�(0�+����-���E�a��r��x�@�.���*%zh��Gp���-� ��I���{x�O�@��������{m���M���
A�x����"��=��^��j<�E@�5v�&,�S&�(/C%�����;�J
��������p��O��r�$l���WSru�I���u�9[�*�?���7	�&$�R
z��{q���BY���iW�6�������$S��]��:�V��x���Hl���������{F���	LW����vi���	1��^��
��
��Ec+/�[�&�n(��s!r���?�RM��������l�����4�s���z�����z%��p��y�&m��g�l�	��Q3�}C6)�e��� . ��V���{4�����e����	�;7��9��J"Ul`z_� �}�����������k����v��jy.�b����Ajb�&����&��jI�|�&c�	�}/�s&A����~2������p��5��b���)��,y��������.������`|��y����C�����&l^	ro�Zs%� �����c��H�����d�Y����GobO��;���z�r����bqP*
�5��2�7�V�m3�YA�eC���n��z� �P��NR<	�����"c��y���Pt=0o�wY�E;��H>��wa�������%�I$����da�g� �Pd5_on��g�++DW ��n��y��8���M�@�D�����/���o��&U�o�Vf�4���K�XXS��%�������!pk
�&>#�����wx�^��,��WI?.}5de�r�����Vm��B
�*�^�N�r���������z4y��z��|�|��5���H
�6�{Y���o�|����$C��`��c�-Vk�K�Je���� ���x�������?�O����V�E�����P���s-zni73|����N��"�Ch����^�#.�;kf��`MD�Q2A�"�j^����
6�����A?��&.��?�po�?���|��p�Of�!
d�m9�i#QzU4�#W���t+3��::.?!�>�� ]a<��nw����lr���C�JX���B��[�B�^��aF���//_�P�Js�\@��[k����&,j	 E����������#%���'9�Usu{��q�"����t@����N<+��V4DN�����im����|(&���K���eu��8�4��{����Ln���?Q���"A�(y���DpM����kC��-t����4�J��(��:~�����9��pqr����&���IP�����T��m��q@r7�w;�y�[�G��������%~�}?,����n��Y�x�G���?x_:���"����"�hx��l���|�� ��8��XD{}��c��]ZM��k�*\�����YW�Q�Z/������o��v�~��(N�^�����_����W95��R+�m�������l����M�Fu�����:������*�~*���s�9>���4%���/Y�|����*��[(�q�`4a ���hb�A6�V;�c�EA��X|�cf,}�@��E6i��r�C=�h#7
�nWZX�[��j&�;�b�?I���i�
��1X�'
U��
"v������T�)PMBV�,n�#$	�&LJ��Z�29� ������A�T�C=��9{{zv��a<�����Q��Q�E�k��&�����)r�9��k>��?%�w�����O��t�a\�	����� �
�lf��c`Z^ru]w�U��x��\~x��-�?�)��I�T�H�yl?�>�&�&}�'$Gig��>�^��N8d:%HA7�/�]��d}'1�+�c�a��?N��@;�`����p3��|S��w^�	�mw%��9�=��g������Jd�f�U�y�����=o��?�z�����t��Eo�y���^�������>g��{���������������p�ya�
�Y]����^���$�����[��C���Z����������=o�]5���o���7]z����2�5��-��uzM����8��>�AqW��?v��������U��&��{DcG����1���s~���>c���E���-��fu�m\Rp=�Xr7��G%�W,j��W��U�8��������ZS�'J��%� ����i@yN�
���dy�U�����+��x���Yn�F�^���d}y7�����b�8u�nC�������r��R-���;d_-?�D\<��#8gC]�c�Tk�N���i�R<��5M1.����9�{�9��6��m����|���VD�?X������Y�������f��D"����Y�x�F�Xl�[�������k�eN�dB�J�(N|T��0Q_��X�k�0���xX�`;B�9��pH�?jDD��
c2M#���`��Ly'����2��7�_O����Z%�'�������E2�77-*
�t%�
a	RH�;
��-�7�@�\0.��D��Qc-��#�jf"�Na��e`��xs3��P�4/��A�?����p�����al_7���T,o�����n���^�;��]���t�^���E�@F���?�xS_3��P/!k��
���P����jO��3q8]cm�9�%�(`�vw��Y]R�i��i���Q� $G�}�s7�S����j�N�C�kc�#��L��g�6�#5�`M{�R_��� t�*���O�����v&,�A��ybe4���u�^���~eT�W[ �x��_����������J[ip��t�
����bT�B��
��'�Z<��<I������y���N%}��N�q��x��Bf�l������>�v�!����:�N�j��"w�1�L�p��$��D����j�r�5���&H��~���g���T�5���.�.*��r��GFg�v����j���+��W��������/��F`B1g��A�g�Y��<���\ ��dt3e��e�}����M��(e�1����f�@6m���\Ei�D�u k2�\p@Q1�U��a68��CF;0�U���u�r{�����E���<x��
A�Ei%�T[R�q�P�
���������=>|�jB�h��<���$F��a~I�7��P�p��&��_0<;<ev�� ����^@���&������$����<�X�p��g����X��-s��Q��;<�J�A~��bY�o$k��?�m����7�����0���A��g?@k�!H�0O�����%�_��{"p-��r��������h�`�v��ea�en�O�G]bt�LRh���f$���}0'��'l�	v�n���3W�
ZTn��J#��Z�Uy��\�r����fI���m����t��;��r=����x�I�5&�
��nhX��U�OQ�j�:P��g�
��T�`|B�a�32��NX����{��gW��dM%�����FT��b��5���j��_�\���5���P���B����I�H�����@�7����B�W�Q.���rc��� ��v	�(U'�.�(W�����Q���5��4I��f��XdBF�E1Wt|��i�s/ut�e�q� F�3��A ��B���x����m����N�������<�Pk�6�>�o7h�x.6Z�Ud�(�*nb�"�;��f<�Dp=�J�������9^w���U�6����L����wu��	�.�=l
J�qXnUZ~c�;��VJ��#�`�h�������x�,�����p�x'����Kt��>9:9�S������y[H����a��_��o��8�)�t}�����+�j�X�����.t��=�v��_*�������������F�EP�8#������B0-P���]�3�(�*�,7�e����
�j���F����T�5K�S�H��U���O��sc����C�pU����`�FM���p�����F��
Fu��o��}u+}���rS�J�������d^��tLagy,�O���w�����?_�����a�D����~�Wm����������,:%����P���_�:7G�U��_���H�*�J���{�J�q��a������#s�����^���u����ss���p�������&�0p�������)c�C4s,[_*�Pg��#5r��m{��j3h ���!A��
c��!��ns�+�;"���F�eH$�Q�{�������%�3<k�)�7��F`���4%C���F#�����2�E��k��-�(�����-�������%��__^��A,����q���
��x@����)�,?�]���~�,��,x�T���<@O6��VK���������6*��E�Z�/����>���p�a��s��K�g���y�{�_N�_�/��U���t����w{ga���=�_����7���_8\��������Ci~Hu�o���Ke�&C(_ =f�U����	re�����93���b-(';���k���%*�U�",�
q�r�-mI��:��,��k�qU�����q�(Z�<�w�.z�T5q;���kHr�����N<G5�#�=;��V�;V�b�-��L��9x�b�a^U*M�@h�{���&�a�Aaw����
�5�^���dw'��+���i4J�V��y��7j��v���j�L�egj�$��&]`������s`�M�P�q�.����`�
�`n��=bt_�Mj�4A�xb����n-�[%�No6��W��x�T����m����}n%�.�S��7�<t������D��D�G������>]�U�,�[���-
?7�r���!�^�zFs�����I��c�C9��.��xb~���z3-�W_&�]��vq?eX�{o��:��lH����m��rD���|�����l���D�����Tc��`�h?�g4A�j�����������l�tS�W�:�#�&bU�����h�3=��i����8�S}^������wz�����?rU4����O��+w���Z"���LV���c�@���,��vi�Y�<��z;�e���ak{N���^��0@>e"��s�3���-�)��.pg5�b�|Y���������k���$������~��N���{���h����������.��.Q�]�aC��-�$~�3��aj�y�3F�_�����pT\��E�0�:f!Z|�������������1�����fI���*�F_��k�~�J�H�4���~}�~kL����G�v�7�!Z�8�8�K:��q��-�`�n�c��y�/(�d!�>��
�??����u����#�����Q�X��r�6l��V�yu���+�>��&��o|����
���9����NE�b7y1������[�|���c�������)��\9XGn��f2Q4n���@�����x�h��O����?���h�� �%��,�F�@9���]��]�(���i�m��V���S�3	�����x��o���#���C�
�_�ju{���0����Y6�������y������W��@�����l5*x
���Y[1I�taRi����#y��u���Lg����]K5]��'����7[����G��
���7�=�~ON�7��eg������(��3���� ��fc9.fy���`r��F��{/4�I
�;�~5�"s�H���I2�5�h�;V@��$���q��6�^����CA���+lNn�I-�-%�)-��J������E\8��=*�uTe�S:�W�l����,f,-��z�=���� �Jc`�.U��(�0��MD�a ���#xp�4B~|��g��pK���^\"C4u��Z�{�z)_E�_[�-������g��sw��MG�V�?�Z9����"m�������o����%��/���JQ�*��Q��n�&�~�dW�����V0�nX�J�����R�_~���k��E"��	��������x��QTj�����X�GY�
�]�q��K��b�I��vj\��L��x3R�#@w����X�6;�t�(�rb�Y�8|���I��W1��D�@(9��{:�r=b�A2�|�����q^3���!a"!���
o������yF�D$�Z1��8kdx��t�����\�M�C��$�D��e��E-n��K�!�m�'9"k��Ca���kz9�E��s��}'p�bO��E&IB��),��g����wfi��z��a��T���
��qSQ6���B'��kI���*��X)�}�,�zX�x��O��N���������|?�\�\��/�w��<
/��!NQe����H�\/��}Un0�b���W�5VVXY�i!�@��d������s��,7f�T��.'��!PkdA^��S
@���YB��48��[0k���$�z�]h�b��\�`X�L���p�O�\�$��A�H�S�Qw��JRL�).<-���R	���@�qN2/�v�MY4�b^2���<}���S�C1�������4]���.���t�o ��M�{B^n:�/EIE_Mf!�x��9@F�$�e����(�(e����l+�i<�F������$����j�e��a�D������X���:��cw��
��������E���kz�w����c�q��qR/I����r�B&d���v��B �,g�vo��g~�V��H6���E�(U�����G���-?�W����Jo}��a��.�������'�����7����-�� ��"*���$�������D^�|�!�Z����b�k���n���>�	��'����ig�e[�N���Ig��s_��P�g	)�[}��s���t�Q�hjHa7�J���66�b�8u�g�k(��������tL&`�O��<B���`V�<_&�[�F4�I]r��'�'���m!����mh�k�O�^��+z0v����6~;��1�>0y�	l��Ce�6�f����`��;�Us�\��������������U�1p�U���(�vL����h$+�La�tFr�k��������s(&��|b�k�^����'�����b��M���f��E�x�U!���+��3�������lp��z<I<��bJg�������X��s}�?ybB���G��OHp��Q�+�$,�9�D�{2�<|f��W�tK�6>a��c��5���j�s���-��Z����W�n����v��Ti��~�5��~����Z�Q���V�e���+�*���O���k�����,�P%~K���M�{���</i��V����E:;�K�������YTk�*�jc�(����+�����/):��/�4d���h{��x�9Hx6��\%�!�E�+�����PUE>J2�g���
��"ga-o!�K��:�W�i���&3�0�.����N�,&�

�PHTl/��V�7�a�����	�~��|�c���2�Z��G�Ln��%�{}#X�5+�Jm�����z��hUv��t2�Q�4�
������?���B�J��oo��
�.� O�UQ^��]�}`@^�`a����������vP��zL��2A��0T9D����8���
���X�cK1�U�a/���)X���cGtxP����md>nu������#�Y_)o���������Dc3����5�
���^($��������-���9�^	����BQ��$�
�,�����MK���"��b�RmU�u���R�����47��H�=3��L��
1��u!�nn���g�����2��Q��E�(�B�����*�(�AEg�`��>z��&;\����a�`�b(�:�hF��NX����>�jc��D�5z��@E9��Y
di����pt�<n!�&���~Vw�S��4b�K�o�>����.K�6�n����"I]��D��[)�����&� ����~� #���oo�����zU�D� Tj�k�r�������_�B��+Al�Z$	I����u9'��M�2T�0/UM�oEX��S�V�K��z�u���R���q�Ug
THl��Z	���,�����,&)(Z��/7�{R'i@3q4�}=��6�2FG�
�&r��W��0����td�eO���F�W^����>����4�z���O��_
��Q�3za</@�,�A7n���.�lK�/�����_��Pn�K�J��^�6����G�������6;�������F��_-��`X�6����T���?4�����+U�+��gq�"���>�����Yv���z)��q�w�/�%Hm�-���N��� 2)����,��@!��?���^)�<�3��I���<��b�����9��q�S2��.���}�_�E�x�������a$rX�-O�F�����"�=�m~��vVMM��X4��x���X�II�<��	��5�Z��<��4�����Q-�d@���Q�r���za�F���!�G�������8A�]wy;�\K����$�@����������Q�8C�-7�q����gfq1�z@����c�m����\��W�~y�(o�F
E�=� ��gH�k�K�����^>�����<jqSaO�L��
����S3����s���/\�Z�{��r\K����l�L78Qg�A/W���l����3_y�����9c����	������X��i���W|$n��h���|�L�������w��p�EC�^��]���Qrs{}�����q���se8k�a{j+9�fx�DV�$�+eVL���.�+?NA��&[�@�Y�H�H�E8�7���	����HX>S���=d'&�[��U��|:�F:�-J'&Wd5�>�C��xwrzG�;�9�uO�Q�����P@o&��z!+����-�]�,�w1� �����j��\���B����#�G���
5�t��%lV�r���1��DD�;7���#VGA%n��M�����N��${��3�t,6���,�<�����g���{�|�H�F�����3�������=��N=��[�g=�G���V�z�)���V��P9'�4	�c��NQ���|MG9��W;��Z���2��^�]��\��#3������<��M��
�{wS�����*q����|�u�g�������P�rV/��ZqW���Z��5\{JOm����C:]h���C��oCT�Og����Y]A����a�����WR�h"�y��%�sM�,������_����]��k��`�o�$`Q��
�A�T�m;��hPD��C����c4�����O�u����P���T;��
=�x@^^����o�e�
#\����Q��Q���@������`����ZA,
�J�7������	jGX���0G���"�����{�p��>�5[�#z�f.�%8�(�g�SL��S��*om	Ds��D�q�����0���,�"�	�4��}8��M�A#�f.F���{"��y�]6������}>�?[�k��#�=jr��-�-k�b�!X�S��`�����0���$3(E�����e���K�
@���|�8�B�J�������Fl��6��h������T�#UQ���ag���P�U�@l�5�������X�{4>���Tr�{�.U�6�]�$�{$g'��{W�?Z�������-re��)�2����
�d�*6(���z�s���8�B���JF���#�L�A#F|���H����`t��@�J��0B��-��a����)#�`>��p�-eu�uZ=�5M�[d"B��kCZ�-e:�L��\��6d��
m��Ev���B�:^]OCDky��{B�E�g��E��.��TQ�>�y��C���{�_N&�o����a�>�b�9Z��!
;�x�8OQT:��5i���.�el2�����-��_��$xH^s�5�F�4���Q
^v�qO;`9���e�j����C �'�g;�/�%,O�h.x���p����(���*�.�"4����������"�x&�	'���F�K1{�a[���
:�CC�d�O�4�}k�p�6/�(�E=�^2��@���� ����_�����q��0��%Z�.L�JMD�������bH���L�	`
�'�Kf����3}��3�@�����m�Zl��pwa���
������g��Sg@����������zh��Wgz]8�k�*��fI��h3f&kF�i*���b"�v.a���z���~�_���7�DD �4����Q���_��T��9Yj�1{
���u^����#��(�B����m�y\�3�l��-����x�Z�U����H��C��Kt��������]���q3����SrQ������fUs���&�G/����d��Ig�
[�K1qC�g�Jx�z�x���}j�8�����b���(����I��\���/��}��(Th>B����k���vhU��$[[�"�,��y�:�v�p|7�(����)�m����y{��/|�%?E��M@X��h)�"!��.@���}��� �qt?�[[o� E;��Fq$r������Z~%��(�)�>=������������O���'.�T�*p���������_Jg�����������+rV�?����aA��S�]�Nv2C�6��h������-J7�;J+	W5� ��0m������m@g���q�"	���^�������>���-|���bk�����y>V�hK&�	j�����u�����_�a�-B�����������`�Rx�,�� ]+������7���?��"���'t[����Z	�3����=g4B�f�.�Q�M��,�"X��S����5r��4�r�
PO7P�tRr��!��NH\��]v����v�hD����
/>�>;\������Q$]�U����bf��5A}�u83����&�
�aU�,��"���������{��u4����a8���XE�}����s������U��j���0��A�����I7�����8amV�����j����|=�0�\������V'���_.�8G������V���|]�^H���<+��b�x�!�s6��*����"d'�k�^�]bd��Ket������FR)�P�`1{�;7Yl�y����Jw�s�W�s���f�5$-xS9�`\FZ1k5�&�}�g���0���3��r���A�����b��/?;��d��HI�:l=$R�z���Ov�cu�QiT���*��	&9���9�(NO���1--����vDL"�����r�J@ob�3���DG���Hw�k�Ym�
7J-����:��=��z
�����^�6�C�B��:���/)SMG�8"�g1\`�GC�%�o�x�����t�[
�����<��A*�$Sh$�K1��g�1�����M1>@	��������)���GH�n��Q��������Vm-�Hs|C.K89Y�Rg����[\!���S��//����7N����d���j��&���a�v���b~P��`0��+v~%$��a0Z�n�"r��@�52l{�	Tp������7�]j[�.��B����-���f�[��w���Y�4�"�@��sVdW�
Q���P���"}�l����[�����>qPp�jl���SA�>�����H�#.�
k���s�g�=���u��K���a�S�w�
*���;�4����U�����r�\uRy$�y��b�O����`,:�t���(9�O�~�>%�)�x���k����H��G�b����ecr`���.MF��1<ax����V��5���6�;��6��5�;sJ?���	I:i�	��dG
IHZw�uR�3�_�G���~~����Q�1P��&`��!Di[KJ~��=(?E�O��F�G��m^�=��8s&�Qx�����J�G����-�$7�/���l|S�����aR��>W�{�T�6��������&����7-cv��x1�8���WzQ{x/�Q�����{Z�)�B�i���a��k`�cg���Zq���������
[>�27���j_5��[G�w���k���"�=9��g�51d{��S��r���8�-l�}�����K�~��>�=�H���^]9��L���e�;Y������%��K\-f�8Jn]�H�'<�����V�u���%��
���Npk��������}��di��U����P(��}�t�3�<u��M���|���mc���F��$�	&�� ���<b�zEE.���
Qo�6����9Lb7k!��&r�Q�����������=����������6�����r)�����c0�	
�.���Di�@[|7I����p��M�9��j"�����+NK���,nf�*e[5v��+n�v+��3���)3��M����o��V�M����Q����������i%L���o�����n=I���������p�5��<����@	��P�8������	][��K�����N����EZ��7��V����n�&6��g�$�����/����a\+����eh��s�3��
�����?�8����������O)o�1L�g���3���|{=e���\@LM�k�Iym�q���,XM\���������������2&��"�N��?C�[M�r����)�����M�x�f%����`���}�;��Nu�RV�����7�R�Tu�%0�9b���0�X��o	4�[�yw\[Y����	���@�2�9������g��T_�H��,\�bo����������������~��VH��nA��xQ��V	x��L`�-�y�T�~Z?���������,����0�jJ?�#dS��3��V\W��c����j>RG�u
��f��oo�g����#���JE*�j\�Z�,�n���m��+����_s]��-3 �+W����>�!|H=��I����j�7�]/\4O�M��/�k�N#�:���AMe0S�N���>������{��[��F5���#���V�H�V.Wk�pgo������~�#���^�k�+U����w�"~YWkU��Q��~t?�zOf�[T�����1H���z
��N�����3�i�_��bS��}���a�:�A������i[�����mNAB�R��|�
uL�x2������CE^jz/g:����_.���J
�_���6�w{����Q�L���������M��~Roc�f��J�J;���>�"�T
�<��l_��b������)��f�I��{\�\~N��tL��9�H���Z�&;lm%��?��|sz���^�T�`1�W���F��cqD����.�5���
P�������}���aHp����-~S���j�S�nU[���O���Z�.��Hczb �X��/T���__@��r���8�#�����/�rz��?������kxZ������{�z��_�o�YX�)h�����T�5�34u��|���
���
����8�4�[W(�]6/��?��C�n���s0�W�C�_�:$]���f����`Xq ������[���Nn�����W����O������=�U��LHD%�D�p9�e�W��I[l��:����La����3���h��P5����
Y��n�j��Z-�Y������V�w	�'`!�5��P��Ju1�*+�~r�����U�M�����N��V�m��;-��a�UF�M�n~�H��Y������Q��=�]�NS�C$��#SO��v�'�?�f2������9d`GbQp�Y�	{<��|-�m"k�8�v�5^���d��n�w�zc]�%0�@��e�������(�/&��������#�(�V�/
��
�dE��|�4pO���6V�����Fp(Q�Q��g�B�HLFQ]��p3?����0�n�5�b3���>�N<�`���BsN4?V����}8�"c�UNk�l����q^�e����)���v��������������D.��\�M,Q��������1+4���=��w���_F
F�~��D&�k�\V'GWGp�I%Y��2�����,{�~.��X�\#���U�T��m#�KU���B�������E;2_k �f��� �R��C%3)������6���0O�I�����)V�7�_��\^�W�g��&N<�4�����U��\�O�YJ%���!aTP��w���o������!*k.��ay<�~U���6�:��������k�-%2-*����=:&"�
HZP�)����"���l��%Sh�
�,�#�&Ce:p�M�#|$��p��o�9��z�oeO���!�����F�p��q���*�������Y����p9�����:A�S��[��
���q�F	Z3U:�z^�R��W,V�|PI�PDa|��Q��K�����|��9���a���q�p\�!"� =�4��z��ty����_�
�{6��
dn�������
i�������Q��������R=���yp�l��HZF-t�i����o*���aB�h����u'�����<�W���t6P����9�I��^5N)�2�c���~vVhd	36����}����Ywc�V�R	�cQ
����/8���RY�.�c(pr���n!�1���G�����������&��WH��3'��E��+^����i��xt�81kC����������5����b��AT��G���M��������EO�v8�@��P����3:�����,����]��hgic6a�������/��.0��i�K��R��a8���!�����k��7(�h���8���wl��OsG�/<�7iL2Q(fl���a�Y�]���M�H���9��\u6�T����$�9�L�����h$f����t���������D�]$��L�H\�T$�c�q���S�\�n��B�|��!��������66Z
�"����C+�	]�w����E�f�Y.5�Wn��Pz�^��+7�@���3�Pz�1	?���7i	�HHC��p���8���^��]�soI�3�-h?HD���ZcN�z�CS�����������%s,D�;��@����������Wz���,��c]?�������e��;��,��c��w�!�,�,#��q���n����F�J�q��9m=^�x��7P��E�(QK��H4�����8��-����6i�
T��G���H��Z��0%������W�
��Br��a�m��2�~%`�Bs�~�+�H�Ato�T|6,\I4?:>	kH�������7=���q��4��T`�����*,V���3�x��'��}4BW\ |�.�G��oTd9}�����Bb���	�J��:8�Q��~e�s`�����b:M���lpaX�r���t�.���^�@��uB5.�I������@r�u
���*I���	fT���I8�ToJ���J8Y]0���P����*�u�hOo�^���aAM+��F�7�������x�l�9�a����O	��T`#�U���J3��P�f����5�(a:��%�N��M�T�5�c��[b��T��}�6��������g*��93�
$\O3�\7����C
rj�T��&�s(.�K����K������&	��=�xD6���j���?���z��cYI�-��-��A��B�r���I�1�*h:�\cKo���9�OM�����@���o�i�7kN�=��_h�4� ����
t`�����	~��j�����)gh��a��3�-&\u+^C�s���3��]�?��
d���:R��0_?S.T����7,*u��!b���!�
�����[���;E��P8�LW>���e)���q��s���6�5'��fZDl��Nr�������(o0n[^���2��59'�=�	�~FC���z���V.�5�f��VT��:���i�-�
u�%fc>2��1���d���n�T��z�x�\.��G^9=�`��P��O�����t�����KU�^,�_���+��6���viP^���q4�&�F�eq������F����It�6�����t���Y��l��|,�tF��?��x!�d�~Kt����E*�%Mj,�+{tE��,��e��� '�1[���"�
��,��1��Xs�����[�,���Pi���D�>.�\����9>X5C��\`���%	?����B'��� ��
��� �_9���-a
������o�@�}l��wz-*���:�4&�[�	�F��OO�b�@���X<���"�%bT�/�0`=�V�'������o/���w��rzQ��z9���`�h�����n�N�[�����$eX 
����������|���,��P��O����vuwC5��v��g'�7�zSmP�
�>W���F�	��s�,C'txE�+<B�v��!��B;3��q� �0H�����}>E��*����O�:��"����|%|�T����_����{���M�[�e���>���6�Rf%I�~��z��{�����2%V����S�0���;P�N���~<zrP���/�q��K���0Ri�N�cY�#5�����L��k6k52��r���:z
�"�F{�V��c8�E��a];���ZK��d�.7��� �gy��3�p^tS�����vS
A]vNu>����S�KK��+ �[~�����O���Y;D	�^F�����/S�8B*-��B����^}k��2��`������c�m�a�>9��.��m9��D#�cp�Y"V��{F�%�94{�����uW����/�`�i'$��|��E��k�B�6���1��H�m�[�F���@���)N��n�l~.ra��B��8Y�k��S?���)(
��rS]���$��r\�zg�������/���u�������_����'�h���l���n��QJ��������Q_����r%LZ��6"���j��%"�R�*�����&��J����d�����.S�8����>"����c���|��hS�[��B������Xo��-,�CL�2T���O������#��������Q4*�d��>Q�SP��6\H#L$���+�[	JB��os�;�t�'�{��#��[�#���gS�3��U/���L>"}K'��:6�t���S K��8�B���b8E0�������@p&��A$q�z�9��~u�A�����a��fR����lN��K�&cppA�=�����!��������j�6���,�pkC��0��x9�{?�^�NZ���w���������dZr�]���L������F�RW�J����~;�q��Sw8���l.��&�����i�Q���L�>���?���=H����3@G�|l�+Q����6�u������f=i�9��v�E���|~�{,�8���U�=�+M||�cc��r�hSM�rI�W�)������n]�cJ@}0k�eq���o\�[������p�
��'��n�o������t�����Nj��:{��~��S����4��tV$�����d\&��0�� �L1t�O^ ���`I�����8G�C\�mSK���!L�{�����oI�F�H<.�����"i��������A������.����Xeg�@�^H�T�}��g������	��,�'�%qLD�����:�7�V�fU������`;x���$V������$�b���4v�il�a�^�������,e�1����|�`�����Bh,��]|�}��z���-kk�ge���8�o��qo�gg���q���,F��Y����"y���L
�b��$�6�{*���_������N����V�^�S�'�d�_[
��A���FH�� e0�3|�]��L���%��P%�/���cN���\d>`�G{��~oq����`0���dM�[u���j�MyP����t�{��2�z/��w���Pzes�j`{rXc	��4^�'�:����: )��Cx����S����C�)������IU���&"�P�+��O���q+GC''��6e����W:����\�r4g|-�in�X-Z���W��)P:�=�*'���i9��19N���]w�*�l{��+�."��
$[��P�d���y���m�������j�6mg�BgO�(S��W�;&�������!rS��V^I��OR�����j��������#��
�K���w;*m���(@� Ffz��"76���fgNmb
���$�z4����.A� l������.F������#5~��Wn�� �@��!B>S�c������]����t#��!�4w�EzxOY$]���1mJ���3���t&�%w��Z@�p�L�9��@TS��v���LA�� �T��Y��Fc�z`�
�q���c���!T$�Jx/R�4��:P�DRC]&_����(>�X�V
D�����}�+���s(�����xAfxn� �4[���1�l�]��R�]��x*8�4}�p��M0u���aP�(1�Y���������%\_o!�0�S'Zr1����A$�M#��.�����N`������\��b1ni���&X�O��4�'�K
���nM���Z"��jA�U�`%�����l�������C�K��]��7�X��<����~b��=���:_I\����Tr�0�xn�q���Jc��w�t�\�0�LX�q@)a����I��;��}KSZT�H��~�ON��25��)�����p��;��C�a�~�(�l6^^�:�W����(�	�'����8�Z="h���w��EM�S�������R�cI�	9���t����~	.�JN�
z�:D�3���W,2�OZ�|�T$#W�����������7����n�d���c
:y��_��\����t��I����	5��-�;����6
��f�1���}L[`�>z�2O��d�B\MG���y�d)�����V���j_?��q�v��P����	7<�bS�75�w�z=�'�6���vD��U�g�
�>w�)f�z�:0�pO�a�,tG^jzU@�">����N�F��p����N���A/�F��:�=�&w�:���A��!���x�����.�HIG���2�c�lO�D��;��9"s/�	����W�e|7��L���&��z$y��~��3����A��4`g����?f-
9��af6I���1�	�}l��t|l�+�(�7Ql��("y����z�������%�ta�s�}�
������1�Gb��9z���o��O�Pa����
�J&Edt�������b��]�~ ����;Wn����F��t�0��O����z��N1�p#�B��sU�k(@W
}Q1�z�h�j��j�8>��<�Ms��������m�tX���������J���|3�1����	J��Sz.�"�B��4e^��[�K-B���l<����~��[�w���E���N���t{db���HG��eh�Z��;D�R��.�������-�Z�?��FM���M��{�.g0�[�������&�J�@6�0r4WX��1�3��e*X\Ie��}3���03e������PJ�!q���]���y��1U��A��4Up?�1��KRa���^*��b����2�(
����p=�9)P5E!����B2�Dva4����&�Y�Etc���rV]���W�*�LV�����6��	[[���'�����X}������MVg�:gI��0�N�B%����)�g����"n`2
f
��QV����|f�g�^�Xr�J���&�h �����L,f����:��Ce-.����q�~s���F��Q.f�kNa_
[M~�&=�_>�i�y�F"�s*��z�-��L���nf��+f���RfI������5q~�`3��2~'�_���s�o�B�-��
����N��dS��L������p`=$��yo�"������b~��VO����'�S���}��ud��J���r�~��~Epo	_�������.%��/�5��4��B���Q�������0{�Nx�gWa04��`���*�"2�����?�����9���b���� �s`� I�b���'��&�*nb��Iw���:d��XJD�Y�_WM~k�5Q�"T��E�i����I���qJ��-���U'Y�~��H����)����~{�q��������Y��������*��HS�d�|26�>�zr~�&��\L����
5Wd��X�	-���{��P����?����->���y�5$m��[HS�UE}���o&I�9v�He5�k�j$���)������S���h{t�-i���uE]���^�C�����c�� ���kC��"X~�8�9�X�%#��G��	����k������lE��V�J���1+��|!m'X�&N<zI�!�yDw���j!����b�����l���0F�V��	C��\�O�v�����)Xr�94�E�o<�����;����@�<�<�'���4	�s�t���fv.�oeP��E�j4d�9e1F���U���_��/��=���t9���r�����#\��X�QV>�}!t�"f3�K�;|mA���$ml1�ib�Z�#py6T ^����5�R�1��$��x(fAn�?���4q
��Q�����+Hb���Uh��v�����F'�,u]�\����0W6aq������kZ�����?���b���	����#��7m`~�X���K�!0XOkH{�\=V���B�u{�9p�WX}�}�I\+�U�ka�k�{UJ�Q�^'���"��K�+������%~��n��>�����:
<�z�����J�\�>��d�
��Y0g��~��Gr�OE�rg|�����k�S/�e�k)n�,��G4�lp��)$0�>��J:K`!��|<~��R���K�V�ak7��H�<8��6����$��l��M�9n*��"���#��I�7eY������G��4�/�JLhY�K����z���h;�v{���#�,��Y��Uy@��\�b��I�7E���sY�,��,���h����7�`���t��%����
��X����~�����n;?��\�F���:��{IR�#ch,������cky�����g"'�!��c�t�u
-�:��`����������>q�;����v}�8����)6E�@�t��^-kC\+�z��s�	rw5���(��hw�}�`�5����������nE��D�9���7Jpt0:�Ate|�#uKU$z��sgz�C-��5EC�T�	�#��q����<I���o��$_�������r$-�r?t ����A�����>���V�$n�tT�V��I�Na#9'�U��H�(��y�)��Z�C,�|F'�
�i%��:�_���g	6&"��zE2��[bM�@S��I4\%6�i�:.0�B���^�"�za����O���c��'���kR�������q�aL<���X�Z�7���GLF�u���x�b��k��y�{zr&?�R����qj_��yL����Y��u�Z������7��*�������S1��N&���R�������;�K�fy�1�6T+
~g>��"�h�[-�e��������� �n��0�A�
��C�	fe��N��O��3+�>r]#Ng�!�����kn��e?���Q����O�j}���14l�If��M�Y&=go�c]�y3�1��
]{C�:J���cx���eA�mi�6o����H���9E2�.d���������������%�E���-�=���SB �8���tY�v�2��m��"��W�b8O����5~#��R���)����{��\�i�cl�]o�	�.@[�fP��7h���j8�����1K���);k�����i�.�g8�_h>�3��d��r�Yk�Y�(B��^�9���[&�E&"��G��p�#�����MP�S���;�����`tq�6"DLf>��b�����B�`;��cO������5(��������`D��\oS�V��f\W|�
$�4��?z�C��E?e�����>��.	�x������E%�������k�F��r���:����h��z,bKv�m���T���_cXC:���j]	������-:���ZM�	t�T�����h�S��& �D��Giq�d6I&�Au'�5uo����w���=b���� }��>���;7�K���h���T���OrMu�������&�������M)!�����v��no�i�����]�&������V
	�Z��n�U���F8 t��������o��c��O�Q9�:�M��8��:�o>n��]�%����N���X������O#zl��Q����k�)��`���Y���Sz�*��������n"����G���ydf���#S
3PXi��Rf���o�u~��4
!~6%�y�]�nr��SS]:5x�z������S��KNH���� 9�Z)�z��C$C�5W����"��!��8�����
���L��9���)�I��'Z��p����ti!�."���>���eR��8(���}�XAT�g�"��Y�B=z������^������i��\��k��s�p������k/;-�C�y�
������@��KWQ���'���@��i�RdU�������A������o������YtM�3]6�#Nb�|_����]�+M��`�N��r<e4e��N��#������n�V�U��b����q'���p<!�����|���m9�k���"�F�����cW���hx��W�w�������}y�l��.�h�q9�ArrA�-�A6�����]�����l+��D������Ju��������}��Ga����C���aX9-o�h��UJ�zV �Yp4�Q���,����{*�j:���&��.�|D�8��.`�5$P/f�s��`'/�)�c3@s&F#�9�0�����`��:(-Xc��:����X7�4W4%�I?����Zs[r�1w�����������M�.�Q2Y�qL���e{C�d~��c�����<���qR1z�5��^����1�������G2��K�-���E2�L�,OS~������95��/�cT��!��'��W�"���{�3pqd��I���}��H�tR�tA���������n����l��D$�����fKc��0�LH�W���t�!�8��
Y�0$��E~K�o������N��Pv@|�wd7�1"�Z����p���%H�#���'�IN^K���`>��}v����/��G���lN���^�r���s ���kR=c�����Pc��>������9%�0��X��1b�>������4����!px6uV)%���A��N��_.���v�T����jYmV`^Ve�Z�v�X��ou��J.;kG��I)��?T�~Z?�2��n#���p�JJ�R^�������y�����YJi|����f]�r)uE��1�)U���_T���<�����`M����3`��Ym�����|^]4�������P:��C�������(�`3;]����-k3�F�n���q�{	�O�2����q���s�rv�������O��N��"+�L�?����6��7���V'�����]��G�*����MEC�kOuu��F��;R�*iglyl��yRo���sy����Q&����A�%��,*���l�-�(��Q����t=���J�{X�����@��'!��i�=�h~����,��j'6�U�Y����4���D������iVF��,����Qy���:j���Y�QyM�����4A��L��wp,���[�ru}������G�xIT9�<�T*{�wQXA�D���K�q���������s^2����`��Y�F���V�v����g��[���v���]���m�Jm{���_���L�6�J�p��_���`������TV��a����;;����Ae?����`k�����Z�����3��������J���j���X�u4���������.����=\6fa��^���2~u3/��	OUw_lo����B�Zo�;�z�~�<�:�^U��W�~U��Xr�E�T���Ti�����}��"t\wDp�U�	8	�(�qP8�Q��q��]�]�>j�1V��8��yR.���^)�����<:������L"<J*�Hpeu�����^�n:���V��az���okp;S��������Y���cI�8V�$�&T�!��3�S�LLx�q[<�u�x�K�JTxQM��|��N��0��d�ft~��u�f�^�J�_"Z���#3p��JG���x����s�(Vs>wn����G�8Y�?���R�B��i0��[����BUU�8�XV�n��t�3�G���Z���q��K��)��k8��b��)���*����XW%)}�U1���L�%_�->��DL4��W���(W�Um��z�0�'���P�k�Y���)	�V����%��v�{�m��Vk�`��M�Z�F'	�
�����fU9m�m�����dG*��(}�6�����+�������������xy��SJ�������u��d�@������F~�=�I�>�P�����9J�}�g�WVZoz&oz�6��8�Hm}B�{z��}���}��3�7�o��m�.�&	��U���Y���+��K��:�D�K{���l�4��������[g����t������G����q����%��1��e8T�	J��"r����7�����d��s/�����a��:�M�d�k����
��s���"�M�����0us,��V�OsY@a���e�WO"�����Q���_����F��
�
Z9��lm��v�����+[Q��
gP��T�������f�`�uSBNE��Gu�/k����l�/��D+���A��
��^�R�U���[K~��LV+G��-Rm	�OMl�&��(�YxFr�E4����S,
�6G�q�{��Xb����b�s�VRV7����T�������j�S����W�1s�SK�Joo��Z����&���%F����o����XH�������y|#����EL��:$�d����5��ND������(�a�V2W(]��,/#����w�����r���J�����*�u%�#���>G�w����nfs8)\"F�R�v>�
�� �5wp��h2�
��P��i6%�qDO�e�<��I_��C�_|K`�$���r���M��������1\��������������5lvcs��@�Q0AV�W'��avoxb�K���z�����f^���8�b%�rn���{���k���8�3�<�������A�3������v�}>
����n��^)dL�7��}l�r�Tao7-($&�J��T>�J�'D������x6f�!���41"�E��C[-�I�I0��B��E�sGr�����}6��IWR���<�m������������.N������zK��l�.��~���)��K`���8�Ys)&i�$�:r����x<]��j����
�;�/���W���?������,���D�{M#�{�,�d:�8���	#�3���@O-C�r�syqkN)m��:��k�>$�+�T�`
��p��y
���4N�|��;���#���r�	X�HP{��xQD�+FG&$����xc�����au�B\���y=��}"����R�5����o�Z$0��h8�`�[�zh��s�{�������T��@�	�8#�x���&95u�\�(�y��0�=A8��n��@�����J���]xT���
;{[�ju����#�������q~�~s���}���4��V�8��](������h4�h�� �H���
���$*%��fx�U�'����WV�2������f]up3{v���`DP>���\��y$!�8{�s�8��t�Q4����+��zs�rM3+��"\�w��ftH�%�����I�4���7���1�}���`:G�oH5�.�H�*�oM�!{����M��.�j�[!ipeX**wW^�����)�l�fD�0��}oOIDE�//��y-��	�>>�����e�[6j���t��3:Dr�rC��4�Htb����p�M14�!�����)��^��d�'�&5E�by���2F=�{������O���x���-���H������5�����4(�2f�`iM�����>l���'������m{������������z[�j]5�_q�SV�t�zi+���K���-�/L�:��~�����}*���8�/��=�X
���1�B�@
i�($��hp�j�31�k��pz���8r~W_�����N�M�}��z��=L�U*<����If��m�Kx�3�����%��1�);30�9����e"Q���_����I�	E��YQ��c:�$c�l�6p����$������Y�h~�d���n�����������"�����>���e�-�,c��ZH�i!N-�����-G�w�A���xt�1���9 p����3I1���j���K���N�I�G����9�c�D��
r�����"���O�w���m�K�R�I��;K���"g��k���dj������L$���6��"LH.���d6TSB|tc�z���C����"��X�s���ZV�`�@>[iz-H�
��^x�H�_0N�g~+�e�"��n��%�O�$3�vRZ�J��Kn��.V�QaY�
���Z�_�� 
gN3m�>_c��t��PX4e!�4���T-=g����~�pg%�B�������e��qt�n�O��.O��g��e
W������F�I��8?��Q��p��;�ZAa���w�����XC��/K\�02O����>����=����F��Z)sW�i�
�����V.�����1�o���A��z�.RJ�H����D����D�t�(�<��c�l;�(D�d�]6��N���(uH�����]����\B����B����T�o]}�����z@G���#D���^�H�2���!��k� ���V�n%�}c�Ha@0��x*�@������;R
 m0B�4W��R{�ZQ����l��h���i+�����y��m:�(:T+j����@���`�R��'�y���c���bn 
�u+����B�C����
��)\2���G:bG4P�y���t��Sh�p�/)��k�������C*!��s�� 0�]=
iGk������=���/W�]~.o��^M
!B=�6_��}!Q�XB�(��F`H1�x���Ld���S��y���3�e�:��I��Av������^���o���� �uG2Ah�E��8�^cvmQ�&����"����x�7��Q��G4C�@��������7�$% ���5����*���	A`Qw0�R����IY����S��
��9Z���Y���G����h��t���s*�������qU_��
����
��������rxx�e�'���'a�P/�� ����	�w������������\�cXw
�����5���e������j��c��!�����>����f���,�����}9���d�1F�����M�"�1�`}3�x$�a8QleB>���=:\����Mr���\����v��Wk��:y��Q�|��!�V$g��v0�u�����5������H�zN��F�u�<�XA|��6jX�XY5���K�K���e��GL��H��2|��a(�-3�^�j�>����]�L�_^��=w�hJZ0#��<��?k3���MY�mYz��dA�R[y�z������BpU+���W����$��b�P��X6�B�0#���)k�8/��E}f����bmK����1���Ik��n1�/��uA��]e����}B�����w����4'��yJ^E�%4�c�/�����l����A���y�V�3�3&W[�*���0�{��~JP�6�7�xeF��<1�F��7�|��i���j
�E	����gz����0L�����_���*#��T��!�K��aAR��.(2/�5����'���1�T��������S���a�Q�J���|���x2{1},w#��G7j����g�`#7�bh������Z�"���^�)���_@���g+�Z;i4��c>�!�	��>
{��Mu}������:�? 3!�yf�L��\4N�&�#�9��+8�n�#��V�([����=v��v�yQx��Aq���jgh�������.8n4K}]�t�|��� r��}��r������1�4����������|d��UeR|db~0\;���`DT�!�<w����s�#�Qwi������,�h���v��dxD���R�y�<||V�q�8B�E�z]K��%}�q����t-4ti)h���!�G�e���6��6e�p��N��$j����52�RX�'�)<�<+f�'�"�������	��$2 �f?z`���`v����9��X������S����1`�@��Cx�G{4��������qB�__��6����]�3ZQ�i�����G;�P6���H���}$~`�!}W0�q�:t��R�9|�@=�I����
�I��*:��@�	`m�c��a���y.��_}�+,��~��9�y>O�t��
�}=�@���$��1LR�21>=�8�������4��'m-H�uY��������G���7���z��U_����/,^��J_X���/_���U/����/Y�Bz^��V�������Y�gAJr�{���������������>�����fJ��{���������N#}G0*���@S|{k�,q��i�Rjl��%Y������H���|{;�
��mzf6
"W�e����zG�=v�PE�c���U��=f7a����,��Vw��j���a�����_>�B�(��f��c��h�>�?����8~4	�{���j��GY����C;�q�p�/������vJ��UGd9�i����C�h��a�z��$6:k�"cY�����$��P����As��N:tT��G�n��l�����^���
�O���[��gQ^\;�?�,��3^x�	/<�|������N�a�y
S��u�v76��f�
FU�������*ob��>�1R�hm�
HbKL Oe���t�%�	
���><d�R���Z0I-Oo+��X��[��*�V�C=�	2�yW���_^�����
���4�,�"�!iw�v��U�n�l}�b[��xY�
e��1�l���i6����LF�LLZ�&(����x���~�w��^;���e�jx��BT�8�4UX������D4/�[�`��sj��C�:o���%��!t��k����$FtrP�+��N�w��*��Y(��IR�����R��X�����R���AX�srB'�-�&�EJ
�R�4�����!�(:��Q���!���\3���-�]�� �;oBo�JjO�g|��4]�*�������y����F"MA4�E�($>tn�{z�	o0O��
{i���`���[�2�qn���`I��D�����IN�������3X��x��D�Ae�����Y���lh�����*���Vl��4�~O��eMC>�����C�%j�zau�'�����o0
��O�^�FXu������xM-�O�����=j�H��B�������m����~���/���a�$�KN�S�5#���	JI7��Qr��vH�8MX�A;-��:��z���[�!������f����i���I����~::��
1�L�!���@%�5�P�yx0�]r�c��t,R�X�gct�`|����K�D����Ajb�8�(�u+������4?7������*�V2�&n��~J�l4������"����V<��y��.�n�����3UT���e��j����p���gG���+���������l2��W{����v�$��T����X��Q�
�4���:����M���}��)c�%\����ESQI��]i2��q�H���tSi
	<��hl�@��e��/Y���Lf$:m�G�����T�=+�5B*�u���fd����ai��oD��yz�
��CU��I�/q8�7p����t��i$s�� =��F@��>n|���/�K�����t��DVn�U�_�P����UQu���Ep�"���
��!������
IB0J��^�M�u�3����^���H���ZmFA���h	����������(����#�fL�}�=�D=��
QOm�Z��z����M�2x���]
��e���D>��6����?�4��VG"���y����~���7��u(BX&��,�����g��:�"G�1'��#w�X
�{�7��G��)�{01
�D8��{>�X7�������0��.H����%*{|sGrq�����P��b�UD/Z���v�)��u���by�W"
�4��$�'��!�����*�.��"b�jF
��ev�\� �wgr_��1[(����&��bI��m3.������|����F�=�v����Z�\���:i���9�����}S������
`���C���H�"��m�	����oI�L!����E��������(���A����
:z^���1<������,�~���U���� �w�n���v���]O2gx���+��c���h�r"<}�c�i��G3_�d�^�j<:���h�~���R1��Y��w�k�����4���	����5Ah�����^��f��y��v��v���*6��S���6��BJ�x.:�,��D��)���Xa��9e�^��.��6�@mj.�q��[�_C g�N���Egh� dK��wn����������Kn$��`MN��m0���nL~Y��L&� {Ng����{����FxU��)�w����N���EL�y/���qR�����/U�U������Y����s�]x���@4���4%�|�8��`	�;WN
\�9�����=�E��`����%���s�����$����Bi��-z���mt���!����eI��q0@�G��tk�E��O:�����D���������I�M���J��W���Ab=���`j^K��k|K|����"V?-%��qY�G��>���^��t���c����b�"���4�&LX!���}��_�w���nmwo�\>8��j��n%[�YP�j\E
���Z�Jq�D�����Rc�1�C��V�T��@a}
�W+����NP��^D��X�Ve�8U&c)�G��OI
*�Z%�S��D�O<<�;�P�}�?�'P�	��1e�w�:��E_e��j�����4���k_w���Z��wO����Z	mL�0
���\����&�-��Ln�5�d6E�K���9���}G��lW��B�v�_�KG�^	���@P��~6��F�<���gq������v,�k��W�O��?��
�Ae������+��`���{uo�`g���{� ����������@�
�R1U� i@B���WP"J���5�P%a��k�fA�/���(D�7w7�h�;��]�q�A��#)�&��3�7�����R���K�|Tt�O��@g�(���3����T������~��\�A@���	c:#w`�Xz�)z����L���S�M6ZJM6j����3��s^��9�������Km�}�����A/^	|TSX��'gu��}l�\.�Jj�%�q!~���mN���i��a�+xb[����/��J�j�b?�1n+�]���V��]����:�������l:vz��q���x���J2��j�]WX��|����/���1��R�c*J'�/�	��_^S��<�O���&�+�m�?4q��$��}d����N��l~�����uV�N���l������j�g�F��'S���!��g�I�W�i����������Uk�g��oK�oE����[
.����vDp;Y�0��+�s�D�f�L?3;�����������A�`����I.Izv9���&`��=6n����vK�T��_	�%p����xO�Q�f<�yO�������Q�<��E�����m�v���x�!����������R�q����=��
z�����T���������Z����������z�3�F�/��z������O�c�u����V�Yo���M��~Roc�c=J,l�����'��&��������%{��N���p�o�����D�������w�z��E����7��PxrM����}K�x	��nB`�_�L�<���S������y���Ia��+�^��'m�{\�*��[�[*O����a	��P����D�-q����(��D^J�"C�H�� �l�����c�_�r��T�������h���(W��j�`�;nb70O*�"��;��=?�M:"G�`�^ {�]S���o���w�|����> $��[FRfl[�A�:�XE����U��k����@e���`J�i'DZD����_'z�f���~?�W�`��Ej�X�n$��F�-B}�����=�������.@h9Tv�4�Q��E!��l��m0�ck��-�"2��$�
���e���pv��!*PVu��~�"�w�����B������{�J���y��X���]�i����e��G���4-L`r�[���z���8;j�E�P�:.~��p
U��/��2��	�)=\/�
�q��7�T�������gy�����zK�6��pV;�	b��p3
�|
S�nH:��\����as�3��H��"������9T��������k7l2����d��������<nW.Z��b[W
�#~D��>,3��'��I���f�(�!�85�������%�Ei���Iv����"�,�VA�kvC�����P]��V�I�;(3d&��z�M��,6��$'�p����O���@��S�K�F9�����x���O�5�_���>�Lq�
��L:�1���l��e3�A�eyQ���1�*��N
�7X�?�s]�d���[3m�q�Z5Z����{O��`p�P���/Le���_�+�i����h��
V��"'���2f0�c]c}P�wrNrDwx-b����s?7����,����9r�����osh%6��#*������3�z��Np|PRHz���j���q_�(��b���
nN���6?m�f�����8����;=v7�%S=e��(�l�wkD:��{����������i����2"N�y#��^�V;�
����3���T��Si���<G�hE��R�����K+�\�?������B�����_� -������� 6���TzLW@�(������j���5�iGf���&��NR�������w�=
�{J	�?2��2�����o���o/�=�=�����K��!Gz������._�P���Zw�[�}�����k�������%�
z�c"'���WQw:�P��&v1��E�%&�q��8�^�%���)��bYc�����D�YV���9G��w�d���h��z�<o��#���C��j6���G-�)f�����kxS���}w����Wh�����l_�TOm�p0��=���#,��2���7�c�]8)BN��������G���w���~5�����_��5Bo�����-��%�v)����FIS��Q��*+��0v�I�����q|tE|;����U=�T=w#7�hP/�Q��� �q�.%=8+c�e*#��O���������:�~KS�����I-��"�o��$�-t�y\<V�eq��E%��f��X��l0���k'����6��di����4�V8S��a��O#e��m[x����s6��3t�����m������:Z�Z�n���
��'^���2'[���e�����	��3��
X���(����Z��������=`(a�G�bm
,�����&�$��������Q��I�Rg��77��v��,��0���h	�!�n���h�X���8E�<�^H�z��o[2g!\n�5��x����&�����|��r�r��j���<
z�
��>�I�eY0a�"�����K1��S Hs�N@/�!��=^����;s���$����j<y���x���L"�&�u7�k��8�{/b�	�M��]5�i��8�w��T�0fS�=�{�Qe���S��g��)%�h���#j���\�39�@��x��4x��JD�����:A��:/����$W��h��4
�*�
���=E�q��Z�)�Y����X��'�
�|!hW��`����{n��0pl���2>����S��������>�a����G����K"W��5Iv�$���e~�'���:����������
�}�x�xK!��#���O'i���#(����M��7�
�z���w�qtO��s��(�$���nd&���>�du����H`��e�p$�)h�)��`����B�y�}����%��a83�'&� =B��$�����5�N��L���"�_*r�A��/..����s����������&�~��sv��
-���~����`*�D��9��X:fySM��i��������6f�k���~~�1�[�����	U
�w����F����}�a�3������o	Q-�[�!��H-���Fs�;�z����ZX��q��j�Z	������y�+l3���Nm�p�s��q�T3�dC�^��7��E)�Z�4h�MxK��)L��x9���2�7�c2X��A�l�Y�3�Rvg��b��n\,.6I�(c��{�Ke9�����\-oK!�X{��{_n5������I�G��9{�&�i�Dp	���4��k
W���*F�+U|`���1T
��L/T�#5��em\��`G�)�J-�W��JR{��J��1WCK�����y`�������D���`�s��F���)FC�����X*��A\��3EP����,��gl�y^s|#�+C�,Q�3��yo���	���	K�����> �c�]]�}1
��bQ=�6I)��/�����8[�|gv����!����X�����G�{��7�p��W�q/,3�������?n�J!$�r9��aSh.
���������G"��W���y]W�+�Kz��)�XJ�l<�a���@�� ��Y2 T�h�@�4���*0��R��n�[w0��� ���@ ��|h2���n���M�Q|po����Z�h���'I�]����b*�P�!fz��1��FwD�=m�%
y����9�IG����a���}{����z����T���[-��3��=TP�7�9���S���^�@��w��+C��dV������9����y�%�x��S^���U�C��p�Y8�tNlD��@�&�C\@/�83K�=}5�<ci�_*���+
?"�����Y�F,��g�����������^���NG�J�������aN�?!\��m�[|�Y8�H�Eu�S���Q��8�4w!_��"�gy"R��a����l�����I����cZ���i�����rr0t)������6��e����U���Y��O�j�OfB��I�`��d2l�8�2���
V���e������V�q�^��fw�(9mZ��F��-E1 �"G�����n32����#;h|w��p�Ye^{�H��z	��b�����j�>b�S)��[��_������Ed��1�������,$	�!S��,-��h*�4�<z���i�����(D�@I��t*0o����iD�W��	SX��A�>O
�
U���������_9�����-�s��uD&�n�_��l������g�����G4���VNLX:��CV
��`Rr�.�J��yA^
i���S���;G	_(	��@U�V^�Yx��U��W���Nmw��W	���+M;v_��n
����e����	_k���"��r^p�?�������c0�lb���$���������,{�O���&��+�-�"
�!����jD&��?-��Vv�*q�'��?�O������;���n�����lw;��N��;�l�������~����t@�O�*���R��T��~E��U���e���"���6i��(�pl����������Z�O��I��T�o�����a�,n����	��~�����m���jw��]9�&<x����y��!vpB�=A�L������w�D��V���I
:�/�:-�=�,����:N38�}�)��s(���9hc�)�R���rU?j_�t�|,/VE�z] ��-����{NM���B
#��17b�1�����,��'Ze�G����K��,�c�@�v������Z��"Z��~��z��X����+��:E�R�-�5�j������X���6�Lx��\4�[��r~
8�K��p(������:������q�}N?6N�o����L�8�Oc�;������I~�h#o������*N�h�o^�2�����K���N'�����Y���l�'����-7��/Q��~<bf{|��o-��2�&���D���C����#�(����5����z�,|��MN<+��������0�����R�@��N���k\�����q���8w�>�����������S���-2�2�C48�.�j#���9���l��������
���D)��pC&��[�|��~���7X���M���5!��D��X*�TQ��c�@�k������X+CS����h3:�&UP?5�?i
e��W0Z�����YT�e��M�K��q�ma�6��N�s^Y����Y�����]�\4�w����Wx��%W���K�&m�B}��L�E����Vq� ���
i"�;0	��rFq�9������s�rGA%��$[���� ���^������+�C�y�I�=�����:j�|��'�$�Y��@O*�,^�+�C|F�5�H����k������Q��C[�o~>��j��r-O�f�(��=qX���T�pa���su������D�`������������0/lA��	����&��90��lz��
���"Mu��l�2W R�u`�i����bE���QDs��<�q���G�X��tn�l�W��~����5i��37PD�
)�f�S����lp��cfY
���-	��~#�t2�oec����_.���1��������A{$`C���|�G���O�i����w,��K��2��#;��+��i��q��x��9�/��X�]c4����X��j�k��#����}`�hm�q��Z�)�N>C�h{�G���,�8>�`��������M�Z��{�r�,�w\mp�������-��z�|pAh��_����A3X:����\��^��Ov����h��=G���{�:1er�z�7o��Hq<so��$'�_������H��Fms6�������;��3�X��G:��:����ZA���G������XA�����Y�������������������w���n��TA����V;�J�V��V��A�v`y���_������w�R�������$M�����4b,���1�K��y'� �E�@��/����C'�~<R{Np�����]Lg�Y����l&�{�d�?���=ty3
aE����F���n�9���L�����t�~U\�X��TS��u������M��wWCR':�m�il�X{<��T��$�^Fl�n.�W���j�����Y��1@�i��xVV������)&����Qj �=��(��&c3�������]����i����Y�z0\���A�������|����w�i#����7��v��9�d����p)��v�B�LC��,���.� L���j���Rp.��a;B��O4�m����xM��G�v��k�l[;�,�`{�tY\(�oV�"�A<�c������_\E��H�X��2�]H��,���>O#��	b`�o��������1�r��_��l�l,M����U���K�2@��5��J��j]#���������
���������(��3C�F�T4�b���H��n�>+��;���$L�{��B����LG�v��;����6)}��|��,u��[�����~��������IK�����5O����6����#b����m8�'�h�y �L��S�{;��#�W�Q���t�8S������^g�������;z@�n�=��_u�������v��U3���'��Y�%���A��Q6��5��W�������?m������x�R������"��/;i��������G�myv^�z��ay�3�������y�S���)���YbC����`�Bm ��6������Wv5��2�����O�����^qE���9���7��1gI�N�9��|�a4�)�����	�b�������iVP�_0���X���r�q���,u��et�n����mu����je�����Kyp��L+G�&�}��}S|I�L�$�"��p@f�����:���%%����g�iS@�(fx�H��"��-f�kqf������S�V�`�`U�����������O�DE�EP�2�������wq��v��QO8�XN�}x��$^���/��~Q�����q�`��f9u�mkU\�����~���d}��< ��
((�����b�_e}�@�Y��h/��v�iux$��$>���r�b!c0��4L�a��(}�g�(���LT:��������hM���g$|�����P��!�(
f�l��e(�'V)���U��$�l���O	N��n�?�C*�i����|'9�6�{;To�A��E%�$�R�1pD��1HU9�KQ�?��`��S��x�h�����)�e���g�md,T".@�[T�i����T�E��,�<�CXM����XQ��E=�XZ�qP��7r�l�]�M���y[I�?���WQ�9�����K������z�����S�J�m!1*�����k�EDf:>nC�gv�;n��*3232�����U�D�N�$�Z8<��fUz������u�s��DEN�n4q�\�R�2��`����{y���y6��O��~��Ln~&�����l��i����aO������(.���m4��h����:X|(z�Y��n���l�G,�6�?�GMf�J��}�A����8��52���c!z��p�b�K���WW��e=EV�PW����s�z�n��?����d���O9��3����M����W�5K�)c�3Jb$�C���y=��cf��&������>v�y4�,��������b���w���#�0�����>�
���J��n���yYU��j�J���B��{6�����6~�=+g
_�E�~��F�W&�&�_���������Z>��9�L���Z@l;��=��&T��m�\����d�+��r"��N���o����g1��b��*��px����k�����W�+~������r����f��B57�8��^��H�7�1����fL�"54�����dG���S�U����
�k�O�7M�����_����&sn(� ��4*;���Of�����ck�����������]�MBI�$16�IG��L�����4I��l�����}� =�����J<�{6��Uh����c��y���6;\�0�����^���I'-ty���^K��`��:gr
�PS��TT���vT/�Yk�����_"�����F�.��a�3�/�\��.8 3�#���v��%�W��:�_0���O�X.��]!��p����}���
���qh���rx��|�sU^���3������/���g�k0��c��<���.2+�/������e��������g�J^�N<9�W��������ok����� ��yI�Q�n+�����-���r��J���_�����L�6,�M��/�@h"~/�Y����u�M32���GR��c�c��&y-<��@�#�rm���]u�c���[pm����j~�f�Y�y�+���b5����KS��`�1�r���K�����h	w���_	�
��6�\N�������k�H��Jq%Q���R�*F������L�GsJ]��aQ�qX��m;K[���/�VOmw�[�"������hiYU"lR��Z:���;��%M���Y��jyu���S�d?2M����>�W��n���������/OjGT�'�/3��wJ������w�;�P������M,p��;��a
[-_�5~�L�
���8����K�V�
QH��E|����YV��G��W�l�~����w�#�4��#�18���9]?q�Q�K�o��Z+��7�\m��n�V��5���B�k�iy�D:��F����Z�e���%�L��� ���c^���h�o�\[
j���wo�;�oe��It���l��T�+�
������������JhiY<������~�]���y�-�%�M�M���r��uSJ�=������kQ���2.�:kW�_��R��*�������&��Y��v�j[hJsL�{�����?V�)P���V;���A`V��8��{����j�,�����"����pJ"xQ��h��i����B#|kG������?������ZW���+��s����$m��VO�pp������ZX��+w{�AN�����*f�/��t��$���::�l����B�����7������
�-��S|��:EN~�U�
�[)e�L�&��eO.L�}J���,Vg-�4������f��F����]_.e���WuXo
1[Ic��-����ym�d�m��
��,v/v���?�r&i?�4��"u��Q�����V��p
[��*(�����K��,�G����^-�V/Z2���Z�\�M���Dii�\a`��n2=!��k��fo\����8?:8xyz~r�����Ek{�	�j
}s0����Zc17���%����H_���f�LnK|�q���3B����Vr���0_����6L\co��?:|A�����$3���`?�x���|�Uig[������r2h;�Vm%��V�6ZDv��1^1�������~Y�~uV��L�����c�b>�����{�qn����=Z����_�#����>��E`k�j�[w�V���=?�T��O��������.��v��#�U�����,g�yo����������u���$U�����z���j��]V3�*M�C�T��(��wp��\�w��z��
����K�{�v�����)]����F��f��s�M%w^1)W�����Vz���89�7>���$%������~RW��[�J��Ru�4%P�����p&bQ�����R�.����m,TL1������{{V���c������|(� �z��8=�/��y�|��;�r{;7���l��h����'T�q~���m>k�H�X�)�]sh����>W7�������k��������y��>�}��e����e�c�f�<�&�94	=���}���D`}]������(�E
S�G�����UK������K���Q%Q?M{�u�6�J>��������A��������M�[�|�mh�K��!��c���p�t �wr��������#}���WG'�/�����x{�qK��',���#���p������O��|D<�_;)h �K����r���;-��nX���4`k
������l���2��*�b�����^?��
�O���A��&P3-:@��������w����[�d_��<8}���������n�*��;O��~�Gm~��H�@%9��i5�6!�:X?���j���0���K���^�$i�v������^���R^�Jh���N�����w�v�P�-;�������L������~�����j���(�r]n'���",|�O"��xi�x�$�����y_����z������k����w+y��DTY���kS����]���n~��}��]���X;�&�Iw��k��;���
�h�Q��"-����O���q#��RP?a'w�����*#�%q�{]��I?��xw���g����vV+Z�<����
��5�eV�:����0��1g��af����F��f��;�Fa�	�m��/���i�O��*i'��sL�y8�R��l"?�f\x qs�U��{�7@{�����m��e���_U�pU���]A����6dk�[Rs_��~sj��O����&����T��W��������z����1h�-���R������tZ�2=V�����Rw���='g�l�������l�u�3kI�[wn���������aFi���~���]��e��0��/0��i
�:�G\}�����/����B�����T��m��/��R���!Z���v^I����U���t2������u.��`�d���P.��I���h8�L���F��y�o>����{@�>p����`��9���r4HK2X��$�^Tcm�'
1�]B��w7W~��b�&�Y���Z��'�W�N�e�sg=�kN���a9��z�v�.>k�?��9��=sW�l����9Gc�
�z:e�s�
Y���\�!u�L
9��0-Y���e1n��UG���B�?e���X7�/�\j%�1�����xYMXj����ok>�1�bF����6
|��tll����R���J�4
��~��"��Z���$����1��.|pg�2�u��*��xj��3�^~W��5|���MB�	�Z��c<�weDI�!�c8�}�}��,y`Pp�+����
+��t�%q����n�'�0D�5����V�����'�%a��H��?�X��Oh-��o������_$l��������g����k��ag����[xE/�+\��3�t���1:��;L����|C���i�=3FQ��������l��rGx�Q����8�0���Y�H���	Np����!��M�����>X��6����w�O��G�����U!F��yuZ���C�$����u��.U�S
~=v�C�����9�m3*69m6��zZ�N��c_���	`�S&�1pmqY7�2�������l��qY7�2��U�p���>�G�>L��x���8�.������}$�zy��}����vA.��F��W|�����s�9�
��n�P���o[`UR8R6�;����n�J���}��������an������{��~��>v�dT�#������h�S��5����p������n/�� i$����tI`5�,��Z%�s�.�Vm�|8����0;d�8o�7�Ohy�*v�&��R<����)�}d\����������#����w�������E����rj��������U���+�<u������aZ�4���A�89�?;���|)1�&��Nqd]e����	��1���xsd.S9=8sN��O�_��Hj������3g���|������������YD�����~l1�������F��'���a������q�L�aY�Sb�[���?5���9��f�������/�:�����o��������#f�s�xz�<����"��ue���
����|��!�+�}�M��*�Q4q��b[�U�,(:�?�rN1���3-E>r��	��a3��?��t�Ca�h�����S��+��L}��e�����_x�$�G���0�|_��Y9��'G�G��.�x����K�#�(���-���G�D0���C�Q������i��~��3b�k7$�>�|wF/�}M����XjN��>9�-�Y�'�I�O� �ik����������bx�V�(�(b	��,bl&����>:q��~����?||0��<1����@D}F�C����8��.q�q�����Y�C�O�O�Qu����N�y���H��b�,v��?}�����&�����/:�^��D��nd�,��Y���!{��������X������e��'�\��R�K)U�>�!}:�y
�>�s�����g�,�*��g���j�4�3���s<�y<5��<q�h���K�{ �%�?gN������DL�������������
�t����B��/��w�?�xH�~���92R���G��<������RE�cLi{p ���,���#z����b�e��o����	Mu�}�����9;v~��4�_S�y�cl�
�y���6��?I,�����h���'���|4�

�W�+K����ea���W`.
������i�J�����m��|���0�G3)���W���]���
~eA<R��1�e����A��|����#����<���5�~M�����m�G3��Q~x�}<���R���<�E���?������G�]���@}w�g�&<�i�x��]��]��]�7w�Rl')K!R��Nw(�wpw�������-.��:�-��
����8��u������n9� 7c`��>�@j�5�"��X&Z���P����>}_�>_�+K�'B��g��g(�BA�%��6��#�P[�H#�(T���J��F�q�%q�>�@��D#�T��i� ��J��F��2N�U�������>b`
B��d���(t���Y����)C�2��8T�/Q��K�m����P�(}f
Q���������x(����+r��q���d~��)������c��D!j?U�?U���"<}��u����??V��BE�"
�(S�+D$��h�6��%]��COQ?B_�+4AJ�Q���~���ce=�D!j��x��L�"W���������E�2�(R&!����e��TY�(S;�2�5�6��C���H�q�l�8Q� W~��DS?�^I|���U����H�*%
KMR�n�La��f(��>�
QwS��A�&��L5��\E,g��H,��,T�:��mO���P��<Y����f���*����X�c}[�3����)VDYS�KF�&j�n����qq�YE"��O�w�a��6I���D��������H�M/�_�������l� �&�����
�r�C���B����>I*+��h���<�'R�6��k�#5#)�����$��������^��^�aW�G���X����[2\�%KR��S�^��^*�L��!�~x�����i���
Qg��A�
�	|���w}
j�*Q�x(>�H������=�P�=e�|^m�]L&��c�}������}?���C��Y�a�e��A��*��Ce=��7_3}b�x(Q��ma6�G	dC*KF�#�X\����gXD��"\�w����X��������u��~��~�`��'��}e������b��Y��:p}����`E4p6 �"�&��$�G*�g�)D��C�C1�l�������Aa*B�������
C1Slb��p�Q��J@]b�4�P{������3�C�x	v��l��	�b��T��i�u��A��A�aP@��L�����*t�m�F��C7��G�)�")�x�B/�D�.V?���\	}��
Q����P�PI������Ib�m�1��P9_	#_FJ�����2�8T&!N��K\e��h!Lbe�SW!�T1I'TH3U_��W�C))S�?#����\E� ��}�YM�)��E������W��#?�t��6��(P�(P��H��Q`q�	tQ��'�u��:�,��X1���*�
����<J��D�o��Ce��b(F��U�H��Y�p�b���h�*�1Q&D��L������bU=�}(&���B��3P��� �D�����{!�H�OM�%t0��#%�)����8�0Q���
m2e����S�^����8M����xM��
Qg)�W1��`�H����k�K�������S�|�������h�"I��JB����!V?�0��6�����dF(���A���J'�85�P�DX�H��O2���LL�I��I��4��nSW���������z�z�������b(���,���i�a2I��+@%�4��:%s%V�4�Lc��&��'�3�4UBOS(f�������R3�|%sc��I�� �BO3�[��`q�����>�4A��3R��P��,TBOi3`��4�#�C1�c��d��g�D�8SEA#��U��h��i��aC�s]���]���9��-#�p{���N@L����sl(0�&	Y28>������C��8��#c51��\�P$`ML�x�%����f�83lJsl$j���O���@��.O��}�'p��n���zW�
��A@l(z^�Bq<���.T?�V���|k6&j/������!���yJ|3�~p8�+�����)D�{�^��(?	���${5'-�%���	�������U�>��H�K�T�>2�1BJ����#�a�G�X�f�4�����GL` =%��q����P�����&P���/���1>�����0�t���G@l(zA��CA��<>��}�n�����u�c�-GIa�!VB%�����D���0���^{������P1	�b�.Lq>Se=�+�a�C-H�G��#���^��m��CQ�������q�)3L�Q��Q�
E������0��6R��CO��(�������P�b����?���������I
,tH�1F(����jb����F<^k�P��Cq���8����0Q��O �b(&d����<�D�������
�M��S��|L�|���C1!=7a���������������?&�D���VE��1���Q����M=�����L�$���������������A��	��H?���*I����*�b�Exf.�oB@e�S��ad=�2Rj00������ ���f�WC��q2���bEt0���8������e���b�J��e�
t����cH��V?�
E�
#���n���0U����|7���L Q�n�����bC��X�������@�������1Q���Ca��I��>V�1B16�|�\0B	=%���D�<7�$�����I@\��d`��O�8}%������|$bC����|?���3B�P|�B�����q���W������"3����
��C��6 vX������u�|AbC����� ��l�B���>�$Q�CO	�5����6����3T�����naR�y6q�)W�Q�1��% ��|�RJ�����W�/��	>-5^2R��bG���#�~(�6R���\%��&>I+�#� �P��Q�
E����&�p1?�U���J�+����4G���O���T��X)fC[���*�q<)�8���M1Qs$J�
E?q�6�p1?Q����W���~�����7! 6}�Z��%J1fx=�,
#������c��+�g�aC�Tl�^P���$J��[�G�����)����i��	�F�b6~��������<z����D������������aU��,���~���S����2\,`_=������J1�����n���CO7�!Qn����b(n�����l��,<}�_	�Z2f���������b����Y��
W���G@;���OB��q��P����������J����M�sl*�e���I����X������J1��(&����3b�4������W�Ic�[�i#*��b�0�������g����$����%T��0�l<�p>/M������3& 62j���W���r�8?;3zJ@��F��D6����&�n�b6ADk�����F@zD�"?������P"%~(�]\����[��@��rT*&�8����V�����q�	L�q����]5������[9�>�@3	����$��lH4�X�$�o�5����-��:I����b�d8^3H�b6A��XO`s��8�4HCli�����q�m@&��,E��R(�\zJr�����cY�
Eb�a���Y�`�#�q=���2N�?D���K�@����' .f���p��n����M�COC7��>.s�4�`}��G��(�lB�u���u���S><��M��#�PO�p�
)��%��Wx()��z=
}��C_����P}�#��R�&���l��H����������O��|�
@(�e!�����:����\_�u("%�#��1����(����J1�0P��C�5Pq���D����LHl(�d�C�.����:L��S\���8���+|�r�
�g�aC����yC��Mi���p�1=
�$�D�X� ��
���xhz1��D�
�����0q�)�VoB�0��s�q��?D�����*�3�p��0�q�)�N|=���PIm����yF(Q�!�s��$���hc�x�0�����bC��
\#L�b6a���d*��B-���
&�4��c���
��4;<����!"l�����w�ix=i$��3�z�����,���������%���1/��\7��:Zxafs%�g����H�+^�Y
E1��P��j���@��9!oa �J������PB��@m���3�
B)C@,�%5��4% �����P�3PY2���
���v�1P!jkd����@��=�y2����.���@m�X7g �����-	B$r��T�Z��m-��V���e�@hS[��v,�m�\-�@huG\L^*���>b����G����@m���%r�
����(r��1b�
�W����F@M����x�"����1r���^Nb�(�g���
�7���L@�A& �=3P�F�!��Gn���{�	���\*@��>K��S������SC��D��+����
q��d1>�" >�"������,�K�0B	>�# >�# >%d�S����P�W��P�sT�%R����������b�&Ha������>��sq�����Y<�)~��S# � �f(f8Z���`��B�p�������DeD\ZO�H*�������m���#^".w�RJq��G�D��8�(����Wx(>�}" ��" ���<%������)�a�qe�i�R�Qp��>CyG@�P��Pz�8A����+@�H@��#I�Wt+t��M��O.��l���G��z��r3J�)��".�����8^���^�c�#/�Q�|����L@�P�R]q�P�#�	�c�	��g���#�U��UE-Z��8�>���Y%������=�O@@��i�)��+KFb/��d\�jP��~������Sop��f!���D�!�%q0�����|I���j�"'���D8�)��X<}1��b�VY��oE�����b(�	�6�|�����b�Oqn���!��SE��3�P���A��C���(P��#�b�p*�3pqff�ab�<E���01BJj�ab������B@�i��K����qfFH���o��T�d1�?gNG\W#�l���5p�J)1�|�85�P�|v�L�(HC�#�q���lR�����}���/Hq}���#�C1P�)�a�BWp ���
]�z��D�)�p�B%u���K/�q]
����<�W��a�5�������q�x�B\'���[8�pm�H��q�#���Lj�b��5�uo��Ae�\k'�:�d)��C@\����H��B��r�f�R$q,���s")��T�H�.�iVx=�<n�W�H��,���[fx��P$���,��g�T(�x��)�SGQ�+�P1�W��D���/���\��F�������r3Je��e������:VJ�s�l���Z�P��z���#�V��:#}(pEH�Z�T�����$��~�U�R�m2�c��#\�3�c%��D61��#% �d�Ji�H�'�b��J����me=3%^3V��G4XOH\�PL<\
�����x�>D��D��K@\/���b�#��$���	���WT���I�P���&1��L@\9�����U�p��(Iq�l���TBO���&��~������8{���+�GZir�J�i�������ISR�T�m0J5�m�T���~FJ�i�5G �����H"oAb��8�R$��]TBO�����1{�D>�P������d�U��wSP$�0Qg�MQ(�b��;HP�k�C�,�w�P	=�"|�	_X��}+�b���B@��3K�
0T�,UBO��WC@�����>�]�:F�z�<���F����U�8���H����)%Pq�!�-���@��9�-��N���
s�&���1IHhb�f��L��[���]]l��%�\|?��$��
�����f��!�����w�q�6��9�#b�#�"l(�NGV-�# ���`RRJ�����+�
��S\z��}d��c^�@����R?$�[2�P����3���p���L�P���1���Rn����LTnF�}�4�xF�P����o������Kc_	=% �/5��V	�
E�/�����/q��e	���%��COc�(�)�!Zjl(��r[/Y28>>f���M�^����PR|�1��b6(Wc��rUK�8�!��cbQX����Ed�+D���6c��N_�����rL.W�a��[�	������	�
��kI���8)3|#{��[ B������!B�r�|��t
�vGJ~�?���������C�P��Q�
���mhz��R�(fOnV	h�#�T.�u����,WH����=��c%���	��,; =��P!�8���TE.���/���S��8Q�g���S����:VnF�W1�=�������K|��&J�)L�I��M�k��
z��*u�	�`��H��$���DA&�D�"�b(�n��6U�'���(�*��	�COI�	�$��#���i�,��b�Hg�}��~�&8�4N�$>���!�C1c/��&q�)��32j1���'	$��2�P$��,��jh�|���X�?�D	^�P��o�,��b���l��I\N�@�OH����/q5u�m�wZ@�%���g���$<�Y�?D@zJr��DM@O������$ .fCf���I��cM�����&O���bC�/'�&I����
���A*�RzJ�����Bz6	�@��=\�&!��b|�+��	_7�
q>R"7�A`�B���!��<�������b�����o��^]@���b6L���vX%A�CO	�b�"�/HK�
Eb��`���$���'q�)Ba���r8����b"��!P)f���
��.��l���t�I/�8�7	C�4eJ�L�B<}\�
"$u ���bG>��N��T�m�����v�"o�D1=M8 #�*�G�bC1�2E~���L1{����$V��N���t�~B��V�P��C�R�&���+2���c�7�*D��p(���q1�jo�$��}��TL0Qk7�$�ru]B��U�D)fC��x=��k���x����P��z5I�cC1a�7��&�a�x���% =M�8�D�*���4U�4���b6IFz(l6S
D�Z��S���I��r��	!�?D@\�&����	��O���r�&L�Y������
E�G����J1�T��%u�����]@�K�F�zo��`C�v=�����:u9�q�J�)�|�;�:�|\�/��r����j�lXB�#�b���r�
�>.��J����wPOH}���6��l�*�*�e\
�}<	~��k�|w:FH�m0��b6�����p� �2L��F�|+J�cC���x.f����� ��y�3Q�3PnFI�R2�P����P)f��^��8�z��Y��:q=�4�����rPD�a��+3(�9�
t�q�)g@a����T0l(����R�&��g�l�4�	U;�H5�����Rl(���U�4vq1�!�[*������w1B!��r�3��`|�&�8�fPg�q�i��X�M�r��f(r�
41���b6l:`����P�M��lAL�d�`�I�<^O�Da��R�&e-�e$�3���)�7L��rS�<������>S\�������L�?D@zJ�bB���z�4���U���eJ1�4��hLp=)��S�����
����A}f�������l27�na2[q}02�q�)����G@|=F�*wX	�|��U��d���c�
"�)����y!�#�+%00��+��/|
&��7R�e8�4�]���|�z������������|�Y	�1>��������nI�A6��� ���,�p1�,��\_3BzJLS!� ��cdA�
����
d�R���w|L��=T\8%�<�&������*�bF8�3#M5���/Lq�[R[p�E&�4@�����
�P�f		U�L+f�E���b\�8���&	mN��~V����Y����,�p1R~+MG�����7a�1�+q�
������J1�,��'��`��CO�D�b�7���i	�5BJ.fC���%�O���fd;`�N}|=�P�����R��
Y�X���R��V=�X��e.�#�<l(f\e7`����^�����q�8}D���3�zd�P�{�5Wh��~����?�� ��4�egZ�&��S3��������M.��S�/�~w����s��pn��K�7���:�d�+������-F�g<�9������.����O��x6�uz���,��b^��� z�{8[([T�/��i�8�M���
�}���m����������>��-g������o��[����8����5��7��-=����>��:�?az�G�^�-T?�}�����s����g��/e�����C�������l���������4����Nk����w��������v�+j������\g�wG��t���NR������XA������������������������\�?����g�����a���'o����	��1���xs�H�g��u��hy��`���!��hi��Uu���0�}*�/��q�G�v�����,`���DSE������.�����p���������Q5���V����j�W&�5h|d��G88;;���{�����=BuZ���u>�G�bt^�.�����{��6��~��I�k��������~�Ei�"��R;L��il{{�~����;;Q'v����:����o�J4�U��"��n1������Eo6��K�J93R��cy��*w��������y���s���-/'�Q��t�'��.����=ij��p�����t=jv��vNZ�������xF�������6s��g����<n�2�	������ilBM+��������P�A�f']����R�e1t�����p���[��yIhJ�=���C3���zB
�����w�����d���h�FsK���� V�<��g��WL�
��(�����J�� ���Xw� ������Pw��:�X������q�^M�EE��J����nX�A�&}!�=Z�=fa����d�v\g���A�t���hB/<�����!���'��Ff�x��c���	)�=V���C����gV��!jbB�������m�O���7��?��:Q'������K%�j6��<�Nh�w��;�U�-���r�7�����s*n�J��0��^��!M��h>e+����1�����-�M~��D���|1���
y���s���|4���>b��o���^��h��=��Z����/���Y�������sr��������^�9�|X��t:���qrp������~B���_���h^�����y}��h�tc�����v��/'�����������p��2�E+���;8z)Ux��^�,�p��\�b��.w�>*��@�4�t�y����SL�O6}<������9X?	<���9�<�Y���'G�G�����������L��}Zg2Q�>i���S�TM������3nm��\>}~�|+?��:$�F��
�3	��o��}
^����>��D5~��`�Bn�A�:Y�i��bF��������G ��������u�ev3�sJv�^����*[��?}����/�?fIRVx��_u����`���Y �_|��Dm	��lE�\���k��^3��6������!�|���4&/x�E
Q����Yi\:����};����Rz�0���Yf���X�&���HfUReUh�������B|��`J���7�H.��	����M&��������GgD'��h�w����;������,k�<��ga��_���o`i�'�n6�=��j��q�=����}�3������K���j'?��D�
�_c}>�Mx��B��0+�s��o���'�cf���oo�t���
��n�������Fkq^�aQnz	D�r.6��]�W8
�~${��y���wV�����d�����?e���x�����=}Gd���6�C�&?\h��a��(�n��z��/��o��.��p�=.T��m^{;�Y�3��.�y�����;�����^���������K���%�����'��;^�yq���8��W�������O�3'^8%T�;Fr�����w�������gqJ���,��A����"�fn�{n��Y;o&c���v��q�����O_np3����lB���~2/�������v�������o6^�`��y���&<�����s7u�]jk�t�������?{���y{^���P��3&�A�����������&�������1�q�����&��8w��LH8��A����|v9�2����t������D�sh��{y����o��
�9^��W���l���T�5��'4������no�?������3��������?c�9w|A��
�E�9���4���|�����i!a���Gl��9���2��p���x�T^��������>��/n��|�{1�
I?�����G�E5��y_���h�/Zu����{����k��	w2���O`'<l�h'�����4�9�����Nz�$��\g{/���.^�S��[��x����Hg���|���3������Er�������;[���
��k|�7�A���*]�/�o�&3 ����6G���^�
����h�fn��W3iP+M�z��2������lr}��G�>�_;��{��������+[�x����h��G��s���/ON�0�|�t�$9C�_�Y��sY�����;��S-Yv���6v�<yRe��������mx�m�����_����^%��c5�f�W?���L}�*,��=SU\���^�F	LU�zi%��l���hg������/y��_OK�)���������_�s����a�����v9��_���6��9�?fsP_����Sj����?4��������A����~w�S�`��(RK������v�p��1H]sn��>����H�9J��r��M��fN9��KI���6����Br��1I����g3����q���FV&}[�K5���iA6�T�H!�;'o^�H;��v1���<|��6�����L����o����������`�����j��^O����^>]����k���T`KX�
�>a�K���S�������!g~]
�K��������0������Q1���[��|IY�����n��_�9P�x/j���E>�`�w�[�����]��=;� 	V�}���
� �g���c��"5����u!���%�z��l^i��2B�q����h�_~�i�;����������g1-��A>*�7�g���m�#��l�}u^%�����:����M)q�'O
���b�_�M�<�n2��~�`R�f��M�/��i�}�{���7��dz�b2��;��{��������>���S��Mwk�'�`w�����I��F��3bc�����f�����Z�����dxw�����8?;xwv��-����F�����g�������X�� d�l����13����zo���Xm����!�6b������h7��p��e^�''�o���X�g����/��Y�hy����(q6���MPo���f8�]�M6�&��cqO�D�0�������w����lv�����0�6�I���L�[�W�����t++tx�5����w����v�[v�z7�.�b��?Y?�����&�6���<>|)��my[�&%���^O.�����OF%�/����Z?��N��d��(����\���I��l���F$<�XJ]���nYx��{��N���u�<����FI��
6{���'7CVH���d�!��0-�w���O|:�o8������������4B�}�Y�����hx�oF�Nqu=�
�T�%J�����W�������m���E���!�����W���_���p!��?��s#Hy�v:&�����Z�n25��'��,Q����Q[�y�����9	]����]ea5c5M]
/.g����3�)��d��*u1����7�Onh�L���,��i��l��&���9�1�O��:�|+�������g���f����tz#���Y��,��684��n��V��NI��������0�Nv������2x���M�p��_�v>�i��q����=������[���Rthu���6��-��M�������
3�s�J���a����������hj��tp�r�>�
�$8��.�w��Z�~�HQU1�������Ox���q��x1�d�m�����N^�<�Y�|�GR���c�����Z9k�����;-�j��3-�;6i�N��CtO������-����'��	�c.�?�b�g��������%{IS�c�|���=m#�X�f��(w&�+�������~w4!S�_o6�8+^��f	r���*l�S(��8��[�59?��`i������������l��4)?2�0.~S��b0-��l��i����&��{l��zx�
� �<�!d����rR	���Z�u� ���z���{�����]�?\��6}^�)�9�m=!�
]�$��yyY��3��<���������1f5�xL�����NNHP���2���N����RG
��������t(�%#^�q�J������Y�pa�B��-���LrCA
�4�l��8�v��6�K�u<l6�}'r���������0a����3�'K&1-�����?m��!}ZefX��O��KH��
g�R�������5�f�k����4�����ZJ�#���V;����|,g�_1�?�^�>>=h���
j�m������e-���k�;����
�)g�9_X�
�6���d&���@N��o������VM������������J�+������i�������=}����d�A���{��{����-��7��0��b�;��mmT�H�,����,���)�4�?}������������Zjhe�@��b���h�������R~�9��K���\;|$��n��#&heM��/�,k�b}\���?���_�����{|�@��������?9�~�M���%����L@3�?|�2	�����/�|�{[����3�u�hC��ff���c�P�]�U��'kO�V^��TE�zE7�F���������������Y���|��|bN�	�cO����<��u!������G�.m�����*��@%�z�k?���~�����'�������=��l�����~��lpM_�C��3'��zB6R��{�1�����o�
��?[�����j��S�oy���+vX�7�!i������OOW��)1b��������]����3�V�>2s;���s@:O�c%�La�]a��-_W&�W�{j�<���5�������|�[n�x���8�y��*�v-�c�f��I��fE���2���{';\j�������]a�������z���{"��|�|l�f��~l����p��Y�k�)v���]�(��������n7?#N��S�\]@�uY��,djV�ym^z��+x;����}s	X��~��}@l�D��Tdr�����W�Gd>oC^��X~�5qy/�����1-W�d����L|c�������|d�X�b���|�'��.rka;�w��&�j���vZ�a��w��������jKg��|?�vw�nJd���u������=!�R��H1mG����%)�����r�ix�OD~~���v��&���xr�����i��'[�&�����wL-���]n=���������Ow��`�������s2H���,�9�b������=�YD�x��)�9��g��V�9z�G�}��������h2�1����*^�^�"�5A�]n���xi�x�$$�?���k�Da;+���V�u���jm��?��.?h4]"�vL�^[���V�5��n��yj�b�����y��~y7�vw{A?��^����8O�wdy����;r�wu���^���M/�q���v�kdm�u1pX����==�C��g <nj��=����%��q?x�(��O��:���I��.��q��_��o�7����?� j�ok�H��7��wM+�a��6�Z���$�h/����g=����h����O�vBg��f�q��������6C��I�jO�Db5]�&�CVf�O=��V��D��2[��6�i��%�����\���
z��?�![����������jyoh2������Uyaqh=0h���d��E=���J�SS�����������u���[�UG|���S��N�#��~�HZz����rf���x�V?jF���*x,�z�4'y;����{p9�h��Xz����K{q��]RC�0K�8K�5��;�[r�E��~������3f��x~�����+hm��,x�cL����W\NF|��
,I��3��hrw�� m��������v��=�X�oz��`\[��u�[8���i}n�S���gO����u^LY�pq�/�y�Dcaz���������/d6RQ�����lH���<o���|���.Wk�bM-��R��qJ�����^�h<(����~����48� >9>:��%���������R�p1�N�+I�����O{J���^#-��{���?��� ���t�EY��u-���������Q ��.2���q~E��7�3� �������������|z{^��}����OF$�F���<�U0���9��:��|z��R@\�`����os}��r:��������#>Vzj����)7����z�]�i�����<l*����N=1��
��snji.X�S���6�{2������~�5Y�������*2+��������y�0<���Fw?��u��y��Z�����k�[��{���QL�1�����`zk�Q!�-��J��7>��;�xC��l�&L�Z�Jr�mknR���r3fZz����,o�o��[3+��2�gk�ljzr��W,sO�������Ho���H����mj
w_��� d�N/�,d8�Ie`�zZ\|'��l>�$3Q_��}���"B����I����)K�J4]a��?�����R������wly�b�����WU4���� m��r6��Qk���_�8����/Q$����K���/�~���������l�����^?	� ����������K��J��Jg�*�K2�qK�N9�����]�nlH�p��H�!��|��u����M������1*�rR��E�{��9��������x9,{�����;���u�|o�0�����������w�K���'�����?�������������������Ezrv�"�~��?�������������d
a�
)YW�&��$"8F$8�U)����_\X���o��`������E>�$J��y�+�.�UCu���lK��r�/�V*��u���#j�����
G{�k����7S>��y���3���,��}�Q��������k1���������Pm��W�n��&�Db�����R�ac[���|�!� ��v�������*h��K9O�{�t��F���i}7�*)�V�P$tm��8�n(��Vw�:�OS�zh.��r�YYll3������k�;�x���)%��st"��c�8�./'3�d�xri-Y���3H���������d���;�+uM��b�yz���7d6Cc����{�Q�s��M����/b���]^;)_�>A�,�6�b��Jb��������X��ly�jH�T��r���q�P��o��l
N����~%m�zKnu���d���b�!�S��_r�:��B+�f��/F��6s$�K�D��`����p.�r�RM4�!qB)����j,�?#X��
IW-� e%k$k��9��4d!$iui��W�6��
m���\��i2��vj��D�����m��D\J�I��.��i!{�(�bH[�)'�zo�I��"���k����J3mJ��Dy��YT������^���0W�	S6e8�8��-o����_K$��Y\N'��K�JkH��?Q�c��E�uM{���E�=�|j6����9-F�r�u��fH�O>Ue�r�})���%��}S�$�^/z�V��$�{r�2���������9��S��|�y5�5�u,��2\�F������k�q�0������spA.=J�0=�mk��,�����
[���zdKU,�?��n�&�6����'�$��/�.9%�������m�#���������;/�4�^��o��$}�����E�����}Bw�ah�1rNBdB-�����I��nzl�����i��4�^�5�;m6\�eGv�A��!�y_�W�6��tlm(���<�/����m�2
>A0�e�V�:����&������Mn�sv���[�O�r?5�� e��3-I %L�Z3�INuM u���`��&��T��.��������7�|%3�Z��U$�B�RE�E���pKz�\pN!}��KI�7��{�����D�3aF��w���j��W_Wy��/��^6��Q;�1D�3I������d��%��_�.��k(?6���VO��n5l��7	���Js�T1��qm��e�[��0�
���yiT��6s+n&�����(6����������m�|*�#����_ZM��a�
+��p�rr����
�_���]�
+�v�~�����R�F�.Hj0�+��I��q����,����k���;��(j�D\�t4�}-6$i��)��X�)�6���C6��AO$�e12
 Q�2�J��X����J+�7*2�NI�f���]��l�\�c\�u�*5z,aAC����l2��n�nNDfq]Pu
.&����x6�� ��gN�����[k4E���J������4\��0_yO� �V9?s6��o�D�'�&�V����{��
��0&�h���]�jqi+exJ�,��v{Ke+�IS��-�t;�j� ��(;�a�������*r��q���yV��0�MZS�3}}z����7�o����4#��YS�*�������c1,Z����x�[3��uo�&�}��f��@�h����eE���W���ck^wo����^�?0��V�2�X�����*�WT�c��u"kbl.�;)����"��h��0����-���`��k�oq����������*:��_���pfJ��7A2����������yA;����y�����r����LG��l���'��|�%��'[�}��%��F{=���r���N��WKlMS��sc�����y�|kHu���d`d�"3�`���7�ge�U\�}>��|��/6�����hN�S��H����41
h�zF���hD�Q�mA��LX�#R9���M��j(��s��sc40W5��5o;�Rkk^}^�o�c&���T*Z���Y��Z~���(��d}1��`�do����]��c>s:�eKB���E�T����U�T��T�Z����%����R~�����U��������$������p�u��+�'&H�$��t����4�H�&N����eY�{5���7I*/�����[l*����V{�����/3���G�*�i�c��Q��"�������k.f?i�M-��x�8~�(�m��k�zjX��"�7;�O�������X���H�����0+9+����K�qA�r���l���|�uG�uo��'���G�+m+�YY�c	i��6�[��[������a,,x������l��C(^�F�Y�M��&�u����N}��%R��x.�d����23�&����xU�:W��T����bZ���kc�����#-e��+�hC[����b��^H��Y�5�ZL��\�k�#�_P����U�:�3�6?�w���T�P���I�����51�Xq��H��`����EM��~�+�Km�[^�M�
���f���G������z0q��k�:��F`��V&@l���?��$���+�:+�� [@��"z����s0��=s15���|hv=�f2�6`>�gr�l����u�r�}g7��9\�Q9
�UY"D�������M����4��X^�u��	���-�b�������Ygc=��Y��qON���T��6k��b�Bt���L��-;��%W2��-��y��1�+IYpp����r��G����T�2���s��7����-}'6��H��$��[d�{V�)��He��d��7�(U6�i��O|�rMDP[�#���;���2����~�H��U�]bw�����������Lz�9�W\0H�@�46=�l4Ax1��WY���h����l���`Z�u�B�����a�v��P��
^B�ub���U�so�lW
�r�pZ�s�K��������
�����0{���3GD��������bJ.��9��kY!�)Le�DYo��o���)�vc^qT}o.3����d���� U�;�.k�@Z_\��������7
�:��f/��cG����3���Y�gzi�*�d�IU���rN��4U�*IN{S����\��&M���M�v�F���Z[��������[�do��
����,DK[�D����{a�E�de"6�K�������t������T��&\����Y�.zI;�OXM�W�x1�^*g���2�2
�)�f��i����r�*�g��)�Ns�)�R������t2�p��-dOW��fQ������m���#���^:SvA����B��="A��������4�Y�+�,�]���������6���y�*h�Tf��0�k4Y+9�����m�v�k����5���Zt�z�*����������"u�^i���������-[#�3E�����u�Qn����r����:|�:&�L������^-UA:$�����{����@����1��^��������iEa�fe1l��/3�6G��T��u:��=��R:��Y���GkB����2�Y�-`K�YH�	e��)]~i���1��gQ�g*"LB��NsJ���-�,oXm��7�ivv�:���[��h��
{"dAnr�������?�Qc�����R{l���*��X�m�hE������d�\�~L��h�������)��r�O���������k��A�mn�)D@��vM��p�{:�-�� �\���{L��Eq6���������p���\*�	���rc�@Jl�Ijv����$��-�?�����L�{[/�MNk����������
��i����1�*G��DO{]��(�K�yk�%2��X�D��FmX��F���T���%�i��"�Z}^i���jw�M����p��[�SA�O�Q)�kc���1�~��U��9�Y%��u��q�b��8��"��I����K�F1�Df8r*b�6L��u�W�P�L�
�4�q�!I��0`��
�� ����Se�y�����on��M=yb]`Kn�KR:���1Q�������Z3����+���z��y��bzR���y�o�����/�x��jQ,��s��oj�<���	�bdpVx6_���zy��i�R�H�lR7�>��"�\�F����U.��������+���7faG�,�C+�)j���w�l�F�9U@ ���)�|F��L����.^v��8	g�������'O$��r�=2*(��$&8N�i���uz�7��IJ{������@#��`c��LdH�g�_6�c��P�Y�;8���	�)��eTNM�d��T~i+������L��4�5U�y�+`���5����� ���������,J�At{�{y���0��2w�n�y?�����N�P����rM@K��xY���������*}d\7��V�lb�7*>K
��� ���e
TW���2$�"�^�x���gi�6[�mm�Ri�O�*6]yq5�#���]����a�T���_� h�huZ�����������=�FT-������.a%o���<���>2�������pZ�z5nF����i
a�/�5��b�s,K�py�T�����$	��t�~��o)�6al}��A������x:/����S��}������9�=��{�	��3r�|������{���������8�����7��3��L���pn�y�~��_?�����Z�4�@�<������0�ol[�W/�
7�~�o�_�l~��7���W{�c�nF�^��9_]O�����,����|�y��|_�U��=��v�������i���`C����|���^�����f��!��O�����b����z\;\a8��&UNJ�4�|��kh2QvD�,&
j�
P����F�j��XE���k`�L.;����� �����������:�5m,@^��9����K�i/�v�3�P���������X��o/�/c����f�Qt��P�'�9K����O�6���4��E����7������ �C�*K������a)!	K0����de��R��lN��f����j���5�|%���gOJH���s������L�vL�����,�O5)� ,�_���t�zx�cQbly���O�h#2F�oL�0���7p|O,v!���(Oniz����A����a�@��S%�/�M�~���%�G|3`����?&H�<[���cl�H�4Z���p�j�WU��7�l�3+@��W{5��Y�en���-*]�	��(�����f��z3�>�3��D���i���
m�nT0Ka��LdQt��osV,O��o�w
���e��,��+��VzKt�N����N��%.o�Wo��7����E9���Ua�������Uc-Y���e%������~�us�w��ED�Y�����-7�FC[~E���z��_JDU[��$]7��u�_��f1?'U��*�8�W=�y�����x�(�{q$^(��c��|��7���!��/�;��J���=�n5�TZ��9��4����ea���L�������kA��Ck������S����1I{B�	ii���Ks��������|���s�z�4�������K,����������o���=&�^���O�?�\��3X}�sX��f���������-�{���~�����7��G2��E:wN�T�^���k-��yl�s�������[�~pB����,Z�1��a&�r���;���<�Z�J�8��\_�e��x�~�F)Y�S��?�&J�DAe����Li��~q�f�I�����^�!�\�u
f�
f��6��6a��3����'�vz;�}n�&'�f���=m��+����VZ�O{u'�Y��n��d��3�O�,��!V�F�q�T����wM[{/�q������dA~
���i�f�?������[����rh�5D����/��k.����s�oKI�����d�������'�����h��/��o�6lj�'�o�8D�����s(�L�K��F�����{A�x_��b������n�{��$<��:��I��gV�e���������c�����5��b2�_��q6z��_��	I��E��lU������a�'�������Wb�L�}k�����o��v��������$[Kv�x�50��C��N����-��"[B������S��M]<��A�!��l^����S�5Rzt��q���[�����Wd���S����spR�jM6<\L��:�(��tO�S��EP��[}o�����������S:A�}���'�����l����HA�E��'��"X�[�Oe��d�;�/���I�C�_�n�	��k�!�������}�z ;�7g'�,?�<�I�	�N���K��q�����3M��
cN3j�O_�����&2�:!�b�����&����-Y��%8Op��iGN���T���km`���������������4c�=��� �"�+�y0-Pi�y��������|��W�g���.
QEe]�	0A��7����=����m������=�f� ��������yEP���0�s�v�%�`v��!��#"v�*��!����Q�x�`�G�\�����k���/��A',�������G3(S�����������{����Ta�	���.��C��R=A���f�-��X^eWy�@x���B����v�f-h���}�����������7�Q�\����N~���i��\\/�"�����1]2�������Z�
-i�I�������13q^]C�s������(���EZ�Y�<�b�E�Yh����������m|-�s����%#�s��������~C0��`e�����?�k��T\h�RT+�D������W;�������7D�<kykG�!u'��S��;��W�I���kW>��4��.������A�m�9�d:`%����-���6-���u7�"J-U�x��u������~���"��^/��K� ����W��^��Mh�&�}��
�{q9�<�Fg����e~zuy�	��,(�j�D�J}�v��������e�I��/Kj��`���|��_w?���-&��t��c��.�����{�c�z�gCzFU���69h&)�����W��g�|�H3�������1������LY"�<J�����\s8�- #���[c��yZ6�*s����c�������`|u:HFJ��,���`	7_t����Y��n�D�9�vI2��;�UD��'=$`x�?��k<�,�G�D��T����?����"���r'.�>�y������4�=�n�7������������H6����&5%f���/G�d����o,V��~�� oq
+��uP �Q`�/��

�w���hG�l4ItIU��kkB��ANR�����=��o������v�����%���?���~@�t�&�|�<����~;-S�t�XN��5$������|�gT��	�n-�~��]L�'��m��}2����K��RtRF>���mw�G����I~��O������F))���iQ���m,<���2o�CPv6�f&��F(�g�� � oQ`���p",����:V&_�^k�/\,�Q�"�AS*4�����_.��i>#6j������h��!�J|�#�o2@�-�VMz]�w����{�������-`{<�$�������U���r���0t��UQ�r7_-����y��{h ��Y��( p��#3�cO�����������UZ@��5���c�k�sI���A<�����y:�������/�]����>aW8�uV�������G�^��0j,��T�uk�m� Y�2�y�Xf�����4E	��������s�,xVR�AD���r�p�"�����1{��E�f'�)9<���j��#�Ot�����8����iw���	$EJ����������Ol$��t��?	��X�7���/)�L�I
=��*G]��W��I�M3�%���r{��h_������o���R���c=����T�����?��^/�??��sq�o>{�R��������[>u�i������������?�F��)������'�w�������-g��
�y��?�.���]��e~����"�v08�fC�Ee$:,z��}�8��V��T�;�j�5�hy<>{).����:�U���_������U���xC���D��(�B	�%���Sp��������k�Lg�a�e��jU@g_\Qx������3tvQ��_�=
�}@oWV�96:����D����9�,z5���{���0���������?��w���~�Vi�o���>x��|P�8�a"p"�:�c��^%�{,���nG�)�r�qL���<�K���<�\"[F���E�S��Z���^x��UbM��n��9��:�)��Q54��9���
A9��
4��_fO��������
@������7o\y��4�� ��E64��2�����}��L�g�l!��7�!�#<�}�U!�����3Ze>��-�xEs�#/I4��mj��h>���\����du�]� �O'��(w�q�tjU���(#Q���cSI�P,E�T�i+=N�Qo�7D�y.@�%��$�p��q@�������}-T�5H���zH��8TX��+�y�9�n��x���L��S�w{U����ez�����������#J{��ab��)&����guF&`Q��u�P���e*���r��\k��R
C,)r�}R��Pq���6��1$&����7����"\8��l6'�m�n�QH��w	@.�Q�f��!���K)��k.����nT���`����U�Q���s�^(7�'��0�i����*<�0�|����CQ>{�fO�����Z�|&Jeo�_�����s���!�G4�b���d��x��
�B-�V�X��v�+d�rS{T%��w���o��p2����������0L!���#�l���\m�B
%0$��G����vX�i�CTJ�7e��c1L�-�CC�?���T�)����U�B�6\k�+���v������p��VX5��D�D����	x���6��MmjS����6��MmjS����6��Mm�+�����[�
#337Antonin Houska
ah@cybertec.at
In reply to: Antonin Houska (#335)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Antonin Houska <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

* By throwing at the patchset `make installcheck` I'm getting from time to time
and error on the restart:

TRAP: FailedAssertion("BufferIsValid(buffers[nbuffers].buffer)",
File: "undorecordset.c", Line: 1098, PID: 6055)

From what I see XLogReadBufferForRedoExtended finds an invalid buffer and
returns BLK_NOTFOUND. The commentary says:

If the block was not found, then it must be discarded later in
the WAL.

and continues with skip = false, but fails to get a page from an invalid
buffer few lines later. It seems that the skip flag is supposed to be used
this situation, should it also guard the BufferGetPage part?

I could see this sometime too, but can't reproduce it now. It's also not clear
to me how XLogReadBufferForRedoExtended() can return BLK_NOTFOUND, as the
whole undo log segment is created at once, even if only part of it is needed -
see allocate_empty_undo_segment().

I could eventually reproduce the problem. The root cause was that WAL records
were created even for temporary / unlogged undo, and thus only empty pages
could be found during replay. I've fixed that and also setup regular test for
the BLK_NOTFOUND value. That required a few more fixes to UndoReplay().

Attached is a new version.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210914.tgzapplication/gzipDownload
�Aa@a�}�{�F���*�=�7eR��v�Z�mmt�('������1IpP�&���_�@�$);�f�H}VWWWU��^��a��4�}�/���F���t|����
���O�Om��htz�vK4�������>�I�[���P��/kG�#��/�o�X\���m����}z�|�V���\��o���������Z�BZ��z������h6:mX�h���h4a8���u�����������{�s��=k5�fsfO�Y�;��z��>fF�1;3g�m����cg%��h4^�����f^��2���R|���'S|k���z�:~�Xu3|�b��Kq�vj���b�0/[�����64����_+|)~��rpd�~����R��PhY9��"F���O�GL�3(S������y���o&l34��m7taJ��jn�on������A
@�b|��Z���z�,CY�r���pmx���t�Y��}Mx���;WE��Y|��x�5� |�,�=w��5���	���{�����I��/�t��Dh~rD�-��
�&��Q�pi��}�'�\/`%��D>�������U�������9���|
C[yA�N����p&��LX�|� ��W��)
�2-��|�e��z���U�g����&L���%>`&8����[�������k� ����R,�{Gx��T�+��l�}g�]����(U3����^�x]_xK<��9w�N�`J6Mt�y>����0q�^����X���R���*xyt���P����m^�������s��G3@�����o�>~�>NF���}����>�?����/���w?�9�����O~��\�u���vca���-��������>q���p�[�_w�6d����MQ��%�����5[�M���z�J����Qo�a��Z�����-��V��uD�Z8���:���'��9Y���_E��w��������]����%>�����X	q�Y�G�:8�����L>�����D���v���L��*���g�v�v������A�./eN�;3��?y�3`r��M5q1�z,��c��������j%7U����;�j�l-���<�n��w����.���8�#3�vQwq��Km��5���!Z�p
��>~�)��5�k����o.sq����c���F��o���������m`#�+�Cw�C��!�o����?
B��������P� )�k��k-� �d��wVs��c�������������<�U���0�����~rbG\�(���av��D������9|On��P�f������d��ak��E��VCn�ha�.g��NT��V���`X�V�8y'A�T�sn�+�kNwFW",W�o�������V��VRSn�lM�h��o�t-������i�2q���/�T��n�����.�h�zAA��b�z4+�Q��JB[{�Yh��x�u��hv����J�[���3w����=�W/`���&I{+N(�����.�����.��G������>`��Y4[��aF�S��V{f�=�1���y-i}����?�I�A
�?��tN���X���k)�	g�
m���wg����d���	M�� h�]^�O�G7��������w���&�4��Q����
7^��7�w���]|<�o����	Q�%�����k�9����H�O+��:8|����K���'��~5�J�����xz�8���9e'!����|*5��J(|2�}���v���t��55�����$Z�B��"nuk�����>��_`r�2����x3��^^@�d������0�8�r����������m�T��/�U���;��v�J�W�����NF�c����	�j�G}
��[p�e���5?�����
�M�py*H��:N���[�i��6[�i����[aWQ���VT�([
����?�����3^�Ww��V��a��p�����|w
51k��u��+(^��j"T�,��k�����fo�}���������/S��Fs��Y#���.����2<����~�
/�"$����zxv6:����+}�\�w�R*����;f2��j����~Hk��c������F\�_�w���"o�W����]�w.B'tnQ��<����\��j9��S3�����ORG�5�i���,������z,�uL���9Rlj�.���&��D�b�]�/+��7
Z���������/<�����&&*a���g
�~o�s'�f��/X�����/�	@������M�%������������F�w���zQ:5VN�����*��5{S�`�����r=���h=�nk�H}:H}v�#�1��W�NL�g7�k��T5Z�z����Npk�\�o`4������7R6n	_U^._�jt�30��1�M�����N����:��gL'Ze����zT�.�������l.�%��������\�����cm�x��~5�E��}D���_x�4��?sw
�!5z(��CE
����r>a#���PS��
S����)�`���u�N�k=a�vwX���`�b&��5���x��l�����
k��L����w�F�����	2������������Tj�_��=����*&����_�|<M�����j�S��*�%A/[����q�@?"���}�,|M����
sk8HZ��=u��K��i�^�a|��b�7��&2h�~����F�������e��p��� ��9�	$|��xSq>�+t�IT�����������)p�#@��JT�&*�0�/��=FM<S�|&9BA��i_��Td@4H���]8��i�z�G�����!U�G�L�j�V[�Q��f�`���v�q;���5:��b�v['S�k�L���;�����(F���'�������k"1��[����#�'��Cz{yy�C�f�Y�������e������$���K����������RD9w������Z����fV���
�*���@F�����;P'��i��it�|fnD��8	B�_�3������G�>����|�|�'��z$R��F)80��=������jNR}-�L����w��.�&k�2�_��<��&��UVVi�H���lO�.q/�v�	�Z���5;�
t�O��%��J{��{��"����^���Qro�dY\x
FM��0}�|D���?��HK�[`]��c�����F�Q���y���/46���r�d�ujJ���zQ)YS�&z��eht�2����Z3��JKs���(���
h��G��Uk�F�%��u����Q��G���YyB'G�]������������mG-V\�y 24I��Quv�PjfGD������qr�'M$�����m�(��-9��IQm_��h��������J���I�w�G����i
�<"�`�f�b�@�-^_�����k���uJB���6m���Tg8��mtv�Q�[]k��� �&��Zb��Rt��@���w���i����#y�7����=���*��_U��J
��;��I��	��	�y��[lR ��	� ���Y�����3��>@�=���oQ��F*��=�_Qu5v�A�7�jh�+u2ba.���'u!�C|�v=7�S^Y#5�g���S��SU�b`}-�D}�����\y;�OW�1��nSJ�lk`[F���z�������vI���5��mJg��J�?��@������\MHgjW~<�n���eLL�&��?��l�����H�E��n1i1+�2r��{�;B��V�����������<4�[,��H��s���a�f<2��
_��t�/>�l�7/e�!_M]s�%QD]�,�mwM��5z���4�����l���
���G��^;�wJ�),���#U�5������1g5�,
�����OK�&����o�E�y�^eh�������E���@0{�����
><�	o6������8�u:]������^J���Hs��{���lu�a�R�������I���S���c<��t�o
�����l�q>"����J�����VF���� c�L>���������2���R�q}D�I%�������8�C����o[�s�����$VOM�n���`V��Z�Y���No�N�V������m�T������ S�����
1*�������J�������������^
��\_^�$M�H*��I#�������g�^H��0�J����0FmOu/��Jjm��.��
��F9h�ZI�8x�=����D!e�������7������f��l�z�Ij� i�-2���xHUO��������`2��Y�$Z�%�_H��KL��k�c2�������F~�����r6w��7�/�Up��q�����������#�Q,c2C�G�O�}h�!����
7C(C�=���)"^��l�+�v�Z��������������9`�����X
����Ah�O��l>�%�g
f��0�fjzQR��~N+4��0?�4s�2��fJq9���-1�=��c
�&"�q�����R�Z���uf��&��5z���i67��Lk�t$S1�c �_����d3An�t�e ^�04��|��j�4l�|
�(Sk���r|�Vh�a�(Lnk"�d8���%�:�:*��cz1>���ZAe>G�S�5�_������r�H�=x����O���A.��n��������fJ��}\b�p�9f�W��Q���O[e)6����<X2�97�O�v��	�(?4���"�f���%h,�Wc� S�H�
���w?�o
a{{x���%Z�|�Np��x<��O./���]�8����dJ�F�2pK�T���w�����>g;}�Z!��$,,��S^!�3���G�+!��i�=��������3�����^/&������T���d&��g���+?z�����@v�zV�^����2z��j�tc�|Y�$�&)���v����]�"Y
N�5Z
�`��&bu4�� ��)�&)�:p���M�eB�#6�Dh�K�Vr �o��_�v)4
�&���3��pm2]� ����~�SM�}�Xr��M=b/�`���	]��?\������U�����ybeRv���|67�\���;����Aj�7@��<�"��m�Z�A����c�vc��,5'�*F�D1���F��/�DJ������������\���!�I�JI�������Xd?2��~p��h$S�z����wq]�b��P�����,�k�Yk���!������i�_��L@���R����U�_J_��w�x��+�m
�)��CeKa{�s�<�����Z�^+3�b���p�x�;���L~c.���������W��1�����p��^������72!d�wxj_;!JA��	�����/���0�X�'?�"�R���FM����\y���i�uH������S�[Y���e3�S*����DQ�2'YC��y���a8�{����	8�gu�K����7(
:��g���<t��5�������^����3[X�`�b����>
��r��r67o��K��S/�a�����PQ��\�9�����E����l�������hr|6^�N���}�.���3�5]L�]^��������h��	E�{MS~�?Gi��h<���*�Y�x�k<m��~�;���]����.o�L.��*��P�|/�.O��%� ��������������p���E���,��kA�/���� G�e�����������f :�,�U��
I_�.Q~Z�8����"���$�I�����MBy����1��A�b�m���R<L���0�&����3E�����x�5����'��&)�C"R&g�[=�Ki	,`uC��X/�~0���N���o����:g<���dQD��*y��0/'@��/)����c�_���P}S�.`Z����(G�=��g�C*��l�$H��+^�D9�����u�S�^t��Y��4���!�^92$��i��H�|<B
f.dQ@�d
Yw�Q!M�@:/L��(:��2�j�#5P�aF\h=ZYe�7�S�i�!�j/�
[ ��pC���$���d��p�	���:�gI�80<�|/��J�����J����XA�i�����s�8UH��6�Y���5Z�z���: ;��(�X��I$5k�=���r-��������JO5�hb� S��l�E8��:�WQ �m;e-�oa*:����c��#�:����.{�Q)*�)}������c�9c&o��2N�|�R�;m�������A�;��w@c��x,K)k�r
?��`�&���z{�CL�^<Hb��,��Q��#�A��i�L���XR���D'��N3����f'����	r�Fv��6����K��b�L�
y�M\�
��0����V�o5,�^��:��?���d�1M�$����*��2,VhMU!������/wkbKU0�##,�k�U�u�x����m��w�O
,���P�F��K��Rz��p���Hu���N6���@&������r����e�0g�B����}��w�V���7������5[N�	�j���a����q:�i��l6��^!��)��8
��z�H	�lw�������>�B(���2l����������S���}�y��6=K����	hx�N_�p�R�:�'�Z����h&�l4|��o�lF�$������������Z���u/��h��7��/�w^����ds����a�����rt�[������?
{��R��������I�~�3�i�-���������������	gJr46+T����.;m����	��Y:~�
���9
�SN;��Q]�_�3��7�Z*i��k����}2�`9��
�Sta���I����*>�u�j#0f�|�74�o��_����c?��j�j��	dAn�	N:����V�p����v�z-�o{>�	~A%��]�	��t�b��s� Uz��s[����@�@���8Z6p�����I���*]��'r����v��� ��c��&�������}Vu�{I�|Or��nSJ���r����l��e����|K��L��&���B\���q8��]*�M���,��rt�g���^7�pm�:�����K�Ux���1o7z2?i�����
�s��c�������4w�u3�N�QN�N�S����.���c��)�x����#��X�Hy��dau��3.���#�Rr��h7��+��6R#4���T�6�a\b�D���#��.�D.��������h�2�%�������������f:}!�2��.��v]B���B��}��H�5��;�0��
k���X�$�@:v�nHIe! �o��B��I����{�eQd��H�Ek�K�)�T�)��~{>���>������}������y�PU��\G�i��;��v��M"�r�U�!&b8���R}mz������`6�&�|#�7�T�e�I����uB���6W+X�0��W�F���_��~�vQ�^F�_���"H�2FC��p(W�����A�l�{
�V���jm����V�EB��!=/�%J�������%����o7�;�`����7��T�O�{{d�'_�����n�BZ
�F�*�e�����t�NP	))�K�>jr����_�
[��Ng�?/{�I�*�@��I��
{�����f[�6�n@�����>�K���3������s����/rli�������o~$n����n�7OX{�}�7�K�-�1�m_M�#c��Q9���������4�ARv�d���@���)�{v9,Tw�u�I��P\UE����`��m�������d7����t���9GF'n"2���~�ezy�hr�T�]��.����u���O
�����k��2�F��ry�/�3�t��u=�a��������0>��Zz��P��#����.��c�����h��5=����e����C�N6,S�
?1����e�m�_s�� gL�j&����$�B���k^�	--������������k��J`<�y�)�~-4�)��dX��bYJ�����e��_�{��?�NC_4dhq�t���]�/p�x	�k�e�m�C&��*����K�I�+���H,~�\�'�V^�'�4Sm���u��|��9-��:�Q�=�
��]O?���w�'��p�w�������xx>�\��x���l��u���"QHC��p���X�Zw9|M����f��R�%�����{�Ya�V��S�]���^HC���K��F���1�RQ���H���7��H��T5X��~8wo��M�m���nR�m���
]�kMtqpL�"ns�������y�(���E7��V+���A�W%�E�)E�����dx}��,JbB�����:��L_Ti��=���v�x�����"]s������U�GA���|B���A
Rd��7z�F��s��-#��@�A��������@��H�+�jba�O����^+O����?�/y�8�/��'��<���IPX�B5q���s���U�cQ�)1l9��Cp�ri��c���Pb_	,�|�����.%ri��)T�0�"V����[�tl������z���`e�aH��`k(��QX��G.�t�(j�p�.4��[���|������d\x5"����G�7S��'�g�(UIS��D��H�-�['��Z�/���%	��n�i�IU4)�6�tI�r)Lj��*�b�E^��m4O�/r6J�jt
�w����	�)���uJI+Q�F�Ma�n�Z�\]��~���v�/�)�����P��H�}H��)�0KV��d����O��������L�����A��[����X�T����UI�y`�zm��f�>4�A���m6��mq+�qT��p`��H��Q��Y��=���������:�p|�����~���w���:^i��������%���wz�"6���(��RY�W�����g$�+��V�G<J�����c�*�'I,M@z���T�S����W�|
OF��w����
��YF���(��)Xk�ex�8T��+mn*����������}�	��z��C9��H�C���r�>�UV���Q:I!$�4:��k�)�|�jF�9}�%�A��H�b�����J�����B4doo�[/q���QT�X^��o�Q
Bl�E��;�����h���FpCC�GM������1���$ZSc�AR����!�/1����w�Q	x��k�;�����)u^����}4��������s	r��E�=d���b1�j\�
�^��N�T�4�����a
R���+ImZB���,2��EL6��N7��rZ�rjGr�h[j�;���������J�7�):r
������n������V�I��a#E��.C�����1-e�.3����{	T~���2�[�l����"�k��F�-�8�����
�b9�qS
�����q"��V;	�%�H <��~�RH�~�������G�B�8�����j��o�A���y������"���'Z�����tF`L&k�����B��#s��]_N@��0A�&�����w��Y�\q��Y�k�6E%0a�^���0�m�K�9����T�StD����	��m���8���
��x3�;�r_��%��v��e�d�F��QZu�2�!n��U����#���x�s�;iK���q�y��P��pf�����jA����<���U����c+/
���"�2Uj�Sg����N6Rp��?�����C�r�,Sf<!�@��{g��@�[�#;�����Yx�2z�l
�s���1x�S�Q8�,�
���4�(�b�37�B�����	g��2 IJ������2���m�f��Et���<�x��o��"G�W*�{�Tyk����l��k=S<24kP��Nc�u`���<KFz��E�G�����E|2�=Sn�T�P��;(��:u�!�h���PdQS�p�Wk2��� ���q&
������N�_�/�'��u�
��i�G��������n%2���L,��>2���x3��=cV���������Hd�v��F�����-u2���)����!�_���_Z�KK)%�n�	��$���+9tb���!H�����E�	Z�1:=8
I%��h�!�i�U�������u�����do�� �������dl5���bZ�����j���\J�y�����.`t�����y�S�Pqj�	z=��`I��-�*2����X�a"�S
�*0�S��&1��\���':������U�Z��K��CAyW�9gE�	�2����@dr��)	�����~�V.q��l�k���R��S^�>���`	9+d�$=�D:��;�"���#�p.s����H����QH�����_�����Ee���-
�&>�")s��<�PA�����7��$bo
�Z�M�2��4R
�t��+�=��q�L�Wmq=�qh�����k���o�?^oF2�+)m�w�-�,9��>�B��'�`!#�'�������>��,)U��H�IJ���
����<��Yr���5�vpD������;����=u���C��;���t����r���b��N�|�xH#)8�p}r��'�g�3�;��k�T����2�G6��3�]VT%'�������z�c�zF��l��*m2����9(7�T�,�7����k�3�1z�|�+��_�I�R/�7���[�)d;l"����>�R��_���0}i��
��)���2�0j;`z�F�=`�(��ijr�.�����~"�2��9Dt�u�?�F��a��VG�2s�_���0���4Yk�t(�cb�&z�Gn����D��O�Y��R��l�r,�c���'��8s�oL������K��m�T
�o�j5ls:p��A�n����S�a���;���X�:��>}J�E�8^��c.O�>���P�7��2C���N��DM3^Qt�c���������y���������Z!/�Z7�����%u�����g��}����#+���[����;K�u�G�%��vd�=������_;���7Q��<�#��nlO����qcrK�o�v7]�q���@n��}��KK����M�:�##~�3�����i����30��z�5���6�1�����(*��2_��� �����;qK���C����u��@�ZCB�q��>�_<}I�$-����^�W!��c�����n��#�b����5������>)G#��N��z��n_;y+��I�������7H�I�K���B���������BC�^�+��;�\�@�,�����z��o�G����>����G���$�o�������w7��(������������b��6��������e��
� *e��|�����C��F&�'*tA� G���KP�� O��6�rP|yo����[zN��^7pH,�Dg�qb�W@�rx;]/V��=���	����7�j�R�_K^��f�Y��~u��R��E�9{������I
�����||��{�W2_L����i�5!$M�)&�����G�92t��s �D�6��-y�\��=��kgFN��n�R{$�
�����<��B����w�C�}9����$A�7������H���)c������
6i��h.p�i��������Q6���
StB�6LQ��c���);W��3w�������V��zD�!�Ky.��i]��%M0����[�"�*7@�	���c��
�x����s`����g����|�rd�w��k�y�*�y���~ VJ`�����
��i�["��O���������������ht2:Q��7m��?�$]=�����.�&,L��3�;bI���i��<u�.���b���08M�g +��S�	\p��r^q�N���u���_z�%�N��$�Uv ���|��
z	���4�g0%���[KF�8*�z����m��sS�����-��������H�#�.��.	�-1��^��}>q�F����S~��Zvu
�� $�|&v��_�~�~���}w������0bB�`
	��w�3��S�2��y����b���F���g$�^��T�$����M��K�*|J������<Jm|ZE��x��u����Nvt;��0�b��h��^�F��m���zx<�\]�o�_���q&���������}}��'3F/c��L�
�{j*TN�8�/f�R���=�xrBH��E;�������\/&7`B.�N"�L\��_�6[���v�W�l�S.x���?��g���5�DE�`����c��%�ox�v��U�Q��]��u��K���/�� wu^�����C�����eh'rQF���	X���� ��E�F�;s�/�Pv��?�������_%ei
�1j���jq��V���J����+�����l�?�UW�/�����SS�!�tT�Te�)<Iy}����R7�_0�`w	 ��O�K������4o��JEr�VY��	�<�	E�<� rd��q�n�[3JPEs�L���id-8�m����Tq��������1/�}aS���!�6�<���CL6/^D
'O	W��n���';q-x���P���<�`&[�CW$2�����/o������,z8N�:�R��.��v�FJ'���r��?�A�v�="�D�|P���������0����6r ��%e�I�?9���#�G	a�E��'R�/Q:���������f/NQ�S�u:#<�X���2�����I��}�MK\N��4��V��w�V�����4f���Z��6��heI��)��I1i��t�E/�`�����>
oFyl�{���uvF��.�|'X�����HR��8eb��y��#�6,�>
|����� ��^mk&�J2`]����k���P]���R*��
L���8��w%HJ4�J�iH�k��t���Ko�(._4*;�~j�F���#4&���8L��J��Y�P�B4�Z��~Pj:�H��w���������,����6������mX�v��Hy���9�<�Z�Q-���W���k�q�:�����9q����Z���4PAe���1����5[8��01i5-=-k�S�0k��K���y&Yp�pvy1]__^����v�i6�r;)?�����������GE���1���8X����JJ��*���t��z�o��m�l%�*�Z�
��d���z�>I��&i �$�KDyJ���er�_�����t];����E-�l���H�I���}����Sn�
1*�&���'���w���fR�����|UV�,T��(,w�>@�d;�Kj����8���]�s�{�i���2{�c��M������\��v��+.#�PJ�����e�� �x���h;sT�H�H�K�Qa�Yv��E'��������:-!i]�R(E�~�NP�|�8]����A�
�����=������7
��bO��G��-��JF]��&,�����c���a�H�^�����3pK�����]�3��2�;����T�T�N�#Pi�R��{8�=L��E�!�%8���p�!�pb���#$��A�M�VB13������^7m������rK�l��Ry~��*�[������R� vb�[����{{+y��e���)d��pM���"B�����G�r\u��/��a�`y��Z���w�/�F����t9������F�H�A��f�n��-X=!E4�V�F�G����W!&7h����^^�j�U��Q�d�aO�v�P��a�{�a7��=8lsJ�0�@0C����7�����,w��;�B�<�!�,�C��]j����%x�)���$0L���&���]�n'��{�he���L�W�^�����gw�z�j�Z���6��=W����
����E!%��hS$����YDJgU�_����;9e����HqWM�U�7������K��{P�le���;l�����I�o_�t�~ O�l24��$�[�W��1F�����0���IE�a'��"}0��z����q4���JpZ}8V�^��
�E�4�X��ROsK����zw-��	�>q��P����[nI�����+�$xE�9�l�,-���t#�S�J��V�?�j'x6�;K��l�����d^��d�����f�/F�f�*\vN������I��la��B��W����qW>,��B������,~A���Z\����

DA�R�:�*1N&+���V�Tx��	~���9���H$���������\�-��@����;�����o�����K���mY-�{'�Biz
����2����k�Q�T���w��+)!��a�-8T�Y����|5{�~��ZYK�[V��	�������WY^<�n����g�����7�u$��fK����L�f�*����=9�c�� #�s�����*$2a,rs3!5���[	k����wxC�f%���y�4�n0�V^*�ye�^���`5AY���!� l����~e�,nL��LV2��J���%�����UwX�7��@�e��Z�F�g�j�"�G���N��d�k�H�G����W�C~���)Kg��Y����F[����J��fA�%*�Dr�<,��O�T�t�a%�>�:�k����Dq��h�X�A�Ub������Q%T��n6v��V�3��^�����Z*=C�Rd�F��[u����iwL�U��n:��c.��:vnQ}����`�~���S{8&���*�jF�H`��,��:��:�\������n'@���E����j�	�M�����n=��P�v��sjkaB���nad�4m����6��a�F�a�i$��u�c4�x�$���<{���v������������Pi�d��r��k��[����l��1kn��UC��[�k��"�w��f�P�(���i������zFP���e#z��]�MQ(2:��c�>��P?6��I�����O��m�='A�moH��Q��y�����h���o�8+��/�Y(����m#{������N�
e��yC����RDd!�'�X���O�r:eg��������V�6I�
D��[��	�Y�9{��������2gk@I��F����.6����E%i��H��mr�����h��jf�8w�������B'P�@�������������t�3�3���Xy�I�02�����b�st����L�1Z	��9���J^��C�DAV��zl1�Y!*�W�AQ=?�� ��f4\<�Vh^�:2���R����������\�S��64o37{������nI��\����R|�{U����F<����������u��u��-�/���f<�{����7��:������W�:�c��k�F�<��S����WN��V�2�:���o	/��$���MeEb�d��hs�W�>]�z��O��%���s�
)#Q���3�%y|�:�D��U�u�f��\�Z8H�G�d��m�����^�F%�������y�t�c���'���8<�IO&=N�_������%s�G�Qq��*��7���DP�"�'������A/���n��im�n�Im�h����.Y�p6:�K��"?��$��N�o��Z�~�YN��XS�3������9���+l���"�A}B�~��P�o����UD�_Ks���*#v�#��d(�k���,$1C7���I6&O���ob��������I:q�y��#��%�8�n$�|�z���&�|��i�mX�L�"���B��(�PQ��Wic��/i���������A���i�	��:��#��e���7y"$��"S����0���U���^����rA�,V�3Y� ���n�,��*��H��4����m+oB�P��n4�s��*��?pr���CjN�m|����5�}���|�q��I���H69�������A�������Mg�i�����n��t�n����
6��A|������l�_�=v�'�b��P"�q+�d:H�d����p�!
9����A���l�J\�&#�yh���s�� T���Q��MVG���r�.�0�a2O�O�z��F�na����@S�Mq���,��8N��N�a���>=��iuU���n������Md�?3M�2��z}��9�l��r�����~�-vP�VRL���f���������$����H4���{�H�0��!��b����EZ��r m��r]6���l�Hya�P�	���=&�Z����Lx��e<�����;����[=a6�F���f��m�~r(�(�I ��H&X	�}�(xP�"������T����)ei�iQ�r{�y]�	�)����o�[F���������K�;l6�Fc`���F�ph���z���(�����SR#��Z_� �l�G�6��O��1�O��n�;
a��f���5��h4a8���q���5^3�P�
����'#���"��a�k���6l�e5����4���f�������n����'��"���0z��xI�	X6c�y)���[�K��[�L��y�'�����f�fH��R��a?��
!������z��j���u�����jxs����h�,-�DKr.��R�Z���o���
����49������D"�&5ZZ���� �?���Xz/� �*���c�?>8��]H�H��=}�^[�tC$���7�f��iP�P�.����L�0���[1���������Og7���Z�:p]�=�F��)-�H��Bkrp���
7]����q��������8�*�?
����hy�N@E�
Q��L�5�D��}X���	�����y{>99�OF'd�#�)�lU��8����x��k&�Q8@b'^
�<\�2���w����z�{��h�l��
!��NU(��~F�������{`�@D����b����]8`��L���*l��@�.,��SbD����^Z��,R��@�0 n*�u�Hb��U^A�
\Is (G=8<�/��+A����w^����n�������tf�n3����Nz%�9J��T!�r�_��KAb�5������,���W0�����z|:�]`������btq�Hk���7G����f������2�����(MpuC�t��r�=%�0��v�����S�w>�hP��B��!z�8���897��w��^M��Z�d���SR���`������%��)9?�<�z��Z��D��9�G�Aip���I�y����!7�|F7\�
��������d��;Z�q��VSd�*�x�5)�����0%����N��(4-���������M��e��n��V}6s-�����d�6t�����r�
Nr�q@v���Q�jqd�����-�C{h#�{��q)���:�����	5�#�.I����hb���3�bb
��)�#3���s�l����1�E65:o\w��yZ8I%!R��q�o2���{b+4Ef #]#�NX�D��>��1pf�9�a�A��H�zX%���x@�@`��SHZ��@�O�.)�����C[���	�:3�9b�6�CX��
�B]���_@fHiP�!��(4�y���'���$���9�����Im�wfB�c�1�����~�U���{��%l��y��w�K IaH �d�`�&7�BZI=z2_��Z����B��l�
C��4�z�
I(
�?8ojsze�0
������$L����H�p�����n�w���Fqj*%���DYd���\�_�K6a���k|I@
B���`lI�Q�������$#
�����f���f�q�����%�����7���fZ/��I&1R��1Jjxe�/j�0K����~��Q��_��l��������L!)F,'��!4���c�L"Z����mj��fOql7��+����a����@>�J����]x<�b!)'�!vCk~*uIN����h)�sX�������&e���A�$�>�,���$Ij[J������N����bs�Yd���l��� �}�F���Bqf�{2; ������F��*r_�����+*�H���� e�Hv��t�,�NX��C�d86f�|{v���?Yz�����h49�V�]3z���q�P�Do�������b|s=�9=M�������k@�
#������9)q7��"3q)p�G�S�3a��w1����B�D�o3��W$���8��7a��M�N7��wo���mG\�����Y�d�_�J~\��e,�R��_�����1s��T����E�����m�L����I�Ib*r�r��SD%�V���bR��y�Z��
��m�����7����q|C�OG���I>�bR��k����U�K�^r�I��l���w��������G�|>��Bf)?h�U`�bd���E�P>�����3\I��S�z�l|�
�G��D�0���n���A���;�?���Y2� 2�*b
�{�K'L
�#pww�
�@rv�`Sk�&
����
�����.��'@�
L���P�A�R�
�!����Z�#@p�
!�}�kja�N-� \�]�<Q����a����v�c�[�M|B.p���	I/��p��d�����EW5$/�<���`�.�����e�c���so�6<5�6�-QM^Lm��(�YS	?�
�b�������{�p���:�U^�b���@�4���a#"b��P?�{�E�,�P6u�������0r>a�$%���|��E���T;��(������)w*D�����9�D�@��F"���T��9g����T��x$�)'x]���J�Y��ph�P[I�Wg?L����������r({mM�!��2:��,��-8�(�nH��"j@al/�<!{��8}D�Nh~r����E��<���k������|�,>�^w���?����t+O��s�Zx�o���t[r�	@�����(n���a
x�,��7�7$�?< �������rL���Bg���Kzk��.O 9&��0�tP�b=U�I�_�����)&����.�n3�FM�\npm�9������n��4�@�A�_�p�,��fx3��.�z��X�J��X���#�^�z6k��PO�&�K��N_1����"i��X�R��Z�2�6����d����]qhp��� u��@���%��*[�Rd������RB)�L����1}�9��,2#���������1��T����|�z�8�65�����0Oo[��/���1������oh,u��eE��@h&4�H2"81�n�GJ�n�q��y����84k�oHW��e{&��^�^ys{2Me`RI�7n�7$q��R�"�Q,���z���%6�4�2�]�����	�����Gq�W��\&d�#g�|f������LLD%���x�R�C)���)�
�$�Th�� ��eXy4�,8�(���������"Z��J <�KXA
�B�P�� ���Z�{�c����UR��nyb1�y��z2�
&8���;�d�k�M��*�S��&�Z�K���xr�"��d���k"
�'�#�J����(��R��_R#���s����,�|"�q���@X����83]����e<q����J@ep�0?�8r���&�{�[�*�\��z���z��;���n���;/$�WO����a����=�����H�����j����'R�K��]��3�E��	�����'�����Ci��b�yi/��q���GL�0�1�-u��S,��`�n�(x
�����Guu
\(4h�9�O��N[��#>jHN8�:��(�C�H�A�-WG5t��I�bP9x��}V�lgI��vX�����\P�JU�Bt��7f{�BJ`�� �=jB+�������$����RY���K|/��
���r�0�A����Q.��4[w	��y��j����8��/���U�J�����71h)5##�F#��V�Hi9�C�2�t���I����j��������#Q��%��#�,`!c���>o��(w,����8�D��g������v�i��foj�i�t�Q��k�����.����������*��gK�� ��gjVdp�/�1�
��e�m�&�B����_dH����>O�k����'�.���0�o����������}^R����@W�{m������wI�'gm6�f�m1-�d��b3Y���
�2#'�X��ubQ��b��]���=q��
�X��bkl��������u���}����v����96��@ic?��-z�Y	N4�E��*}�T��(_���>>0��m�(�?��g1 &G�Vo������a�n�%�8�Xd�nS�7$�l�
������������h;P����R���q���U�&���rt�dE���y42�R��%��2�P���f8�P&B���}���I�����������k���� ������q%��iA���J�n����l��maj��;J�M-������#�����iU��aQ�1]i��b|�~+��3���OhU����G�����������Tu����-��2P�
HlD>8U��Wl����������`W����N����)�����W-L�\m�"�QT�<U�
�O��R�����������5g�RFf��jR���5�i���5u�Ac����q���g"�7�FS}E���I���D{��������nP��A��B��Z���j3���c�P�3���%�R�[�����6%E�������������7�`�;��M����//U��-�f�����+=)�S�K�'�����Si�b6��'���"^UND�*�_��hXm���	9tl�����Y��n8���8S��'�s{�4W��N����~�@~�	�9!�+�������H,r�cw�c���Q�H�@�����O��^�w���J�S��yA��h@���#�L�U�J��N�F�����i���Y�R �D�aH�� $L��B����w|�&�$b1-�%�u����������x��*$�{I!��1��'-�W��GS��{=���M�$��S"�
��c���0��z?Qx��{&���4"�(�&�v��[����fO��'.��� P��O-��D���������)��,����5��V2��������?����c
l�gz-����5���8N����t�2�~��S��V�	��3)���m((XO�����@T��?��JE����(�t�O'gY���yd�4S9V�h�IB�'�|2���e�����Y;�������v�&$��~�&|�oJ��l$����'�Bb�ao��� T�C_���Q�%��C9����������h
2����+�H���b�|���7�P�@F����tkS��9�)�����\�S�t�E�M6�@s�Y@sjFK�I�y��������8o��I��d��!l�5c�,<�����r��	Y��m��I������t���8�����~��&f����%m-��w��H}H���KF#SE9�=u&#1l����Dxj���?0Z�B�!O ia�f@A#X�M}E�A���: Q�s��}C�P�r&h1C�W�v^�\8�������������&4��</d�[� 1���<���i~��2���y m��Ww����4tB�[g�0��$���:��u����G��DqtAt�F 7@X���4(wJ����N`�+��za���9sM16��p����P|0M��C�D|g�`+,�s���eu����K
�����wa�
^�� \q�����p��l�4G�����O>����������6��09���{���oF�}�sk��!����������f]�����P���?���E�J���1	w���	+�&Q���iB���T�+?�,��S4JU/|�Vw@�R����}6�0�_�?��l�c�eG��m������&��8P-B�V�p������jE�@��r&���Z���D�KW�K�������!E������w�;���o��}��b1����4;�^N��-��H�8yF���������#rz��XB�Gx>5M�E�kdpC�c�j�D*��'��n Q*���,�7[�jH���Y>2��������F6�!:A����8�)%e�*Rw����p��td;�-&�������0��$���,}U������y;
��h�s4�?��R�Pf�HN=B�z�9�*du�GG�(��Q&Xn��8��E/��;��Z�vk�%Q���E���	�Q�U
���
'����f�\
��h[�������z`!�n��1�x�VC�`�s�t���E�z��p��:V�;��p�4'{*?r�X�������E����yM
����^���O�<Lw������{vz��0}��Ejf���=���%�[�n}�v61`F�;U��O
�+��7��yK�)��|v��Kd��d��k��I�xN���jE�D���c��jF��Z.;�����8pE����-H@�K�$�Jzv�����E����������8��le��j�z&@obv����-S��STO�p�.i�
��X�����5��xz�FI5������%��6�N�50b�����]��^��?��������T#9v��j�;N����[����-�d���
i���_ueaw~e)������T��\
+B�D
�l���Q<;�x��e��k�3��L��3�v��:5���nz��[�D��?�f����1���Y��f�(%���+��_a�����K�{'�17�|[8��U��������1�W<�,��z#��]������Ew�#6�u@��L���LT�3����no4��t�v�g$��+_&������UN��N(Jv��|��U����kDv����'T�T!O�+�;5+B6V+����q���s��]J3�K
:w� O�]j1��K
�v�1O������c�,)��%"b�s�3D�C����N��O���t��[3����i�[)L� ����?h���&��;�m�
��#�o_�4����9��Uk���75�h8�����z�v.���w��#:
L>~���Z��1W�'�����E�E��b!CQ��=����K3!�����=�	���S��G��� U,>m\���F.�3���B�o��3��� �]���:f��f�+\�lQ��g�{6d�|��cXL������_�������p:VV������\7��3z[�/����|f�#���Bh�Um��_�����}�o4����-�h��������$F���6�h��k��b$����+H����r����|A5w�1�$����O��*��0��a�ajPo��������	prsoE�������h�{K7��H}\��hZ��������.]S
�,��.nN��oN//�T�_��W]�#=�8_�"�}A��S�Y�KyQ��^�,*�#�c�JA1YT�����>�d�4T��~�v�LZ��	1�eEUA	��R����#UN<���3L����b6�O::�s���|����?��G��Q���|�f"����<�J���V&���M<�
M�_z���g5A-��'4! JO�A��OZ��0z���3�L�{FE���X�wdk;�3��%�n9������W������erf�eZ�����s;>��&��jr�q�*�-��23�32��c��L[����S<D��f9�4�G@�\C9���a����3!
0�����������z����-�G M��+'<�)��o[�<��|�s����o�Yw�i���i�95Z���=���9����� ���Kw��^������g9��9U�;����[��l��^S��D�������}�s�����J��M^����I�vQ1�>a�"4�{\Zl�t
0�����2�@Z�������������u%x\��p����������}[�@��UM���f����_�T�x����	UO��k��j�/���F\�����8���)� �*-~�x�?]s4��Q���Y�A������[T3j*�2q9�_at��c�{V�>0{m����:[oU����U+�X���uU�h�oZY1��w=eo'|uU�s5�C^�XY���.~����+��{��NXS�[s3
�	�F��rZ�z�!�����Ow��\�Z��b&�&�|W7�`����"��T��E�q$��z�
�v����F��\��r>W�skyK�.�U��(��EAA��9��%g��4p�!�1�D]qD�nAZr6a������t,���>D�N�@]Hq�M6*���A�"��0N��@���x.>�J�����jx�s�e��������h��9�sH����l��S���!+����z�g�9J+.��x%\�--�p��D`j�����������RQ� 9�N���-b]V��Y�����������X+��"��ef���N���3�e
�9huYr��]!
A�k�X<��tv}��eM��9LLE����Hl��c����� q���py�a�������^�s��n�����z.��������6U�V��A|@�(�D3FG[�+���h=����<��(q(g��>?�]��+�������@0P4���Oo]{��5�	|�P�o�����TR�o����'[�	%@1���*���&S�mMD��)C�0�Ee������w8`�w55Z#���U���PR�J�,j"��A�A�k4{5��hio����h�X]__���/�_�'g���7�����.D��������C:5������:��Bp���q�,-��{�`h6#iQnV�?�c�in�)�P�u�.�FH��qx|c]
�G��be��x*S]���5���.N���Y��9�HG%�O�����W����-yp�X�
�P�O��p*K9
r�Y�@�����4%n<t�?�����#�jC���J|�=D�RbZ�\�~��1�������oq|}��C���S;t��u cRl�=k=�p�
�B2��a��?�YgPk6�G3��rm^�E���Z�?H\���Y:�WY+�
���W�o8����6���T@oU�,H����`Xp�W����
*����ga�\^��?��X{r�J��$c2�R�vJ�[8yB���Z�S;f,W�����/�*w�^D2x`+��z��U8J�V�I
��M�y�#��G�����G���V��)�~�%(
�|Dp?�.s��s�������]�>��f�J�j���9���Q�N�%�n�)�R���#���^*�^�6:����<}hm���]9�c"���d�N���ZC1�����#��2<�*��FF���7�'�����������D�|��oZ�P�/F��w����)�r�a4��� ��d���5���e������3�7���1��$9r�H�49����������;!q�D6$-���^�s��d�G�'r��0�Qnh���iG����(����#��2���{t�F��B��54���(�8�M�'=	%D8(�����#��q�����E]L����+x�ih9�z�����Te#\z%e�4�lu
=�it��bN)S�p+/������H8���3�L��gD4T�haSl�O�j����E�|n��<T�/2>|���<4�D��.�����H
�[�0�A�g���rK��3q�#ZxH.�(�qz+L�&��2A����?M�/)�(�T��O�%��X&Us(��=J��~�2��lU��K�H��eBW"�Q6���8���*���,�*1g{�.��bU9������T����u��m#HG���0���x���G���N���������x2<����@������������G�kk���+U q�����mc`�����k5��]�F>���J+Y�6;e����|uU��R������-4U����uX_���4QtT���H����xD������������
5G���������:z���(x�������>�~2^��<�e�A�>-����C�T��I�b:�����/�A�R�������e<"�E%��*�*y<U�t��V�0�Z]���A�Z,����:S�bx%���M+D�R*�[��VD�%�D�F�.�W���p���W�n89�9����a����5^O����Km������I����b0����)�<�����U7�#�������`���P����Zw�x�X�����E;	���i�E�
�j�{����#n��tr�C��nrN�\�������r���xk�r.���
���Q��j����}kWY��g�i�-[2�2��e�� �t�]=5��)�l�F)��������������g�;��l+3O�y��������������Y�
� �Of�������4�����s��k�O��Y�c����l+�����z�_N'��z(�F�h�y���dXS4�M���&3*�4���@�l�*��D"�S���b[��& �hI!~��`N19Z��^���eR����I��]�V@��-�Q!�
��kfl������78���8 �v�
��z��Th�|�^�s-���<��_�Qk���b��Fq�E9��xa��s�r�yoL,#>���|�~:��Uj���x�	�vy��q��>������eW��1,�j���m���M�8�9���r)��)�
�$���T�����KrW��X��x�L��KqA���v{/,\��A{
lP'���'��G�aBzTl!�*��g�w��@+tXt���A�Qp���h9����%���H���!6�?(�wi����[.2���������	o���i#�U��Zk���5a�\�H��uM�M���u���V]��$����������f���x��5�~��4G��ON�XP�\��K���%�?�P�F���$���&�es)�/��{�+S�~a8���3�xS���h��1��f'�F*y��&;�F�W�$�0�i��3�g �,L\L��������;T��D	n��a����%��K��c�@����C��^�yJ56���l�`x_31�u�'|rA�s�T*�|`��;'��[1�b��1>�6�I77����=�����`�s["�(H���xa������������I�3����L���Qt8�$�k��,��|��:J5�onn�b?�tqJ@�����v��s'�C�f���-E��G���lr,�J:���`V0��t.z��
=y"�i*�4��&���>����<��i21��r�-D�bi��y�{el�[���}�T�����F7��r�GTB,�Lx3�k'X���%�#M��^����O���7�12�g��&��S���-�L�3���X�r�1��0��G�%N'R���5!�����Z����1�j�W�8o�����9�dq�?���y0���x�+
�W����"oq���t��yuW��3L'���q�=���Q�V��p�0�!���O�E�h.a��:U:��~������5]��z��^�Q��Y}6��aA$�; �9F
5vz�)|�������DY2�������vx�0��9e�rg�CW��64�������_ ;j�v�����{�(�7���7�^�Z�/���`��a������9��*�I��#@�\�B/�TT��3��h�h�e�*���U�@%��&i�X����|���y]G\���a8UY�;�|gx�Vx��I>�8l�>���DV�"\)����kD�!�S�Q�QyQ~����
���a.�D�C�T��x��%�a���.,���(��s�O
0]'�5���Y^FGv>Oc�s�^sC�#HP���l)��|	7F����fm�lR�W�zy>"p�rjn����^|�\5���<=�%:Ci$��F�&��%���;�L�<F�`X_��R!���)�!�Y Zw�Fa ���Fa ��9#���J#��8�Y���lOl!Y�J���>Z�w����D������V�F�����9(�����a��D-��r8�8+��EG4�;u
��9�mD1�H�@��$�p�*n��d�R�JF:
���_+.�*��K*
�m^�����R�nN�������X56���=���j+zL7A@Z�g!qFRg��T�r��Xo��0e�1�����!YV�ih���8�����W�jbpc�v��B3�30���V�\rB���eE��<�����i�����h��}�����s���j���[0"����$S����V�|����I����Z�vW��Cj�5*��W�����U����{������z���?L�8���Q_q8N�q�:C�1B���xq��Z."H�Sc`T�@���b�]=/^��s��hS
���u���6 :���`h�-����"1��B��VP4UF��{L�n��� U�AGyN�O�%Q��VI��Ab�x*��l~�����x*�������%�PkF�-o�#��W5Q�P���ym�g��g���������r���O#5��`�F�����(rbC���=�9���o���
���I#�K+�wt�S]������>+�P����1i~�Ro�����F�T��$T$��=���3�9�"a��G�������a~�)o���PnSd�H.Pi��[{�X���/�[�(T:�
���66�F�d���"d$��Y���|CH}K�E����A��C+Z*}�l13����uK����.;9'^�����C�t�������u<
��^'�6�!���-��VWW=�� �F@Z��Q���(��Z�M1"�akp�S�y��@���sh���U~�&�5H�~���1Q�8�0���n�&�����I#f�����u����bDFV��C�R�9��PT���1�=!o���^��q����K����m`��Pu�1Y�����*o�E���
�������iv�#�0�������/���i��8f��0�����e��i"��B�s����'��4�?1����LZ2#�j�a���8
)� ?��9��*G���h���K�F���9Z%�0$�k ��\��kHv��t6�����
A��P������d�o���������8;b�t�E�����(R,�q�t�[\m0#�?������6���k�?<n�n����B~-�W����������������3i�7wwF���~l�2#:�^�����1�����9�&��(AG,��J8l� ��0������np���3^�)���5�9 ��@�EE;��Il������.)������$���y>X7�Xq�	�R's��RL	et!����d�7d����&����\�_(�$�������w>IF�U����{��C�R�=�a���%�;�''�9���������V�v�E3����ae�@��3*~�x���:�
� >�U�=�?qR�"�k�\�|1��+��ml��Y������{�~�1�_�d���[�D�_�{�c�J=�v*�P��z3�Z(���i�J.�<x���4��7�N4����)��
g|�C�� �z����`�T���|����������Yd��9U?}l��}|�������=Nz�z�����������$�������>���)�oo�90;�E�="��>�_?���
�h��x�2�]�Z���rJ�O�����P������S8�v���:����jc}G(1�+>�F1������_CF�>\	��-����Ik/�_r.?�e�CN8��s������B�s�s��v��E��+�]���^�L��8��H�9��	t������nl,���g�����#��I" �]��g��O�����������L�T?b��d�>����!��MV~��Dq��"�NgJq��@_���ML��5�/�&NqiA���P4B������Y�1����:z�c�&�cn������v�J��*�X������	gAe����y���H<D�A���6w��zk����mo
[}��vJ����D�"c���g�d��A24��K.\2��+q|�\Q�)�"	��om�\r.�\�i�"���In@}�`%���X�L����y����Q�@lS�$n.o����^e��#����i���)KT�	�!^B}�_�PS�����2V�.�����Rk�,O�&(�L���Qp�'q3)��m.�Kmq�*k���	�#�@u��_�B�\��z9N�Z#\�>K�qrS�����nU$�5U1��;�_��d:��t?~__a:z%�)I���Z[���*\G��5��#4A���'T ����w�����Y���m�/JCH� �g�����	�����Q�[�e>/�C����A�i������=�*�f�������1��b��JA�[����S�w���0�Y"�[�k��U��v�R����I��n&:��t��s�������������et�.y��\�Wn�:.���Q{.��]U(�������G#�!��!@�J���g����2{\f_�w��~W���6���IPo�1����P��
G,�`%�)���uh��V���J�����}{�1g���M�i�}_����h���9���/	Ao�?��no�;�[�]�C����?�/�?/�]��,���_�0����5�_� �+\�����xc����12U&(���
��
�l�����5����o������)�7�67��_br�{�h�OMrj���G��	�x|�:�Y����H=�����=����M��������q��$��
T�3 t�u����U�������~�%��{v�}����*�
�d�����\;|o�Sn��2���9���N�82�����I
P���[`��YJ��������4����e��FON�c~M�`!��|_����F��1�A3��X���-����?���
����g}�L*.!n8 9�h
|?P	W����D��6I��&�PC
o�d�,�������1��)�<��7��K��<�,A��������H2-�A;B><�Y���� ���F+���Or��J�
��`�N'��1T���������V�R�k�km�����U�Y��������������	7�Q>!�tU�Y�X�\��X��*�i�����Ll�w ��L6��i��$
b8����?s����l��~�����_��}��<��m[\�]g����J/�<JN�Q�f��������j����,P���������xkc���K��4�?����{�a{xPJ��&y��y�.Y
�V��,��
LD��������`Su��X�mR����rJ��{����@%X2n��$&�G�����~$X��n�����J6��s�
�<�����~����]#9�Y���[����uhW�������Y�>��
���:���8e4F{��\g+�����0YZ	���
�Z56y���D��RqS��0�TF��oT�����.�����O��]��NG���������\�,(p*��b��H+�z����������l�������j����j��QQ�5������;���[���i-��n�hS����c����l����c�P����]���B���?����L�3��W�^C���v ���6��D��)����%s�oG��D��KV-�F�JF��"�0'_�_����\�HK��*��Y�/i1��h��J)ta��4$KK\"wq^��%UV�����R���S�J;�
�y>�x~t���a�,z�^^@��������8���f`�g����w?_���Y�����w}����9�W_���G���������H�x_��U��o��2R�](�]-`f�Il���

�}fd���q.r��\�@��w�,�������Z��K}@�s�0�F*04��!�����<���A��
��W[/��x���C6�������Ac����1k�������x�>V��k�V|�7����66x�^n�=P����1�����y���bLL��C��UA�s�u�H�R�u_�.�E����O�k���m�}��jC@3�l�dw�s�
��Y�\2��R6��1�����g}��|��n�z-:�ERQ��*.��/��?`��P�m�>��\��I���Yg�*���q�wL����6�YA@i<��~�ZgJ�*�u�W@����r��b{���5�	@����4A],D+u5���bq)�i���)QkKuK��\���U-��U�M�Oq��p�0e%�
P���k:���Q2]�
:E�n�����o����1��r��J}�BA�q ��3�7_K�t���h����u�w�&�Y��u2c��P���"=,��L���n~�L�d����GW<�["�r.���$
��	�hQ����\|�g�)�a���1��R�������F{�A?�#�j���!,>I��S-.������\z��A�\��}X
�`�����Z���4]��������\A����obC[hlll���N�@-7��k�����q�'��)O��'�j���'���n�u:1����St�x������W���>=��:�J@�kF�"��L����h�3��D\�O���O+��B�����b�0A�	��d~�da�Nn.�j�Y|��(�����������Wf;���D��\����`��]7����E��~
��W\��W+�����H?
^_?��2
m�<6�Z5T�>�X�m
� ����w�8$=[�2�<��Y�����V]a����pu�s�U��2��8:3=Cu������[F��X�������\R��X�vXOi�h�V����N5�<��P���"���V������pV����L�H�x��`
�-�)�Y���W�6�K�����{z@�qe�tu����S�p�� J9��m��6C���DP�q|
	���i�����EW�V��(3����L3#��#��u��n#�^[����L�eQP4����y�r��u+u��{$��>�o�nA��������)-nT�#<�*�`n�:a����z�_�=I��#�����O3������9a������d���W���2`��M�2�1�5��<$PG����0jY�j�n~R[�:!��4Q6P�N'����!����GN���W� :�9���#�����	9F�
0P��{��G!�o=�?'��HU����'���H�gK�oX\�f�KzN�t�������?Mc�[�F#0[z>����KZD��8EEt�	7�@�d3���!��2S��IwpJc�2�&r�����yb����Q����8g(�
���������G�l�t�5�iR�{�������K��V���O<�:��%���^;�l���}p��}	�$��d����7�/��}t�����n�h~������+*���������/&-e����{C�Z��o����~��?���s����3J��\��5�f�������)�*�K����E)��t,y�#�tW��{U�v�x�Oh��kb5��Kc�����<g%o�-����������?�xF�1R�R]���t����E��FtA�1��TE �j�rv8���(�r���~�����m<��4��*mk8e�'�����IVb�������9Q�SU`ZQ���@t,���dT�i����/�TP��S����Y���H������O.�"<aN��V���S{!���wK��]��9����+�%��^�����b#�����V���W5�t�s��K�{P�t�'
�����[j�CF���]�@�6@"Q��4��kkk����)�h�2�8���E�O�|�<esH�sS3u/ e����@����!w���1�}�9��
��3�?��B�K��VT7R/��<'P�����8����qr��1��������/����~T�}1�L�mP:,�f5&%�Ns�+^X�4����(�[D�S���#�[%w�T������FXd�i}���s���. `��eZ����=��/4���)���o�H>3k��Kh0�76�D��fMGtsp�T/�V�OlE��y�]����\my_m�^��Ed�+o��B����y�w�:����
���g��Gz����:����os�no*����u������q~��K�?�6�x���!c���c���p���������2��in,����68Q�#G*3%T��� ,�G�HB�����}��piQK pC��[K^7�z��Li;�zk
��+�������F'�?�eJX+>6�����/�(�"��AFT�m{�����=�-8��������8c��wI�7��_���Vhl���w1jd����������D4�fw���j��W��L��W[�:��=�\�4��o��������9,�g���Lh���=��y����3�]������g�����^�u����duu���z��o�������������y�����<�;�z��h�Z��;��hI1�1��������`�E��tu�t���+���E�L&������i��}m���[�q�2����!z�����sK��.oN����t�������"����z,��J&1WO��J`�%\���lmL�0S<7��;�Q��
F�Sh����Oz9P�lI
i�7�w���*	��s��;t���]���8yAU�6������x$�xe����s�{m���)��"6����+=F A.%#��cm,�_qM?c,J���b����K�[&�����#��/����y�B?���e��~fD]�0�:j�tNN����v�YrKK��3�M����c�q��|� ���8Q,��.r��+YH1�f�\f~�i���]�,�9�CH���[�x[�:J�TQ����i�m ���� �gP5���91z����-cg"���[_�����j��,�"���Sm�4��&�M��y��H`n9��D��0(i��+@	��%��a�_�y��61])�.5Sv��.��/����-��\j���� �T���rRd�����N"|${R6",V�*~�3�*'�	���kN,����g�G6d�����X}x!;I��
.T(�wFi�|�5$��|�G]q;�(���o%;_���9�k���K(>��6���J��I�p���C��Z������B�;f���c�?���>��>��������a�������V������L��w'a!��6����]Z��?��aT��:�\��MP|���w*�������G1�;U=���_�omm���n���z���l��K=�-��$��q�='F�e����*1���+#�W��9q���W���]���.��yw�4����lz��vL�v'q:�~�[��8����>� �	��	�����>SJ��15k�T���u1�c� g>�WXx��������%��~8�E���w����c$|zn��O)o��{h~k���7�T>�I!^��b�E������NF���A��1�@��1���-,�}�����K�[���������X�����$��,{�QW���Fr�j{ccuu�er�O��/������������o�V��.�05{�_�|K���VC�O%��5�oR���Eyg������V����fg��l���������3I�@8���P���oJ���sg�j��4Jsn�"��0U}��bWA��5�%�9��X��V����JQc[��o��w���>>��8����*/���w>�&���mE^���0�{�
SR�=��`�/����]��i���&���~�$�m���G���r�s��mEK<���&��9qT����J'�r-)8��}=���k��c���_�{a`N$�Yi_�Wg��2�^��o���N�7�5�N�o}QO\;P��W,�@3;�Ar�������e�w~����-I:R��8>3��9��R��eY4��,����3St���1�*MM�E�d�O�	��]�
�OR��'��&��6W�N��T�����!������xr<@����1�.���y��0���0���w�ua�HP��j���H�:���#������������������u�g������BSu�;|
-���Z�l�o����{��i��/�w���
��0p�n�Urg�}p�y����.���<�;~��r>=�����~���$u��������>��-.�A���]M��~��� ;����w���4�_fU����������c������?��U�[��5E./~�<�V���K��a���W_�[�h�Fo�n�w�om&[����7����%�������w\)���kZ�x�N%��]�V&:�����1�p�	w~A��)UC����Pa�##�/�q����4�
n����h>x+*�;��o��3��1��Aj�9�G��8i�$���e�.#�"�	0���=�d�S
l6���f?�e2Q�y�$�!����j��D�7������,��	�z�,��t�$bx���U�'6z�h���m����j!��b��;��m�l#!�����;Z����"Nl���0%�+���/���0"m�����X�r����vz���
����)�����|�=���4����tX���`4s�lt���{���E�p��|v�������/��+)0������e�\��o��;�;�7](2mx��#���	�DX���Q<������G�vN�y	���C��O`��`� �+<p�9�
QY�}�N����w�������q>�.�?�<�O#�mp��:��Ul!��:��u�]���&7�M���h�)�^���L����$����L>�o{���Y�:��F(b"%������-8�s.��Y�R����9eSe��9F=B��{
�X��hNV�D�=e�������z�
Jb!/Yd��Z��7����N�������\�����_D)/(�������o;�g����i���;�\0�:%<���M����Q�X��_��4�%�!�N�����r��=�!L3��4����Kr�����}�n)�H+cq���D��O�/,�=��73��p���\��r���q�<
�
"O�t�O���9��-���n�AB�������Z2pn���V�	gH�n������2���$z�����?�b���W���GS��f�ag�b��a[�q���?�LR�l^W�����H�l�p#
��.�Q��\����Y����%9��y�m�]�Tp�����JS<IT����u�($Z�=����������������tO:��>qY�<��m��T�6�X�$��/�Q'�R��s�%��%vX��HY�*��H�3M��F�$h��/�%�
c
M�u�4���
�*��J��l���?�x�V	�D��l�������9�X��hi���I����O�R3�^�6�er���l�"�����?ql��E0�h�"��`!��>g_
Gc������*����-���������������V������������ +N�j�����N�
�Wv�����#:��}�9�9���b�~`��B�e�����c��d<&�k�]���e���(��*�h,����L��-}������wWK�H?��t��Z�an� 3��)�e����=�����7��Po�'t ���`�a���#R	�@��i�}v�~RXe�Q-������P�S���V�XV�M��3
�����q�}�����Z�����Vd[�5��3�}C�X&
�)� �v�I�pN}��A���#��>2�V�/��77
a�K�k�������fUI+M����@����B�F�$o;b� 9g���K�K���$�%�t@����'y>�shh{���Q	���DK����tAeLE	��W��13^�	)���
���� �j���M��+"��i�l�����v�3*-�3��:���D%
r�R(�7���u4�cc!C�l|��(���_v��)��=�I��^��b��F7A����7,Awb�Z����9�|k��\r��&�rs2�dn���jJ@H���Ye�0 �(�-�I
��,�p3T	����a�W`�U�*�,G}��f[,�����3,\��f<�:'�����#o�����/��o:�s�}���<3���9
G�xE��E�w��o{��1�U����V^^��]��T��Qp�I�_`P]�n���&Z�j:�N/�+
�����14��
�h�9��}�hY�u0�����t|���E�U^5�C��d�X�5G�Xr�a1"C�����\1_)�<�'������B1��P%Q#i��X�_y����!������0�sa�.7���G��Zo._0�s�S�M��b�M&oJn�S,!c��?W���v��_�nD���+���@�\y���^}� >3�Li)��^7dd����W~�R�v�L���HzRck�y���S��cW��7u�:k��-j����J���S���9�^:v�*�st��T����<�4��&P*3���������p���7�x�A�p��=� �,)1���C���tBE��O>��D����I��������7�����6�����B*mU�������!���P�+�~�����g����v:E��G
���dhFt�m�K��xE}�}g�}@;�����(d�J�i]���6��`6"#�^SW�
���r�j���5��b������ ~���V��IU���a�v���P=�P@P{�w�>l�/�g8c�������4B4�����I"+�^}�
;yWj���H�:����N���
I�3GA�Z<1e���PU���Q�P�8\������E�iX�����y�R�����j3����/���� j\������4�#�����vwo�t��{x|�����w�O�/�!P�����'�����)_Tr+Y-�w\�l��{�>,�Y�XkW��7������6�z�\��o\����z���w�^����[���y7{�^��������=�"�^n�7�7������X.�T���/6�P4�$��u�T�����J~���\�%���bFf:H��	i@�H�n��
���w�$���Y�R���J�"�Y/�'���3�+EM���r�F�{9�N��C���L��,�K��q?���$WF*����^�1uF3%laC���G����������>ch���aY9��"F��1[�v��y��_T�j����]x�����%`�fW�_&����[�yi��g���T�1��_���t`�����6�b��9Ak�'��zL\5��f
/cd�f�l�����7���bZ����_c2�b�m��H<o=N���S>�T�2.��^A�;��~�,)�(��]N����D�����M��y�����O�#H\}�C���4m�K#���Tz�K��� C�D����Re��4'����k�<����F�����������������w�������_����������"�,�!D����$�)b3=0��I�sv|����?�oy��i$8�����}1+a4"��F��X��0:O���\���@U�kk����5���E�������J��Z��e�p����DH@�,��?�O���0��KC�>�L/�-h��Z|�Obd���$��[t���rx~N('
����p����nB���8!y����awty�S����[�"�]fY.�t��8$����{*����Kh?;Jy�<��E��}B����L��J���z��N���Ei��E`)�2d��{�u��W���e-H!��1���:�t�qtE��#��R��:��������G�~���
����I?!���~�@��:�(���e�H�v_��
eE�y���!�*��x�q�~\�*��������A	b�����hs?r"�����;���h���M!��ds=,@)���*�_w��G:lf��,���K��X�V�T���{�I���m�	~Rt����Q{Ye�e�����g�S��M�.Pvd����j�;�q�X
n\jU@T[�c���}!���z�`*�U0=����*7���6`��J���.(]	��UdK;�&b��'��j��'��=�������"XE�c��g
�C���,H!<O��.��FTP��&��2�MKB.Eq��Q���:Z^N�K�JMG�q�ue�'���M���^�~n��������Rs���k.��ts�����d�3��j�f�x���J`O�h�3�P�����j��C�����4�U
<��pE
�j/��c�Op��
���	7��4PYC[-U�`�����������U����ZF�����u�
h��i
(jI(��1LJ%9�7i'wHS�G��rD��]f�1yS\���a4�e��R{��{��UC~�����]Z�������v�BA	����(�E�:�q�,%(�c��T�����A����<�����XoBf@���<�i��i���j����� ���u�0��^<!������,������zPd�8[�����6�e��[s�S�I<�Do��Z������N������'y���"�q{�>�p�H}�3��=_�y,���������������;�W��h��{�����!C,g�pT<�\���!���o�}�c�&�7�v�(V+��
��z�N����(T��^ \����\!A��[�'I,����(��%�=I0��$e���r��E�� ��\����������er��^z��!�0F��Y_H�PBmr�$
��H:��gD��j�nm�������������Ic$f�s�����W���Q�-E�@����X��p��'�|��%��^��
����`k�O���V���ds�����>cq�t����Y��4f��y~:��~����v]+^�?��H��C��CB��YC+z{����?�32J�"FwY	k������)����C�0w!�<�0�.0E��2��B����@�M"[Hs=������ ��P
��A�7���}��RwVM4M�,�I;�f�W�7)^�k��o���X�OF�����}U���i�W�:��pBZ�����\���?}HOM�
�mx��yJ�[�	���V�p�:�����e�1�!�����F�[��D>�&m���s<u����\��8I�� ��;����w���aUn�VBe�8[:��>Q�QY� dL��*y��@�5]��S8�i�#��M��N��&�+p��t�S��T"�MZ�Nr�J�S���������M�&
d��1�?xW+�+!s$C��H��lcF�
u�!�.TB�=�J+���sX�����Q��}0y���V���\�����u�
�`�E�{�v����h���� �G���:J���w|x��yRA�jb6	D6fo�z����L������G��Jl�,�i�e��{�x�5���q��;�����v���99��]�^���yAp���X�zho��p�7��u/���_c4�/�������e�+�����������d����+k�S��`Y��7+���+��X�eK��o�S����N ����[-��L�'�+T�
1A���������Mag,�k��I'% H/�$pBa�������c��K�"x��^X�Of
�\�AW<:O���&��/�*T�V`1)��;�t2���B���F
�1�����I����{V<6A����h$����*=�|�
���1��x�@M��%�
N�G\��lF�
�I�U�\������Qo������	��5�EB����R�jE�[�.#C#Z~�����U���=�����s�������.�/3N�l�����P����"W�&��Z���b��"����+�&�����H{����_
��6fe>B����F���Y0�(���s�V�m��x����n�gSO�����v�g:��In�T�b8�
{r����DY������B��e�������uQA��-�����:��6D�[7��e�|��/�_�
V�#�
��%�	������-���5Z��v�\K	l{�r��6mg�s�
��W��-Bv�^ �h
)�j�{gcnz��X�%�k�I���sL�^�(�:����?5
Kq���|_���a��M�^������NO �����p���������>M�>#R;�!)��<$�t������o���rA<�xV����+�\�e��u�8x=��
B�?���r2��~�S�%�c�}�����E�,%���EQf�>H����PgQ�r=����u[�����k~��Z�
���N����di��lV
+��}L���VE��
��M��hc:�K���_1���l-��2w��Yp�#
�h`������-A���V#�w�]�,�!Bp���w��^��X>2��	�%gy,������x_IY]^���Z;��e���*$7��8���L�j�yRdgQ�5�>��9��g����O
�Y�e5k��ktv`�^��F�I`T����!�*��5�����p�����X�W��!�Zp"�����5�?MZ%s"Zr����
.C�����	}�-j��'��K�t�G��R�.����������dY�N�t 1z�2�	X���P~�������,+NB��*Gg`�(��t��a����x?���y���r)�t����>m�!�����r
�\	�x��`+V&�sX�m������
�/�H-/������/��!����H�a�����ms.
�y���R���FQ�"�S����=��������iN�  Y}���>��~����.�NxY��%C%'���^���p�(�J���j��E~��v%��+���	���oU/P����
��P��yd0�����U�M�OT(�����?��D������"���A��S3�*���\�K�b�?��u�G�����A������!�P�u�AL��`#�4��SU��q�W������X\�/l����<� A}l����K�}<
��7B�_f`��f1���[[W���V��__��������f�4~���W�1�g���v\�BL��=��=����4(����p..W������^3qL����[�R,3�:Zw�����3����R������R������mw��3&����9����x*�z��w�P��/�:��^O���$C���T��u�)-w:�%C�!/������P��j8����s�D����"_�����&�L������-� p�R(85t�����37
=mkG�`�&�o�y�)���hs��#*��P� +��P��4�{Y������r��do����b��PwJ+�Z��G� �X�n���!0pg�~<���J)�4�t���e�	D�OW�S��3�E'���+X�?3���yG�FM��_�\!O��f4g�q
�?o�V(�A�B�4�N�aT�:��e^Doo� fNA�H}K+����2��U�,�}��W��Eg)����U�?�@w��)M<h�f����������1��*;����9�*o�{����!o�3�z]��i��E<W�9AL�G�.�v
�
	�$��Y�PB�V9�$1Lb�M������!�O9.ia���^a������"�W9+���+�������jO�����}[�����W,5"��&�O�e�Y�Tl��!�4��&�����f��B-V�SF�V��X��N8���`q�g(�;iy�BN�����,�]\5b��y�fE���b{�������>!���S�S24@L�����!��)�&MR��w��k�Ox����b�_���sB�i��o�����'��x���Z6�G0���$�i��M(���o���Qn�����]�Q@��~��"b.k�����S�.;\����������'��f��=V�lkI*�}���3Am�$<_�1���R��q���H�=:���I;?F 1F�:�b�O�.��v����nN&���$
:5hl������=��N���/�JZ�U����!���;�}�MM����f@5b��������G���KzR]v���T�
k��u��J������rL��_=fi���_U��	�;M<��:L��^����������;3\5z�\�7X�1�����Bj������@�A�,��j�����Lk�f������v(��v(�3��������#���{�UDK��Am{�V�EL���iM�.��3r�o�d����8Mm�2�<O4��kN�hdXu8y���2���(����G���	(�<�Z��w�?�d;�14����X;��M��E���
MH;��������t�'S5�&�|�M��.-C�������1��b=��
Ur�up�]W���0��x�|zan���=F�#�����W���������<�$v]��T�BM��4����V��������������,�4FS��
�U�����Kd�$�F@U��0&r $\H�
|��|b 7���P��jo�.��U����1�f���*��4L�,1o�Z�����m9�9�i������(	���=?�m�m*�JYR��!�;��I������P�<D�����9'�5������Y�W�P����������j��m���
��A]�:F
���@	���"#�V/k��q����B��d4\M9�:��x������R|���,Y�oS������r�Dj��C&9��{�h��cs��U��������*`�(�&�aF���\m���������'��1��n�
$wL�o�O�+�8�@���{�og�=��%�fI�BpP���$J�F�&Hv-��dC������Q�����A��:l���P��X���J/w(�*��������3���7o��_�F�����}��Sk��� �����?�)�?���M�{�*��1��W����B�!�D:���8��?��@niKf��:{|$���c�J�I��2�(�DT����V��6Q�4�t�����R^�_]���bB��v�A6J��w��x2�����]������`L�y{���B;��k�dv������B�kO�2b�
�+�dqY�6�N+&i�
/��oLh������u�
���{x��=i����[��NV�p��`"+q������2�r	�����������%�@]T�!��~rS�=�5�����|����}D���������fQH����p+v&7�9����
�/��K��R
��2n�)��oVJ�.q��N��>=�+�$|����{\��u4(j�i�;��)������z~��TV8H�5�f�*����
R9Ze,U)�R�C��!�m�hN�>)Z�dJ����_�te�����@��Dd���Zs6jA��k�te!4�����J�bBl�D\{���8
�l�ld�v�w����C��'�.�k��>����K�����W����g�8��F,��U�,�Ca�8�Wc�~�oB\a����lJ��5�`
��Qr��=`U5��X0������-�e���C�C)>K���.�.{oS���`*����������e��\���})���:\(1�HK����!K���tF��]J�2�B;��g;��5m,�VRqi����tu�"���~����j,�j�#�a��HV��|uIS*|�j�y/`DF��r�yq>�(9��a����$6�&|����db�����&�B���g�����p��{*�������q��|#sz7�<�����o
F�I���cU=��}T���w���Z~<6?'j����i3z��[�J0_�r�>)-T��ng�J����H�R�pyyV�K����Qyl�}h�GFC�l��SBDsN�q�vS�x����B5.���Ibd� ��|�V����k<��p�TL���n���v1�wz�jX3B��D}[���C��J���)��N�1��0P����0�K4�Kl�#�	�q!Bd=�-WL���~�����'�`�
��w>��8I���c}6���6}������ �O{_n\�����Ca`�P�g�A��Oz��q�<Dq%(,��1J�f*"k?]�g���4�YX������L�������
��Z����
��A&�g�����m�<���Ao3����m�Q��O~#I��*���4�(�W#kFq�'��������o�!/��C��J�I���������}��W%���jqL
"u:�������b�C������2�dB6I����6�Jx7����)���K|u���
���������!|z��`W�� ���U�*B4_T����r�Q^.��0�d\At��d$�t���:kJ���Z��:$�k-z��p��Rd�z>���)������d�4%��=-�[>Q���Ml����VaP�G��#&��eE��7���.q����C��������T�W!�5�(xI���8��ta�������c���E88.!�f�$���C�OB{������5;�
6h8�a)x�����\���s�(��@�v���dB�jYR���<a`���!���?���&4��"��R�������c�G'u�K�+���%���{K�����Q:�3���R-.`F��R��^��_Y@Y�)8���(X���&
~�X�vF`��qg5�l�8-�n�J%��<��)��v�t�\�FT�&cw<�K���G��~DXV�.�Sxp�����Rv�%Z5�a�U�S�
��[�F	N�u'�Z��-�:�>Y�R�5��>/x���R��[��o���<V�������s��4:����u��:26��a�Sqf#9������s�d���!w�EW�X�,�j;���DTFd�-i��e�P��+���?����>�U��
�;����o_*/=��ou&�]����;�����Ra����@��r��Xt	z
���z�P�K��/f��o,��[/eS��.�z�u�~,Tv����S2B��o�����Hs��	���_Y��#���E�E��}��4?A7���	-�Q:J��l2��!a�"b��d���q{���ON��������,B�xj�N����l�]��N�x���^)*�z�R7��r��3�iN-}�s��s�WA],X0K+P�K������Yu-[���J�MnLt9��X��|!m�I�S�.�J��r�^nE��Y��vE�p:;)��2���^��,T���j	$>�����t:����~�e�-�Z��b�R����"�-��_7��s��0���BQb�Br1��!�p
������ZP�r����8��y��m�g*G$]C��-����,�������j�_��)6��^aX���2H�%q������1�]��m{2�7�^�Gx��Q��3�KF7&`C��X�>�n�E�}$I��1W����e��@e��3��*v{YP��	=�s�g�u7��Z�[/�������R�������
�Y���h*G���	�#�<����%��n����B�Z���|���3k[nPDtH�f�v1,��L8��>�e��/�+f�Y��}��x��K�l�Cr]�K�yv(�DF����������(a���A��Q����-{�S�w��_�3]�r����U�kb�������F�����}�\5R��3Cl:�Z���)?�l�#�J��YOV{rY2��j1��q����Y�A2;O��um��4��
|�n+Q�W�7Q������9;���������!t���C����AUei�f��Z������\�U%H���%5�g�������������S5T��I8�����dp���0�Z��hi����
�\���.�"ZY��]#��e���R�+z����>��fl���Y���lat���E.�%�K,<��k�J+�a^�AOK�<��$�3��9,�������t#�7�,.�����4�A�����3TD2�|���l"��+����B�"�l�c}cr�
&��R)>S��HN�Q�L�����f3���9����~G�E��e5�F�E/�v�����^2����
)=�9�� E�u5o��=��~����t��yR��zp����;�|����������D�VvX�Q7H�Q��)�}S|�,#e�+�������'c�����CF$a\���m57d��1j��Ou�H
�Jo���Xo��90��=@S�cO����c���um]4���w��9�?�r����Zc0���;�i8��5��F���#z@2"u���4�tl�
9�q���fC`�%���	S.�M���&��A	f-�c�!/�yA�P��b����r`~� \��<MP�-�m3S1�v�h@�k1L8��+a9�J
$����Rn��1�=���:$).��J�[�i1�x������[P��Uf���c_*,+��7�0���PNfN����*�zW�Ml��M���jL"��B��V%IR��;o ��5h.���"]#N\�*���z
��z�F�H=R������$�:���K)Gc&b��&dn��������3 0�I���.3���/:tE��y��T�����h%#���zV�A�5K&��2"h!���j3�[+z�>��M��@yG2j�q��Y�	�?#���r�
M�)M��CP}�n���ROZ���e���{gw�����hRdwEzW������s�$
�������IVJ�T�`�qk��Bx,`ia�YZhy��Tuq�$\l���W����r�h����O�D��me�3fku�p}��b����B�����9���~L:��Y�I&oF�����,���O���p��fIO�+�}����3K�_x���?�Xr<E'L�<�E�Yabf���(u�c^�Nb��"d�V+{j����W�H/
����.�nj'��P��($)�RD�1�pni�4m�a���+z\���M&�y-�bV)�Ky�B�c��#�����*��'��=L���oOwq}�$4H1�V�������������:��������$%���h��������&�����N�d�&0�R'�<p��<�N�9R0������{��,�V��������T7V�K��	�����]��=��oS�gD�um���2.�\%��1O��)����D����'��2�;��9���L��tjKQ���J��G�!�X*��?�.��(��%�W�	2X���_���Oe�)�u?�1�K*����C��I������K���T�,"u�p'd�}��F�S�e��0��i	-�l�P5�hP���������Tr}TV��I;��tU�J���/����R��P��W�2�e�.��T��A���|��v?��.=�@~�<[�i�4�5�����M�L�����_|`M�@+d�������x:q<�S��������"1{[�F}`V�j�{��(h:
c���&��w���s�:s?QE�$�Aa�H����#q
y.(��{4Q�h(�)��~j-��5�Afjr�K�;��P������E.��+
��g�@��	���1���xo�7��������0;�������h�H��etKk��FK}\���	+�!���.����X$��c�B`����r�������}�Q�33�=HQ�?������u6���bbk�m��*��T��~RiG����o�y������O�])�z�9�Q6��F��a|`����j]$�dZ~�\�j����+�!�3���������N�90	�j�Zm�O���q�=!������=I����|���v�	*��e9�d�q�R�?�B������B��!�op+Y���$���������4�s1�#TUx��9��iO���$2�a.^aJ<�������S�*;r���t�
�\Pi�c)���fLo������m���g
���-0��e�������w&����F���� ����
�G�&l7_��:���D�S�6]W�J�]�&T�)�&�CC�9��C#�����-�`�{�����}��S��2s����q<6k��U���+��R?kTK��z��^I��=B���`�g�����0�&l
����
q.R��*M'����������9��l1�S�LH��{����!��4��
��(�
�%~�aM���[�8�)��"���e,�b]��O�����1�\�l����V���2��
7�����1����@%!�{,$%(?W�tp2���
����|��H�F��n��_�mK�gFB|��+mm���la��X!�F��)���9'v�H��S��1w�`�$D*���!�pG�F��(���Y�����:���
�j�G�����������0~2~|����*HDu��P�>8�=F1x�����������������On�y��
C�G����\,�UE���9VV������N"��[Ija��B�?)����������(�L0��]��-0X�	��Zib�0U.�P�j�sN���cg�,.����m��3P*��	�$,����W�T��������S[1*�����[:�9�[��X��>|�P�l�����{����0��;��BK�NPG�O�����Rz�����d�����Q���F�r�F���A[������tnx���n���(�K(�y���(�g�� ��`�`��8����pN������XiE�a�	������]v���+���r��bd��jM��nW8>"8#1��`"bDN*��2�h
������,�Zk�&�����.,/���3k�X�G��MG��~��Z��^���-����Q���P}���W���(�2���0@�NG�j�Kc|����"�2`����
yj�J�;���O����V�a�;1�B�r���m�3���V	������~ ������"p:2P�P�q1�8��rS��e~�SG��(>BiJyhR������=O�e��T�5�Y�<��`�r����D#���3�$��oGu"E0��0�5�$=��1���h�N:�[N��
�^+z�zBq� �9�N�����0��(>�vy=�8w}xx|�*'�
F^�?1�&�l?���v�������������H4�x�%,V>��;{F^�|�Q�6!]BK[~O"N�[�XB!�#����^1y0H.kD�9e�)�c���O�U��3�?����K�	Qr�� �#)�=o�'��3���K?�������O{��{�TYG����|��tN����-��
~���c"W�K����X��C��]X^O���I.!]V���AD��f�H�W�*�\6�<Q\�L�_��B��|�0m�%8]��������!�>�&+�	�h*,�.�"�D��UR)i]��o�+�t����U��?�����=�:%E�%����EP�r��PI|H�Q����)!�(����!���br�`�b9�8+V����9�;���:0V�I���`����^_�>������g�L��P:������3���iE�,�s ���&i3��'�����,�
�������������s��8�2�S����BY����oUb|��{{��;��OOw���O�vv�����
W��W^�ds������}�y�x�)I�e���W
c8<��B
�\��`��x+)$y�X�#:Q�8r�3� ���%+RT�.�c�cB��(������E�^���gF
����.}���7!l��[�'�r�������C��eb�6�A`��k��r��Y��KKeBs��S���#��9�r����!�]!�%�,��w)�3�j
5��j�3�W�^��5t�WQ{���\"��_��?�X�A=���Qf���8��
\�{F������iEDb����s���t/-�)�� ����!���
���_E��'1��rp���v���R�K��Y����"�V�1�n:aW&�V"�)&i���r/���Q���x|��`���#���zj�Rh�Z_`UD~��+*}����I��D��������>��9h���WU��rR$��]����Zja��
�����Kyl���y ��T��������VS��[2�����R\��zTJ��9���~g�{�s���}�>��j�F��k��������l�.����S���I30G�^$$����Fj���g��e��Xj����)Ojgw�#�qq-�����S{�O��?��hs],8ww+�
�L������2s����c��XZS���:'g�9Dl]�A�Qb����6���q�	�t8k���hC������
�� kM���&���U	�*S�#B4Z����z8�g|hz�����p�x�Fg��L�sddp�)#�+��O��D,�Q��M���MU��	(��Q��zPNB�]�W]��ATux�c������4����xh���f�a�6Y�?��F�T�7g��:"pb�r��!+�e����yH�U��*1��E���t��l�+��9����)pEf���TR�����Qs�d�Z*�d3T��U��p�N:�+(�CT�)���1�g#�V'%3�d��u���!T���B������I�sg��k�dx���z�_�"W����<6�!�@��VS���rw5S��k���BV���C�'��9��OG/h;��a�5�"�����z
?��Yp���`}I�)�HC2�l����y�Ca���lv�d_� ����|�E��-�L�+=U���U���3�Y��<
�����6�������V���}S���"��H�g9��^
�.�_n�*�r��!�}��W��hw�l����J��	�������Q�7��H����f�8v���Tb0Vf���]�������*�����A�(\�p
��'?����$�-T��$!Q�m]����>oz���rlay�B�	Y�u�������Wi����n+>�?��R��1�!�-��l���/���T�pV����[3���W��Rnv�G�yl��5h���BQx`�i��/[�2�G����J60��������������}�x�J�GUR���Y����J�	q�2�Z@P���c��}��� +���:��="@O8�Au|�<� �;><���
�+N~��\���ve1p{��5��PPb<�re�X�m )�G�h��fv��3�&"�L�2J�<\��s��-f)L~3�,�2)5=G�%*��"�8>�
�I����B��kq��D}��Y��W���|C�������~K+lp�#-����FFQ�5��3m��i9\��d��(b����w���<��X��&q���9����F�*-&X"�.�(�q���;+|N"�0�KO��X.��k�,��G����N�G%o%0V�A���}���fR�����rm
{����I|�B��M��RM��dcg)����RTr��v�l4P����P$n����K��q�d��5n�!�����C1e���w)��}��|���	�������\�.p����J���`��g�H\�1�k��>d�i�fb���5el�6H�%i�����t�Ar�e�t57�/�Cuq��=@hBee8K-���@d���E?%c�D�4���@i���s��U����e!��?���Y0�����w+M�D8�L��AS���S�uD�@����`%<JZ��Y�<�����y�S���F c6�F����h~���
�C��h�KN����*�b}(;�������AU{Wh��oy�������*��IH��Q�Q�w���"��
?zj]y��~Jk�0�4�; ���GF���U���_��	}U�������i�}c����!���~h6��5~
��t��������w52/[y��r�yiu��Z�����.����|hN������K��~4�h����w��p���^�����9l�f�%�����b �I6h��9:�}���R+
qR��h*�x�������r�F�g��s����</E�<tw���F�����!��p���E�|���-j|1�8�(AD
��	���R��R�U(G�����71p��l���%�"e�����_��:��j���0;pU���-7���4:|���QX�1�)7�T�^1�!�3�R(����r���B9��96F�*�����
G6��Q�Q_��r��x������t`v�����l��w�`����gzS���|�wu�$T-'�|N�o�-���!��0��~aN�;�
>#� E8�����,�14ea��!���^]R�/)�bpI�������?����T�g���G�=������������	\���������^������������_|W�����`HaAa4B���c�B��~�c���d���+���s��O�'.%W4�\�#O
��������"��<���z��`I3E4S�P�*/%.��Rb�E�;���K����Fei%���\��%�|?D�M���]3�L���*Y�(�p
OJ�-�\�=���!_n������������1�mN)>�Z�I9�A�(
9&�k����0f�k�2E�mE
o�2+G�|�oW+���?�V��T����������Q�&��K
��G��?��v�;�N�4�_}#%�It�l$�������tw�O�W�� J��Y���y=5�?�D���ft��K��O��;}��t4H����`������H�j��>5��O��I�J�j��k,��m�7�)_6�W��j��
x(@�j�+��	T�
Pr��cyem����?�������K���Z,������%0��IF�?�=��;�s+
��9��������6��N>�}J3�i���w�s���f���<����h���DQ������;��,�G'��?v?R9I����p���L���1h(U(|������ZX�����t��K)E��`�"Z��t4��>�"�or;����
a��v�j��@S�����)|���6mC��>RvX9GE-!z����1`�O��l	�tuB�����w�6���t�����tr�Nz��Y#h]������viw�ML��Q�l���ZH��wb���q�h��F�k�����c�O�b��N����@)���_7��vC���X�����5l�E�Fj}����@vb���R��'��TD��o��_X�~�����z`��t0�y����c�~�S,&��@C�4�L0z1�~��7���!_g��f�����S�+��{�8��jV�Y�TKL��x�Y�G���=��%�$�}�bj/��
!�C&����Cp�u��	��A��f�x�g���]����W���1��l��_�~�����,E�?�55��1�Y<�-8L;��b��~���p�c
�W���6[QqC��T���C
[�f09�u�#�{�R�x|�����	�9�����3�x�A��|��3�yl��l~�����0�N\
����!�
//��;~�������p����NJ�T�3���3~���N�e$�S&�����L�j=%R��Zy�AV�U�%��%�Lj����j��;����Ta��P��D�P��5�0�\0$
dx3D��	/^���l���B����)���V�|�I��~�nuW0�&<T@��5��"�)��
��7T�k�r������PUg�G�{#���o�B���
t��$��T:��3
\�5��R�=�
el'�sx�� ��T`0�&B�B�y:��[_<��8P���u�0���H��0SF�h������J�������]I�0�J�#&���UR�P"�6U�G��u=
���' �?��(������N���������PT�I�27������nn4��ie�Q�:	kI�=kl�dlt�q��4@�0{��`��;.&�0��aSt+��i�����.YKeq�O��/�X��9�������En*�b9*u�#J��F��h�[�X^
��^Eq�W:�I��"���/.;4����{p���B��v�x�)�vs�6[�������o]�����Q�qxc����-�,�:]i*�����{Ss>1w��^Z>S�
�2
���`K�*�	�;uD�8y%ud4Sn�#t!P��i8m����f����$G�@Q�3���?�
$yL���*��wp������e���9E�}6��C����Wi�N�6��]��8���H�v�@I{3��)��^�:�@���:�K�\�6����bs`�eh�������������sm�;v�Q1�9 �"��8��TP�k����p@��e�)�p��yF��>�a��?zz30wP8��Z3�*�lC�
il�6��3~��&�]5I�}k��r�l���s�S@���j��_�=�������X>�_}��|������W��E[�����x+�X�E�%Agw����O~��p ���������/>�]�J\*����}p�����D��u^(m�@F]
4�w��XU$pV��2J8��9��E7�_��C�3b�i���-���8������+�X���tS��l��*�[h�Sg�����D�-�s�f
�� bN�6���v^[�-��^�%�����I��=w�����4.�B�������	��|-�&'����0����	�(�+a�$CD�-"ox-���f�v��PxGkJT+j������D
y��Jy������b_���~A��nkv�/�'���>�8P�u�,z������T���+��]y�&��B���as�y�@���/���)����X6���O,����/x��i�[�|0]z���^o|��*4�&��#�������>����W�*��ee���K��
|)�(k����D_e��`T����Y9��T�j�&��)��r� )1\�}���n������3�QJ���2b���(����a�c�P�#9��	�7^2����i|�No�����o�n�8���c��� %z��m�rh��yLQZ�Zyo����0�3�(n���o����1()�����D��������� ��[��-��a0�[t���E�f����3���x=xup�3/ :�A2��,�@�/PZ����\k)Q_�������Q�'9�kwuqf����,M������������Do�JZ���Az��7����
;�}&��F��b~�[<���*��^r` �(Lpm�lc�I�~���1�]������m:���1�{��B��;|��ng6�{I��f�N�KzY��t�G!if ��)b<��n�n��*
gY����G��U��}
f��M=|����T4�1�������5m���Q������7���a>��������_.�����o6��������c��llmm���?�1�~��m���������G���/���kS_���T59:��]�9��a/����o��~��]��h��uq��'�K�|^�PlxYW����~qu��q����S�f�}�#z��Q�?-s;�5���~�U��N�TN�����3�z�s�������������w����01yro�'Ts8�����:0����d�FE4ki��`����O�p��g��j���/����:���v�����6����w���H�9�a�����h`
���^�"�V
u���
e��g����|�\%��Gk��9��m�8qU"a��i��B&U
�	B�u���i��J�&�i�Q3E8�$���!W�K^_/�\����rP���!!�����&��7�5���,����?A�n
���}�^�,�J-�qf����z�o�M�F���Q��.2A��GJ�y�FRpxA,�m�Zw�
B����y��-�����n�H+���Eq*�g��v�D�����$
!�"J�4e��B�$��V�cR��L����
bs#������kE���[J�������\���������A�0?j�G�N��!x�4u�iz��s��<��4<�:���Z������S�?�WF��q������b�����/MO0�Ng^�����Os�����y�G�����[g�C�O����?����ZS�+����v�ip�����Y;��?`�y��G�������X�?t�#�AQ�����i���}�P�4���l��-����,<�`{�|��;�������Ma���u3_h�Tc7
- T���K)�vWp4>�R�@heCE`�^$H,��&�X�E�CVn��{�����8g��`���j\w9�2V��X��
%W�AH�;4s����8��_r&u���8�V��SO|����������|UN�D����"�k�`�Yb������,1���Z<�:����f�GC2����q�|q+5�>�&'u:����h��H�K[�BI������w]U��\k(��-V@�}��i�v#l!���aM�r�Q$�<3��S�x��nLwo��7�~��_jD�+��H+���}����m���>MH>Q���Y����u��c�a/��Z���"��.��t��*~UA�S��p"[�[O�Es2_t�Or|&l���TK�{���6�J�/�K�YT��D����x@R'6x`��$U�z���&q�>|ho5�Aw�0I>���!���hM���(��E45,C"�{�c5�
��Q)�r[!���������d��h2V�J�&~)D^{��"HJ�[����^�"����5g���g�OG��Lx��5��j2[H1@�d���!�O���%=$l$^yq��lV�)-E}�>�����&H������n^��JjE���:�����"�E���"�������)|��RT���S�9@t�r	+^��,�Zm�+��aJ\Zk[�����?�f��^#�!y� f�����@t�x�V���F���30����*Zp\k�����3JXq`�!]���S
P.���lBk���������;��d�A�>�����C���p������iuFE��7�"�����c?"���~��L`=B�>�� 4,7�}��P�#�O1��Kf�!�Vs(�a{7�����
_��RN�	9[�Qo	s���F�������b����9=gV%�I�FJ(8������J_�����[Y�@�	J��&��w6�N��WS��[���F�\��iD��2��Z��E�iD<��H�~�N����z�������l����}���	�V_�_T_�b�PO])��5�����XXy����.�����,c��)1��z���m1�����*��Z�]BH?�E
$��8m�1W�\����,l7�^�p(�5�	��&���y?g�����c�C��O�X�.?���k��'\�O��1�yXP4�mI���Q������=�
���e�wr��������}���������}y�w;��q�����:�K(|��\0P���F��^����jx5������o�|�s�_~�������������Ev��9������pC�{��Ev����������Bj�z��S$g��e8&/�J�9�,z�xk%������}��m�V^C�L�0G��C��mNf��Q`XY���Al�TYnl~���bX�H9,	Q���W9��B�,>K>�����H��Oq��<E5����|��xd`
6}|���\�<% ����������!-�
�g�r��iz' ���Q�3���dv����M�D��3�)�+{4��s�N@��������������������l�C(q�e��hK���#�-\�0u��.�,H�[g��m��O4$�E9��j{XHO���e�^�3��TF�����$��/��4�BW.[nE�kT�b�����=����P�����H�wbEB�fY�p�S��?<4��DId����#K�YF|�{����A�"rF%�����:8>�x}����W�^P���_
N_��:8�~�L7�3~&�����$�a`9�u;w�un���
��Jr[v.U6nKKq��<�	�����DV��������)J,�������,�mjJ�6�I����n�]m�$�������:N�mvX��u�V�@d�L.�5��a��9]�N�D4����M4�����&�&
@�M!A�U�� 6�� )+M]�I`6��oE<����5����!c�i_�e��0����:&�1X��/�`�G�*NU���f�������Fh���q��	'qS�^)e"V���B(m��7���ohC=��j#���K�����d�(�i�8��eV�j��j��D�rV�c���$CZ���I�<�0�L�f�����(�r�?Sj~���t�*p��1`0�}xoW�F�~ne��X���!9d�i*�G����T����Z?����Gg����?������)c���zS�a>k5����7�������w�~��E������/H�����p�ef ��l
?���f�>u����������&�����������(�9e��
�~'d�(��<VtR�[��7��&un�%����h�����������C,H�@�CH�1k�/�������p��,�f��/�������d��A��?��bao��k�hI�W��T��y<C��Y�>�9
:qMz�g�D
;O_��Q�;����&FU��t&��p�|�bD6@�/Tq�`#,X^��(�������G�������g$72�����(������^G6�����H�v��?0"�
l�q�	�QI���(��X��UE�@�����b�zGS|E����RAk���r��|�u]�H����L��ap������7P �E��I�0�,n$�CY�s,�������&�FF���8������6����V�4����s5�R�Q+�e��b6�-�(��`y��s�yd����s<	�I�+��@Y�\��@
��� �����6���z��9�\�/�,`��[�g�hy��mF����J%�2y��B���D�t{�Mr���]�:,��������K����J�;��0���3&�q�p��T�eA���j�0��8�T�Z$���^x�y���$����O��$b�*�E6)G�1F9Pj���[����^�t0>j=C:R���i�0h�.�����J����mK�i�>���� g�b�@+XA(�^.F~J-z���y)��G�M������`���
���%L���J
��=C;^^�� =f�x�z�����_���?_����,Wd�T�(�j��#�p�4A�,iV%��xX;��U������*TEV	1k�#���md��rA%-0M!l���I2����2H$x�0�����u���EN�RT�����N%2`Pr��i�w�,��L�{a���#�P��0�|����Z�J�cE���0L
�����a1��N�����R�dI%)G^S	OF|~Q������R��"��s|����O��)a;[�Z��	��� ��.��:������������=���g���I��5�Uw4-W� �6U����
}/E�"���?����pfK5���r��z��}^��_�v	���V�)�
���a8)���"	�mjO��K	���������J	�5e�;f����A��qps������D^����AZatfAu����
���C��GF��[�����*�����(�vv�w�o���0oW��G�����'���W���3���gw���c��]a)�9�����a/3�h���������&E�]3����'V�Q���J�qyk����,�DV9t���5z��������rpjlv�;����Ov�R��ekk�:(@{�61�Z���������B'�J�z[^�$� o��0\�I#���=i���@�;��epk��TI1�9�)�����,9�~,Nx/e�����c��4y(��C��~���-Zz��Dm�A�{���"��*���r]�!�6b��X��$We{y�1��g������5G��~7^����]�m��S�05
V8���7bY������k�o���G�/�/�Oa�g����CP���F�\�Z�uY�A�*���C���	"L0��l"���e�~R��E���Z�2@e<���+�m�@5�R�a�����/������,���Q|	t�Z2>�)��T%1���4��];���`���M��Tx�0�U�,���!G�������e�����/�PI�N����"h�������H/\����m�L�e1�������h�z�>�a�wG
Rr����j(;�6/$m���P�"�j��n��+����"��*j���F*�t@L�h�=�u9�(mC1 ��1�{*��c�e�lN�� �a��q!�`tQ���W������F>�*�X����"<����v����O�i�-m��
)�(�$�p��Bw�L M����:���V-��$�J�!A��w
d�P����EZ��p%�F�����?�����A���'H�<q$��?�mYV����/<�X@#��7z>���
���
O)Vd
�<��/����;���Ah��o�dQ
RC������g
��L��*�g7��>�"��v69}�i?.����M�	g��G?�Ss��P�����f�^-��
��|^�;01��9�p�� �5b���������5�V�6S��}�yV"	}�K�2b,�V��nn�+yj~U��P��Q���e��g�������R�:{�4����%��1!Q�o���1��t�6v��Fy�?-�D|G�Ku�+��hS�(���v�e����_�������0]A,%VO�x��<�-��/� �)���-D^��tE(c����������9���hz��E�H"!,~�Q~5����pg
?4T��V����#2Az)��dD`� �"���l��7���{J��F�B����Z�Iw����c4��s;=k7���C<��N^Z>=�v������TmU�JPV�2JK�J������	m���zP�d����C\����A�d"1�����];�D���BDX�Q �J�
�C2<��pLH	��l�������,��P,
�D���G�b��K�PI��W3,�����50-m��;o^��=�4F>8�>=���+	�nZ�=I�kBl)�+�s�	pd��3c�A�����}�S)vV&����4�-�����H@([�����>a������<{���f=��p���l�v�M�0�d?.��+��P������R�h�P'TY�n��Cg�:��#��Gn��,�s���]NE�
e���Yf
,=&*���FQz�AMn�L\�#>�
����J�����c����������'K�\����-����z=�u�Q)��w�������>����K8]�B���!]j}�R.�@]��N*:�2��r�#�(�H�x�������l�mV��5e����mP?
��~H�}�X-4~��Yt�
�X*u:�����-���FJY�l������5��oy��x[1���;����d���I �����@����
s�F���0����r��+0���	��'%�l�tI��6����J[�]�O��>�+���������85����4�r�@W����m������"���4}����s�����b��������]>n��a�%��UxXE��d��]�)���B�]:qz�-J�r��Ebo9�X��y�H�TS#'����/��dGMM���>��'
�X�h�c����=-^��[h�����=�������G*\����	X��z� C�6?)&��l{��u��[�Eh�_`+_��ST�|��e��x��F��� �:cKH�eZ���w;R��<�i�tKG=D���;�A�x ������q��7]b�co���-
�?R�B�(��
��/�f�����/�#��w���h2������v^�GS��\������������o}����&|�~I���\�x�x�kW���PA������bx��p�6;�'�"""�a�O���lyU~0������Rta��|(����9]�O���}^�����:/���\�gZ�.�k�r-��`�fM1�k4���w�)I�R���'�0k�y��n�E#<?�bk��������e���CN������#y���e]�1p�����C\=�!��o���n n\4P� ���=l��U��4�Z�H�����.��P �5�&���H� ��b�ht�q�99(�q��YI����bs��}����_��b���)��f�1�u �(��a����;4���9���[�	����gB��u@4T�>�u��[L���~�Z��sM�;|��3'��������]��
�l�����%�:���v�B�I�F��]M�)�
d��H$����U�\BV��u+I�E��Ag�J���%�����~�hD�N��3�7��~��n&�.�����8C��:���I�\���v8�T��4�iy��x���&���� ��+T���i��q������V~��-sh�k�S�Y^����q�f��j�W�9��z�������%�^��Xy�O!�n0-��Zg'M1~���P�^
��������uN�~|�� Jk0N�#k8�����zz_08����[�D��)���P�	=uFh��A_s���0 �~��WPN�pRf��A=�=}/v���%��������2�y������J��P'�������A���h����aC�;6Y��l�1O��~nH4X���Xh�����pK����1C&^��{��������.
����auU�!w�?����s��bMa������\�$���S�E�.�a�j�x1�w���Z��Gr&�����|�'?-�ny0��n�z+���U
��]��o���=n�}c/��bO�!X���l)�yg�A~7������k���7�g{��-X�������������.��o��c�5�b���|��1��R�Y{i�]���'��	�Q�8��]�x_c�������O�����0�E���B��v�Y��9���X(C>�\2}��bK���n����+�a�z3��#-X(����py��������)���@���r�u��f��f�����
�1�oj���zD���j�	l.Dae�C��]4.����F��W6#�����Q8��+��D�4�g�d��l�%�EcK���u�=n9u���^S����9���?������BL00	��o�\.����{�c?>�}Rm#�S�U^������wW�������$����?�c�e����`��4�
0��rUQE& �F��.i9�B(A���e�����=k/,����]�t�*�����F �h�-��y�^V�nY�z@@7�w���.�f����$gX��+�@4�Y5�(>�`"��U[BT���^��{���15��A����Hn�o��������1�~^N��3��_����jNe'��M@�ywp�����@�Sx�uC��%j�����*sP�4�7L'��sfFh�3������N&Z�
�jI��TH�}��4(���:�x8����G�)�a�Z8?a)��#��u�r��'})<FC>��G��Q��y�������l�� ��3�%9	������A�ifp��R�$u�T�W7~��k({r�De�p$�;��k�$G �%��}1�����|�a�:av)��y9F�9!�-"8�������b�u����U��1��"*�3�z���N��6����~H!fs�o�1��^A��7�A4��M!a���pz�T+]�������S�����;R�����J4R�D�������/J��i�%<����h�8R(�P���pXS95�	��k�'���#>f3�;���x+�����dF>������!��PT
8{,
��q��Z��ueH�fbk�����<��QWy[���4��&'���bW��%�9�2�;n�3�p��T�z���Z�����~����)�Af�`���
������yS�t�CQ�]O�a/����o��~��]���b��z��W�fl�A�f(�����lEO�����QLaD����9���qiheZ�vX������
��b����g��XZ��k��C�p������P���sP@�0��,~��_���J�sRS��/����U��G�g�������{�`��##T�W*Y]�$~X��-O����H��8���5�b^	����MQos�fU�1��V$�gP������<���^N����a����`�+5������T���\��2��M��T�7[jU:#������h��[����-&�
�����;��O_C�������!�TL�
Sx����G������>Ef��/���R�������O4���O^�=�n��s+�Kr�T�Hx�cb�����K�����q�>h��f0-n��A#iggO�'\�~��prA���.qV>Q������0AaSk��~��a������~���]G��23A�536y~T��������^X�\��b�`��W+��3�p,7�S��<��A�rga�'� ���e0���_o���Y>�.8 ���
1�7��+��A��JV�u>���S���"��!{D���gj�0�?�o�V�V��R�.xC��FY�A�'���/�e���	3���������o�N��k*ECb�t��R��9D���E����E���%�����q��~,]�[�����k~hW9G4��6��M�c�>oW���)�Y�� ��0���'N�v���E�%�a~�i@�ASy�����.0w�$I8�8GN���S`@F�$�^��?��yy9��������������������������7(�c�������>�k���@lG[3y���gdT��?S�����AS�3�=��w�3�F���I���w��X�����^�������n�JF��E��`���-�G���^S���u{��b������~�#���|y��/�m;�����
g��"�F������e�3�Wy��!]�0wx�~�j;�0!�����%X����de���V��][��g*�S�����������:-���������M�7Xe��KC�F���2Ew�"���L[QSV����{�"��)x�Y�&{�E�7�c9e��=�/�aF.\��~
v����1N�3�9f]�8�88	c�D�D�������I/c��fR�KF��!C�4�N��#{&�g��tH�b�q�����s/@��������E�_��J�4�$�5��v���p�[��s��Fl����������+N*�47�Y�d?�0�����MC�O)�I�mx�rmZ��77��?��F�V?k��{�����l������������%��=5Y�����2��)�]�2i)?��4�'���N����
�(QBc|nZ�,:��V-s�����"`$eq��O��l�`J ��T��%��8��Vl#5���r4�J����A5u��7-,\������(��@(�C�y.QM�%0I�{^��+s���:�rd�XD�D$��SZ+�g[[��zQ�<��<���	�^�P�����>u:����d����s�_�|��uG��!�b��e8�'-�C�-�wHoaEf�<���a!N�&�����V��"�J/���z�5�l�����{�-�$[0Jo������$�ug��|ipd���(�����-����$�������VHZ==�N��/�'o����(���M���bA���>���<[�������������n_���vP����~����Lt�6�X��c��V-�>�4��k����M(+�(F	#aj���rP~��#F��C	�$�I�����lV�sg���r.����1�nJ�TKX�)D�=J��T[�2�3g�7��,qgg�H-�g�|�+�����4���f��6���SU.Q����(�v��v��yW�v�w�D�������
Z��A���lg�/l6����YMiQ�41��v�Y����6A(���7|�]���D�&��a�i5�a�&G�bo�g�&7�S0H~��\�3�J��<'s���EA8��k�_-j���	��]���5����rzK�!��'�y�B.���u�:�c������T\��i�a��4������Am` �����?�{�j:��,G�'�����IaJ��g��q�2i$���w�������~�����\�����e�_��a�j6B��l����t����}Q����Vd�P}����W���l
�I�l��Z�
6������w�]����N���u�{(nH�_uJ]���tQ������Q��~T+�~$zq�6�Q"��\���jx�Y�;4O���!F�"�:���h1�1�����r��+�����)�p����L�y����Uyn��6@��R��^X���eu���{cP���r�?�o�XC�x�����>e����W��,L��g�_�>-�~�%&�|x_C��q~�d�������/��98���������K��|e����g���������Hg.����h��4��'������0�s#b�s�E���c�!����Vf/� M�xg-I�v�-+4:�xC+�mU�����T+M�O=�jIL7�������|�mm�I��
��~�[�m��`8���HVC�i���� :Wr�C��g�6��P�"S�d�)�%3��lesc���<5�\Gk�a,�9�b�<���A�����Y����M�#�����	�D6���-���G�P]����i�&����*�1;��"��%(iK���=a�I�i3
����E���I&
�4;��4�!R`~�
��{��Zj/:Psa��h�QE�#4��d	�"ya+�)	�A�Fid��j��_���I��/���-��~;��z��:�K����?�5���>Te7�(��.$���4d��Tk�ep�7�~&H2�<��_���t�F@�����f�U]��4~DB������i7v"`'m���N����K<�Z���C��b;/�X���;*2�r�l�s����*�62��?14A)�S(V0f����=u����������j![L�����$:)�4Hp���B�1VLo6���2���,�kT7�
q�T�r�@ �%���L�)Am��>^%�B��6Y��c5�iAiB�bI�@o�����o�.���,�������(�:�
y��}��*^B�d���+Q�}��@{�`���l�*�zld���KntC|_�y�y���_w�?������U����}�������fO�]�
����n�u[�>����6H�E��i�o��?s�
�a���'�V����M+��_r�H���_�n�n�1���dPi'�p�M����!��c�.�[R�(x���$�T�0A�`��c��b���&����?���[�����V�������v�QP���N������2��/c���o2��
�:�����9��������� �����~S�B� ����F *A��H�h��B�4�W9��l(�����w����b����7B����Y��z�(�����e���I5���6(�;P�+le�x�P��t$41`�.��l�C+z3�>'fM���&�6�i�!]�`C��1��n�8R�n��%yR�:�|R��{a=�.���c���6�0y^�:�X������jCE�wZ7W�~�~	d�n7�.��w -�M�0�e��������3Vq	X�dp���8�,���J�9#8��`���\-���$��e���!�n��W����8<|'��2�|��:��Jb����8*U'O�Y��>�jT�0G������R*��m$�:?d����v���1��
FeD����#n�Q�-�#��HO#������0u{�cGE>��8�7��|�_o�t�����tz{_fQ����{\\���"�d�R8��x�_�k����3C�w�x�i����|=�(��������5�3��.�^��;^�}�����������t�)��(X
��:��hPWP���/����\��@Mf���'��g�ZY��e�t`�uw(��c�����&l	.>�(� j+Y���������|s�Y�WBl�
�� ����p�T�D��2�����0�
�Pc�#w���x����p������z��>�WrT�v��]����qY1������5(�����Nyi�&v�#����J�z��p��o�������m��~3�Z��qH(^B��(�/���G�Y����� &U���L6�A�5�v
G@�%}I���,��;��zQ��s��/��5�=(��!�nP�F��p?A�\��MU�I�-js�M�a�q9�����/yS����yy�\���4����@�5=���^�vd����J �@�������u�r�j_.��}�Fx��!J}Anr��[�_&V�
e~P'l��}[B��j�}�A&5?0"���Z:���8J���-t\V�u%n��� ��T�I'9�X��)��(+=K-��y���%��6^vB;l+��rm/�I����lo��������w�K�f��l��u"*��o���Cg(�g�6��%�Y�[��8��������g��
HW�e�f��M�dIT����O�
C4h%
(�~v�2�	�5�&���2z
U9��F{RU�>��^B-{��>S��KB�t��+.'�[�@��oV���dcY�h��&%�G�z���Q��,�/� ��R12�kt��gQW6Ai![�L3�8dK�������x��j����^s���HrJ�W���]����D��"�x%���wn���������{nv�h`��1����{�hz���z�PB
���9|���c��)���G(������X�]B/�������3������D��2�q?6��B��r����
��c�,��g�H�;����d~��S4����9|���%��/�H�X�1�H��.�6��4�.����,�,0'����Tlg���vm�����bX����J��[K����#���~�lm�����������	�R5���Pe��E����9�QpMz��n�������b� ���b��������r��2�������>.5n����	�����L`.������G���,&X����F�����L�W �!��9�{��:BN��FV����&X1k0Y?��5c�\/K���
��g����T
g�8]Z&�uG����m�T��W����%��^�;pTf�
�+�6r���(��Eg�
%o�1�+`+�����YP��3������aH������qn/<Fj��%�xP�DkWX8�9�,1�o�.��;�Wt�\B��+ �p! IF��I���lT��m��g�dp���do��HF��cc�
�P���-��U�3t8�i���� 4�c��+Rc��Cs9�__[l@�1�RCy5 �F	��&u��gS�����}�
��r���B�E�W>��3�/�����%_����l1�1�J�FGK�AD&���)k����j
���t�K�F��o���
[���J��w���&>u�O��=���KO#��&E��NP����T�����C(
v����#���pf�"��4g�q���A��n*p����>>���=����N���Tm��0m����}���������@�
b>^4R��t~�^��W���������_���z������u��F�	f��I���PRb?�������Y'�;�h)�y/em7�����#6���A��]k�
O�f�y�-��v���l��������q8��t�^�����*k�����`�V}]�z1��X*ZL�~T!V-Hp.[�M<_9�M�,O<�pw-���}���	�K���U.iq��
/���0b
a��(X�e��'����� ���������Pl�B*y5L�����|q������gK,(��
���=kS 
�&��g��,�G!r^�^��V�p�������lx�lo�D�`)����:��HDt�Ru-As����!�S����A�o��A\�Y1r]�����<�������7"F3g�w�n��tsp�d1����jL>��M\��;+J�+rB]��$�l�j�L��B]�� ����?��M���c c�����n'Vb�{z����v�*�m]�����n_�t��
y��,��q11::�`;�F*(o�U���� #=��E�����E ��n3n�C��/�&�jz&<��bl���D�����1f�����<�>�(�'8�W�x�
����v��2���e�"�S�`��$/>�����_�7L�
tuz1�zO���-U�w��V��Q�v*�{�����V���P_S*����x��D�5�n��g�Z��.��*�@�_��Q����_e=��	���o�b�_a��E��~���5A�
 K�3�h�6����7:���
���t��,�=	���(PS
c![���U�S��;�V�L�������7�T����z*4��ZQ�����b���7E|���b�tHv���nU*�����]�;�gs�7J)' E�hg�f�I�����y�gG�H5o�%����N������8�3j��6����&����8��l���'��._��������m�����G}�
{Gp��R-k�`��L���rK��z�������������������O����^%�I�����?�g���v�N�5��=`���z���������$dt��
4�t�Pv�Cg�k���D
�\k���G?\`Qh�$'�����ap�']�k���q��e�z��_|�q�}�d&��> a3d�[��������.��5�������d#�r���s��X�N�o�����{��4t��1��0X�������F��)d<�u��=�n/6 ��Y%��K"4~ny���q���
�	���N��K�"�d#!�5oh/iF���9�R��&sr����}���s�N������/��)@)���/U ���h�f������W�H�gF�������qQ
��!��C�	".������(����������O_��*�k�<�aW��)�Bj�L#CG��X�u���Q�~F��=3��-""������V$�-0���*���d��'��,+8|C!�����jt��/��	������H���e`D�w�+��#�NC$?��5�1�m�E(����4���������a#w?���& NV�	�!%>�>q���v������6 A�\���P����D$�q�SD� ���������GGo�.��GS�l[~�W~�a�7#q�%�A�c��L�T�@dt?"s}	e�F��D~+���
3��|���INd]L0��t���n�@�Q�F*J[��4a�
w=�Se������W��������r��<9?M�Of��a����#�3!�J�L�st�S�8Y�������rb�f�!������9r�-�(���@������D[9%o��������i�������b��)d�/
}8�C����@2��F�@p�S��"Y^�ACX���i�Jtj%��/��~��:P��������0���#�M8��8�`UL��:e�kEW����{E��J�����G�����\��@��p�q,�f����}��3X�����������%J��p�+�\)a��s���r���%�E��Jx���~��9��*�a�$f��O�Fv�P��MM��<��KS���@�LB���cj����g��8[{/����{e��o3L`1T��-�~h]�$�{&�
�`�f�C��r��IUr<�`��|�N����S��g
k~+g0D�����M����^(������ �Z�����gQ5�V����I�=Q����&��R��=��\x��d���J����������j�
�N9��>��(��3������P���7J����`v���odOCb`�� �!� ��;	������02~(�#U��f�ns�n�E����Fc1G���$���a]5�6�@�z*�y8��@���K�{����o�@v�cT��I������7?b����,Z�#�
�������1j���aKdO%Z�9G���_:UK����m��&0!	�1,�!����L#}AGYf�v�$H���P�?������2�m�?JLu��&����NL0���2��C�@ O��:����L=��AL�I�L5(O8�����t�d�L����pvO��u��	7l�%PP8X8�x�B2������������&�(X8m�lDj�d������o�TfQ�	e�EbY�a��M��
�v[(CMOu�]����,���X�3����/Iv��7��b��L�:��/��A~��_�6
@��ON__�xm�yf�|
��l�@"G�*k5YO���K*4��8�
������P'z������:s0��#����K-Y6W\��~iU�m�K�Ff8��,.^'�
�fzGy�G�����w�Y�km��I~Cv����[���Q�dJ1�d��`�����>S����H�J��������#�.����=(��2!)�T����s��$h9���t������k �A���{��8	&d������������y]���G62(!���Nfa��t���bC$��9RAlO{�OKH'v��:��r��am��c(��Y��i{�a� ���MBW����HW��C�e6q�v�3j�--�1�g�>���m�bu@G��ua2�BpN`�����`w������9
�������~l
�Jh�3��u����Q$JE�&[�����������`Q�?��l���ZsHN!����U��8M�����s��,���&�6*e3U;�i
UZ�(e��r:W�1����eq|��"WfLt���wo�]��W��B�'�Lt0���ASE��OwQ�\�W�g����rc������'h�?N�M��qy�m_��s��&
�E�%��/�>������,>tP�S�l��E��	".�mi-L�-������5m�_@\#�5�9���87�3�g�(�e:d�7�k�!�	�����f��(��]9�X��}8���'�Z�N\���.}����\����[��-k���^����j9'�Uc�*n��/b'�*{�_�P��5B����
WF��"���g��:b����v���zS���z3����Hs�7�{�����Y�y�Z/�"Z���=&���/�����A�=�z]����K���^�'rC��	�T�������v>�����4�`�+0N/����"�O���S�B��EJw����]��t<V�����	4K������Uo� mP�)1��W��#����������w�0�u���S)���5�0��8�'b�nm�������X�5t��7|��B�X�^,U.�d�O�:buW�>Qj"@�ZR�t��lnq��7
��Cg�\�l�i#����J��D��\��vV��|,���)xk2B�_��B���c����<�����
B�*( �yo���?L�K1Q;�����e��8T<����d��7B N���;�?K��?�>�f��i=��	�{+��rv>���q�	�z-��B�U��mT����:��T��B~xO�:�i�g�������q�s_����R�^�9c~FRdT�^�����\8��\�q��2m;g��-'z]�S�k��an����Y��k��#7��9d�uySq'�����%�mX�����.;�������B_��<���|��"p t��:���vA�,�H(dl�O���:���������	�:����HI��p(���*&�����x����P�����
����TQ	x��1������i��X{�[��
N�HT�G��Rl��9������
-�	8�e.O���I���_����N��0H6T�A_Wue.��a���a��bd�9�	D	
&�JFt�?$A���
���d��E��C
i4%����F�7$z��*1���TP��@'x�?Rm����=z�����4 j|���Xy{*�'�����2>��j��{��E����:��}�����.lo��)����%�������sC���q������}Q�����1���?@�=dIDOw���_���d�������jx[�pT�i���F�'p�c5c��NK�����\j�����F)�H�7�aG�+�5��)
;N����d��<���*	��3��/I������|��)���X�H�vJ�.��������|�n�eQopL���C�BBs�:�r�����(��%u�B4
��`��D�(�&��^����J�H�C^�<n"�{� ieE��$�tq�DraPy.p��{gL�d�#����	o&���'���-s������UO���&��e�3�\�1��f.6PP��?-[X���Y.)����S4��i_���=����}
��*�������7�Afk����@g���2�R�����7������sq����_�����i��Y�FT��u��2����I@C�h�i-bX���2�	��d&��5�):��9�(h40*Qad�����P��x��U��0T�.v52�b����������f�e�~�T�4#��z_���>�_V5�_�u��uP����B�0�b�$k�/���ze������/|���^���#VC�����i���2%�	��f���r�u�s*>�)&e��XV�������Sw��h��G��|x�-1�/��4�3iC��������7��-�zg����)��Wo��^��C7��<.�cW����*��W�s��,���Za�^��h���l,�M�Bh�u<��/V]��^�D��cx��C��rcN�~�c>2��4%f�/��3��+}(�E�g�6�}�T�v�{C4E��h%��B
0N�C���FX�����!������YzH����%�E
�8�-�E��^}!�^M�x�r Hc�IS"��@���Eh��7�KF������G��Y��c�/�Jyk�Bp������f�v'�����T?�����45�����
�4@e����
eh�
,Y������1�M�M
G�*�[��}"�����cmLs-���,$�����T �1�fQz/n6k�����9{�}�m�8hB�PtH2W!���D9#�4��Xe2�>�>�r����%�@�:��;����G�9Y�C���o�xn�d�df�]W�QFEN.
��2ZA������5�8N��g�U
�p�9���X�__<��f��M��p�=Q��
1Iy��� t9�+��RI�>���m�$3�|�)Y���|���������?��U9,��O}��n����B��p�g�j:86�X(���[���
86{B�.�v�TE�[�HSJ������R���_��vm��B\��W���;Jd���
� ��k�)�s/�c/��9	�u,r��-���1S=���#�C|�\f+��O�T���5���%[��8k���_���6�d(��� 0����l(�>G0R�8���p��h�5��(����d��L4n����o/�c�`����
CY�
��_i����t0����`�#���������o�Y�Y��=w�0g��/���c��|p�4pv18<;��^�>�=G��=��k�U�l}�OQ�#����c��p���S�������b��A���f�9#S� E�tX�P�Z2G�:G����"�(-���~D�F�H�R�oj�U]����j����Gv[<��{�����+�	E��N��k,[tl�W����t6��41�H�+8�����g�<�"���{���La�+�Flc��[������.�����j>�+��{���������(��Z����q�
�#�]<������Dt��p��
4��
���Ux5��$/�����G�I�(�:�a���>^���$����0������vBk����,�3]��2}(�%o��z��1�������1�y9���J��8o�z�>0Q\�������
1�l._=�~&���T��U��`r�&>���3&A����lp��`B�S���T<��Q�������qpU1��3��`�i�~8��>��k�v�+����p)��C�U��o����bJ'v7���[C���~�"�{����0�"�� i
%kg�xC����1�A�$	����A���S����lKJ�b�x"���hP7��#�ww1%�����B����%K,�~|��+���E�����g�P�5<������Y[�r)�����I���b���Qg�
���8�?y���9�JA�J����2���W�H�D0CB�8��y9��	(%w�`�x�:�E��PW�.Pz^b)
�T�D-��7�m���=-!b0�.}6NUsH-l���L����w�$�`P\���B�P���o��G�q+���AG����(h4O�cb_���G$�xX��N$2�$o��xs�@9���}����=+�/o;E�n@��e$��U�n`'���Mz1�7���E��~�
=�����Ke'�>w1��hjW6Z�>�Cg��2CYKGD��0q_*_#Um6���j���N�;s����f��"n�&�"[��P=�jLz����L
E�U��`l�
2�c�+j$���L��5�������6�<�A��h����k���H�P��jE�]$�~X
�	:,)�1Y�%���X!���X���L%f(C�jt-��
M�%�����������O��
�c8���A�m�1�^o��W�G�=�����rTZ
l(rR>�����.���������?�@9*G����c��j�6��UF3aY�[�����>��}�Q��	z"N�����g?�}������?�����������4Ci2��i��"A�S�J�7.����5�������igU���w(�Rg��QU1L,Z�������uF�q�6"?�,A���H����p�����A���[D���G��8�k�3�rd��-�B����E�5��dR���6>�/���I_���R�UBi�?S�H��X�@�R���YY��wcq�=�u"�`�B
����\�4h��?_�R����j�n4P_�d�T��%5Z��s ����s1���d����������r;WN��U|�����U2m!�����
jQp�rJ��v����l
j��S��[�,��[����eI�^`����zj�LV��jxP��9AM/���������;��8e������ G�b�Mw~�~J�;:y� �_�g<GY�7�c��
z�zl��%xAx=Zxv<�*��0+(��+J�dx���kE9{1s������FJ�I~
h�h_�P�@����q�����x,Z�������>���bfz%�-L�j��M�����l��h��m������*
�nVl7���2�a�<A�.L�r��``���2
9)��v��+�=���������e����e��%s�����Q���������h�����v�T+Ve5�������6�?J����t��.�j������i~���� 6�d����_�gX��l>�k=�i)��&?���-clK��tO��~��.uv��M9]�&���ZLf�;R��dF��i�e���Wy���R�%U3���c�_1=k�7I	�)��y�w���8�M����)��&Q,��<g��#��K%	{�
l�h��$�wV4+Z���[��3k�s�	�������g�)X��B�)Qbv�=]BPJ9��a����syY��F:B��p�[����&�N8@��-Ci�|����z�(�^"\��5���6=�^,sU�jT�#�d�*��e8�*�\����$��G����|�&e��������|��nr�#���mX������qA)#*V���}D�Em����=�����X�A��7s��m<�I�J����,��|��2�F.�:��x�q'BHC���~4M��_sd�=p:D��Tk���������>�_|�/�y�*������7�.H����p��qSin���X���m
����r�D��7������{�MI�#9	lS%��]e/�)��u�5~q���:��������L�����7�����T�G]�Z���q��4�`�j:�iT1���9��*x���%���(�3;6�N5#�{��}U7-i�MJvd�@��o�*�E|�������ci?! �4I�?=�UMlx���&K��,�3j�3y�k��j��~�Z��L~�����S#��f�3���[�_�����i(	�Q�F���0����=�e��6���2}�VT�*^a�p�"2����]^s����9L$��)ZK�|�[�����b���N%��
O�g>�5�D+Y����
�A`$B-A�����$f��N��:-�u��(���~����R����+V����uGj7u�>�x�3e�"l!��2��la���=�������MqMq�~���OND,$����&�]�IU8�b*���/Z������!���V�r��
���]��d,�������N�����ZAB�~`�Bs�C�FS3������Q�����L[�v��?���������{_�x��=���y�/*`���6Om�w����=^�(5r���M6� ��&E}]�9��Q�$�����E����t^����Qn3���HV�Tt@��g�i�x���.�D/�7G����9�:H�(��b���b<��q��0���`�z���bpqvpz~��<���1v6,~0�nF��/ ��^�t�|6���f�A!0W~���b"�jc��R��`������b�0�������F��z����Y4��H�wl�A������MG��'Z������������"}yO�N�^�����7G�nT������� '�y���{��=��/��o�]/�������'������E7�8��e�ox~���r�q9�g�������(����)�Tet1*�����[���:B.�J�SM��:|}�����K��c�R�����#�A����P�f>'
w�_�|e��$�
����9������J���
�"�#wg2C�U�{���o�RP�n�`�$0�m��`��D8�Ng3����`5�i��\�
����T��`��6��ZLL��\J�O�������bl��������m�O����c�*;��q�(����RP��NB�hH����0��Nt����\0O��i�A��_T��$���B��9Fy�_R,i��-$K��>������1��	n�� K�6�c�!���MUO���H`+��=����$!���y2E�2�W�H�A���Zbv�9�?�����F��]�0��<>>�����!�a���~����qP�8���3��y-tQX���[ ��<j1��Oi+1��F9���/��Q]4MAw*]��y�����
X
����R	���`�7�i�<��MI�BSr����S�����^#0����I_{8Q��
� ��T���C������F�>���})�E�-;6������O��������p����i ��Z�3�#�%�Q��,T�N��@Ec��i^��:4L��c?����p�U���l��,�
'a�����
��xn�1'����:L�Y[�$�$�9 3$1�'��b*j�9s)q��>�v�X���x���?���� ��L��np�SX{Y���	Nk�>J�$�B�>tny01���N�Q��s����V��p�ki�k�o��WP��F���!��_W��zx�U E4T������%9��� ���}��6�����)s�&��i���/+]9�"��b?/�8z���_��<�K�x\`4s�������A�'R�U���o��">P��&T��'�6v�+C?F\�m�"����_����<=���a�����a�]Rk���8,���qe����n��5�����g^V�6��x*�k,gC�eqW����up�q0J5z ���@�^Y���al6�1����d�;,�?�XN%����Fr�C?�����5r�B��J�����������nK)��07�5%�-���6#��J�{�>YL�c�-w���WsIp6K�9�:gO�"�(a-=��|�z�n��-t�qM��em�lUhW��*H���q��48
:�5H[��0��G������.�������]���~����VE ���������)�a��FG�#�����~/��})�@�IvC���Eh��E]��L��9"V	���hQ���6��u'��P���h03,f�g���}��).����5'V�+�.�yE�iH_���3#�Y��uEK]�ikMq�{�;�Lb����jXoC�NxH���(7?������%���Z��	��2�������r~-��j}�E^���(*`l6��R������zl�
�������[J����<��|�����E�X��?�H����$�nxGjr�Z$����D6]�D�	ozW�[���z�_q�*|y�-k�_1*=�E�5&��
H�GVY_�>�H�z7�%&��=D��WW�������w�	H@��r~��H-�/���FmZ�[���������[u~��o���
x����B���+6$� 6_Tq%=w8o����~������z �V������1����J�)����~c����k�lTd�H����cKXz����
�n*
*���
H��[#z���Dm�S�t'��Q�)��[%���R,���u�\Px'E9�Db>�+�MB��q	/���q�2g���Z\;L�����__��+J�	C���7���V����(�Hg��=�?���O����'�U��75����R5C�g&���*]
��F�_W<|���QS��	a�����4Y$!~Z�T��G��s��+����������W�����h����>�K�"	���IY�
�4��������7y�69�	�;��V���,>�gvixZC>V�&cmL�,��?��V�=m2�
'ayk�w��C�Q����EB7��

g�T	�Q�us��	(7�����o$��>A�U�*��&-�
\7�{�z���7��e����p�����w��-9��\Q�=����o�_�F$�{S t/��=h��
���,���e�Og��%w
L��e�����BReV��V�0[�Z�A?����rl0l����i�Z��J����[����|�0���	���G]��,�����2���!aI��l���4�	�FE;Pti�eC����-%������H3n����C)Z���.\�
�.�����g�X��^��@����@��r���(�MK�x>����Kh�:���>(��}�x�����j2)����9P[N������������p!A������")����S�
�H:�C;���>������@���7���C����r�!I!2�2�pDd����>[��7�db�7���	��_
�j�b�S��d�l�'���s��etH�uD�,v�w"���N�{W���!�Yv�*ClH#�@8����jz���!���4j��7��C��pJ^�������)����.{j
a��B_���wF�R,^��p���7�h��O�x����!���#�@�l������Su>����"h�r����_[��7�Il$�I:���Ww`5��5�#/��H������H��`P���0R���F��c.����3��O��v�#w�{��M�����N���I�m�_�����
���#P�y�j�H�"����vA���b2��^��_����nAY%��v�;{��,�i����62j�	mqT���mUR#)�;8�gr�����>%	��8���������@�V��F%d7���/���������U��t@G��]m~�g����M�a���&M�X�(�i���XD�c`pU�[��^8�����l6�9�v�aF-'����#�0��@�H��"��X �6�,���;���o�c�������_F��E>]����>l���c�XNfU���H��n2�a,�c�(e���{���W� �e�;G��
^�)x��?���8-s�I�=���K�B�0rn�r�Y
N��r}f������	U�R���#qY��(]��F��X!���R�T�����[�����Ia:e3��'�3+�W��)h^�M���A�1��d���{P��1��@B�=#�f�n�C��� ����2��������J�����A�y
����^Cf�
��\>Vp����y�1#SA�*�\�l��E���mL�|Er��
]m�����Q�:(V�
K�
yD_p���f��k�s���>����nZ����+�4��'J`��*��;�
7{�
xI)�7F��=������z���<&����)�_��|e�w�����o�)�
N�N���'�������������w��h��w���?�O�����u�O��������lk����m��?+��R��c���y���B�9�3HVt�?f�����{��_��s�
��`��$HLCp?����t�*�����������������^�7z��}q�a&�X`6$��H��r�\KD�d.e�G���'xT�MPQ�$!�t����,8�Q7�`�	x���aD��d���0Pa�
��U�<�������;qq��>}��	9hv��"B���F�C�Io�m9R�v�f��5�[�t;���</��i+R�(Z/��XH"�(l>"��P9��c�O�g�NWZ��3t!�������T���b��,4`� ���c�jI�4s{Y������c�gm�)��r���t}/#���(c|�������?&�fP��&�vF.B������N�������/G��#���������"�l��#l���v�VH�	R��~�*�3+��<�G�
�~������^z2o.��=�
-�O/�}1f��{�8�o�����e~h�d��W���������������F�h�Mt�u;������������Ncq�+��g�o\:����	=`��}�E
�g�'D����Q
�UH���|�OM'K'K�Y���k������^�$,��\����j��}�8�H%����jU\O�W�;C!�\�c���{���l��nJ>���+F �f��Iwt��=�f,��5��"<�{:���eS�Xu������Brp������I_c6�S��kD:��d���d,��e�2��D�L!�
�u��7-S����E�UNCgY���=E�myg)���7�����6p���h���-9�5�Y��3K~d'[���I*.��k	k]4����.�T����
�p��R��5ZW�����'�<���o�����>���?��_�jz��"i��;��A����0�@yFlGo�I��r1��Upd�������d2�i�P��5�+��F����P�M����E���R�3��p�������T#�1��5;�wp�0ePX�iLC2S�����^9���M�~�{]�
����F���dx�o1?�2J��YZ,V��"�����>R���:�Gc��*�y��q��)��G�~����^$��������ffC��,�o0�'/��q|�I�f�T-o2��-�5���������)��>�R7����hg4���"2��9
����!�*��Q%������L���Q��0�2�����g4����w����
_���`��g�@�r�cD���0��'R�`Z(�a]^r�
a#�����?lZ�g�����9��lw	a�������1��B�\�y��*5@k��1�J�qX�H���h&]�A���? ������X����R,)�k�H��%���g���DFm��a���
$�F�L�A��
h���MU����F�b��f��}����Ov���hI;����������s���!YW����~�����#�NK������qi�sZ�+���e�75"���
�bUl�>����X�e��2����I����9��e���&�T�����h��<��2����rM�i�;,�$E���5�-���%x\��-O����S�g�E6�/�xn�@�}��5
/�#*�C�d�sq1@��~�"�VY=�R {O��`>��9�w�H)���GJ9�EE����d�m�v���������~�����~z�x�Q�����0.
{�����o��a=1���f�X9m�%Jy�K��)���^u
�\��{>��1 N].���\����g��t��������2����5��L����,�=��x�p���������K����#w�}��
�s��C�g�O����;n�}���{e�H-:�Rv�����b��gl������9��`
����|m���=x�>�h�4��5�/.Ux������������Q��h�:��w��+
f�,�p��g�������O�&���(�+�4Bo���l0�d�^�)��w����?{������E0{
�}h�����|�'p�#�d�!�U��(���j���j����S���
�`"��]#���z�{�!?���Z3�%/}�vyOaM���?����.����k�G�&:���|�����}����eR� ��M=2���7ok��6�q�E�S�Yb����@<���:i��t��1�3!��C�[��ab�:�o;����v[}����Di��7E�K�9z����(j_��G������J�0|�Cs�z��d9%��@^Q���$�*~�{J����w���
�c����p�!N�����L�
vTM�E������:5m�!�/90N�	t��(� �����!�89��6W2���=Z�3��C��IY��;�e�!���	�2o$k�C�_�6e���snrlc���
E?����T*��(I�{
6w/u�����DD�Da(t_� ��L���&k`�+��Du.x�'K���.�S�	����(	
�` V��wM�����P=�{��
�/� b*K�o��v����y����f�_Fe4�IF�H��|+��tC���|}���C�{�a�����G��^�pL��2���(���RWF6Dx�`a��T�����-���B���������i�a�b����X�o5�t��l������:G��\0�V����-A_�U|FJ��r�� �(�ZHb\���E���h0��)�64
�y��n�k�H��/��^���s&�M"�����������@�t�i9W��Cl�eK�xNr�QL
���g��;����+f�^A������_�]�0�q^_j=�ps�W����w�+v��2�<`:��PBXn�k�>��kN�S!v��@=�����s*Wex�T�$�.�����dk�pg�0:-(�����H�s�X��dr�E���+���=�]6����a��3}�����m)y���C��l]hdt�������bMt[���������4C��na�_��u�T�='���q���|����DC�����XI���I�[����Zzg����EF/Y�,<�z�������/��A���H(X������C�.F��u�u-+��9	��� R�5�z�H�mR��-������4X��W4��OW;'���#i��`��%wN^���S3�(��������N��?��e!]
O����\BU	��k~��c�J�Wt]!��2�g�)J2�c�����T�U��]�NE5G|	'i>G	�h���/��y������9�b�
�H�����'��k*Li2�X�o?xk��������%��+�Y�%Q�g:����V��Y!���AL�<���@ �)T�wf����e{����mq%�����d.�k��Ob��yY��d�O����[Ry����9<����tK�#�(��1����71�o��M��7�����L���7oo�H�F��>����EQ��Y���-�D[H:�Lf^> J8& -k2���k�
I9��s������[uuuu��8J�xbt�V$N��r������ZZ}��bcl�EW����� RS���R���=�I�{���E��i���	L�w���'�'l��r{�/q5��Va�y���y�"i�e 
�>h�����H4+�|��AX�@>_X�?F��%�?�A���J�6��J�����8���b���Cc
;FQ45����o�b��F!d�?�+}:R�ir^I�L�;�1�����e" dp� 5����HK8����R�YOf����c� 1�wCp�@@����e�W8��R7vIC��[J�����q
v��@2�� �5mg.Y���E:W��o[�T�R���g�������������&�8��s�O���b�G&��e�H�����@�~rC;�����cB<J,wA��G�K��]����a�	a0_a���R�^a"J*`KE%�0��y��2\
�*��N�t��!��������+vr����
T$� `�]\t�7#*K+��i���J��k������������N�T	B1�����v�t����HS��6�Q��y����z��!t(i?a��_8�%��)�@t���������S��?v�w����������gV�{d��S�#*�|C��'V�(�96-����@��yo����[����/8�����@�\��������������*B�l]����@���4��FD��L{tG�H,���������IE�8�0��u�9�cL���2�h�	I�t�OW�s1����������0!��B��t�$���pF���w�����6���vP~���dj{'5<�*�e�\�8<6���n��	���{}��R�5������}
�&�}O�Tlp���i'&�J�������[k���h���.}��6IV��� Wr�e@��O��s�$A����
��)�p:U��������h�Q�esF��Z�ZjS	�?� �1qIzJ1�c�zmH�<H$��%��CX�� �3�S.N�����N��d�X�L �.��"�Q�1��uQ.�e��a�*�`��!]N��>K�IaZ��TC�fbv^�|��.'�=ml���Hus�a�$��>��b��e������nz|c /3�H}/QGJ�w�������^_��Wd�j0�r��T47	=���;���1��*e%d�(�W�G��q���@6Pf������:]���$��M{������7��R�|c��y
n,��/�`�G���#��V�f�>�!S������0<�2&�0q=�+';�"m�����+:���I���-\9\cc���#3�5m���AF���=�Bdmn#��.��;gkcg�=����L78�[�:�~���R�����A������XR��d���L��+���:�=����/XHyp���j�'�[b����V����:��+gS
X9!g��IQ���U���iQ����{�}G�[�����7E�I�#���`�;B��hX8�V(��$�����g`2����=���=M�k���tf���Xh��m�(Tr�E��UZ��+	i��j��r�3"�rZ�m���crJ
?�O��?���$���o�
cZi:��GOg3q��k�jA�����p�0Yt����BCP5�/O��)������������B)>��7nl��aH������J�@�p����2�v�[J��;Zt�����L0�$����_"l�d��uo��*��TPu�T]�2�l��L�Pq�}	
���4YN����N�
q�2K�#h��$E�8s,q��W26�����I��}���;���js�`Jk$��~�����r6�u2y�P<�I�d^I��V�T���r�������v����#���\P��@���>J��B������'���6
P�	p:��!��g��`u}A�W5�Y��9�������4/@��H�����q�K��x�:PW2=�Q����?�t`0l}��"�3�)r�U�G�7qc��SJ��$���2���i��A�Ly�GV+��2�cl0�$��.  z|��\	(e���/�q���<�|
-�1-J�����{w�h%H�G��7!,�j���������2!��f��cq	�E�j%{R�!��s,�C6����K�W=�W1�)xNdH9:e�����F|���]����b2[}x�g��'W�$����}���Dp��rU�����2tT����4���C�w	{����h%�cK��vs�v���u�r��e��f�]iG�����Y��i������\���l�)����D�V����*$����h!`Z��-6��ImT�w�k���?���/|��=�~��:��^'e�cM#���>J���G��F���+��������]1�mo#���}�RC�\wE����:���
�r�X�%�=a[�zH��Dm'�}w;}������$���I@I�x���(��v�l:,�l8�nl�����7�2�c�r�Wiot� �!�V�`4�iyH�
%u�J�c(�C��fB�d�'!��7q��	�T�;U�h��N��h�E�}}C��~T�;��p=0��.a������4G���/�e���T
�o�bt��i��:����������7��g��=�E2�f���'ID���&������{���%d�!hC���G�A#&?��3<��������h��u1^�)�������
wXm�jw���a�)w~����Y'f)k��X���Gc�B��@�������\S�Yq� ���1��Fg��|,�s���$���x�8H�XN�Z��Ru�(h[t�"5l�k%���\?/2����F����(�'n���W0�(5k
<i��4�����=�K1V��)�3a����A:� ~W2�BSZ�rO[����)��$Tx\��8pq�
g,�i=�s��8}�u9}n����^���e�������7��&r�t
P���*W�^X�j,Nz6-_g�)�
l�aF�S����36�C�JI=d+/dJ-t�z���A�w��i������R������#����3��N�(�z���&��j,<��� O��`�����j��W�Vkc�v�h�%
������r(�J�d�q|R9vv��z��z�L�bz0���Z!A��8��b9��������+����=�kY[�{������X��#xb�k�YSa�c�n�L�L�R�Wh(�Y.n&�fl%|=�lYO!mYB(�������Y
O��u���,�J��I�i�
;C��gk�u/�"Wz���sf03!����p���_���_�,�.�||p09��G��W������G
�d=g�3s�(pt�qpTi:���2t���;N������,b��DB"|���r���4������N�s�98�B3Dio�?`��q��0_��G��!,Re����-���a��C4����"��i�P���pL�"�I-�I,��NnT��:���M����y�%EEt@��8���������
���J+y�X/�G��Y�WFJ������O�I�zt���j'��d����a��r��CgW�xc��`���H�\�;9����E��ks��xYD�����������G�=x�=�Qk��F
�y�1��P���/fB�L�{m�4�|���cq����@
!E�_"���a�����~��
m�
,H�D�������do��
q�!"� B��BZ�[)�T3iP�N&H!�e�5<��p������q��-��,���@�$���s	1�?<B�e���%���>L�f�Wha��K���<��#"���\%������:���m��f��K1f�P���iF2[�F�����-ZO����^����]��t���@K�����|��}�����m�gf�(�x�,8j�w�'�&
"��.��/:��b�["���?&����%H5�4`��=��qK�~�j�V�M��x�^�
w�U���~��8�
�� "�y���^��D2F^V��W�Mj���N�l���K���|(�����E2����)_�*����L��*<2�6o,�j�l�-�A�,���k��L�M�8�Hp�3�O�����B��SF�(
����l�����-:�RbeD���B�����\D���r9����x�m����x������Z���u���o��\T�W����S�[�����.��(GV���{%!�IQ��dn?�7��S��#������KqE���/�X��H0��m�R^&`�[,4��4L���iW�kv�U�ID�8�rL�M�W����ahu��x3]&�8(�J��iBkj;��}H�{vL=���X��rC��#�h�,��y�*r����}Vj�I�'��x� ���LQ���+�,=����W�����)��������s��\w4�2*�l&����c����r��/E�X����z����B��~�u\?<��BrY	SX����z��p�o��D|�F���i��;^���m�q�V/����r��-���]�\
B��#���q������0U���W$��G����?���{xNGb�&�C��[��p#�T��PFc������,`��Y�p��DQ!	?G``����|�lU�
1����J�����*�1��-���%��Nu��f�2
�k	x��${��EX�b�?�3hAY��jKO�+���X�fwl���X��'V_D�t*��TP?,;��������D9+<�R��M��~�b����s�[��d1P��^{&��������%�����\��~����"����1�G�T�6��!��H[���gZ�V�ze����cq���������Y>2�{%���(��(��<� �z�Y�����9v�X����J
J������]Gs�5�j����1�<�[(�����}%��Le�^�=-������~�l���8��{�[t���.*��n����5jG�������x|�m��dH��|2����N�)�A>��B�,5�n)��@�J��y�J��&�'^h���g7WW��p�v�_�/z����^�B�Ro.:��^����p;����4hU�\���`
�l��/y��#�#Tq�>A���br��q+R��-'Up
�����(3P��3��3<RP���P(��d�O�������<������Z�3�JS�-S���o����2��W�;��4+��A'�M��-x�]���z��
����~�,r=��!�\������������3vr���8H%�~VX�;��������A�t|3���������WB9U����:r�:��Om�v�sL��/��5s��]�w�}��A��.T!�g*�[�����
.Y������>���fA��������: ��HGW`����7].Fj�&���T`�- ��,�j����'�u����B@l\�����xh�R�R�!��
U����o������<f!��~�3��.�v���y�ut�u��F��������K�sN�_�Z�@	�\q�7�9�:����
�{[�&��&�����yE�f7����&l-��U4�-�����Z�w����DO�:]P�Ix [��P���|�%{_�w��������S�����^)|/������[(@Zx�W_qTWDS����H��s�/I$���Q�qIW�|�-�k���~��M�7h_��7'�X�'1�|a����mC��x#�l6w�Ua���z>����
��@" #RS�=�Y�B/ay�a����l����=a��
�����u9���Z��������������wb�����(���:��U4�z�����r����6JB�Jl���������Dcs�+�#���%�$y�'c�����`^Y���|�2�.��XoN�'�����Z�����X�_s='��Y�(���MP���Zz�]B�c�Va�-4{��i}O���@8���!g<����U�a������ 8^�s��a�.pz.��������(�-�_�79BlbFq�e��7�%��/�m�����t�h�-(���6��
&�����s6|�>����>����c�� C��I��5Nx�p�"����C��FL]��B�K��5����|F1$v����_hS!+������U����a�vr�<h����Qc��&uw�r�i�8�����#�/9����5�0��P��m����������$��mgx���//;������$�vn����mL��S�|��
?��pz�=k:��n����[?��[��u)4Z��^T�U�;���MG��1��������[-��>����^I�.��{�1���%$���3}A��:���e��&��!,�����	������S�����h<9�uU��k���V��_q����FL���N����}&mC�N�4�	T���/�u�7��2[��.�~�8�	i�����>v���L�{t�&���!�8�<������
���#��S����P�!q	h�G�*#�q'��������Y���C�kz�h�d���g�.G�mG$T���mi�����������g�;����y��}���.V���F���TQON�!��n�V��F��]� ��0����:���H����z�#�7���{4r��j�GG'�n�Y:kF�����|��f^��\b��pi��kC�`qXA��G%*�o+b�t�����!`���I����i��'���r������&�f���yC�P����%�p����rj�����%���F	y��k�LN\�*��N�F��&�5��hSd���fFMe��y3�
^ ��l�j��_R����w��}�;�$�7��c�����F+���S?�^�?������x�f&V���vAr�����=����_�]��{�vHD6�(�X������������r�A��QD>3�!�E�o��������,@6�>1�p�Q��E��4?�E��EeA�+����"����b���bp
g����q`w��B���FA������Q�4������r�1x��t��������To����K��@�%�P)@�TH�K���a�������+�S+�<bW�h������0C�m����_r#�	�r"�!�U�,	9�����R��%sL�"�ea�h�y1�+��^(�71T�Trh�\���!���f�0&�]S��V����`<�������i?\N���jv�Uj�*�&�S�(.-<U�D���~��K{�&�� K�j����0>���q���b�GS�'�l��`��?rRD�j\9Z�E�.���}���/���yU"�'�)-�Q4-�A�hf_�������7i7V]@�)y�5��}���!3	�K�>r�c8�!6��0A���Q�Q �$'�S`���\q*�L����{�c��/��N���p�Q��T��!O1�b��9��"���5�Z���D�D���\'��X&b�����V�Y�Jko��h^�]}��}�y�u��[`C���!������;��EJ��+x��"��V6f�L7���Gs4c�,1�@ki#���c�)�h��'N����_�����V>���0��Q2�A!��p�(��B4%���Kn���*2e&l��,B)��J�T`�����l��}}~��F[Y���n J����b��2%��r����F��Q{��t��FT�{�1��
�����D��� �1@5���4{	��_R
9�I�1D:�
BE_�����-��,!$��-�^���5��8D��e%�1k
^�
�HI�OZ
�F���x?M<@xz&����
�q@�@*Bt���D����
�G���T�>II�-L����4��<c3���)/
;�bb�0��K�E���=�� X��S#�������}^������y2�)u��*���ie�u��l,��	���������b��*sN��C�
�Zo")]�o�����������m�Z~p[�����~g0<��i����W?�9M��/R���'��To�)Q��E8yI����Q�%�,bd��Ue-y�����6��.�h3*�SA��1��7#�
y
�q�I����:z�;��#����m
���9�G���`9T����:�=e8O�<?�{� $�!K��JLm�`�n�	N�}���S�y���������o3Js���:�n8���2���@�2 ��b�����
���������cyB��]����A�X�q�@��#��	���&�^���V4���}���~���CLDsj���
aw�jw���7�u�StJ����#P�
5��(�s���������;b���Y�l�b����c5��~�/\�+WLl�����������D)v_�

������v�	{s.{Y�~S2���D*}v*W�Zm*�)E�zDt.��"�r�L<��#h����|Z&rF9�!�9���te���HM��!K.S5q���1B����
Yq�f�y�'����k:� 0�u��e	j�$�y�+���2.ae��P0?���T�R6����������[���_8���C�<����?������s�FA���A{xv�����[��q� %!A��we��M^�bU��V���������E�h��M$����F�o,o#�3������{��/Y��{�A����$���Z��fcrI���b���
�%A����1�k�!g������	����~lg�tW�n����*�k��!t�?U�{�/��g��2y:�t� ^<���j�"�6����#{�b(C��u)��!|���-J�H>->H�>
8�}z��\(���|�`c�[�W��pxl�'�k%���^�N0/�ra���6��"����Rf�<s��(�%�	��.#�f�"��1#e���Y@�&�,�)�<U��^���7���oe�r2�^:j��:rv����6����������1��dz��D�n;t�:�8���Uh��C'.�^�e��?�m^f
��3�R�w?�3�������������^S7��|�=�W������
�[���D;�fF�<%���B��/W�����[�M���SK`W(�Fp
�`��7�%�����@�%( �>����A(���u�������?���S�-���n8��m/�@��\�@�Y�1G��P� s��<�.yF*�j�a�asm
�%�A�`|8����������=@�~����dj����Zl|]3���d��
a���G#��b*B%���]���:���J��a���4����H��T�
B��yT����!����@-4���3U/���a�������z��{�]����g����bIolEp��(���0�a��4r�~���;���3%4���G�|d�Gy1���9��IPv��+�)��9�h���R@��y���xI$&AR�x�=Q�MB.@� z�g������Xpm�-n8�����*���2�O�{c�%
����1�.UM��zR]jY������@��&����^+v,�	Y���P���5�(?��
�m.�
�I3
o{	Y�#��{�*���y����}�Kq-�0��it\7�D:�ii]U~x����)���f��-VKK����q)�2��A��9�Q�!�
����&8�s�r��O�p��;o�`�a6e���B���9W�[y
DvXy~�us:n�8�lx���� ��w�b@BT����[����p�.�1����"I����#XL^F�[�q5�
���V���x��
$�� �]�Ac���k,���Uwx�� A���/4������&R���>�0�n�l
+Q��(4@e�(�]�<�Q�6V~[�L�k����#�6�ZU�S�RbMxl����k[�!���\p��%O=E^��G�H������A������g��y)���@��9���u���������0���;'���{4[!�`��[�e'���-���V,=��%+��F���1oY+!UP�[|���M�&l���l�f�AzllU���T�������},�)��+�����>�(�u�r0����0��?���E���.:�)�{�I�IPpz���y������rr@�H��E�;	�N���~(�.������:���NiR� ����4_:;�i�����-�Z*)Y��l������C��� ��H�P�c�A�1���ytS	������K�A�Cd*�wZ
���b6�8MfJk�P2YJ�)��;����������m�^������������<hm�dPZ�No�B��:b-���)?�i]�������E��^W?�`�Y��'���(!��w��r�e�
FM(���g������q�W�����C���E%)2A������Q���qR�d������J��bE�������1���h��S�x��U8�5\d�~EI��:9�G���Q�z2>9��4'�Q��(f�����;��� �����=��p���;�M��y(+�8�p7xs��������U
A���3`
u��CH�#��|��d�0���v�~���^�A�����A(�)�
�7���	����h�v?�8��z��T7#�����J7�'�[9��mJoWD��'\5���`�������~��\��A��s����v����_y@�|��\<f�4����G`���HpGt�Q +Red��h|�:<����a�>����U�����C���c(paJNa�g�C��d(��	�������C����C��5�����a�������p�$#e>x�g:�-���-V���������]�)2�X%���-�����t{�v�>"�s
�
(�(Z$������
a�#<-�@:��>�i�vB�&�t�un87�^�FS�j�L�N�C�D���pLu8]�3q��H�#�3�Fg'�F������-a�a�m}�I����B�2�^�D�I��q?��xc7��Pz�����*��C��:����@�T��]J0�"��������1�7�E�|>}���u�t�4���C�9L�����5����i�p�M
=��K�����B4��o�+�d�<S�>Xt������e���~����l4'nk��VG���`rr��8�d��]��)��3,���!��n�a��s�>����#����w$�8�qN��<��5��Z�a����{
��� ����K1�7�z����~G!��'��c���9"(����Cfa��'�����q�FT&�PB����aE����-n�8��OK���t��x	iq���Sf��~7���}���II�n[e(�)��U���A���O���� +��.�����\_S��+�g^eg��JgQ���i��S���G�����)0�`Vpys�&�&L�Xk"&u�i����#��RJP�Y�%���(�'7���,U�[�9/�*�������4��T������~���*����%5�����8)%e�`Kk��^���%B�.��!���{}�n���Q~zw3�����>�N|���4�r$�/p$�l,F���=
n��i0���C���h���X��z3x��\r��`b&�a����0m�����J>��!��_$GS�$���Y������ZmL'��w�X��Y@�P���)� w���m4�(�	�>q�����v1BX��a�"�O`?�B�.�~q�6�I�NR��4��^��]WD�qv`4��FBX�:���%�o���O($���5����K��`}�N��H�f!(����J�� `Y�F'� �D����6���F��T�xS�������vg�x�w��X��q0�OBw���WDn�'��������k��7>��@�%U���bx�j������o�����GB�[l�/��6����o����d�T�Bw�^���A_�g�P�Q:e�
��&f��H(�=�G���x�}�[�x�
�s8�.�:�1�=�A�E�3�h!�IBf4Q�{XJ�����U��>b�YB���7�hpW��5��.�����{��#�x$����&QB�������(��#���p��ct��<<Pct�����o�[�^^��|�!|�8���"���.��SB�P������v������|(��,}��WRV9Wt������$@(g������F�
*M���y�1��~���K����
�)_T4�:�J|�2��h'���l�am��Z�5������(|�-�����0��a68�i
"A]E%����dp�zL���!1d���#Aq�l�p���$���2q���o�6v�?8�G�*�Pz$w,��m��C��zJxp��b{��d�"~��p��%`0`H��_������i�������!��r|��PZ�?2?���N�����f�9��Aj5xK<2����Ds����A�����gb�A|��0�	$E�p!��P���9��p\���:����J��C(�[�l�������c�����on��S�����$Z�c8��������j����h��K��6��cq��h))��,W���������1H�6��?FTVU��aHp����M@��!
�_�V�
���l�������o	���������{����w��Vk��������4�9�:�-����/���6W�v��b���)��d!7>ap}�@"����F�U��'�2=�M4�@�Pz3"��k�N�o�*H+�L#]���-��;���3��%���(%�������wt�W���Q����Q^��5���]UM�'xp�`�?s�F6'y�IXX�
{�es
g<)U�8�o���	�d��E{��>���E>����-�R�A�
F��!���?���6�!1�k�j�L�����������M24vm6����4�A�^� �o���&e��7�zd����M��nJ�i<��f� ,/����#	����
��<p��8	�����^B�#�� N\�I�
v�����Y[E)`��7-������R�K�RO�<m������<$������D#Tq�U�m������T�+��	qA�IO�'p]�����j|��4�_'E��g�iA��'�!��l�����Y5#V�I�2�nD=����"z�S�s�(����oo�������jC��T9�gx`��dh����dp(�$-W���4������U���/+b]�r��uFx���YB�k|��n6�9��R���w{����+��e������?����z������?2�~������#���6'���W�4�k�zB�JF?���#��<�?x)������a�n	7&�{�.���{��$������l^�\����gp)��.�����
w��x�E�����O�
>Y���a"v�xYW�b�����L]����I����R�!���������f��22aPkt89����`8��>:9v��W@+�v����e���YX�D�A�h
��).\<��Z��s�|vF�~�|�In��8v1��n��������Kx�,��H�!�zd�kL��A��������)��1)����|����-�c0�vN2��B��N�Bdp7�����2V����?��]����e ��#�'^��y;�e��[9}����f���~�����Q�'�� 'B8��&�pn���c�n���Gy9�C�W����(��G:/���~AnL�N���XTeXe��j�p���]��8�O$���8��h��l���p���Z�F�F�k���h�
Y��4 ��akkKl�C�3U���m��e�X�������"Q!1�
�P�x�����N`W�������D�E�Z��k��?l'31��~	cErh��i��p�)Y<�.�A��0����H{k}��G7hxH4G�t��X���)��}{^q��S���O���t,�fl��)��X��p1b)x�%~���G��2B�����rT�����c�5d�%���`&h]��D����$���������}�m�~�~'�4+�W��8���a���7�N������#��p�d�i���L�}j�L8/�$} 3�|ss}��
/�i��`����
���$VZf QYV�����)[�%��s3<�u��:�,h�?�|�������&2�Y)�t6��K#�A
y"�\�@�bG���������)a5|��.�T� ��:Jh{b�n�U����G�0C(n��H�7|-�9~�3�O7�,8���������Cz@c(�r! ��0
=�
�/Z*+�B�
������n�����Qv��Y��������{���uRk������
��(��2h���+a����.F�?�

u���/Xd���I�����cz��� -��1	�1���H������.���q����@�]�B	�'������x����w�&1��� D�#��C(��aW���{5>�����W�{��t�P�	��]�z~&ub�[.��j�#������j���<��*���$"63��-BX����:�a`U�d��������]4*�j2��;Oz��^������yR?������f�Q?r.�r��,�#��#��u���
�|b6q(4����C�|�
'��\HoTq��Bvx#�x��;w�$(�&E�b������:(�y2J��]A2��$�t:�����t	w���1���%�e�1������.,�z7�5��J�?�V�nN�����Dx"� 	q2�(����fA���i��
�}�R���0�6�"�8���D
Z6�!l:������||u��g�{�[<�OW���V���+��jj���7��2��p/�����[W�����Q���@���x������; �b�$M���i�������������rP��{�jHY�V {�.�e�-�D@~��H��E��i)��!��H�0
�(d�3!�0R
+����3_?��"a���������/R���6���
Yn/��Xh 1���G�X�6
~hP4j�j��Mo�s�YhKWVg���JyK�z�V��G�"�Ka�� ���Yb"l��:��n�l�����!���If_Q��������J1.��D���(���	��NJC4`�N��y���QI���
�Ag3��S6� �������kn�����6!��;�����gG?{���B�������s�����:j;��1��gN��\�	����r��r��P�{b
�bH<���e��4`p�$6@4MZ��c�W~�>���b`�����C^s�T.�~lH���nu5�.���'�=7?��},)T!y���tw�p��
�}���j�YD*k��a���Uk
Ev�l�������5�T��f���Yq|.S,*�����)4�.Z�w>�-V��U��n,!��r�C	b+�E��2T�4���C��E��S� }�������E��� _���	���W�?6n[[�|����Q��`�-���w����'25�K����}�h)�����o��<K�^GN9�/v�e}��,,��fck�>8���r��X���z^q��x&!Q��A�M�����/�p������`��{�c��c��	���Hh3!*�p��CU�|wW&�BHA��}�)	\,O��;�}�������} �3�����
b�i����iY�����T*�����b\~p����/�w+p�'@�gk����b�\�[�ta>8j���`bY��u	A�*��B#�a"�(�	��dM��2��7N�FCZS��b��G(�A�q��]�2�X8���Y������p�����_bP_�w��P�i�;�Kz2���`�ONZ����z��+E�p�����7�S74o9��(�2�v�)��s:�T��.��n�l���.���.E^T���e�I��b��f�8#���z����- _�Sy�X7��g�D�%x:�����w�r���F�����[�7�R��Z�(�����[$�Z����F�\����K����r.CU�t�+d��
N�����u�=�CN�`m�L��P�P>i�����4���������Z�&�G��V���!��h����X'"����J�1T(�M�����p�8�����'Y���'�� ����:G_[��B���Ui
���0n�	���A������n��6����y����ci���T����1��}-6�I�+|R��\xJ���K<�����M/�����uI�?u0������@m�&��/���������j��;�������)�2Y����d:�(�D���o��?vv��3�S� ����9fON�),�p�a�WC� ��_~��mg`2t'e!y����9�����w(�s���Iy��:u�>8{mgo��A�`��
�7v��X���>�������!�9�$�K����&��� z�,��	����-6���_�3q����E4������EI���8�����jN�:�4�u�1���d�{���k���v�JE�!�7�����}�r��ww�M5���7m.��k����~��E���Mp�������/�>���1�����`�2}�;w����w��mG/��������^������w�U���!�&��YL������Mo ���l�?���]��n[&q���%I����s�'�#���i?\B��"���k����+��\cJ[(�v�>�'"��PhH��w,N1.����4j����`U��.��i4:�9 �Fst��4�����,�M4�#�	B���~�����z���"����1��_B�F�����/�`!��fc�M�������G�6G�2	(�0�F�}�< ���6d8c>[8�-G��"��HL�?J�3�d�Ll�>&#�����It\�C�3\2���"�eS�60#�/_�lM�a��FhK��Sk�0��8��x�^�r)��q&`
!��URr�`}��0D~��
�;�^��H)��S��������T4���7�1l��Q)
[���R�/@:5�|�}}cc���;��H��\�������6{`O09�����t�x���/�(��v�f{F�m$�������m��>����\�d+N<��esM4��@�y/i�9���2!n�
L:b+Y8U���yn��w&8'`�����5�X65g7W����Y�qF�%VE����A���<C��O�����CQ_�wrhQ��/��S�s���gc��d����!6���I����N4���:�a�D*�aD&�BJ,������u:8,��Q�E�z�OAQ.'H�d8���w���S�>�P��V�/3��k��/

�3��963�^*S^VP��|��)_E�D�+�J�^Q>���nUmHT��w�b���
���*koSTT���r��G���V	��eD�}x.Y��
c������r��l������ev&��@Y�6�r�B@�\�9�pP�B�AxX
��B���:���u�	O	�Q�Yz����og2X3�B)��9��n���'9H8��(DB�B�@"�`���u�s��yI�{;���nY��5�|��.��.%�O����3�od5�cf�+�s�|Tq4�5��8��Zo"5����9
��s�I���|�3B����L����!��q{��@i�_�b
 k�$�Fbq<��%`
��|�`F��!�v���w���F��f�[D,�X4�`�=�i$�:z}���q�<�	�@��ri��<��_R��������&O������k�RX~��O�����K>����0
0P$�N$	\~h
�K���>�\�M��`�8��~K������r��X*�|�G��GQ.�xK\�t����L�(�w��`�Z������iQ%��,�T�4*YYp�l�2����n��<�4]0���31�j��Q�@���
?,���^�sK)��?KiBW��0��I�9!��X*D.M���E��Y��()EI������������7/��#@��2����1(RZ)o�A�v�tY9��.�UYSc��^��;XOIm9��]��F�#����IhqsU��W�799�2��I��FE��7��
�N����Uc��iz������76�Eb�e8t��G�\��aolb.�������`�E
H�UT���?}$R~Z�s�
�'�8�>�������������%U�������2��->4XB��#���@�7!����,��FY��{w�ys��e�K�n	P���&�,���~��b�1.Q�k��]B�������N��*���������`��=�.
���E[��J��{�bit����Q��j���������~���k�`*�-���:���'t�[|..�7�T m�6�M��xvj�[�v���+������9��P��������|�=�����	+L��b�G�p�8�/,C���>b�y82H��R$y��z	mT���"��;��l�����1}
,�l����T$Q~��|���C��M��������B_�l�d~#�7_�Hcp�F�/V7R�R�Z[N���2Wa(e�Z��Z��Q9v����=�	�_�k����&/��NQ��G�b��,��RY�H`��4�DE�t�\Ye��A9�L<�L�J6!�������|��Y!�a-\+�B��)��J:����i��
K�WL~��-c;��"���9�)r6A��)���c��D�d��I�0�R#�0�,�W�{(`&Y���:��(�X:_�a������<���v*���g�U�+S�����J����_2Z��!bM��61��'5S�	W�1:'6���Zs���aS+	����V����i%��G��V�*F����|�E���������1�3r���3���ZgLKu��\C���&����u��a�J�����4����r������S$�S"��m�h������[�+���������F�����n4p�4�(x)!mq��?����N���
��!�G�3cT�VZ'Bq
A4�&��+�N�M��S�S$��y��UA���q�YC�T_�M9������
Te�W[9ny��hC<C��b�#�s��e;�Bj����P�F�|����[X�ED���
3,��n$���y�R+�b�����c��S%��}���a.c�K�f�6�sW���\�d;�e�P��
�\�j����9����Ic��qal�EOZU ixb#��T�����y,r���	>���]�2H�)��T��}�����;&�%J�R)Sx_^�����S���Cv��k���z��F�=W�t���t}����
�X��������������;���M-Z%�Sg0�Cl�hZ�nMb�����|�\��)V�JCf��e�K��_�����z�Zo4����y��r9t2.j9eQ��Or��O���O .A�����������]���^C����z#���]���+����l;��\�!�s�=����A��9qN1���b>�E�-����lz���[����dt��e@.�R8��E�I1Hyu�L�����.�A�x�q�r���j1D������l_��/���7���A�����$_Ni�����^7��(	��X�d9���4����m�1�U��_D��
/��VV"�~#�g��a��������|�����z(����
q
y,�ad-=������ Z�;������Y�_�����u=@�oR��2d'���g��D��^��9��
������b�.�~A	%}��/��������lw{M����9��s�=���S�_��x�v!�a���������w�<�'�j�u���������T���R��1D�NI���?��N��:yY�i|����
�s����x����"<�+�CU2�q�vH	O*-�&�w.���.��D���h�����Ca���p����d��x�/����N�i+�
H������P2��11A�y���_��&��P���C:!�{�qN����=(����+|��"������A����+�4��DI����WA�!p��!|D������37
H�ZXTc�{����~�j#�w�_�QO�P�A����J����Z��T'S�.q�w���C��$��N��%�����!�'���,��?de��.�;��	B��r(��k�Z�H�QG�������go�]�X�^�>��y�
�eB��R#Dv��	�]�o��og���Q�X]�f�m_��|]"�FG����_���u����^��0�)#�t��1[~};�.o���d�����M�J����O��st�
�����V��Y��
eSb"&�p��M	47<m�t������>v|� �� ���8h5I����y����#	��|Ji��G�[q
v��0k�I`Q3^�_N)��� �r���& )�������#����y(�������U����^�������Mp����R�f����0�v�0����u�K'��2����{����K�tsn�����}����}��0�����1$j��T���6�k^��|���h�1�����bJ��R���it�����5�[�S���>�����d�-�/�XwP�����~/�����m���jc|pt8�[��g{�N�A�(�f������XZ�o�����'��W(��x�t�����!Y%��>e��LG�L?��6M�YJK����Q����W{��^����W����c�x����s��y������[��\��Nb*����nk\������pONrG�ih��O`����FQ�#�����{-����������90r*c0��p�N&����g8?�g�����$|!���3�d����1�&��3L���A�`R���ZG�z������Y�\��A�q �����Q��	���ES^#�|�t�_�>�^7G��#��+gC�)��g~�me���D�$��
���6����h�.���%I�O&
?7%�q$�'(��1���O"�?7%{�F��Fa�Ga�Ga��Q8G�$
��@:����7������������;=A���>�A���p��r��y�O0��2\r5�o�I��ie���U��j���+w��{��P���<3h_����d�1w*���s�/f�k�*�v��
������<do0q�M��w>a�0��3C]g�i�����:����4��6�&��[S��Q,(c<���������}r�{���G�	~r��gA��U��E���^ s�����/"
�4M����z�zO���d]�+K��d��KV}�"S�|d�I�2U�;?1��.��
���*�X�b���
��������;�\�@����������'�'�$��zr��0^�f
2���\��WY�g7WW��p�gy�(��d:�.)���,���������^W�\��O�,|�1�(��S
�T�L=��L=�����u���
oq�1���,�1�_�U�����a��}y��
���)�(�����+f�d�Um<X���������5U��j���R\��Q]��]5���?C�)��OVo�+����
��3������
�����W�oU�V����I����*��j�
������T�u��j�
�z*����OU�V��������L5����JV�p�Pz;��P-�}���F�������q�"��z-/'c�?p'��jut����N��Z��&Sh��E����4��.�#}�(ow������[����������4�{	H��=�������60Q	4?����������w}��AY���,�A~����W&CG�L�a���y-N����F�Xw�,�.�2M��������.�y��G�����@r�����}H_��#�?��G�
	�J�P�!���q0����[�C��3��FA��ea��G���e����h��o���-1$�&Q�:d|y��Bl#:}�m��mg�KfC��w����-��c���#A�9�_i�z�	�w(�B	��t���64��J��}�
@�J>������M�e!���O-x�c����kt���	�����S����O1��*�����$J������J.�-Z~���1�+>�AXN	��C4D�"������G~�=����j�=����7Y/�:��(C.��QH�P�g��t^�[n�s�-�������Q�)'iQ�3��SZ�����Z�#U����?����Dz9���ww����=f�X������J��.�B7B�E!D�O���8���m�j�r���>R�/��dt6��������o:����u<E���sL�6��m������B���7�x[5F���Z��8�W����b�(ZP�J���b�8f��ON������R���!�0�U}\����g�4�Dq��Y���%��x�~0� ��	eC]����(��m�yK>s�Vh�,c9��[1*��(�B^	���/
�_�~�\+�DW�R%�*
M��''@C�&;�}1&�N�b�s ����C��<�=N�m~d�3�2����($v�(>[YC&�ZXH���7eRh�B��t=E��U������5�a�*�\vJ�7���,sKd�����bz-�����dy��FR������*1{��������I�;h�����+�a�����*b�B����c��|8
JX>��WA1L(�d�c?2�B��>��w)������9��:�������e���I������{��������y�M?��g9�^�����\��Q�c.�@�cK:��/���5���(�qb��m:l~���U�L��FL>�I�V�P�� �������� �;W��K�}��D v�
x[[�D��<�M�~�s�t�<��{��
���b���4��:���.=�c�U*�?��A�����n�3|���� ^*�����j�l$Y&M��4j��f�);v;�b��t2�.sC��4i�	N/��;�7�7bf�D�X�r�OM
��1R�`�[�:�g���}���\����S������E�\����G(��Nc{�w'�|*��Z!�&�����x���k������d�7s��%-�����k��~,(&�0e^�W�*��XU��>��5*��m�Ip_�
0�\���#.+��8 fd�2D�h"�iQ_Q0��(��KL������+�PCB����"a~��y�����F���-�
��pr����W���l�P
A�P������0����,��}�H)T�t?\u^8�������A>��G{, 9@/�#5�	���C��$\�
m������O����X���M����u'�q��z��EU�IfN���Ej&�`���,[��@���1/�0	����FKTl�I
sTq�:
x�@�Db�ZX��;JRq�8.AA9 �U�}w.�����o���)����R��R%�������E�q��xfYH0}�H�hq��B�6{wL� �cNP�%@�@;���E��\�}� �
���0	����0���������5R�D��U?zB������z��zGzs#����`�i������TN�aK�kX�=�a�#6��%���:��B"������I�$�-1	�m��/V���������{��Te��>�����QU(8	~��<���7B
�I^-��So��t����p�����a���|����Zt��l8_i�[��wI-��T�
g*5�&�NIEmr�[�0ui8��C�P>R53H �)�V�J��������a�h���]�]�]���p��^���m��F����]V��J=o���A�A�������Y��,N%P���"
����b�A���X����TX�-��\{y�c�3U�������3���,��N,���'���lS^�T����^6*��l����Y�=x
J>��]���>��it/�ej�h|��3&����?�#X�bQ�U��?
�2��|(��� ���!�b���@#8�*���/����eh4Q���( $%J��W�,>�\��@���A�~dZ�_����%��S��D���P�	����x,��'s�[���6DM'����l��Q�"�B�D�>
v��JK����8�jN������Ow&F�7�}5������6�R���������X}b�M�n=�9�0���-�8��/�(�
Hk���e,=�r�t>�������2������Q�J�x^F�3��O������E��Z������{	T�n���R��?(Dds�q��C5�X,Q�_�/
P!A��DV��C���/��_�����x6��	�G{�@������x{#�a��{��n_u��������a�����`�k���)�)(`CT�L��@T�Qq���!�����-��X��U�{
J������-�l�dl<�����AJbp��a������"6t�Y3)���Y�j�-��Ua�5��vU�@h�����.�q���rz�L�P!��HRP�f����f�[�����|P��w�2�Ph����~���J��Fs��]q\��k����Pz�7�"�[�l�'T����n��[����{tLAb}��W�I��F�J������>��me��/~+x����d>����w=zR~����V>5)��(��M��T�z�Js�������BO\���M��|G@N�Y�P�s3�c��$u'�z�*��tB�����%��(=(�A�Q�#�����iM'$\��5/k^�����xF�U�#���oi��2Y/�/	k�>T�BB~ �*����z-�k����Yw"��|�P�4����Z-���#r�o��� ��#���&�_��s?�xw�� %
���j�+IJw	������A�q�%�vg��,�x�)JTPE��y����������e���X��8Pt0�A�
Ym��Ob"e�N�]�9�f�)���2^��/�-�Xm�E��&�n'Z�npGs�������&���a�m��9�J������c�Q�vD9���������N���g�RES�AD��d�p�g�cBm#���s6Fa����0�-e<Z6��.qR���LWH�'���L�0NTg@Pv[H��
��y�11`�/B����jp&�g	FJ�c�i["�0�V����A
�Bl|c�AFk������In{�����sI���G�����g��|Z�&���<C����a�r��x���,��WTb3�P���!V���S��e���w�aq6���K�qq\+�(OIq�`�? �������.�����M���
����iJ<�����������
�o��g��K�R/n���G���*����E��h��C����H���}���n���u�c��0V��`m�RpnH���|$3o���"�'R�7�B5��T�x ��S.��ih��4�<���&V_0qE�?BL�4����=0&��2����9����Kd?)kF>}L��������Z5��.^(rS�dF��W�@0#�E�-�#�.u�u�����l����Xr���X	:11 ��� ���(�r���#�;���<%�q�c�E��������g��'N"4*�@�	0�0��R�('%
�G�: ����2���{��-5H�p�}HLf�I4�o��)
j`%���XpR��}W��d%}��L�(f�������@��rO�PY��u����P��8��4�x�"N��b�t�98�,��}�], ���?n##+��n������:�H���B�N,7�l�����T�c���+�Uva4��s�C����R���,H`/F
g�R���I�����e����O��W�����['���	�JtmT����+(g��e��N����;������� ��IigX�.�x���a�0��J`A�v��	
�sG�V�d�y�YD�O{��`b���I�)�|@���+�K��!�mN��?�W���Y3r�D�����GoO�=c�&�?�L^$}��������Q��$����l�f�./�i��i5(�z�ob�����2��%j%���)�A6��
�fS�K�=4�w���P9�@.��y(���2K�)�t�M��jhd��/@�.�M��0���,�[m.�.ME����N��P�&�����&��������/�H��]h�CZY��Vvz���y^0��S�~'�N����)-KW��P�;o�������w.;W���p���N��E���*Fs�.�sk=�X{���U��������vC~z����F���-���4*��g7�t`�<������8����^w������E�����e����7�nHc?����
����b{S9bo���`�:�&Al8|�+V�y��l&���j�"�N����r����%t�\G�ixD�wC��c�m�N�962�g��������-�N�k���z�%�Z����o�����>.���DT�2��+�(�o	��
�y����pq�x�a�J����$t���u�L�(�?L�Y-D`�Z�Pt�����^�1���-���I-�Ue������M���\������gO�[�1u"�T�@7�����6��\f�r�\�����M<�
��MW��'��GPm�O?���h8-���w��!�
�X��7����G��8���Y&,I��q!�E&����
���r��������:Uy�I���
��+���[�;��.s��V�����#}DCktb��B����K��&>����A��ed.|$;RU��5I���|.�jr�$O�z�����S��2kv
U]�r�;�K(JN�+1=��Fd��e��T�Uc������U��T�)��N�3��;���&�d	���\I�"� ��o���=G���U�|���e���t=0��U��F��%0&O��rA�qQc2����4��(�t����>��y�3c\>��';&s_�����m�����we�����6�����b���O���7�6����%��[]������ 9�#=J���0.�k���L�
&�J`�$���_����,��R�K�I�I[�&���6�L[T��f�E �@q==?����3�9�x�UQj q#,�]����_�;[�=.zWPh��h�6!�'wZ��j������l�����ZKp��N�22��ly����|t�xb����9j��Q0�RzP
[1�G�_[bt��`�� ����CMr����\f:������M����dB��>�je����v�����z����R�s����z��C�fM:���s{�������B*���Wq��=K�$�����7��0��2�W,)���l����}�qxO��n�/{�p�E=�U$��3A���R�CekF��MWD�v=T
sz���B���^�T��le�b��9�;-_u��%3*�s+�%��)�K�x������u��G�xp��*{����Ly	�!?��~�pk����S�s����Z]�|QRj2���������
�
��(�:o9L@"L�z�+r���@�����.a��G�$RDU�e����{�e���������?2�U^�d��Vft��YUC����K}���a^bq���|��X�����T�����D9j`������*��o��O\���l�<E��:���8�S�����b7��PJY��iEE>���|e�X?��C��e���n
XR��e�o������^��6����d�(W����X�IG�W��.��FP8��Q��LBh����'���$����&�$���K��C'����N4�|%�W��\�����E��}��C>����@K#M�X�d?|O�*�+C�`lEI8�&Wf<�o��t�J�]Gkr�����>HT ��0'�
����M�90�L[�%L�%=�}�r���[�W�he���9�;v3�c������C���W���e����H�5��/�Y��ld�/R�u�$�z[�Tv��~	$���I����|�9[DFfFrUuO���.�����gy��U��!$x6`��o��KB��l�P���*�^�8�kz��R�[�]F�������!���M7�nd���m*����H��?6���!�-C4!3'�=��3LRnd2�.{[������������90 R���1R�����$_��9|��{�2EY�d�|

�?����%^�
��_{U&W%^����<�z��A*�����v�����X%Fs��x�G.�R�Uc i�����)�^C�::5����+/XM�Z�B���E�]����>l�8�~gU!E:�u���z�
�!������WX]3Q/�O�����]�:�c�Ud������8�D5�9G�S2t�Ck+�$J�\�I�����P�?����`����GC:r�0;�uZ4k�����\���0���L��f�m��h���]G��K�F��Rm�*m���T������t���Gr��	gH����kC���'}��0�����4�\�6��*�	���H?D��R�#�	�a�L6ar�<�����D"7���BZ����5�����iIIl��%�sK��J���y1��f���&s��sz�8�$�R���n:���8zg��`6v?u �8 ��I�}��A��N)�
6rr���q�����n�*V��g	NP��Np��������5h�+��N������M�BV���d:Y"��X�a�p@!����J��.�J��?B�����vN�����
�u���*y��:�H���'��l�=$ �$��^q������K�:��5�n�Xn(n���Z��WE;�t����~L>.}���wt�A��>���~�7�,�zI.@���?:]��>��7aF�����~p��K�#;Lp,'�K� ���u���V�*I,������5��	B��/�E�J�6�7,�_xS��}r^=s��l-Ms��B������lo�c�d�%�`6�3����B���N4�;2>�gwA� �
/�������U�P��T�����g%c�JG
������N�A6�T���:P���}�Z,I��9S2�fx%N���)����2D_���������X�&������-DX�M�Axu�F�'�H>�PVc����VCV-fh-&;��������Q=sk�tG�g%pi�����U���^�����Q��6�88	C�G����)��n�H���K�[���W��D���ul�N�-?6��j������caE�@m�Y���J����u������wO{I�xa��u�!�	{���8
�#��CzDU�`��c�4�R���q�����!X��N�@�7��@l�C�  ���������G0\��O�`.���0�P1�y�F0qtf'~�Xx/��E��`���e�s�;���r^�^���K�q���h,)B���i'�������T�9��WLKpsGR'g���>�\�&���W�T$�n(
��Rv��6nu���~'e��dy'M��H�B*�ko#�������,Zmo0:t$�9zoak���������>��n���X���s),$~^r�VJ��)���2�{�C#��>������{J��j{��S����������;wt��rZf�vrw��A?.�&�	�?��8Y����>�� 9���� s�x ����sW���������.������������A��v?�{��QR1�n�R�wx��{����>�l��O?���o;g��7�\<o���Q9mt	����SFXd_��5{h��P�5c4�=��HX�,	Yk:��;o����Q�k�G��G	���1� ����95����*-�����Y�RA�c#����W��1�V�>@�z[�W�
&���Tw$�i��(&�b�P0�����^�v����%R��":J���
�yk��5���-q��C��|���<g�8V����?ES��/W���Z�����{i�YC�?
f����-���O��w��\;FE�<����q��H/�1F��_cz�OI��/l����n�I��xMD��t#H�B�S�^HXCD!���q�B���l�=�e2��0>)��������}�	�x<
��,����-���B<k��.�LM�/���o��dql�S=�4@��h��k]�4Q��,��\s7��Sn-�h��a�Y����fT����du�F��_���������y�|�L�]��u��|!��~����vG�����rx������G�#��#��C����sQ5����p�T�Bv@ur��bv1z`)��(9�\����D��M��e���QjJa�#�l�=�p=@�`�$9]Jz<��$D/p�^j�;-�2�p$C	E`��C�y4�!����tE�F���t�e��Nc�5V�$�������^���Tle�/�$�4��4RM�����{z���gZ�h�������&��w�Q�Z�����.��/:�?������C����;���1��{���99�y^+�rU�;g��9��wP3�
P���$�0T������� ��m#�%K-����;���L^�BqR�2
V���V�y&���L���d�-�L�12Y���qY�^�����Fo�J|r��5kZb�d�A\T�
�e���:0!�g��((2��'��,�A�1	������HQ���Q�L~��9����k��!����P���dd@?�U����ct�����=d��9�����*����y%c�`Y(F�,�B9���?�0KL�w6r��&V ���
����<b��#r�EF�����|�p�tx!T����V�2L������0(A��-(BxL4�a�����%�j�8�B�{#2�(�qB0�@l����Sf��~�.�qQ������e!�l0�J�t��!\c�FtI'e�j�`�)#i�`����#-�R��"���D�@�=�}�A!>rRr�5c�Q��
�i	�A���!R�aSgRX�"}D�<Nn�����a?��a����p�UJ��-"�h��R��
p���"t~�����~��?����E'Rr1����(����`��pDa4��=�y$�Q��6��L5����������p�c��fq���G�%��U�`��D�@eYHLDK
e5�u3hE�0	F��<�p�G����i��g.@��}�����&x����x�1�PlG���������*^�����A�|J���CQ%�^@��{��o�[�t��d+BB���j�rFu!~/-y6����_-��5N�t<�������t��Q�����l�*�� �&��w"�)Wk�F�Q���p4l4�-w�%6pA_W~��F��`�\�7��KV%�F�����O��l!�5z�7�����x���K�aK��;��W�Ul}�\����N��{���xt���}7M��h�1�p��;;q
�/�m��e����dB�b�_L��,lq���h�7oN��TBO���wy��?�r����RH�6��qqW�xn�����l������C��'q
j�*�N�Vi9�%7�v��6��/>yC��d���q�F
2i���j�)�������|���$y!"�E�����j��=Y���*1�v�-P�DPF�(�5����}������E�mb6���������)����]��\G�I�Q/E��������;&��!1W[K�����	��������������j93��:����COy�A4������4'�U��z"�<���)X
p��Xg_��,���w��Q<!f'�A���@ns��ex�Y���U9��Q�
o��1��:L<�?N�`T���5H6�w CW��24�3�A���ZD~�����v�e7��2F"s��PP�B��5���F�S��l���IS@P���+(��E���o�#���T	��W���p:�m��  	������3� �k����5��1pxPRLf�y��@l���	gD3}�]<�-$J2^�x���`�w/���r�y�BJ��6�5o�U&P[�����1�����J(�I��Y5�������
6���M��I���M?�|���`k�-��F��
������e�R�z��1C�������0m�!oAKzT�TC���p�1�sC�`�
S!}oD�\hS���K����+U���x���p��Nlg�C��H�2�c�x�#+��#�&[�d�)F����,�7��-IE,��D�� ^60������S� -�]_����0;����@��6�.��:�yK��������$����M�y��C���;Me���=��pn���z��l5�5�1���e ����XB���I*�VR�����wzs�9G�H|�|�M���MpB���FJv�nNtz;$�L&�
���o�9�m�rOy������������LL?	���������~�va<&B�8d�������$��`"�7��������V�-���V)���Q��6��A���hhUmT��k]�Z����ZD
-"�:���g(�4��=
�)��������?��^�O:�@ ��H�X,�b��H�=y@S�HM:"��,5��Re�|��5Q���h�_��R-l/�u�T��0������0��,��,P�����`�*��Z��P�6}�T
��5����Yo������6�^��Y�����NEdQiE��1�]�F�/4��w'�50�a���f�m�I"-c>&QQ��L�?�C3�m������B���R{�����B�@��W|�^EE3(��;o��� _}~��"y��n�����g��Ln�/�����j���a�����C� ���]��;F���F�Fk��W+^�X/����<Z�����\�Tt���:.�R!�(H���d�s��y	�p��&��K�a5]N�����]<��cjwq��&W,����M�������K�+d���}S��2�������_A�*r����eH�Z��G��)gtf�s���e}�������`�r���)M������6N.�M'�UkV"���}��m/=�&��\�<����d��JS�N��~�t�	E-u��P�e��px����_�#y� ���a ��W].JrU�|�_���^KBF-S���F��(pv>��x�������L��&(2-���`�;���x�"dat{?a�%Jk�5b,�u�����EF�WI�En���tF�SZ��z-��`���G3��� ���O�dx{��;?�NP0���\[f+��4�@���Z�Z�*e���=�Rj4��j��d)(y2Y���T�W�~��Lz���4�%�u�4�=4���	�A"��N�Qt>���"�O,�b�{7���~�/����������7����/��+�z��P�1![|1��!Z�dD&�l'���dQ�at�V��9E��k��k:�ON���[7��X�����K�V�=l���W���p�j���&O���L^�D���?���a�l_�!���Pr���8�m��64��^�x_M�O:LR�#e��I����V����RP������������@�"u���c��cb���e����T�x5��4��z��6����O���Db�Z,�������#��&p���}x������O~���}�����;;ys���\�#o�;7�����~�����$��a�����0:M����C�;L$�i�����F�{{����](���d��5�$��3����y�{@�tE����U���]��zc����x��E�J��h:��J�p�W�v�|H��N1�0 FHX��=u�X�l����-dJG%�k�"�����
lu W0@�k
����[�xxR��~W'�=�b�;��Bq:����u`D�?Hb�����F�>�-
A�������4A1r��w�>q����	F$'C�)�~:�jx�B�|�8����(���p��4���pr��mP��1*il�7.�7��~)K����V��6��qc0v�F���^+$}-H\�2�`�o�r�t/�7�c��IC�a1}rK8
*/~W�M2��,�������?(��@!�#�;���FRXT�����y�����PF<�F��!�7��],�xU(8��e�Lq����	��
�]w��6�U	���u�������
���F�V5z�G�ye>��4rT0��=���xwc`�(����5�$#�e�J�/��������z����M��7�P�$�D<F����
L_:m�K���G��p�6�"y��6+�-h�YC�����t:WY�qH��k��0���������9��Wg�~������=���|��m���&������<�#	p>�.�.Oi.M����s�6�a���}��R��.Uk��,��Q����-����H��:�':	N]��h��*�`�\��q�;`�g[GT�6L�E[�E85k
>���h�����a���%�p4Ep�Z��G���N��E����H��urr�J����E:�^c-mUH(��o�����dr~~c�V���)k�|��I�����V�g���?�����`������o�|R�i����rx��&8��������U�k�v3u\w��nQ������&�?�)�0����	Dd����d�q(�o��d�c����7�!����V���Uz�D��X�ld�������{'F��{���
��
���dpa�����R�����LFT5��r:`�|��{h7F��%>���(	u���|\n�m%��[���F�7���J�%������&k�s�1��keu��d����7����5�U�����b�1��F��7l���f	I��|K��j9�h�����s�2MD'=���6��q-=�0Gs���c�9�{�y�v��8��>��.��y4r�7�i��O�9}�i���-M�Y��,tc�����E�N�=��
M�����.��}�������-�O�q�@�t����y�V����F�>���(�����D$��!���l^�7]��������N�����<�]��]���g�W�����un{W?v�oo/p�"���5���u������9./����~�_�_wX�l>��9=�����������dQ�E_�W��`0��a�8l6�����*oa]H���$,.��!Xt.��edD��Q+��������{�R�84��r�|�-���D��O��9m�����7��7��mX�����sTf�b��.�;E�ZI�Q��#��B���1�>�j�J�V�9����V�=j
KCw#�D
J��H"��.C�(W��'X:g�����&z���w�����-�����hm�7g�S\�F
Z���O��|}u����I�)���Lb�$�ej�����m�}�[�|��wO/�D�wvqz~v	��Y6���R��\]�����uJqFik�"Q��/��}���.���b�{1z7
�I����[*��a�
�P{<�F�r����TZH��R2�V�L��Y&E�2s	�9W~u��x*�Wp�PG�6���,�����]k��w�?�W�%�|{suaI��>�?�9u�YKY��(�C��s�'�������h�!����|�G���������=����P��v^�/T��n���h���[����c�*2���$NJ
m��.�a��.4�K�����C�uIv�r��69��y?���u�"I�{)������������ ��n���r�|���}}6�Z�S�K����Yq��D�MH0j�����w�xw*�j�X�����J8)�\j�k��R�V8Y��e!��+��%��+��`h�9J6�Q����r�^6?��\+��%�\+WJ�z�[X�Rm4�����!��+�wC76�gm��?�E��"P}\rG���=��a�s�^�^i�\s��v��l��83���zs��tJ����V��b��l���3~���O��'����OX������;�X{N���5*)N��j���vr%(l���'�t�_�;���N��_���Y���(ERF�A�;;'�P��?Ce�:��."���4D B2A9�bJzR��H9p�:����|�iI��s��D�z�rZv�^�3�������G,?NF���
�E���������|FL�h��h5���s����L�t,{�
��������l���������
��?Lg�G���Ti����	�e���aHF#w���>�h&8R���W��(�BR�%G������D�e������QCq��A@��|���	��4��f�+�/�����
T��hr��V�,���]��h�"�����s�?R|������T�����	
\�s�&w*�Kl��V_kB���f�'oY�8-z^��� ����X���-q�J�
*+:�j�e6��@2���&T������XA�$z���
w���'i�=\�	�N� F�X7Y��@Q��.h�N�5R�� ���m�;�5Y&�\���	�����e��������g����W�3��*�r���**��p�@����.!����(�@����ru]m����J�#�^`���8�1�7#�Q�a8|W(	A�)�
���5�#w
��8��W������>��7�vp��|��-{�cH�P�wa�;���J���B��>1�Y6�A�0��]�x�&lM����Q�>A�:����Hd����H�!�2:=y�,=����d�Z+�>b.��"^w�`��U�l�h/����8t��W������_-DCQ�(��ks�f��a�U�o�E��F�U)99{6��}�GO&+�lN9�2�%C�D����)�ZP��K������:�\2���}������2/��B��h����l��	��W������&����������� EH'5W�5F�\������k#���s��
������)j����\0����X�dU�*�8����g3������������h8!���d�4z[�*6��d6;�6D�B!�s'F�XF��Z�K;c�s���&*�<��Ie�xe�p2W�*�����aM��iV!�n���P�9#o�������E��r��7�y���NR���f?�2��F�]r�#wT,VK��W-{M���r�-�~�E@}@����WHs�V~���r:q�_���~������!J|��>����Cn��M����nX����98�)5?W��7���p+����:��=�,�����*��EZ�}�Z��%�M��7�����7
�G|��s{v�k��o�n.:=gW*Q�?�^��~��]7ZY^#"�^��|��>�+:����{D�4���9a�6
�7�Y���z�G�1���hc2.�Cl�GW)�(qCK��@�9:b�S����wr��r���)��p�9������z���x��x-���c���do�vaa���5�N6�������F�^,���a�Q�y�7Ky�{�%-�X53?���go'�Ip/�����j����W�<�D1���IC��-
�5�-\��o*��"7����7��n����<�.:��w�lB�ado�0�Pg
B<��z&��cN/g�"8���
���/�m��S`%�<k�0�)r4�'%^.���
Ats�l�x����aQtL^���������f]������T*eu���-�Y��W+
��-�-~��J�H�:%�!��y`v����.wr�B�lE.��9����i/�X+|��r�����t]��S����n���j��B[���r���0�g��M$�Y���������O����/����n��Y�=��d����2o@�K��B5����Pm�
]tER��1O�P�ddx&"&���,asQ@8��'w�'�,=���)�Zd���+�Z,�������F-�e5�{�z�h���pxLF(vOLrv]Qx����G�Y�&��@���B��2��R��?���88)���;%��c(��c�v�e.<��<�>��c��<o���g�2�	����G��q����M��r��1m�t`��g'E��c�N�+1�b�v�������h/�f����a�����7�{|���6nw�7�z�L��I���_O���ga5�����*&�1���-��F�j�i��k�G�vQ^�bic�\j�d�����)�������G�w��9K��@�H����<W�<W��x�v�L��S<5����L&�-A0��M�B>�3D.*r'	/'16N9����=�����B�,�)�yI'���r�����g��������I�������]w.��eL�c��	#G�s��RcxN
��f)_.�O���>�(�K8
�Z� ���T��f�i?�8$�&5�� ��;:z�E7<���c�JO����0�*0A*�{�����o��%��R��Q9��E���,8z�	d���(�5��v�^���1��u+�`���i~�k��9�u3z���C�_8���T�m�����az���~#s��@qr���J���GYu5��X?)���Wt���~�T)�Y>|)W���n7�������;Xf�o���(����~#�nO�L��(QaD4�da,p�/���L��p�{)���R�\�
��"�$��i�#�L�J��$��Z4�_���Q�)i�
i���k���i�=����G!��@��%	���H���p^�!-��Q">�;�����o�g?��6�YSiq���[�y�*�b����E�;3�C
�_�� W`k��wa���,���	��<a�J���o[B��D�w������Mca�f�OZCo��#$J��~�X���#��qw�1�XY��VS���+nd���I+�I�1��Ri�n�Q��j\w�YL�����G��4�v\����j2�&X*������F����f�=�FY��i������"0VoT���3�|��xGQ�J��� ����=�k`��:���z���P��f�o��0��x��|��h�<4-��[h��bh�p!����!�6�V�����{�z
�n������c'��6N[�)S8������%�B��O�]�f�� ��`��� �S"��Ib2���$�JwG�Y�������0E���;��"��8Kd��i�r�J_�4���������c8q���*LpG�N�@�Yl��H?��x�wJy
������������9��������?�����/N/�����n�LH����*��q}t���H��gx��38}fK���O'�"��oT���|<?�����g����lY##t�Nf)e$���
���?j��RGyF	*�h6����w39?������7�����C����\=��{H�� >�4�X������U�������h�+�s�|������K��}�!�e�[�M��=��������N�;���Sz�/����K���N����&NK������"(&G��w��Z�lY�:��Y�cA�B��t�f�I�pO���@y�|�5D�)��$�]�B����H����_�V�N��2�N�7_�I��-"
��rpd���(���������)�t�?��
�3�m��h�H���(��qs{��fC�0p�Nz�i�{+�,E�2`���tqr~0aH3'g����I_����5z�e���p�����;�A2{��d���qUN��:�1��;Pb4<�co��i��G>Y�E@�(<�4A��H�]�3+nh��bgZ���L�I�]U�x�B�;�,=O��"�G�+���*U:B1�>�qY�Mvp?/�\���e���)#����`iJ��?��y^*�ek�SH�����E+'�%�C�.���	Dgu^����5���y�G�7��w���~�����\�bU�|���T!U����%v��e^?xK#]���Nu�<��0��NSi��h]�Q��k���(�����H��H"SH�/�	�_/��I����;d#&�F\��a�)��_�ih�P��dA���?�'\\���?R?����������(��8+����e��_/`��"-l���?6�Ot~����'�W$0������5���"o�d\��<�\�y�����s�ru��C7�/��!p����+b"��~=��N�}t�����p��~}�O���8���s>�b�a��5'��R�8����:9M�xH���ND�s��$��4�����q��$�C[�����a��|�4�������'���G�]�(���j?�P�J����n/�G�}V��9y^L����������I��	�N��o��\�����,��+�J}���C�Ue�{,G\,J�N�p%��bQ�������G�����"L���w��jT���_(��Gr}@�R��S�[
�]��>��^<�����y����E�|�?�������F��D�p�<�r�; OM�[�3���dgD���!7��)\�.���0r�����;{�D�&|����9zI���=�W��=�����z�����E����{of����"(���sv�g������e3�������_���}6��w��*$��j�^��.G�r�Y4F������K��	��J
� q-��,��;�V���Q�&��D�B����b�{�v���Sh'!u_/4#�<!��O�x�v��&^�wQ� 1���8��v�{"���Yj]���U T�}�8*`�T��e�/��H���-(��>F�T2LpO�v���1\���$���x|(��h��5��w���C�����!Af��jS�C%���^1x
Cm�g�
u{�UA7x��R����hA���qj�>g�+��2������M1��}\�'GWK��7�_����:%�0�P��LF#�{�i|���S(����:�p����fz��0���|���@�;+b)��/Mk��p���=f_r�!B&�x9�j����A�c�at���^��t���/P����j�^���J��S��GY$�%C6'#r�������(������;����g
6@�3�u�L�������q6��_��������,s���]���q���`5O��A��G���D,��.�;���A�ZbD,�����t!��0yJ�M��=Z��������[�!��K�2E����KnG�?��|��k���B,�*�2���[�jy�������J�iR6<��>;��	���c^I;�r�OG�������Q3nH�d��*#=P��54��
~CaR�*��G�*D�K�O�
Ep�����$,�����M,X��;m�=��.�	��� "DXm@|���x_0����h�y!�tY�tDS5!�9�8��j�e{�p�|�	;���z�rBt(�xW�R����7k,X�������k�I=�S��2�n
'��i5�R����q�����J�E�x��D��y�����FQ5��;C9Yx-7b.��i��E���T�f��5�CB�Uv6S���c��a�|b����'��~@@;J~�P�n$�Ei��4*������6;h�<~`eX���5j����f\F�	��
��gp>�G��T��06�.�@�@��KY�H�Z�i�-�j�e�'g\����e����gJ�G���������_!���Y��f�:�Ev;C�c2��6*���oV`�!S����k������U��h����w�����DBP.2��fQ�*
���XSVB��K�f�}����H�q^&i0���H���er��(:�\�6����f��1��*C��8WBQ��2mf�k��V[?|�5n��D���IJ�6��<*qRr���")�~��oM�����)�W�Z�b�W�����&e0St^�U�c��pe��V�-{AZ2����m�8��`�ol�#\�d��vh ;����F�8�����`3�	Y�jCsW�� ��Q��:[�W������%Q���in�ytZ�;�Y��E��>���K�Q�S�,��wp�CU4iB��7�� `�l��327@��2e���&<�C>B{��������0[�`|�S�v#�V�Z�R'��5�~�,��
�DP�����}:��/"I���8��q�� b�b���S����y����������8�"H�4�d����E"+:��>�4��R���i&XN9�g2�Yb���\�[.F���}4O2�����i�8z*eH
"��#��������a���=S�#��A0�2���;|���j�6z�'�lv�L$�/����`
%��/�^�/�7{e�����~�r �3��aEE
�{�w�P���M���Md�6Hc+!�FL�����y�V��Z���}�p��Q��������V!g��@�P�|�v���TlD��v�����������D^��Z)�M�f�x��d�!7��qVu�8���T�B��	n��i'�k�,O��$�W���W��N��>*�/���vI��l���|�a#�#���u�,����d��3���\�?���m@t�0��Gf�d���I�Q�=��16�.��)�cq(�"�SwF�	��)�J��m��-5j�J����������@S`%s�|���c.Ig%��T�0m�}&�{�3L�>b����t.��~{����G��v��^��������O?�x�����/',J����
�
���s����z'���>�_td��f$�AS
�i�}k�(���f~�f�����h9���Y�w+��f�&��\�S����"�V��Q������KO�-�MTy?���7��P�;�	��E���mOK8v��b�D�����n��ml��h���;�������b�sBJ����&vP}��-9�l�3aP���l��4L
��N�6�0l�b�QZZ�)O��ON1W r����P^5�-ib�B�L���X�o���u������B
��5�X��T�a/h�Lu>�
{�|���1KyuZ�ip�Z�=�{��������Q�s�����=�B����Y�,����
�A4�/��D��{]Ll4������-j�'k���Ox��!t](Ar�}c������._6)C��S�G��!��>���_�ZeV'&������$���(L��H�	u'p������*��@5t����EHC��� Z���%��`�I\'�@s���\�_iP�$�������!G���#��`����p�M���Q��o��^���S�|v9+�	i�����Z)���~�������wg��|�=,LTw<N����Q��T���N���8�>��(2���DVqhR�G(��p���_)���*�HB�z��%4B����z���
�?F�rO�"X�D+�Z��Q*���m��(a+�![���o�{����i.P��C�x�^���CdzJ��Yg6���h�I�R���]��o���m��G�B�1d������P�U� c��J���]�b
��fs��;��a
���f2��3�n��h%��)��e��6DLk<+�2L�e���8��_+?����&�Yg��������zz�:/7gX�����G9�h���]d�#	72�+�n
��a+Xa5,pc@�P>�2E]���ZE�"!�j��X@���B*��l�T�����m���b6����K���}\l_d�<E���.8���k5��C��+���q,>#����8W1���l,�%�.#��	Y�9�5���x�qVt����&T�"����m#�3��"b�$���x�3��1�~�/`����3]��
��(l�F��
NI�y$�	�*3�t�=Z�E�C��LzTb�%����!h�T-���*0���w��\]�<[F���Xl�"Wf�`��`2�eC�=D���@�Z�Aj$L7�2	�O$.7�OO_��y{��#����lX9D�6Q���N����R�n(�D�����WF&,�b���q�"
���\�Fb|~jme�J���:��J�!�R ��IUHx�f��s����S8�>�4W_�,�m@�;�c�2���~��#o���c
s��|5��LC����a�<�`���T �i���h4^�D�JEM�j���
WKm4b�6�9�h����nYr�������|��|��"C���{rAa7����KSvxv�1���X�z�_g>6��I���ny���xC���|���:������=����[,�[�q}\�n��^�3`��h� ��_�$;�00{d������M{���WKr�99����u�����~<=���W��������kD+�n�{F�x�"�>!�wNN���w�{�J��/��Xk���^��y���ic.����}6��YV�^p���$��N�kAc.���6A��LP�y�\���\�,�����������0a�D�&��3���O�����o-42��Bc#�u��x[o�>�8>�k!L�0�+�RuX��Z�b��.����vk�(l�
4��C��\3�}��f�5��xV���;'����0\��y@N�;�������a&UO"�������J>�7�eX�~t����C��+y���U�M����9���1����aMDhy5��8	�������
����!>7p�2�)��So�b�����S���|�M2|��*0YR��@;Qu��"T2��kH��e"�2�]��q��
�5(.6t+��4\/���F\p�q2��-bt�+���v� |Zi9��.����l( ��eOb����d��4��a��^��:"�
��M-�|t�a���nt��"4���E�
�j+�[l��b�(�J��B�B��Aqp�a���.b��D;W<;�E�r����'Z����L{��S�L2����'c�
�����>�
`��j�b�U��F��L����$��d�����I���0�
+���u��������]w�]Lf��v�x�BT=;!&w�b��B�w ����25��(���;��!�5|
kt�C��@�Gl�q����/W�=XH��n���,�`�H3�R������L��"��Q������&�l8��
��[n��cA�`t�����=F��)�Q5��_
�(�t�t
��1�p"�f	�,,/}���TlN#���T�a�����jX��`������Jn �t	xX`l�����B���@y��'���4
�N������\�ek�l�2���Z]!��r��(��\�]�}�q���KN��t���y'�ENy%�� $���39D��"i�2S��L%D1��_)
��bq����Q���"�Hik�"�����0�	�����Q����9���h��/Uj����z(�j�]�3E\���#�1X�
�����{�?�����~o���D����G��
u�����l�v�����th�� �,]�����XY��+���4=��lW�R(�k��#����
U�Q�7��R:��JJR�-9h��|�x&���]2�
��4�e�9�\�q}��D����8rP/��:������Wy��B�P��fd�	���h�����)������3�.��`��J�0l�����<nt8�V�<J��u%&gt]jv#c/2��E��x��[�)���E��S���'�#G�pa��B:��7g=�:`�6�H`�0����4��m[����n�@X��'��&�����i���cxg��k K�M����;p��1�����cl�kJ!�J��VJ.�G��Fy\�*�Fe����42���R��J-&�����,Bs��f�00�-�/;���d�5�Zn�|����Q.��R��f:z��>��/�4z��������2(�_>�b�k���}"��h��e�����H�E�-�,���#A�r�]�������e�Hhfv&�N����'P<��=n���F"[n�`������nJ�NY��w���r��{�����3��q����^
e�G�/�ec�)���b �D�aR�Epu����[������,"�u{����D��=��v^�&�!:d�]��=�!�����^+��p�� H_8��"�BP4)vC�1���,���j��v��Rn�l"�����[�"�r����������f��D�������@ekx�����&0�FM��
dh����Q������{�Q�(;��g�[�w(�v�&<��n/�z;���R���se�8��mS��2n���vk����2p��V�����-J4��-Ra5W�)�����>���)��VD�~v�\�i�E��x<���W?�v9X���l��r��K����-;l�Y�����7�����>0����hm��+�-��a3w�=������pl���������4BF����;5��I��FIj�vy�n��R�8,�F����m*��e%�6%������-(J���]�A6<�p�]2��$x�;#�
�EFdL�p��!�z�����C�v8��|��������A1Kx�Hd�M)�:B'O/x�$R"����`�<��lyXX ���M��/�(�.�5t7�b �"���;��I�&H��PX��6����=-����*W/If��qN��r��3r~�9���w�W�u9�������WC��G.�J[�	���aU����������uv��\�Y����`<=x8��W��/�RL���#5����e���X����E$Ok0;d�Qh�X�]�W*�����Q6�K.�-�d3�b1�UH���S����j��VC���/���(73|��_N���+"�C����J/�W���5��b�'"�(����R��Y��
��n�V��g�r����;x6-���v��|�����v���6�~�t�8jW�"G�l�y�^����\<~B��� Ya4��-��i'�;�D-������f�������5���H���C�:�"��<���"E[PYTp��Z��/7�!Q�)Z��=N����W)���<�B���h����1�4�����;��N<k���tD������&i������Ye�%����~�u�L��?|@H�S��Y	�;�d$����h!�����	zY�qc����n:���TB��ye>rN����o{'W.OO����%�t��������c��yZ.H�n�a��$
}��(1�[_����X�/�/����^q���0�\��J����KsQ���p��
g<���[���h�lm�ORQ,��z��5I�.�&���K�.�G���n(��������7�^[z�mE�9���9�����<��UnM��1�
c[)���Z�m�w���������^���^]����b���tz�����f	.��~���&��r���_w
Z,|p�&�d#`g7e~��0(5y��p��e������[j8Y���<xh#7��nOzX0{��J&�;\bG?��������c��O�2���e�$���^_�O����x5�b/r�E�	�����V��L�7��g/l���E�T(C=��9{v~��a2���t��(������5d�����wS��$�'�}�t�J{�3,|�x*�������(H���t	�]��������7�6qWi�������GWo�b���D�9����j����i�1������ua{���R72�����/zm�L%}ka�W(���������v��)
�����`����'8����!/�8�Zb�!TL��sn��H������~�V�{�VG��.���N��M/��M����|���|�����r��������{�H��s���}�Jk���0Z��-1��-Q�n_��yk�i�pn��������s6���6�uk�Z"���u��m�����^.|��k2�?�n����(���^� ��s��������w7�+��by�A�G4v�;���|��������9�U%Eb8�o�H���D;���~��D7�����+�����U��*)��G��}����
�}��/�]�/�j���gP�X���H��7Xg����y���P�6�����@e9O��w��?�6�sN�m�������_�7�T�;7������Mh!.���1���������� K��u\)�����g�H��������|a������vo1�P\k���Ii[N��4����E|s��J,�,����U+�����v��([�FR������OHR�����J�&*��v�s-��%sKB���=�&n����h�ZaL�Y��2�Z�)o=�_�P����t.JB���A�H��kL�"i��G��D�B3]	tCC���N�>��
9"���H���7J�}u�]�t���l��
WSw!`Fj���w�L��|t�v�6_�{�*����F��]��j���
�����2��
���z��e���lN2p/*�#Q1�Pw�O2���T%"T[�����:�����9b�LNj�m2��$a	���=�jv�(���?��4�h�hT����>������)V|�|
�oN�C�kc�c���F'g��_`i�������67��AH�*����MP��};��0��<�3jw���:|/[u��R*y�bq�T�q�=��U����F��*)����e{VS��Z���~�dR��]Z'����X>��\k���o����0J�V5HO�I���������sM��*�Z���j��"����x*E�\ebeK"t������+�@!�A��������$iX��])*��r�B�Zf�v����nkS��C���T���(7����&�P�Y����zV�6���0XP_2�Li�q9D�� �gq�&iJi�w��o8��>;�L�L��(��p�eOF�(*fc�+�����e��cB������\n�����^��C�1����+i-�T[R������o�a�y����2jDj�+]MF�%�e1����:��e�0�$��g=����������O�89p��<���@���4��4,	fy��M!5���m��2�/�~�w�\�kX��o�R�����HV�5��+�{���N�x��6L���C{��������a5�Sltl:}��-��G�\K>�\4��G��7#5�)���3S��0o37�����
0:{*	9h���&,��-�}0%������z�_F8��i-*w�_�F0U�V��~���i���;o����A�:�-[!��C02v�=z�~��������{$2L��]�aOZh=�C��5�Pj8>K[�����'��c��~d�z�����m5_�Ca��
�H�F#2PY#���l;�1�~�H�P�m(6���q~�)�3?�a���8�}���Fs�r[�r��h���r��X�8�
Ir;�t���a~�+v���������Ws����/��l,<!��"�+2
>e���9��::�2�8G#��Y�� rf%\��d����m�6��V��g*bC1�*P�����1����
:9Y���l�=�,���H@���}��OG�\M�)R�~wr~~v����y��[<����]��d��V�=j
KC�qTnUZ^c�Y�����IK"��6����\����f���_�p�x'����Kt��9=>=�1�}���D�-����0m�/���Gp���c��S:��J�Z.�wv����?R�����.��K�R�px����7*�1�UTX�
3RX�I��Y������ZG�����r�^6?������N�V�@��J���*�F��oN�2��AS��������G����E�Sn6��Am<n{����
������R�T�f�U���l�]o�R�������w���3[�38X��������������[,�a�]��9�����>(����A�zPo;����P��_�;���N��_���H�NH��"K���,8��,������\2'��<��w/����oN;��k4�Kt}�#t2+p0�������0���Y`�J��B�������fv�c�������m��+����j���V�:"�������Hp��;u
�-�K�gx�=R�oJ-
�,=���"@�
�p~�;�H�9\G�������
�
�Ne�����,-=�5����Z���#a����C���}�O'C��X��f�It9S���Z����{�R]�f=��bD��`�}���K.��������_.����>kx�^����9��G������������f�/��,�o��_nG_:�������y��o��Ww���N��;�.�xr�8TW��!7�?�������.KegC�@�@z�P�89��g�&��9����Y3$��a-�'=���������D��,�q�2�!9�_b5��h��F:W���<}'���.Op���auSc�7�#�������0t;��D��p��l��Z��X����t�2��q���;�0��N�Z�(�����G5��
{;��Dlh�h��F|e+�[��m)6N�Qj�ju�-�q�>lK��j]��p���LI~������%��o�D�N�>�q�,����`�*�`n��1�oH��%I�+^��������^�	e���:�UG�&j�J������(p���i��4���!�kc:�����J���f<s2��O�����}K)N&C��Fa���|����S�x�yY�=I1�����P����w���_�}j�\���/S���j�w{x�2,���@W�WX7�F�^�6�Y*��z1�XW���Y��Q.����!C���h��-�����.8������<�9?s�^�M��J�J�C�y��D��r]���z&�'6!�1�}hgvj��<���]���{t����f��S'��]�0�����::��g�|���Y!��T�T�6�,Z�\�����UF���#'A�H.GV �2������q����N�x��O1G��xv�jSrP���G�p��������^��h'�vs�#q�Q��t���uIH]�MTk�����5	�Pe�pi�u�+F��A����������a�T�B����
�G�K	�g>
�C�c(o�%�"[O�h�6�*�r���JiT+���p��++�T��#%:a����h���j`a?�4��O����������}>����w7|��;����
SXo��bp��A�4(�F�V�yu��6����l_����>�_5���P�������O�M7��D�������\����[z|��c������,(����+7ats1a4n���@�����hIK��O����;��h�������,�F�@9���m��_�0�K��pK�V��������q`�t'�q?�/f�
r3y���j*�ar,�Z�����-C���e�z�_> Z������
	Y�`�{�;j�Z�V��nu�z��h��Y��r,ai�v9����~��l��/
�y�VM���t�|zp����d��|bK0o�
K����i�����,iX�J)��y0w�(!�
zm60��E1�;.�^����%�$���p�����NR�������������$sX
��c4�L"�h��Y�"��]*B��8�a�pq3Oj�o	vNqly���"@K�X��:o���m�f�G��
���^byB��J�M�;��u�E��%�X�>�w.d�Q����3�53����kc�bW8u 	-���	 ?>|���(#[���m=�EW���>W���U�X��
���$<��K=��t��0�|:<������S�:��k�����7a8+&���1�������8����
���3����e��2h�]7�
���7s�h��/?�F��-���2OZ�\Q���������Q�j%���{��G�\Q����?
4����
0%dF�5�tB��K��9IC���;������]@�lR9���k�U?��
��M�����"| %YW��pyGV��Q�@1��1q�t�8�+���@�%L$�b���:wDp����E�q0#@@X�����qF/���aK��"�g���p=�^"���nE�C�����
�t�������d�lY�x�M�{�rAJ@���@=y�W�3�"����3:��;Lx�w��+�IHQ�F�It����Jx7'�f�\��h+������/F����;���8-97����7��n������/:���=���;�q��@����D�G$^�vbA�*7a�U��[��v+������J��3�<�a{�}5�(�;��8oFj�P�I�����	Pq�P���v	���g��N(�P���"\�0��U�W����`�=sc�So��~�i{a*!&f(4��A�������L�CF\|�7�|��[`�i(R:u,oH��	)W���B�
a����M�����S�pS�~c�������d��k:"w��|$��\74��*Q����4����$0#�K���p��W�)�>��=�"~,���4�����sMz/�@���/��^b�3�Fx��y��6(W7�������{��
��c���5/"	���a�'A@�%��X�����$��L�Gi_6
v�w�Kz�E�m�1�yb��"���V�����^�X�UD!j���7:{#\	T��p��I�=���1�a������.�%��wo�g����$M�
��Og#~~:v�ETgC�%H�,�%4���ce5w7(P��h�J�l��������;�n<����}��U���$;J�S����B�z�9Z%R��&�4g���W�b��8��yQM��F���o�rt& �O���<�s��2ml��g�=��n���MM�;���I���	�$F@��-o��(������q����N=9�����0w:���h���F������6]����J5��r���V�^e�\l���I�;�g�ygW�<�D;<���Fhg�h�,����Umhv��tb�
��Q��7���d�O�t�N���gf3o��a<W�W�a\�`��Lm�!E���c��6�������������+,���>1���L�� 8N�ia�N��?�b-)�� ��	��*�}�B�L��������Xm|U/���������q
?����n#7G��z����/%��F�i*m�}n����h��k��xM�={YkB?G������.s�Ig7�{m�V~���j�������tO-����%����|��I�G�I�w��Wa�j�R�Tm�b�:��a}hS&��c�*�HC�am����� �,���K
M�:&�(_���Y��2/r�Z��x0��p5vp�*r���4������r��
����w)E8���i���\�������H�N��=8d&w3
���E����E�Y���
�7��V�VL���R:�j�J�R�0:g�^-7ZU��iz9��+F��dR	U�0������
!��� �
��.������lH��]��ww��.rs�]����\ ��R9
����h����oH$�t��j��T����US0������
�t�C��|��kS�?�
��_)ot���������s3����=�*���^�$<���)���-����w���
����P�r0
�C�K~pe�c��~w��W�Xl7��AiX/m���6��a,��(�%�ne��Jz&,S���L[�8���8�<9��81��-�KKeX��|�\�R�\J��?�4V)�����gtN'�x���,>Xc���ng��Z��p�)C���b��+J�&��evu�SU�\0GQ������
A��}�E�?����M����F:b�K"p�WU�'\;K�6�oF��,"Ihq"��LB��5�$�!^����d����`S\���
�H]�J�rm\����u��!~�u�_Ja��"IH�.Ev���!����FW�s���9��@V�C���)})�l���TJ!9Yn ��jM�������)Nr	>���(����+�r�r��e��AM:��I�`O)F����������!5L��k$Y��K f�f�w��ja����5	��Y&{��$���/���(���0�M�����G�q����-F��ya	��o�(7��f-��P��J������#��viT-y�V{��[�V��K�F��+���>�#w��'�?4��G�D�N�,'f��Cd��s>�D��)>�3c�/��J�F�u/��;�����3M'A��K�������AI������M_��l��)��$���x�A1����������.)Q��T���/G!"�H�FZ[m�E�1b9����~�^�{����Q���?F;��:�}$��r��02z��:��."p�k�fZ����A�9�%'�l�}��\�;a�0V#������Q�p���+\ ���F����<2P��>�2P� �0��r�,j��!��s�D��,��Ylt���2���~o��C�X�������+��-��!��r�Mq���6�D��	*�P3,J�G�q���C�=9Rf���O���/|��
b�N�`�k����PU$�����3�p�����\�/'�a�[L}�.�S����7XM�p���_bSH`����_�}�TE��l��e�n���^����k(Z��;7���M�������e������6�F�p�����@:S�f���l��'W���"�7]�J~�4'L6��L�i"=�b��R��Q|%�$F����y3����!9q���"�SS,��;d(��P:��"�����9B������?~���r�{2���M���t_�c��%!5��^����o7�p�����x�e_p�)�z��sd�pBq����������
{����<g�����N��"{��"hD.�4�Y)rOq���`w�F�����������g6}� �����b��o�)����;Z�zFA5�@�T<TRN����j1������T���:x�P�R�d2(9�Y�����9dS�p���f�,��wv�/�>��0dT��J��X�;�q;������9��$���w7C������a~��>�iu��R=�hu�d��(���o�����j�p�)=Q�n�W��
��B_H�#~�\|6_-�h�
�����������I�������-a������k:9Y2���z���P����3d��3*�p�Ee��A�`A���>�8F���O}8���3����&��;2\���C���g�����1���������P����`5!|*�r��2@Z�"�0�����r^x�����WBIt����*��u���ips�v�2��=�1W���}���ff#C���F���G@:Ga0e�1�r&#��s������OG���"9�`�������}����#��	�v����z7�]>�������_��	�Q�H���2�(6��m;��O�l�x���b�:J2�R��l�i�e]QTje��+�	P���T��:�Z^��5b�Hsp���hm��>3�DG�.;��*�b�u���<h��X;z4?��L"�=��S08]�^�G$k&�����G�t����(##^%hA�J*?^� ^E&%�1��h�����-���#����
�N<'��#���D�`t�'_�J-��L��.��a)	�v} �v��b��jo��dw�}�y �1VM�+�;RT�[CR�!me*�,�����&d�������F�����(fu�.�������/������)���K�����������I)���:�t��G��t��m5~�"����@d5�{��.H�� �+N�S��trb�]L�Y���%����21�<r�5��}%FC������12���a�S��t�g�!�9�_/�T�+��=!>��|�`Cxb�r������r��F!|�I�K��0���L�B$�	(�19�3�O9y^�4v�i��cy��R�:� $Ko��J�7��h�f��^8��-#�����M������������|�k(X���R��T�����qp"�hB�:5�#��[��M�*�V���@	��a�TGjW��
�6K=6Tx�0�wG�@0��"s���~�	�8�����6�c��/�)��m�"�EK�	�����V���3$"@0�(�������f@�v�s�89��kuuu-����^�����>�Eu�S�7����}���$i>+���I��8MG�R8D��
�9�
:	`��	���U8�B�%l~.��(������!0�����W��1g
���8����^���`�M������y]e2S��)�1��<�������������R%�]D��er��b~�S������x	���]��9������35^�6<3^�iUZLZ`�HW�J$����3��7���1�H`�x��#��1d�o+�4?;����N��>���?}�.��D��6�t����\�S��C���������F"�R���g��Q
�GN
9��H����E: ����?��W>����������B���9�	U.19q��O=���K�l�y��Y���yvt�xE�������?L#Yz�~�>���B&c�����m~6<;@y�E��f|Gi%��fs����v��3���
�L�V2�W$!�:��?s�����'8�����8^l���Y_1���m��3Am��^U�.��V��+7��E�58���p�v@v|b�Y
o��p�kE����7�f����>�M4�:��Y����-�UCA����QGh��3!�
3\��(����Na�������Z��{E��:��Ub�A��(J:)�I���z'$.�i�.���g��h4"�v�Mu���H��h���L���(�.��YIQ�
13V�����:���s�Z�O�l��*[�p�SHh����|�=���;9�����0W�K���>���o�9��Eki�*|~5�{�Iu���hs��Z����p�R�D��6�����Q����[����E�Mi|+�����/�u�������E���e�.Y/$j�u��@M1	<���9�KW��a~��z��.1�Z��2�_GY�R�Q#��I�p�������,�����k������+���Ke3����f0.#����C�>���B�j�Mn��� C�� �E�Rrz�V����A��`#v��T����m�'���:��4��{a����As��S���w�������j�D	;"&Sq�]q9s%�6��U�W���I]�;�����v��o�Xa�����Q��g��^Q/y�G	�!E!�@�w{������#h����.0��!���7h<�sO�[:C���v�xqtt�oS� c�)4�����3��l�\�i�����UR�t�Z
F�"
Q�#�D�t�(vP��z�Ze���I�9�!�%���B��_RU�-�~�WD�)u��V�g���wIm�X�X5�X�\�0~��Hf1?��c0��;�w��?-c�Z������t �����=�*�qR��hG����.�-Z�h�@�Y���gi3�������E�B��G��	=�o�9+����(d�V��h�P�>V6��`�-�`d�bU�8(8|56	����� F�]�Q�
Wr$p��5������3����k���O�����0
���;���������c���*�
�@pC�L
�:�<���<Yf1�'i���0�D�F�����b?@�g��n<�@x�\[���
�I�#[1��N���19p4?�\�&�G��0��x��p�S����b}���xpLCi���9���������$�4��V���#�$�
��;�:)��U���#dm�M??���������p0������%%�E�����'����#��6/���i��9��(<���p�e��OY��A����c�k6�)��o��0)�G�+��f�K�Iz�����G�Qzu���1��`<��B�G��+��=����Tf��=��r���E@�����50�1#�@�N��R��mc��@�i�-I����l]���G��#�;��`�!������������`�FO}����G������
����.}������0$�:{u���2}����d-���/��.q��
�(�u�#m��l��Z%� ���8�+��.;���6�:�����k��
�OV}<g�TC�(��E�a�H��u`�7��n���f���ucnp�O��'�L��~���1�M�<W6D�1�P/�FC�0Y������o���F�/�����g�~s���[&�C{���N�hoj���\��b����-&(�Dc�M��m��$n���M�c6��<�����'+�8-Ep�b���E����m��U���i���������k4-V�j�i7Z�7
`�G�Fi���N�:�Z���0q�Fl�=�����$����j_^�*���X_��
��$T�C1�h7�W�'tm��/q�*O;�~��i����6Z��w�����3���X3���4�.2�q�4Kg<���a�,�M���+��v�[�����s�����[,�?��E�0��isX<���DAs�����AFD3GW15)�m'�����k��`5qMgW���~O�*�����Lc��;=;�!o5���b"�t��k61�u�����j�����
�X�:�eJeXo#���PpKuR�}��`�<���Tb��%D�oi��qmeuCb �'8�2N1����7;�c�;��S}�#�3�p��i|�������Z��\���l��aoo��_�����%T��n�P��_������J�����+���;���~`p>�����S8B6��;�Hl�u%h6?6�?a �f�#u�Z�`[i����yqf
�;<Z��T�R���U�u����-�v��B��5����2�u��r5[/,�������t��(���S���E���T������j��4��3���T3%`c��c��ij��N����Ro�I<����a%��k�r��w���;i@���>�y,���YR���>~,��u�fpK�o��G����d6�E��/@.��O��@����t���0�<��5�5%��XM_C��}t+���l��8�i���$�&����P���'�J�(�9T����r�S.*)p�����K!��0�U����h�~��<m���m���iq�~8�4N�'��1�ov	��z!A��]Z��)�����������K ��]K�1P����za6��1��e�����zA��s��i����k���V�V2@����7�Go[��KU:s~U??�j��<G�L_��]���������.y������_�W�o���7�q��\��<5!�V�%�a^���8��5�����0�'�`���l�B�������*�����8b�����/�'�7�c���~k�������������'I���F����0+���?|�
�A��P3:�AS�m��w��������/H~��#A��u�R�e����;t����:z<4�u��AB��X�Ml&�<+����}A�?����L��&����{��0n��tX-:-�[e|��DTrJT
��Z�}5����	��<��v��;��(�>O��]U���|����A�v��j��2��q��������,�|��h�*�-�������N��W�7��#��t��Z��w@��X���W]	7�����l�f��W�C�W<G)3�v!P;=L�����L!<I���J0�������������yl�E��gm$��d��4j�����8w���x�:t��	#�����u�K��P�}�YF�#^:^��������34.,
r>��B�Z��(t6��������=A~���Xq.�*
�D�G�*�}��
1!Eu�n����/����q~����[>�N<�`���BsN4?V����}8�"c�UNk�l����q^�e����)���v��������������D.��\�M,Q��������1+B���=��w���_F
F�~��D&�k�\V'GWGp�I%Y��2�����,{�~.��X�\#���U�T��m#�KU���B�������E;2_k �f��� �R��C%3)������6���0O�������)V�7�_��\^�W�g��&N<�4�����U��\�O�YJ%���!HP��w���o�����Q!*k.��ay<�~U���6�:��������k�-%2-*����7:&"�
H�O�)�������l��%Sh�
�,�#�&Ce:p�M�#|$��p��o�I��zWeO���������F�p��q���*�������Y����p9�����:
�S��[��
���q�F	Z3U:�z^�R��W,V�|PI�PDa|��Q��K�����|��9���a���q�p\�!"� =�4��z��ty����_�
�{6��
dn�������
i�������Q��������R=���yp�l��HZF-t�i����o*���aB�h����u'�����<�W���t6P����9�I��^5N)�2�c���~vVhd	36����}����Ywc�V�R	�cQ
����/8���RY�.�c(pr���n!�1���G�����������&��WH��3'��E��+^2���i��xt�81kC����������5����b��AT��G���M3�������EO�v8�@��8����3:�����,����]��hgic6a�������/��.0��i�K��R��a8���!�����k��7(�h���8���wl��OsG�/<�7iL2Q(fl���a�Y�]���MfG���9��\u6�T����$�9�L�����h$f����t��������A��-��@����0�8F���Xj���t���.���H��<�!�~q�$}W"����w����0P�l��
$a���_%F!���V`�2�
1l�96��zs��A8���@�(t�X	��N���b���IB�}e�����[=��'�|��K��������6�GN\>Kwr���jA�HY�.��Fh*\�#`�6��
62!(����A�J
���a�x��������6�]��fzx���E�XnD�`d�u��3����+���+�t�^q�����f�:BQ�I�G���W=P��^D�"!��~�	��#s�y]wkt����Rz���z�nBR�������g�m����9������d[H��(��@@gn��E\���Wz�����J�S����}W����o>���`���f8��P��Rf���2�_�8�/��j�G���j��#�����[�F1�'p�����W�
�S��L���m���D�R����^�>���C8T0���<ctJD���A���f���Y�I�qr�{���p	����l�.�^k��L�^qS��a���0�'�COc2x�G�:��F�\����Qz��~S� ����t�5@����@�a�������c�S����'����T_6Z���Z���p�u���^�:�
��qHL��t�
7����k�PGTIrS�O0�4����w�Q�W��SD����;�����J�+/�������HzXP�
��q�������\38 '�&EoNm@��(v�S�i*�?X�,�(�u?HK���P�y5�DT��[Gu��C	\Bk�}��M_�?�lQq/�;L���Wi���-��K#�d���������N~�V��cJk�L��7���M�B7P�7�/m���6/E�_N_S;�$���pe��`B��Olg�*�Er�w���g��qq$Q�,<�_q'�G,U�@�����n�ftQ�����N1��������yx��*2$���&�r{]T!��B��c���z2�w��}h�������w��3=��Ezm�S���f-4��[LX�V���\5og\�
��(�T
-�%��!b�~�\4.��Q�oX����C�3to�xi������cw�0!����|�����+L�G(�m�N>5���*���������

�Q�`h����K/e�1s�+b����X���
��W��Qj�E�$4��h��uFg�H�[������|dv�c����@������?�C��\�[��rz�����R�����K�����%�����X@����Ap��m�,'l�����R��#�h�D�8\�E�%���a&����f�W�����h���Q������a���������-�!`������4��|�:��E|�i������lHD�9���>�Yh c��:"I	E7*Z�wi��03I�.����sL>��`55z�r��n�!"P�\U�9.��i�53��w���� ����c�uw�{��'��4P����~v���(K�"v�	����VO�bA���XH����#03b��/�H`]RDC�'������o/���wUr�Q���N:���x��A������N�[�����$kXA 
�k�D�����`}��r`�?���]|vWw7T��jw�}vq�x��7�e��s%�!�����{;�0�y��f�
�|;����YY�����x���?��TfN�>��f{�������Q����w[��Nc�����o~I���{��b���2��eYs�N)���`?�X�o��l����~���J�g�)q�j~��Z
DH��>9(|�	��������aX���a'��,��
O��Z����!�� c]�wvq5��Y�
�\�u3��r�B{��Z���
�.����@�g����p^ts�����n�^vNuDm>����S�KK��+ qv~���-�O��Y;D	�^F�����/S�8�*-��������}k��2��`������c�m�a�>9��.��@n��FJ����D�t����K.�a�C���]mz�|���q���Xh1DVIwL>�
=���s�3��"�����E��y���f�(|k`�����E.��U(<'�uM1}�g�5E
�Qn������B�k9�Y�3��w�F1�� �:�FF�����yV����k4�C�6��A7�E�(%` ��b����<�������u�
��	BW5��U�t��`?�
��A%K�YK��@aGs��V
GFC�q�G��1��d>��L�)P1g����Va�~�7[����!&��*DJ���"rL���������EN�(Ub��|�(�)��WI.�f��~����%�U���M��t�'~���$��[�#'4c�gS�3���U/���L>"�K'��:6�t���S K��8�B�1c�K0���/���@p&��A$q��P9��~u�B�����a����R����lN��K�&cppA�C&���!��������j�6���,��pkC���8��rJ�E��z�ta�c+�y��S'��Z�]Q ���rT1.u�l&���D�
#`Z�+�]��M������8�S��;�G���F���>����K�����I)!b}H�k��{��9�#��Ny�+Q��y9���u������f=i�9����F���|~�{,�8���U�=�+M||�cc��r��M��4I1o�)�q�b��n}�cJ@}0k�r�y��o\�[������p�
��'��n�o������t��X�������V
��T����N��r����{[=��[��s�����J��pB�@2��>9��G��%m���RM�wI�N
,	���0����f.��%U�"��HT�P\z����O���.���"6S4���1��b��y��z!���w�n������'tV���k�A`�
�s�J��,[��UIlg{�����$v��X��Jb7+�Xw��e+�����A��u|~�{K�����D6�8���99�#���nv��3���x�#CA���Z��CY9�����i<������G\0�4�#��Y����"y���L
�b��$.:��{*���_������N����V�^�S�'Q�_[
��A���F�����S��]��O���%���?�/������1�\d>`�N{��~oq����`0���dM�[u���je�����V��[�������h[{����(b(��Yg  �6�pHM����kT�����O:��>/�<��
� [�	�{���T�^JT���"��1uWu5�Fmm�V���O��mJ��Y)*�t��[E#��h��Z���l��Z������z!a�j�GV���|a� -�P��1�	������P��m�z�$A��1zE�dK�3��Ls�8o_���W���Zm��������������U�Jy��D<���[>D�b�w��+����@������:~^!�p_�}�}b��t)�W#��b'@�m�x$�^`���L��q9�����M�ay�������.*9f�����o>�\�a�K����2�i�w�3����,G��`�������	3o��H�C�h�������cf���N�c���C�4��?�������RF���1����j����R��	}`D�SS qvo�C3��W�z\��>��`�m��^��T3M.��>���P�I�age-
�@�s�������C�B�Z�{�L��5�/�����V]
 ����~�#������;7�
 ;Me��EuL�=;xD�[<JLc@~3��v(��D�j	���AH/����\L�ujdI���K9^�j���,A��<�(�X���H,���h��������r�?�J�����Vw�/74�|���T�+���kW�����s>#���$p%z*{�����1S���$Ob������=+����X/<$�2KI��(��6fZ1�CF\�>���/��M���.vC���v��!E� A���<7rGX���O�X���.s���s
���>\��Z�u,I=!�Eu��YqT�81�P�	|A12Z��{��������I����!��d��j��z]?q����F2���m��l?�x�C�'o"S�+4�qZ3����#��e_ej�w[PwP�W3m�����cbUS�����}��e�J	��i�0����t]�&��x����Z�z��}�}������u�CIx3g@'���S��mo��,�9��\���`���N�
6$VY�E�������Q�q��H�Y<����-�T��z�=������:ke��9��;���v��1�%�b���m��I�_kv������K�>^����t��)����4�
�������sA;2�����N�!{x�^�w����]�n"�G��
����1e�L����Mv���o�c���Q{�gfs��.�����J������~�����"�g��R�����/���I��)#��X�{8C{$�����m��]��>�B���oR7�*���Q�S�wW��*w���p���\��C�>�#W3?�t�M�����P��>@���sU�k(@W
}Q1����j�l�8>��<�Ms��������m�tX���������J���|3������Dex�)=��d��^�2/��-���
[[\6K����R?V���;W[�@zp��Li�=2f�d��#a��4���	����?�H�gD�����cP_�M-�dP���UV��&l��Q�3v����jC�eG�b�?@��9�+��C�����2���2���zIP�����&�vn(%��8{��.�w�<!��*A� z`�*���HJ�%�0k�K/�CO�[y�{[�rZ����\���1�g�pr!�
"�0����F����"����L9��.	����`A&�
�&��}��?�8s�;V�$���=�0��!8�YM��,I3��iV����_8E���3���
�)WeP�QV����|f���^�Xr�J���&�h y���L,f����:��e-.����q�~s���F��Q.f�kNae
[M~�&=�_>�i�y�@[�s*��z�m�L���23K����b)���oJn`��8�Zl����n���/��tLy�7�c�����%U��N:�dS��L2�����pP@���{K)w�����������=!���������#cUbDf������+�{K����-��'p)!�`~��A������L�����u������u�#=�
��p�9}{�%�W	�!n���QL�t����T��fa�1pB��J�5���t�6L^��u��!�,���"(�2��j�b;��*�z�-���pH���[be0Zz�l��.��8�z��_�F����L9��������$�7�/8�X�����f�nzlW���G����$������G�����+q-�b��$.U���";�x��Ohi4�������V%��y��$�h�1�]�#�!iN�B2���*��@.�|3qH���{E*��_+V#	�
�H|�����M=�@��#mIc���+������2���]|@���n��]v�XJ]��c�������.�>ZlL���.�XM��'d+��}��T�7DX��X9��i;��
7q���K�@�\��2�/>J�'�I�~0��Vo	c�m�8�0�Y�%��i7�������%�C�YBe�|�6w�|tk4�2�8�����y$M�\�,�����
�;���Z48�FC��S�i���Z������K�����cN�S9�o�A �m��:�59��e�����E�)b�0#���������O����&��>���
��s����\��C#f�����,�
��G����&�A���?���p�q�IL���
-��N2s Q�������W��3~�����@�����]��"��3�9.~R�����F�!7F*�wo	7�0r�����vI=��i
i/3������Z(�n8��
�O�O<�k��{-,q�p�J)7�����:W��r�s�2v����/�2C�
���0��R���XOq���P	�+�G�,��^8C�����C�HN��(�_����Z<|��w�el�,s-�m��r��F�
.���7��G�WIg	,���o�P��>�y)�
=l��ai��S����C������S��7�M��Z��={du7���,krQ! �(3����]	����2�t���]�C�C<���mG�n�9�t���4+9�*.S"�+Q��>	�b���}4����e�=�`Q��E��d��I&����',�~��P�-��!����[�W��u3
�����6r�����H��Cc	/
��X[���g�?q�8�q'����chQ����k'8F����H?����&�\�����AouH��)"���jY��X���~M�c�Ew5���(��hw�}�`�5����������nE��D�9���7Jpt0:�Ate|�#uKU$z��sgz����&�+��t��nG
������y�z�%��E	=� z+=t�)\'��HZ��~�@$��-�(�+�{�}�ai�XI���������t��FrN��<#��QJ%��S�����X���N�>�J8�)u>�8'�lL��5��,�d����8�.���^y�h�Jlf��u\`��28-��fE�����sq5���=���O#@���,9��=
@�\g�C�u��V���r��Q{8�C+��,��?a���������T��{s����D'b��&�`zz��Ds��sc���#�(v�'Sl�r��N���;8�k�[,K����jP��#�P/�������P�(��i,�������n��.��R�v��/Vr�<�:L��Y7o�'��av;q�<�2������u�8�q�H���;W�>�-�=0���
�tY�F�o�Bf<!o��9:�Gf���]'���7�f������u�����?+t�
��(�"��a�q|P�w\���u���wZ$�"=�����������J��o����jpt��5w�l�H�n�N	�����Z��e1�1�$��	.c��O_���<���������Ke���T;O�-�r��e�uX�	v�$���mr�AMR���c����.��~�,eVR���E�R�'��V����~���W�X���[|�5g��d���BF�,�"j��e�Yd"��{�\��;r�z�����8���8�{q*FWi#B�dF���)�zh���!�
��SJ;��o!�=�l�Q��<�I^F$���6�hE��m�u��@BNK��>��^��SZJ��cN�������'��LJPT2�(��n���tw*��reo�������$�Mv=�%���lW�{�@��1�!�8[��u����n@@v���w�&�L�j*Wp�	�o��)y��D"�����^2�$� �������������D��1�qJ�>_~Di���%��uM4��D�g��'��:��lB��d�k��b�����b���V��`��].��������j61���$��RHP��^u���
���6����t�H���&|#x�;�|���)��l8�����!�q�(����-aH��\t�����4��O|�c�=�b�^(D]�M!u�����"����T����&w����?����#3#����j����J3�4�2Ho�x�������P��)����v���������;�c'�����\rBj�� ����"H�������������@�)
������x���T�}g������M�Lbm<�zF�C��o��H��u��}��].�:�Aq���s�
�R�8�D����������tD8&���N����o-Xs�����.U&W�_{�i����V�����B�\��z��g8~]p����O��"�����~%���L�����x���?}��k������q[����
t�Z>Xi��cu��|��)�)�-wJ^�n^UvK�J�ZT;^��;�P�@��	9%�����%5m��^�d�xy7�������OG���
\�"�|��������f��tQGc������bmi���7��W�����g[!L&2��%�V�;N�~��uh���?
C�t$�X������hysG{����R"��I������-]eY���E���P�W�qo������G0�����YC��b�=��v�q�2��q<64g�a4��3
��	F�����5��C/�k�u#IsES����C�-;�5���!�s~L{L���K��D������y����i]�74J�'�<&oY=��z�'�_3
�5���n�[.�z� �����,�]d@!c�����4U���01�����S�9k��;FU��:q�i~�.bX��g=G��~��-�����tJ'�H���p�Ql�{���������LD�H�H���h�0��s���xe�OB��.��5C"�m\��D�V�q�x���eT���qGvS#���,w�Y�D8��:=p���������h�g�+o1�*{di��$x*��?!�;�>r�����&��1V�
�5V�*��*}�h��S�Q-��8!#6��iH{��NC�|:��gSg�R"
�����T���nnWkAu/Hj���f�e%Qf�Uk��M�VwQ�9����vt��RL�C�����+�
�6��<
���T�'�u0�����a�)��J�i^����w?}_o�U.�RW��	��RE��E�������L���m�x�96��6(�~���ES-i�����%���>�+9������6��5��p���6s�ot�6�����p��.��/m��<�.�a7��/>n�������	)�b�������j|��>�mu2�{����U�yt�����a�T4���TW�@�h4n���#U��v���&/�'��z�=�����qe��9�k��_�L��B���F����
e��(O��O������I��8	�?��oa�f�S���.�O��P��l�vb�_���/]*AOs�L4���fe�.�B
m���G����N�%�G������HDY���;~��z��U-W����	����Z�rP�~^�T�J����Q�^�l�f��n)�'%��=�d���.��Y�F��4�V�v����g��[�RU8�*����I���Ve��T�)�tp
�+��!��-{��Q���?oP��	:��VP��Dr���je�����{�����~���Wgp�j�U�S���O��U����h4�X������.����=����[f��O���B���6U��"����;�P���[��_��^��/����W�����_��(���a�H�j>Q�,mZ���z�/��SD$�N�nP��&���5��|�:n��_������G�:��Y7O��2��+���[�G�u�H}�#2�d�����oo1�L��;���x�5��(�m�i�#�H���a0���i(��,m��{D7@��H�4B%y�I�?��Z0A��t�2��/���#�]�T��jB7�+��p�-�1�I����4��X7���h+�mA�1�	L�$o�`������4N<(�sR"��=w�8!/~�#�����T���\�GQpK_BY�_��J��K6�M�@�tF����S�@��?�vsIR/e�x
��lR+�PS��zBg�����/�*f�}�Y��`����&b�y ����UZF�B��j[5��!?)����\��}]O���}tV-)w�������v��Z-��tS�,�R���k���M@�����	�����:*�'}�6�����+�L���w�������(S�)�V���=���$�����,?�����g?��'����o�Ei��OC����
��A�C��M��f�Z��O�k�O/��O�{�/�uf��������K�$��?���I/���w��n�PRG��}ai�O����F3����6p��������5q]�I��Xvv��j��X������_	�J4A|c~A�@�����!=������sJ_��<�8�y������~���#���u
K�lU��Q;<T�p�n��I�X��i.(�������I�/Rz5*���K�<���P��MA����U��QQ�ye+���J����^~3�'�,��nJ���<����0-c���>���e���*���A��
��^�R�U���[K~��LV+G��-�.l	�O���&��(�YxF��E4����S,
�6G�d�{��Xb����b�s�VR;��`������amwk�S���Y���5f�qjiZ���]Zk�����d2��D���:V3�
���=Ti�9�od#����`u)�G���lN���f���LH�������#l0D�
����edUz������~��\����Z	w���W%���z���������V�7��l'%c��WJ����Z��_q�����0+�a
K�fS�������Q������)<D���$�H�Ip.�;���il@L������zan��<�;:�PO��N�Z#�f76�f2d�~u��6`��'f����K����.0���4����'I|s��n��0�N^����A������]H�-dH����g}����@��i�����ps��J!cZ���c#�Zv�x�
{�i�� 1qdN������xT
?!8��:�L��1���8�D��ad%�������i�����kd������+��:l\r��:�h����dm�L���F�'�1n��������:"x�;����`/������w�p�����38��x�\��?������hD�;�F�l�?(����������������x8$�}"�^��6K=�>��uh����*�S�����\^��SJ��<��N4���	B	��!���?tg^C�<������vg��wD#�,QN1�@	�^9|"�+��|����$���o,Q3��8�nW�����#c���'b��5>�"��-�U��"9&���F�����J�C�������������p
����^��o49����D��3DA�	�t��ts��*og�W�}�������U���*V��_4%��E�J�������w���G��y=�z��1�B	X��'�E�yG�_aD*^�����&�%��7�[�j>�g���"�9�����5��������3�$=��9����${ �#|g�p���-5B�8���P�Fe�To��@�if�YQ����u�:���1��p�:������!�t;�lb�pL�H�������P��
��8do\��i��ERM�|+$
�q0E������^|�2%���������)�������6�E���������w���s��]����pF�H�[n�v�����nc������)��0�#�Z�\�S �4�k������D�W��f����K,���APFgz����mqXr�iQ�O1����	C�yB��N�������f�"\��,��>~�����|�VZ�[���mO�V�7Z�PW�|�Wo�W�����+�w�J�NU/me��bu�^4�%���~�Z��:���O��'�Ez��Ka��2FRHH!m��0
�p<
w&�r�n�N�������K��������)�/��v���jC��S���!�L�<2��-t	Or���W�q?�;eg&6'u�	*����V��:i4�H8+��y�2B�d��A�.1����yy���d�oC�ap=B�-�6�<:_�|}_������������jQW)=-����;�qR����:�T���.<��r<�9�����a&)��_X�y�`I�<~��)4)����3��o4'�,B��XA��=����|�?�	�"����}iP�2��{xg	Q�^�dQy�9bH��m������1@�$7�}R�	���6���jJ��n�>B�g�cdZ^�@d���cn#��a�*L�g+M���Xar�������L�oe���B�y���x�����d��NJ�^�x}�������=*,�P�!��YB+�k�����i����k�����N
j��U�,�c��]x������ 3������R�c���?�Y����7�N���i���	��������WX=4�"0���|"I�����8
YN�v`S+(�����?Qt�a(PN�e��|I����G�c8���u�T����S+eN��0
X�@�B�����������!���Mx0��]��EJi�C��c���n6�������Y}Lj'������U��\�����i R�;�+�����KH�?U_����j���/�u�]R�h��JcD>��k��C��0:d�q�C�\�J���D)U�)?O����|�UuGJ�
>����Xj/Tk j�R�mA�M�ut#m�����4�9�kMjE�v���l��\*���:��\cHL��P�
��n�R�>Q�t��!��a?5�KBr"��H�h���f�:[�NB�cff��'%�Sx
s3Rz2�cH%��Oz��&��G!��3
a4�4;���G0S���������r���!D�'��a0�"$J�AHE�z�)F����l�t���w�j=3Z������d�;��i��5*kP��>�[w$�X���C�5&m5A����K|+�m����z����zD3�
)Y�����pzL"Q{�pX#8�vrj��u�-I��y����O�8U��������l���O|������q��OG��<�6���K�8�W�%l�0p���\��o��1L<���*��X�}^x�rM���8��{��y[�����=��u=�u�0m���[�l;�_��m=_������?�]r���n��i�i�`��M�9�����sj�N�c�%�i��/r�F,1��G����V&������������$�����@	kG�|�V����'�u��G�s�kEr�_n#X���P�;�[�;���]�����hhpR'����W`�i��5��U���t�$?\�M}��|�����������2#��������L������x�s���3rL�����6c�<��������K�,���G��Y	�����-W��jX�qEi���K>d/
E���a��(�3"��-�������_�g����/��Tao{[��A;��Q�������\�;�U�k�9�� �(�x���zg`Ls��y���H�a^B�?&�2AN�� E�/��^z�g{ae9#<cr%��"y�
3g1������j���Wfd�l��zcIs8�F ���� �p]�,����p6��8���T;��.i+�]8��1b�HEl�������$�K��"�rZ#�ZKyRJ�CO��<�)��=���[u������(��'����r7R=yt�����|F6rsQ(�6/.�Y*h��-�|�������4���q����FsM?�Q�#�������G��T����9����2bQ/?3d���qR4�q@��P4]��w+ym�R<@��Zm������������p���-
"����T;C[�m��t�q�Y��r��� �L��
�W�CP�����y�q��|��GX_�#�U��(��#�����&�#�
����C����k���KK�e���g�Ec�������%�#r%��R�K������c��-:��Z�V-�������k��KKA+��y?*,������)(���/v�
'QK����������=),�H���Y�0#=�)���46�MH� �i5��+/h��{��9���H���}�
��R?�������/�{=��������~�z�����y0l������1���M����^�?��1����Gb^���#��
�����������������PL�l��UhM�QW����Mk�c�
�5��sAJ*�}L��x#������<���*��D���r�r�0�)��������~w~R�8���� ��e���jVK�������>*S�]�V]|������x�+-|a���|�W�����d���nW[����v�Yx�
g98���Mi��&K�n#>p$gp�w�>�kTO.�_�2?��)�W�e��+v���>�9�������M���������J��k���C�
_�#=O�����7������4���Y�
�������C�����W�f����i�2��|�[����Y;
�}��:�~�0
��H{�=����V�1�8���#���$���~��U���t��J�����Q�L����)9^V��$��/�f���o���!��������e�";����&C�>di�)�6��Q�2��]#V��3j37z�K�0�>�"�n)��%9��������x�i&����gZ�:��q�)L!��y����T�A8~(YT)i����?��������H����* �-1�<���g����X'4������YJ��k�$-�,<��Pc�[n�Z�|Z��&�x�]�.O[|yU4��S�64��� �������:�-VT������m-��e]+�u�W0�p�I3�CB����#wL02
0iJl������x�cV�k�a��y�8���Q����Qmv�@�Tam&V�*f��Lo1�5g��Q�51o�t�:���������/3���Au�XC:�����=eY���&I���>,fK��bQ�V�>K
&Ka1��	���8���)5<J��$�zVxn�Xx����vZ<DI+j�3d�sY����<v�����	��*-�=������t��Tb�{J����������4�����|X���
�
�e'���O�[7���O��a��n=����2�%2Uh('OS$9]�kdk�:p*Z]��
��G}������g�v��I��&�L�[�M��d�=���5
�g�:��^8�������9����H($?E{�a�]`��.��5��?u�G���L��#�B
%~B�K�!b��������(W������/�N�N9��0�t#P0'(����m8�!U�4a���p�|D����nq�0\�C+_Pf�a2��a4�b$�Vv������*��3�l�v��\��BY����v���M���Hc���9��='��Y���r
���M8�9'�|�q�<�/j�X��d�S����HZ����}��](����3O����`���Z��_��{�������~��TQ5����f�������
?������.�����S�R����^�e��������lP}k��b�WG�+(��j������7i&;�m
�D8�pE��/ME%QSt�I�5�]"�g�J�M�)$����]Y��Y��tdU^33	����u�'j6S������h��y;�"��&���q�q���A*��U�{l
$����������]�n���1_����x^���M�.��.���o�M�Y��V%�B��_+VD�i�������z7X���g��+LT$	�(��{7��	����z%��#
�Vj���P#���%��R�k\T����������1��]�l���*D=��jq{����7��@H�i�ww)�z��J ��?�8��XZLJ��@���[UQ�80�G��c�bf��/�tOH��a������s_��1SZ�X�0Y���mx���b5��Y�P$M}���#��8�(��Xn�U��b}��������� iF��C��0�Q�����X��2j\:@�GT�h�G����;�I���a_�4���:h���$���#/�[��HG�\(z����5D����RP���q��}Q$��l�$��V�~�p��%�����Tb�Fvtv�����������vj�r��>88����p[P�_�]�7%�:H`���V��~8����4!�w/���0�������r:JD]�(���z>)���xD��L�����/�����.:�����w�;[����
Bz�{���No7�8��$SQ'���z@���8f^�(��(7�'����S0v��f�4�%�A��u����+��A����']1�(���{��f�n�h1�@Z���	�qQT��������xl�����k��k���p�b#*9�[�mC,���������KT���������S&���i��Xh���
����R�_�u��1r����a��Q�Qt��B�4i�V���i�Y?=;��VA�<&��4�iO~����U[�dB��$��g��A��0��7��*�O���W
�w��-bR�{��������4�|�R��B��E`���O���4p�v������J��D�	�8��%��\��������.�l��S���='�G�&J�n��%5m�����'���(�Hb5���z=�[+.��y�'x� �'��/����OZm����Wr8����������?S�ZBV_���X>��4�7��i)������8��a<%��7�������P�#����95a�
	���k.(�����vk�{{���A��U�t+����
W�*R���"T��$�>f������	�����J%�
�k��Z�d��u�"��"Z��Z�*(���2K���8��}JjP)�*I�z�P� r}�����`�����	�8�M���)����?-�*�(�VF�
��g�\��WNKm��{{��L��6&n��^J.���rU���[�&�}��k2���%Y~��f����MV���'O���?������^����K ���a	�>�vJ�_�����8����Ve'���V���O��?����Wj�j�����;��}�S
��Z��{������w���1��6V���u���Y*$K�dI��1	Y��_A�(a��C���r�us�a!�@�C�A��������v�AWmR�������� �:�
J9��.��Q��?�����@g>����S����b��	;s�]���&������b�1���n��
<0�jjN-7�h)5��M2�������y���h��KO�/������.�x%�PMa�w /��������r�+�����e������9�k���������mq�����~+���������w���[��w��?�lf�c��G���������]���SIfx]-��
K��oy��e��?��TJxLE����5�����`*Y���I��d}��M���&����}��L�W��I����S�����R��W���^xP.�o���l�+)���z25�Nr��!|��Dz5�f�n<������\�Fz�;����V�	�k����=�
naG���	��K��<�KDj���3���N[�����A���%	j���^5HY��:�r���M��{l�h�{
��z�~!�K�ju
}�����x�!��F7�������y��.7���,������C8mG����S�!7�2��YQI{�����A����]�5GK
ji���A	W����w��vgp�>_n������i�������O���Z��"����i���:�<��z�X�&O�3N<HMXU?o#K����O���6�o�_�^��A5�P��}�,.��oo����2�
�����z�F�������y ������w%�}���	%X��!V`�0O�(���8�U�?�n�T�(B/]������#���[�����QT?>h���$E�Z��;AX�
�;[������?�U��1�h��)P��e���*v��n`�>"TE�yw<�z~��tD�&�h�@�*�����/*��`������{3}@H�Y��������+t���r�������s�u?���Es����N����&��
!�N�.�B���8~D1������l�H�+��[����'>`g{�sO���]��r���i+�v��B������`4����[FEd�9�It��7�����FwCT�"������D��rKIs��`��������`o������}w�l�~oa�r��M
iZ���0���[��e�qv������t\���:���_\�efSz�^@H��Uo^�������������w���m�G�&�vn�4��f�3�����t�+������f�|�P'E0l�����s��U?�_���n��d��]����`�M�wy��\�F#���LG��j�}Xf"�O���%�1��Q�Chqj\�W(�1��K
��$$iY�2����7D8jY��B���������������
wPf�L�������Yl>�/INJ��S1�����7����L���r1iK�	��5�Rk`�h5�}������t"c�C�����f��2:�
�4����c�UF���o�$�.���1�<���f�����j�p
Q)9�� �������?�_��4�y��Wj
�n%
�����OEN.;Oe�`(������=�����:>��Z���'t�~n�qsYD'%s�n3�?����Jl�cFT
�U�=89g\���������V�x�D�*���Q����8�4��m~�,��jm3/�q2���wz�n�K�z�x}QR�d��0�t���Y�]�#�3��PoA#eD�,�Ff)�:�v$vn
g�#���d����y�D���!�������V������O��?H��7��;��AZ�m#����A"l�Ma�����^Qp�=�m��p�1�kz����[]Mf����/�[�)���"{����d��e�#)�?�B���^�{(+zb��aE����C��c���5C]����o��V����C=�m�j�-���3iK���;�DN������t0��5M�b��Z?JL�� Pqqb��K��S6�����c��7�&��~V3s��7���,��1�fq��y�"#G\����u�l_��ZjS�4�GW�#���:=:���m]��o�?�)������`<]{����FX�e`3o~���pR�����Q�O1���	�=��+r��j����V���k���O[b�K��Rh�����6?��V�UV�a�����U�M�����v����z��z�Fn���^����A,��]JzpV���TF����EAY/'�u����d�[���Z�E���I�[�0��x$������J< �}��O���`='�N�?��mxK?��>:5i��p��u9��=�F.����������
�l�g�����5���1	��u������;�W�mO�T�edN������
�8*��g 1���Q����!;��?�=�+B{�P������,"X����M|I`])�/����&A������o<n���}Y(=DaP#���C��8s����K�	p�yr��*2��5��d2�B���k�����ML!������7�����-��W�4x:�:��}&����`�4�EZs���:bv��@�����^C��{��
>2��w0�r�I�o���x��1��N���D(�Mp�n���q��^�4�	��%�jh���q�$�����a��{<��8R��>4��M��)��1SJ�3�"�/G�Z���gr��"O���9h���2��)g1`t���Iu&^��wI�j�x�8�i�U6
��{�D��-�pSx��E����O�N=�B ���g������-~a��a�e|L�SA�d���'0+��}��u9�m�^>?3�D��k"��(I��O3���O���u����+���//s���%����B��G�7�N,����GP���7��9n^T�p=��������O���Q�I>5O��L��}���c����|����H0S��S��R9�_������?��5Je�pf�OL`Az��I�;�-k���]���kE��T�<�B?�_\\>-�	�����;,'[w!�M�	�>����y#Z 
�+�,����T����s�A�"t����`����ik]/@}m������'�"�c&�jkk7pq���b;[C��K�;���`g~S�����Z��PCE�Zrw��w��hS����<:
��;�`���K��9��W�f^}�����f����0�fB����Eo|
��R�?�0i������S�M�r��e2o��d��7p�D�P��gx���>��h5��X\l�Q�^[3����r����Z��B8���y�2��j�i7Z�7
���NOs��M����!if���#^U�8W:���F��c*�0�5�^�Gj�	������,S��ZJ������-�l�c���
)7e��H��q7'�[�"y+������*�
�S���/p���Tx����
g���3�Yz5�������F�W�VY��g�G���y�P�E�l����}@������*��b���z*m�RvW_��ki�+p�����#�?Bj�I#����5y�^��Oo>��K�F�^Xf�=��-1�e#~�H�BH8�r<����\N�_=����Dh;b�������PW$|��rGS����!2�x4���`C��A��d@����h�#�T`�%�
H!$����`��A(|���@,���d,f�)��������������6����N���h����T�-�<C��BIcF]���`{��K����s
��ho�1��S75��
;_��}����3N'$�Z��g�[{���o2s�9k� "�-
�����5��5W�~�����wiKsR�!��RK��9�����1�@��]+�Z+�$pd����pG�`M�1���^�pf��{�j�y����T~K�W ~D�Q�����Xx��<�_��
6qG�y�0	i�����7��?�ba��NB��#�������p������v���f�qJi�B��E���D��g�@O#u�8�1u�0<?=���*��������`�R�[�Wm ��f����z�M�^o��p����M�&B��E7�*d�n/ppe�Ic�������#��!p�|���<m��Qr�����r\[�b@�E�����{�fdx}/,EGv���B/������H������V��+E�}�<�R����%��-;��1�����Gc�����A�YH�C��7YZ�G�TLi�y*�Z��(1#��Q�������T`�L��
��^�*������^}����SU����C�r�9�+��[l�H5|��L&> �2�2��+�5�}�����I�M�$h�7�i�*���t���6�,)����]�92�(�������Cy+��Mw��P�{�������P�����h�
����vo�>��W�v����!��}�7��O7���&D�1�E6��
���	~�c�����`���D5�[>H���� (�{��I���J��M�W�ZB=�C�J+!���,��Z��T��nU��O�������?����GX�^�[�u�������������{�Jm����tj�$����d����,�%���+
_e�8X��-t0����IK�@I�c;�<�w���U�P��~
�N��|����w�NSeq�����H�������nm�\�U�����4��C,�����e����	*d
���l����@� �%���NR@��|��i��1g��$��q��!�#La�C(
�A�M��*Gpx����Q�2���cy��*����oe&��sjJ�|j����9�����F�`��<�*<r
O�]r�fy�����m�_m�rl�����C��;.�Z=����^!��)j��my�i�P��o���������f�{����9�2PW��S��]z�$�C�_��aP�wo�4��s���qZ[�Vue�i��A0U4tD�O�sFy���~�x��Vqz�C��������^����xt�8i_5���W�f�<�Mwuo��o�x�����3�����v~k���6�}%�o�"��]��DQ���)���#g�s��lr�Y�eF�H���?
�i�g�
]�rZv�d�_�:�gG�sL�����3�9��}�|}�>�h���Oul�)����uqW��xu��Dd��-g�
o��n�p}�'Jq���2�E���c\�L���F`��@&h��D�	Q&���"P9��:c�R_�����^��Z�
����F���,09�������IS(�U������o����J/��wh^"���nk��mwr���B��u����������;�4����-��:��^"0�0i��>e`b�+ll������&b�����,g���C�����8G�(wT2	M�5Z;.RL8�EH�\���>��g9����������&��}{�L2�5@����"��U��<�'a�]�Q~�q����}L��e.>�e8����_��/��Dk���H��U�(N�	-?W���>��I�
�m��*��{�8an���d+�`8@IXk����8���l�@��+�T7�a�&Q-s"�X�F���-V�=�!�E4���7�oz$�EyLW�������1���X���<sE����1nf<%k��G�?ff���������w1"N'��V6�I\�����s//Z
�����G6�I,��gz�a�\�4�������i.#�9��*�������'~�G9���R����5F���)��J���6�;����Am����&���E������3����D��-����3���o*����T����a,���y���
�����<��Z�����V���;��1���[.\����u��dw�*yM�&��s��=`|�����S&�1q_�z���y�7�3��MOrB�E��,*��$
m�6g����n����#�;��hz�����������{�������$�������������nL�W���������C����nu���W�����N����+��~m��wP�ow��N�����V4�;��.Yj����#1��	ppS[�B�%��3�x�\4��$���p��[� �t����Gj�	���t���L<K�2��{�����z��#o�!�� ������u�
8G�s�i��������k���j�������R}X������jH�D���=��k����������������JP�Y�y��_^?k�;h=�������������=������1J
$���P�dlfb�"������K���2��x�w��>+_f��"2<���x��������.;m�����&���A�;G��Y�.����Y"�i�����}��������WMR���}8lG�`3�������C��i������.�z-�
`k���l�� �%���U�2�G}���Q������H��	��Wf�	}q�������i��<�@�;���:���V?�vz�r��_��l�l,M����U���K�2@��5��J��j]#���������
��������(��3C�F�T4�b���H��n�>+��;���$L�{��B����LG�v��;����6)}��|��,u��[�5�/����j�����%a^V]��'�J��]��W���^�e�6�N��<[�y����������(
	u~�d�)�gU\�j��S����e�=�Q7����/�:H�k��[r�����X�p���m�J� �h�(��v�+�
�r�[����6Y�~v��A<s)�A���x�����4��F�_�[W�#���<;/b=o����eYG]d������������,��d��c0|�6�jwmngGW�+�CE����^����MQ�J�������{���������x���d�e>�0���d��B��1�I���T�4+(��
�Fr,�V�F���8F�^��_�2:
I�R�U���[��v����U���<8VW&���#��U��)�$p&~o��K8 �_j��O�M\���cFJ�����?{o���q�������U��4Ar�C>��D��F"�$k�M>`@b
�M�{�������HY��d�����L��uuU�$����F����[���s'��u�� �5�4pI���(�+s_�]��{���cI���%*U_��U�-��Y3:��r�����6�q*Kw�(c��KI���]T��~ZUd�d����u�����Z[w����Y���d��-��>PAa>�Z�aL�XB�)J�^ �������+3��<���|��L�.�b���%����:L��\A���r�_W��Y�F�/��AABz��C�U�"���H��������V5$�����{s���CQ;K��v���&��$�����@���2�����9����VA+2~k1H�
�[�v�Z���7�}�'!'e�'�z>!�j�������%�+k^6�H:�S���+�-���/���Xj����"w��J�:��Yd��V_Z:y^Na��\-���V<Y�P�
�i=O�U��:o4���!�����-�b�:m4H6�px4iaUz������u�s��DEN�n��8d�j)l�|P0fEL��<�M�����#���B8'��_H5�2w"�5�_Z���w��#;%)�`�e��Q���Mr�V�wEo>+��;����q��i������[	:�+9�q��GG�F�p��9���.�)��!L#��Jb������
�wu��\F�s��������������r��g�'��z��[�~K��)c�3Jb$�C���y���13G�G��n����o�)Kz�G����X��r�;{�����bBhW�h�J�T%�G����yYU��j�J���B���{6�����6~�=+g
��E�~��F�W&�&�����������Z>��9�L���Z@l;��=��&T��m�\����d�+��r"��������k��
qK����8<zN{��xl�����������s�H�|�3�;
57�8��^��9��o2h��1�����"54�����dG���S�U�1fi�5����&��m����|K�97<Hh5������7��+��y���v����'�����]�MBI�$16�IG��L����6�$�k6�]]o�� =����K<�{�h*�U�2�v��i���6��p��8��R�{�~o'������v�Z��A�8�k0������BMM��z�X�� X��M�s���%F6�w��
S��m1��|u����I\A0���zZ�^ej
���L~�>	c����������Kj����*d�^���	�������S���*TF��0��|4�FE�G�=+�0_�a�����-h�p��YY�u��l��,�>�g����p>�W�*w�����:$ttmp���m|Z�6g�pg�%�G	����/�s��:��I6_*9D6>~1c��F�2���W�;�l�M���8g��~F��7���*jIqL������M�Z��w9,�8�}�9��Ut�=��Gn��e��+�Z�h�^�*W�5.;��d��_��ft.��l.�$��Xl��p���p��Hl�������8�./����1�WB���.��bT)n�����z6��e9����;�������J�Ri��v����-rw��VGK��a��e���U���(f,i��y������W�Z�:N�#��Y�M��~u)��|���������vDUzB�2��{����k��W_���C�f\�r2��6���[�8��5l�|e���2U�Q�~���Y���_
��k�B�G� (�;�(h�����=��n���{�[����p�������'��@��N������k����X���ZAh��1�j��vK�������^cN�+&��]5��\��:/�&,�f���at��
��@��P��jP���yu���}��(�@LN�+m=e�:S5��+r��{�V����f\+��e�Dwk����wC�0��J\�d��]��B����|������kQ���2.�:kW�_��R��*�������&��Y��v�j[hJsL�y�����?V�)P���V;���A`V��8��{����j�,�����"�����D���U����O�/�F���6Q�-��5PT]Ia��ZW���B�9!��9I�o��?��l����������N�����*f���5:m|���n�d����z��?v�n����KQdkb��j�T&�lAy�"'��*�����2_&}���'P������Y�"M����?��q���3#$~��K���U��B�V��r{�&7�c^�!l��"���6���]'�����I��-� �H�n���v5�t�a������
\*~eq>�v�U�j��z	��al}�z���o��$�HK;�j���'��}�\0���K>~���G�O�O��t�hm/�#!^M!�o�}�\^k,�F�=z�d�9\�������-�b��1�[�zP��f���p�[��Gw������������145;���A<�2^�#���� �l�v���_Nm����d��j�F��]2����k�����f���_��c�+�q�q������O��r����w��=5F�~�mf�=�W���2���m���Z��-����l�g��J�#�i����|����ES���~6c����[�Y\��l:��V��5:�����Nw^FR%��{��G���C���jf�A��r���*���n}O��ws^/QZ��0�{is/5���=�6�K��?n4
K�����d*���I���t�����7�������u�&)���.������7��V�F����)�R�'m�3��l]�-��t9��(��P1��s��##��Y)X�/��V-�E�CY�]�c~���}������=_�����Ef���S��o�P����N����y"�b	S����v��o}�nN�?�m�e����]�WA/�����h��j���l�
�]X�Ms����$H�h3 ���s������G
IUQ�����������,z��Y���ww�8J�~��8�Bmr�|��%#u%	��#9p/�K��B�k�|�mh�K��!��c���p�t �wr��������#}O��G'�������x{�qK��',���#���p������O��|D<�_;)h"�K���
�r���;-��fX���4`k
������l���2��*�b�����^?��
�O���A��&P3-:@��������w����[�d_�<?8}����������n�*��;���~�Gm~��H�@%9��i5�6!�:X?���j���0���K��]�H�4_��pC����������������32���)�f���e(�$��+��+��r�������z�kS�`������|������'^'^/	!������5�H�'y��E�^;}�G�[����Qe����M����v�G���a.����a�)��v"M���� ��&p1w��)�v�h	�"��EZJo����#���FN���(~�Nn�����*#�%q�{]��I?���%�o�m��yg
��V��y&��)��V��Y���:ct�/�8���1��I������v�5����i'p��o�����Y>m.����4��1����K5�����q����m�[lTj��=�Q#�_����	��~U��U	F�w��������nI�9|������?���+B��c�����;��3����#Y�{q���Z�X�R��N�_����]��^�n�"}�������x��_z�a%[{�V��5����[7���]O�����0�4�z}?�B������L��������4��[��#�>R����_OLE!�����T��m��/��R���!Z���t^H����U���t2������u.��`�d���P.��I��g�h8�L�����x�o�+����=�U8���j�E��
z{9�%�r��C/��6�����������+�Uu�P��,h�&����������v1��w�)�q6,�pT�n��gM�G;?g��g�J�M�rx5�h���oBO�lz��"�s4��!9���I!����u��\�V�]u�i�M)�*�Sf���u3�j�R+��/cs�����������k>�G"����y1m"�����$���m�����xijW�X+E�5?����I����s�C]�����u��*��xj��f�?>���5�kx?�[�0i�M(��4h��#���-#J�
����k��f���{]a��moX����,��,�vw?��� ��M���*���%>/	�ND\���b�8|
@k�O/x���7�:O�*a�_L����?�T�M/X��
;������+�xa^�b����c.~�������a"�ov����N3���1����'����?����Y�%F�WG���4s�'S ]�R&8��br���.�a����t�������$��v��>2���?�
1�����b&'�&
�<]X�iq!�Ru;�����N}a���T��=��m�B�&���t\O������e�����8��kXX�:T$
*�������lP�cY��TA�|n������s��)�o��3����r��x��Gr��������;l����l�Q�|�w���)?w���P��}
\^�m�J�G�&qGb��r�-XI��o�������8=���u����~s�O���������*|��s;����
.�C��U�pn�#���|��%�$������.	�����S�4�����U�� ������yd��7��'�<|�s���R<����)�}d\����������#������������E����rj��������U���+�<v�������aZ�<���I<;9�?;���|)1�&��Nqd]e����	��1�g�/|ud.S9=8sN��O�_��Hj������3g���|�����O��#X���XD�����~l1�!d_J�u�T��8>q�������j8�|X���X���c��O����cN�%L��?z�����W'�?�����o7��;xqx�l�qN�O�'�'s��1r]*!BzB;8z.}e��i�h�����aM@o��Vr�7�N�O���EL;o=����bY��Z��-�q��n�E;����?�{��h�0�G��L�w[����$�������K�3+'3�������O25�s)��b�v��i������G�sx�o�s��-nb�i��8�����
I,��6����_�5�vf�0���s��O�d��$ I���#1m�2���zux�YLO�*%D,��q����!?8��'����/�����b��F������+��3�����u��q'�������!��������"U��L0��������T�,���b'���W�H�`��[������{$�Ev#�d���b�M�}��nU�,���"5?]@�?���I�r%kK!/�DT�������5T{/�����`YVU�_�.?�j�4�{3���s��y85��<q�`�i�����#����?gN������DL������������l�����e����_�+��R�������sd����G��<������RE�cLi{p ���,���3�����j�e�������	M�c���p�g��sv���4.di�R�y�Cl�
�y���6��?I,������3�O~�M�`���'�+K����ea���W`.M������i�J�����m��|���0�3)���I%tp[��G��O,����z8f�<�$f�=��3�@�}Z��so;Y��SJ��A��F03yy��O�7�`����C
������ao}j{��vW�`.��]�����=�Ig�'��v��v��v�������I�RH��������g1��^����}U���|TQnzE��[����A�f
���10��Z�~����EZ��L�fSm@�2�S��}}�6O?V��O�eJ����A�4h
Re*�F�a��/�����EQG��g�(��U��5��ce*����
���Sm���2�4U��4���yz�2 ���T���>��a�S�+K��
Q{�B�^�*�0P����X�2�(S��C}�B�^�l{/�����g�5�[���*�=O����}E���2O?P����#E~��z�q�4�(D������
��Y���
Q��h�3�c��@!� T�� �e�Tb���DQ?����d��u�)�G�+r��4�u*�G���0V�3L�&���,�=!r��<
�k�j�P*S�"	Q��/J�G��dQ�,v�)dk�m(��
Q����X�Hq�lA�����~$����
�!v�C���lU�(,5I�M2������j�P(DM�M�3V]�("2��:s��i�"i�}Y�u)��t_e@���d&j��a�����b���m������XeM.)��=R�q��b(z���f}���?=�����{�$!���+���+�"!�^��0Q{���-�2�����^c�&+�/PE/T�CD
Q�n��$��S�2 �)DyXO .��ma��GjP��C^�*��:�3��#�~x�b(zq�]]I^�g`��KB�o�pU�,I�N]�~x�b(zi��3U�C�����e.��^��g*D�)�17�'�mG�O��5`�5���S�IF�>=���C���%�y�!�w1����	��~Kb���o?PE?�dQ�(��2�0�P��~*����������S��n��q?J� RY2b�x@q���	X��q�E��(��}Gx*I���(����
Q���������	Vy|��
�W��)
��)���e�����I:
VDWQaR����Dq���(@��<��b�x*�b(~�M� p1Q��>"l@��OB5�q��b(a�M����3
�\	�K< �?�&j2���b�P$^�]A�8�$�z��3U�}A(D�*�G�*�b�y�����~����
]��&��M��z��H�$^��K0Q����"/,WB_����B�!�0<�@1C�j0�B5Il���0�zB*�+a�k�HiV3��S��
�DA_�*�O���0���N]�LR�P$�P!�T9|	3_�Y�l�L9��\�3r���f�f5���gD1n�W�"_9����-��}Q��Q��C��?�0��*
,��H�O �u%X,G�b(yaU �~%�1y�Xq�%�$"=^��<S�P�2�v�&?#�0!�b���h�*�1Q&i	�ncO1c/��z�+�PL�O�4�g����A��:�����h#�>5�����m)Mq����q���4X,Wh�)�L�P�*�J�X~�i��g��k��T�:K������H��F��d_\��LL< _	=%����h��f(�T���$T�	Ib�#	�o�H	=MHv�EV?�2�\I�D���J��I�a�I&���i��:��#�C����6u�9�,q!<��SBOS/�HH��/�C�u_�d���M��IJ<^*���F�)�+x*�b(�q��`+��4Q�=�&�a��z��D10����<�,�,5��W27�l��,,2-�4#���,�2_1���"���:#%O%��@�B%��6&�LS?�X1�8�*O��fI��3U4�X���6q����6=��*,����C\ �0�
�G����DM@�?=7��"Sh��%���� ��>4��CO=2� Q�[�UEF��$ �7�\�>��4S��aS�c� Q{�,P~z<\�|�2���!���z����p���q5x@!V?�
E��\�" ����������8��o��D�%X����Hq�y��G���
q���{
Q��W8t�O�x=���D�Ip��0�zq���G���'m��>R����0U�GF8�z������?�#��	,p3L��r�B�o��#&0���L�<�?D@l(zA�bV(���]����yA�CO	c�
R��# 6� S�� �����>C7�������S�:�������@�����B@|LN����K��S/�=L�a���^��L�j�8�����Y��0��$�#L����W�
E/��6T��(����Hm�}�8���&�(R��(��"chzyQ��	�`~)M���^D���]|�I@l(z���C���^��X~��z�]f���lV51��Q]#�5P��Cq���8����0Q��O �b(&d����<�D�����K|��&J�)YH>&j>��}�����������K�p�TbE~����I�u���J+�"Y��nI�(@�����2�����L1H�a���b(���di��a0�dB�&0��0i�J����������f������P���#f��CO���p�&��+
C�R���!`��8��Xd�":�i��8������e���b�J��e�
t����cH��V?�
E�
#���n���0U����|7���L Q�n�����bC��X�������@�������1Q���Sa��I�D��x@16�|�\��zJf����y*nVI��i?@����
������y�J���&j?��H����Gt�~��
>g���$�|���CO9��������
Ef��J1>A��m@��bo)tu�'&� �����"�v��A���0��m1|�I���P!j*�S	=l(�_	�g��!�����.��l��S���c��K@����2���]��/��	>-5^2R��bG���#�~(�6R���\O%��&>I+�#BP��Q�
E����&�p1?�U���J�+�����#�g��'�q�
E*�P�����aq�8�Xz�����9O%������~�x����(e�����+��S?	}L�I����>��v�R���^O>K�RBO���1Qs��3���H�	�?i/(�PJ�N%
q����B���A����CT�4��R�p1?s]�o�D��<z����D������< �?�g.f��&��Y����,���d�)D�e�>X��z�'��/p�b6X�%��;��nC���{\�P��o	���.iY}�_	�Z2f���������b����Y��
W���G@;���OB ��l
P����������J����M�sl*�e���I����X����%��b6	PL&$;0�g��hDM����W�Is�[�i(T�=��a=%cOY� ��oI)��Y�J��!`��x��|^B������3& 62j���W���r�8?;3zJ@��F��D6����&�n�b6ADk�����F@zD�"?������P"%~(�]\����[��@��rT*&�8����V�����q�	L�q����]5x@=
��$r�}�f(��I����h��\I"\��k�%#[u����
� �p�f�*�l��������3
p�i�����HQ?8�(���Yt��S�q�J�� sq�)�9�O2_�d6	�]z4\�&���Y��I$8�y*�!�
��\R����p|<q1N
��>t\�lZz���q����C?�LE)fz�����������\l2���!�zb`��WH��/!��SIq����p�i��������b��8)��b6!�g�f#|�G�?=
����~�����l(�@�/�~�DM�)W�������@)q�x@��H�R~�/P����rL�����O&"��dBbC1$�v!mlL�a��g������d\�����V�>#�4M��j�l�H��
���q�iH&	&�(��Il(���C���4&��W�m�4�q�COI��x�����#��3��!B.fCd������������T�:	��d�cC1$��FB�Qx@������'��D������� �dU�za��	SE�%S�j��,U0Q��#�\l(����yf>��`{��t|��H��I3�D�)�cp=v�'dYln~t]�/,���ye�������3�+i?��������
�jZ�(�����X�Z�p�P�������C0�B��@��9!1e �����9>��` �2����P�1P���r��P�3�~*K�a}��PSa �q���#*D�a���X�c ����5OB���P�% �������Z=���%�@h�D�R��
Q+���aQ�Jc�B�>�mJbk����e�����%�����k@����G@@�]`�B�>�h�B�-��D���a����E�:F@��b�B���������?�_��1" �8F�R�����I@�% ��P��!����	�=���gj���:�����4�a�<�_�K(�>�g	�v
A@|~A@|���rj��-i�
�P@��!N�P�,��Q�'Y�����E|	P�O�����O	���y*�����4����c�����q�t�������	R���z ����\���D=r3|O@|�i���#���V��-��� \22:���)Q��S�8������8�$b[��������)�8	��s�#{"�8�(����Wx*>�}" ��" ���<%������)�a�qe�i�R���"�}�8������)���q���7V�8���QG8����V�6����q�\I��1�5% �Re�L!j�f��SJOE\(O%�q�|�V?��G^�����S!j%�������8�:�
x@��& �'����?�|WQ?|W1�hy�8��K�f��B*���p>q&B���D��,�����qA@��A@����B�~�3K"N��}8���O��J�����W�v��}$�7����������sb1�b�K�F��d1���|%�+��P��m����������8*�-�C
Q����g���+���y���Q�\]G@�^�8T�g�����/����/��R2P#�}�����L[N\��E����3s����|�6.l@J3q�3q�t�u5��"��q��S)%��O���
b��N@�	�b(r�?n6Q�
A�3�#�V��T��)�O@:��~�b(J5�:Q��
TV���oC���8��/TR����B��  ��A@\��,p�
Qs�*�J������:)T��a�k�DZU��c10��d�P3�W�! �{��
*�Kp���������qe *�"i�
if��K��
H���`�r���q8�H	�'�b(FJu)N������p��r�G�f�$�0��0��C�8^�H���R�,b,(@%>!R����Wb#�b(F1��E��o�_�m%�V�D�7!�C!j�f�(�C1�p5?�ou������X�������	�+%F��quF*�P�����$	����!�_I@E�`�H�����:�p��(���&e���\�����i+�m"-�����+[#������x�X���������x�.�3�}����R����^0���G��I��P�'$!�����x@�z����3q�h*�"W���j��$��	�kkP	=M2\�����Av"������S��xi��I|*������Wv'MI1S���(����R�>��Q)���F����+�"����*�TJ-�8�wP	=M3|�_������2��@@�|%S�"$ ������ ���:Sn���@1���A��"\��e�k��J�i��M��b���X13�6*��Y�o�!���e�z�e�����]��q�*�1b�����7��|g����l@����O)����mI\
u�A�h���v�]�V��_00�����P�IBB3�03�g�o�"����b�/�������'	ll(�\<7����FF$�{���A�H�q5x@!V?b/��"��td��x0���&e@J)���b�q��{�K��8���w|�a�������W���|K&�
�P~�\���o��
�[8�[2���[2c�%��Qb_)M.�<%�6�[21�R|wi�+�������r�*��H�������%||�,�]�$vq�i�%x@����q���K�����i�f|�s-1<��g�������Xq�\��.�w�C����Xtd`b�
Q�>���a�
��o?�����8T���*=< ��p����0��xbC1�Z�x*)�G����s�8 ���K�Q�p@�r�|��t
�vGJ~�?������@���P��Q�
���mhz��R�(fOnV	h�#�T.�u����,WH����=��c%���	��,; =��P!�8���TE.����|^�)�P�(�3Nq�)W��D+7�����	��J~v��x �_���zJ�u�x��`����p�J�k&X� ���3Ip�)QP��:Q�����`�M��	1�-J�Jir��S�w��4��`$7C1��%�Q< �q�����	=�S%I����vH3�P��K����I�y��'����Z�j����I	��L3��,5�p�Z0��,V��,QDGF�(���q�aC1q�j6���$.��a �'$��D�K���D�G�p���PqI8���b6	�7����COI����	�����bC1��R���l��� ��2��}�iBU=����	;�TBl(����$I��! 6��=h@%^�CO	�`����QHo��"hb�����$��B�B@��O|%�4����B����
t��e@@�lH8#O%��+����c���%�,�t��/�. ��p1�P�H�;�� ���L1Q��%��"�K�U0Qi�������8���0QsP9�J�aC1�J���IB�oR�y6G�`�
��c������H���?�h&f!F������Ex�#�g'Q�
E*�6R���ex;D�7I���&��*�G�bC1�2E~���L1{����$V��N���t�~B��V<�H��b��M�1Wd�cA��+n6U��# �T8��)��b6|���I������`��nFI������U�D)fC�	�z&����7����)p@����$��
�����R������F8���8�4I�u��J�T1�����$����L)Ik�CO	�b�&���'�)�!�b6I�\Lp||��8���7a��2\,u]l(�<����U����U-��|���J]�4
�{#�������mH�������WRO)���!H�����})��+���T+f�2���|���n ��l
P�����zB���|��.fC�TiV�/�*hx*~�c$�������x@�m�����nL&~��Y�a�4��{X�T����[0p1�4�mO1f��p�����r3J����e�<;
�b6i����a*��)������3N��/Ed&X�0�b��<�@'}��r&�HQ?H��"q�I)�lRNy��FXA��PP���40Qs
D_�bC�TM��������^1�rP!8�4������)�a�>c���`��Sl�q��a��������/���j�"g�@��?.f���+�����)��-����,?I�����(l6U����E�����}8�4%���:Un����xr�T�g���������)���CO�U�OH3_��f6��^�L)f�f
�	�'E@zJV�B�������5 ����p|<����M��-Lf+�F:=%}������\�+"!����J1��s#�>����<%�4��p@^�����J	�q�
��_�	��a���y=�|��3_�#�}l(f�2�}*�l2�kVB`��2?���|�-�[s��g����%�,<\�&h�>��������T�:���Y�`C1#��Y��!���eN��*OF�	&j6fp��������HS�D���A��)�Kj�������\�*�,!���i�l�H��Q��g�Zh�����	�g��>�����.���b�!5A�����q�>�J�xvc�'�P�8.Y���M~r��8�4K��!�~c=!������]#�d�b6dZ)�P������q�iF�&����c���
�,
q=�,U���5����,E�o����y8����cd��
���l�f�"U�+Y��2.��G$��:S�� A
E���[s��������?�������-;��7�����9�u>��hrQv�j�����o^<ux��pv��&WW�Y���{�s��~v��hr��'3g\�������tz����F���4//��������Dzgk�v������W����q�����i�6��������^J���|v~Q�����-��N��a�����T-=����>n���1z�C/��P?�}�����s����g���d���_�C�������l���������4��7;��l������7��g����W������3���,��
���;����(e���%������5����g'�g������'�'��n���f?��N����
���F����g'�#�@������#-��9�g����Q>���������V�����$����4���=���N�e�����'��(�D_�G��X��������/^<_7���&�zr�NnU������3 ��<������� }%�+��=���P�EY�]��|4*F�e�����|�wxic8����[������~��g^�f1+r�*��4�����������ubg;�x��������D�!)Q%�� ���S�Y��x\�f������3#Em>����r�[h����7��7o�:g�ly9������v8���p1���oIS+��34-�&��Q���ws��.�<��87�z�_�%}���3�=��I��f.����Ft����v �M��i5�m������5�!�$x���I�u�S�e1t�����p���[��yI���{��s�0���zB
�W����\O����������k�[�V��\e`u���|���{����.�R�z���o���u�R,�2�kOu����u�m���7����_Th�������E�a������c�hpm_L�n�u����L����&��S����q��!�+�DP7�5����V�'���X��L���7�Y����	-�f���{��>�/��K�|��D��B���{�/�����OH�:��i�:�8W���tC&�i�4����T����9�8/{y_��41�������zd�<�na���B������H>��/������X>g�=�G��-�"��ViJ���Z����5�������e ���	�8'�_�?;p���������3��eq^L��)Y'g?��:o'd|������!�������0�/x��nl�����N3��d����y�~2���-�w��r��>��=���q��^�,�p��\�b��.w�>*��@�4�t�y����cL��6}<������G��\1��'0k�i���������4C4;���=��u&������:�j:G?�|��qk��H����]�������&7D�$��zux�1��?��f��"��S}�
��{q |,�d�>0I������??�[�� ��@j�k"u�|��L0Ni�]����Iek����>������ p�$e�^�������U5�@��:��Dm	��lE�\���)uD�����M���7�c����?#���gQ���~9�?+��^Q�`��G�s�n�/�G������2���6��5��6Er?��*�rO{E&�l������SB7���#���$|�`����m�������N&��������;�������,k�<��fa��_���o`i�'�a6�=��j��a�=����]�3������k���j'?���E�
�O�>����Y����
��B��Vh��1�����7u�C	z��>���bwh#z��Z���tX��^G������=�E��b�����d�������a��
�k���[��=Q�-�����}}?���D6?ll�?Uh�����K���������o��/��.W���=�S��]^{;�U�3��.�y���b�;��}1���i��v�W�.���D^��R���F�x���\����\?�7��C��gN�pJC�o��mp3���?�����+���$��n/�'^���4��I/K�n�������;�W��sZ\;^���S������f�:����$���d^��;_����{�-��������xNr��s6/�8Hb�#��irS�.5�q:��O����_��=����=/��#di����p���&`�rc�%G����c$9��dLr�c�m��	A�!����3N}lP�5�?�]N��=��K��|<�lHd8�[Q���z�*������?������OH�_�}B�������v{K��aF����������_����y��s��=����=v3���2����1��fZHX3������������>\�b8�#U���?��n�{��p���|$�^gC����"W����&����/�a�����P��h��K_xQ��K�p'�a9[�v��6[�vB�,A��i8<�����K�m���u������VC���|J:kh��hBL@�3_��>�����u	`�����O{oT��������i����?>�� w�`����p�7V�w�jP����S/�Q����h�fn��W3iP+M�z��2������lr}��G�>��8��{���������+[�x����h��G��s��w�ON1�|�t�$9C�_�Y��sY������;��S-Yv���6v=zTe��������mx�m�����_�����8K�[=�
�f�W?���L}�*,��SU\���^H�LU�zi%��l���hg������/y��_#�%o3(�����������s����i���8���r�5$����m�3xsl���������s�	��o�3z$��Ho�~/�G���F�.�Q��0��}���|��c����BI}������s��;��s�X+���r���*�#m�t����Qc�����Of���'��!%��L���$j�Io��l��$6�Bx3vN^�8�v8_�b*���y��Cm�[�%�N}����7�g���v2�=^��T���zR�8�l���"`E�<^e��[���W��k]"������_��^
9��j8^z>�
G�o����6���qn�n)R�%eI�N#<�P���~��@�����������-��oi;/~Nv�J��L$�$X�����h����?����C���P�b�P��.�����]�����W�o���w���m>���mZ�N&#ga����`�YLna���������jc�qd����/��$�����[�%�7}���A2�_L�������'��W&�n�������������}<zU\M���M��y'��y��[�h8p�O�������nm1�vW���tho4M:#6��}�lP����7�'�g4'�+��g�������������������yr�����x+������{>�y�Lee��[��	��
E��D�k��O�{�����h7��p��e^�G'�o�g��X�g3���_������k�Q�l:"���E[ �����wy6�<�\/���=��zf����������;d��h�E1{f>nm�#�F/y�6�Z�����VV���{j���7o�/�w�[vyl�nz]���]�~�-���&�6���<>|.��}y[�&%��o^N.��^���F%�/���Z?�7N��h�K�(����\��G��o�
	�Hx<���H���.������1;�C��q8��OcX�9$y�/����3~�~r3d����OvR
�b�*����C��s=��x�#Q3���:�F��8���>�M�����S\]���@*��������'U�CQZ��c���CK��t������7����M��I��E������p;����HI-?������N���Cz���-��w������.oCN�����������3k�vjLsJ���i�J]Lv�u����&Sz} ��zZ�@<��"D?�S��t\����bL�6t2�LC����L��6�NoDu{2����E��f
����t�Us�S��������f��n�����P&o�r���n���k��b5�;���P�G{4��4"s���Y���������e>��I{�?�#;y�L���R�$��D���u~Q��zyz�8j��tp�|
���N�/�w��Z�n�HQU1�������i���H}��]��6cUr�|'�X���n>�#�U��!�lf�{p���\�+����b�e������Q���!�'��R{�����g������1�H1����L�cZ����%{IS�c�|���-m#�X�f��(w&�+�������~w4!S�_o6F���f��Hcf��)}v�]��-�B�O5,M;�4h��hl�9��vyLH���q��'�i�|f�MC5B8,���d��%j<0�M�r�!d���y0��"v�s�����/��F�������X�bF��u?o��5q���C���'�\
WD-���h^^���n��[���S�q��D�0�.�f���9�A������!�����7����z:��e�d��0nX��� ���xR)F���5+GK��o<��PP�+
'��,��s����z��Mg��\g%�zkk��'�FX���}rF�h�$���J�1����0�O�����I�!q	��]���V��b �����9����q
S|�4��2���VKiq��8�jg�A�T��,w�+&p�'�������
j���� ^��X��.k9��^c����-7L����|i�7���~ ZL�����_��5B����V!Z����[��;v���Wx��GB3�i���2�9-{��[K�9��`��9����'�l����?��])������FE�����|*�b�Z���Kh����^�~~}���w�RC+K���G��(�������[���/��kr�������qGL���$tW_�Y,�����X�<8�m����Y�����������>9x�r ��Z�f�����|gGr���?~���szh������ua��L}S
�!�u�Y����6�p�a�j�������9U��^�M�Q���'�,q�����x|�9x���*�iy����|�����?m8�hw]H����4�De�Q�K������50P����Ox��_���yg�6q:����;�
A�}�����
���uHz�D�cO�FC�\x/3&�q�����S�3�g����Q��SM�}���-o��f����9$m��)c���_�zL��z��01p�~�5�������U��n�}v~H'cD8Vb

[�
�h�Hm����;\i��y���b�|���O�W��2l�)�]�6����]��R���}�������&�����n?��~����p�-�w��F�
#t�n8+���~�
�
�a�p�������}��������,g�������v��8J�~�����<��8Q�WNmru��eS���YI��y�9�C���t��1����:���7{��(�.���$����'�/��2|���88:89|���k��^����ccZ���������.������������$	�rO^;)&\���v�o��M����S��:�,?��!52�{�;�02���^�+�����u� ��u[Zm�E�{B
�/�@��b������KR���A��Lhx�OD~~���v��&������~������-g����/�S����[�;� $me�.���o-����x��yJ	{��E9'���V�v$�*GO-@�A<����������KP+�����?��<|���+����h2�1����*^�^�"�5A�]n���xi�x�$$�?���k�Da;+���V�u���jm��?��.?h4]"�vL�^[���V�5��n�V<�B�����[<�I?��x����y.�:���V;O�wdy����;r�wu���^���M/�q���v�kdm�u1pX����=�C��g <nj������%��q�y�(����w�:���I��.��q��_��o�7����?� j�ok�H��7���M+�a��6�Z���$�h/����g=����h����O�vBg��f�q��������6C��I�jO�Db5]�&�CVf����]��F��^�-K�T���P����_�T.]�w��������n�����|u���l��W�����X�����ch=0�h=X?���G��y��U2��
��4�V�L����v����:������z�t�a��Kg��C�t�exV�3;�V�s5��Q3�&�T�R��Js���mn�����n����nL����^�#]5	�l�%�`�sKcpK.����OY���b��b�L����38{�a�����v���z�����������%	�x"�M�����������}s��7���'��M�r�k�1�������dZ�[��?���#3�7o��:��,L�8�W���G��0=�}uV_q������TT�4���
I����-���OsW�q�+����-e������y������R��8L��!���I�S����3�Z�����?�)!��c�����)��}�S2�5�iyo������I7L� ������,�E��ki����e��8M���0w��9D/��+2=�u��y5_���%����_]����%	�'~��dD�hd>(�3Zc�p���c���)� P�!��]���c�O�]N'7�����rv��J�M�c!w�A>���_VO��4
]���0���&���F�2�g�97����j�*����pO��V������&k_6�����`eq���	�a���5��g���6:���<q��Z�������n�w���QL����m��n0�5�����_Y����-F�/��$8��	C1�$7���&5V���1h�M�S~����o���n��������������[_����?p�0�����?��"�kN���p�u�1{B������B���T�������\��cN2�]�7��*"���L���$LY�W���
�E@��������/��~��c�c����7���wHR7ah�����X�ZF������/��B?�W��_���	�^<���t#��|?u�^8��0N�4�;���������S���@��%�u��%�
��w����]�nlH�p��H�!��|��u�o��M������1*�rR��E����s2�	:?�y��|X��%�?u.g������	`�-����g�����s��/'����������o����������_���Ezrv�,����?����E�^������M2�F�
)YW�&��$"8F$8�U)����_\X����wr����W��"U�%e,�����i�����"6����\!����
!n]"���!���^��=��������)��<������N\����o�b{c[����K�T����n�6A���K7y�
�l"1���N\����-W�r>��}Bh;d�q�����*h��K9O�{�t��F���i}7�*)��d�H��<q��Pn���:uh<M�����r��ge���d_�*J[��9V�8\�����<�\�f���tn�i�������8��f����d��w�c�$�F�K�"&����c�����5�O����q
{Cf34���Oc��3}����r��x��g��N�W�O�,��M�������>���,Z� �-O_
i$�1�|�|�8�����&[3&Y��B���W�%����E����X���w��48Zqp�s�l��������?�)�:2�<�����T
��8��]�R5���Q��
IW-�$e%�A�Vs�i�BH4����!��m2g��!�&��dp	���ag�"�JH����9�q)i'E��t�B������!m�����U'A��p[|�}���*��)�o�=�gQA[�g��zu���\�'L�����`C��u�:�~-�,tdF���r:�_\�TZC�4����8�L-��5�]���!���S��/���i12�����&0CB���U�=�)r����?�h��M��\zI��-_X-_���m�L���'k,<ol���'N	��]�����c9\��*7j���DP�_s�s5�j�����)�����#��6}��������*;��G�$Q��������k2mCq�h�|�J������S[��<��=���b�����q^�|����7+�������5��hQ�76�O��x'�z#F�I�L�%Y�r�0�#�M�
���h�v+OC�epXQ3��f�U]vd��a\�$d�8���j����[�P���������m]f�'������^�U�`|or��|\����8g��K�U}��.�c�>
RFM9�b0�R�Yk&;���	�n���V���S��W�%�y������&������MX�A��AU������"��nI���i"�Op�b�#���r�b�������(w&�h_���UYM����*���E����)�c��A�}0S�4�����2��9�����~M�������h����mGb�M��-����/U��*x\���z�sKU&Z��jk^�i��������gfa&���i��j8�,~c�5�J���2)��V�zs�J�2���\1�,z�|��3a�������d4e�J�e#C�'$����R����$��q����,���=�H'��w��Y�l����hB�ZlH�B'S4������D���\�fx"/��Q�����W�v�Bl�/UZ!�QY��tJ�5����Z�`f���z���� V��ci4EQ�
�f�'u�vs"2;�U��b�8I��gs-R�sR
��K�oy���B :*���!�Qi�,*a��� [������������Z1�_�"�I�6'����)�Cv�����a�6�Y$3����V����y["��v&B�jA��Qv�%�������0�=b����j�IiT�h7iM9������G�Z�${�
[b���TbfM��V�?1/Wb���h!lrLlR~�Moa ���^M���_���~H]��gbj��/o<^)����y�}_U��*����B�p�cu�fl<W������d�Ycs��������@s_��Q������,�,�v�7@�M�/y�h��/������;
g���x$��xh^��K�1��cA���������7]�AG��l���'��|�%���-�}��%��F{=���r���N��WKl�MS�/sc�ni�<E>�5��Zxk20�r���
��l������*.�6�k����g��EAk4���XT$jX�
[z���H=#�V4����� �l&����M���f�j(��s��sc40W5��5o;�Rkk^}^�o�c&���T*Z���Y��Z~���(��d}1��`�do����]��c>s:��%!gs�"v*Q���*�-���[K��=��4533Z�O��`�����^���W[��`���@��wN���%��)����N�1��F)��)���FYV�^�e���E�����?���-�6�y	[�v�=k��J���������RaD���a�m�������x�h,����O�zSKe3 �7�h�C�.������d�������9s�l�=3��:6R���g�l3�J����{��c\���m8����-_g�Qy����������y�J�JzV��X�CZ����V���e&���(q^�����<�}���s����&ml��j��V�a�>M���M<�s����6�f�d��p��Z��3�j�zR^L���|iL{�U6v���r~Em�ak��}Sl������"k�&Z�Iw�at�s�����^���O�q&�����pQ�J�`�{���"�8~M)V\j1�x)X�,�sQ���������E[�=��&�����������-dd���L���ZP����6nb;��1�'�?�^��Y	�bU����O��G��c���9dU�C��i6����A<�g��{�i3�E���&�p�r4��D�\y�%"#�|+���i 1^�����U|%�[���-�'C���Rd���8sg�=9�&�R\Z���m\l���
�-Vr3�C�,VE+4J�d��[�}���c\
V����}=���G����T�2���s��7��]M����{a&v~f�-��=���p�2Z���+J�Mu�v���\���H����������a�����'��y��.���M�DD���U��U&=���+.$J k��C6�x1��WY���h����l���`Z�u�B�����a�v��P��
^H��hy�V�3��f�R���4��:7�����+J9
+�����
���_�<sD��]����BQL���=�Vx-+$7��,�0P�[o��eu��X�1�8*��7����_4���d���|'U�;�.k�@Z_\��������o�u�}�^ju���)%'g�����gzi�*�d�IU���rNX\BU�J������aN��v��:j���n�!��V3=��1h��V*��`zC1)n.��tQ7�cZ��0��q�2�%��fH��X�L���V�lU*]X�vJv��_�����QM�W�x1�^*g���2�2
�1�f����Csx�Y�3���e�����i�j�sj�f:�Y�����+�A�(Vu\����p[��
�����]�>��<��k��FP���l����z�e��J9�h����iv��/��x�|^��
�3���F���c������o7>�V@�[P�0�a]�E��7��)^o��{}?��-R7�eA��8�|]K�1
���5<S$�9��x�*Q���k� ���L����{���	���~}���*Hg�d��z��x��H���2F����b\pY�>�(Lx��,F�
�C�e�]��q�j��N'c`�b)�J�,����5!�
��h�,��0�%��,$��2���.�2ml���YT������g���R���A��V��>���CZ��N^0TX��7�������'B�&��,�!�h��5fl�Z:.��v�	����%���VdN��n^�L��+�S�$+��my�2�E"~X��	R>����xw�>����?�h���i�������x�m��{Ps4[_g������|�m0	���������-7�
���&��f���K���b�3������to����imsav��q����Uat;�>y7���������h�i�Y�wi=�s�L�r"�-���QV(��qdu*������ib�>�4TWP���&����q:��-�)��i��T�����}���T�j��O�������:����1��1��"��I����K�F1�Df8r*b�6L��u�W�P�L�
�4�q�!I��0`��
�� ����Se�y�����on��M=zd]`Kn�KR:���1Q�������Z3����+���z��y��bzR���Oy�ob�?Z���0���Z��\)���Z0�-!�L���������^��r��<R:������e�H>����9q���A=g1�o���z+���B��3K���
+B����3���yN��~Eu
%���5H{Zy��k�Nz�#�L]v7�r����~Tn@C�GF%�����������y]�w��{������&�$�fQ�f4F!�D���(��9�U������ F�M8L�4.��pjz%����3H�X��$�_�`�U�y�������]{���	u�|�k��+��W�#��Y���~?���Dn���~G�$�� �^���O����-�\��R~6�Wa����$,���J��M>��<������)'�6�r��u���������7^+i@�YB�fkd[�VZ���V��+/�F{4���b���pAh9U����W4	��N���9~p[�R;�b�G����������W�%����9��9�u�G���=��]T)Nh�W�fVN���F�bZ��,�[������A��?p?O��L�A7��(��ra���`�<��=@���������9UO�w���r����3�#�q�7�p9:#���O���~p�__mX<����$��������q�g:}�-������������y����H�w��Q�-F���!|k�r��x�o�I�|����A �����k��|�'?��f��V����z:���;�n�<�W�����}�W5R����U�����������
�'������f�6�L���4�)�i(��8��������mR���AMC�k��&e����2����n[�Fk�Y�m�XE���k`�L.;����� �����������:�5m,@^��9����K�i/�v�3�P���������X��o/�/c����f�Yt��P3N�s��;��m.��4��E����7f<��#%��Al��U����5be��RB�`��+���/�(m+:����3>����E����k��J�������$s��4�A�;_9�h�����=�Y�O���D��/�sp:Q=���(1�� 9��&��<����JsN��8�%����n��[��9��:Ay��5:`6P�j�T	���xS�_dmii�#����n�?&H�<[���cl�H�4Z���p�j��U����l�+@���{5��Y�en���-*]�	��(�����0Pg��H������i���
m�nT0Ka��LdQt��osV,O��o�w�����aYzoWR}�������_�����K"\�8��8�o|[��r|�����/S-�q-���Z�ke�JpWW�)����dvw��t�,t}E;[nf�����T�3���?�������I�n8�������b~N���+�8�W=�,6���UO�e�~� ��E2p��o��&��wH������`����m��~*����Nnr����0����l�����?_������5_�ZQK������1I{R������*�y)9]����;1�^��i#W����k,����������o���=&�^���G�O*�~ �o��UI�
�6����h���`���3����g�u?��G����"�[Q(�|��"mnC�c��v��O���_���'�\�/����[sxfR-���������4�����e^\�G��k��%>U}�Sn��M�QFMj�����������m�������
[�e����H| �b7�����hW�l	�����������KI��\����.g�fgfg������f�>����K��.��hMPv��2������������h�~z>��a�	i!M����f�{��l����^����������&p+���7�,������|�1]�n�5����c/����7���������Y	�4i���"q���c��p|B�������]]�d3�����o$����9��H��{�/��pe_�$�����[��������Ar�/*�P�����I���.�z�������K=V�o�nb��;}�m����GW��s����}����tR���������H����dq��2��������.���H���h��X4����k��T����_�R��L�#Q�o�U���{G����_A���O&����s"���sR��?'�e�t�z/��t�Su�7v� ������C�������FIv�0��� |_j������C`��[��h1B�j�d���A3H����S�O��#�i��N*��$��_�I@�F����H��������9;	g���p���th:�����+� 8hU�i�&x����vF���3Z�����d�� �I���m"�1��n���sZ���k�v��^���ym�
(������^e��2��$c�=��� �"�#�i0-Pm�y����������_�U����KTQ�zr��
X����e�8�r��m�����=�f�  ��V���7��"^�uB���k:�
=��.�Q����D���}��J@� T��`�G��I����*?�KkJ�	��N��LnR���)S��6��	�d���5��q}*�����G���6e~!O�\�H3����L.	/�+?��Cx���J����v�f�-h���M����=������M�
�c������9BL��gzp�����'U����q��|�ML��VhMjH�����Kcf��l�Bq��a�W�QR������yv�b�@��>W
���zB����X��T�)L%{���� o�@����t�~�~���!\`����V�����#-�����]Sn��yZykM�!u&��':��;��W���ISW�?G�[���j�(�!D�#(/ap�wN7��H�������ypr���0U�v-��BU�����-��r��U�,��L��vf^��%����e
���_�-�tu�xAS����
���n���,?zu6:Z����(�3�|���z����(c�������
��/K�%R�����������_������>A
w���������2��#=�3"Cd�"a�-�A3�BY������<K��GZ��T������g������t2S�('�R�$�p���N��b����[-�|Z6�*s���f��S$2?�}ut�����;6YB�G���t:{����(�h���!��GtX��-OrH������^�yf={�I��;�����'��~\Xy�2��k��/�RT��������E��"����;I���g���Dk�M(%d����G�d�Lo��E��_��-N`%<��q��$����J�5]���!�5N])�umM�_�8�tV�+����H���
4�"�������%�����'����<td:��_����_LE���n�;�@v
)�zj��%����b�[+��tfw����V��1Y��"�4����=�'y��;�����8?z���B��Ij�M�����Dz�(���4��H�7�!;�G3�iZ�3rOv��(�����nB�r+�/e�������c=�S*0��3���p������������Ef�XV��}��l��4�}tt��m�����j�q�&��)�������3A:s�W�b��V+a�fI�����n����?�+f_�!�\��s9o���3{hfv���Q�C��������I�u7���5��?W;�?����EE�:�@��������}���/�N0@B�E>�6�����i�KVcF����*�n
�-$���`'�xb���MQC7{=������D%��Tu`��<_1�Hv��r�h�����EQ��$1$����N��2~��N{r�u�}=����#!�IU���mr��H�n��Ol$e:@��
���K4����)�L�A
���T��@W�4���V�fDK>I�b{�`_�����/���P�B����M��<U�;y���=~Yt��������N������%���7����Y�&�������QG�������b����
r�GO�[4J�-����n��������z'��Aq��t��hP��#�d{�Eu$:,z��=�x	�V���+�;�b�%�hy|�R�1���U�GW=~�$��M�y!�����&4���D�� �B
>$���p���������w�3����������>}E����NOO��EI�B�}�^X�z���O��	n��$�����f��A�����������Z�3���
�y7��u�[������`�!v������y��;Ph�YQR���%��1�����,�����J�3=�\�W��K��T`��`H�������V�5���%�s��u�Sj�4(4t������
A1���
0��_dO��������
@�����������@q]�PdS��+�>��@]�����,�-�^��0�vD��O4BFUH=���ecD��g��@�2�W4�8��$A�D��F�F�p����?~)�c�h����k�����0w��]:��w�&��HP����:SI�P-e�P�i+=������"�<���t]��O���8x�q�����}-U�9Hr�K�����q X��+�y9�f�	�x�����Sw{Q����ez��������mM]Y��=��Ab��(&����'�uF&`QN�t�P�`��3X�����k�5����`K�9�>�LY��8�O}�������l�
wu��/��6��!;�6_7��K��wI@!�Q�f���S4�C�.�dK���T���aQ2jjp[�n�����J_t�[�2�D���n2���*��c��N��Uq*�'o��
�`������5:��R9������x��!>�}�����gx�:�n0�n��P+�j���!v��Xnj�����1��1��M�Lf1����(���*S�53�L/K���0W����B�����3VT~;(��������M�#�e��%uh����y
1������
X���k�z�5��Hg��K	P
GQ{�
����)���E���pw�����v���]�kw�����v���]������e�m�
#338Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#336)
Re: POC: Cleaning up orphaned files using undo logs

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The cfbot complained that the patch series no longer applies, so I've rebased
it and also tried to make sure that the other flags become green.

One particular problem was that pg_upgrade complained that "live undo data"
remains in the old cluster. I found out that the temporary undo log causes the
problem, so I've adjusted the query in check_for_undo_data() accordingly until
the problem gets fixed properly.

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

--
With Regards,
Amit Kapila.

#339Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Antonin Houska (#337)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:
Antonin Houska <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

* By throwing at the patchset `make installcheck` I'm getting from time to time
and error on the restart:

TRAP: FailedAssertion("BufferIsValid(buffers[nbuffers].buffer)",
File: "undorecordset.c", Line: 1098, PID: 6055)

From what I see XLogReadBufferForRedoExtended finds an invalid buffer and
returns BLK_NOTFOUND. The commentary says:

If the block was not found, then it must be discarded later in
the WAL.

and continues with skip = false, but fails to get a page from an invalid
buffer few lines later. It seems that the skip flag is supposed to be used
this situation, should it also guard the BufferGetPage part?

I could see this sometime too, but can't reproduce it now. It's also not clear
to me how XLogReadBufferForRedoExtended() can return BLK_NOTFOUND, as the
whole undo log segment is created at once, even if only part of it is needed -
see allocate_empty_undo_segment().

I could eventually reproduce the problem. The root cause was that WAL records
were created even for temporary / unlogged undo, and thus only empty pages
could be found during replay. I've fixed that and also setup regular test for
the BLK_NOTFOUND value. That required a few more fixes to UndoReplay().

Attached is a new version.

Yep, makes sense, thanks. I have few more questions:

* The use case with orphaned files is working somewhat differently after
the rebase on the latest master, do you observe it as well? The
difference is ApplyPendingUndo -> SyncPostCheckpoint doesn't clean up
an orphaned relation file immediately (only later on checkpoint)
because of empty pendingUnlinks. I haven't investigated more yet, but
seems like after this commit:

commit 7ff23c6d277d1d90478a51f0dd81414d343f3850
Author: Thomas Munro <tmunro@postgresql.org>
Date: Mon Aug 2 17:32:20 2021 +1200

Run checkpointer and bgwriter in crash recovery.

Start up the checkpointer and bgwriter during crash recovery (except in
--single mode), as we do for replication. This wasn't done back in
commit cdd46c76 out of caution. Now it seems like a better idea to make
the environment as similar as possible in both cases. There may also be
some performance advantages.

something has to be updated (pendingOps are empty right now, so no
unlink request is remembered).

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

#340Amit Kapila
amit.kapila16@gmail.com
In reply to: Dmitry Dolgov (#339)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

I think the discard worker would be required even if we decide to
apply all the undo in the foreground. We need to forget/remove the
undo of committed transactions as well which we can't remove
immediately after the commit.

--
With Regards,
Amit Kapila.

#341Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#340)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

I think the discard worker would be required even if we decide to
apply all the undo in the foreground. We need to forget/remove the
undo of committed transactions as well which we can't remove
immediately after the commit.

I think I proposed foreground discarding at some point, but you reminded me
that the undo may still be needed for some time even after transaction
commit. Thus the discard worker is indispensable.

What we can miss, at least for the cleanup of the orphaned files, is the *undo
worker*. In this patch series the cleanup is handled by the startup process.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#342Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#338)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

I was referring to the code

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

in undodiscard.c:UndoDiscard().

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

In meant the call of TransactionIdDidCommit() in
zundo.c:zheap_exec_pending_rollback().

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#343Antonin Houska
ah@cybertec.at
In reply to: Dmitry Dolgov (#339)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:
Antonin Houska <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

* By throwing at the patchset `make installcheck` I'm getting from time to time
and error on the restart:

TRAP: FailedAssertion("BufferIsValid(buffers[nbuffers].buffer)",
File: "undorecordset.c", Line: 1098, PID: 6055)

From what I see XLogReadBufferForRedoExtended finds an invalid buffer and
returns BLK_NOTFOUND. The commentary says:

If the block was not found, then it must be discarded later in
the WAL.

and continues with skip = false, but fails to get a page from an invalid
buffer few lines later. It seems that the skip flag is supposed to be used
this situation, should it also guard the BufferGetPage part?

I could see this sometime too, but can't reproduce it now. It's also not clear
to me how XLogReadBufferForRedoExtended() can return BLK_NOTFOUND, as the
whole undo log segment is created at once, even if only part of it is needed -
see allocate_empty_undo_segment().

I could eventually reproduce the problem. The root cause was that WAL records
were created even for temporary / unlogged undo, and thus only empty pages
could be found during replay. I've fixed that and also setup regular test for
the BLK_NOTFOUND value. That required a few more fixes to UndoReplay().

Attached is a new version.

Yep, makes sense, thanks. I have few more questions:

* The use case with orphaned files is working somewhat differently after
the rebase on the latest master, do you observe it as well? The
difference is ApplyPendingUndo -> SyncPostCheckpoint doesn't clean up
an orphaned relation file immediately (only later on checkpoint)
because of empty pendingUnlinks. I haven't investigated more yet, but
seems like after this commit:

commit 7ff23c6d277d1d90478a51f0dd81414d343f3850
Author: Thomas Munro <tmunro@postgresql.org>
Date: Mon Aug 2 17:32:20 2021 +1200

Run checkpointer and bgwriter in crash recovery.

Start up the checkpointer and bgwriter during crash recovery (except in
--single mode), as we do for replication. This wasn't done back in
commit cdd46c76 out of caution. Now it seems like a better idea to make
the environment as similar as possible in both cases. There may also be
some performance advantages.

something has to be updated (pendingOps are empty right now, so no
unlink request is remembered).

I haven't been debugging that part recently, but yes, this commit is relevant,
thanks for pointing that out! Attached is a patch that should fix it. I'll
include it in the next version of the patch series, unless you tell me that
something is still wrong.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

sync.patchtext/x-diffDownload
diff --git a/src/backend/access/undo/undorecordset.c b/src/backend/access/undo/undorecordset.c
index 59eba7dfb6..9d05824141 100644
--- a/src/backend/access/undo/undorecordset.c
+++ b/src/backend/access/undo/undorecordset.c
@@ -2622,14 +2622,6 @@ ApplyPendingUndo(void)
 		}
 	}
 
-	/*
-	 * Some undo actions may unlink files. Since the checkpointer is not
-	 * guaranteed to be up, it seems simpler to process the undo request
-	 * ourselves in the way the checkpointer would do.
-	 */
-	SyncPreCheckpoint();
-	SyncPostCheckpoint();
-
 	/* Cleanup. */
 	chunktable_destroy(sets);
 }
#344Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Antonin Houska (#343)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, 21 Sep 2021 09:00 Antonin Houska, <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Yep, makes sense, thanks. I have few more questions:

* The use case with orphaned files is working somewhat differently after
the rebase on the latest master, do you observe it as well? The
difference is ApplyPendingUndo -> SyncPostCheckpoint doesn't clean up
an orphaned relation file immediately (only later on checkpoint)
because of empty pendingUnlinks. I haven't investigated more yet, but
seems like after this commit:

commit 7ff23c6d277d1d90478a51f0dd81414d343f3850
Author: Thomas Munro <tmunro@postgresql.org>
Date: Mon Aug 2 17:32:20 2021 +1200

Run checkpointer and bgwriter in crash recovery.

Start up the checkpointer and bgwriter during crash recovery

(except in

--single mode), as we do for replication. This wasn't done back

in

commit cdd46c76 out of caution. Now it seems like a better idea

to make

the environment as similar as possible in both cases. There may

also be

some performance advantages.

something has to be updated (pendingOps are empty right now, so no
unlink request is remembered).

I haven't been debugging that part recently, but yes, this commit is
relevant,
thanks for pointing that out! Attached is a patch that should fix it. I'll
include it in the next version of the patch series, unless you tell me that
something is still wrong.

Sure, but I can take a look only in a couple of days.

Show quoted text
#345Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#341)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 20, 2021 at 10:24 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

I think the discard worker would be required even if we decide to
apply all the undo in the foreground. We need to forget/remove the
undo of committed transactions as well which we can't remove
immediately after the commit.

I think I proposed foreground discarding at some point, but you reminded me
that the undo may still be needed for some time even after transaction
commit. Thus the discard worker is indispensable.

Right.

What we can miss, at least for the cleanup of the orphaned files, is the *undo
worker*. In this patch series the cleanup is handled by the startup process.

Okay, I think various people at different point of times has suggested
that idea. I think one thing we might need to consider is what to do
in case of a FATAL error? In case of FATAL error, it won't be
advisable to execute undo immediately, so would we upgrade the error
to PANIC in such cases. I remember vaguely that for clean up of
orphaned files that can happen rarely and someone has suggested
upgrading the error to PANIC in such a case but I don't remember the
exact details.

--
With Regards,
Amit Kapila.

#346Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#342)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 20, 2021 at 10:55 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

I was referring to the code

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

in undodiscard.c:UndoDiscard().

Here, I think it will just skip undo of temporary undo logs and
oldestFullXidHavingUndo should be advanced after skipping it.

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

In meant the call of TransactionIdDidCommit() in
zundo.c:zheap_exec_pending_rollback().

IIRC, this should be called for temp tables after they have exited as
this is only to apply the pending undo actions if any, and in case of
temporary undo after session exit, we shouldn't need it.

I am not able to understand what exact problem you are facing for temp
tables after the session exit. Can you please explain it a bit more?

--
With Regards,
Amit Kapila.

#347Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#345)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:24 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

What we can miss, at least for the cleanup of the orphaned files, is the *undo
worker*. In this patch series the cleanup is handled by the startup process.

Okay, I think various people at different point of times has suggested
that idea. I think one thing we might need to consider is what to do
in case of a FATAL error? In case of FATAL error, it won't be
advisable to execute undo immediately, so would we upgrade the error
to PANIC in such cases. I remember vaguely that for clean up of
orphaned files that can happen rarely and someone has suggested
upgrading the error to PANIC in such a case but I don't remember the
exact details.

Do you mean FATAL error during normal operation? As far as I understand, even
zheap does not rely on immediate UNDO execution (otherwise it'd never
introduce the undo worker), so FATAL only means that the undo needs to be
applied later so it can be discarded.

As for the orphaned files cleanup feature with no undo worker, we might need
PANIC to ensure that the undo is applied during restart and that it can be
discarded, otherwise the unapplied undo log would stay there until the next
(regular) restart and it would block discarding. However upgrading FATAL to
PANIC just because the current transaction created a table seems quite
rude. So the undo worker might be needed even for this patch?

Or do you mean FATAL error when executing the UNDO?

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#348Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#347)
Re: POC: Cleaning up orphaned files using undo logs

On Fri, Sep 24, 2021 at 4:44 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:24 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

What we can miss, at least for the cleanup of the orphaned files, is the *undo
worker*. In this patch series the cleanup is handled by the startup process.

Okay, I think various people at different point of times has suggested
that idea. I think one thing we might need to consider is what to do
in case of a FATAL error? In case of FATAL error, it won't be
advisable to execute undo immediately, so would we upgrade the error
to PANIC in such cases. I remember vaguely that for clean up of
orphaned files that can happen rarely and someone has suggested
upgrading the error to PANIC in such a case but I don't remember the
exact details.

Do you mean FATAL error during normal operation?

Yes.

As far as I understand, even
zheap does not rely on immediate UNDO execution (otherwise it'd never
introduce the undo worker), so FATAL only means that the undo needs to be
applied later so it can be discarded.

Yeah, zheap either applies undo later via background worker or next
time before dml operation if there is a need.

As for the orphaned files cleanup feature with no undo worker, we might need
PANIC to ensure that the undo is applied during restart and that it can be
discarded, otherwise the unapplied undo log would stay there until the next
(regular) restart and it would block discarding. However upgrading FATAL to
PANIC just because the current transaction created a table seems quite
rude.

True, I guess but we can once see in what all scenarios it can
generate FATAL during that operation.

So the undo worker might be needed even for this patch?

I think we can keep undo worker as a separate patch and for base patch
keep the idea of promoting FATAL to PANIC. This will at the very least
make the review easier.

Or do you mean FATAL error when executing the UNDO?

No.

--
With Regards,
Amit Kapila.

#349Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#348)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 24, 2021 at 4:44 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:24 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Fri, Sep 17, 2021 at 9:50 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 14, 2021 at 10:51:42AM +0200, Antonin Houska wrote:

* What happened with the idea of abandoning discard worker for the sake
of simplicity? From what I see limiting everything to foreground undo
could reduce the core of the patch series to the first four patches
(forgetting about test and docs, but I guess it would be enough at
least for the design review), which is already less overwhelming.

What we can miss, at least for the cleanup of the orphaned files, is the *undo
worker*. In this patch series the cleanup is handled by the startup process.

Okay, I think various people at different point of times has suggested
that idea. I think one thing we might need to consider is what to do
in case of a FATAL error? In case of FATAL error, it won't be
advisable to execute undo immediately, so would we upgrade the error
to PANIC in such cases. I remember vaguely that for clean up of
orphaned files that can happen rarely and someone has suggested
upgrading the error to PANIC in such a case but I don't remember the
exact details.

Do you mean FATAL error during normal operation?

Yes.

As far as I understand, even
zheap does not rely on immediate UNDO execution (otherwise it'd never
introduce the undo worker), so FATAL only means that the undo needs to be
applied later so it can be discarded.

Yeah, zheap either applies undo later via background worker or next
time before dml operation if there is a need.

As for the orphaned files cleanup feature with no undo worker, we might need
PANIC to ensure that the undo is applied during restart and that it can be
discarded, otherwise the unapplied undo log would stay there until the next
(regular) restart and it would block discarding. However upgrading FATAL to
PANIC just because the current transaction created a table seems quite
rude.

True, I guess but we can once see in what all scenarios it can
generate FATAL during that operation.

By "that operation" you mean "CREATE TABLE"?

It's not about FATAL during CREATE TABLE, rather it's about FATAL anytime
during a transaction. Whichever operation caused the FATAL error, we'd need to
upgrade it to PANIC as long as the transaction has some undo.

Although the postgres core probably does not raise FATAL errors too often (OOM
conditions seem to be the typical cause), I'm still not enthusiastic about
idea that the undo feature turns such errors into PANIC.

I wonder what the reason to avoid undoing transaction on FATAL is. If it's
about possibly long duration of the undo execution, deletion of orphaned files
(relations or the whole databases) via undo shouldn't make things worse
because currently FATAL also triggers this sort of cleanup immediately, it's
just implemented in different ways.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#350Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#349)
Re: POC: Cleaning up orphaned files using undo logs

On Mon, Sep 27, 2021 at 7:43 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

Although the postgres core probably does not raise FATAL errors too often (OOM
conditions seem to be the typical cause), I'm still not enthusiastic about
idea that the undo feature turns such errors into PANIC.

I wonder what the reason to avoid undoing transaction on FATAL is. If it's
about possibly long duration of the undo execution, deletion of orphaned files
(relations or the whole databases) via undo shouldn't make things worse
because currently FATAL also triggers this sort of cleanup immediately, it's
just implemented in different ways.

During FATAL, we don't want to perform more operations which can make
the situation worse. Say, we are already short of memory (OOM), and
undo execution can further try to allocate the memory won't do any
good. Depending on the implementation, sometimes undo execution might
need to perform WAL writes or data write which we don't want to do
during FATAL error processing.

--
With Regards,
Amit Kapila.

#351Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#346)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:55 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

I was referring to the code

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

in undodiscard.c:UndoDiscard().

Here, I think it will just skip undo of temporary undo logs and
oldestFullXidHavingUndo should be advanced after skipping it.

Right, it'll be adavanced, but the transaction XID (if the transaction wrote
only to temporary relations) might still be needed.

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

In meant the call of TransactionIdDidCommit() in
zundo.c:zheap_exec_pending_rollback().

IIRC, this should be called for temp tables after they have exited as
this is only to apply the pending undo actions if any, and in case of
temporary undo after session exit, we shouldn't need it.

I see (had to play with debugger a bit). Currently this works because the
temporary relations are dropped by AbortTransaction() ->
smgrDoPendingDeletes(), before the undo execution starts. The situation will
change as soon as the file removal will also be handled by the undo subsystem,
however I'm still not sure how to hit the TransactionIdDidCommit() call for
the XID already truncated from CLOG.

I'm starting to admint that there's no issue here: temporary undo is always
applied immediately in foreground, and thus the zheap_exec_pending_rollback()
function never needs to examine XID which no longer exists in the CLOG.

I am not able to understand what exact problem you are facing for temp
tables after the session exit. Can you please explain it a bit more?

The problem is that the temporary undo buffers are loaded into backend-local
buffers. Thus there's no guarantee that we'll find a consistent information in
the undo file even if the backend exited cleanly (local buffers are not
flushed at backend exit and there's no WAL for them). However, we need to read
the undo file to find out if (part of) it can be discarded.

I'm trying to find out whether we can ignore the temporary undo when trying to
advance oldestFullXidHavingUndo or not. If we can, then each backend can mange
its temporary undo on its own and - instead of checking which chunks can be
discarded - simply delete the undo files on exit as a whole, just like it
deletes temporary relations. Thus we wouldn't need to pay any special
attention to discarding. Also, if backends managed the temporary undo this
way, it wouldn't be necessary to track it via the shared memory
(UndoLogMetaData).

(With this approach, the undo record to delete the temporary relation must not
be temporary, but this should not be an issue.)

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#352Dmitry Dolgov
9erthalion6@gmail.com
In reply to: Dmitry Dolgov (#344)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Sep 21, 2021 at 10:07:55AM +0200, Dmitry Dolgov wrote:
On Tue, 21 Sep 2021 09:00 Antonin Houska, <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Yep, makes sense, thanks. I have few more questions:

* The use case with orphaned files is working somewhat differently after
the rebase on the latest master, do you observe it as well? The
difference is ApplyPendingUndo -> SyncPostCheckpoint doesn't clean up
an orphaned relation file immediately (only later on checkpoint)
because of empty pendingUnlinks. I haven't investigated more yet, but
seems like after this commit:

commit 7ff23c6d277d1d90478a51f0dd81414d343f3850
Author: Thomas Munro <tmunro@postgresql.org>
Date: Mon Aug 2 17:32:20 2021 +1200

Run checkpointer and bgwriter in crash recovery.

Start up the checkpointer and bgwriter during crash recovery

(except in

--single mode), as we do for replication. This wasn't done back

in

commit cdd46c76 out of caution. Now it seems like a better idea

to make

the environment as similar as possible in both cases. There may

also be

some performance advantages.

something has to be updated (pendingOps are empty right now, so no
unlink request is remembered).

I haven't been debugging that part recently, but yes, this commit is
relevant,
thanks for pointing that out! Attached is a patch that should fix it. I'll
include it in the next version of the patch series, unless you tell me that
something is still wrong.

Sure, but I can take a look only in a couple of days.

Thanks for the patch.

Hm, maybe there is some misunderstanding. My question above was about
the changed behaviour, when orphaned files (e.g. created relation files
after the backend was killed) are removed only by checkpointer when it
kicks in. As far as I understand, the original intention was to do this
job right away, that's why SyncPre/PostCheckpoint was invoked. But the
recent changes around checkpointer make the current implementation
insufficient.

The patch you've proposed removes invokation of SyncPre/PostCheckpoint,
do I see it correctly? In this sense it doesn't change anything, except
removing non-functioning code of course. But the question, probably
reformulated from the more design point of view, stays the same — when
and by which process such orphaned files have to be removed? I've
assumed by removing right away the previous version was trying to avoid
any kind of thunder effects of removing too many at once, but maybe I'm
mistaken here.

#353Antonin Houska
ah@cybertec.at
In reply to: Dmitry Dolgov (#352)
Re: POC: Cleaning up orphaned files using undo logs

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

On Tue, Sep 21, 2021 at 10:07:55AM +0200, Dmitry Dolgov wrote:
On Tue, 21 Sep 2021 09:00 Antonin Houska, <ah@cybertec.at> wrote:

Dmitry Dolgov <9erthalion6@gmail.com> wrote:

Yep, makes sense, thanks. I have few more questions:

* The use case with orphaned files is working somewhat differently after
the rebase on the latest master, do you observe it as well? The
difference is ApplyPendingUndo -> SyncPostCheckpoint doesn't clean up
an orphaned relation file immediately (only later on checkpoint)
because of empty pendingUnlinks. I haven't investigated more yet, but
seems like after this commit:

commit 7ff23c6d277d1d90478a51f0dd81414d343f3850
Author: Thomas Munro <tmunro@postgresql.org>
Date: Mon Aug 2 17:32:20 2021 +1200

Run checkpointer and bgwriter in crash recovery.

Start up the checkpointer and bgwriter during crash recovery

(except in

--single mode), as we do for replication. This wasn't done back

in

commit cdd46c76 out of caution. Now it seems like a better idea

to make

the environment as similar as possible in both cases. There may

also be

some performance advantages.

something has to be updated (pendingOps are empty right now, so no
unlink request is remembered).

I haven't been debugging that part recently, but yes, this commit is
relevant,
thanks for pointing that out! Attached is a patch that should fix it. I'll
include it in the next version of the patch series, unless you tell me that
something is still wrong.

Sure, but I can take a look only in a couple of days.

Thanks for the patch.

Hm, maybe there is some misunderstanding. My question above was about
the changed behaviour, when orphaned files (e.g. created relation files
after the backend was killed) are removed only by checkpointer when it
kicks in. As far as I understand, the original intention was to do this
job right away, that's why SyncPre/PostCheckpoint was invoked. But the
recent changes around checkpointer make the current implementation
insufficient.

The patch you've proposed removes invokation of SyncPre/PostCheckpoint,
do I see it correctly? In this sense it doesn't change anything, except
removing non-functioning code of course.

Yes, it sounds like a misundeerstanding. I thought you complain about code
which is no longer needed.

The original intention was to make sure that the files are ever unlinked. IIRC
before the commit 7ff23c6d27 the calls SyncPre/PostCheckpoint were necessary
because the checkpointer wasn't runnig that early during the startup. Without
these calls the startup process would exit without doing anything. Sorry, I
see now that the comment incorrectly says "... it seems simpler ...", but in
fact it was necessary.

But the question, probably
reformulated from the more design point of view, stays the same — when
and by which process such orphaned files have to be removed? I've
assumed by removing right away the previous version was trying to avoid
any kind of thunder effects of removing too many at once, but maybe I'm
mistaken here.

I'm just trying to use the existing infrastructure: the effect of DROP TABLE
also appear to be performed by the checkpointer. However I don't know why the
unlinks need to be performed by the checkpointer.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

#354Thomas Munro
thomas.munro@gmail.com
In reply to: Antonin Houska (#353)
Re: POC: Cleaning up orphaned files using undo logs

On Wed, Sep 29, 2021 at 8:18 AM Antonin Houska <ah@cybertec.at> wrote:

I'm just trying to use the existing infrastructure: the effect of DROP TABLE
also appear to be performed by the checkpointer. However I don't know why the
unlinks need to be performed by the checkpointer.

For DROP TABLE, we leave an empty file (I've been calling it a
"tombstone file") so that GetNewRelFileNode() won't let you reuse the
same relfilenode in the same checkpoint cycle. One reason is that
wal_level=minimal has a data-eating crash recovery failure mode if you
reuse a relfilenode in a checkpoint cycle.

#355Antonin Houska
ah@cybertec.at
In reply to: Thomas Munro (#354)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Thomas Munro <thomas.munro@gmail.com> wrote:

On Wed, Sep 29, 2021 at 8:18 AM Antonin Houska <ah@cybertec.at> wrote:

I'm just trying to use the existing infrastructure: the effect of DROP TABLE
also appear to be performed by the checkpointer. However I don't know why the
unlinks need to be performed by the checkpointer.

For DROP TABLE, we leave an empty file (I've been calling it a
"tombstone file") so that GetNewRelFileNode() won't let you reuse the
same relfilenode in the same checkpoint cycle. One reason is that
wal_level=minimal has a data-eating crash recovery failure mode if you
reuse a relfilenode in a checkpoint cycle.

Interesting. Is the problem that REDO of the DROP TABLE command deletes the
relfilenode which already contains the new data, but the new data cannot be
recovered because (due to wal_level=minimal) it's not present in WAL? In this
case I suppose that the checkpoint just ensures that the DROP TABLE won't be
replayed during the next crash recovery.

BTW, does that comment fix attached make sense to you? The corresponding code
in InitSync() is

/*
* Create pending-operations hashtable if we need it. Currently, we need
* it if we are standalone (not under a postmaster) or if we are a
* checkpointer auxiliary process.
*/
if (!IsUnderPostmaster || AmCheckpointerProcess())

I suspect this is also related to the commit 7ff23c6d27.

Thanks for your answer, I was considering to add you to CC :-)

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

sync_comment.patchtext/x-diffDownload
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index 1c78581354..ae6c5ff8e4 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -563,7 +563,7 @@ RegisterSyncRequest(const FileTag *ftag, SyncRequestType type,
 
 	if (pendingOps != NULL)
 	{
-		/* standalone backend or startup process: fsync state is local */
+		/* standalone backend or checkpointer process: fsync state is local */
 		RememberSyncRequest(ftag, type);
 		return true;
 	}
#356Amit Kapila
amit.kapila16@gmail.com
In reply to: Antonin Houska (#351)
Re: POC: Cleaning up orphaned files using undo logs

On Tue, Sep 28, 2021 at 7:36 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:55 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

I was referring to the code

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

in undodiscard.c:UndoDiscard().

Here, I think it will just skip undo of temporary undo logs and
oldestFullXidHavingUndo should be advanced after skipping it.

Right, it'll be adavanced, but the transaction XID (if the transaction wrote
only to temporary relations) might still be needed.

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

In meant the call of TransactionIdDidCommit() in
zundo.c:zheap_exec_pending_rollback().

IIRC, this should be called for temp tables after they have exited as
this is only to apply the pending undo actions if any, and in case of
temporary undo after session exit, we shouldn't need it.

I see (had to play with debugger a bit). Currently this works because the
temporary relations are dropped by AbortTransaction() ->
smgrDoPendingDeletes(), before the undo execution starts. The situation will
change as soon as the file removal will also be handled by the undo subsystem,
however I'm still not sure how to hit the TransactionIdDidCommit() call for
the XID already truncated from CLOG.

I'm starting to admint that there's no issue here: temporary undo is always
applied immediately in foreground, and thus the zheap_exec_pending_rollback()
function never needs to examine XID which no longer exists in the CLOG.

I am not able to understand what exact problem you are facing for temp
tables after the session exit. Can you please explain it a bit more?

The problem is that the temporary undo buffers are loaded into backend-local
buffers. Thus there's no guarantee that we'll find a consistent information in
the undo file even if the backend exited cleanly (local buffers are not
flushed at backend exit and there's no WAL for them). However, we need to read
the undo file to find out if (part of) it can be discarded.

I'm trying to find out whether we can ignore the temporary undo when trying to
advance oldestFullXidHavingUndo or not.

It seems this is the crucial point. In the code, you pointed, we
ignore the temporary undo while advancing oldestFullXidHavingUndo but
if you find any case where that is not true then we need to discuss
what is the best way to solve it.

--
With Regards,
Amit Kapila.

#357Antonin Houska
ah@cybertec.at
In reply to: Amit Kapila (#356)
1 attachment(s)
Re: POC: Cleaning up orphaned files using undo logs

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Sep 28, 2021 at 7:36 PM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Mon, Sep 20, 2021 at 10:55 AM Antonin Houska <ah@cybertec.at> wrote:

Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Sep 9, 2021 at 8:33 PM Antonin Houska <ah@cybertec.at> wrote:

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1], which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation.

By above do you mean to say that in zheap code, we don't consider XIDs
that operate on temp table/undo for oldestFullXidHavingUndo?

I was referring to the code

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

in undodiscard.c:UndoDiscard().

Here, I think it will just skip undo of temporary undo logs and
oldestFullXidHavingUndo should be advanced after skipping it.

Right, it'll be adavanced, but the transaction XID (if the transaction wrote
only to temporary relations) might still be needed.

However zundo.c in
[1] indicates that the transaction status *is* checked during undo
execution, so we might have a problem.

It would be easier to follow if you can tell which exact code are you
referring here?

In meant the call of TransactionIdDidCommit() in
zundo.c:zheap_exec_pending_rollback().

IIRC, this should be called for temp tables after they have exited as
this is only to apply the pending undo actions if any, and in case of
temporary undo after session exit, we shouldn't need it.

I see (had to play with debugger a bit). Currently this works because the
temporary relations are dropped by AbortTransaction() ->
smgrDoPendingDeletes(), before the undo execution starts. The situation will
change as soon as the file removal will also be handled by the undo subsystem,
however I'm still not sure how to hit the TransactionIdDidCommit() call for
the XID already truncated from CLOG.

I'm starting to admint that there's no issue here: temporary undo is always
applied immediately in foreground, and thus the zheap_exec_pending_rollback()
function never needs to examine XID which no longer exists in the CLOG.

I am not able to understand what exact problem you are facing for temp
tables after the session exit. Can you please explain it a bit more?

The problem is that the temporary undo buffers are loaded into backend-local
buffers. Thus there's no guarantee that we'll find a consistent information in
the undo file even if the backend exited cleanly (local buffers are not
flushed at backend exit and there's no WAL for them). However, we need to read
the undo file to find out if (part of) it can be discarded.

I'm trying to find out whether we can ignore the temporary undo when trying to
advance oldestFullXidHavingUndo or not.

It seems this is the crucial point. In the code, you pointed, we
ignore the temporary undo while advancing oldestFullXidHavingUndo but
if you find any case where that is not true then we need to discuss
what is the best way to solve it.

As I already said above, I think now that the computation of
oldestFullXidHavingUndo can actually ignore the temporary undo, like it
happens in the zheap fork of postgres. At least I couldn't eventually find the
corner case that would break the current solution.

So it should be ok if the temporary undo is managed and discarded by
individual backends. Patch 0005 of the new series tries to do that.

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20211125.tgzapplication/gzipDownload
���a�}�{�F���*�=�7eR��v�Z�mmt�('������1IpP�&���_�@�$);�f�H}VWWWU��^��a��4��9����_���u:>�^�����n���'������v�)��n��Et~��$�����a(����#�}���N�?�������[��>���y+gyh.�������o�����m!�vy���Gx�h4�v����n�F�i4Z�"�����m����������h�����Z�O��c��N�;���i3�0������R���0z��xI��&,�>6�R���t����>��[��O����C������3t^���uM4;����y��/��VKTF��?^O��X�K��������#������"��B����1b���xb8b��A��%f�/�����.�@x3a��)`��m��S��Vs�xs{�w�&>
jXZ������o�g�������k�Cw�(�s����h�;G���*r��������q�;0pg����}�����O��gd�{�-&L�6~)�{�&B��#oV04a>��b�K
�>��z+A���� �p�YM.��E}�
��6�0��y��7�k��w:w`0��x�3q�g���������Lip�i�O�s�,&�;�X~��=[�|>5a�8m-�y3a���v�:�V���\K!���b��;�[���:\qf�p�;��b5wp�xE��9��������{X����w*S�i�����_��������O��Z8���.W�������:(����o����-� 0o�C�>�:��|{�����q2j,���<�����������x������������}��������?����o���-��a���w�G�C����� {��_�h��V-�LG��>���jm�V���U�?�z�����P-�l���*���#���Q&���7�8����
�%@�*��M�;T����YG���.�PY�ea�����8�,�#��
M}wI&���mby�a�T��Z��WkWB�3]�s;���j���Z��2��������0������y=���1W��\d��l
���*O��F�_5R�T�w|7t���q�X���w���c����Z��6t�����_����V?�d��5w]��7���8�A�����R�����7sUm�
���d�H��6�����;�������7Y�N���gP]�m��
(���5���v�C	�H�;��k�1v�`����v�@�p�hP�L��*���`��i��A?9�#.{�\�0;
mi"R[�pF��'7[j�z3�~b}�o�n���j�"A�J�!�q��s�3gk'�ie���Ch0�G+���� �I���9�����5�;�+���7_���um��e��N+�)�U�&B�ZX�7U�V��sT�4|������f�rj7�T���t�F�t���Vz1T=���y~%��=��,4Xp<�:6I���u@��R���v���~rxl������I���J�b���|������Q!9-|���|�V�k����������v�r92d^KZ���*��O�FoP��x0���,V�c�Z�n������E;�����zq�$���pB�;Zg��������j�~4y{�����+��+M�~��{'D�������������w���A;{{BT`I}��@������~��' ��������p&�)9�I��_M������&�� |/wN�I(}�"��D���
��i��v�]�'��9��cM�||*j)�VE����[��1������k��\����G?��.���P2Yj��dx3�<�E����'��_���:|�+��l�Gzf��(���D0���w�g����F�,%�A���Q_�:l�\Dpw)�v�Cw�<`n�?E-\��
�������VgZ���Vw�n�:�V�U�p)�U"�V�s���O��G/����G��]j�U-�b�'0��sa�,�]��EM���`���
���o���3Kq����z4���`_����<�1p����B=C���3m�H�&����m���&��*���g�����cq5������x�n��F)��]���J�{g����������������6�xt6:�W����������(+��pW����	�[T��9��@z*<�����ZN=B����D�<����AsM*G��a�Kb�~�(����F���a�T�Z��%���d>Q���FW���J���M��V2��������(z�-�	���J�;���������I��5��������FE���xq|���hIi��>�=;=~�D:�Q������^��F���d*�(��lw���4�>�xw�\�w&<Z����R�R���H+AL���������Z�%U�����������7���7%��������[�W��������L8e�~�t:��t��S&jw�������VY�k������x-�q>�wI�y��7���33��pr��X+^|�_�}��fQ|=���-���/����}H��-�P��;�5��OX�H��%�Ty������3k
+��4{����ZOX���V0�C+����Ii
��)��%[���������S?y����������h��xr~z1�C�|���$��W�l�hl*��	�$'��'�F���{���������
I��"�%|\9��1�p_�_��������VsoO�����"{���x�|���f��Z����(�(�Q��f2�~?Ft/\��0���eN��A	����T��+�
{>|�4�0 at/*x
\�������0�:k�Q��<�IA�P�n��� 
>wi��g����k}ykHU�Q5����VfT����/��l9F��g��i�k�N��X�������Z#�-�<��c��� �f����5�t�����H����%����������^^�}���Yb�'���<�A�~�r���q,&�����C���:�����F�Q���o4�{�w�*���~>|����-�� >2����itZxd�6���Qx2N��������z���Qf�5 9"�6���������Q
�}����(n����A_.�5o���������Z�����0�����y���UZ=�}�*�S�K�K�]k��ltk�����]�S�g����v������ ����8uT���Y�^�Q��,L�7Q,3��c���VX��w�XEm������Ab��g8��
�
�~2��%t���'f�^TJ�T��^�t����+:4����������v,1�$G�u�d�Q5�G���Q�y�1m]=1�xT��Q��jV���mW59�x<���t$"t�Q��M��rrT�'�����>|�y����A�G�IIm��hz�7J�mK����DRG�W 9�t�r:�f����irR���"wrbZ��'�H#��Y���P{���d��;��'�DAG{��Pul�M�n*!�N�o���j��V���{��	u���X���]�5�)�������h�`����A����z�{�c��?�WU��������g��fBdmBo����T�0uB:2�}5$�em��"���o��g�[�+����o��WT]�]{��
��J��X�K����I]����]�M��W�H
��."�Tu�T�X_<Q�:(��,F������m�!����8����j8F�^�4{��m��Fg�i��v����=��47P�'�s�0W�������k2|����m��s����y>�u�.��[LZ����\��^��������k����xv��>M��}"��}���bo��E��L�|��W� ]���+���K�d��FS��uIQ"�i�]��m�^��4��l#2$�*��d1D�A���D��D4�������e
,�H�x�.���;s�Y��K��wo�������	2���&Ae^�W��i�,*lcQ����#���$���w��j���'���"?p�N�pz��%���(t�:�%���3[e��{��%��{�;����p6��p� ����s61y.[�o\����*<���$ux������5��3�O�!�����;�a��\�R	!t��&41N���$�������.@�d)��S���}�1�����l��;����S�� v�$�v��U��3n���&�T���t|C���n�)8(���"�o�!�!�����"���&'���7IS)���j��������q����Y�R�$L�R%�a�'�Q�S�����Z�������y�Q��VR%^m�|��4QH���mk:h��z�i7-��k����f��,H��A�L��C"R��ot���*9X���h�/��@�����R��S��Z����+x,�7����;������]+��
��Ks�yx�"�w�op���d�Hv�������Ax��q5w���Pi�hf|��$i����j���7.�f��`}/��c+�eX���F�$.�B5)��e���$�O�k	���+�7L���^��w���
��=���\�����RB�b�.�EK~O*�XC���d�zr�s������{��5��I7z�^�3s���d$�Z!��D���~Fo'(�L�%]k��<�� � �ZD)
[�/�a������_��d�=
�����/���f	��N���y���D�OD���VP��Q��xD�������:��\:R{���,�c���u����`���~2���{��!�q����c��������DY���/��1�Lr�M����A5`#�
�����Y�nk	���4���>p�a������B���sg�*���d8��o�����?c�(��/9������/�!�G����sg&�����N��V��F.	�����W�L<����J.r|��lE6q�6@�A��L,��i�����t6.�?;�5�I���/}���^.�,�:�������Awj��^s�Z.���_$��IJ9���;��Fj{�HV��b�V�@#�� ��X�A
|=��q��IJ���c���x����+�R���H���C�W�A
M�I�7����@��9\�L��0HD�og��t�Tl'���@nS����7��G�gBW����dq19=y��2�y�@��X�����|-��M5������)n���
��,�GD���G�����q:�z}���f���6�!�M�~��^m�~��H��5S���^���\Y�����"$1�c�C)I�4Qpw����GF<��7�d�>B���}�.��S2
����>��|M8�`�;:���|0���A�?P����1�C>r����h�<��Jf���E���P�R����\%8����&��>�0s)�?�����������7��1�[��o��{�xe\����w�@�/�@������	!S���S��	Q
��O�-����|�&~����2��gX��\�`�����������+����2M���c3]�Z{*u+k"1��l�wJ�y<��(��_�$k��:o6��#R^o�Xbo��pf��28��N��nP4�t2����y�nk6��/��
�����g����>:z�b��}�g�p�ln�Da�@]�^x'�<g)����n���s���������������������l4�������fey�����.���./j�h�����m`4���"���)������Z4�s�s���,�<t�5��\Q���M�.�dX��7�a&�����sp(Z�|�'�I�k�~%��
�9��X;���]�[$����i��^�kr$ZFN?��Opl��:�m�S�[������u�������/"��6h2���kH(�r?�?~;��Z��MBZ\T����3sF��p�w�hq73w�&���dx�$H�B��lby�G�q)-��n(t��6z���������Q��'��,��U%�p��A���%�5u�}L�+���o
�L�z�����Q�LyH%�Y�-��Pp��(����5{��tj���N�?k����>$�+G�dY�#��i��G����,
@�L!"�<*�	�H��	�EA�U�A�w��5����G+�,�fyj�=m6d]���a$�B�p���%�o>0�$;��[M�^�o��<Kz�������{�T:X����W
������M��m�������Br���t�N�^���V{�i����h�p�������]v���N�\�FQL����J4�s���N6F�"Lu���(���2����0CX����]���K|�ok�=�������`�����L��1��Gb�s>q)ug�4�Sg�kw���c�v{��I��-�X�$R��[2����i[�������1){� ����OF����I��C0��JbI�GH�
���:���~���@���&��I{�&��.MJ��2E�-7��6qY7,W�����Z�����v��=���Ak4�dL�)Ifg@���D���ZSUH`��y�������RL��K��~�z$^�v/�A�q�]o�S���?���6��F���^>�'��g'R�u"�����=�;�����("�D`�>�>���Y��Ou����;i�����hDN����-'��e�[��������8���ao6�i��R���`��F=y���;s��u|Ev�_!gnK6�w��ILR~���)�p���<Ni�������4<P�/l8��d����
-M�@�n4�t6�S�7}6�R�������?�\��[{�����Z����z���;���U�B�9��a��0�z���9:�����A�����~
y)�M`���p�$}?���4���Nx`w��N���p�m��3%9�
�*�rxK����^}��j�,?R�e�����)'�X����/����`-�4M��e�����_�?b����me�)�0���$wvU��:k�3w>�������/[\�����i5Q�Z��� 7�'����a+v8��fK;m���=����U��e���F:K����9�]�*=x��-Mq�s E s���
�	�sV�v�����x��\��
�d�D���fi�1ns��l��O����-�%a�=Y��7�M)Edv���"���=������-�v2��#�d8�&
q�Z����|6Kt�7�'N��r�����q~;}�����'ln���K�#b�n�)d~���%B���,���U����i�2$�f��F��H���^S�]�	��z%S��f��KGF�����������f0\>�e�G8��$:���OiS�+��6R#4���T�6�a\b�D���#��.�D.��������h�2�%�������������f:}!�2��.��v]B��\T��z��H�5��;�0��
k���X�$�@:v�nHIe! �o��B��I����{�eQd��H�Ek�K�)�T�)��~{>���>������}������y�PU��\G�i{�;�:���n�3�21�)|E8��k��F��~e��7���9�����.�L�?���}��Z�Z���`���5�����K�;�F�2
�EDA��1�N�C��/��r��Z���6�z�o�V����fw���:.j?����(e6�rws�x��o����x��G��Sit>������,|=z�����i5H5��
sV���:q@%�L�0.}����;~~]+l!O;�9����'����;'aK4��n_�X����8x7�p���R�����T�~Mpc���eZ���
9��q~R����7?�p�Fm7��'�=��������������&��1J����k����G�og� )�H2u~q���J����=�$7����L����*��_�xon�}%n'.�&��}�������92:q�9����-������@@�K��`���v)d}�0����|RP�w�V\�TQ4H���<2_�R���H������[w����Go-=�Y(U��{xP�q�1���ab4d��L�
O�2��H�!C'�)q���Kus�2����9�Qh�3�
�Q5�����f�N!r��5/�����@6~=0�F0�{�m�Z���o^DgF
�_MpJ 2�������^[�f���i�/��P�M�����!C��]�#�5�2�K�K�\;/�o�B0AW��\��H�\���Db����<h��
>���j+���������iq��q$����mp�z�i5�(�>�������g���M�����zt����3�F�z�'���H����y-���z��o��]_�w�Y�����d����:��gV�������Tz��4��Pq��Kqz1��)E`�@��d(m�P����QLU���s�v��D�����&��6�?/��E��DWp��,b�6G*��j>/����	]�������4h`�����=�(3�^��O^�EIL��^��^'����� ���gxy�no�p��Q�k�0���|�
�("V�O�V�"(�A������F���h�x������:"ekv��sh�&� �9����X��S�~��������*���K�-���<��c&O��h���PM\%o���:%B��XwJ�F����\Z��:2`EW�m_	,�|�����.%ri��)T�0�"V����[�tl������z���������1���b�o���)"��?��ME>�Fs=9�8������G���1�������&�������4JU�Td+�j3Rm������V�7*I�b��������:�%a��0�}���uy����<]^��l:�S5�-�w����	�)���uJI+Q�F�Ma�n�Z�\]��~���v�/�)�����P��H�}H��)�0KV��d����O��������L�����A��[����X�T����UI�7���~��F����k6���l*���Vz��4g�mhK�'m�8Dm'f���@��:.{n3����}�_��q�?�Y�.F�x�=D�#�Bs��R/������/���^|?<��GJe_e
j2pB���l��
f5��n�h�W!>Ib�h���|t�B��<������hx2���;��Tp���2���FaeO�Z+/�������_isS	�`�U���v��}�	��z��C9��H�C���r�>�UV���Q:I!$�4:���4[�
_z5�E��������[R�G	��B�ap%`ZYUA!����-������(*�~	,/������(!��"�	���VjQ�4R�S����!����eD	H���_aP��1� ��}����E��k�;���<O�����fal��:/�xI�>���Lm���U��9a�����~Z��5.
�Z=�^.��z�\H���3
�)H���W�$�i	�+����_�1���:����i�����m�]�t�Cwls������r�����z��5������G5��	�
���F���]�(��32b(Z��]ft�������Fe�����a��E��0���r[<&q�#k��
rv��W����D
�9f�v�KL�@x���L3����>Yq���.n<aq�/�S�<\����64_����>�jN-�H�x��Ly��Mg�d����\�(���72�������
t��j���x�|���%��p��%�vhSTf��9Y���l_���1H�������#�8�%�W�H�$m�^D�I�Dl(�����Qu��R_/	H65A��/$+�0�|�����Aq��@�f��������k�I[b�����s�����3#��_�U��(�6\�q���/��[YxQ8�pI��R��:����u��
�s=���HG?�J�+g�2�		b��;s���Z����v����S���eSH�SGw���C���b�qd@o��M�9G)c��AX����N8+,��I����r��R@�3���L?�����P"��G�3r�����C���J�v��*oM�Q���r�g�G�f
Rw��3�w�Y2�s�-�<�Dv&/�����rc��0����AIL����C�G����"��R���Z�1
�\y�>�3i0�����$x�w����}�<	���V�lO[0=�WE���u+q0�i�db����y�\�;�Y���z}`��~��m�D"����5�eX+��(T�Q����t�e���/@�W�/������I7��mngmy���:��l��������c�"��-�����Ph�d����*����C��O���(�4~���V�
�Hj^��H�V#���)���M���f��j����GQ�-�Kk�FN��o���85
�������
��]�rn��"�+{��&R8����~1��+nS���=�z��KN�.[5������_1�w��sV���|!���a
9D&G������j.��Wl�B��j�����qi�@%��2}o�?�rV�Hz��t�Qw�E�-,:G�\�l#`75�t�������i	2���o����E7hi7�1I������
�,E/����%{S ��m���`d��R`��D��XY�����f�H�j�����Cd0�.%�_�e5uxs���xx3b������l;��=��3A)�S)t{22r2��?l	����Q���$���@���p�o1�#K�%�>�]siG���Z�Y�c^y�w��e:�����{Ng�t{�A��|nS9�^1f��<@}��4����	��'G}r���8�����M�}n,s~d�>>��eE����iw�1�i�^��0��������������LV�
�vN��q
z�:F/���xa����9i�@���� �xK�:�l�M�5���T�S������/���!�|"���^�FmL�����{W#MM���c����4�O_&�<���;��G��h����?#(3��������C�h�e����sLl��D���m�_~������0k��m��J7�W��q\�D�gN����;����s|i�������w�n���[3X�sL��0K"�n��vGq��S���O����wy����'����f�Vf��\{�����i�+�nt��#�au���>��8����|��`�0W+��^�f����+���8;�����Y	D��J�������Y����`>�-��#s���7�����8�l���'���
tc{��6��[�[Zx�``������`,rk��3�_Z�m�$o�������7��^i���9�9�n��h��fsf���{�����(*��2_��� �����;qK���C����u��@�ZCB�q��>�_<}I�$-����^�W!��c�����n��#�b����5������>)G#��N��z��n_;y+��I�������7H�I�K���B���������BC�^�+��;�\�@�,�����z��o�G����>����G���$�o�������w7��(������������b��6��������e��
� *e��|�����C��F&�'*tA� #���K^�<����v�A���y[�n�-8�k:x�@�!�D�%��Y\����t�X��d�w��'�F���#�p�U8J�-y-���f���m�J��9����_�3��')��o��m��p_�|1=�3P�����4���l�K�-���)�r��|E�T���s�������9
p ��K��7H��>B���
�����I�����N��9���*�V{��4���I}��`�����j�6I)���n	e�[��0E'�i���9�)z��s�@9s����
���Hi���G�r�T���������[������-��rsD�����?����7XN9VQl��|V�=�,GF7�����P�����,�r"�O)0�r��Z]���-��'�|r�at��������b4:���@���6���V�����O�Gz��Z������d����
�����U�I�U���3���u�)�.�F[9��|'C���M�F�/=�A'ei��*;��NH�t���nC�����d��%�|�I=����6i��)SvHn��Q�O�[w�m��qzH������F/��>���|#�jY�)���x-�:�r�
���'v��_�~�~���}w������0bB�`
	��w�3��S�2��y����b���F���g$�^��T�$����M�g5|J��E?5y��������;��R#Iw���v�wa:���D��2�j)�������x4��������L�wg�����&n;Of�^������-�)Mo���|90����%�����Br�-��MN�/�&�0���xx1���pyvYd�rT�J�����8M����Ze��r�;�����?�v.�Y&**��������.)|��#�����������]�x���x����������R�56�,�@;��2��dN�Z����$-b7B����x��K�,����'��*9 (Kk���Q#��V�l-����8������+�����l�?�UW�/�����SS�!�tT�Te�)<Iy}����R���
`��@�}�"��#S[k_i������j���Xyb�xy�A��@���n�[3JPEs�L���id-8�m����Tq��������1/�}aS���!�6�<���CL6/^D
'O	W��n���';q-x���P���<�`&[�CW$2�����/o������,z8N�:�R��.��v�FJ'���r�_�A�v�="�D�|P���������0����6r ��%e�I�?9���#�G	a�E��'R�/Q:���������f/NQ�S�u:#<�X���2�����I��}�MK\N�����N����z�i
�1h��[��h�m�m�����SR��:b�����^��d�=�7|�����5�T1�bO��� �]��N��K��?��L�q�&����6�GLlX:}���/;A@g���
LD�d������Zi`+TW����
;���t$N��]	�
�#hRF��+!��i��6����N������Q����I��&�SD���&l�7T��M��+����(R7�>.�o�$9��"��=5��.���5lV��l&��m�����j
F�F:j&���A�1���LuC���s���O��@�mi������c��+�mk�p2�ab�jZzZ���a�t��w��L������b2����&�+m-�*��l,��v R~lI'����g����$0�"�mc���q�X�9��������N�p:MLTm��m�l%��e-n��Qi��GF�J��_F�4�^��%�<%���29���WQ�~Y�.���d�����d���G���EH�>iuM��)���L��d�������
�;�~F3)	�L��`���*�\*uE�;q�~����%���QD����.��������Zf�ql��V��t�tV��:�N1~�ed�����~R�&!��(���3R'��~T�e����j����fi�`��NKHA�5�JQ��_��!2NW�q(kP`�|1��zD�6�C(��D�UA�Z-���Ox4Y�R��d���n������;�x8v������_x;���qIH����=s(+3�#y�0�K�O��;�V� ��������kZ$�Z���{G"'9J<B(���i%31��Y{0hO�u�v����[�-�T���N/�������uh{K�*�b'fH��nI���G�����Zf�i�B���$�{*"�	�?D*��U�n���F����e�\|���o�j��N�c���_�it�����zl�����a���RDA�h�.@ad|D��.pbr���?*����V]�n5L��Dng &j�gv�����6���3����LzS����R�p����*�C��R>�����H)_������L��
@�l2������v�;���V�8�$~E���w�k�:m�I�vV�kO�}��s��h;��i�Z�?�6E���i�E�tVe���O��Q6/(��w���Qn�\�\���P�����f�,����&�5@���d�&Cc��A���z�8cdx��;�C)�Tv�+��sn�+�kG�JD^	����u�N�n�1�4Z���������b�{����8��	�>q��P����[nI�����+�$xE�9�l�,-���t#�S�J��V�?�j'x6�;K��l�����d^��d�����f�/F�f�*\vN������I��la��B��W����qW>,��B������,~A���Z\����

DA�R�:�*1N&+���V�Tx��	~���9���H$���������\�-��@����;�����o�����K���mY-�{'�Biz
����2����k�Q�T���w��+)���v�j
{V�w-{�2{�~��ZYK�[V���.���(�/�U����8�m��Y������f�%��$�%+S�Y�J��${O�X3:������/��
�L���LH
d=�V��e������FIga~��8��L�����0A^��W�45X
@PVG�rwH"[��1�_�"�S�<���)�R�)}�m�vr`��V�
�1u�8�V���Y�����n���2�<Y��Z1R�Q�j�D����_o)�o����}�������+����v��Y�{��#��1�r�?-]zX	���N��5c��Q\.Z8�|m�e�3D&�(9CT	y��Nwf����3�cw�N���	�W�R��"�4J���3
vfH�c�����v����cpaL��s��`����c|�@��:���1q��dV�T32D�!���\��C�LB].r���DJ7� �f��"�O��i�(�PK����n=��P�v��sjkaB���nad�4m����6��a�F�a�i$��u�c4�x�$���<{���v������������Pi�d��r�;�����2;��c��������%
��oU��ih����.��L�E��7�y2��]��~�]#�lD�{���)*E�C�8�`bl�G�����9��v����[����e�����!m��GY���9"2�����k�����g�|�e�t�n�������::�7����
M�?J�����c��3?9����}3cK3h��[M�$�+M�Nl
�'gi����Rrc�Z3��u��Y��V�i�z�]���Z������Ez�O��f�6���x���K4CC5��
���G`^l�f��@ |h�D�T�����
{�S:������c�����E�����v1�9:��AO������{Px%���!O� ��o=��C����+������fjP3.�R+4/�a���qE��L�N�L�z�D.�)F@����=y��qbi���xd��U�n)�����x�kt#���R��Q�^�`�:��:�����JJw3��L^z���f|������i��+L��1���f#i����
�����z�+�gB�az�������HQ�ed���"�j�[o�9��t�.X=jM�'���D��C����Y����?���<>K�Z"l�*K��E3yp�D-��#_2�����[�K{�A���_i�l��<n:l�1�l���|�����������iH������	��8�OO���~ke"(p����do����`�u7K��6v7���y�Bw�`������%�_�u~�m'���Oz�b��,��r��U�
�;3ms0��8�fJ�!.B�����j�BU#�!���W�~,�U��*���a���z�!�P6�d��XHb�n���lL�d���<Mi�o�
��t�,��0)G>)�K�qn�H��,���!�MB�:f=�����;Dn����'Q0���'���.1_���D{��k'��R�k��l�u"�GZ�f�6%n6�DH��E��g��a�-\+���{��[�+
:��HY�tg�,)@�����Y�oU6q�0Nisu��V�&*�R�nB�h��/U�'�>~���M����&��H���kJ�N%4��T�t���	�?�6lr5�E����7��[�;��z�������V��8��8����A|������l�_�=v�'�b��P"�q+�d:H�d����p�!
9����A���l�J\�&#�yh���s�� T���Q��MVG���r�.�0�a2O�O�z��F�na����@S�Mq���,��8N��N�a���>=��iuU���n������Md�?3M�2��z}��9�l��r������~�-vP�VRL���f���������$����H4���{�H�0��!��b����EZ��r m��r]6���l�Hya�P�	���=&�Z����Lx��e<�����;����[=a6�F���f��m�~r(�(�I ��H&X	�}�(xP�"������T����)ei�iQ�r{�y]�	�)����o�[F���������K�;l6��a4;G�F�y8���`�Z�`{��v��)��tj��L@���A_�����'�k����0�F��kt�vC4�M�S�E4~�yG��x�C17�g�{���>���{����)��O
�i�;�~�������ov�75f�����(��Y	�'����h�r�c3/�pzKw)>x���)�5��d=�A:V����u^���uM4;����y��/��V[TpF�����:V�R�t5�9��pd���B�%9�h�|����7^Tfd�C
���q����{�S�--��fyK����Io,��e�oC��1���.�H$�?���g�-G�!�~{��	S
3���4(G��C��l�K&T�[@�-����lx����������WYG-s8�.�p#D��T
�YW�598�����
�.V����wK�A�rtx�p�
u���cc�<J'��e���p��Q"��>�����O�|h��=�������'������`�*�j�m_]<x�5�( ��S�a}DBoa�|���}�M�e��_��z�*�?��
�	K���L���=�c �Q��z��e��.��X�U�E6]��
 [�g�)1"gR�_/�zh)Hw�W7���E$1z�*��R��9������� �t���In����cu���O�=�Z�V�%�
f��J
s��K�B6~���H�����k�Q���3YT��`������t|3�����������&��n_�o��'B!|�<��e��eL�'�IPQ����"� u��{J�a���l%=�����|������>��C��q�5]qrnv%�p��N����+�a���_��9�_��)��}9w�E5}R��(�G�����fg����M}��������&��/.������pi+�cc+RCj{��w�hm��ZM�m�p,l�a�������;h��H
�;M�O������_l���7u��!��EX����(���Q��e/|'�qk48�-���:�FE���e�Dr�B�����=P���p|����W�K& �
 D���$[�?��
2�4��)����������-�
�������q��>b�i�$��HE�
�	�0Fk����]���t��
t8aa�@l����������j q�a�����Q���N!iId�u?=��h.&""m
�>28&`��t��m�aap@�c* 
uuG��!�A��|��0��G��L'B��*���c�8W'�$b��	1�A��:�:3��* �u����M��0���\�.x	��")�	D�L��FYHB+�GO�KQZKX|�_h��m�a����W�]!	E����@McN�l��BaP5�w8��)�u?@)�R}ru�m��.���(A�A�$Z�c�(���P���s�&�2{t�/	�A�~���!	<�z��1�/[�b�d�s�������l"�<8����$;7Y���p���`B�%p� �$�A��9FI
�lu�E�f	��[�����0�Sz��x��9��r9�3��)$���|9�&�~l�ID���M
��)��ft~���7=��x���Pi����0���T,$��?��nh�O�.�i#2�`�-�xKt ��w@Y��c�$I��/K��'I�����l9G��~�'@�"��y���-[��:i�,E���P�Y����H7+D�q�3�������h�y���
EA3�%�6!HY���]�:�;��V���0��Y<��7�O��$��D5���h�����x�!�7�E����(���>��\OnN�G���fr��������@�ec�xNJ�����L\
\����L�~�]�";?�P�4Q���C��I�o-���M��ES���
�������p�Wz)�k�8�&�����+w�T&���f�%�t�\�7���h�$��e����?&I�W����$19G���)��@�eTa1)s�<-�o��6������?���8����#x��$�P1���5�X��*��O/9�$��h�^�����������#T>�YS!���4Z��*�b12{�p�"v(��������$�P��P=b6>q��#g�P"IxA��x���s���B�,�[M�
����=�������;s{ 9;d��5U����e_a��PL�����C���z�� O)���ipfb�� �����>�55��`'
��p��.�|���zI�0���\������&>!��������[8w���Ia�n�����O�[B0`�P��S�2��aV���D��
h���&/���a�����K���
1�V�^P���L�rNp�*�F����FV gh|������������"J��O(���KfQ��n9��n���Uss>U��tWe��RLg`L����;
�G�����J�J �M#R��^�\���S_�o*�P<������n�t���jK8�H��$����&������S����x9�=��&�d�R�lQ�k��ao7$fB5�0�g���~�A�>"j'4?9��X�"bl�n��5q�hr|l�V�P/�;���Ivd��'���w-��7g�F:�-9� �NmIm7y
���0�
�D���{������poHeH9&|Dg�3v|�%��uW�'�����L:�o��*�$��|�rX��d����u��f��G.7����h�x�k^7�bg�����/J8Ad|3�M�~=zG�H%Z������z/N=���n��x��%��������Za�4�C�R���e�JF}
�G�v�C���.�848y��e�:R�p����jD��-A)2P�l���
)�u��@���>��tJ����HU���T���i*Oe�h�J�Vk	���SVI\���-����J��W`�\�74�����"�G 4��Z$�H7�#%E��8s�<���v���7��@��=��r/A���=��20���7����V)w�(���
Y=d�`��f���CE[�
Hqt��������I.2��3q>���d�jR&&��jM<O)���C���\��L*�qEI�2�<@�A]y�woZk�Kl�-�S%J�%��P!�m(���Y�sei-�=���[iB�*)QV�<������h=��?����X����5���ww��)Fn�o
-��K��<9g�]q2�m�5�������R�P�T�v��D�J
�/�
��ZO����u�X�h>��8�Uv �uPyO����uhv�2�8u�}J%�28[���
9G[L���-Nc.��	�?��S=�����^��n��]bh��R=5ejX��4~��5�>�3����.E�Z`�g��T��7p.��DkQiAkB��d$h��g���n�P�����Fy^���w�d�8�QS9d�lK�G����(X�[)
��q�g���Q]�

Zw���s�����������y����+RkP�G���@
��s��T�(b����Yf���V{��&�E6��RA��������F����)�{�����'���o��	��,7�TVv���K�f}�j��\/�n���4i��'0��]25Af��g��p�����G�e��������MZ�E�����*���7RZN���D��43jx��0g�9jk�"��f�H��z����H.X��#o���x5��"9�0Q��i�,4���oZ����������4]���v�����SGn�BEooa��JCt�I���%DI��35��
8d
����~���2�K��K�I��@F��/2$���G�'�5z��Jse�w��r�fa�����>�/)Rtsy 
����6�qI�a��$j����6X�����l2��K��,[���y��\,���:���f1����a���8�M��^�d���56��`�HBFl��@����jr|�U�O���Eb���������'���d�>[*��Z�/�tz
�H��|��R���
�#K�7�	��gx��A�b�z��,2p�)����[6�L`aJ���r|��z4�(b�f�h��������*X�d�Z9�O�"���<
|)U����
n�[�I3W(�fD��Jk�$�k���������t�5�X�I��uC�u�������z	fP�
j�p�el����05M�%����R��r�Q�j��4��u��(���4�P1>�o�M��TM�'�*h�C��
��rP�o�X��E�:qzz�w�w(�$6"��H�+����[��~��Wq��z��N���t��D�j��&H��R�(*F���
�'��h�������YmoO�~����)#3�[5������c{����� ���H����T�3���T�����l��Z�I�������WuF7(f���^�}Z-�T���lN�1p����������}���r��Xs����y�|~�V�da�I���d�����&�I����*���D��E{�������%�~OyV���O1������m�*'"���Fd4�6�A��:6R������g7�hbx�)�������=^����'��^Q�D ��������kq�i�m$9����1Z���w�v �Tu	H�'�K
��;a�I����)R�� �[4������|���*|%�^�	�`��m�����rf�lo>�����^�AH��7�T�3v�&�<M�I�bZ�;J:��b�39�/�����~%UHZ��BB�c�
(OZ��Y-��V��zv��7�fI`��DV������a��,~����)�4L��EiD�Q\M"������ya����O\��	A�2��Z�����C�a�1�S�_Y�'��k5[��OFF������(���pzF�����
�eN�i��S�����~k�2[����������?!Zb~&����>�-�)���5�*�U�G���@�a��}�������,"+��9�,�f*���6I��d�O���l�[;k�#6�6vr��n���������/�M������7���T�A�7�-Q��*p��q�6���Qq(�����z2�MA�8"�U@t���T���������hT���n�aj?=e�}�Y�v*���h���h��>h�@�hI�8	2/�]�?wB���M�3����40d���f,���?�WZ�C�8!3��<7	� ���`�T��]4��_r��/W���l5���������.2��	7�B}�hd�('���d�"�-t�u�O
���F�W�4�	A$-l�(h+����2�r_$�#�}����oh*[�-f��������'�|�������;wC� ��"������b�$F4��g���1�o�Z@&��7��������T��N�q�,��a��$V@_'���A?��(x�h �.�.����R��N��>`�	�v%�[/L��5g�)�����C������	{w����l�E�`�_;���}8}|Ia��4���.W�������+Np�8:���������o������?��}��4�����&g�t��������n�W?���c��0�y�����c������������h\�z> 3&��6@��8aE�$��w?M�^����p�'"�E�u�F������^*��`S���f�k���9�\v����	jI_-m����"�i�
'���_{
�V�T�(gbj\��j�L��t��4�^+8��R[����{�������
���.�z�L�3���e;�#'���A�0u���p9�- �����DM��E��Z�P���� �
�����z"�HT��2F�;���V�9 �@���u��iv:���
i�N�� u&NfJIY���]���2���l��d�|�U�Qf���x����������7o��
s�f �r^��,��GhS?�[@��N���eY<�d�����2��%�p�0S��n
��#�����O^9B9��J�x�`�����tr������U���!�
������v~�.:V��8���ufj"�z�������&IyAH�^�.�(���N;s�%������&p%�l+q��z$~�7lSCM�g��#��<F��e���u��D��]�^75L�yp��~�dy}v����[���M|���NU#���������1��\x��u
�9��<���$Y-�����+�S�.�Z� QP��P����(����j-I5Y��p�~���P�������a�{iDQ���y������)F�/[�(�������]q=����%���S�_�KZ���"� ��l�c�+�^�QR�vfA~o�fI5�
��k�RV�9��t�VI��-�?��+�F�u�����S�����}�U+Z��3	[����������,=9���&��<V�(����j���X}���X^����lg����-g��X�ujfQ��0W����\���%?����@�M(�PJ�''WT���z���#�4��A�sS���3(Q5���_�� ��{�s��Y������E{aX�[�=b�]D6J��*�D5>���L��F��	�n���������Ny{M������*1_�sb!w��������	�"
���J��N
�����
$��zy\aa�\nb���D�R���+�Su�Z�6�R�9��jD�����8����X#K�7'������������0��i�>�u���4���HD����J�Q���o�V�������^�����/Tv��S��P��������s�"|{rz=F�hf��K�� �,���K��t��+��]NC�q���"XQ���}��["yWd�]��Zp�R����pL�)���	�:M�*�6.c�"$��b�����{����,����nO��qa���p�(;���>�?]���1�f�`�Q����\��G�I8�++zP���G.�G��-K3P~AT>��MY�p!����Q��^oO����7���v���k�n��d�F
x���4�h�z���E�
�}V�M$��cXyg��l>������G������U�@�A���05(�����?��{���8���"�����jCKm4���=�>.�{��f�cs��H�����qOF7��N��7��r��/�l��������og��~�.���������<��v'S/p�q���1���P.*�����]��i2���Q<@x
�X�:~�����"��X}���v�r��*'������u}wI��������k�o>�xqr�������(
�y>Q3�g���sW%��9����:'�z����/=������������+%
�'����'
z���a���x��=#;$���l��;2�����D��u����������|w�s���2��2��Sl`���]�G���c5��8x@��Bi���y��BN1jkh�)�"�x�	�e�@�#�R����A���g�������U~U��z]�h��j������#�&F�����$���3��y.��e����7��;�4��������Y������h~�o�c���g�B/���em������*��}	����I�zq�)ub�Htm�BH��t�@�EN��W��-/p���$9���C�0���=.-���R�e�����P ��DF�z�~r}���<.�V�F|��jx�����Q|���&��j3�t@�/���D�NMD���'TE�5�o����K�P#�����|@�S��i
	g��P����9�����
�g�����E|k�l�-��B�n���r��O�N��Z�z������ibJ�-��������E,�S��*~��7����y�������*����!�H�,�}c�QWE�����=�e'������^�W[9�I��Hf��U�����z�~��A1�R�q��Z0�{v\aC{*^�"���8�r�HE;��ld#nA�XJ9���)���S�*�o�KF������L��3�@�����FM��8�o� -9	�l�3���^:��B	H"�r|/������R��Um��i�FWI<�]%dAib*6<�9��2r�t_EQRf4��	�9$�l]�h��a����[rL`=f��
�Zl�����S��j"x0��S���g�P^�i�`�j��'����.+����cSC�����I�����j�23]��\'�}�$��������)jZ�?�y�������^,��:�>����&w�&��a}��$�������\|y�8Qo~���0N�|���x����{7hwZm�X�QP�Y��(n��v
��x� > @x��.���L�c4zm���F�C%��D�8���i�����������h (v�����=�����T���7���K��|*��7i|��������s��F�f�����&�����d����hVQOf�;�l��������*YXq�t�x5�� ��0`���Au����k��}4u�����/O`���������������c�2�bfaT�Ep[y��!��FbXTw{w|!�����8��Q��u0�F��(�+����1�B�����(f�Tq#$��8<��1����#�G1F�DR<�2�a�����?�X���,���Ty���>+Q8O����f�����n,��R��'jO8��y��W ��1�q�
7���DAn�q�
��!qH3���+1-P�H?���|��`��7�8�>��������:��:��)v�����s���_���5)
��4��fc{4#�.���\$�.�_�%���E�������q��R��w|Mu��So�_jc0�� K�Ve��dI�	F�E'yU�����B(xzq|�v��������'����H2&�`)�k���U��'�==�e�9�c�rU)����B�r��|$c^���W����������;B�{TN�?DKlE��W^�Ba�^���P�@��2�9%�j��.�(F6��s��h���d�GJ�Yq��^�6(�����d�����r������Nf78&�Z[�gcW���i86Y��<*��P�>c�j�?&w��*��J����r������8�(�����7�8�#_d��nT�����]��i�=X��a���<;��+�>z��+�G`?nG���D2�����bL�$I��@��MN��D���<{;<�NH?�
IG�����e.�iR���/3�r�R�=b�����#�~�����4�������������1��
��$
-�a��ID	�%�`J����|�G�w���.��n�Y���4�N��Q���e��.=�����K�D�:E,��4:t�1��`����F1���{$�d�x���3"*���)$�'���p���y>��X@���N�WxLx��P��
H�NO��-M�����Pi_�%���^���-<$�X�8+fwCx� g���	������Q�d�'�Lt,���������fz?j�\S�+�M��|���2,��(�t�G���x�e|z����qls��p���J�@�*F����s������o�[�A@�?���#vFg��U�q��l�~<���S ����x\������#����K:��*���T\��V�j���N�����m6����+��F��,G��Y4Y�����T�@#��2kMU�.jg�i�"Mg��S�i)��=����s�d�n����"�Q���$��z|3��^&�5
�*P�,|zA����#'p�P�OGK|�E�P$>t����#�E����s���$�.(;|{���uQIy���JO&��Ds���|<T��1V�Y;�3E*�W�j��Ba�"��oE]�O��`�NL���:����F�JtC����,�<�ON5��z�#��]jc���J���y��)C�Y�r~�C� �.�\uS9��9���	�+���m1��up�����U��J~�]�������[d�������A`�-?��JJ'�;D��&����N�JI8m,��]�?�����,��3��43eKF`~����Avip������H	d[R�*%�]�}N��8�|������Yw��2�d�W�x�����l�K�F�Do��|��6�q�1;����}|?�X�����X���4��T���r.�"{�)�2kyB%����mE2��i��UO�����]�����Q ���R�k�F�im>��dF������@���Ze�H�u���^l��p�D-��O\7�+f!G+�+���L��~]5�;���
�}�%>*����`����}?��'t#����.���V��������}�f.:F���r�+0���Q\{Q��9^�hd�\`�.F��9���&1.��
A�j���=/^k��]�u\�l����c��N�qm���h�y�>#b�#�m���\
:y��07��-c�'q����4,�k��FC����W��(�5,���^Z�"�����N�Y�O'i�V�����B\�
!0�B����V����!�����X��R�kNK�!��Kc��C$>�Pe����7\��Mu�+-%��S�R��Fj�����"5�k��R����V��{�*C��<���I�I1K!��f�������5�~�j;G��ON�XP�\������%�?�P�G���$���&�es)�/��{�Z�~a8���3�xS&��h�52W�f'�F*y��&;�F�W�4�0�i��3�g �,L\L��������;T��D	n��Q�������K��c�@����C��^-zJ�9���l�`x_3��U�'|rA�s�Ta�|`��;'��[1�i�����8>���9����(*W�w����m��� )����A>�z���;\jv���&>�<:7c2�$?F������+��Z����P�as��3R��.�C	���J�lW.:G�q2>�oFQ~��2+�}t/;����������`
CN��G�
*��'��
K#9h�
�C�Z��q�&���i��K� ��B�,�F��w�W�F��Nz�5J����nt!	-?zD�����7��v��(�]�<��y�U|����D��z�#�y���kBN?U��o�J�$�0Q].m��+�P�My�<V5:P�t*E�QY��Z_m�j}�i���V+���y�|��X�9%�K�Y��8�����8���XQ���w������>��k��]U��(�R@��]�\_�G�Z������0�ns<~1����dT��s,�A?���yw�t��#���{eG���9�����0����P5���Mg����*�+�e�XB�/06[_��-��t������*]Q�O�� zH��{������
�i�nn����
�t��{i�b*��b�y�)L_6���X�?��\E9�9sh�K�%���$~�P�4�
��_�X�J�d��d ���]^[��������K}�"8�*K8c'���n �
O�:��?�-��� :����[��"�y�~������?�=*/�o~\Ras�7�E��|���t�,�?lM{���#���qn"�������f���7������i��b.�kn�p	*����b���pc4���gjF�����2W���c�Q^!�V� ��8����G��i^�u��9(�J#I�5r7�� .	����f*�0�1
��*�
�,�LQ�Q����m4
�M�s4
�m-8��w�oW:���8��Y�z�.d{:`���W����J<����� �����6��b<0j]���A����4$���%j��y�Y��-:�9��S�-�I�h#�AGj��a��`U9p�$c�Z�V2��Ph����ZqiUi\�Qn/��2�!���C�%�������X56���=���j+zH7A@Z�g!qFRg��T�r��Po��	0e�1�����!YV�ih���8�������jbpc�v��B3�30��V�\rB���eE���<�����i�����h��}������sUMx��)��/7`D�e!,��I�6P�u�����������GQ�f�����(��kTb{���}���h����?T�+���O+TA��~p,�+���p������c�&�!��6��,\D0�,�&����&I��/�7����w���Xi����GjNT�?04��N�K@�nI�{H+(�*cr�=fX7��d��������'��(�V�$g� ��<�|6����tvO�	��}\`D\��t�M�!V�6������o��JH����3���st����]u�w9I�����K�|#i���[�9���wZ��������`��J�������;:������i��X�`(�KO����K�7���vah#o�BI*�U���c���@�0
@���3�vw���������A(�)�v$��[Ck�mn�����pQ�/
�pC,����
��7��?�	�r�=��,_�R�����y���s�����_�����r���GxP�����wp���y�!m:����g��[<x��� Nte��L2#pZ0���Z__�<��,i	k�F]b���K\�7��,���5O��},Ss�g���qO��y�� ��!��(�DU����;Z�I��{J�S&��y�c�����Ym~qK���gJQZ�
��$���1vR�ye�&y61.y�ot����FE�&d�b��a�����1uyKuc�;����a�]�?���'^`�	��e=p���a��5A�~'���skK�����3O��7hX�bL�	��dF����!qR�A~�1s2-@'T�V���o-+����%Rs�J����������o�]�8���_�Py���~d�d����Sw����@���M|MN�1a��"�iD�@�1�8
P:�-����~���w����~�R�����Qn���F!��W���g��
B;o�j=}:pv��C����x<����QfDGC����=�;�R��0�6�8� ���{X����yCd�Yf�z��
���z�k!���fC7w���1��h�0�-�8���>�%![��B-�D�#4�G�&��".9aT!�c�X�)���.d�\��;�����p��Q��a��bI�PN(�~���i2���tf��+���J������.I��>9����u��t���������,����+���Q�k������l�������A����y �\C����Y��\)�lc���Fv����a|]����n��er�n��~�����J(�L���C�o��0k���[�m�*����e�j�<W�(;��kN���[*��-����h��}?�g�)���7�:�����!y��C�>��%s�~��6�i������I�Q{R����g�X�W��`�'�<��U����g��~�zEt���hU���g��~�H?H�*\�Z��1�Hw�jU.��)�?	��J�B���c���.�![��WL�3�#�����}����zQ������m�>�!���S���x�����H����I��Cp.w�%��{�����0r�v��?��t�������E����N(�6Q�����wcc�
��>C����P��W>H�����?�E~��������rS,�������qY��)���h���(� �0���Y���zY^����S����S�Z��2!���}�5�i������������E���hs��3�$����[V���Ta�zv*�k��L9?�(��o�.j�.E�;�M��O�sg�n�6o�����`���K	�T���S�D2���*���9:6$w�����%SI��������� U�_��f���r2}-�������-�\"|	������I���� ���,7eS�6��z��x|�0s�`�1��O{���La_bCLH'�����J����F\��1������e�X����d��`71D�e
"O��c>��q"�rs
`j�;V�X����	�k"�`���B8��q"��2��AX�O�aLa!?��U���Ta������^�x�D�u�����,t�J�8R�41J����%Nb��tIk��KIh�<��[�OxA�8��<��&����%�_$���AX�3�9}���(�rk�����L^���&�����=j���{�U���rU�-yc$o�����
�/�	�#�v�2&t��������g5~���E;y)�����$�G7	S��Z:O'9�
U�r��]ah�th
�2:U��ss��+���u��b���m��m?_x�`���*��?�
���RW��=wTn���2g`��X�N������������<�����"Pm�0%+��N�����@���b�V����-�����}7����}�������n�����L$��z���~��LVl�w�����l���<]te�N�������tK�����p�sh2wj���_����T��p\���q��}��4��1�/d�_}Ko�=��_/y��mlC�������+>5�m>�L�[zD+������ ���J�����#j:��L�s�hn?��h���j�������(*+P����I�/���:[V��>g��S�B�5r���ag��KZ���`K�<j�����8��,C�M�#-X\����#�������������D-N�
���j@3p�h�?S���
`�x���
`���,�iQ��/cz�D����� ��L-���
��y��YH��nE���j���k0�#4& ���Z�eIb�~�&�AnSJ����7}�x�Q�iv���&�q��	����%�A+z� ���O}_�2�]$����!��
��9j�
fs�R�ug�5ds�'����%�	%d���g�C�%*O�j�u�_�q+�/-�����g����U�Y��/��/����� ���	7�Q>%�t]�y�X�\z��]�|�	��]�=����,�@&��lxe���I�p���5~:��y������gO~��v��6�X��ulG��]K�6���&)9)���9�^�|�g�F(�(�s-��������:�\fW�[+U/^��"p��!��?����R�F^m��,����0oU,�C3��D�,������
6��?�%�����:+G�c�'�IHT�#�f�K�(Hq����N~�G���v�M@��4�(J�����i�����.�5�����Qoh���^mm�N6������D��#QMnu�4G0)$q�@���/i,*���G�G_�m��6��=A:O��
�[5�y�����ZqS��7T*cTeX��FYK��/���o�n�Fi�u�h�T����m��m��5��B���.
k�`�N������Z�������_+��@�{�v���W?�4�����?����Z
�������=��g��M95��m��*D���i
����K����1~*�[��&����J��^G>�2�61o,��f�SR�(L���"������[����<�Exb<I����!��)95��R�V���b�'bpe�P��Zh��:�Hl����D�"�@K�������+���o�v(�@�|����c;V���Y�������o�y�qXd
��������o�
�]-�I�G���%���h�N}��T����Bt4��"=Z���Y���9��H�~��wu��YFh-�i�b*Vl���=���y��AsMJ@E����nK���hE�.�5d�����P���t5����[����};��04�#�/^y���c���>y�����lk�w��:�������.�}�����Fk�����m={u�@-yL�Nk��h���fT�B�qo�
=1��`����m�a+�u�}U� ���B4�#S��A�����������B�;d�b��>X�@T������!���QG��f�����X�T���I,�zU�	��?������G�����G}���:��UI�^����`�3����m�@.���O�[�L�X��n���y�uX����l���b���xl�S�����(���)�)^�r�FH�Q�m�T�����9\�\1���T�\�����L��^V�������l��%�����So�<���}M�Z�/E�4�W-���h=�~���b�����v���<]%Z�j��u�\'s
U
�+�2�Q���������
�-pt�S�%bv!���J�����,�v�5,�N��gy6�A�Y�E|%�G�G:z/:`j���v�N�f�y ����4,<���d�M�����n����p���R�[/,z�d����<�T�������
��GCs��
m��9��}��t;
C�����$�rG&�u�h:g<�����>w��w���|W��|3������/�b�?^dJx�#�J�l���������L�#_��gH��J'�'��VO��>��/��a�M#|I+��������\_��$(����q�3���[i����C��������t�s���}0r���y�z��H��>��+\�������|�l����,�W����T5P*�	E,�<�_q�]q[�;y�-V�O����|��
�w���]^�P������
`D���s����ril��U�-��UfC}�M�YK
W.WJk��R���w4Zk��D<����"!h�E��jv�H�,Q��<sLf�	����&z�k<cF�����%�-O�=R�Es��D�M�=�#��2'�:��O*�\��Pl���@�6�j
��z����e"���?>������4dq���+�+ze�q�gZ&��z}�����:�j�1V��c�\p���((H�a�r�=d9�������=��s�����M�C�#N���,��Q9������������w��i~��$�Z��}..pf>�,f�a�j������fb�e��^������6���4:��$�D�@��*��e��o��Im����*�D�F�c8�hk���dw��B_9
j_����k������bn7'�	+@�@�K<!<���-E��������n ��F����&"u�-��Y`q���.�9-��eb0#�Q4�}�nU\��l��$\d�V.i\lej��	('�	�r�L�:���\)Z&��q(!��������Z>�(���N\cF�Cs[q�Pr8�%��
�!E��GQn�t�5�iR�{�m!���K�W���O<�[R��V^;�m���}p��}�$��d����7�/��}|�����n�h~�}��M}���-P���>����1!*���k"�Z��F���������O;'Z�>�T����Q��iF��)�oIv��)�*�K����/���,��#�t[��;U�ve~�Oh��+bL��Mc������`�p�- p�olm�n�I<#�+
���Dt:�|��"�y#����C]*.P	j���8�����r���~�����m<��4�+%k8e�������iV8b�������9Q�S�aZQ���@|-���d\��i���/�U���S����Y���X��=
�\Ex��"Xa�B����Bx?s��������s�"[�W
sz<{�����O_�2b�����*�����n�a��{/"�����A8s�`qK
p��q��H�H$�W��6^<����|������A����5�+�~2���1�C����{��)�F���T�g
���/�>�������l@��9�ImaR��Z��(����9[���M?�����^���\����6�}����~a8�N��2}��ad�n��d�7�1)�t��\������l4XE��"r�J��:���b���7�"�M�P>H��7��sq���(��6�����x��M,�H��~+F��Y�g�/!�p����t8��5M����R��Z>�=���t���r��}�1zm#��?��mSj	�S�	�-�-(�)�X9y��W����H/�=��Q����b��\b�����c�:Hy�����8?r����J�c�y��}�1�|�c��o8�w^���x�[����M����2�`U!���p�PI�'����q#ys��^eG��'��E-��(o,1��y�3������4�����n�g��t���)a���to��O��t8���Q6.Z�����w�����6kV�V���%{�%	�����3Z�������������~�?�{l�["P���c?��^{{=��^�z�~��r���b�����Gkk�������!"z��!�	[+�'Tu���~�23,������^�'���/����|�b�i���a������"�[^]]]��Li���M��������$Z�G��o�d�r��*7XsQ67]]-]����
}`�EQ#�i�{���b��b_3���x��L���i�^l=���G'\:�A�����5V!ER0���X*�;L�1Wf���k�%\���l�M�0=7��;����
F�3h����Oz9P'lI
i�7�w���:	����DT�A_�&�
=����)���2'�����>���/���x�r���Q7�����7 �L	MHd�J��7>��>���C5[ 	D�<q�QW����\�F��'�X�7rYAcA,K�"�b����Kp\������$�/����,t��]��gF"�
�����I���}���jw�%���;#Vl��&��������M�<��"��z��0��0�{O��
��s������7D&�u�G��:�����K!�AB��p�5#s"�S<5;�<�\�/7_�@��-���^�mB����<j���:�&���<��Lb��x��~v'
9R
(3.G7=�1�p��&J-���f�~���l�%���e��K�����B��N���B��I�+eO*W������S�9��cc��[!Uv�� 3���L��F69,��9q��.d')^����]����;�c������O/�+n��6��e�kN�ist/�������z�L���p�0��
�8���$���o�it�c�����)���c����������O��?vN9���O���k?�8���c�E<�;
k�������:f���
�R�����l���N>�C���{��|����"���)Z��~���{�O^��?{�$���E��-.�K��q������l�������I��8^L:����?�Y	���]R	�� �i�'����|���~�N�t5��B5sro16�]�A���~-E&}���3`cf6�r�j2�;0��b6�'�T�(�$��(\��)����K�r�p*��!�����H������Q�������
��o�����:�do���F��fw2���xZl�%��O��P�ni����l��5_���?�D��t$�������m���� _d������6�����������g��y�����;�zmp�E�7�[l�N���fFbO���o��:���V���`���MZ>����/6@��B�����(��Y$*���l�'�:�����GR8�o�9�p(��
�R�7�������������� LU� R�up}M{Id��<V�zm�o���2&8��Mt��c�?���]c��Q��������#��cc^s���+���yO�D�L�!�G]
�����_�u{��e��I+�9 	d3�����e��tv[�
�F88�I8z����%����dH+��E�����BR.�nD����w"��J�r0�1���h6l�{��?�edCj:�����<��v�D��X���vv����A��+��������=�t�(+r|ftss��v�K�hPY�w�g�����c�U����t���|>�������O��M�#m�������tu��B���q����4x����EB�]j�!���a���Qf��o�$���<��k�"5�e��w�[Gd7��
���|�|�tc�oT�'������"Xu�;|
-���F����"H���k���3m����]�!���:\9�~����k�v�wva��3�v����������)�k����:����Y:��}��O���W����
��&fD?z�b���z����sb��/�����?�ts�td6�S�a�u�]S������o5_h�T�M��l�>���BG�5z�w�����v��������;-ry���������<�W�`g�mD�E<^��SUw{���	��|vb,3
�c��_P=m��P�#�Y��7�����3�r�2M�����"3��������y�[�����g��h�k��V,tN�0�$��Z����������m�	���)��r����p��T��,�t�<cl5JM"����������qD�I��m:�1�D�*��f����s��n�j!��b��;��m��##*����[ZzJ�E2��X��QJ�W:��_v�aaD�(��g��4���u1����1w���S�W�@�|�+z���4���a:*���6�?�9z2���&���^�"Y�FJ>9��\H�u����d(#*>W�T0���Lb)���8I��uz.��.�e�6��9G�EIx� �KK�xz������>����+�FS���#���u�������yp���Te��u:���C�����OgC"�$]��~������!=t�?��"��U�?D�*�=�M��%��c��Z�	�6���i�_��|^����/��v4'�P`E�L(�+�AZ�2��;�����\^I3�sJ�2��%��z�u=�J�"�������z>���12�3��k��0B^��|����o���e�Hg��$���|�]1�=��R^P�M��w�	�vL������q�yN�`$vJ#��s���3�������$�Y�K�dC\�D��#�1r���{P0�f��i�G/6��3o3���P��V��*
�	����*�������p��Z�y�G�Q.�A�����8{��t
�'���'�K�P~�MzE7� !R�Mu�i�8������)l7�k�n�h��JY=Mv�����G�c}��kqJ9&�Cx����8E�$�0U��8���b��b6�����Z�q���P��vY����l�GBB�,�@$������
�6�.h*���h��e3�$*qvQK�Q�h�[�e��������I�TIn�'��l��dl
]�6WU*TY,Q�p��8u)bZ�)���Z>�Jj�,|�[$�%�; ���J��^x�-�Vkhb�s�1�DnpYy�P�8Jg�P�Pu�	�k��H0'Bx�f�~��T��9��FK�����\H�g�u|:��Y�����,��#d|dK$	g55���c��A�G���a`8Ik4�����_��B�"�C�HwB�O���|�uN�?�~<�3�u�8Z�Yd�	�D��S.$}#��i������������_�DG��:;��������^J�^Z�}||x,��L&��t���R���"6�r��
���JO����8���w��|}7\iE�Q���@��Bs����8�/CH��������.��,�Bxs?����
 �"���JLb�M�������Z�4��j��&w�^\��z��E
�����m&���`mDr�sx��������3�E�?
�Z�my��b���
�b�4Hgd~X9�e'%�9�e�)���������Z������(�i/eJ������eT%�4;�Kw��g�2

jMrQ�������[�.�Va���p� �E������|l���Y�����G%g�{%��;�u<�1U?��^�F�� x�'�<�G��*�o����"s,6\V�����=NR'���Q���$����t`W.�(H9KI���������e��y���WS@9�m���/�D&-�z�ZZp��S�Im������1o
.����T\��;r�U�����������J��(!}Ff����P��:�T')��4��P%�
��%^��Wy�@��I�Ul�@�R[w��q�p�?���>��K���������|�?������1@S�� �����(%�5q���9������8V=;&_X�xu)u��R��F��&9��AuiT�����h��@@;���($�^����:/L�������tf%�"N@��wg���B�Wy��%���eQ��Y .b�a����C�f���|�d���pt"�Sv�H�D�d7�b	��������l�s�cs����]�t�PZC6j��|���IN	9���6��)���L����N~�\A����*��>v,�-�+#�(_r�j0�F]1���A|f���W4X"�n���9y�4*�����	�������*����q�������o����J�[�P�1���+�����t��U\��@��
t�y�i�+M�T����A��<�6�o��u���2�ztA[R� x��������s�#�|f���������i�7���7�����6�����B*mU�������!���PA�c&}4����;'��N���A�|� �]{���*,^Q_{��o������ONH�����`�S�GF0��{��������d�u5������}1�����h����QF+Iy�*�d��R;�wT���j( ����O����3�X��v`rS�!�z���'I"+�^}+;y�j���H�:����N�M�
I�3GA�Z<1e��PU���Q�P�8\������E�iX�����y�R�����j3����/���� ]������4�#�����vwo�t��{x|�����w�O�/�!P���f�'�����)_Tr+Y-�w\�l��y��/�Y�XkW��7������6�z�\��o\�W��z��_t�^����[�&D�7�^�����9Y��=�"�^>�o=�>O�����r���7�|�����$��a<�/P����f+y�!�r)�xf�Z��� ��(�1� �.�Q4_�6H���Mo�/SgUJY��(UD��g�4�Zf��<�5`/���B����:�����N2���/�G����V�\�VD��dz������
���
��k�_��2���Mo�e�S���o�l��z��Q
���R�b�v�]6�:6����]Q�H���n������{
wP��x�~�gO���������9��)����1Q�\��)���yB�a��c�������Y�������-�
�#���8�/rO�xBP����zPl�8�������sv
8H�rk\J�J�7�;���AS?9�< p
���:���u/�p[�O�R��.���r�������9�2��$Z�����(+_%��Zn��{�c,��/���?w����;��;�~l|�>�W�V������X]�$�G��x����@O�1����Qj6z���e�yT{����R'��[���������b�K��<~Bsu�2mTa.�]|�D��H�-G>x�?{x+�(k�p���	G.<z�!k�(��\>V�r����.
	��0E,����)�k��>��I����J�n���~���9��(/Ox���MF���	����l����w��G���MN�.�o��h�w�e�t�
b������T0�/�i��~v��.y�-��?&������A�$E��PP��0?���$��RFe�&	b�hk_�C��[k���w��P�V�m
V�8R>�X����h��Aj�j��:c�����a���C�"z�O���<�_�P���N5Jo���������PV�������B_���W���rJ���*O�� &���{�&0�#'�l�_��n>���|@�)���Nb.�(�x�B�u��~��fX��WIL�$����n5Ie1��^L���'�A�1k>���P]=���L�9��a�4�eGVy���������VD�=��\������8��S!����9'���I&G���Vf'�uA�K��b [��5C>9\U�w�$�m�y���p\|�5
c}�u<��)�]+�� ��<m:VQ�1F��r�`��p7-q�5�~�������j�^bVrh:�++=���o��V�"�s�(���&����[��\sy������$S��-W�5�D��E�{�E����b��6|W���wmN���R�!t�+RXV{ao����ln�PlN��I.���z���[='��$��\^��TME5F��%��W@�NS@QKB���� �|�QR���,�AH�8��CB��=����#���e6��7��� FX�*-�k����P5�W����]������`���h�0(������[T�S'�R�zO1&xD��U>��84����%���zJ�,u��O;�L�U����=��k!:���	�%�pv�dI�/�t�3��"����U����-�����R�BO��&�x{����Ow�vrD<��=�3�It����!��G�����������cq6���'���|�.�������D�ux����wO��`9������r�l��]$]x�{�5�����D�Z!t��V���sw���fF��p��b'��V�
	r���?Ib���}G9�v�8(��I���'��mG�+�,{�����J-����ad�����u�Oz�0��Ca�\O��������*I(4�H:���D��j�n�����mln���t�%��H������#��Y���[:����Cy&�,��E�� ��K�Y�<�D����t��1�W9�&�7�����	$�L}���� (���:5i�v%��t�=�J#����V�4���.,x�|���(���>V��<&�A�g�d�$E�����G�5dk)$S��[�a�B�ya5\`��e*'��<&	���7��D����z�P�3�A8$%<��'�q������S�D���h6��Y��v����aoR�����#��;�
�����1CuQ���/<���uZc���`
��o!�?�?}HOM�
�mx��yJ�[W	���V�ru;�����e�1�!���<��F-\�E"�v�6����9��eACq�tz�$�f���}��n��[[p��.�r+��Q�-��e�O���g2���Z�<J{�������4������qT'HP*��8��*?�)j+/�&-a'���?���M��������&{2C��������9�!�3$�|E���
#������w*����t��G�9,`�Kn�	��J��{���
k+e]/y��[�:
ev������};_a����wWJ��#O�D�}���;><��<)��u55�"�7S��x�U���xC@�#QT�?����e���=�?<�u�������v��q��������.y/PM�� �x�_,G��i����Y���g��1�����,�������������]k��Ys2o#M�����)�T�������@o��v���%u�7����M`'��mD�����wh��#�*�����Yv�K���E���3�5�������$pBa�������c��K�"x��^Z�Of
��|�EW<:O���&��/O*T�V`1)��;�t2���B�j�F
�1�����I�V���{V<6A����h$����*=f�����_��,!PU�������5�Q��d�r��WF!Gn���A�d�e�[��@	�G6c�sbMoQ���q.���ZQ����������_V(�(zlU��9Gv?{���\��}pxr�����%�3#�z+=�p����0�I.��%tv���3��,4��������C�#g>����x���m��Y���P9�s����u����|����i�� }p��[����d2a=�����-w���.��M9C���������)QV>(p���lq��q����,r]T�oKtF�Z_�����q������}#�h�+X��X
5{��'d[�����O*�rEX��s-%��-X��<���y�y6H��[��(�@��R��,��8���,U��K�W�"FY3��
���Qvu��56H6n��E�����Q�>x����J3 ��!����@4��+|�����]�!�%+�}�}��v�CR�/�;xH�����3Q1� t����x����-8/W����B���q�zRa���)��d�o�&���K�����uc�9�+��YJ��=���
�}�04�MM]���X�z-m�!���MI����P��V&Wu��Kc�����lV
+��}LG��VE����M��hc:�K���_1���l-��*w��Yp�#
�h`������-A���V#�w�]�,�Bp���w��^��X>2��	�%gy,�����Vx_IY]^���Z;��e���*$���8���L���yRdgQ�5�:��9��g����O
�Y�g5k��kt~`�N��F�I`�����*���5�����p�����Z�W��!�Zp"�����5�?MZ%s"Zr����
.C����}�-j��'��K�l�G��R�8���\������dY�N�l 1z�2�	X���P~�������,+NB��*G�`�(��t��Q����x?���y���r)�t����>m�!�n���r
�\	�x��`+V&�sX�m������
�/�H��������/��!����H�a�����ms.����	lEf)t{��C��D�T��jyOa�%���/�s��'HV_(�����_�6������^�G���D�P��p�3��l�9t"\ ��Rgr��8e���]�lg��:0p��~0�[��8-8��%��$?FAY��7���aUj����9�u�Oe%��-n�A�e���w~�������v��
K.��K1����U�:�����a�� �e}����{���� &PA���Ij���*Q�8������Y�r`,����JN�z~��>�~�n�%�>����!��4�D��u��V��g������W��p9�z���#�����U�AL�Yb����2~ldO;&~4
����;���U~�@����l��L��w���V�������]!��m��������_g�/�v�T�#?�9j�]9�������}��)&0�I����k��:�&q�K�N)����4>��ys=U�c]r�C���{��~���0�d;(�9bF��� ��D=;�-�������'��	,�jj#�$b�3\�
N

S���}�F��m���������@5#o;E?�mo5�DEq�de��q���/��k�3Z�������@�Q:�Ni�S�>��$+����5����gS1W)������Rw�l3���������h�Ifh�
V����:����:�+Y�k�I��c����l<I����!@�
?�!S������)b8�j����]�E���1b����7��2�iL/��Y|�r�w[Zt�����A]Qq��	t'������oV��;}�����L��C��-Z A��&��<�\�>������^�s����~���o�P���H���5%D`�`��PMb�$���=+���r�������5F��K�
-{����Q�����]��|P{j^4,���vD?5�b��&HX�3ax��-3���bs�q.�	�4Q��-7��0j���2Z�=���U�y=�~�#>C��i�b��p� O��fq���sF�cE0+����S������	1������bRG���i-N�7Ah��o�;�]�}�;�-�����*���
N���T�<�
���m��q�8�q�-%!N;��hr@)��U�Nu�rC�Fe���l��sYK�(��<�^��p��rG�.~Bv\?1G4c���"g[K�P����w��	j�&��2%����%����[��GJ���p��M��1���0����@�~2qp�����W$ws2���$Q��Ac[5����7����w�~S�0xT�z����'9�����oj�
>�5��$���|�>J�<�\����[�����>PX�����Uz���&��c���1K��NE���gNx�i�Y��a"$���.�m�/m_U������0���������5�RC�
v^�>�d��U�^�G�gZ#5�|��7O�C���C)�	gg�6�!�^����"ZB�j�[��-b�/��Ik�vi���~S'C���ij3���E����pXs�E#����K��9�E���M�8�%�O@a����8��k���$����	���b���m*,2��WhB����G�'����c?���7���l��wi���
D50'��o��hBU���������B���	��k��sK����1j)f�?�\ (�r�22_�N���l�u�/@PA
5yR�<�%?Z�G�k��N�?�����Y�i��b�2�`?��
2��"=H���>�o`L�@H����4&���@n�UU�����Z]�U�D�c�!��MmU�i��Yb�>��^?�M1�r�s�"��y'�Q>1S{~\�(�TN�����C*�w|����E-��
�0x�,%V�=_+8>rNDi�3�;^9���H����������j��m���
��A]�:F
���@	���"#�V/k��q����B��d4g�a��s<el���f�>�z`���7�)
�����Du9~����!����L4���9]�*��KA���_b0N�s��0#��f�6y�GH���T����cH7��;��������;N9�.p����|�GtfI�Y���}:>���8#���e��l��R���J4^9�9�qR����j���YI��EWE�����x~u�5����
��kB��Uy���o�uj�>^D�4���g3��g�_��xR��t*f_����2^�3��H�~:��g�-m�L�Qg��d�2`�Ri�1)�\��e��j3����&J�f�����0]������zPL��{�:������N�OfXC����+��$�9���c4�a/Z�Wh��}-z����y]B�c�)RF�^�y�`E�,.k��i�$-Z�EQ��	m����T���!�w���'���XYy�����*�nC�Ld%��P��c7UFT.R�9�y����������"c��O�cJ�G�����W����v]���U��~5�r@^��,
	32�.c�����24��A����R��5�@�M6���-�J���%N���2���ue��/��5�w����E�A7Kq�;?�T�Y~���A�����
)�����~z~� ��U�R��)e�;T-b����d����pL������k��P��hAW�}����b'"�?����Q:��\K�+��0��?Trb��0'��s���i�d�e� �s�3�i4�+��=	vY]k��F�`�\�-��R�^>�I�6�`���
g)
������c|�
����d3o�)�k�|%�1x�VUcLO�li�RA�0YF�;1��0����x�Y�2���6Uj�	���,pO��(Z)~�X����Q��W���������c�����Tk��Ag����D+S*�+~�C?!^���i%g�Q�F�����W*"����i������:�}f���d�(�N��W4�������V�@dT�,���3��3����9\Jb�i�'���H&|`����Q`b.�IN_p�k�����B:..�(����12'�wc��z_l���`�j��|o8V����G����G�����c�#�p�v;97��h�����,������G�O�v���������*5WW����:*�����FxdTq0���M<%D1�*a7��w��)T������$F�b��7l����SN	�J�4��nW(�ksy���5#t
I��%�;:$���tk��b��d� �El/nc�B���0q0���b!D���r��8������>�\L|��� �|�c|���X-?F�g3�lj��;Z�!�(>I��r���Gd������<�=�|6���3���!�+A�`^e��P�4SY��R<c\`���������(��d�|�'���Fo���r���l�e�2�=svE�|����z���m�Nm��:}w�H�-P����)�Dq�Y3��<q�D�}%ow|c
yQ4h��W�MDH%]X,4�[/��(QO'T�cZ��A^�������T�^t��$���i:�?��m6�Px7����)���K|u���
���������!|z��`W�� ���U�*B4_T����r�Q^.��0�d\At��d$�t���:kJ���Z��:$�k-z��p��Rd�z>���)������d�4%��=-�>Q���Ml����VaP�G��#&��aE��7���.q����C��������T�W!�5�(xI���$��ta�������c���E88.!�f�$���C�OB{������5;�
6h8�a)x�����\���s�(��@�v���tJ�jYR���<a`������?�dH
��{�Nz)�E�x"�������I����4�a@u��Y���62��M��Mi�L5��T�������e���WP�~
�uk1
��]�I��_/WA��4�w��B
>�!N�[�R�w1�slJ1�]����������������������K�\�mrt��c�V�lX�f�s���wD��Q�S{��v���mo���O���t���}��K���F�����k���Eb��4�0���.j���#�%���=�f�x������t��T��H�����7���#�qe~�]x�� �%K����,�1}K��zw�(T��J�����#D�Ox�,y%���x����W�K��[�	zWy���N���9��R����"Pk�\�1]��f��^3������'������K�T@:��*��`]����?����q�[|��9-�*bB"j�W�)��jhQ&A�,�v�Ba/�O�M8�pJ�g���A:*��neH����<��v��?"������n�k��q�@��=����i�=�j���� �*��W
��c������������@ZPK������UP����/E��|DolV]�V���Rt�]��')<_�F[�C��T���"���[��k����]��,��N����.y��a U���Z�O�c��.������_z}�%�V��X�T�*��HxK����3���o*L�!��C���P�\��>�5�B�?w��?��!�Cd=��d��A|S���I��wrK����#�%=�6(a�Z��W+�C
�Mz��Wl����|I\�����hs�n�5�e�M������^7lTi�_�������	��d3V������{gI�U�vn��G@��'P��������^*�GB���\����v�������Kf.4��������~�� ��Bn��'����,zB��C4�t��aI�w�[i.F�����|�G_�y����R�Y�]Kz'����n������q�|l_�&j���3[��\E�Rt�
#���~��n��1xG�&J�A#"h��(^Hq��=�)�;�����.M��	�l_���51�X}JO]E	#n�X|F��S�����!6�MH-������o��U����'�=�,�A#��t���v��,� ���Z�����hPK�}�>D��(�+���@���h��������l����:�{����������e3�o��A��Fx.��$E�Z�������	��[��z�oA���*e�$���\Ra2��\yX-�D�4EBJ��L��e��?o���G����
����k�I)�=�M�C�G36\
��,�}X����]��"���%z^���b�W�0/���%�\yw���������D�|����fVC��m����IE��*"Y>���e6o��1�1�����1#�X��\f����T���E�>�qT=�0ax������d�-6���z�m�e��Qe��]�.��������5�BJ�kNueH��^]���c�(��i|'&�(v��7���l|��:�4�4c��G�k�2Q���h�
�tae�`��"�H��
o*���t���{�1���I��w[�
��g��+�S�0R#��� ��i,���{��_�Be����#��G���lr][Mo(�]ic�����\�/1���!��mN+.�@
���,����HA,8�(�>��}C�z�B{��x���p��KmS�?���c:(�L��s�0��4/�|S�?r7Z���+}��	J�e��mf*&��
-�	��y% �RI�D1��Q��q<a��<�Y�$���u)wb:-���Q���U�t�������s�K�eE9����F������	Q�c_�_C�J��M���S���Ids\��j�$Ij� t�
���%��P�k��K����������i���#�9��H.H���\Q��r4�"�+i"A���;Y^�8�����|�2cN���CW��\I�N%�:�V2��x�'E�Q��db�1/#�B�
�6�������,������4 ����u�p�3ri�*G��4��t�9�'��*<(���k�XF���v�^��*�V!EvW�w����>i�:WH��N--�*�d��Ne
����*���F����v �)IUW�@���;�q��<H� (�����i���K�A��V�8c�VW
�g�-�_��-$�<�I?�������3�����d��T�
-
�r��$~]�`����B�w��m�1������O��%�St�$��[��&f��n�R�;��$��1q+B�m���v�|8yu�������Q(�B��v"�U��A��/Ed>�a	��I��V�������m��d�\�R*f�R���O �9�_@8����[��y|�����_��t�JB��nU��k�x@�;���^�����_h�y!�:A�Q2@M_��8M9�Y��,kB+;�x���JFl23.u���������Z �H�Z[��(��`��l���XOuc��T��K�Y~��[����6�xFd]�&\�-�b�Q��-���/�����N�M�~�^*3�Cl��!0^f��fcP[����U�?"
��R�-���ywi&E��/���L����$Xx�jB*�Ly������XR�����XM��M�P��4�]��?��d��;!#��Se5���H,��F�ELKh�f����G�2@|�M����e������@����5(H�y������tP���%}y�������\ ��"��,kw!&��j���������Y�ht������F�H+��n�?��,�nBe��o~~���k�Z!;�g��p�������)<����g!8���B�6Z����PV����P5FA��i#/�7��c7����+��*$
�D��-E�k�sA1.�����GCAO��Sk�t�12S��\�e��F�2G0�M���(r��]Q�=;p�*N0v^O��4��-�t�'��J�������r�cp�
�.�
"E#��-��+-Y�Ipy[|&�\�hF����J+cY�x#V�i
����+�9'�2v�],F����w/E��T�wT����|�����U�����R���I�a V�O����o\ Gd>�w���e�T����/%���%�b��Qt���I<h��p=�UG�{�0��[���[S��K?8�c��$���k��<��~�e��/��2�$}.�5�^�=@{$�@�����)�iJ<���:�NV�����d�#'� x'�;��N<# �$����PU�����R�=Ij����F�x�)�����O)����,��6brA�����3�1��:"�#�Q���)@�"��f�
�zB7����\Cvn�������S�7P���T��|�rj�D@tZ��Nq�t]�+�v��P���T
�V�\S�
�8�cK'7����%.V��w	SO�"���~J|������]{8Wxj�.�\�"H��Q-����{%U����������6���6ak�~��m�s�"dVi:�<(6��m|f���(Tf�q��dJU�#��
���A�V8�D��j�/��k��}���YNa��!�&/c���x~�'�v��Q��dk�~M����6�!�W��)}������`o��*		�c!)A����{�	]n���T���MFr4��v�>��n[��wf$��������v!����lLn������sb����7S�s�IB�b�Qa	wti���r�h�%*-ay��SL����F~�L�
������'���I���DT�����s�c��8<]�h��_]-y�o�O���@�G��0�yT��N��bZUD(��ce�z��Q~��$BL�������/T���	\�q6��[�������l���:��5�@������S�2�	ep��g=�N�;v6������F�8�����J�B?�{�N�\���*��y:������z��CI����8�����'�U�f	h�K�G��Hc��S�(����ud��d��h	+��FY��J��Z�Z�_i�*Wl�]���yl,~�H�������5�b�����'�j���{�*!B�
&6�������d��O��a��V��<���`~�8O�ewh����(�/F&�m�6����v��#B�3S�"F����.��0�1|^�����Vk�z*�����=�v�|d��l�
�G^���%���Jxx���+j���~E����� �<T�td���4&����9QN��-�!fy��)� ������d�CK|���$nEf��-�(���#�mq�����*�z0���q��q�PZNG�J5.���Qn����}c��7�G(M)OM��=�>���)�,�8�
B��2��'1�Q�B���h$�9t&3��������N�f�f����G7��>�i�~�	��!�kE�Z�(".��9�7�i|-6�y�������.��GG��O�/_�dW�����'&����G�w?���q��vAX]T�1����!����G�sg��K�o;*���Khi��I��zKK($Dv�s��+��"�e��9�L>�v��^����>c���hy�T��%gL�?��������y�!�<�>�<�����^�{����wO�L�ut�>n����I��
?O����wI8&ru��$����=D�i����T�����e�\�Dt�i����|���RQ�eS�e�e�t�J/�Y���6�Z��%��<�|	���i���������R/2I4�]%�����8a�:��k�lE�����agO����\�i�))-���F(�B�C��Jz�CP�"�n���hN	�G�U�/�4HF�Ks�q�Y�^wX��9���5}����M������W�z�RVR�i3�_�t�-!wq�gz���&��Y��@N]+M�f����k�QY�>b�-��m1j9�C��J�pvd���)u��<����������80
���w>������o���	���a�(������
�a�Si��,��S��
�R����pxN/�����4]�>VRH�����t6��q��g�8@fO�K$V���#$\`�X��,HQ"s�_g��D�.L������k�]4�.��MoB��������62Ks�*�c������M�'4�c�Y�Jd1/�/-�	�%�O��R��b������:
lcH�X0w���L�<�g��h����)�$j�q�P_�zU���A^E�Y��s���~I>�c�10�d@��G��{��$7+p)�����+����+��!�C���`4�����R�����G+��nE]�������!���a#WJ�.��c5�l��T[]V�����]��ZM� ���YRTb��HF��&��Yj4�I�j�|�R��mJ��+}�U�yV��T�")�/�!�>�����`�3��}:0R�s��m��
����H��p]����� "�;����iY�@���4�����[Y��p��d>������K����s�����^�h�x�c��}�5j�F��k��������l�/����S���I30��^$$����Fj���g��e��Xj����)Ojgw�#�qq-�����S{�O��?��hs],8ww+�
�L������2s����c��XZS���:'g�9Dl]�A�Qb����6���q�	�t8k����iC������
f� kM���&���U	�*S�#B4Z����z8�g|hz�����p�x�F���L�ddp�1#�+�����D,�Q��M���MU��	(��Q�zPNB�]�W]��ATux�c�������4�����o���f�a�6Y�?��F�T�7g��:"Kpb�r��!+�e����EH�U��*1��E���t��l�+��9����pEf���TR�����Qs�d�Z*Wd3T��U��p�N:�+(�CT�)���1�g#�V'%3�d��u����!T���B������I�sg���k�dx���z�_�"W����<6��@��VS���rw5S��k���BV���C�'��9��OG/h;��a�5�"�����z
?��Yp���`}I�)�HC2�l����y�Ca��-�lv�d_� ����|�E��-�L�+=U���U���3�Y��<
�����6�������V���}S���"��H�g9��^
�.�_n�*�r��>�}��W��hw��l����J���������A�7��H����f�8v���Tb0Vf���]�������*�����A�(\�p
��'?����(�-T��$!Q�m]����>oz���rlay�B�	Y�U�������Wi����n*>�?��R��1�!�-��l���/���T�p^����[s���W��Rn~�G�'����k�
a�����4���_��e���%��l`��b�)�o��}'��r�6�������v{M����e ���WM'�����AV��u�{D��p�����y6�A�w|x��W��v��w��r��v�kd����dJ�����;�@RP�������ig~MDv��e��y ����[�R���hY"e$Rjz�K(T\EZq|*�(���W��/����������q��OS���O����M��V���GZFc�����+�90?f�V��r�HS���Q���I���Sy�{(�(��/M��=�%�	r���UZL�D&]�Q��b-wV��D�at����\����YF%��O��b�J�J`�,��}����[���y�m!��:����Q��,��;��>SM��dcg)����RTr��v�l4P����H$n����K��I�d��5n�!u����C1e���w)��}�|���	�������B�.p����K���`��g�H\�1�k��>d�i�fb���5el�6H�%i�����t�Ar�e�t57�/�Cuq��=@hBee8K-���@d���E?%�D�4���@i���s��U����e!��?����Y0�����w+M�D8�L��AS���S�uD�@����`%<JZ��[�<�����y�3���F c6�F����h~���
�}��h�KN���w*�b}(3�������AU{Wh��oy�������*��IH��Q�Q�w���"��
?zj]y��~Jk�0�4�; ���F���U���_��	}U�������i�}m����!���~h6��5~
��t��������7�����w9������-���Mz�����c>2��hz��?���z?Q��]����R���s��|�L��b3��~��P1�$4����>�n����F�T'��J3��2>vj������k���9';�/J*��p,�:��"iG|�s%������_/�o�_.��$JQ��<�@�r|�T/����@�Q��3}6B�M��%�/qI�Hpw��2�7���N9�+�%�\�&r|�Mq��D&_)
���r�d��A��+��D�p��L�
<zm����PNr~A@����J*`/����
�xj�����?����{?�]�����CA-[*���-1���4��^�q$��]]-	U�	����[8E�j��)Lc�_G�G�u��H�H�,�$�*�zMY��g����?��(w��U1��{L'�dTl�uwPW���3{��#���~�Kx���O�Q�.
wy������w�����M�n���CR�/>�+O��_�$���0����1P��L?��~�n2����U\��_�'���+�C.��'���f�
��g��A�l�S�bX���"�)m(e��Gg)���"�{d�%��qV���4�jJp._��Y�"��O	���G&QJj�,}��'%���LP.x���������/7F�US�kdO�~h��E�6��
��,G��� a���NQFo3���p�"�����g��#U>�������vd��P+uO*Ihi��l��:6*�4�vIa��b��C��������Aw���	����o��(�������}�U~������I�j�DI93��3:�������7Q�p������j��M_x:��$�-��o�k�J�	{e�k��.~D�R��1�qr��M�������Z�i
P�����0j�v�\��X^Y[�|5�Or��&'s������x4��s	:r���D:�������l.�|���s�������OGG���F���#�_�n
��}��!��Y�2�6�"�D�3�"60UT)�8��ko�N��(����������TN�9�>]�����<
�
�O����SK+������:t)��H��ZDkt������\��Mo��|��!���.]m�ha�[�CY�8�O�r��mh����C�+���%D/��q4���I�R�-a��NH"��]������4�n�P���N��i��4k��u{R��.�.��)>2n����X^�w�N��8�-�Hq��7��v��I_,x�I��U(E�`;�����n�\#��`��?����
���H�os���N�_>Y����[��H�@����KK���/���W,4�
s�5��w��ox��d@�hH���	F/f�/:���[2��Z��L8��1vJCp�p�p��C}B�j1��j�I�/>K�h���ZbLB�.�v���R����RrXt��N�C2��7w�,O��&=���q��j??�B��B�o�������
���������{��?5�>���G�i��ULC����b�u�C�����Z@�g�7��K~!o:����l��]79����'�G��=�w�2�?��_�aF?��"�d�h��,�_����,-��W�g�4p�������-��_�r��p�3�no9����*v���;�/��x�����}��X`?r;�)@��Dj�PK#o;�*�_�Y���F[<�+`��+�c{l�P��CEr	BUF�hl�@r��(����^�'�x�����RH�_��J{����[���'�������]�����P�f��w�,8h�H+�_�P����]K��V�V
CU��]���Q
��3�F4��&�`R�l�K�(0p9�(�K���+���L.��u��HR}��R��8uS��t&n}�8x��l0@�����,:�#e�[�L%����Zf�+)���t%��\h*5���RrVI�C��0Ty�%F��(@>6��,�8���rKSC�;��n�'���CQ&1k��l�Ws[�sp����"��9G!�$�%���������&�
�5��Mj��Z�����$��M��dP��
�c�\������ _Z�*-s6)�������T��rTj�#J��F��l�[�X^
��^Eq�O:�I��"���/�:4����{p���B��v�x�)�vs�6[�������o]�����Q�qxc����-�,:]i*�����{Ss11w��^Z>S�
�2
���`K�*�	�;uD�8y%ud<�Pn�#t!P��i8m����f����$G�@Q�3���?�
$yL�t8L�����>{��g�+E�eN�y�M���.'�UZ�D�
�d�;��,;D`��>P����c�����1���R,G�M���2���q�y�(��u��p�zz�B����jT�A�t���<b/U��6��8 ����a8a�<��d]3��=��;(Pq�I�s�!��46g�Q�?�j������5�x9s6~Pe���) ��M�B�/�}��e�B{,����>�3>�SbW���������Mx�E��^,������;�iR�'�Ya8�B_r���`������V%.�dF��>�Q��c|}"��:/�6V ����Cc�*8/�W%���^���/�j��������P��_Y@���jO��]����r�)ve6�[	�-���3\S�Pe]"^��X�9W3�Z1'}�F�b;�-�n_���_{Tj�$|���j�h�kS���DA����A�
��O�D�qL���k��Q���h�!�o��7��Qk3I;�r(��5%��wF�F�/QC�%�R����q�Q��W����_�����ZE�����	;��O!o=�������?Uww�
��@^��&����w��y^.f���m��`��o��9�����K<G�tl��g��?L�^�a�����
ME�	�����vp��"�/��h�����8GY�ob�����_�5������9�WY�*�$�{V��z��������kw��.��meED��6E94E�\�(
I�4'�Vh�=�8��(N�������]�@J��d��I����������WEk9���m9���}����{������'���p�M�!��-�btHu�[>#�F%p�M��O���5d�dxs����5�q���������?:1�����W{����s$���r�`���B����V�ZK��jg��w�u�=�Q\������Geiz����^����&zsT���
������;>��P��2A�7B����@�]T)����G�`�k#e��M�����!�
>L,]�h�8��m�q��aH����������U;��:�6+wj]��z���[?
I�0y\N�At���uWQ8���d�>*/.���K0�<n��c�<��i���� �mk����h
�v�b� ��'O����5�0|b���}���:����o�]��������G����������l�I�I���������'���/���kS_���T59:���m�9��a/����o7�~��}��hf�uq�oG�K�|^MQlxYW��������K�8e����Q3�>��x�(�F�����
��M��*aE�����xn6�����l]=��!�h��c������<1%LL��[�	���6w�d�N��$�8��Q�Z��G*B>��5�>���%���3���,�No5��,w�_{����:-f]��8�l�l����l>XC��������UC<'�B@� �������
_#WI5������e�ay�(N\�H�=BZ{��I�Bj���i����j�����I}Za�L�a
���d�U����s8��,�T���wB�7�'����q��x�6`$����O��[�Go��c_�W�G�R�u��� y�^%��b�Q3�,G�����LPc�����e���^�S�����r�#�}�3k��2�0/�5����dQ��������]#Q��3��'�B����8MA�d�P;Ic����b��S��������'v�m)`�Z�";��R��6&�$���1��4�ztDd��<���w�����x.M���G��9�B���:3�y�D$
O�Nz@%}F�V��=�r�����]ya����g�NON�={x�����tf�,h�4gKNm#�|��h������u�?D�d��*�c����5���K�)��aW���<?������^�FA��'LD�m�x�����C;%��oQ�fjO�'��IC���F�0�L��x���S���wWY�C�9������V<h_7���K5v��B=���h��A�3/��V�T�E����`���[;0d����=N��sFV����Ep�S/�a5�8�.�Pr5�dA�J�C3�xX�3���%gR'����k�y:��W�\��p��W��KT�mz,������%-1|	J��y���3��=���k�}4$�[��W�7R����orR�ci��V<����� ���@�:�y�Uu�^�k
�:<��
�O0���n�-��|9�iS�|"���g�#�cj���
������&��4�O<`�K�hs��vi%:~�KV����4���	�'�Vt3����t��t�:��q@���R���%W��\Ze��/*�{j�Nd+r+���hN����I���M��0C�j	s��@R����O��E}�?���h����Z2>���
��7I������I\��[��@��`L���'B`�,Z�u`&�kc�@M
����H��X
`MbtT
��V�B`���)g�7,��3��_�����_
����>����������H��!f�Y�r���7+1j�}������U2x���'Au�����6�3��8
Ae6��������x�hk��n�E]g�Hr�
/G{%�"�Ob�heEPG�"R
Xax�h���~�_!��U�����o�X��Wp3��G��
�b����&�����?���"�W��xH�)��1�o��"]>��; �Q�(����������)�<}���V�rH�l3����0&����|{rz&{|�-"Yb���'x����!�v�`�%.F�A�Q�����M�H2!������m-��5X��(��5M��@}��t���>F"��c�,�G�j%3l��A�3{�3@��S�	["!gk2�-`��_���v�p~�P_������5������8I�H	��=��VT���!��rt+k�5A)��D@c������v�fJ�n�������}9��:\�Q��u��#��'����{�D�^\�Q_`[��H�������(PJ�0m���E��.������Z�n�������hJ�2����y:�"������������K����R����N�%���]�@b�a��VsE��[��v#�$��X� �hb�\�ws��~J�?8�@����t�������=8�j�C����aA����%mBdX#Dm�*2V� 6\���^,��Q��ys|~�;G��cw�	����������:�(|��\0P-
��F��^l��p1���;���T`�wO�U��������~><����_�e{���{�N�
7d�g�_d����N�O�1�.�F�W.:ErV]���c�������#��G��V"yq�����n���r�0e�9��`"�lr2;E������b��rc�K]���F�aI�r��p��)D���,��nZ"��>�=[���b�n�R����5�|��������)d.��,&E]i��(8=��[��M*�;��6��P��'�k5%nr� �f���N)_��!����tz~��;��]���N&��H�N7Fh?�g�B��,S�F[�MNio�����E/p��x`Ab�*�m��N�!�-���e��Bz"��(�����n�22�N�'��Y��I�r�rK�8]c��%+%_���a�L�j��6G:���+�0�Z�{�:N���i/&J"����Y:�2�[��W/O0
��3*������������_������z����jp�������3f�y��3av�8��&Y�������sg�Up&U���s��q[Z��n��O�}�8�NYB'>�N>��'(���.��Z���)Q�Z7'e]���v���z?C���8a��b`i��[��a3�\���e�ngt�;`��vC���[WcN���(	6�x8EW����ls���4uYPD'��`��,�~���[��=p�}��
��`�W�W����`a'�0����8QAc����{W�!�3�m�'��M={1@���X�n4��-�[���b���
�������.��{_^���������Y�������-�YQ���/x��i9G�Mp$�l �<2����B:<�������L�������!���x�wYd��]*E���Y"b���������n�z�fn  +�j����/^��^��Bo2BZ�����B"�c�M�����K�R��F���������=���f� QX6J>���!������1(0��3l����y�s�Rr��.�$6w3�,����D���c(4�����L?�X�I�nI�d���e�n�p��m����#32V�� %(!����,�O�k�B�Y���y��0�W���=_!c�7���<�W��1��}_��y<C��Y�>�9
:qMz�g�D
;O_��Q�;����&FU��t&��q�|�bD6@�/Tq�`#,X^��(�������G�������g$72�����(������^G6�����H�v��?0"�l�q�	�QI���(��X��UE�@�����b�rKS|E����\Ak���r�|�u]�H����L��ap������7P �E��I�0�,�$�CY�s,�������&�FF���8������6����V�4����s9�R�Q+�e��b6�-�(��`y��s�yd����s<	�I�+��@Y�\~�@
��� ��%���6���z��9�\/�,`���g�hq��mF����J%�2y��B���D�t{�Mr���]�:,��������K���J�;��0���3&�q�p��T�eA���j�0��8�T�Z$���^x�y���$����O�$b�*�Ev]�Fc�r��;?�<w�����`|�z�t�J{��a�����g�V	`�����}���;A�V���V��*P��\���Z��M�R8�K��N�5��!���1��_�K��q�L9{� v����Az$�(���`��#�)��UQ�����X���=�.Q��~s!F��Ti�pY��J���v 9��g9E�U��,bV�G�#-����.�JZ`0�0B�89�^2����2H$x�0�����u���EN�RT�����N%2`Pr��iw�,��L�a���#�P��0�|����Z�J�cE���0L
�����a1��V�����R�dI%)G^	OF|~Q�f����R��"��s|����O��)a;[�Z��	��� ��.��*������������=���g���I��5�Uw4-W� �6U����
}/E�"���?����pf5���r��z��}^��_�v	�����)�
���a8)���"	�mjOw�K	���������J	�5e�;f����A��qps������D^����AZbtfAu���
���B��GF��Y���w�*���&�(�v��w�o���0oW��G�����'���W���3���gw���c��]`)������a/3�h���������&E�]S����'V�Q���J�qyk��m�,�DV9t���z��������tpjlv�;����O��R��ecc�:(@{�61�Z���������B'�J�z[^�$� o��0\�I#���=i���@�;��ep+��TI1�9�)�����,9�~,Nx/e�����c��4y(��C��n���-Zz��Dm�A�{���"��*���%r]�!�6b��X��$Wesq�1��g������5G��~?�����]�m��S�05
V8���7bQ������k�o���G�/�/�Oa�g����CP���F���Z�uY�A�"���C���	"L0��l"���e����%�9���c�xe��xp�U6F�n-�j24�����S_�9
[��Y*~O�����d|���J$b�?4�iJ��vR��D�m�8TC��aH��Y0�C:��;5�b:����!^`�����w{E���#��		%Z�^ ��"����,���bv��Me3�����C^�0�����<�IA�Pv�m^H��-�?�rE���;��|	W���wE&=�U�����T����>��t{0��rQ��b@�b'�T|0���$��:�%,?@6�(0=�L����f�%�%��
z�|xQ��$WS%Ex�Jo���GK�(�[�JkR�
P`I��������@�����s�����CI���C�2��o���p��'����#�Jd������?�����C���'H�<q$��?�mYV����/<�X@#��7z>���
���
O)Vd
�<���/����;���Ah����dU
RC������f
��L��*�g7��>�"��f69}�i?.����M�	g��G?�s��P�����f�^-��
��|^�;01��9�p�� �5b��������5���6S��}�yV"	}�K�2b,�V��n��yj~U��P��Q���e67��������R�:{�4����!��1!Q�����1��t�6v��Fy�?-�D|G�Ku�+��hS�(��'�v�e����_�������0]A,%VO�x��<X-��/�F�S���{�2�RI��0,P�
f�)?=�s\�������f�DBX ����j��
��~h����2
O}]��a�������c�\�LW��Ag��|dz�)+�
yz�_�k�'�-�r��0R�������O���:=xi�������vz SP�U�*AY��L(-9+~kWO'�i��QW�A��13jDqe+{k	���p�g6c\wQ��yK�*
a^GY�`2(Q6�����
�1!%.�M�^�����rB�4�MF\U�=��.eC%�fVM���
�R`<�����v��y9x{z��l��t����?�$X�iMw$��	��L�D��'d�����,P�y}�'�_�-O��Z��������HcR�~ �l9DJ�f��������#�����g��h���������6�����4G���(�B�p���NRJ���B�Pe!��'��L�nL�h+�Q�Z2�.�=6��9?h4�u����)����0F"E9�}5��2qE���6H`��j*�`Ls��30&�[:<$G|D ���,�s�w���T������qG�hO��^�{`g��r{wHV,�xtu�T��t-���Ku��uU6[������qt���"��-,;xSc��k��i�Yu��F�N��A�(l2�1;��b�����g�m7@b��������d��)qd�����j;������=�n�al��#���N�&���~�@�kA��tS�� 	� Gwa9�����`bA��+!N:Jn�L��RlmL�����|����.�K���������86����4�r�@W����m����w�"���4}����s�����b��������m>n��a�%��UxXE��d��]�)���B��:qz�-J�r��Ebo9�X��y�H�T#'����/��dGMM���>��'
�X�h�c����=)^��[h���V�=�������G*\����	X��z� C�6?)&��ls��e��['Eh�_`+_��ST�|��e��x��F��� �:cKH�eZ���w;R��W<�i�tKG=D���;�A�x ������q��7]b�co���-
�?Q�B�(��
��/�f�����/�#��w���h2������v^�GS{o.Woznf��z���v��>����C����TMK��`<���[bM� k��F�P1<�O�Z�-	��{�N��'��W��(?�{S��fC)�0��	>��g������V�!�K�Dge��@�3��3���x��5J��H�w����5�m�����$�p�II��X���<sU�������z��	�F��{yN������;�!'A�C
�_�������������f�!������7p�c77.(k����6�Q����dS}�k$JLp@i��Yh(��b��-��H� ��|2ot�q�99(�q��YI����bs��}����W��b���)��f�1�u �(��a����;4���9���[�	����gB��u@4T�>�u��[L���~�Z��sM&[|�w3'��������]��
�l������:���v�B�I�F��]M�(�
d��H$����U�\BV��v+I�E��Ag�J���%�����~�hD�N��3��6�~��n&�.�����8C��:���I�\���v8�T��4�iy��x���&���� ��KT��}?]�8�kB���i+?��9��5���,�[]P�8c�^p5�*�{q����I��KM��\�bo���'�S�
��k���M1����P�^
��������uN�~|�� Jk0N�#k8�����z�P08����[�@��)���P�	=vFh��Q_s���0 �~��WPN�pRf��A=�=}/v���%��������"�y������J��P'�������A���h����aC�;6Y��l�1O��~nH4X���Xh�����pK����1C&^��{��������.
����auU� w�?����s��bMa������\�$���S�E�.�a�j�x1�����Z��Gr&�����|�'?-�ny0��n�zK���e
��]��o��=n�}c/��bO�>X�W�l)�yg�A~7������k���7�g;w�-X��������y�o�����7�0v_S(�_����x�K�g���w�oFL��'�Hp��A4�:���$������&~�UV���}(*�����Cp�2���
�B��g��+�[
���S�d��\	#��i5i�B�n]���[t�L.���Nt���\���[�k~6cG5cV\O��0s�&f.���G����6���-��(��|`(Y������<��HC������0x�4�a���J��Q*���2Yv(�y�v�X���?r]aO�[N\�=��=l�g����Exl#!�'�LB��[8���2�n�����_W�>�6���)��"��T�F
p��+��l�?Y�`����0����[g�7qP�j������"�E#�Z����
!���S��yNq����Cu���M�f������X#m�������<�/
+B�,@= ���;�}U�c��h�U�3,q��[ ����s0���-!*s\M.��=����f�����s$���m�I�C���Y?+���3��_����jNe'��]���n�(�P���v���<�TCK�g3�+U��(@i�o�N��L��6,�(������L�&�6������T�<�IP>%u6��p���5�HRd���p~�R�GVa�N���O�Rx�4�|f	�*���+� u/�����YALg�Kr~e+;5�������*|I�Z�*�n2 �2/��P�����j�H>�w2�lH�@$K4r��b���u����&u��R��r�sB��7DpF�#����f!�������?�c4�ETlf�1�
�����m����B��"��c����(+
o��h���B� 3���d�V��_���
���E7E�%v�V���1�2h��`�q��O�?^�'��kKxj�����q�P40���-���rb�=^s��O�}K|�f
�p���V<i72��Q��|������},���pvX2}�0������������k��s�L��F]�m-'���{���g�]�S�?�����m�����Rx��[ka������-��f�'X���i"�7l�B/�MU�uEmjt=u��l����������}��Q���Q�r\���=1���<�����%=}�7j�������%�A�3�����������V������~�8�����*��� ��d��T�P���/�g��P<Ld,��n��<!��������ssE5�cs�������`h8����4�����*�+�,�a?,���'F�X�
��s��c��i1��������79X��~�`U+�3�_����p�@�p/G������G���C����]��@*���.j�y��|G���-�*����}��L4X�-pU�G�wAw����v���!\�Sb�h��E*&�k�
Sx�NN�g{g������>Ef��/���R��������H4�����^�<�n��s+�Kr�D�Hx�Sb�����K�����q�>h��f0)n��A#iggG�'��~��prA���.qV>Q������0AaSk��~��a�����n���]G�
m�� ���	�<?���PzsL��X/,g.Ug1s�����x�b8��)XB�q��b�����Et���2b���WET�,�jH�X������� �K%��:�rq�	��n����=�y��35	��?�o�V�V��R�.xC��FY�A�'���/�e���	3���8�����o�N��k"ECb�d��R��9D���E����E���%�����q��~,]�[�����k�oW9G4��6��M�c�>oW���)�Y�� ��0�Y�'N�v���E�%�a~�i@�ASy�����.0w�$I8�8GN���c`@F�$�^��?6�yy9��������������������������7('c������>�k���@lG[3y���gdT��?S�����AS�5�=����3�F�-��I���w��X�����^�������n�JF��E��`���-�G���^S���u{��b������~�#���|y��/�m;�����
g��"�F������e�3�Wy��>]�0wx�~�j;�0!�����%X����de���V��][��g*�SL����������:-���������M�7Xe��KC�F���2E��<���L[QSV����{�"��	x�Y�&{�E�7�c9e��=}(�aF.\��~
v����1N�3�9f]��;�;
c�D�D�������I/c��fR�KF��!C�4�N��#{&�g��tH�b�q�����s/@��������Y�_��J�4�$�5��v���p�Z��s��Fl����������+N*�47�Y�d?�0�����MC�O)�I�mx�rmZ��77��?��F�V?k��{�����t�\����������%��=5Y��v��2��)�]�2i)?��4�'���N����
�(QBc|nZ�,:��V-s�����"`$eq��O��l�`J ��T��%��8��Vl#5���r4�J����A5u��7),\������(��@(�C�y.QM�0I�{^��+s���:�rd�XD�D$��SZ+�g��zQ�<��,���	�^�P����e}8�t�g���[c�,���F���,�Cz����p>�OZ(���[������Ry#�B�7LP������E��^.�C%��k����I���[RI(�`��
xM�)�I��Bk����0e=Q:���/[����I(.�y����VHZ>=�N���������~�P�&F
�k� 
�D���T�{���lN��Mcr�H�
mqD�/Jvo;�w��pl�c��v&�c�Y�~�1�g�R]���5Y����)+�(F	#aj���rP~��#F��C	�$�I�����tZ�3g���r.����1�nJ�TKX�)D�J��T[�0�3g�W��,qgg�H-�g�|�+�����4���f��6���SU.Q����(�v�����yW�v�w�D�{N�O���-j������f����h�J����(�[�6��v�X�����A(]��7|�]���D�:��a�I5�b�&G�bo�g�&7�S0H~��L�3�J��<'s���EA8��k�_�k���	��]���5����rz�!��'�y�B.���u�:�c������T\��i�a��4������Am` ��������I5^��������o��M��0%���{�8t�4P���;P�e���T�J�Z�\�Awy`�2�/��0p5!P}:�g����:�Ha������k+2x��>��~���~e6���~������I_~^��[��G_�I������=7����:�.|�w����E]��(��M?��t?�8�?����@.SCi5���������`�Y�S��G|�y����e�w��
����wV�W8�e��K��Y����Uyn��6@��R��^X���eu���{cP���r�?�o�XC�x�����>e����W���M��g�_�>-�~�%&�|x_C��q~�d����������������D{vA���_��2[_�v3gIq��\�J�3�G�
��7[��RK��x��a[|���1g9�"������JR�J+��h
�&l����T;���������*|�e�s�����k�$��l�f�[�	k}��66�$o��n?�����tF0�D�a$�!�4��M��Q�+9�!��3B�ae(I��_�~�������lf�����ZB�A�"���0����1D�����Hg^d�,xa|�:���n}�����?��"p����~���c����z���cQ�^�����AOI����%FB��0���������Q���EH�$�q��@t��)0
?�m�=�a-����0Sl���"���cR�����0������ �	�42]\����/����$����Jn���t�VO��G��	������Lg	�
�*���T�Q��q��~����8��D?$^�����s��#�t�����[3���nj?"!�n�Nd��;���6vB`'[`�{�%Z�{��!~N��,L���r9{6�9�Tj�[�I����MP�����o�}jO]st���l�(��Z�3d+� �N��'
�oo6�w����
pj�L��(��
hC\<U��.�Fn��(����6l�/��V!�r��,y������4!}��L �����A���s��dY���B�T��}�}��<����\/!�s��d��(�>lY�=v���]6{�n=6��lo�%7�����<�<{���[���{q�T��y[N��i�J��H
\���.�������z����5r�
�sQ�d��[���\{�u"f�I���<�s������4��&�������[{���$&TD��x�yB���r���X�K���2
�#�'	�.�(L�"���1��D���I.�}���e���"�>�f�����b0�E�f���a���>�]v2����|�����������B���E�3�i�n���[j?�+�;�������#e���F���JP�*R8���?
�U��=���(&�]�D���C���|2DD��"��i��3��.J"�t[dY`���a�k������]�V�WI:�0J���@6����g����`]R����.K��B��@�U7��s7���<)W��W>������jEB�1���U�E�<�x��,bEN{rO��"��;���y�w?���?�e7�E��;��&{c�2iv�np���	����Z28p�L�M����S����Cz0~	V@.��q_��o�����B���+���lW���uW�d�����-����i.)�J��S�b�������a��8+&�
��@	���Y�wi��>1-A�kn�Q�����hTt����,�������q8L���Q���/��g>��������+���.����Y�����3��H=�����+�:��Zu0*����-'c��}}_@�<J9����.�v���x������s��zj��SZ���YC��.8�wk��U'�
�����e>�p�����,>���9��`Q+K��L�����pu���:S�5[���%�"��JV6�84`�-�.3�c����<�� {�8\=U3���H(d��@*rC:��F���"?;��s'+y�bo �$�f��U���h�><�m@\Cwv���Ey	
x�p�8B�S^��-���*��R����@��d��~x��b�f�����6xc�����y)�(�������r�����&�IUkh9����k��]�xI_�/+���N)��@�,��\0���e
j
�GygH��Q�0�F6WlsUUx����\�E��x�l\�fc��1�K�����da�C���,� ��@'7'�mM��������v���<dh���v|]��������!b�E��l�R�_���F;�� ����pC��	[G��d��#"�a�r�I���� 7�N�� ��9?r����u]���b}(Hp)���I�8���@�+-�J��RK�m��a�������[�@�J��&���F��1:��S+	j	���x3����,}�>P�,�~���� '9�iH�N-B��5@�@��,F���#���������$+[����~�'�7Z��3����3�����,Q.
�V�U"T�i��iS�
��-�q�3q��t`�P����C_��F'��Q�PN���E�d+xm\;k���b2�)�����}�+� �t^�PK�LA�.���P9�f����}���3Y"ICHHLs)	������x�6�#O�$#��D�ueA�
�0V�D�0�D�dq )�p~
���^
����k�X�N���\�������x���.90���	����,XM���@�v5� */��������F��p���z�XD���:F@��j,�+GaP	�cy�zl���g�F|�x��p��v��M���@�\���N���AC����4Ru�#�H��n����F<��t���;�]$/e^$R/����M�tM�]�l6�������9#��*']v.�@���X��n:N����V��������:.7i� #���Y9����SD�U�]O�c�������9�AdC�|{M
%�u�X��ug����3�!r����f
�����\�����:|^O�f3)��U���B��f~�S	anT1jZI�����
�R��a�{��#��ed��J�h�����s�Z3���}�����zV�{
@A��6���e��y4���JI�yU���X������F�F�QQq��F.�[78L	a��_��;�I�e�S5]0/j����Y94Q�)�v{���6�}����QD��s���
�5���E�����qg�j���s�[b%.�#�H4?I�����S6����L���������(�b�`l�L! �5���e�1� ��r��K9�P
���s��3Ejl�sb`hvG��k+�
��"/����-�F� Ju"�g���U�<�
Q��Ut��=�Rc�b��:^0t��eCF�l]V��(=�.��!��+o�u������K#�� E@��P����#�
��WA�
E]���#�n����� B�>g4g�q���A��n*����j><���=����N����">-
���-�o�'��xoY%��1T��4BK�T4$�
����%��{�|�f�%F�"��^%%!�=~]����V��)dI�.�|�?��E�l�8��ns�r�4��D����M���(g������0By�j�����t��|�����k2�-`>��1m��~ 3s�.����q���������l#����]�qU�-
�QE?Zw�3q|��J�8����PHg^�+��'h*l.V���E\��4b+��)�u{
��W)�('�&F!.�,�dMf�n�y�����gp7��<q������{~���`?p����CA�0-���+��!�;����Bl������c�Bv:�YX��PLo5�6=�tL
����(>3�(��H����N����r�l0�����=	�u��4��Id�Q������	)u�����c+���D��)Qb�lhwZp�h!s�6-t`�����EVF5-
DIp��*��>}�����&����5��SX��N!�./���[���Q�v�T���/�&�����Z�-��������q�����uwt��u(�$W|9��6�`��;��3���u-��'�O��������Ks�N��8)/j���5]%5o�K���:l��(��j��F�+�d@/
�
���	�YOt]��g-��Ib�2��'X9���=I����
����-XW���_U�6�ZOZ���I���J_[��	.��x�]�\��C�J��O?�1~�6���f�<wr�z��ob�U�@p#�4	����\�v�g{:����J����|��U�E������	�cp��m�u	�x~��W_���~�}��w.po&��> �,d�[�������v�����-�=z���8��,����\/?��S��}��|����O�2N��f��X��5)�H�0���j�����bME�~���~��$BS���;kw�q_S����d��`�c��@6��6��k�S�Q��|�qI����]�^b!��Qdgu�<+����� Q%P�sp���8��P4��3����zG?c0�n-/�����a�!��^���-�HTS�SXn�����>}>Uc�4��g�(X�(�Y�"L"�@�E��`fa������F��������d|g������C8i���3\��|�LE�wb���
�7T��������cC���|�lk������BW����NBT,�r�5�Mm�E�����4���������a#w?�|%��s�!%>�>q���v�����6P.����P�K��D$���?"q��N^��������7��{g{���	��-��+��0a���!��� �}4���U��0#;(�3�P�h4�A�����(��M��h�L���b��H��`�@��dJs�j�)�.�^��#������2TK����+����~�wx9�T���8E>��;DY`{z�c��c���p)�
���E��d	h$��N��1�)��X��/�O`�����_@y,*'k�E,J�2�
A�H��!1���
��*��k0N� C9����(]����j$3
���>��A����0hRt��>N���U�E����!�bZwHpwq�Q&���:_K����<�:��Yw\�D]��.��Tu�}=zU�����B���������V���QV�`��K^�k�)��7�5�l�]~k.��+�_Cma����(��l�!o�c\�4����/ ;�]E17���^�����y�apN��]�!�h&!����qe�_Ow��@���D^��2���)�c*�P��^?�.��=�������M?|��	4s����6��8�n(�-���@��1�PJ���VN�$�v1���0aQ��p��5y�AZ�N%��*��wW�����Q�MUK��<Q���@}�JR��=�\x��d�<�y�^����6v��Vo�������|8p��q��!J����.�N�A�����Q��,Y�h����=
��M����|���x��$�	
y�
�g���8���<[����J/�D!��0��q�?���%���i61'
@��.�E����*I����a9���P��8
I�C��~���O�����@�oM;��\����s�\eoG|���V�v�8�SK�e�����DB��Af��O �#�����,�|8~D���x���������6����`�sArL�"�itI)$��`@��'�r�����������c�ITH���F�O��fj�k2�`��}8S&��:�	�����7
��C������$����?�YW�i"B�Uahp�`���
v�ea#*2e��2�D�&{�4'Q�������P����$;7��Y��=���a�����8�Wn�-�W���ut'Z�������6: \�O�_��xm�n��|���E��LRD�vj�SO���H*6�`��F�7]o�L�j��������)��i}��
^�;��������I�G��*��h�-
G��f���X�]���(����;&�uz��"����b9���2��M1�������(,��9���fS������]����z��.��&Q�!Q�e@�%ES�ueP%�PV�V�Y�6{��Y�T���Mz�&���F%�B�����
z4�k2�U��{!�D��>]�C^��������i�;-����!9��E�s��p�����\[��;�o�A����=p��1��%�|�1�8�R�l6���r�N�[(1�����)k�fv�.|F�����e����GY *�-V������.LF�	�+xQ#�
�����1:���K<�����=u����w�6Of G�(��l�z�k�SCLC�Jv�EI>�$�z\i+�!9�T+g�V
?�4����o��]���@�X���-O�J�)Ti�T���'��L����y�b>�\���o����Nw�:^%�>P���,���V�v>q �EE%!l6�9�����K�L�	[��=�b�8Y�2y���E�}�#�d�Y{��4X��h�z�XT��c��{7v��8�A�NA�mD�g!�d$,��H��5��T��X�[�v�L6B\S�#a���ss=S�E��]��@����w
3b'a{�\�\]s�?�pFA�/�&m�28��I���Wv�K����-�{y����bZ��HU>��pi��	��E����j�-�������^aD���rb-Pz��`��h�
$�j�?�����=.���g�.���E��y����@����8�*��2����x��2/�1y?�|9,eG
���(��bV��\"�?�2������M�����5 d�/���|�����8(�o�8���N���?1��M�))�!T�Jv%���Xi��m�O�R��j��%��+n�q��l�\�s/%{G��C?!�����ba8!���K�r��+�!��q2O�&�����
�Y��+�-<!n�$�i���\�X�\��1��:buW�3]T�qu�y$5]k)�YXNtwV�y�5W0[z�H`���=}Q9=W����l/�o���~V������y��2�mq�"�������+�*i��yo���/&�������l���Vl������c2��!���?����%�����vj���������wg9[��s�P��Z��Y�Q�*���6����g��E*uKR ?��R�����m����^������]�j�K4gWa;U���z&6|x�9��0�[Y�m'C����D��x*p��<��~�U<�Eb�r�&Z8�,��.*��������+��w9;����� ���(��/�#[���B ����Nd��]P�K'
��l4���)�j%�/"nA���0�q5%R���6�C`���4�;�27��c(>�9E9yw"����H5��#��&���p��]�LS������]�p�D��=z^�a;GhM3
F��WhO�	,#p�������{~E_:k��YS��SX>8/�l�Iy.n�u���8�&%(�TJ����[H��4���*�_���;�#5����A�?�oH��Ub �����#��
`�#�i�&�	��{�����q�=����w0��.�TOz�yW�X""����\P��;;�������)T"7%�3�$���bsn�}r9.<S�#�b�/�?gtc?�k����f�C��A�tPZ�����_��2a>�����j@/����en�}�{0�0X*�<�L�?|�����whT��j ���vD���^���������8��IVKe�+l����::�zu�u���C���-����F/8wHR�w���4�%����xj/��z�[`��������Y�&���GY</)lL8f�.'jd�@I4��b�T�T�xE�8��fY�Z����������� �w����{gL�d�C6�2�	B&���'���-s������UO���&��e��\�1({�f.6PPJ�?-[w�2�Y.)g�:�S4��i_���=���
R
a�"��������7��Afk���@�E=�PZ)FrW�����}s�����������_�����w�1��z
#�K��V�@��8�vZ�l������b`2|
�b����'jN2�
�JTY����G%k ^k�@U�������]��X���|�s|pa�q	U�l^P���0�C��S8fu>ir[�����XS	� �P|�-&�A���BP���W������z,8�d�O�;b5T�����1�������>Q1r���Cy���F*>�)&e��PV�������Sw��h��G��|x�-1�/��4�1iC��������7��,�zk8���)��Wo���^��,��<.>cW����
y��hr��,���Za�^��h���l�<Kp�!�E�:�W��P�.I�f�1<��!�X�1'�
�
�1�J���U�����J�������C�B���b����MP���Z��������������v`F�R?Zg�u ��N����A4������{��{9=�����t�'M�X�����u��|.�s�J�J@	�f�Z�=��@�D
�x�����n�����
��B��������t�w������
���*�u��7�����
0���d��pT�b�����=	���Vkc2�kA�g!Y�6.�����A7����{q�Y�
wX����S��mC�A���C��
AJD'�T���� ��i�����U�,��)M��mQ-*;�B8
O
]sz�33����JpyvYUF9�(��h�2Kk?�&���8�nl�1��Y��Sl_`eO}�x;��?6u;�
�D�J6�$�-��B��|��F����DOl!s�:��CJ�m9�b31B��.�&C�OqqQK#�S����'k��P�5�*�NF�����D���7��Ypl6��]�!�)����c�X�HS*����kR���_��v�a�Q"�WJ�W���;Jd���*� ��k�������M����:	�9��������s����!>S.����'�R*Q����s���������a���������uH�@T1��\k����{6v�#�(m�N�ai8u"����b����V2az&�[Ub����1A0�\MR���R��f��4�X��]����pYD���
K��is�����v,�����������}�������Y^>8F89���
N��_w��#������*N����(
�����B��[o8�����)�|��Hyaab�� ��T��ia���0H���JQ�(�U���yb�?Y���%v~���@�(I�8��M������[����r)G�*�������%D�����5]AB��-D����f��k,[tl�E.��t�6�6(�H�8�&����g(<�"U��{���T��+Flc��[������.����j>���zX������(��Z����q�
�#�]<�o���Ct��p��
4��
�!�Ux5��$/�����G�I�(�:�a���>Z���$����0����-`Bk����n�3]��0}(�o����1�������1�Y9���B��8ofz�>0Q\��������|���o���3��oD�:/����#���1������V��ve�C��"�bd�����9���[�{upU1��3��`�i�~8��>��k�v������p)��C,&��o����bJ'v7���C���~�"R��+��-a8E�H��J������-�'bL�(>I��>����Q������VA0�W��IMDz�
��8~D���'����=��^�� �*����O���������_B������"X�r)�����I���|���Qg�
���8�?y�'�9�JA�J�����2����H�D0CB�8�y9��	(%w�`�x�:�E��PW�.Pz^bu���D-��7�m���=-!b0�.}6N�pH-l����.F�"�2���T�Aq}i��CN��y����@6"U�����<�W��|97*�8�a�.;��D��i8V������:�e�$Q'����m��
����[�a�*3�
���78x�C�I���"���h��/Z�'���}����.&2M��F��G���gtj��HPW����������_<���U��Q��3C�
�i�\�*o�/��i!�l�J���1�aT���E�6mj���5�r�^�^��S�����ak����[�����[o|$G(Fl5��"�.c?��������E�z�@�mD��~~G�3�!X5��������j��x����/�o_���
lu� �6���7'���g����W{��g=������rTZ
l(rR>�����.���������?�@9*G����c��j�6��VF3aY�[��us�}0�a� ��E�D����~z����	�N�;9PqEwS�C���iYT2��������I�L�K��t���v����bV�B��U�rb���K�( F��0�h
R����Q�1���0d���"�����f��F�o����s��t��`��9���#�N`��Lj�I�@$���x�+�P#)��%�D��o�f5Ji��rJ����SW[B��?S�HCX�@,R���iY���ci=�U"V��B8�E��\�4h��J_5�S�{�H��h�����2�*�2"��@�? ��R.:m�so>�d��8�����w;OqpJ���g�`q�B���-���������^�_m�5��8]9!�l3���w6	E	�)6X�.������eI�^`L����zj�Lk��jxz��9�M/��S��Z����;��8e�����}!G���Mw!?Z�Nep�	"w!���S���hk��M�x�����F�@x
^�^�^��-��
o�Tc�%b2��\��������9Y���rv%U�$?4\�OT(��D�����ZVMm=�1��lX��U~�y>5����b5M�F��MeS>��n���6h��LG����t;-6p�]��Y����i9�1��SA����R�;������T��hj~T��{WE��������oG�����q��J
a�x�"�P����;;������a��Ds�`c�Y�8��P=�]���M)��L�������g��c!s|���������?a�5| �Az��RO�M�e��[6��V$��fa���6]���$��rr�M�5'����nIi�����B!�Ae-sw�T ����/��DH���F����y��cXMp����!`��g(��l��b��s�zx
�0��T��@-A�qim��T2{�E��"c�,}�������5[2��	I�P���g�9Xl��B:��{�=�CXLy=�bCG(LB�g���fr'�x����wmn�=�M����FPa�h�?�b�%v�-X#m]h�'h�2W1G%D2L����
Pd��������IJa|��������u�4�7)8P|X�-*� ��3�p���z/!qBX���%a��Y�Cb�e^�c2j�o��gw�4Zv����\�}��_��V��pGp��%��B{���J�d�lD&Cf��P��+?����LG��X�96��)8��K�������Q��rf��D!�}���T�i�A$�rP�X]���47r�C����&�cL9� *����b����=��$����������x���>VF�8��P���=X�y,{��/�������9Q�����K�G���i��h%5�i;��ba�tZ�e�u��Lg�Q�j ��7��j���t�C�^b9|=;-Ui[RJe�D��oZ�u}��b���B{i]�M����,�����h+�zt|L��~�1���<�5�c���~�Z�cD��j�`k#
�F��a
v��`�_��w��i+�R�K���0�EE�e(�6��K6}oWT���ua��^�^3���Ps���29�%��/�0K���O����*b���N�)�#��g>3
�D+Y����IB0$$��P���
O%6��N��:-�U��(���~��9[�Y�y�v�����8h���m�������Lg��Rr&�V�_��3�aW5��C��).)��#��[��k�3�"v]AI�P �*��HE�Qp���c���q�@
Ku���0��b]����g2��~=����f����v00z��
 	������y}i�(��k����o�f�q�cCO��N��������
Y��<�V�
N�6Om6��a��1J^��;�/��U6��&�)�����3�C�y#fQX�Qq�10�������uK'&������'�?����� ��������9d�O�]�PLF��y2�0{C�^)4�xQ�E�cR���E<s���g������=$`���xt�������}p5� 7�R+�*e@�����4[�s���V�0�s{(�7E���VQl��[m�����F��6���p~�
�%���-[t�f��'�x��zG����<xsr���=m+����������Y��xs��F����?������g���G?��������V���.��?�{�w�
�
|nZts���������� ,7�&����N��^�O��n��"{0.�����z���r
��K��1v���QG��iF�?��� �+��$H��F���qqM��������q���
4T�;6Z���s��K������'&�-w�2=C�����3� ��������e`�3������~6_��F��
����W	0��*�	�i�������U���'B�$�Rf�'�t0p�c#[�'���d�n[~vlX,�4W�)P���F�0���f���,&�u�N��
�Th���~���eR���QB'��~^?�!_�:���n�[vG��NYZ��]������l�k�`�hnw��k1���<
f<�.�����z���6�l�7��u���K� ���0��<><��$���-I�a��~��Y	��q������3�&tQX�����j�&D
�� �2��4�I������O������b��A(5Hb��23��t�b�W�r��k��Q]4MA.����y����p4�KJ�-���9��C`>n=�f���
���D,4\G���;a�{���`vI��R^w���(�x���?j��|L�c�0��P(���!|�H�S�'l���X��*�6<����f>�V,�`�
��CB,`W�c�:��_��`���Wor!vD�f��I��O�&=���YW=�k8��.I�{�K����1����C�4�����������'�Wz��(��Z��K��������NQ���������Th���>��������?
������x�K�@<W>���[R���>��+d���Z���g��5�39`Wr�.���]�$�_76�B�?��9$��_�*B^[L'��%K?`�6de�v�D�C(����A�
���CTCu��^�v#E+�YI��;�h^@�Y49�����t��E���q��:�+�<�{Ng�-0ei(������N�~6���������l���C���i�/V�&�W����"<�]Ke�c�J�|�����z���E�����e�%�/m'l��h������R}5�
�J�B���Hz
���
�d�H��V���q$<�������������-k,�x�v�h������he{P,n+v��$�e#�Z�[$�� xF�-�33�S�e�-�I����V������ef��1�flM	
����D������t��w ��&1��5 ��/��Q�{P+@������P���	�w����h�yS"R��X�$K������Pc�E�/���K��VF�)�R��+��fl� ����lQ�%���\�T�����zm�Xj[�%��%�G��u5g-�;������v��+W"zh�k��E]a\j����S\�2�i����4�!A��5bT�9{�G������4�K}+�K7(�+7���7�Up��������0��)��m��A�������r���l&`�d3����r�x:��R����YHE����n+3/���R���B�����6$o{JV�yH�2w�f�j���7ue!8D���ve;��c�:����?�t_*���@��Q��"4 ��zO,_��B�y�8�)��Z��'d
�\�
~\�?��Z@'��.��5l^��8N�FN)l�����2�4;���O���jD�bK�u�6^���#k�0�J���_JT2�,�H�"��j�Hp��%��m���c�q0W'��|�%�R]���l�`�A�������;t�G�}oQ�[��	!;����mk�e�]���]�����wr�*�z�-k�_q���LkL�Dql����#}��8�$'KL|>{����TX���3�_�,���5e��Z`_��u���.,��EU��}�-�����W�C���bo��A���W�L,�l]��b��.p�_��e�Auq�@0U�D2�����f�/L+��<��������O��q�*���-�]<x[rQ���v�]vS����^T�Ax����G ����8�L}@�G��Zn� ��H��]sg7�\���#,'c��J�u���>�����m�%�(���r�W�T�a����mR�iF��a��|���9��j�Q�*�
�d������r}n��	oDE��k�M
!$�]��U�L��+=�V�7�k�R�B<~jA=������X*�$�V$l�Q��Lpj��!�����u�>�@��X�h�{j
4��U���%�v�S47���>�THr��;����:��:�D��z��y��U�D�'��.
/BkX�RH,��6^d :��jOqLg�J@�J����byT/+'� �d�AX$�GB�����\�SAREb�u�a����p��qaT��*�A<���������`�����|	u�o\n&��#o>RxY������=#&��)1z���)
C
���Y���e�3���~y��
���~����t�:
n-!V�0[��}*lf(���Xc�p�}�+7���!�e���o>��S�)��6a�?���[u����������^����4b�v�vy��V�@���E���P6����S2r�k=����s�&x8�>��)�j�Ba��0�B\k9zax���x�u�
������H����IJb�~��o�����a	�Z������w���y9�����5�6	T�c��,�=�5�:���i���H�Bz������< �������!1���]�a-�z0�tM��F�v	\�P�X�3$)h�C����,��"�g���fB�lTU_w2%�b���Ca�
\�p�>�����$N���2:$����
�[�[���Gw���+�����4�{�!$��a H��mD��`����XT�S�����fU8%/]U��i	���z�!�%<����B_���w��R�`��p���7����O�p���.!���#�R���L�����n ��~�A�Se�I�^*����D�F��kzuVR�_�1�����A��4���u��
�0s��hT�a	A8���K��N"#/p"
	�Nd�����gsC"1K;}��AzYlo���!���kv��[Pl�G���Dp#"
;Jlsb����o���`vj�9��[,h	2�]��N^�<�l-B!�E#a����>�\YZFmS�T#)��;�gr�o%D��6��hs���AUM����*[X����\�>(d:�#3���V	2�����j�T?sf(Oo��=�l;i6�BhA�y�MFc�	�`�
p{�$�f���j�C�y!u=�����#�09g$��%��L�����m~%Y.���6'�P�7E�#;~ep��_����xvc����Pb=�P"���
�E.�2�.5��L
��{���t� �e�;G|��
^�|��?V���=s�IaOt�K�L�0rn#!��-#7p�P�ep|>x����+u3��,q^�n)���F��X�����S�[�b����[�������"����J_:��Yi�J�NA�j�JD;
�����O��y���a�����n��{F����h�2�5B�#�ve Y�]A+���p�S�_�,L�v��1�c�1��}����(s������Z1#SVB�*�\�l��E��=�m�XWr�]mc����
5�(�b�
K���D_p���f��k�3��>����nZ����Y5t�'*������U;.K��-pt�����4�gs���qSs��T,�1t���Wx1|1n�������.�ta����'�~���>��mm}���h4~�}���G������mll����S����$���oo���O��6���X%�mt��A����3������f�/}�9a4�K2lz$�
�����QQJ����	d��8y}|vp�<�.�&'�
��u_|�
�����q KR���$%��L�f�1�vDh�}Be��L�K�����b�u�)����)uF��/��`����`@���#��8Vg������V\	D�9�O_JC�
���l��������{r�����-G�a��l3�{��n�~����5m�C�V+�e�hI���GD ^���d�3�Sb�3��JkZs�.bs0��q?��8�X,���`!�}b#�a�/I�fn/�B��|y�v�����G]Nt��.+hdW�"Iv�T*�SS���
W(�C�W����K�����������}0xq���ywQ�� �f����?�������%���o�	�������
m���A��~ba��������h��C�����AE`)o��^0N��7<��x��-�|���n��){��.~�;������_�(�z]c�>�{~pb������`;��Gm�Y����r��s��X8e�}���Z�	.�t�i|�x���0��c������u}���{y������Y"�������"���<#�8+���UM'<!_��A�s�O����=�w��r�.Y��A����T��+���N���!�)oM�wO��s#o8�����Oi���P�w�����l���e��t���!�Yw�X@x��eb�^�B�v�>�oZ���������,�����%{�����R�-�o���m�6[����Zr$+r�d�)f���"N�����T\,����h��-]b�>Y�%-�������k�������?������7w�8�������'3=S}����|bv	�����������&yJ�w.f1�
��_��TT��LF2�v��qH<�8GA3����c6i'��:}�}M`������������	�c�������)���Ic��R�FV7��ap�En:�1)�"o��nT@�Q�R+��'QF)v#K�5�2X�q�����G�a��\��hsT�y�^q �e1A7���Og��}��=�u������l�����
&���d�:�o
�L�*1�MF���������<WgXV�F�R;��{\a�|1�-����XQ�f��#���2^�$^����l��dYHLz[F�&��
@���_������U4W�/2�<|(��)��I����2���
�Hy|�i�h�uy���(���fCVZv��i!�9�b�����?�@����0YX�	�������XY��O.���I�Q���	@����jR��D3�|����	Q���f�`��<�A���
`I�]EZ�,H,q���k�&H42j�5sk�
�@j`4�t�����X�T5���Q�kt-v�h�����n~�d���i����;�^���|�=�.�)r��u5�.���~f$G�vR�������KC��2_jN�/�������n`�b��1�?��O�-��&�F�O�����/�|41�"7�ON����1\���~�� ��u9	~�e��A�x|~I�A8�f	��a������)����"����r<3�����N�y����1p����7 |��?�Q�t��Q�:���'�`0���]�w�H	"��G�.����c_���k������g�w
�?C��i�z?�N���X4�hn���^��z��]���6���f�X9m�%J��K�*���^Q�\��{:��!����/����G��:��U=)���oe�Q!Pk��4 ��Ic�{����`)�����S91��yG��5����*����j���Y�w����������Ztx���E�����fM�@��=#r>������#N�������}L�4	�F�k8����$&&��C��":�\�;k#������c��w�:��`&��g�x��Z�����4������D���XJz�,~�X�e�y&;���y��S�_/����*w��-��S���Ck�2N�3=���J��C\<���tc�bx�'-���1���r��r���t�� 
TG�����V�V�E�E.�YQ��x����B�������`?������>yt��f���;��`_���j��:�#�,�W�q>m����Z�>CT�[�I���u���x�,�p��������l��V���T ^I�$
���(��LD�!���ab�:�o[�E���[}��s�0Ti��+�E�,PC^9z���n)z_��G��������\!�������;��O�O���.�����@���BDn�k��>R6����G���8AS.hl2�+Xf5)a�!�8S'����T�`��qU����E���;����������������z5d�fH�z��is�&�L�`�y#yP����I�n�s�C����(����}��K�Gi���h:��������t� �
tX \�E���> �N�rEi�m�F������U���H�v�����?��pR�������b����i��6_���Gv�����8�DLe�@�s��c�f�2����C��,��?��"���o���n��5�������chpG>�>�2���Q�+�)�c�T����[��H�B61�����T;�z�ch
��5�����7M5,Q���^#r�������
���<qQ�X� g��'�T[�n	������S������ GI��BR��B��0�-�G�AfO�������������l�����ZoF�33hq�.�X���}�|f/�'0N��R�bK�z[�s*�&{l��S�|��-D�Xx�4�dX�FD��%�b2���B��'Pdf.�|�^�u���+�����)��<e3����������9�|�'3C�6��a�
3F��]��KIAf�,��5���?-Tcw�g$��V�>[29�����A��a��B�n�`)��������3��K=q����-p��N��9Y}�Z��n��uY�5�fH���-` ���KR���*�����6Np��7A��h(��Q�+	��<*+45`+Co-�������%#�������\�a��S�zzp6�R	K�V�rsq�h�"H�N��.����{Y%\�&����x
"�]A�''�A��h�rm)>��g�
�/����	�
o�E�z=ud��_�����L���i}�+���S�-�jYHW+��-�P���u�J?z������]W����9�Iz(�59���*�J �������O �$��(�
�������k����?d���60d8����R�0p0��>9%�(�;�������{0�/
G���V/u+dB���0P�8�SZu�f���1��Iy4�|�����&K���2����������qR2��O����m${�K��Il2�(n���si��y�-${&3/�%���5�|�[[7������;��y��)��Kuuuuu����m�o^����E�yf��l4�H�>�EG����$���������k#P�������>�D�s{������;4��!���)�R��A q0�K�krWh�F^����ga��a�DS���R����Y��f����:N���Odj���_r������������,�����,+�g�r=If?�J�"�l|��A���� 
�����r%J%v!�3��>�r��
� �2v�}�n!�8��P�D���F\�8>#���o��a�/�����Doj^Y���;�!����/U�/bta8����hK<����B��YO����!�0�b������ b"�*��^�4���rc�4����d?�x�&�<�}%���*b[�v����.�S�h�Jw.a��!I>W'9����6%�%��7v������x�	���5IE�?q����:�0�aJM�	v��p��3zL2/ewQ�y�C�a0_a��'�Ud^�R5�Y���Z"Q���@�y�1�����g;X�A�CP�)�>1L�"B�j��L�B��0%�"���E�|�JUi]�vK#�M�{e3�|	��=������*5&S��q�.����qi����v �(>o�~wQ-9;:�-������]do�����t?'K>��Oh�~�(�W�s�Zm���Y��sl�B���G\D{��Z��$�D���R���mCw�= ���&�O|�I��_�pz ��]��\���7������)��	q�bG�D�[��%M�������]�oSys	�I��3�T?��u�{
��(s���?���;c� �3�Gq�3d�H�o�
a_�3�	
!R���~���N)<i���y0����y�o%����=2f���������-�Pq�>��'�(��c��l���7p����zg1 [A�Z��>{t:�n|���
��l��Eanh�R��9%��~��g�1%A�A�9�
�R�����3��L��T�v��ZQ���)?9�#k�����
��q�p��P��p�vb��X��)
�;�����K�(R<h��M����j"-���V�8p�7v`��g$�6j��
T��2\���~��6�2�&�/��3�������/6��	�d��s���D7,=u������o$,�=C:�Kt��R���Rl5?���L,E7�������A�NS�n��=��A��H��O��������ll����W1165=���(��rF$1�`-�q`d����m�H���� �1��f����R�!& ��(��.��8�_��N�bE yv[�`��`��R�~�+�v�M��AT�$n	���'B���d���rkd�������t>x:���W��+9���rh�\QJ8����IP6_O����������g�|ku����_��2�h��l.�-)�{�Lj9S�z�bE%�v���E��M�^�d�����R�������P�&c(���C�t�"�����hK�L�M��n	|�s���CW���l�3�Uh�
��$��!���u!}"���*�����Jx���!���x�El{�������K����������ZS����N�TZe�=�-�9����l���.9NU�|q�Z���V�N����[pz?��2}0M�����e���Y��6��hr9��!�����'��|du�;�����HZ�X��M�y���F��gsMk���!s�@��;MYJ��&��?��H���"�?�tx�`��
�)�f����
Ec�
��M��hHD<������/E�C$��9���D�lB�V`X�e���`HI �G$ub�@���:IO
K�.DwXB�U���S04Q�znH����Z���@�=3���U��D�lA����J-����7����@���dOjs&��]�"��	.z��G�������k^
H��*�YO�$�r�w>��@	i���g��	�����<��t�Q��1��UNx���F��@"��'iJ�$��G#s��Shu:g7��~����\7/������X��(%'1cac���1���������o�	�����)GVN���*,�����"x��R���@�����IlT�w�k��P�������#�9w�i�6;�L�h�l~��#�����h��,E�Z$]S��=��8�D��h��}����Z�.���%nGQH�_���#�ED���Y��@�@3z����d���E[S��=s�s�.��)����E��E��
g�
md/����2)4I8��Dq�  ��V��`�?�}�%u�*�c�q��.�f����B�\����"���w���8&�Pv#�W4�E����9b�������wi�0�.��6��h��u�������D��*b�t��j��uZ���^�������k����=�y2�f�)�����	��E:� �h�����(3�F�Ee,�B(*�A�7��S:�F�s�t����du&���c)�o�t7ab���5��Sl�)���Y'f%k�����Gc�f�7F7������\p�f�����Et
�u�3q�w��ru�R�]�sF����X���B�:FP�-:3c�7
��R�em���|,_l#�F�D��7��u�+�wr������"yrfHT��G�]Q��)��P������H�"P4f&����e��,�`�F{��=��P�q
�m�EO��iP��k���w����8�>�r���s��za^w|��lf��|��6��m"������pir����8����������u�Q��nK
3��#&� �W�~|�T
��Xy1c������vG<���~�=�/��Q��?�[�N|&�������
k@���kQ�F
�.��`�5F�q�R�*�������;89>P�v{{{������*�xv���I�����G<�xS8 ��A���XY!Q�*9��b9E�������C+����>b���b�x;9�q@0��<�G^H�����a�n���L�P�+2��,7c8�[	��
����U�e�zJ������xP�����>��m��_t�\��(��(vDe�QV+?��JH�
�:O�h�m��4�!���pV�����1��V7&�^�V�$s�}���t=�3W����Y+�u����b����a���`8*�+��pP��_	����C�(��j��Z�;��//���7?�����ss����g!x�����\9��`��|��u���.[g=��s7�
�.@�,A�����:�z@P-�m�����3�B��`~�:T��I4	���q6d]�����$���A�n�`u�u�h�r~n��SIh��X@��iva���� ����R��3>(��f�h�� s�eV%/'��7<�����Z�Q9T�+�$���,��qpP:tv����N
,D�7�m���xL�.a�Kj�M"u�Q��_�������������5���C��jt�������H���gw1M4��{m�<�|�	��C84�^��I�q��}����pv�����E�'��`�X�=���p��C���������T���RT�{-�u1�6������P
\�����:��H������$�*��l��_�@�9�4���%X|JL����*3g��_3�1A,E�r��"����/�2�]NEJ �.�lK�78+��]��������@e��s���A����}����:������^����J�b���}����;F�H�n#�1�C�D��g�����}9�JQ���Xx���y���9
$����1��g6]�y�k�~�CjG����Z���5�L���z� 0�g�"iB��~���8�
�~0�
�y��^��F^YV����dr�����T^(���%kX�N�r

b��"�+F���/g�L^�r&���������R��mb1�vK����<:y�s��d\�It�EC���HjaqE�M3Jc��(���/��+�;7������z�4�(�5�%��"����ij��)%��{R����A�+�q��y��W���jK��y%q}��Gxn��U\_�l�����h= ��+PL@J��������o���u����t�KiE����X�J��44��
�}���EhBk.U��U����]v�1���Wu�N}�*$5�fk'����-�o&����AR� �-���6g�s�������%�u'R�hH�xT��,U�2�������zZh���L������{J��d�a~a�������Xs�7�_2�k&T�1��tn2�*��s�=�_6���#�����`N�|9O���b�8�4��*p<jT���Au���[]Z���-��y[8�Kx����Fo@���}��[��exg�RZx�xGn��=��cL�����	C���3����@n���y�3w�2Z]/	�AA|T����������l�]�?���o95Qr���E_�x�0���Y���\�\	Q��1�Z1W�<'�R�LH�4?APC�4J���MtM8?B@-�Ra!�v��#R�������$s�=�"��v)���uaY�g+���R�f�4w���\-��cO3_�dJ�3����}��������V��!�ZS��a ��n���m��u��,�@���<W�oB����)P���Is��y�;�M��������/9�<JS���T�qX�U������X]P�����_=)��G�����r�VY("����P@�QD�>|t�����>�����<����|�4	Co"�\�XMA��_�1)����m����/�� �L��?<��"��R�IA��W�Q,���o���m�a���x�#���r�J��k�����w8,������htX;�l�JU�z�J'F<:�#��	��%�����/����x���<�"=�J�s�?���j���n�}��|�i�^�t�phK��h]�:�3z�?��J`��Q+���8��g4�r���C�#�9����}�G����qH�wrF.�c�o���9W�
��B�gP-�gz�x$���G}p|���'���Wk��NjC7�=�jKsF^Id�zy��0^�����bT�����4K��E'�!A��y+CQ��j�v������rG<�}4������������5��Jc��%�����G��
��JV>9���g��|o���� ��@r���k�9�@4s��m��Gf��\��/�9���M�Gv
��1;�0�G|��YT$����B�2Xs����S�>��F��I�Px�o�i�j����\����_#v+N6F��;W�����0-0��*BZ��������{u�!!;� 	�����3�;N��������V��s�>���O/��8�L2�Ez3d]������q�(����!&#���M�\k*C������f|��z�7^��m������h�"��yI�-��e�1P��pbV��l�pz�����;e^�1M�$�Y ������!��5B����'"�,����3���B��Q|r���s��B��a-(�l��A�A��x��gA����P�0X�Kc��~=	��K�����li_�f����7�N�yQ�oJNr�XOB��k>����!��FU���Y��Y�t�1�&4#�H��F����9�*z����o��3��>��=a&�x�����q9�����d4*�+���[=�
V(j�������d;��]��T4x�F�l��I9c���6ZB@#�SA�K�����D���������
�RUy�EC�{���h�Y���|���s2. �x2v�^��^�@'����`�1=��|4
��P���\c+���K���j�s�-
��bR�S��.V�xi��2���P�j��Q��N�u[�0.������(�/}?��g�M��}��O�/���@�(K2pY/��o����v��] �f��8D��nNQr��+�/��eZZg����1%sn��SY-�0t�j8#+!"\��@G[U��?�E�9u5���JL ���[���CV�8��.�3��m�b���9w�Yed��*'G����\>��qc\��f�����r|&?$��H_2.��kN���{P�����/������S~����mv�����~�����%���b���I)�vJ��Z����Y�R��g�^����^��s���xz<@PA����Z�yQ�����w�j:p���[�M'��[=��>����bU�]�M��e���$���3]� ���{�I�3z�B	�px?��dy�y�~�uQ�����p|8�uU.+���F��[�������,.N^<u�b����NZ/���Ji�(���/�q@7��2�J�.E��8����Xd�y��K
����������ZzK������#�+�)����T�1u�o�)b���8896���z���G�`�d�u���]��#��X��M���"��1��h��E�K}V��[Ad��������wX��Q9�{[�D�X������>
�+��Z7az�����������~/����{4p����������pC
S=k�Ke��GGx ����t4#].��e��M��3�`���]����%�}z��n�����kr;"�����y��^�/()������Y�{���>j(��)w�/��8���>fm8}�*��QB��0c���	L��[9��
Vx�f��f��Yc���R@�L3�LG9/����z��_r���s��}�;�C�7��c�����Z#����O/���Ao�[e:@�"�b�R�v�r�����=���E�_�]�.{f��HlHq�1���dDH@����G�,����b��F��x=��~�==0�
�,AO�"�j�'F��l��hF~b�������
�"��{�bR��bx/g�����o�$�BH��F��9t1�#�<�K�����*���Cw��+'��{*��m�>x3n;��7.6}W�d@��<���Ay/������������$M�,��9\��Fj�V%�A�}�Wl6~)��(|+�9���8�;�������Eso���\v�Y(/�9��?�5A�{CI@-�V����6f#m���"�Y`W������x|0��c���#���l9�
�k	��R�3�ut��qd��42��<�7A]����r��/'����y��tL �2h��Rb���|�i��b-��(r������o�����
�:�����zxC��.�>gZW
q.{	m�n��@�W��k(�{4�x �
�T<y� �����H��D1B�(
T��}���>{�kN������y��e��s3�^�F_7n�[]��r����S*����OTDR����4�>'Z�d�B4\bx�ki�����j&j��N3�!��:E���-}���9�,�|�?��YP�cE�����[��b���T�>�z�]b)x[fl,��v�z}���>�l)��*���m Z����b��H���^1Y$�Q��b�����xTE#.�=E������ ���|D��Bd�)�Y��p����h(�G%��,6�y�&g�C�z���Q�^h�e�;�n���#�Y8#������7�.V�F�I�_Z�N�H	]Z���#��z�"�
���8��P- ���[�u��7�<��kJ�Q��,�J�~Q`K���������`��X�D�le�!3��4\�,���%��p�O����;�.v�ya�G�J����0��n+9���������J�������0����<�nc��&3N������,���r0���n���B")W����m�Znr[�����n��?o�i�����W7��:M$�7���'1��h�)����H!YiH���A� ��bVel��TU+Y�����6��!�h�Z�������o�f�@�)�L��X%D�����3�?r�1��m�pa�����i��>F��A�)��,����FE*g$�i!��;K{(o���`q�	"�_��|J4�>���s�C��>��4��Y��������.uK�+Vl� �9�����qo
�������������������������JAfR��{��3[��y�G�c����85��H��;^5�����;#�)�Z��h��	�V����a5�#�v�F���T
��u=t�D��[1]�%�S�W��� �b�D�]xMy )�j����5�� ��3��i~DpA��Ej���Q��)����D)}v2Iqj�>����=aJs XH�~�bf��l���,|�Z&qF1�#�9���tE�3�����tu�����edP����oK�e��:K��&�Z?������@��j���T��K�X��/Pa+#6���D�>���td&'��=��n����|75T�X���o���B�F^T���7{���M���6�m���[B������qN����z�1m��Y8��>�]���O�D���m���6�?C(�i=��9��������o�H�h�%�m�@F��{�xS��n�=��t�:zc0zl�JEVh
{*U���~l��|W�v�������k��"���*
����;�Y�J�L=A]��&��^���M�����^��P�~]���O6����Ei��K�j�������)�J9��\4�����/,"�-��|#c�B �����	e�B���F����k"D���M���y�~�Q���$$K#�eP�L����j���,�XS���TD�.+�/�E���w���i5{/M�x9��c�?�l�D��\�{X����:G1�N�n:|�:���);$h�>�CG.g
U�0������[+��(����|fPg�R��_rB�WH�yM�Hn|����_�R_t�7o����'��63�e)I�xi�j~���m����a6���
����Z�MmI�!70l���������]�_�����	���^:�
������fj���}�@>&�H�R�������"���1�&�)]6\1$
���n�+�:	NA(l���u����LL��Z����k&���Qc��r�C��5%	�R)U���`j�}]"x�
7+�M�1��D���xo1���6<De��(�_Gj�����v������h��,cL<jG�k��/��8���Kzc+��FEI�����u1)F>��%����o��^�3&2�L]��|X^u1%��Y������P���+�)3�9�`B��J@��yI��tI��j�x�=(���
!K�id������Dpm�-nHg'�������2�O�{c��
����1�.���	�U���R�:�'�I�L��97)%�������c�N(J���>����a��X�w�X��\��Sf�,�R2'2��{�*���e�����/	F�h-#��q�dh�R���EkNg�6Q�i��sU�l=����R�_)P\���(|#J�	T1��d���&:�K�j��O�ct�F��l�h�6Be����J���W�[Y
D��2?��7t�l��cA�-�D��l�r-�]oQI!�^U-$�����"�����#XLYF��d��E�m�W���Z�>����=B�(h���e�E6@����)������f?�j�c�D����#��]R�%
3'��P�Kt`O~����oK�5=s�$i����{�x�[d��c�e�Xf%X�JO�m������,Y�9*�����y������
��|gh��=����<I��P~s�|�S;�+W��/8��a��xwN"�w�[b��P[�e'���-���V*����`����ub������(f-�\f���Vj�h6�P}bl����u����I��>��7�X��qN�P��:@^9�z�k�Y�A���Y�"on:�^�)��$���O
M��s�7[L<�%m�\u�a'���e4�b�w;�b�E���tA�k�
B�~�	O
dH3�A#p���	�e�;B�����ZJ	YL�l������#f��+@0O���9�2���������J���H��g\dZ!��@��R�kzp�.F�h��Mu&��
%�����i�3=���Zy�{{\B��5*�u���(��jetP�
���5�p�7J�R�����N�����.���	qZ��L�"nW�d0�,��#ZvF��]��,|D��J�Q#�����zw�kS\�U��u�c�P�*3d�A��B������Q��)qR��������F��b).l+�fy�
wNb�W��< ��'������u�H����:��A}\iT��r�s+��I�r�_bU��u��4��!
�?5
5b�{:�Qa?(��[�M��y(j�8�p�{s���m���GM�Q�����B_`�0R�T��t�LVL������oo��=r���@v�(���@E�N/�������V�~Z��������Y��[(�R�����V���M+���U��*\EEey��������n���J�}��l_��o���M��B�4��'!���7�g�[����`�5��R���f.T����q�~T;9���r�Q��p�|�*�V!��$�ew�B�!����t'!@�&bf�����3��E�q7�#e:�����j�P�d���T#�r��R�#��6��(�������g��]O2/-��7 ^��l/���u�Lgo��U���:N��Y��Q��	K(S<F*��a�N:�T�y<`RQ���+oT�&�k���fXT�-g�3Hj#��r�$�R*X��*1���s4(,��0E��L��'�jz4�84�0���9���8��0D�1��HH�lA�H�s�G
G���6��|D�'=I����#D� \r����b�/�,�3o(z����o#�=������^�g�n�`>�H�{_�x����L�C /����\(�����m��@�����J���H;�_��<�0��N�nI��F\��`���J��?r�V��B[iVk����@�7juw|���.��+Oz%�q
�/��%y{��$P_�j=�*#��������#<���*[65�I>3<�����,2!T��x������OH�<N.i��\�����N��&%��]���SI�#�5��J�����U��3H����*� XD�;�Ih�;`��-�A��r[���R���]��}X��B"2��s ��%�x��e&�u�?�'"/#!��7�#��������x�'���i��O�Y�%QTF�'5h�4�����t��u������*���#3�����>�<*B��9:�Q7���H&r���$".��-�V��k�.��::p��1�Y�j��cw�����F5y"�(B�]g9Q�8�ug�uF�����K����	��N�$NAfX��%(�{�����8G�{�=�qQ[A���`2�KI�N���}����W�A�x��U�N]6����w�;��'��`�����a��Y�;�j��0r�����P��y���x����x�S)�|����y���K,���N��S��ynn{���./�Z���Z8�l��������o]��Y����r
4�k��9�1�<�����w?���.o���F�j���������9)Q�B^�j�-�_0���9�������d��fd�@�N)�.D���v�,�Srn/�����N��+Y�e�mb�Q��o^Q�*��Pi�������#�����������z�����w7���S�g�~�S�|��H�P�H� J�qMe��mg����p����}4�5���������:���CJ<���)�}�#�[��"L�u�(���h����eR�q��G��a�
��>�
Oj����Te�25Y��G��������d�Y�$���_`2��yc��p9+�TB�1�Ge,���nC�%p���
�
/���uEtD�$8'�,k��r��6�o���O$$�uNv%���Nn�����#IX����o���y0u"k0u"����aF���o��~�Fw'�Ta|����#�1U�	=v�Z����GpV�Bg�*���R�z����-��u�=9i�G+p�3��c�D1:�4(��C'�.����1�=�m���������r��V����������KxCL���E��K�d�o����69�Y"}g��W���������IH�g$�O��{����%��V]���LUd� �T���`�hQPA��<b#R�3q������g����X����b��J*�!
��|_

��������G�0e�@��
m��u8�*�P�sr�[���W���H��1��$	��"���]v�cP���>q��JG�����r���>�#�KZx�WT�IcW��M��zaA�X��3����&S�����Ic
�-�<c[�.#1�������lQP�S5p]���8���w+/��V�P���h*�"�E��`�Vz��I��0��R:J���"�]�5��Gl��|��\sE@2z�������
C=8�-f���%�K�{�r�{�G���
����m��I&E�3��o����1���^�QY~�X8h�G�#�4_�U����o*��J��)`��Q��J�-~��i�������|2	P[F�C�
�T1��� 6����s%��y�����z�3�n�Y�fg�,I4�A��Za��ZE��t�>�C[�����P'�������
�a���`������)|5%p�
XT�	��E�4�_�MJUPdHL�^�Ul/]�i��%GKU#���re����v��2���(������#90�K%GB-`.FU,�����
m����Aj^Q�s��3����O�e���tl�������k�IT^�;�T���3j0t����JW0��.�[_	����c����]_O��������t��x��o��L�����x��x��o\����rC��j
�F� n����=���d��tF�����������^���ry�8��<�`0���U����$�DOj�uR$������I�50��3
�{�e�3
�\�9�&�7��L������9��f�"FE"��$lK����E��GC�c���P��}�	���r���
uT��$3�0'}�����_���)����	�n/�W�An7��&�e��x�Gd�#�,�u���+�z"�v��(�����j,!�Cxv����<��3|b�B���;
c�)
����C��$s�T|�V^�ie3�MK����{5�sV]n2�`u�Y����j_�$�������*9���7��L���t�4�r .8�����k�Cy�������������*�0�88���	h����
�N�3b��t���j��Zwr}�����(T���oo�������j}��t9�gt`��dhT���d��Mu����3.,K�}��o������G��r��uJx�N��f����}]�)s.���n��=ow������"��������\�a�����?
�c������#���������2>>�f������R%�Hp�Nn��]
~rC�dh��[��	��e��wO���`��Y�[8���^\&1/E������"	 `����E�&���O�r>Y����vo�y9n���|�-����SM�'%6�;�J���c2�X<��w������c����������������3\N])��)G�GA��`RtN��:A��0��������:K��3.�3&d�2����la�6����Y����w���"�"�*�h$�s�������w<L����
�
LJk6�����,��?��������(�V��{ 2$
����/�
��*�~��E�.���o��d�.�'�2���vz��0�2�F�S]M=m�~����S~J�'�� ' �*|�;7rp�����>��FG
a�h������VE?�����Pn��UN��A�4Z��\���f�pg~���8���V��2�T�3Y)c6G�PH4\x3���d*Y����c8�kC�0/�|����M���=���2nZ,��2�����W?#��Vbx2�i<������wu�l�����M�����j�������4�Y�&h��lVb���u��������gE�/�{���	�\����KytC�����5K����%���fw�o�K�{w/�����5D��-��Ms5���eI�����k=".Q�5��0{��,#�2���Sn���d�1���>�\Cf�b���g����/�1����l��{\H��`~
8z����8����]�z����{�
<���[g�����"s�6��k��|��67�pY�$	�$E������kI�[���7?@D�q#�f%\#+�5EHR�����>xq�^��z���:�fz�B��o�A��VSt����#f��@��21��L�@h��"bt�s�4P#�����9X��:�X�ye�NX���:��@y��[�;
'�^+����L��M&�
��`���Y����0
UT?F�w�QQ�������KE�6�C(_�}i�����
%�{�R	z���hP����W���(�
5��%7U�L�t���8"�%�w!9��U�,�{�<�U�*��Q����
�g�z��Q]�3���!���H�����w�.���8�s��T L�S�����m~�y������}��4_�"N>���cq��qq����g����s���l�s�L��<NX��Bw��r�f�U���Q�p��p��{����Z��X��%���L�f�T8�b!��	�I����P�0�n�l�������^�.�]5���'�H/�6��`\?��`z���Q�R�����Q���F �T�2��G��D
q�g��7c�����=�� aF�����1�#:�!:�'�a���8.6��q�L���'������G}Lk�N&}�!)�M�x��=��_�[��"�|Cs���w�_#R6�$��	��wsvT�����u���&�x;�sN����'����K~�>�.����`��%���p����6�������|���_*�������g��t0���D�������/"V|���A�\g�RA��^h�G�w��8�	��MQ��!�d�O9���>d$Ql=A������X<�N�<��p.�����A%�WC��`���+bQe ����8��o��@��)d�5D���q��U�.����(3+z��!9���})0$4��cY��E"�K1�����a����x�R�<��B�Q�#FUD�����t�?�:���`pey��4*[b�T��J�<�5R�n��.m��D���8Z%��-��>�wv(&TPdc2��M��f�������9Q��_�M���Y��\�o�����Q�]�����SJV4��CB�(��&<���>�M;��e�������tn�H�??qh�i�$�F��o[����jL�����>�a��
�.��r���{���"��XM2hp�$u:F&���X������X18G����C]s��,\�
��Fk��	����p*O`O��{"�<��|,hX2u���tw�p��
�}���j�YD)k��a���Q��";�7��������UU��f���EqC�)�/*�����94�.X�w�-V��U���"DBd��
[eV2������iF-�}2�1M&\���������n�����nOO�����T��1�mm�[�E�''�F�"����0�+�UUWOlj����9W����%,���o�w=��N�����b1�B��K���u��ZO$nv|A=/9�_<W��$Y�(S�N���b������Z����^8����>a�R�1yPr�}B?��f���*{a�z�	9�]��by�������w��g�>�g��kk;$0d7����Y��'��P(������@��z����!�ne�������l����\��u�t@q!G5q�L�Z�w]��'�[����!k���!��z#�q=i�2���M��I+�{V�?~�s��:W���q�%�@��E��c>�I8�~��A�wc���������M���nQ=+�^e�����q�89���-���mB�sdL�!h�K��D*�9������s�j���1%2�?���{�����4�KQ�-�z�|����L5g$��W/c�tu��T�!�Ma�3o"��5��v<5��r�V;����������)wR�R�����ke�W.
�s
���2K�n�I�1����
������������n���)���8�u�1�O"��Z���n���`|x<.����Q��Q�=�����^�U� �!��L�UC�����$pE��������$�����D6VY��k�����6JuPno���y��|��N�v��� S^3����n*�=���X\��N���#x��O]������S�<�Qb���?���������X�J#�K��y�Iv��uL�jS9��7y�\rN�����
���aeT?q����*��2U����
�T	1����W�[;����A;�X~�c�����R�m;"����o����o����}��9	�������������{�'�y�������5�����g�;C*���Qgc�;�����l�����a���P�yJe�%�p6� \��r�1����\@m�v��O�����`�������AIA��v�(j���S��9��C��&�dtn/0U���o_#�$��#W*����>�x8����H��J�o��|��8�Ygo}�7�����q?v�xy{��]5���{s��������+�QNU�Pj�{����1��v���|{{�w{�i��hp��������:�<	���bgoN/oo:=��6�c��I�N��w�*S�C��"XzT����q��zP;98���y��gKL��'#�~d�J��~�^;�kLe������flY�:p��B�N���!��\�Z�zRr8����s1	���#�[0'�D��o�;�+r��$S<�������p����_�_$@���Y�K�H�57t���0�kC���0L�fD?�9������3eT�7: ����!����Qn9@��?�"��Q��%3�rtr:��>�O���� �p)�6����h�2���]Q��>/�@��c;�i
m�?h�;��D	
�D?�C���KA&W��9��#WY����1G$��+"kJ��������p?f0�a�j:�pHG���L��D
[������sP:5�}�|cC���IZ8R->����{V������=���&��r��O�^��K�����R?�.�b�H���2mP��_D�#}��*z"�k��4�-9�P�VU�5��/}����&�9���2anG�
�Z�b+Z�4U�����2�LhN�����?09z~�lb&�n�n�g��z��gVE���)�!���<���O������Q_�w2���_�=�����������D�S���H�+����#)`�`�?�pQv��*�T�#���H5�F��{�3{��IRy���Z�e��<N)Lr9��X�#�a?����N��f�im}�!�Lc�zi�h���8M�L=4{�T�iAA��L������w������0/�z�zC���cK�G\8�����6�E���,glz\�n�P�ZJ$����ENRK���~?[N��-N��,���L�����!M�M%�)�`.�$}D���}|�;> ��Sgw���=��/�?�2K/c�2�k��L�k��b�OQd��v#�`��H��H�B����R�B�|�UP���|���,)}�og�4��Z���&]�1��^�����>���p�p��F5�>�r?�G%'��b\S�NSP�M���	�@<���Tt�<����l����;��0<�b
��~�`�U��v��$�?�\��RbIr�����=wx��Hv������E3�"xD;��cA�\�����e���w wLyu�[N��S�p^���C�'�T����&|WRX}N���������_|I��O,PW'��{nw���a_Z�������
��CJ1�������b�Y~�a���aH��������|�<�?��QJ����������i^#��,�4�4YYp�|�2R���n��2�<]8���s�NM�p�/�����O�eS���?��B���T�� taG��M�G�!�c)�Z�x

���Y�>�F��ol!b�uu���G�-�h�����L?y�<aE	B+g�ItkM�U����b]g��cj� 2��$|��)�-�������q$t��qk�\5���MMNl�!���xI�����U��<'�Ni��3T�iz���L`ml�E0�*p��\�h��76��xWC�uQ���L��$�
���z�MY�T�������$�)����3E1^T���R���q��Q���������v�V��`�@������+���S���bz
�[�QuJ��Nf�_p��vLKT��"��������"R����"�{��F�Q��<��G������[�U������*�F��)`)5O�f�o�(�-��\��y��/�r��7��������������&H����(AS�	��^�����]R������{fR�@9T����Q:p��dD���r�Qj���x?�}`�V y'q��Up�������y�8*H��${��z	oT���"��;��t�b;���_L_G�7].��:�$��Tot�w���i���UuY�z���������:i����bu'���J���pJ�f���B)�*����+Ud�#(�N���]0���.X����p�<d;F-9OE����
E�#�y�o���e�!�sm�Qr���6�H!�(���JL���U�������p-�!~��XW�cn��anBU���%��7��Q���X}Q�z����8���Y�T����1�T�S6`x�R6�X�D���� ����d���uR=���r�4�H�=%�/{������b���W���z��o�P��E���-k���lO������)��+��
��:I���Z�Df�������fk+mrL�����hE�JX��=�i��Yto/����9�3�&�3�W��`����Rk�v|v��Y����9�W	�#�c1��L��2����oZ?e4Qr���Hi�]�M�$�3Rh�=,���B������������fn4x�4�(d)Q�p����Z(��]\��T��g�P����)*<VZ��8�P4�&��+�N�M��SrP,�����j��j����!k��/����h@`�orUY��VN�A�&}�d�!*FB����/���T������J���mS_u�b+��@u��0#�v#���J%��[w*�e�O�Xg�1������/QZ��9\��Smv���q8.���}o�P��TC����$��2�
���y�P��Ue������R�D�����y,p���=���]�2��)�������2����$y
�T�}u��#OA��s+��2R���d��5����+]�n2���?V3����xk���'��%w��^7�h��O����m�q�Rwkz��|������N@�o8
��6�~�<�^����r�V�^�2�����'���QFNQ��$����^�q	�#��oF~�?w�k�~�����#$v�JD/��3;��%��G��0w��t���n���
i�|+�>���Qex�����`<8��:�"U��8 i�!��B�)���A8���1�E�n\��/�Z�b��������E�y���;��������o����8/�@���x%��-h�����w������
{�������a��Jd�����F����o��T�m����|��S���,��	����W��6��-���Y��]���-nX���u# �oH���L��_�/�[�.������v~-<�m�C�9�'������1$��e�������������k�}N����Ci�N�~i<�����l��a�"3K�P��xX���C���x0<�*�aV��5�.j�-lr4�r�'#�0��b���kR������E����,�}������"2�+�cS*�p�r�	OJ
�&�w�P�KoV�tLXi���q
%��Z�������s`��z	�S�:a���V�5�=������4���z������Vv��Q���N��	y������=�GE�?�{E�_�w�PWL�����q�I���8!1~����!d^{����O�]X�}LF��Q�
��y-�I�%G�S�06R��z����T%�G����1Y�����^H#������g��N����{�*Z�$^�a����������}Q��������0���HYV_���E�}d��l��������9����]���'?XFJ�5Bdw���E����v6Q������l�����yQ�\�1�?g|����}�muz9c��>Q��>�iD�}L�_�����nk%%��4��^):o�����sN\~������~���u��CQ���������)��F�m��o��8��������9��44�(u����v�G����B���r�b��KN��B�f�`	-�����{�
��S(2)a�� ��/��L��}�<.��+�M��
&X��{�n�.@v��nD�\�26�D�G��k�y����%W�4v���7��i��7/��-��v��w�)�5���A��gI�R��2��}���f��D5T>�y�h��+@������}��&~g�rp�a]���&qi��)1��`��h����J������`�u�}�9��u(pmt<t8
��G��1\�7��=eR��z�l�������7������j�+|w�x��/PX���wU?g��LG��8�pl2G�j9-q�M��&��O_%�Uzu�7�^u�sG}OY����W��-)����7d
�[� \��NtO��C�1,���x���''���u������9�����G"g{tJ{�u��^8�X��%O��R�y@��������;+�W��>���?�7�OTE��T�l9
DM<��Y5��^`|p\���r���8�Uk�Q�I~=+�@�0�A�v�x����2�3���Y#�|�t�_�>�^7%G������b�����/�FIVq]��+d	�kE�
��E���Y��4r]b��KU�?�j��i�t���,�Z<�K�������V{��p�T�T�T>�
�|@����������7W��7�����{{&4�wzP=�H��7 l�����^;��+�}�@0��R�������7m����j�rKor��w��q��6�n@���gz����UlC;�N���Sq��%�#m���V���
���y
��l�5��s��;�b�0N�3U{\��mTm����_�:-a����C��R7����w\;�MbA������,cL��[�����5&�)c��9c�W��/��\���2���I���+�_q���=�D��d5�����5-tk�B7-���E�A��h������~>�~]����U11��_�d�7������<�h�d ����������'z$�$��:j��2^�g�~
���\��W���n����~�+��P�{�t��_DJ���E���7�[��7��f��7��Y��bI��'�E�~���~�i���V�-���C�?��XP�.����Po�����}�����y��W����i���x�MV
p���%+���*J~3_M]�o���.��]5&��?C��o�OVo�����
��3������
������7�oU�V����I����*��f�
������T�m��j�
�z*����OU�V��������L5,���J��t��z�8��p�X�Pq���M�[g���y�;�Z]N���w�������^m��������\�]\����R����?�G��vw[]Z���Gb5�\8d�'�`�TO�S����;b-l� c��|��J-����<|��{�O���3|<��2�z��TF}�'����n�f@���h9q)�iL=�c��p��;|9������	�f{�b�1}	!�,T����G�
����`���{��S�.ne�`x�n�d��NA��e�a��G���U���,h���n����0$�.q�:d|y��Bl#:]&������5��}��w���[*�Mh�
��9�^���t>��>t�����V��M��[V����@p-�|}�
��<�B��@�O�z�S����kr���.	���
�;��f�aY�s�a} �y_�cLX���/?�j����f ,'���!�C%^�������������x�c����5��/��\f�7Z�(�|��g��t^�[n{���[,}�-�����~S0N�P�3��sZ�����Z��(����~lF[���2j3����C��O=X��y���N��.�B7 ���F���h9q�t��l���nyc��_�)��.�l�A!=�;<:��oZ���/����f�1���������Q.ov�o�"H�{_�
����p�!5����0*]�69��
�[=99�{����%����V�������L=s'�8g����O<)�U�������������U�Q>�o9�$��}�����E�J�5��@�r���
{B����������5��.���t	j*�&�����a���#��f�b�s�X\������`w$���������[�Qv� <[�B&�ZXHi��7URh"�C��d;y��5������5��a�:�\zJ�7���,KOT�����Py-�U]���4�MU�Bs�7�������o*^���A#O���!b�k������n�
|o��4d(c���^XF�0���nk��(P[�������L3����9��b=x��<$�28VH�.��=J�������p�	�U'u���[o���@=����e���2D�"�9L�:�#��tb�_��kq!�(��qb��m:l~���M�L��F�>�Q������0T\2��ua5*�X:���t�Ra��jb;��Uz[[�����t~�,�����y�����!��}�T�g��?��d
���0��V��Q$Z
��hG?�g�Lm��%�B��C�QA{��b#�2�*
K�5Q+P���c��*��K'���t��M��F���h;���yC`��0b'R�z��^bR�EL����oH��k]������y�����w)���������9��f�'(��Nm{�w'�|���Z#�F�����V#��,;�'_�.u���_��td��r�Z�u�R�1����y![=�k(�c�`���������m'�}A6\�8 �s8)�G\+��80fe�rF��\�(�%
���`����#�*���`
cH(0[[,��o�=����Ox������C�?uN�>�]���X�MJ3� 4�.'E���P&"���D(K��/)��7�W����$`��ewA���h�{,"9�(��#1����C��I��1���0��>I�b3c�RD�7���C����Z�)z7a&�9���z7�0	�V�l�--'�y���/��hGl-I�	�	�UT�)�4�a m�&���7�w����^r\���rX
5A���Z��6��U3�����zB1�K��^&�6?�[��_��eU ���"W���7����S��)bK��X�s����(
�pv@P0����$�*�^��sv�����T�%�����������+�;��Pz��������wJ9�J�XA\�Z���>�;,�%(��HR
���H��G1���?]R��*�sV��������(�����w�1��qxz���TN�_�/�~�����c��������QM�z]Nq8S�f�n���m����!nxf��;�k���\���d}�����QJ�G�3��X}���6e��[�0ui<��!��D��m��Uj
���U�G��Tl��j��'^����
�`|~ �s�`��+
��b�JO��1�8�=�:�C+�\�)�`0�D���{��q��y���&��p?�"Ja��x�-����&c��1_c���g�����O,���'���lUS^�T����Z4��l	�'�Q�&���|,Z^�"L9Cc,�it/�gz�����gL�A:U�?�#\��(a�`e����@}�@N�Fu�pa��b���@#<�3*��'/� ���eh0����(�$'J����,>�X���Q�v?1-�W!����"Y�	^���_`��)�=��"�����u��KkC��f��`����,D���8&�G`w����-���o��{��xQ��N����Q��y����W�����g����ft%X}>a�M�n=�9C�0������A�u��.��i��-c���T���6#	<K
H>��6�%.�R)(,�y���lg�?�W�6k^��Z�u�F������\w{�j1�$DTw�q�hCw��.� C���/
Q!A���V������/� _�����y6����G�N(�����~��:���/[���V������f�A�M��K(&���@��������0D5�$������������l�����(��f�����]�[��|)�x&�@�I����`#�yh���<6t�E3)f�GX��j�=P�Ua����vu�@h�����.�s��dry����Fd%���T�>�wfo�oa'���~P-�w�"�4��tn�@T-ao�9��.'X�5�fZ@h=m4�"�-���Z,N���)
�%�(�o�������1�*���(����"�nft���I�@�����������o9����b9�i4/�]���~!g�O���,��{��Ns
y=K�������wGp��.h<E�d&)�	���E�*����/�oJr%��#D�sg�6���8m �mA���Ge��>�t$1;G�iZ�		W�m�J����u;/�+��dEP����?)������v�C�)��pV����k�_,���cU��K���)
Vj��OR�4�Ta�A�m���9M#o��S�����I���4�6'��$9�%~S9J����/��;���hy��M(Q"5v��6����%����
l��V�d�?���F���ka�-6�	&R����5b��lF�kH)0�����J�slA��i��m��qq�+'�
��`���g��(�vr����~�m��:�F���n��4��S;�]2~J�GI��y�A�AR����T�9��l�� �O��,O5gCV4_�
�1���G�&`�%�B����i�
�){0w�0�#=����1� $O6%�e���Pg�FQ�:f��%�cn<9H��p� b���3Z��228�1�m��s���b.I\�u�*9b��d����5l�w��l��vJr����Y*�o<K3k�K:��^�������N�SO�e��Sv��$�b���\�k)>�sEZ#����z����Kz}�uv�9G$yCbk�x���lOvT�|#�gZ�GK�������Et�E�l}��}��*W����E���d��c����H���}���n���u�
c��0V!m�5�	�;c@rVA	=R�7B�XA��qU�7�Bw��T�t ���.�Hih�
i�4r�X=`��?#��0��-�#�C�
,SJ|J�#H��$�S�f����Q����� �;/i�u'D���BQ���f|�Z��/royy�H�Kj3��.��5aU�	B�b-�`bP>i	����Q�����G�wL����k�<�a�g,���5�,��8i�S�j����3�,��rV��x���(�z+�_�G"mmi"�
��!2��$���Ib'j��U5�|D��S���,+�[�&f�%1�-��V�.��<�#e�>@���z}��cZ�P��1�9������zs>p��p�+�!w����O�q�D��u�}����BH���B�N�M��l6���#��Nl��1������0�R�{��I�w!���~�G�������gNU������#�&��A��
�0/�3���o��Z0z#T���T�sFWp�T���;�8]f�sXK���=����8���LZ;+9�bw����h�
s@	^u>?��JLh��;��'[���"�<��8���b�eJ��I�}V��^�H_JV�_�����zE��u���I`�'�O�#��Lt�d��"{�t��8��K���$aD�8(g��?�vy5gT�[�%R���A��;��{����>�D���0E3(��B��l�y��G�b���[)�-�e�u �)�����x�1�t�a��Y����Hm�F�#�V�%#�^����S��������-�W����)��$�������`��>R�x�{�VVs���^��Q�����T��	����$9�e���u����Am��Y�}��u����{�me��Q���_�b<��"2��c���w���G���S�����]��
�E�F������h��������LX��[g�pN<{��n%]9b.���������N�"\��~F�%��[%a{�9b5o�A�,0��=�&�����Uj^�j�	�u|��e�M'A�qo9��D�
:X�#�4B��]6tJ����	�c���{�=P���V�<k�����uxz��}�=��Xb�����$)���y!=%iL'��?��-P%Eb���0\�'�l�B������D�-:q���#�A����6��l�j[���pH��#f�6���l�AJKU@�m��V��I����/� ���<{���y	��Aw�5��l���e2�K�������m��Vx�o���>�x�OA��?�~���	h8-�A
��k	��X��7%b�w�T���y)�~�
K�alR���	�o�J�����Z��:oaq�u�NU\gR'+&����*��7��g�3��$������^y$����� V*P"�<\@�������c"r^W!(�5������lG*��f��uX6�59C�'�z��`JY��}j�5����r�;�K(N��+)=���Fl�B�m��T�uc���� ��:�OOj��%N�3��;���d�d����\J�"S'�M����VV�����,��������<��jD4:��(A0e:������u����Z�_N�R�&�n�"i2����z��q�4%����}�*JV�9z��U�r#x���0�>�����<%w�p�0|�nb��@*nuu�'Wr8�Gy���ia�K�k�A�L�
&�J`�$p��������t!�%�$���Y����G�/��T��"�%��N������Li�����J�6���5�����wg+��%�
��5�&`ar'%	�����Z����M��M]��Z���H*���)Rf��c��W�����#`T;F�Q����Q�2���P�����f8����c����9Q&�j���.i���
�0�C�i�D}�lA���Mb���R�)s���5;�X��W��yw}F5U>��
q^�w�.��W���<(�w|�aQ�<�HE|�*.>���eI��F:G0������B���%��]���|Z�O1��)���e����G��$�r*�"ZG)mQ_A*[32�m�"R���V��d3�
�tS��%�R��tc)�YQ��N�W�rb	������{i$���K*�r�N�	��D��gU�O���Qf|l:S�ag�O���2��K�n#���(���VW1_������>�onn�`�eD��E���&�&T=`��V(P ��!~���K\{�A@7�Q�sY`.!^FLw^�#�������zU�W,Y,B�JQW�V54_z���%�J����)�N��@������7�:�>U��Q��d=��W�8�~S��x���j����S�i��m���?U���w*,v��*��
&�L**�������`a���v��V0j;(�)PIM�K��A��5��^�m
�q�I�Q,�]�8��������.��FP:��Q
��MBd����'�����&��F��G�qo�#�-��N:s�9�
��z^rhsU�FkdVM��_����r-�H|4�C�R�`�=����T���`+�������'������"t%�SK��_�����"���|��acZ�,�5��Q3���yC�$�_2"�W97�����{������y{��7:�J���mS�b����Z�m�h{D)v&��O�,JS,�EZV��~�{6�PU(�rw'�y��M�
;�����,H�tF�8�;�f��������C��%���)��U��!$x6`O�o��OC��l�R����~m��Jx�����GK�n�wi�+�ZsDV�#4�D��	�wx���S���H�D�q����m�	�9I�P�4�i`�Jsg���K:��^��W����>5D�{~FJ�s1|��k�8G�2x�D�8��NP��!�����V7���A�K���������"��>A�W:�#��6R{4.^��Y��h>O�%#Y��j$����1%�k�_G�48�se������Q�pL�A����8=�E
��?8UH���!����Bk����e4V�L�K�(j��q���0�}FG�}��t��N.Q
Aq�����\����9��'e2.qk&=���F��l �My�T����\8����4���h.v�[w��K��23�7tD4Nf����i��~%A;)���U�.��T�)��ZiInk)�3a�c9,���3��X����WP6���SSvC��]�m.�l�"[��&UH	���g)v������a�L6Qr�<�����D"����BZ����5�����iII�l��%�sG��J���y���v�z�&s��Sz��b��T�2�����MCGq��G�A��Q ���gN$Zd�0I��oq4H���_�AN.�64j����PT����
y�����'�~[��W
��N��|��D��!i����D*d���N��%����l)Y���gIV������l���{����gOV��8t@N��H]�E��~:	�'K�>A'�p�w��w�F���]��0�����`��rCq����QBw��h����jAV������g\/G�t[�����x�����3I������E����	3���
W��:4.��0����/�p��f�e��:��$1� f0�.fNB��f�����	*������O����yQf�$��Z6������8A9/�S/v�����X��KJ�l��f��t����3�� h
o6d|����.A^�;G�����������U��Z�.���3��Z�l����	u��m����X&� �WS2�fx-N���i����2B_���������Z�6�$����-DX�M�Atu���'�X>�PXVc;�������Z*��*:Lvt%�[;k���z���'��%%�&N���e0',b�)������o���1d�lqp�&��2�-J#S�,~�P�v-3�f��m/_Xg �v\�9���I&����hP�k�N�����E��Yv���*-G~��z������w|�f��>�Q����'����0a9��KH��
1L�u��AS
8�Pr+2k��PU��{�Q�Z	��9`
b]hX
I �Y� x�u���	���
C`�����KGgv��g(���<��Q���q}�=�u�O.������T�9�8��M4!�K�����k�AVA�M:�����+�%��#��3�sb��Q�����+n*g7���p)�I�s��V
�d���xW�����3�P��0���Xa�i��"9�N���n��[���.�c�)�����p|9V���������K��J)��#�2V���/���g��1�)�������a��T{qt��{���u��.[�SN�L�n��G�6�'�`��2����&KX9y���v�^���W��� s�x ������W.?(������u����V3�nge����w��b�
��o�����`�{j1��/�<�������\s��e�|��i�K��el7�1�"���o�CK��r��1��A!C�jeI�Z�a�����P�e��D��8*�>J(��c
8Z��0�O�`�2�?j���e+�01"x�Ky���k��d�gq���}�`"J
MMG�����bR�-&��j1����o���=\"�-��$!~`��0z���H:C�m���z.���]�9��B����(��}����9\(X����s��Z��i8s^�,o���|��[���1*���1��t����@z��1r]�2��{L'~a�E��7�NZ�G�k""u�avB���B�"��(��K�%
Y�K�����C�����d�'�~#��u{$\��(d����~�|rV��-#��w�h*{�/}��$�c�������F��_�*g����'�������(�rk�E�2h������7�J��
$����6���-�F�����.����'*���d�;}��0����k>tO�;0�$8H����mT)t=��-1�9�C��AD�DLs
���Sq��I�w��>b��R�Pr�=#�����c5(�3��O%� ����.XG,�"�������D!Hr:���x*��I�^�,�^j�7-�2�r$C	E`��C�y�!���]�D�E�1�t�c�1Nc�5Vq$��������T]e�/�$�4��4RM�����w|u�	l��X;��}'��fR��x{�}G)�egw�w{w�?���h�GFg���%]w�ub�DW��W���{t|��VD���wj��9��wP3�
P���$�0TY������� ��m#�%K-����;���L^�CqR�2
����V�y6���L���d�-eL�12Y���qY�^�������o�Z|r��5kZb�d�A\T�
�e����&0!�g��((2��'��,�A�1��4����XQ��P�L~��9����k��!����P����ddA?�U����ct��!���=d��9�����:�����%c�`Y(F�,
B9���?�0KB�w2R�+���M��J��m��
�1q���9�b�NX�kn>�8F&<�j�q���V-�
�����a[��K�#<&��0�	r_z��]=D_!���o��0!�I ������i���q?e���(��t��Y��e6�c�d�J�����#���2w=�V�����N��X&��
w)ky�9|Ubc ��>���9)9��1���LP��D���
�������),J�="|���doug�����{��a��j%}�
�h�t\�M�bx��j9����Qd?����@o�,�)���Pa�����|8���|�>�<��F���@&���S��f��Szj���iY�����M������j^0rw�~��,"&�%�����"E��c�iK�
b�p���V�������L�t�
|o�^}���|(�����n�A�a`D/�hP�� @>�v*����?$J�=R�,���-C:N��!|�d�l9�����<��B�V��u��r�N&cR\"���6�]���6�mS���d"�6�N�1�F�9�����T�Z�q�^�u-�������4�3�F����/Y�(���=D�O��l!�5~��������%]����<� mw���Uj��Z�^hC�U��{���xtW������L��i�f���"��k��j�{`}��H��,&��q
�8|�rw��7�bx*�Z������o:~��h�ei����5l����������������59�H�O���5 �|��$
R�%7����6�,>�K��e���8NB+��nNv�������D=�h>��T����Q��jy1����o��Z;�(C"(�U�����Of�>P�Ix�E�mc6��{�&���	���)������\G�E��(�������<�=$��!�W[K��(���������!��y����j9;��:�����Ly�A4������4'�U��z"�<���)\
p���Dg_��,���w/�Q2!f'�A���@ns��ex�Y���U9T�Q�o��1��:l<��O�`U���5H7�7 KW���4�3�A���ZD~�����v�m7��6F"s��PP�B��5���F�S��l���ISHP���+(��%��nn�#���T	��W���p&�m�� Y 	������3� �k����5��1pxPRLf�y��@l���	gD3s�]��.$J2^�x���`�wg���e��
)�f��N��	bhdT�Bme�z�g�����,+�&�N������^�zo+�H.Q7!����6�d��������f�~/�:j�O{�aFi�u��y��F���%�0�E#��@3
Y"����,��B�!Z�B����-,�0�.1;�����W�����+���%3���.����e%���$�G"VXIG�M� ��3�"�`�]2o��Z"��X�M�z�A�4l`�1&J�(�$�AZN��cZ�Qv>�-�+5��%�rm]^�u�����GS�$�i:x'�5���$1�_3o��.�	{8,��Vs�
��Rc��+��p��,I��@&�T�CR����h��e���W��S���G��$��',��l������D���A������;j���������B�o�������>�?�����'�����X�.��ha&�`"dLB&n*H}5�'AFQ��/�G�����na����N!t��:5��iJ%��DC�Z��]����IE��&jh1����?Cq���|�sPhC�W���������������rZ���B)aa�i����{4���d"r�(�R� U�'�/�z����Ek�j���ja{��(�����FE�|5�N�Q�,dImf����(	��R����!�j��I��d�5�~�]�J�N�����Zc�Y�����IEdQkE������]`��B#~x�q2Z3��j�N�$�2�����M���9T0����K,��*$��(�������b��1��"�����h���6X�
������>���A_���_w}�J����2*A�����+�*Y�X;�9r{9���c7�o'���B����k��hX+�j�r�Y�T��E�*)�\�����*\��J�'0P�h�/�Z���9d�Q������/���t9����{o�������-o3�\��R4�^d�7����W����}S���
���������"�T�o���1�:��s�N�S��2�.��������KA��Gm���}�3�.E?��Om:�\b�MR����&	C�Fq���zMB��>y�!3��.��"�D�.�^�<Z�����-�1���L��?CG
xA���@����\���������3�����Z����0$|���|�2��'����r MPlZj�i��w�w/(�4E����~�XK��9k�X���$��d���������{���2�ng� ��z����� �f��[A��6
2��8�&���jw~�>��`���[���V�+i:�M�jc��[�a���4}�SiC%�G������HD�3���T?:|&�P�3�_xC�a>��!�r���s�/FBb�ce�a���b���$Y3��W���~������������W�}��/��k�z��H�1![|1��"Z�dD&�l'���dQ�ct�V��9E��k�	�k;������[7��8����
��Z����*�
\����n�$�k��NT����<~4���[d��J1R��������K���Y�I�I�x���#IP�v�#b�*��W���@���SDX�4�y��r
Qd��yL�cL
x�����U����J�q��iz�a3{�S��' ��X�6Kn�#&� ��7�	��k��'����������:>�_��9�>�^bo/�W�����~�����(��a����N0:M���w�\w�Hb���=9=�����ozP,�>*�A��D�6���������K����ZsTtj��
��7h�kv:wY�O��Wm!���.��#�)&�
	k����k�,�?��B�tTl�V,�Q��1��/�h�����8k�m���"������(�{&��+^1-����JZ\O�����h��
1��}�[�Z9���{�i�b��y��}����W�HN��S��T�jx�B�|���`FM��	�K4GXL�nQ49����C`��N�������+�_��h�Fm�]�z�Ri��=�Yn������$.�fw0���8Izg��+���o�������>�%����+�&�R�A��q�k�EA���cN�������^#),*j���g��D�	m�shG<��G���7��[,�xU(8��efLq����	��
�]���1��	��=�u�������
���F�vB��9��~i��`R�{8��w����*VP:C�kHKF�����^�/q�����G��;n�-�&I$�x�x��i��l���Z�85����c������U]#xL���9��t��^%1��
����a��=0������S8
/��N����B������	�o�����$8;>{s|��p,�G*����1�E�i[��fN��?J�5���Y�T:�Z}�y���>���]���B��:����D'��'�����b]���+62�Il�l��J����h����L����-�=W�3,�?q����V�Q��6�5�����{:�)�IN�R��1q�H���`���
�D�bA{�������L�o��l�
��>�,u�o�5;�y����
��l����5�����a���W�B�-�O�0���_����\���UfO��]C�������O�p�����4����h��tF�m "��i�$���@������������������,���G�%be#s����,�[1���}����(#Vx��%��K�D�N�6�g2�����gt���c��G�1�����EI�3�����s�rn+�Y
�6*����Wb�!�7�D4Y8C������_+�[-'����*}��h	�Wi�j��7�~cP���l��.!�}�oI���'u1@��i��r�2MD'=���6��I-=�0G{����9�{A�[
{�j�fH�R���<9��������>�4�p����MK��ilS�f��N'�I�����������>�IOn�f���g��I�o��zy\��A�Tj
�Z��7�-H=*h
�G�HQG����o�����k	\9����������������yvxqzz|O�Q����O����3��H%rq� �t><=9>����}�||?�/N/�,j��^^�^t��x�p��pyr(��/e�k�A�]iy~�Tt<��W���6�d��I@��
j�?:J�22����X������K��q����Yqj�d>�I�A����/v��6�^sb��\����6	�N���Y�M�?j3j�U^���#t�$�(g��{������Av��v����~�
N��V��re�o���,:�%���dZ����'X�'���?����pq���C%��h�TX�Fk|�:�>�%k���{������'��kY�d�b�-�$VO2^f�No�����g�u����i��M��O��OO��Q9��F�g��B��������U�2�8+��5E��L~)������?\��������4�&�V���r��
��5;��?T:����"�������y���"Y������+���1�
�\ ���
��?�ko�`��>�����lI)�^]�9R������U.�(+��eyh���V���U��A�a��z��h�aP���Q�����.���
��z�P}x�2�9�>n�:���I�M�:��<?�����0i]"�hi]26hZ�B;�K#�P�����`om
r�]�~>��)����3��=��uu)h7#�OA�)(n��f�9Cf.!E����������mh|V��s"x�^%m�$��,��j�V)Uvw�����?��W�T����r�^<Zx�e1�y,�"%�*
�`��9
*6�Q��Y�g������W��+��V��z�Un�OU�������?d����m�����R��_��-b��e�S)����W����q�w��A��w���_+��P�3�����R��+�����b1�Tw�f���V�'O}�����q�?,y�v���_���UAU�<����`�_��ku�/�9��[
��W�/������\Y����,
qpi{e���.�&qS&^f�P��fX���0�Lh0�^�hQP����E;a�5\�zlr�k�oZQ;�R��m��E����]2���>cP��)�8����{��F(�@�d���^�\��E�`�>m�H'� Qn4&��U"#"B1��a��]
�7+8'�&�?W��6���~�d�b,S�4R��Kqa����b�H+w�����h�8�������(�CR
sG����xr����D�������K� V�j>G��t{
S3 ���AC���
*�+>��s�����.Ws4��K�JA�z&k~0<�F}`*��zD����5��K�r������@���Y��_%������pA��;���>��ViWAe�gV���&�H�W9B%z��V����U%��X^��)*vM���\e:���8fx���EE+�h D�H�
�d��GfK�T��du��v%^�G\^��"[������
-{Ce:	C�kg���v�"���T`��k��=��=����P��:e�@����p�C�0@�����q�/.4b.Fw��A��b��Q��`F��$@��)t�B��^=���� ��������b����!eB1�yF�C*+���
!q�<8����.	
�l[|
��K���]�F��Fi�}���w�"u���[8�8j �������Z��=����xhH������KxS�%C�+~� �c�k�����P��/���l���\����ks�f��Q�Uil�E��V�U-��;���3��O�4�l��Q���g"�U��*�6Tf����?���_�t��O,�/��y����\K�&n�@Q!��p����n�l��Y�{h$�rp��d���&h ���v�D}�m�\�d.��~�y�|����(W���&_�0��\�J4��Y"]v6���������=5��&��T�p����]bUT����4�
����,|/���9�+e<1W{M.�'���K(�DM"�'�>�����!�N��Qe���=3�*01�z-Bsy`�5��>?(��
��m���~��w=	��%�[l��-jW���Z�7G�:=U�������-
�D�[$&�q�0EqLh�6������!��r:Q��\�����.<@a�~���z����	h4~����3Wr"3�3���:��&��%�
0-s����`�� �K�ULT	W���"-�.������R�
������#>nO���_7���Wg�k�'�������U�{����@�? X�~��~��>ty:����#w����Q<��6��W�]�k����td,O��d\Z����6�S:Q����CY�z��!el����������G��`��=�
&��n��W�CQ"G������'G����\��t�YT�^�9�z�r����~{�j8{6���W8���W�If&�	�%<{;�M�;���lpV���%��c��&�a���,@���������k��MJ��������w��>>?$g���9��U��{vt�0
�F7��������K�V��=\x�]A��B��3��d�gkF�>%������#�%�#��!�o^�mjV�5,�������"?��88��6�<��N�
�P*��*�Q�T/���#��dlJ�H�&%yH���7��C�y1$�S*���,rA�i����H��vd�T���i,���]��<p������W���/9c������D8����vb�OB���stw��"����2�i��]<��0V�7���#Q�HV��+V4	���FF��
���^RZ��)�T��Mv��G`"�_��6�y��{���}��1�%�*��u�R�ep?��r��������"2���qu�����I��+
O����	{�s�)0���cN��X���
�\��)�����U�+7%�Nm(��c8y�e.|��c�>������|����B��ei�����<x��8����~�\�s�`AE����QI�"y��p%��@L�nz"8X��p��8*w�N�\/���A{P��#�A���6nw�7�F�\���>��{K���9�,l|�F�%rP�3Tr2�e#��x6�M�����'^;�����	F.��|:SZ��Z�yG�����'�wz�9K��@�X����<�<�G�U���|6�����~�1������}a��R��������I����J
��S�Q��p��9v`Y��]��*�����xwy���X6��g���h������������]v�OeL�c��	�$�B��QcxNe��F�P)gO���>��K8
�Z� ��I�T��f�i?��8$�&5�����-=�=�O�a��e�p�	���'��P����B�4@d��?F��z.���[�\�BXC�ar����Z�P6;�z���!�pM+�����i~�kV�_+�:�=D}�!C��O:�7dz����0=�lC��9�F��8�{���^�����Z��Y���p���B�}�R�W�*W��:B�����u�|�NK�.s�AJ�I�7Jr���9>��H���/��'D1�0Xo�0�l��B`G����=,O�u�R
E��]o�[J
&K-�T���
C+�)i7yJ��%��8#x�cy0���c��pu`+)�
��$��i�����;����J��u�������w��c��v�TZZ���g^�������;zQ'��~��C�r.�X����]��|�p��5xw�/uF���%t�K'����Q����lV��5��[�`�F����/���~$mb�>.���!�+�mk�7��b�����ly\1���cI��J�	������J�r�5���_�����������h(�V�&>��?XM���dK�Tz���k�����j���E���GZ�x����#�`�L�{����/���?���7��I
��(�a��h��� �a��,8�1�^x�j�m��"{h[h;���%4����Bx��qCX7lr%��?V�wd��0(7�GQ��*�6�X�iS8��	H�����%"D��OD��f�'h|a��x!��P"f��I�E���$�J�F��Y�����
1E���;��"��0Ke��Y1|�J_�4��������c�����/��pK�N���,��G�e�<(�r��u����,|)�}���Z�y������W������~�������x�/��7�\���W���>�(�N���j����`�`��v����O��A��;sj�'W���y�YF��t`������he��Q�P��(�
=t��,XN���91{e��=���-nQ0t�-W���Rh:H�=�5��5��px�,�K2��Q-.��bnWLwv�P�5Er����F���$��)�	�����7�*��������
��e
�e���q�5ie����K�9�ap������C#xv,��C�J�����m�a�zr��$z��{�>P����l
Qr
{4�sD����!����Jn����S�x�w��Wt�"�H��������q�b��|�5{�/�tq@����m��h�H���(rusy��E�?`6��"d����_�M!����������FH�9:�v������w/��n���
W�!�_[�`L�L#Hf��Mp�1����!RG.A�%�#G)w+X�O�(>
��(�D���	�E"�y�YqC$��ydyJk>�m�$u�t!���f�.�<��|�}ms�,�%��,��E���4�l��e)6���d�DsUNL���^�L����8K+���i�R/[��B2e��4/^9�-�u���@�X�E_���n����������@.x��l�f��������������L-�+�3,������07����8�����k�9��k�'15[���v�������H�?��}������bB�����hR����������wb��"�+�WQ��4�1Y�9|������O���>&Qn({�!�9���{,��;��n�B��7���[Bo����S���������������`?��D=^���&V�T����K��'���M��=LnZ����a����:);�����p�������d�����78\>�__��?�_4������m�X{Txl
�I~�U#jo�x�'�
ei�����?�>�d���������F<>	9�����m>dDx?_-��}�#�$����V��c*J��z���!��R����w���Q��9�Q��>�&��D�\����D�����;�qD��h.����O�'�����>�A�!M`��2�=�#.-��X��Qq���f�h����:!'���D��-�Z���
�+��\P����������V|hFO&�B��h��4z��F��'t��T�����h��H�.���/��yJ���H�O%�$#��|�����T���+x1�D�A�H|�����#}��rN���%��_�_�'���F~<��_��/��G����vV��Xy�Cb���'���n�{�_6c��k��?��u;Ya�g#[z�H���|�R��j�k��vj#o����g�%��&S3�1��������K�����^��eL!�r>M=��3�j�C�
�QO�U��N����}�\����D|�9���rDM.���E��@�f����������!�l��F�U�V|}�8`au���e��������-(��=F�T2LpO�v���5\���$���z| 2�Z��������>��H�>����1��1�;T�M?���4�����8��n�z:��#Z�=��-(�v<��8��|�c[F����)f���������j�l�W8g1�I��w(���$��}k�5�)��(�;VPa������R���\"|�L�}��B���V[�V�m���N������L�Q�P�h�s�q6��Q�#��df�)Sl�,������������a�WV1��8r�Z*�����h����r�m����K�5�F��B96��>�{p�� 4X��x�c�q9��3�XK[%$m�@oX}O2�xx�Y���?���g��G��������ZC��ha�<�v�Y��}�7�-�����dD}������|g���\���������>L�&N��n=�}������@csx����B^��j��V�K��6kM�UO�Z����h��A����0>�\�kl�u#�������������wI��2��W���V�X?��X�
6/�&�}�R��j&�J���k��s$E�O�=�����7fJ��G8_�F�!&Z)?�$��m�*{�P�,1����>�HKj
c�!=	M�����"G/����,�@�9�4�L�|i���)��N����(VNX�'�d�����U���.e�%Q���co��U,��,����J�K�y�`��b
�����������:��"��l�����Mgl�����
��-��(��.D��G����8����V��oV�O��6��\`c���.���?	���|��{Kz
���6��un�6N	FZ���>j'���v$��#!��K�9���Z��4%�����H�@���������]e��r�#���q����1t� �q�Zi+ ���h�����R�$�rY���
���iZ�_��to���~!iO�~����d�kU�L�Y�l��[�������}M��1$�|���z��z�s�?[-�#��8$al����DlSAF���9�6���R�XYl�v��l�$���j���cA!��	�R��7�]����H[7��`�Ff�UM�r�����<P�
h��_2��\>������|�O��sg����&mV�a3_��:Nl�w�_L�����VudbS�
M��y`X�C;�c�
-����j8�Y'E�F!jc���Bs����.	�T�u��(E(b�c$�Y���MK,��7�rR�#�^�1��#�J(�OX]�'���W�z�m���gp1��!c@�N11+
%v�|��h?Al'�M|�w������F�VCi��j��:Q��.Z�����N>Y��1�d�E*Bt�>�=�r'�"�0{.���-���w���V.0@��y �/p����L?G0l���w+$2R���
�
���s�����n~7M,7������=�5t���}k�(�!-���������rJH���V�+�b,Q�y4;H-���t�d�R8J��z�f�������I���9�[P���N2k�|��]�e�� �mo�4��B%y"�E�I\7��66X�\�-l%��eQ������-��������$����E@KN2��l������1%j��]���M��Y���&G6v�#t������TD�ru����7�K=��E�{�3v{;���8j'C��p�W��qJH�q�r���Ow���1������'K�0�i4S}b�)��e����(���;p�6B{��o��#8��"�!X-$(e�n8��bv�<�������)Z* �<`��O�@3T\���<�!-A+"�9Io�_��[J���l����W��?*��{���imrk����`�i�]�1��E��d��	(�T�dhF��DE��0���d\����E�4��\�Z����<G���gY�l���n�������o��&���)��n���C�)UC������ �F���!�I�4��	TAfn��������$.���-8
����8L�x7u0��Cj�u[�,������w�Z���oX��.��c�|r9Ovm��n4I����#��hg�
;�f
x>�%/�p�
n�KH�rM��X%�"$��H~�-4c;^-W��
8i^sY����G�����A�+�=\O�����5���bq��O����n�e���!V�0�<�x��}w<�����n��G�������f�Xt���~Z�P0SY��]���2����="5�h����c���=��H��0�]���S�Y�T/��(�{#��>�Z#��a���2R��P�
B�:�Xq������aw6���x�i�1�����}$o���mBf	#C�-[�7�(�{F����"�"5[�y2c chLA��}Dj�b ���\��p+�
X|;f����\��k�"��8s"�PkR���d�("�������L����J��yCn;;����O��C]������>9�����]��u��F�ACY)Z�F����
���P�$����T8"6s0	�g��a�|��4�.c8�%X}djq�3���q�u�b.aBB�����3n>#8x�hce�3l{K�P$6�)8���]��E=��i����(NO�W�����d����4�7e��%�"~d,kZ��M�r�����]�v���Vr���=���.=�g��M�sB��*��P���'?E�RL�\T��@����w�)
��K�3�a�8ZBrq���%H{,��N
���$/�3�G�@[n�*�Q��
���p���:�����R6E����s
��uAzy?�X
�����;Z��5A%�k�ER>E�%�����L]�d���E!���f	?�����^��919q�L$��[����k�sF�����N(�fJ��[Ic�wI��1�n#�
8D�f����'��*Z�x��C� �Z+�E�XO�������`������0J.��~��1wR�?&�!�y����)hCy�"�r�a���=� �(�?�I�;������=����6r�n������jZ	�f_B�l�e����z��hl`VPI�������:_!]��c�\���������'��e������U�����a>���a����_H/y�&�z�<��D��f'��F,���z�z�B�7w���1�����-������6s,���sp����m�]�|�������d�����mY�������H��^ �@)F�`u{��\��%�!��0�7����f��(�2e���WS*n:����>Y�P�����;��"�������4���M17���|�{sN�����I�
�=\�a��������s[�rr�����X����g>6�O�����k���������^!�u@���
|Og4
��T�[���R���?��	^3;���"�K�]G>�U�
�����v.��z���Frt�;�:����F��x|�{��F�G��~`P�X`��	�����J�=:�_v���+�i�opf�����oO��h;�*sY�d���q�I��E��p�$�Z�r4W�=+C&�I+��h�����uE�z����������?#�g�����R}�����^?��������;M���]��������b�d��(�8=j�Z��x����V��5�-��U��h%�u��V�
������^h<�j�t����)X�_XnZ�H+,����E������(��'!���v���+����2����+w�~�pC����Y'3�4<>�^�|�"��X�2m��TG�'�uX��Y���
)����k-l��X����md�q�E`�lq��m�Y!R�[�4t��,C)HD])�������"���X���������@w5�yZSj�gb�����O����7N�~'3��#y?@����M�����a�*�w�qw���a�fL�]]D���x(�#�`-�bs���L��eB�Hu	:\^>��Z37v������7FB��8
��I4g��&c?���k��$5t�S���o�=�1��y�C`H�`
��6������f{��u5���M=�l����G4�����[y�]��f��������w�t)����I��6��������Mt��&�EME���g�=�}5[�pHO�����2���a���.�\��������(���&q��C�-�)���-Gw�~T�H[u�m��8Y�:X�������Y�m �?������e���D���K�H6��_C�&zNf�hp3�rN��e��@��`t����Adu.�����X���N(�&bg�E�����$��g%<����M��p�9�l�rc�����)�j������5|�����N�UV�	����R���t�n���U�t2�����o!!�%^����"K����-^��,�&�N�s�x\����G��XP�'(�*h!�W��R��9Xw�d����;��h�����zyu8L���
:[E���dK�����$�������Jw?���z"��n�_~�O��+���C��G!"�E\���!��/n����?���}��N�p��
���6M�u:��?L���v]I�l�n������Th����b�x)���S(����4=��\qJ�R�
Z���+{lCUT���V��Mq�����JE�m�	��� 
Ht��6dD9
%���"G��
��k;7��3��5�&����k���%���Aq����$<gt
����0�,�Hg���X��mS�L�^�ot��v�Tj5�-�H���@YWbzF��f/\���Z����o�e����9D�9��]y�8&�K�B���=��:�4�W��	�&z��7���I�m��A>K���yE��x���@��`���b��=���S0j��M�.lpq�1��������gJ!�j��U��GJ�fe\���fu���%f��;�H��$ij3�$}�"��+��i�p��?�w(�����_*���!�X �<�'�U���NG��z���Z�ECz�ow��X�X*e=��A��5��}"G�h��cw8�
YL�����"�����CII(4�d$ra��^�Vy*	h��������	�;��?G#
3�J�.[$����wS��I���u�����~�/���E����3�I)�s�;���B�"���eW�l,!�
.�f �D�aR�Epu���T�������,�����g����"��*:I���������>���JO����E��������^�b0��!E*�����Z��S`���K����;w��w��q����A���������uO�*�`��M�m6�Z6���o��B�&5�����
�����Gir�x4������2�����{}s�3��i$��L�+[D�l�R��q�Rw��b�
�N��n{���%Z�����Z���-����>���)LF$�~n�TB��E�d�W���:����sE@�o��!,y����
�o�a����uG������������H�6��J����+AUb�g`�oo)��4�r������\b!���������$�N�%�^�2���!���ry��{��TrmY)��Hg�
o0# B��|�b%�A�!+6��G����l����{q�1��<���������H���v����}����q1(f�N��)%�K���o�DJd1_n��W�+�D��S<�����P�]�k�^�����	+��RP���<xN�&Hn�7Y��6����=-Z����*W�If��qJ7�r��r~19�����W�u9*������WC"Nh�R/o5't��U�R#���`����m�sazd�^��_����_��W�V����C;C`�r��5��^$U�7�|��lI�����
��x�,�����6��1��E�m��s�7���:����)���`5�A��V����z�h�V|�
aN�~��_�!���_���W�V&�W�(:A�N2J���jZ����
�������!.G����lZ<���}��p�O����W�2/�A���h
D�\E���2�}�1p�������d����*W��7���t�7hDA�;���sI�E�37!����0V��P�����-��)b* �&�\�V�PinC�Rr��{��\*.8�0�?����\5e�����cfiDC<�adw���������w�s�<I����'�`u���j�.�?���n�K������9�s]c�"'#��D���n�{���57��X�6��iN%��7$(����M����������Q����B7@lW�|9�\Ps�A�9O������~���o��Fz�+��3�����er��gcL ��f��^i2;~�}�b.*A�}���*�xx��l��1p�� �9��H{���k��]�-����������P�Y�z/&�'G0�Go��v�~��(�q>]�S��W�/����*�&a��k���V��!,�*�n}3�Q�o���������rq����J��:�^w�N��:Kq)v�f&_a��+��p��|�)I���k4��� ���1?�_���y!��X>a�=������a\u����#�:�/=,�=�K%���-.��?��������c���2T��2hqX�//����u�h�"�X$0��(��j%^���������X]��N�2����oON����Of�����e�9_�&�,������n��q��������.�O����.|����E��t�a4	^����7
(w��f``Y�����&�"�y���������5�.!�aN�.u��:���iL�����	��8��sa{���SX72p�"���/f]��$}ga�W,Z������:��-��1R"|��!����R���pl��cA�q���4C6��(����;�!��w������.�������]�i���M�^�i������O��LK��������Gc�=0��=���������p���M�a���[b�i[�)z���(��*�!��n��?m����l���m�����D�[��r��n���m�\����fJ�:�"�5X�R����A�e����w��77��`W��N���Y���+��w�=;�~�\��YUJQ$��/����Y�h���NF��XT6����x�ZeX#������%�m-�����>Q.��.�	t��M��3�z�VxW$����o�W�q�a�����FM{21'{o� �_$��k[kx;���q�51���t�&qh�/cb�b��Yk%���+��Z����R��i���a{�L�U7��p�	I*]� y���\A�2��`��|����X2'Q�$��n�Nuh#�'�n������Q�"D��`��L'����*�$bJ@>���$@)�@,�d���4��Ax"c1h���������D�������D.�H�B�Dx�Qb�Y��4=&���6z������.�%q��h�N��@���:J���G�Mz�|+�k'��y�V�?�)�������nQA*����������f(��B6x���S�-dq��U^��3Q8����a�D%`�����Y]P�f���if���� $g���k7�P����:J�P�
d;$�6�9��%����AT�,�|Z�~�4����>�
)[%y\��)�{����;�v@O����;c����V]�G����~�T��fm\�7n�V9����g�F�m��������Fm��g8���e��I"�=4�O�17�:��6w�����������I_��B����Q���%Q��T/��/�[�y�gR��U&1Q�$B-�f�~y8l�J��W4��a���4i8��])*��J�A�Ff�v����n�R���C�������(�����&��@����`�Y��<���aA1��H3e5h�:878_�I�A�T&>��7��H���N��(��h�dOF��)fc�+�����c��cB���BE�f���T*
�y-oT)����������L*2i�;��o����`^��A=�����JW��BE[�� ���AbEY6��I{�9@�)��l�.�%G���L�8s|�H]���t{�3����|��P����6�v���� ��e.�5�tq��\�1(l�_,+��d����=y��}�QS�����=�{��h��P13(�86�����b�cO��Y.Z_��A���H�\@���)fYXp���P��<��Z���I�hc��FL	�S�6�`W����s=��E���Y!7��j�z����=��#o��xSm����_�xvl�$�����	��������+Nj���09vH$vS�5<YM��
Q��j@���,]�
c����O��y���=���2�"��X|q��J64"=��h���� ����H�G��U#�A����

�~�-f3?�a���$�����`<��1\��r���z�5,O��4��H@W�&Y��G��`�/�.�����x�����\��2{��r�2r5��"�`�T�O{��x���.s�s�aB��l*gW��^Nf��?�6n��d��mEl$�Y�Z��U�N�x���Ob����g�s��&/��#�o���m��S`�.���������W/"�q���&��������N �Xo���q�\A0��p0�[e����4�HD2�Z����+�?C?������\=���u!�����O����w|~d��o-�p������[�#8�d�1]��,��n�T��*�������?R(���R�6^�aeoB���D�/�4�����������BXfE
W���ufeZo
>+�F����z�	y�^��Z��^V�j��,��*�C�nh���~����;��������j���;
��*-�o��������fy��4F^���juuGV���JK������
�����R��2���>X��<��w������X�����a�x�W��nUP��:>+�<��W�(>_��y��1�_��\v���r�e���0��,�&K<��,��D�J�/,��b&�cn��.���wW������q�����%:q30�9�EZ���3��E�
�H�����z\�����#@��>6�	��	��3��������W��]GZ�����Op��;u	��K���}kqJ--��#>���!_P�!w�'9������q�r�FA(\�]H���,a]
����'��]]\���A,�����������dH!����?�Vj���uW��`��C���l�OS�(S�}7��C���wU+��~����y���K�U���������?�]�9���o���[�b�����b�����f��{<������v��}u����dZ�����<��r ��d�%/G�v�V�_��WQ2���c�zU����K �(7f�?�&����5��DZR)<�)�sTZunX�/9"�q4�_cM�������/�v�0$��	�
�.x`0�9�Y9,�S<G-�#1};���x8�gbT0����vU�"��U�pw�K������D����]�b�=[�ww#��r��������N����R�w�V�M���3Q���-��qY�C`K�b��6�@}�xC#504U�q-�)�B�_�z�G���K����n�����j�W��Q�U*����_i�����n�y���4�
���8������N�DL%�l`3�8$� �]Ml������a���-G����O#
b~�{+��U���~�b�{��i��7���z�e
����n�F�
���?�����'����>G%X�0��Hc�-z�O�(��G��:3ZsG?��?�j]��n�iN��]�����������>@��0d7AK����-YH�M�(�����L����W����p��������������g�`��uVe��|�����Mu�A�k������5:8+�tW�8r
����q�)�S�K�H|o��Pv�'�Y����gW�6-���^���'��,�$%��S�aVB�
$��3����J�R����������.��}��'�j$~�3�������W���B���������\G^���h�B�
�%v��4�]��|�]���<��i4V��^Tj��T��z��7����DY��b"�;�	�&D�G��a�����}2��mN��'��%�,D$j�����1�H[�T�4�������7�3� f�����"vLrZx�)��6��f���"h����Y�sd��o�Ey��k^��ARW����2��Qk�n��UX.���>�����.�G�T�C-P?����C��������v�1R�L�����7_�@��G��-���7|��~O���+�w������9�p��@�jbL���Erph�gC r�+���s��{/4"
7���F3���SWT[��$�J��c! ��1������;�$�t
*�`��&a������u���a}���w��FN��u�
���l�{������%vt��6�P�;]FY$�U�����wv���'v�����=����Q�i2�L a����$�i�6B~|��7����lf��x�Y��2�F�5���
���.x<	�x>��/7���8R��a�����,�Y5�i4/DobI�N�s
b�$���wI�.�?)0Et�3���h�c1����}��6,;��?��m2���?�F��-&w���m"x��W*'�o}�[_-���>6�,��d�a�;��\����2�W:!;�%l���X��8	������2;�t�(�tb�Y�8��?K�����*F4r��E�����,Z���-�F��
r`����gGM��H,���T�VP��E<�?���IlY�8H��:$rl�U�3,nl)UU���"9��Z�!h1;m{��+�.�=��;"8�r�!�e��$�3�#��)b��a���y�iA�b$�'{~Jw��<Z�d0���e�����*�CO%�!�8��R���(�e�q����$���*~�X)����zZ��w�������I���������������3�N���@���H'�"iF$
��wb�X�4��]+4���v+������J��<`{�}�Px�5;��8�?#��(Z�$��l(B�����M��1����|�O:1P�������ra�q���t��E�xr���$)��7&�x���TBL�P�U���3�iS-����8�$o���A��]Pb�S�
����OD�����	6�w����$'���1�K�h��

rSLN�d��	_:"���|$t	�\74eSQ?~������!��
�kc�z_�����9}�j����(���y3��xSt���+.�1B�
�	Z�#�qI���9h$�G�c�>�KJ������U�\��>j�#���`{&J�A��K��r���a�qr���)
a�)�������Mx
��=g���{�;�c���&qk�����d�Qdp!�l�:������ -3)�B����p����h�l��@s��/~�^b43������RP�^��u	;�i�<LR<�o�h�ch�������1�'aH�%Q,�W����#%>t�=E���@�������9��an����E�7'�&F*�s|~��8��Ws='Za�}��E7:�|4��$X�I9x�lX��1j(��t�|cN��5��8I�������D������k�Ja)e�����_�����L71�Ka3�yK��p
�z����=���W�8��Y|gZ7��U	�H��*�PG��U"�I���`\�����c��gN�;�-r�Ci��X�����|u_@���R�e
���<v�7�|tR��f����(
��D�+R����7���q���8���o��<S�(��0|�T
U�H�!:u��#T��Y�\m�V����Fc�U�����t��{~rXP{&�+�H4K��~�0;�@�v.C����7��)��+��Q�4����u��02�'��C�����E���ly�p"OU������Is8)��������� hh�&z}|v����I.�J�=�_i~�@��a�'C����Px��>�"7�i
��J5o��#�yE�����@�e@$
��������
���	>3�4	��������6�HR��;�L\<��I�C������t�i��n��`�l�f����2g9-o�Y����C�}����9��Am�m���<g6L�0�[��f#uD�;���,��!M��e��*|�
ux���]`������M�r���p�v�[|��y.q�����2oW������B�q;4A�����{V����
>��m#w���x-���w�e�]�@~��.�o�Vo��?F", 4�|���ft#��u�l����\V�K�������C���$�le?"1��}�e�[nM[^��{�u��k,�V�q��4]��{�z]�F���So��k����ZK=��-j
���'��l���8��w�uP#~�k���n��q���2=�����o�����M)������������Ae�)��fc�ju��9lf9�0M�4d���4y����8w��d��<�@-K�<`k����\��B8��8�q�)qV�#6�&���f^�2����e�~���8��nc�Z(�1��K�Bo���;���9�%>�Q��7u�H�Z
]n�/d��e����k��z�U�T��v��V�J�]s�Iv9��FV�=�dR��Z`��(����A�	b��/�A���x1�+(q�-��6����;1���{�moQ�m����\ 3�R9�����x���k.�R�`5�� ��W������hUc�=��@��S�L�q��MU��*����Jy�cwh����f��0�����
��Tn���B%�yT�����o����i��P����h�U-�p>���W&?���7-���J�N�R����f|�o-khY��-C��Vvl	��'����s�%.��.,Z���0�p��	�\��8n���H�YH������������`0��~HC������+r�[�c��:>p���N��������h��=�1�5������'�hu4e3����"�kpY(���.��5��i��|�NQ�I��x�����q�&���@�E�)	���we'%K�����$1����dd������3���
�H\.�+�q������p���~�m��$!��*]"�G�����B���;X�����,�&��-c���Rv��2zHP-G����@��3�(v�m��6�I������Nv���Q-TZ��=�0@7����LB{F1F7�
�.����,i}v%_c����^	C�,���z�*�C���4~�f]������G�Wm@R���O%#�q����%x���������TZ�F+���������G��Gc�
��F����i��F�1�T�^u�5F�F�3uZ��?	��E���Ad��d�Y �,������=�������x�+��MB�LP������.�&��h�t-�S�K�] ���@�@�(f�<��P��a��!^�Dn���!,��Z�,Z�����|`	���%8�E�������
�(�*����na;������r�{�������j��p��^��������d F�����dh�Z����l�a��ZY%3�+��2T�
U��C��E��%z�����b���A���� �$�,�8�D&�D6Am�E���b�u��~���.3��$6�o8�r3��sC��UL����a��jU:�R��(�:#�<pp�����W2����������|�3|_M�
����z&T��^��W1ix��W���w�o��[p�o��C\���{�!���|��%@U�7�<�yn��o����r2f�a���W�b�=&Z�����d
gqr`�%6�d<�)���t�kU�d��f��$�����w��psC��~��]��P�||}su�����Q��,W�;��qf��@�*!�f$NV\����2<�F~�!`�lJ��E*�F-�]�x�����%PI��k�
���Gr����E���8�O��H��C��tO,���<������������U�L����hx��fnJ@�G8��!Z��M���l\�,��a��O�O��3n�z�-�9��Y���m��v��	%t[�s�V]�9��D�u�o�r�,^�F\����9'E�k�c����
��h@�Z���&W� ���f����\m�+m���3E��sG+X�(�%;<$Gr}��(z��_-�}��Y�(�+X�*"j��6�,M������Z4n!l_�qJ)�����������A	�h�W��#s�������xL��{�3T��8\�J���{���Cu#��6���V��5��$&�W��jU�����D���_�,�}!�	��M����|��Ek(H��&k�K�z'��&���������K;
n/��da���e��Ea�<�!��k�	IY���,R�
&p�3�{��cE��U�#OQ+�ci�1�n"��c�u`=�����d`^v��e����B�&�D<0���|��4��i�,��s%q^i���VP ��_	�����R�7����jj�f��W�2����	>���}
������E��y�*��Bj:a0e�1������G.��g?^V0E�~��`T�@��F����hLj�Rdk)�'���7��)�U�g�b����������i��|;�-��a`�N%����g�4h���pD��L�����zH�UYE��Z�����*��?,�������x�PD�\mV�"�Z��5Q���eg����������C���^�d�=���N*�=��-Z�.hb�#����:�U������qKP��x@��X@bU�P�b�,6)���v�s�&��xI������Z�7G�H:��{���{������l�Fa�"��[�!��]#��qQ��v���v�u���i�����z��L������pH[��/��97��
�k�&��x�!~8�z� �����'@�%{��#�c4bv�\���
C=2P�R /Qu�������T�m5~������@l5G{��)H�!/N�c�v�c�]L�Y���%�?�����|��5��}%AC��#���	2������G)x�:�3���r��E���N�����C�
W��JxbW}7��,|��������a�;�gA�HHT
:��w/>�r���i�L��|������=v��!�����?{o���q���O���M@X��h)�"!7�.y9�/,�A0�(������z�)�I�'�#�3=�VWW��+]�s�G��63C��L����� D�`L���yH�����x�����@�C�=<�a_W"����)PO�\	j�Cp���exv,
�F���bf�/)����4P�����������X*�.,��K�S��T�Q9������g�����P���`%�W����E��:�����Q��`>��q���Q�;�3(}L&�g��vP�>v����������Ud"6!X8����>��xp�O���*(��]d��@u�������%�~�*�AbPH�_/�%#��[��IB8�I3K����b��';�38�aY��%�����NY�V0	�&����%���9R�����_ L���%���;k��4�{�"
�_���a�����P�^�P��1�
�2V�	��������0���#�Q(r�r�a
�{��d�S�5W��~%��a���Cb[{���.��d�nK��l����/IW�}JH�F���i���#r'+�Q�2�$�j3k��2K#�x��G�fW;�8���jw��2�)�S�;������;�Lr��ou}�
k�����J
���!�������m�3�M|�*N��`��MN)�!� {����������?O����h�O9Vf�����X�����bV�"Z��T��!V�{���,�P�\�$+'B�7�C�������M�-�VL ���c���w�)�W�<:7.�:]t.x�nL�E"�����-�Q+G��z������KUN�Y�"����/��������x6~����>�f��
>�����[���X��t6�����{q�:?>K�"������,��,=�MG�A��!�,�������������tI��������@�!O�����!H���� ��i%C)B�^'��c��"�{Z@G����U\����%�OZ1t�[t� L)j��V������R�L��WI�y��=v!�1�Pl�����$���"��.H�J���7o�����_X���B[�&��S���U��UoUF�������
�u�fQ�s�-��<6<�j������M��r���19VD����F�$�E�#C.���P��)K�n��9j���c��7�_:|"},v�b���3�['��7dk!�n;���k�r�u�0���R��"���E�y��z���z��N�57�Fn�������$VQr��z����J�\���w�p�?
�A:��"<Qk��t]����e�x;2�9z�J-
V��( �h�o�q2�t~�j�uO.��.�M����z!����Z���E��Mi������}�a5J�����#+C_*c�qT�/��"������Y���q��b{�k<�F������B�[�T�b�!i���Q�2��Y�%4���8+�c����-�@� _{(�D��PJ^/������.LvU������C� C��_��M�d�����F�~/�y+�`�3h���%��V������Y���N��$b�	���!.g���'V?�*�J,+y��4�����M�p����+�����3��C`a�%o�p�R�H<�&t^v�H3a:�F�I/�r}��~��	�L�sO�[:C���v��^�9�M��xh�/��D	�5dd�\�i�����TIE���\���L!��E<�Iv�@��b�"�P�'l��&�D�C�rY���*�:�eU���Z���"C�;��+Jo��S���
Hl�l�rl����7�oW�/FC���O�����_	�;d8Ob�Z������t �����=��w��j���u��.��Z�h�H�I���gi3�������U�B�����	)�o�9+���
�"�k�Oh>B� +�fp�oWr02��^�����������T#�O��(G�+9�����v9���\��@d���bp���R�sx����_�;��Mq��"I��c���*��+@p�Lr���H�
�O�	�IW2 F(:�t�����/�b�CO0g���6=����d������&<�C!�V�
h2�dB�	��]o��nu2qZ�6�9������x�������t�1���7#I'M4�|�l�H!	iC��N�N
d|F��+�����`��>m|�A���n�B�������w���)�}��@9<w�?�F[Vt��4�~��If��~����A9��,h� I�M�f3Nq�_W�&?n��0).>W(x��l&�{�O�MF���p^����p1�8���������^x������{Z�)�B�i���a��kN{;�;��J����_
��|$en�_���vZ��c�;��o"���w�z��������P}4�{x��)ma;��ZG�z��#i�1�
��WWj�+���p~�n��lb��r�;�
W�� ��[Wy��	��*:q��Urw���r`}6`���k�~a�����&YSA�d��s��{&y�sA�����eVv�oW4C�E{s^���,j2������5�aI���sk��m.6��9�9Lb/k!��)r�Q�����������=����������.Z�����)��x��`��]�M����b����"n���-�c���<���y�#�q5������q?������"�@����p��:�p�����i�:�v�M����i89F�g���~������V�������"��w���i���^]����s���
j�$T�C1��6/����[��K����������EZ�j���v���J7B�z��tk�����f�E�0�f����24��v^����Qp��Ns��^^4�Nw\u�Jq���/)o�1L�g���3��\{=g4���a�@LM�k;Iym�i���ZO\�)0��������*���2&��"�N��?C�[O�r����y7'�����x�f%�7�����$��;��Nu�RV����7�R�|��%pbDXJ{a*Y�o���yw\�Y�@6'��8�2N1����7;�c�;����Cdd���(��=D�P2����^���d������/J���=���)��zw��j7�'��-��t�~`J�C%��S0E6����)Hl�
%�M�7?`�'���O�7(&^�y���i]�������2+��-r��AE�T!��W�������;xPaX:�W�f�U}R��z�!�Nt%5/��l/[���z������x����zPs�� ��_d�O��f����;���3�y
�C?�HG���j/����W*��0������!2�������@��U�~�����_6T������;"Xr��b|���_�\�)��]��"����xt�g�ey&�(�kh�:p`@�X,��s�TG;{�`�+q��'�)Hxd�.��@CW�����M$�@XG��Q|YT�"�����_>8u)��
n�������~����6��d�v�fi�E��}�<k�6�'@��.���0�x���x�Eh?�����yry��y�N�`�cTg���Q\��.���L�����(�H�?��"��k�������7g�o� 9t���3tu)��}<
������(�������������^@������S��~Q���(�3��|�:i�>vhH��K^��?X�O�Yo����+�������|��5!�f�%�aA������������Ubu ]�P����zF�S�''1�W����/2��c��#��|
O������)�#u=�i^���YMX�)(!���_�Ty5�;4u��|�*�
p��
=���8�4/����Z� .���c7����0����?��]���f����`�Ys ������@��W������7u�= ������n{cV�����V�3!����'���H-ng]�b����<��6_�}����B*���@�"�z����U����qX."Z�^�������W�^�m#`um�Z��z���4�4O��KP�9�(���4k�1
���13V��[��ovv����r���9
��������a
8k�o���p���B���52�Z��TTL������p���S�Q�@rD>�w��y�"���M����$u���K���q.`�_�%��yO���&��%�l�(�A	�17�����t���lp9��hb�/8Xn����[���zlZ'{�h�[�����w��|<�]T=�����rM|�J'e"8J����;��O����yq���f�E�|���t�N���6���.�����prE��������?���aA4o�>����)w��!���D���CK��"�E.����(�;�����&;D+���*B��w���_�
F�~��B>�k�RQ���c8������^������T?�OKp�����^�*U*v7����*�K�_��Q<�����5�L+�~b�B9R������x�xm�bfxm���$�t!J��������|.��+�3VpLf�!U@i���3���"��_���0��(����u��I�d>QE3��M*���UE��ZKO�?6�R��	��(����^PP�����4")�1���:��0n�����LR��
�.�#�K����c��I�)��2��a�1�^����8�iHn���:�:\�f�m����jG)%�i%���`e:��C�n$�N���z������y��Q��L�NX�W��{���*TS9Q']T��h\�A�����o#f�Q���3��,!�)�$�0��6����+{?�2���t�g#��D�|x�?����f�<���1F�X���C�R�g��K���Z���e�Bg��R���u�V�;L�m�������\����
>u��&j$a����:��?�)e��xl���c��j-��j�b��s<;^4��:n��Z�.=��D��$��w�����c�9�prRbz��PKD`��(��v�������\/�aH��3'�F�~0^����i^||�<5kC�����h��1��6���b��ATR�Hj��!&Q�p�d����<�Vp8��4,���9�4��9y��1�g��Sm�I�c�6�����5�0Kcc~�.��(��/1����Z�&�~�G��������e;_���eR�v�����\?�{������1�D���z6��g�v�"��4��5���1E�Ts���"P�{R�D���������1�����.lA��+����O�Q����)jB�W�p�]A�b��"��F�O�b�`~�"����������]�8����A�r���M��7����X_o�KHh�*��iV9����K��B8+��@9����~�'�u���I��f���"��=��g�|�lH�Q�Y�1��L���O��S'��e0��C5��R��_��d#4�,��	�W�S���%�B���hV��X�>��7����-9Fkc�|i����Z�E�F4�1�h\�<=�>@�������bpH�G_���Ll��K���_q��_�%���Ed.���Q����92g��u�F�,��=����6���.�5>7���z�j��� ���q^#-:��� 2BQ#@	�=��
����
��Jc'z���T�)�)*@�q�i��v��1[��!�QhC���:h��(���xJ�UO��h�v�C-~��@�[���(+�n�sr[��T�rJ�I^x
�����X*B�����'^�|�C�
&$O�g�s	z��A�q�1i&�h6~��(�89���b��_	�d���K���1k'�FI�le��������P���^������)*���#w���/��p���}e:�#�	�b| �@�{��Fy�{��KS���@*���Tg6�����Z�����
���N0?��a�*@b>�s��q��}]���:���U�~6B oErT�aZ�����3�L��<4��������6s�e:�&����jL)3h:a!��3���m��;��G>%��R�����^����J�"�����&�"g�:���J�Z��&m*��1eK��{1�a����J�f��l�o_I%KO��!E�&p��t�k�]8��v�4A
}�
M�D(tE������P�������kj����#�C�6t����\e�H����$��6.��`��Yx.��N��X��2;�5)
�������k�������{9�O�-�����Ig#&���d�vo�n��e(�?��i�+S{����=
K0�y��"���~�����F�H��c�ozKI/U���q�A_o�	qm�Y��5��[��v&�&�|�`�P�p$��-�%�oR����)��l�����d���%����<|�n3�%��@���q��b�f�l�u���#g��.b�'��~Z2������?d����l2�`AC��2�xi�S��:�d���g��rk �\l�H�yQT�Y:
��*W�S-G��GB��QQe�n�/���B���z�T�Q{D�(����`��C�g�`�SFS�i��l�,�Eh�c��r��H�.T�=R�"��8aE�p���5�,L�b_8���pIjT����7��]9\�KP[{�_���p3��R����ZK�	o�E]V���8��|#��o�������y*[�.����a��e�������})H"�9��{{������P�[�X���J����=�Tu��
��9�5�c�����F�55<�T���.�����t�
���E�F�QP�0r(@���������O���A��,&����C� ���r��Dg�a��H�?�IW�g�9��,�4�&�(0!d{1BZ����Y�bI����NIJ(����DW�P�i�#�
������c��fR�G)Z���u Ez
����?��j��l�q������)FR\�������������@�+�7v��p?;��D��"��z3�*^O�j9P�k�����8�0E{	.�y�+�Y�#Q�������W���\
��5qPm��[�bu�~#�s��`�>��?b�y���;�V8��	��#Q��*+�X��B���0���S+����gwmoS5/:{;������f��6)q�t�+��i
���5�9���K�h7�l����Q��gOjk�����E���������U#H�X<0�d�D�g�-�Q��q�xW���B��Q�\�E��"yOt�F�!l��C�>� 6Fm�Rf-
�l��7a��g�`E�L��z%�������I
ZyDHFF'KrP���w�/q=�K�}��wi�N�c��Uy�&���L
�g��V��2��v���Z����6,i5w���(�6K1���&~X�e���
OJ�\h8��#� "y�}`k�;%������C��h~�$�O��#A�1�`d��n|�2��#U�f�{��T'���#�n5�~�f�HM�_�dck�a�>9L��7G���&L'3��m�Y"V��F&'�[�}�����_�3�3��C��w�)I�D�z�C�*W������x���M�(�,�%���.4��{�m�:��T��l����0Yl`���<���)(���rs]���Ur��c�u�3
ob�*������]4��q.��Z`��;��|C��Z�� �EJx�#H��%������i�=��A��6"�������J���M�\�4yRT�����m
}p�e�Q�d4�G8]�����f�E�g�KA�yFQ�
s�=&0�������-"����:y&~������v��z��;Ys����G�V��
W
s�u��J�V���*�����O�?2���������,���������j�z��B>"Q�g7ul����3��@���q�3�/����Z���<� 7]u1���#t6����6�����w����s74�H����9m��1�����]��Q�Fn<��s#��\�����7;rG�`�`��I�.�J%%�'M�Aj�����9&o���N����u�FGk�R��fb�Z��Y�0��KW��2=@+��r������26�e�T���6���&�?6NJw�C]������4%��C�BoTq�]����M�^w����\�n��&1�lh�~���������C��)������������X0���0]y������E?�
����Cg����M*��h����f���������,�������6����	������;s�@��MI�e���hO_�h�eo�1mc�~YO��<m�>Z�(����a�6���*��t�q%��H��.Q!��q���5)K#h�F]6OSt�F�p[�Oi�C��'��!�(w�
k�����K����(X�U*�5�=U-�@�+�k���y��'nD'����$�-e�N%��@�B����^�6=�Ou���#���GN�@����F��&���z��|�u$�A�G�������:�������������'���O2G��d� HWK��I	�J6u��ixgB���O�������my�9����9���v���x0�D�������a�;���=%.fi�W*�&�����n���I���������F�7���G�v1<	�:��a���1<�
���`m����v(H��%�:�-���@������G��N����O���4�{@���x�A��&b����[����<�%���b����G����FK���}	�)��,�-�M1�7�Q�QL	O��Z�]�]8T���j����W|���K����V�f]������N�	$v��X��Jb7k��`�q�k������5R����%�Y��"�sL�O]�1�����Y�a$$6$r���n�"+�$
��A�y�PV�F��(a|f��|��#.,8�O����'���9i]"���,&(O`XbXq��BN��z}4�V*��Z�7���$a�kKa�7HV�;�H"!";a����A%���m��$H1��%�~��c�1R��,�c��N�<���-.��z�/X�Y��V=����ZwS��kA�vX=������ �������(B,);������D��|7%��An�o��H
|�!<�yA�)���������B��q��R"�Q�0j���`�ruu��[a0� ��w)�Xf�hv��n����9�k������x-Z����Y��#�j�G�V���c��I������ �T��!�
m�:�S_�N������4�������F��i��s��"�OeDaeO��r\]��7�:&�e�����C�.�F
����]���������g;'4����k������wU;*m����)��%�fz��r7 �I�kgNma
��S;~��t��$�z��:����"X�2t>"F�i��p���E�)��������{�O��@�1���%\  ����`rOI�H0(��1mJN�����: ���a�th)chW1��{ FC�KEC&$�Y/�0���q��#��&��u����+������-�D5��r��\O��T�I7fge-

�,����G�"��.
{t��|���S~��C� sv��P�!i����������6
��t}�)MM����Q����#�S�&��G>Wf�y�Pf�����f��AH/�����\�#��1Y�)��7P�9��{���`=�8��i5
�MK�i2�gb*���0bo������{A�Y|����=�$�TlT�=�H�x�����9_��~fbR��*^>��$����f.)�s<7��r�J��c�;6��Af��2�"��f�(:���20�a�~��Cl����
����S�'�X���%f��`a-��6��w�����
�)�J0dp�k���$-<���e:fAD��1�F�����m�z���a�J�o��>��y��[��������-��7����a&e���cJ*��4�B�N��<]�rD���lB-�n�j�j�M����}L�jj�X�����S)�-Gf�FZ���i�e)����v���tO�k���y�v���L��ig��|&��:[�s<��7��)������d��
N���.�cRi���Q
�p�Lw��R��QE��c��g����|)�/��P�*]����%`�������iCY��><��4@4�Su/�V����
W���bML�i��h����c�3��e�e�7��C�����f����x���Q�$�&��y7c"���3�Dg1�q��<���Y�B���I0Z��E�hdL w�(
�Q��"�ITn9�H�mP���$��#s�
������#1�	��C��P��R����M�W%�"2:�~���4�b��wX?�G�����$�c=:jX��?|S���7�@�3F�0��sU�k(@W
}Q1H����jk8�y	6p|�_y"�:�>�! ?���C%���0�D�/3si���?;J�fvM�\�N�����s	Q��X�>��)�����^j����e��8�x/�A��ue0�z|%��R3�����1���6�@��.����/�jw��"��)]��p�w�A}!Z5��9�A��:�Y��b�~�.g�[�������&��C��)_����
+�]g�[�T�����3����03�f��:�+��RV�C����|/����������
��]�
����R9������Ei�O]����C�'CQ���3D8�����]
��rj��p�i���!qaZ[tI?G^�2YUH�S>��t�`6������'����yH�+����l�c='��f*6���P�3��p��Y�g�-��1�\�Q@��\E�����"0�^�[r�J���fa4���9g-�Y����Z���u��xY���]�g�v�\�����b��d�)��`��_����2�B7/�,�pN���@��
��i���f`f��b/�S�K�&�~Tv!ar�g��2������<4�K'�	U���PuK�o�25AyK�)�}��Z`��L�v�)��v�[�H�,�x�p��J�mW���pj���/c!��]R��eN^r�w���y��=�9�R�RB���2Y�*Ks�)$���Q+����I�v����f����^%xD�����Q�G1���6g|�SU����w������8����C�)Z����#*v1u�<�5`������u�eF�gZU�B��X[������My?��.���*���]^5q���w����)��r�������WITyo��-q|������i��{]
�C<�_�T&��%��|�L=9����s1t'q��������8.�Ohi4�_�����Ve��y��$�h�)]��!iN\C2��#�*�����j����c�TV�W�GZ�"���<=��P�h�R�g#���.1�)��I��.N r�Y�XJRq�X[J]����s����]2"�}������]n�����'d+�����T�7DX�9Y9��3i;	�Wq�0�#�/�Hb�Xs-d�c|��L��8��������q4ah��K���n"�W�q3U\|�Lg)�C8����\�����QA�<�<�'���4	�s
�t���fv�n�d�p��e7*2��r�z�pnu��&��.=�{q�yd8]N���%�#v4�4��0�{����z�N�������_�������-f4MRK}�R�-��r����\�e#f������.�
�����EQM��?��>s�q�IL���-��|�s Q����
���W�p!f�4����1e��U����"���\8>~RC�s ��!7�*ZnV�a�����k��z��S�^�t1.+�nh���=���+�>�>�E|+�U�m��
���r�H�N|�wEJ/���^�n�p���PvC6�9JF���i��6R���3T���u�F/nx,z�����p����W'����(�������:����K�hYf.�o��rq�F�
.���c
	���&G%�%��fP~���J���K�V�ak?��H�<8��6����$��l��-�9n)��"����#��E��dY������x�2#i�_��0��.R�H��}�u>�>����*��d����IG������e��VVs�Obv�d>�>����e���`I��E������������������(�b���	V��������i���Pxr]9��t��m$IuO���G��W,w��O�3���	9T�"�8���Y�����C�n!#���[���H?����&�tn����x��MtL��)�rc��i�W�W��^�FhS���XGs��M���_���1���dz�{NK/"�m+z��GZ��S������M�+���[�"��W��8���K87�Q$0�K%�x;Rx��"d�Y�=��;Ve������-�piKBiy�G��n�K[Q�W��N#����Z���m�QE[�se�:���������J���t�����tk|��p�S|~q�[�%����n<�	P���o�q84]�M��&�p����}����e�;$<;gE��b�u����s��'7�������u���G��
cr��N��z��z��\x�dT�^����w+�� �O�'���!�a�3-U?��9N��R�1��w�aoxz��D�st�s����C�(xh(Sl�rg�N��
xp��%�X�
-m7���@���������c�m�V��4}�eEf���ZB�~�I;O�+�@}�az�,���G�L����9[�P�:`Vb��F���C$�q��k^r+n��C�~��#��7_!3�
8���#3bh����J��N�Lz���t����fTc������u�z*�0�8d��;.����
��m��;-^���ws�d�]�hU�m%i�7om�}58
:K^����[6{$f7}��@�qH{mT����eW��1D�����p�����k�FH���[�S����{��\�i�cl�]o�=u�C[�fP��79=0
�$�����1K�A);k�����i�.�g8�_h>�3��d��8��5��,{!Z��2OSH-��L8�LD�9b����2zG�]�Q��<���?Gwb#N�qD�*m���-(~V;�P���=�T�4vJi���-��G��4*��������xJ��\oS�V��f\_|�
$�4��?z�C��U?e�����>��.��x�!�f��<�T����� �z��a��_V*{��j��V��%�mV�d�[V�B��{�Z�QE�a{a\��z2��+���<�<3yuQ��W�-5�N�N��[%���(�M�������>,	W<~
B>����F�f/�e���?888�na��h0��9�9�9)�dC����������#3#�[��j����Z3�� w4u�l���6&�����p�Iw
���KNM���AQ������N	v.9!uoB(����F����D|�iQs��]kr�������w���;�q3d���0u=q*yES&���<�zF'��otM��%T�g���A�)�c\�B��2V���EXb�*���,�G�~0,�8�h�S�;m��k��b��W�\t�2�����N���u~h�ymiY�nCH����=�	N�_W�)@X��0���hi�������j�hi+�qq2��j�EEDQ�����L���2	t�*�Y���c,��jW�����r2'c}~PP����r�Z�������N�~o�N��pF����p9{����^��b#"�����I,���	��p>�<���+�;��i��w�����V��r�"c���R��}%�`���NF��}��<�s�hLK���KI�is��k�_+���keb��+��7c�"�����`����1[tR"���������s;:�H�������B���p9��v	�S�Y��7Xx6v������p��N:~9.��e�L�Fs�:N��SuPZ0���u����[���z�}@+"��r�a+C�7�����s#�o�����C�.� �dY��*Hoh��S�0L��z�ut0�F}�3z��m��Q����
Nv=bPot�Xbl���.2��1@�(R����l��2���w���
�z�[������4wh�)�����g"�p?O������D�f2�P@������z5�J�lUaH��������V[c����LH�3���l�>�����4�b���E�JD�������N��,u��}��?6�1"�Z����w������#p�{��KZ����6!N�|�#����q������L����k�	yL	��9�C�GL1�~>������+c����Q���0��hQ|	�1E���s�uF����8<����ix�pgf�v8�T��`�V���{I
�C�Y����(3�k�C�h�k{�<����nt����������I���������	���Iy�[.B`��{Xo����oZ��)����5Z
����
n���l�?��q�u�����`M��������cv�Cy�k��.[���?���0ED�|�W4�9�_��lfg`H�������m^��b/��	]��_�:�2>y�].��n�j���������(�b�$���3Kj|��>�Is�H�g��F��L�_�����h��[�����>h���`G��|����M^�N-��'=�G��qe��9�k��_�L��B���F����e��(_�g[�K��?qH�E�"��E�q�=���d>����D����
=k�T�����h�����fe�.�B
]����G���6y��%�V�������HDE���;~��F��]��66��_�N*)&j����j�zP~e���
����������Q�B����p�����6�t(*����[s��?��������v���{�����w������� @Wz+��_���{�2����y�
�j��v�?����������U���^�>���������:�Y;�����V_�������P��E���.\F�{������A^�Jo�j�������eI�w�E�A!�������wT�
��F{��+�����q��;U=x^��U��bh��d��3���M���	�����^w0��E�`��RJ�H���q��q���q���_�g��6��J�\��d��6��"�HV�Hp*��������a:��d�a���B�*�U�=�Ozsm4���N�"�B����(������\�z3T���(e��{P��L%jb�&�o��%�������NN�I���y���a��E���jQ��g0=�����U:^.n�y�xP���t4�{�d�An�|H�����Jmo�b1��lO��-}	e�|�j*�^,�� �M:�����n�
}r�����9)��kX�p����A]�	�o����������[����/��h"&����[�g�/`�o��0���\�jr�k�u#%c���_����z�����!F��n=��{��AJ~�ujt�:�Q��(O����<����J�����O�gm��>{Y�b��L�:�S����K������c)�a�G����� 7{�N��p������<}��0��p(��q��_Y1�;r�����$�*S��iD�[?�]���"��^����'��I���IB�~FU�wVVt�c�CIA���{����\�����c��������=rC����Xv����{����%�.O���J4AY�c~A�@n�����=�>����sn��<���+>���~���#���u�BtiT���<T���n����X}�q)(�������I�'R~5���K�:���L��A�@������QQ�e+��J�j�_~
b�"�,��nJ���<����<��cG}*�����W�;��`w��T��juX��l?��cue��X9�Pl�~b[�|j��.���I��gdk\QDs����0���oK4if����%Z�TY,u��J�����A���[��������\��3�8�4��^m���%>x<�M��PK=����G��<�7�n"RB4��9�s��F�9�wQ�Rpo��9�l }�0J=�x��=aDcT;AX/��J~���U���;��Je4��j��s��*�u%�#���G�w����nK8)9X=F�R�v9Y��������w���.�
�����i6%�&�I��+a�<������C�_|K`�$���r�k�M����0��c��0�-5��
&�~0�S��[��I����'����z�:ywpp��Q�M���6���=����8�%}k~���{��k���8&�v�����|<�[��>���O�J��������{=4g��1�+��i�f����PR����*��1�����C2�}��O�i��6�p6!{��8���aH��<����N{��-��Y��1w$����>���g��\rW��<�m2^�z�����������_�6��=�����{�^���M�9��K`���8���[)~u����h��;	��+���T�`�����q��
����$�L��l�H���4�Cw�R����Ip�![T�<AO���y�����5����y��h�5Cv�*C0'�h2,��ty���92��2����F8M��b~1��!r�DJO����	It7&C+�X�f��qX��3�'O�x������k|"E��[ ��F�E���N'��1���kG��?0��78�M���F��p����)>�{H�l��Y�-���BL���`�y������+���wU�Q���*��o�j�����l��"8����E����]�����YA���qV��@"����h��k��8�H��!��/�d>$��Vp�U-g����WQ�2������VC�q3{v�\��� GP>�~w\���#������,y�t�����h�O��T�M
�x��V�E�(��P��hs�K���YMt��t8	����H
�io>#I`��P�\C�7�N���q�����o�H5����4�2@�����z�Y��X1g3"�s����$FY��Wo��]�����5�/�]�3��|����m��C$�
7��*st��Awa?O����o���	����60��H�>��Z��%�,>�y��@](����l����fh�E�<~ZRo�9�W���S"ah�@�`����r����,�T�������,����}�|�_`��9����z������������z���;�w'�w�J�NU/me��bu�a��%��	{�Z�!;���O�O'�Ez�/3���C)f���6�b�8��a(8�L����1�1.����������3kS�^"B�|�w����j��5�C���yd�C[�
��
�+)�~��}�w��Ll^�(!�W1�W�n�}�lA�
pV)�^�&�*���\bq�+)�4��!G�(Z��~|=E�-�6�<:S�|}_�����G���Pg�6m���SzZ�S�'wb��@���u��tw]x���x(�<����LRLi��^�n�����e�����W�����������b�{�`�������Ld����6��A)�$����%D�{��4��A�1�-Zn{����$m��6�"LH.����l�������#t�f[1����ED6��Z9�628�a?�3���|���Z��5&w������b�J��V���+D�7���7K|�JIf������������]*����C*>�?2Khe~�rX�4�y����|��U�O��>@M����D����_�j�9+,�L.�;�;+���(���x����'���n�q�}wu�<sF=�@�i�+�ao�p>�$�����$�������
��|����_@��0j�����
C�$�g0���!��S��H�a�e���2'pU��X�@x2���������|��Mx0�1�]��EJi�#��c���lJMtOG�B���>�LD]���U��r:W��9���4�����{{~�%���^���j��k$iU�]R�h��:!B��M���D�!�N�����B�ke�FQ�\����s���&�gU��Ri�����?����ZE��f[��D�n�H[����%��|��J�qDa�ZQ���`}���}#�
,=���=���T��7si@�[���O"jf���P�OM����Ho9��$� ����F�-O'A������#RI�\���&����R	��������w�(�9@\�	���f�2�f��R5w}���])x55���[�}S�8B�D��dQ��'���b���=^3�m��N@��>[�F� u���O��s�#�)z����aj2������uba@��t���#�Q���D���	�h����#Z "iO���X���
�7�Y$J@bOckD�'�FN
|����`��"�1��\�I�*Z�^�w������i��W�y�:�=����)pp$��>����qU���
�����
���������rxx�e�/���_��C�\����3�/������-�pMO��������S�6W���y������������Tk����
9��
������'0k��e�^m���95|'c�1j
�e��/r��F42��G�3�V&������������$�_��z������G�^+������O@�S��Z���������:���VxT�������
�J��y����
�1m�����j�������������:_ "ji�=vCa`�h���bdP����M&�
d���:��b���aFN�y�Y�f��'��V������������0+�=u9�����\
�1:�?��$��b�P��X��N�0#d��)k�8/��E}b����R}[�wv�1���Ik��n1�/��uA��]e���}B�����w�!��y^>3/
��<��h���A&�I�C����6���b/�,g�gL���U���a�$&�<{���^m�o�����bb��P�0��v�y���j
F�E	����gz�H�� ����������*
}F*b�������M� �_�����q�Z��R~�z�%�YOi���9���a�Q�J���r��h8��>��������vl�z����B1�uye�R���n��Ku���x�/�a�D���P�N���~��Gb7�1�;��������o1_r<Pga��	�(�_2fs�<-��( �L�D(���h�����W-"�i�~P:�~��q��at8D����]CC���+v�.��W����<��r��� �����
�W�GX�����y�pTF>^f�#�������q�I�����p]���Q��@�l������G�iwi���t��,�(�j}���Z2<"��j)��D?>+�8f���C���l���������[�V���V��������JqNW��2m8�b��p�d�o���])���������3��"����
iM��-Hd@Z�~�����1��'e�s�9��"og���_���t����������tI~C��?���i�m����sv}�(]%��{q|�h�_]��C�g�#� N`�������]���9��MSKI�a�\(&a�D�*��<�5t ��p�����i�x�q�\����f,���G����P��:CP���c V�X�K�&�D���\�6��.No�����
���	2�jI:L���D���Ge
�k����y\��W�|q��/�^���/{q���\����y1=��z+^|����?n��"��@����S�d��]D��N���g~����k^�Gt3����L�x�N�t��?���#�U�W�)��5���c��W)5�f����w�R��z���s��
���}���M���|��� ����];T��X�|�hvO�M��-s>�7��])�Zt�`2�:������:�����(~�h5���������' }��T��\�8���?Urh�<N���e����!-����e�1YNb�k��X�v�29���Z��X�[e��~�d���la� �%e\&:�Z�SX�kD6vFm�F�yI��'Td�-���$Y��a��/~�	/~���3�m����?���%L!��y����TV�A8~�7��R�.m��?�����G��H����*#�=`�R
�!K�c��^���cf)e~��������m�r�������O�����	2�yW���_^�V{����|_G�U�������{���*��w�?C���v�lh�����n6i�yL�4a�u��	&�@�&-B�
�In<d �{Lg{�;q=�gq�2j5��!��H�*���jW�L"���-F����9
���!�
�R����:���5�q�#:9����H'{�R
��<(��IR�����R��X�5���R���AX�srJ'�-�&#�EJ
�R�4�����!�*:��Q���	���|(f�	�{8v�����	��*-�=������t��Tb,xJ��[�����4Qx��Q@>,|����
�e?��R�[7��O��a��n=�����0�%�0Uh(/OS$9]�kjk�:pR]��
��O}�������g�v��I���L�[�M��b�=���5�g�:��)�������;�W���H(&?E{�a�]`�}���5��?w��&�t��#�b
%B�K�!b��������(W������/�R�N9��$�t#P0'�Q����m8�U�4a���p��D����^i�0\�C+_Pf�a2��A��,b$�Vv������*��3	q�v��\��BY���1O:���iu��
�l�����8~�#IG��t���M8�{'�D�q�<�/j�X�$�
�'5��z�������������(MJ<��7n��f7~���u�����V��!<�\�)�w�J��8�j���v�q�*�|~|����]lD�����P[,���9�/I�����]�T���
*������:P��������7i&;�m
�T:�pE��/WME5QSt�I��.��3l����x����.����,�_:�*���L�s����5���{V�k�T4t��G��������	���}��8�����=6�Z_�p�o�����.^7�H���Az�M���� |��&i_`��������D���^���R�^;��j��<��Up�"���
��!������
IB0J��^�M�u�3���G^���H���Zm�;����cY��5	.�����������1��]�l����D=��Zig����7�6�@H�i�ww)�z��J ��?�8��XZLJ��@���[UQ�80W���;bf��/'�g��P��L��Y����E	��)-tl<D���7�G�L����o(�h��sS��hb��p,��*|N�>n~��G��zs<��1�]��`H�7�w$�ai�*�q�H,Q%���}ph��R'Y+��Q|%��J{�A���$�=y���]E:��B�[DW��!���������L��"�>f%}�����_,i��m���72����/�(�lk���p�>�T�������~J�9�����}S:�����
`
���C���H����m�����oI�L!��T�%��������(�K���}	�)t���%;�u2�E�{Y��Amw��w�]EH��0����%'��d2�d:Y	V���%�"�&�Dx���
��?�L��f��7�����x|���h�~���R1��Y��w�k����6�4���	����W5Ah�����^�'f�Nx�v�v�+�*6��S���6��BJ�x.9�,J�D�S������s�v^���N�6�Dmj.�q��[�_�B ���:MK������3�g��I�;�jJ�������
�yoM���mo&��/����nK�LhE��d����5�{����FxU���Tv�k����a}����a��9������!_����X+m�y���M�N�d��4�s �QCI��h9cgx��;WN
\��G���������vx�GY�J�����=So��s|AmF��o�={��t���!�����eI��Io2�^O�=��5A��y�'�h� ���O.���/N�]����Wr8����������?ESs.!����:��;&��ME�~ZJ�_�� �5�}}Oj��
�(���$e9����E���iNM��FB���5~1�
��{����J��0�����l�fE��q)L�k��E�?3�J��nD��U.W�X��@a=��kUK�iZ'(RO/��}�e���j�*��m���>���R����W	e"�'���������b�S(��kjH�����i�W�F��0��h(�=;���]�;�g����=vG�VB���v/%��~����v������>C�5[�Q��,�fN3�{��&+����N"�D�����pX�d�e�S���g��;��?�����:����vu7���^���'�������m����h�W=��ey�:����������`��?�������R�W K�d��,I�4 !������!~M�1AIX/�Z7�F�d<4
���M0��1jqtE�&�H
�I������TP��}t)^NK��i�
���da=�X�j�~�S����=�z�8�i�����*�c
�!�&���#S����r���S���$C*���{��)z�f=�����R�z��/]�bo�>�)>����
��l�R��Jj�%�q!~���N���i��I�+xb[����/����Z�j?�1�(�]���V�]����:������t1�����Z5��u�����^~�5��)��2?���eJm������	��k����W�T�p�/���&��Tl�?4q��$��{b����N��l~�����e5�j�~u�[��Tz�$������u��L��S�,i�e�������������Uk�g��o��oE����[
.����vD�v�p�d�R�0�����2���g��d4����CX����NmX��X��������6����q���5\�������JH,���5��{2��7a�>��F7�������y��.��4
�,����p�>�w��������R�q����=���
wj��(SI�Nk��$�����%\��/O��5���5�|��6����O;���?�h^l�[�6�-�4����	�9�����6y*����q��@j�����EY�G��j7���x�J�_�G�9x�z��?���rry���
��)�����V/^�(�������`�fv�1�a�]�o�y���Ia��+�Q\��.��C.r�����-U ��KW���xa(BE��f"���t��=t����"?H�"C�I��^P�v��������?��U��1�h�S�\
+���U"�0����<}@�$����x8�;���#4���z��"��0��
8����~�}v�S����r�!Y�
�2c�R�����Q�+��|Mu^��=���TV(�����E�7��m��p�w��"\����p�M��H����$�h���Gx�<�=v��19�T����Z��C�bE���Q�2��{��.1�5���Q|z�����e+�pv��	*PVu:�~��"�w�Is��`��1�
k���n�)[cE��v���"����z���0��c�0�[u���<?n�����	?�&� ������[F10a1��E��4/��VG5/:�\g�>+�����5�*�N�-X���i���<�g�5L	�!��WLs����f�|�P'E0l��������8k�t��k7l2����d��6�����]�+�h��VG�t���.��e&2�D�?i]����;��f'��@!OY^^RX�!I��T`'.��	�Q�2h�f�1�����,���n�T��2Cf�>l��.�c���H�R
g��1
��]���>e���x���I[�@�����1���V���)�����I'2&8T�-��b�8�����B#/��~
�W=wj�����s���U� �4/��i�������5D�d�=A
���C����0�iR�~���6��J ���=>*XI���\v$����P,>�u��Ay{��9�u|����igN���"������
NJ���fl"��-���0�
L�~���pr.��%�;��AI!���#����T�}!�6�y+kp�y0����UR[���VAh�ty{{����,����)��EIe��[�^4���wO�P�u0��1:
�4RF��2od�"����jAb���P1q�?���Av*m<���H��R�^�>|n�+�G��y��O��T�|����K���^:%������k
�{�����^-8x�����#�����Y'��s����������}I	�?2��2�����o���o/�=�}a��aE����c��c��k��|�C=8�������S���]�����?��-�o��A���0�HE��xF9k8(������~����qO�����/��&�l��e�M���q�$����X��9��� �l��@�E�]��MF����a��j�t6��m�%f����w�5����/��;~�P'Gk�����hJ�/E���G8_�=���#,��2���7�c�8)N�����/1�O�	�=��kr��Zo{w'�}>����tl�
.��K�%�G%Mm~�G�Z��;��5���N�M���C|;����U}��z�Fn���^����f�X��-�����=���8�?�%����^�z�A�[��o�'Nj��~�~$�o�������,��.*���5�?�
,�I��\;������-� K#�D�D������z6���r�(;Wo��s���6��yG��k���ls�$����B��Gp��^m��=�R��=�9���^/�6F���NPL��t�h���G�]b��g��HN��@��C	����P�``io�__��%�u���j5��[M�K�M��x�(����Pz���F�%��H�y����}-�&���F1Udp�9n�m�d�p���HS�d�ML!�����f8����,��W�4x:�:��}&����b�4�EZs���:b6�S Hs�N@/�	��=^�����;se��$�������9�9���;���	n�����	����"��!M�d.�U����� ql�c1g�����H�*{?�hg�6
��O�8'L)q�@���0�V'G�:����<�.���+T� 2���d�E��R&��x�O��%����e�4�Q���T(�f�)�3���M��zUNC!>�87�z��@�u����=���-~a�X���2>����S����S��yx�0A]l��W����%�+D���$;J�����2?���m��m�����U��p�>��}�B�-�3�4������a�-��C��[PEU;����;�8�����qc�{�OM�����#�:Y�b,vo*0��2l8���%��`�?��J�y�}����%��a83�'&� =B��$����V4�N��L���&�_*r�A�../���������;,/[w!��������y#Z 
�+�$��W��d�O�p�bEh����a����ik�(B}]������'�"�c&����
\���&�]lgk�Q�c)q�w����?��7����
�P@Q��������s=���`-,�N���N5X����R�����YP�~��������8L��P��h/D�_c����-L��&��D�&FS�0���2�7�c2X��A�l�Y�3�R�b�[n\,.6I�(c�r�^�RY����,_/�R'��2�~�/�[o��v�M�������=g��s"��C�D�Y���+���W#���*>�Qq��
j8'���H�0aY���2\�����QIj��R�.<�jh��rS���t2	y��J)X	F?��lT)n�b4d�|�
�x���L�azf2�����#��<�H��*KT��C�h���3��j�j��m9w1���r�_W�_N`�XTO�MR���~�Z��
��c�3;��e���Zi��E�<�����u��3���j�
�i�ma��,��F�B�)���6�&��pJ-��9�-N|$B�{�^^4t��"����;�3�������F�
D���%{��Mp��=��@f()U@
!���u���B��lb-'&c1[O��O��T����������p�$�E-l/��m��`�J3htG��\����N��P��tD{��CN����k\�|�{�/���8��@�jc?�ao�������Q���@D�[�{Qk�9W�~�����weKsR�	��RK��y�����	�@�&+�Z+�$pd����pG�`M�1���^�1pf����j�y����T~K�W ~@�Q�v���Xx��<�_���M6qG�1y�0	i�����7��?�ba���~@��c���7���`��")�����w������|����H�������y�3b�&ax~z~�iU�����:������� ���.;] ��V���N����ht��j�OfB��I�`��d2l�8�
���
V���e��Q���V�q�^��Vw�(9mZ��F��-E1 �"G�����a32����#;h|w��p�Ye^{�H��z	��b�����jN?`�S)��[��wt���0�;{	��4� |����$<f����M�����B��h<�32^`�H`(I]N��f�"o�Fd�zUi�0��a��������P�_0U�Y��>�+��C����v�T����d����+�����B]���=������H���������	K��~h��Ba�LJ~2��#C�B?y� (�	M>��bj��q�(�%���W��>��+0�5�J����^o���3��_��+M;v_��n
����e��[E	_��^�j��Z�
/�m��G�X|���c0�la���=>H���� (�{��Iv��j�rcz��;,�-�
�!F���jJ�G�?=��T��m���O�����?���>8�
��j����h���~m����;��c�����A�������U	�I�%{V[�T�,76:��P5p��+�[���0P$z��&-�J:�����wG�
�U����$�wj��
�~����0UV��_�^��������7��T*��`��S=�&<x����y��!vpB�}A�L���Y,�w�'�d	���L��t<_8uZ�{�Yx?��u�fp�{8��e�4X0]l6�Z����O��q�*��by��*����oe&��sjJ�|j����9�p���F���y�Ux��x�����q�H����y�����,������X�w\��z ����B��s�,���^�h����\�I�%=�j3!����Q�EK�e��.�������I��
><�<m������i�4����}��������$���4���`�h��,����6F����������/��-�W�vt������������i���u|��4Z]�7����&�^�%���Sf���������;T�$v��h��v�@v��D%���p�_O��������g�2 ���0��\�7tQ�h�����>�u8�����/�����7�A��y���u~|�>��E�T�v�G��]mF���5Cx��^��]7��������(�5�n���xk��q�o0q�&������C��&Dy�I��@��*�x�yH}
�:zvy����T�m�74���e��A�����N[B�FFS��
�6K*�����	x�X3.�-�n����>���
���%9.O���U�{w��h~��I[2quL9�D`�a��+�{|����t�ln��	�����&��c&��y�(��#�X�S�9q�aQ�(�d�dk�v\��`2���q}h=�r"����5��YGM����D�d>k����IE�����<��A4��D�(��8n��}�fT6G���l����W�e���<��a�0R��a3�S]��A�����_���#oy�y���fG�*N����!�
&��Q��(���$���=�*P���4�-��IT�\�H=��������y_���"�K�a����C�����p�f��
��XH�f�I{M��=E����1n&���5hd�#�33�j��oI�����a|#��$:?]5p3�c��e��y�|{��H��4��0��m�.7>.������cV_�4���i�_af��������<�	�x)\�����Em���B�W�a���������sE��������H��u��G;D��-����3�����{?V��+��a,������
���DH�oi�d����KB�����.�����-��b��:�~��r����Dd�9��0>]p�c��)����/���x��D����{��'y!���W�E��6j����]g���������4=�y�O�i&�5���������$�G������������^L�W���������C����>��k{�{�����h�6����A0:���p��s�5��g�K��oc��G$i����cIn��/����������tG��<��H:�a3�����.���!��B��������0�f�0JM((��W���(z2
����}yw����+~��G������gh���"���5�%��4I�A�A�4��gUuo/�W�,�R2�Ld����uk�����{����]��.��|g�����_G��W%M_~���tb�� �r�/�g��J�i>Y���U�-�UR���������Q�h���������6�##��G�7���r�Ih��������Y���^����U/�>\��&�g;���b�>�������[���\������6�r��8[��eJ�*�	�����v��i���e��q�_)? 9���[�d�h�(�����d��">��C��;N���uS���� T�W�=[*���B����Mt�_��9����Ez��Mt��/B�v�`�*��n���}��l�XJ�
����w����
vv�~��� 
�[tdFGo�j�Ji�z��nt����Z�a��\�L�4*�+H����J�,/Ekq�d��'l���S^�k^�C�q�0���+�y,�a�����������\�^/3h�Z}���J�+����j�GJC�+�tg�HR�������m��=_}�V�����6�:g�����N��)��z`�2�'�����7/�������L�w��������5/�7�E^���w�����&&=���:����-M{���V�3&��GS��F#A���I5�G�x�qG�{������������;�����(g]�]~�q�geG"�X������S�#�T��q^�
zs��u��*�l���n8U�?�>w�f��E�c���������P�!����;��������h��i�����mc���~�-/=1�Y7����%�w�Y0������f��j����K��U��`�����~=�?^�����Q3��(��^�y^�����]w�&A?��	=7�
��Ho��KmA����������%!���z���Ab`���Nu6i���36AIw��3	��#��b���*��b���ZG��]�J��<������Zd��d�S�����T}���eaCN���w�Y~��;OL����,�$Q�p�EIJ��,�~K?�(��*��2y��Zs;vYm��y��4����������Dx�
(,��W��$�� ��"��	�Z����l93���#�M���(�T�(�N�4-��0qkR����r�_W��Q���/������z��z��E�/E0��ACS����V6$�����{s��(CQ;K��v���&��$��������)��n����kap+����$W��-z;x-o�a���>Z����2��d=��T�n����	���-��5/�X$}�d����K��%�.��m"c��d|����+��l���g�qZ}i��y9���r�� /5����b�������Z�x��F����.~&
��*f!Z��f�M.NZ��2�Xd�B."�g��:a�S��@2U���>��B&�^��'s^���##���8���_�5�3wB���/-�^�;l�����0�i��Q��&Mr��wE>+x�Mw��ms���=���&�o����� ��z���h���Y>f_��G��&����0���3i�����2���0�A���z��ak)8~Q��S)��5?a����L��7�.�����*��9\��c��H2�0�w�zak���-�=�����>��y0�,������w"�J�����"�F����}��*yS���������a�����8�C�l�/1)�W��l�{V����$.5�&'�:�T�M�1��q��Q��|�!FsN�8�M5����a{zO��F��*�����u��W�����Tx?�����{;��� �$k�������s:�/�"`3��_�������^�I!���WL��4��\���z��gG�d����7cZ������G(;�_�����j�W�v���=��n���V���7�7�sCYq��Y����~c<���j����j��>�~r�x�h�Y���TT�a��l���I_u�^&���M�W���oH�L�������6�j��a�q;�4S�F�mN�Da�U{)������NZ��SW{-J�� �5�2�CMR�Q����P��,g�ttm����xbq�����]���gx[L9=_�p@>2j'F���z=-Jv�29t���&?��XN��]!�U�wS�fOj�V.c|�2���Ov//��T�w�R7����G����,������5��>�����F����7��e�����?��?c��L^����G����d���Ys�mu��b��{��;;+�<��m%��e��]��R�!����;_6l���������e�m\��D9��_�3�n�4#���y$�1M?f=�Ml���o��<d/�����V�U�<�O��k�V}W�k%��z��X�����F=Wir����O�T�Y_zI����-�N�*��&��&Z���q�q~]^r�IcD)��"�Y�]�����~�{�)�lN���|X����pD����gU��L�'����-9"w_����hi[U$lB��X:���;��9M=<o�["����T�W���yd�6��.}��.��������������JN�_f\wo�t�q�(���3gW������OX��-o9��4l�leV��"U�Q�~���Y�n�_r����B�G�yP�wvQ�:m�eez�m�R��[�[��M��(i�a#
�1�����������T�%���{u2B���*W�d�Y��t��W��(�C��W��C;k4=��u/��-�&��~�:��y��R���YHrm%���������>X��#&��6��Y:�5������S+Uq���JhIY<������~�]^��y��R\�`��]��B��&�|{�Q��G����7��\�v�� V��%��cV��W�^M��������6��f�����'��S =���v�C;���lmqn��;���Y�]1oqFz3���)��EQ���C���^
�p��6Rw&���?�JR�er�)�x�-�-��s�6���'����V{m)�=��3w{w��.���g�o;�rZ�$����;�����F������9�K^d�B���T&�Ay"'��,�����2_F}����'��.)�MG��I"M�������q���3#$z7���U��*�M!j+Il����Mo�����f8	�P��b�b���=/g�cS#�.R;�1}��
oe�
��I������|k8U���|P�x+��,o���blm�����m��$BOK;�j���&�c�}�`��)�}�������'g����j�[����H�US������<�����h�-�m�F��FnV�gREX4���e���.j����r���>��-d��F�x�wx�������Ijhm
��~�����h�����g�������m�Z��lm�hq�K�P=�u�*:�x��W��WwE����Jg�+��P���\�}5:��-�fW�����M��|���Bh����-����o���[�����,|S	z?i����Hi��v����U����b,g�y�*�w��vz�w����H���0]���5��b��bf�A%�����(�`�j}O��wvs��(-Wz���t���a�����\���_�7���J�����h2���I���p������$������2F��u��y������U�J9��u�4)P���$�p$bQ��b�RT��q�E��kS>�kvo�Jy�B��6j&h
�e�������2^�!O�/{� ,����Ej���S�k`�P������o�Y�D���J���K��k��^�����J���������~��K������(���(���Ms����$�h ����s������G
JU^���������-�4�{� ����A���ye��P�\E�u��H]	���H�K��s~���!>����.#$�=�9��c�1����/��^�{����?�?>x���&������[LT>a���V��KV�w��|�<� ����qA�LvvoH��j/S�jq�7��OmI6���|g2^�mWPdO[�������^�� ��s�~�
��i�z��=�9m��{mG?��4����O���
������?���[�J`���:���Q� 4�#P��<uZ
uD�\�Sl^���Da�eI���w�"I��~�r���zI��Jz�*����;>:<%e����Bm��|X�
2j�2��"�/w���]����Q��"z:�.���EX���X� ��8��I����u�xG�x=��������w�����?y!�(\���P��:lW~4������m��d�ib��d����/I?OqU�E#%��P�i!���v����jK2@�����
�������~���"�������)�o���v���;��m��?lb5��=�iVz:��=a
b���$���`u�p;��wx��t+p6�o.Z�N�����E
���y����p����D~����@����-6�5��+@������m��e���_U�p����]A����6dc�[s���|ub�����������d��W��C��F�A�H��^A#l�=�$��o���������u��6��[��H�l99��`;^P��5CX�����2��H�����,�n^|���a�I��qw���gB�w���`V�I<���q�����V��zb2
9�&����$m���xYo�(�m�"���K���:�[�^L'��[Z
�]��T37;?��R�f&�������d:���n_G���.�����i�����~���z����
��V8����'����D�#���1!���\���[U����gY�j���G��w�x������Zp;n���a���z�6�.>k�?��9��=S+q6q�����1����z:e�s�i�����d�:&&�\�B��������\���LnR!W��2[�Mt����5�\��,��j�^V�Z�f�����z$��H,�����+�L�&���f���?(�K�P;�G'i������%���;�P���%��������+�"c���w������R������o5���6�\a�<�G�r{[D�4�[R9�����/�%O2���~�����1���2�K<R��s����^�������������VD\�����8|@{�O/������8O�&n�_������lU�M/X��
��[��H��y��yo�&[���x�N���-FR�f[�����4������<q~��B��n/o�/��9rG��=�2	��A+d��*"'b�"�����������&������y�m�i���|n^�3���7ip����L�	����&�=;����Sm�������ML�u���o��yy��b�?j��p\[���t-E�,�������>����t-E�,�|�*�2i����s��)�m��(�y{:���|�+9����x�J����p�WV��E.�]>���m��':z����\m�EI�H�$�H$�C.z��Cl7��:����(^�?�=4uz��R_;s0��S�v,�A�3�T8\�U�pn~K���\���+I#�` �%�����sj�F��w9�j�8���9-Ykd<2�r��	m�bwn��U��T?��r�#���~�P�����G�	�t�s�����,�V���C��N��?�d>^y��3��B�x7�������L��������g�K�	41�u�#�*{/O���G��>;z���CSL�d��99=:�{�o�#�������N�N���d��'O�V'�G������4���u�~l0������F�����a����}��q�L�aY�S"����?1����8��V�������/8;��������������C&�s�wp��<y?�F�����`	!�����s��+�W����<��U�Hbx�E��R��(��:��4b:y�+-I>p��w�����wa������C���S��;��#}����q�n�����IB��a�|_r�����������W,��L���?���!<��#������0���C�Q��)��x����y��3"���H�m�9�^�$m����`,5'�F���,��p�8��R���9
H�~v�����g1=!���|�G]Gl��W/|p�8>z����g�,&xo�>z,��Z�6��7���D�����P����I�}�O�/�Qe����N�y.Ue��g1��f;������F��]^��������#�,�9%�S�h|���<w�jd��<����"�s�lA��H,%Y[y))�*;�g0���7���{1����~��*��rr�)E��i����=�Y�������gd���C�O�^,M�����9�p�~NV�Db�6�n>��`h���u�`CM;o���R{��|��I�C��s�������}���[&�2[H�-�`J��Y��g��-4�y����K����?��>:8<%"4��������;,�����i<���4>%s���8������J�(�X��c��ig�������474�O�W��;�����/�\�����������{G���<7�<���!�R,��'���m�A
>1������������A�y��/b����i%���f
��O���}�����!�?�>���N����<�F���?����������L ~���S��{v�
N5O����������k�����%�	�S�7����g1�������'}U��g\�(�=�"u�
�b}��G3���j@�O?�����X���"��X&Z��6�L���)}���|�6O?V��O�eJ����A�4h
Re*���a�,_)
�-�4��B��(Q���4kH��TW�3	��K4�N�c�����T�3��:S�����<7�S!���$����2 _�2�W�����T������D���D��g*��(H�%���ROP�(}f
R�����2 ���T|OAj�W��(��e��PAj?R������J����~��	~����Ex�WA�@?�|~��(H���D���L�J� u�(�G��� S�,t�=E�}���)�jH���j�3�����.�-3EN�\o#O�Z��<��T�HY�(V�/J���Re��L���S�$���8PP3��#�8��r��D9�q��3���D�W_!5Dn`��7������&���I���TSSMJ���)}�
�K�E�Rg���3MQ$	/_*H�E��'�WP��<Y����f���*����X�cy[�3����)DYR�[F�Fj�n���(�Qq�YE"��O�w�b��VIH��H��������H�U/�_������te@A�UL/�1R����(��*�!B�I6�}WV�)6��)HyXN *��ma��GbP��C^�*��:�3��#,~x��(zq�M]q^�g`��KBLoIqU�,I�N],~x��(zi��3U�CD����e.��^��g*H�)�7,'p�#����0��UD�����#q������E���-�y�!�w1������zKb���?PE?�dV�(��2L0�P��~*����������S�o��q?J0 R�2"�x@q���	���q�Y��(��mGx*I���(����
R���������	y|��
�W��)��)���e����I2
DWa����Dq�$�(@E�<�"�x*��(~�U� p1R��>"�@�bOB#5�q���(a�U����3
0_	�K< �>�FjR�1�bEQ$Z�MA��$�r��3U�}A(H�*�G�*�b�y����,~��b�
]�~�F��M��z��H�$���K0R����B/�WB_������!�0<�@QC�j���.Fj�����a���0T�W��������(���L%�E�e�WY�D�Z�X���U�$UE�	�L���0����E�A�������<#W?�9`�A�aRy�~F��f}�/�����O0���
�/
E1
y(��g�]Ea�])�	�`�����(VEB/,
D�Bo�D�&��.Q���D$�+�P�g�(�Q�a�.��gD&\���� ���EL�	DR���S��K����<��S!I�(�`qc��CE���=< :H�OM���`zG�CS+�}qa�&	�:d�<�>������q�(��)���=��R,'$��(1���D�?���,!�W\O��a�&���G�)��U��+	{B�X�H���$R\O�x@Q���L�W�8Q�����$��G�d�����g�`�N2E�H2EQ�e�x������n��P�)�����EH�/_�+�"��p�R�~�F��h�T\OS
�SRW�TbEQL��4V��i�{M0�HS��4��b`��+x@Y�Ij���dn��8IY�d��iFx���]e��(f��Y$-F���|<�0�=�S:�3M��bEQ���<�v��%�2�T��)c�.#��}f�~fXQ�\��\�"5��@�%`n�N�	�����|zn�E�P%!M�'�At:}�@��z��A�& ����(����I@�o���}x��L�g�Ui�-�H���@���p1���������C�	�*�[/�������X�  V=/r�	�����b��+b�SZ�#��`����w< ���y�3(~�;�+�����)H�{X_a��?	���$}#5-�-���	������tT�<��H���K�TY>R����S�9P��	��`��a���f�8�1�����xr��(�!bE�"��@��'��b�����zJ��m�b{����"�w��v��)ft�^�E���������%�z�X���krbe.\���za�a�cl��BEQ$`�E�0��HL���B,��v� ~a��<|�B@�(z��a�"E���gDb3�3����0RG�"~D1V	C���/H���Hqh" v=�"��g���ObE��=l" ���b�����'	��d n�x@!��fQ�m%�4��^���<'���S�zJ�#u�a�*�bB*/�
�#��	|�|����Dq=%
��H����PQ�p��y	!.�J��O:�x?��c�N�!�iEQ$M�-������S������)^�sx��PQ�P��4��0�b4!�#�g5S%H@\��TREQL�����M�{������^FB
�_�����!&�Y���0�d��~��XaL4��R�K@EQ���H�u1s%`��O�:GQ@Q��1Dj�������y|7��l�*�T�C��`�S&�}7���Dl��H@,��b��@6c ���l�����}6���b��$�.��x@1V�|�\���zJj����i*nV	���<@��8�
������y�����Fj?��H����G4�~��
>G���$�~���]O9�#�������Ef��J2�A��@l�bk)4u�'F� �����"�6��A���0��1|�I���PAjv*�S	=�(�]	�g�$�!����$.��lb�S���c�K@,���2�'��S�,_��|�j�e$����<��G>V�(P�m�$��91
�J��M|�V�F�@x@�"~D)V����(��l��v6+�C,�c���>�?��C�(P��b%�
]��8��`$�b�S�7�H��x*V��U�����l�DI;F@��GT���I�c�N"����XQ�ii�h�(�l�����4< ���O]#5;X�>S+�$�`����"����T���J<*\��(����������@bNf�g���-�Hx@��]O}:�������������,��lX���3����%����2����V��$ �����&`+�e��xb���
c����|o��(���`�-q2��%)/_��W�����D���p�������=9nVIf�Yy���	��3���|�6(��gc�rB�e�~%�]��&�����2b����$�g��k,������J2��(F���2b�4������w�Is�G�q(T�=)��`v=%eO�� ��o�)��I�J�$�!`��x��x^Z �:Rk1R�!�gL@�(��B�1f����� Lq|vf����
���#�<�(���MH���l���7a����4�b�����3J��D��P�8�
3�����>v=e�T��q������b�1�����A�j��2�z$.�o���z��(��I���k�1_I"���s�-#]#u�����b�d�_3H�d6A��XN`u����4HC�i�����c���4:��)i��Y%�P�������'���cY�Eb��'�	�(��G�|R1e��}�t\�S�@����' Nf������n����N�]OC7��>Ns�4�`y��G��(�lB�u���v�)��|y7��'\#dWO�p�
	��%��Wx*)��zv=
}��C_)�A@�(�����B_If�}n6�x$�c���O��J���<�fCJ|YH�;Fj�N!�"���d��H�{��pyB=�(J�!�|���&�k��h
\D��}2a`��c�
���t(��t�1R����a����Z�r���C�[��<�(�4q<o�%�	#�|�7�3���!�$����q���U/���c_������!v=%���MH��18F@�g��C�<8�
���?���PA�$��1H���bHb4��L���E"=�O��	�6�S�� +��U�|a�$�	SE�%U�5�S�*����3+�!Ivx����C�$X_!9�w�ix?i&�3�<�c�rB�������\�����e�����ha���$�g1�{�H�	KhV��@�Q��(@�����'G����ie <������0R%Bz�@H		�i(!�e�6OL�y!�! �O������07% �������3P�2���
���v(1PAjKd����@�=,y2����.���@m�X6g ��� ��5	B$r��T�Z��u-v��Z���f�@�Sk��z,�c���d ��#N&�z�c�u��
R���A@la��e��������~�Z�������
��*F@lO#�&~��T���������J�B+'�}����J@�����K@l& � ����SC�[�#7��g����v}N���%D�-��������o["���EP\	���8�C���G�dP�oc|{q><������2�)�T\�	��+�m(�=*�)�M�e�T�
IM�}1�	R��bz �&��^���F=r3|O@|�i���=�)��V��-�9 �2R:���)^��S����������$b]��a�����)�8	��s�={"���(����+Wx*>�}" ��" ���<%������)�a�~e�i'�R���"v�}��������)���~���'7V�����RG���Kt+xc�Mb�ON�����{��x� �R%���S'J�SI�/W�����`���K��1�w*H�x:PQ�{WG�(�������3�����*���*���-O@�gqj��ZH@�|��'  �D�4�����-#��7�W".�c5��[?P��pdI��7��G�����`"_����8�`�J�q����f5E�P�T"�qL,^��R�R�lY���"_����DQ�G�E�R��}=0[�SG@E�%qHA�T?�LQ}%z��8���8b1
��uT��C�}.����&����\H�@��&���(�!������ ���G��)�|�V�%���8���8r:��x@���8X���c���S�1�g' ����DQ��7�(�� ��s+�g�����' �Y?�LQ%�q�(tq*��U�����MD�	�/TB����B��  ��A@���4p�
R��*�J�(�����/�yR�����f���.�<b`�3�D��(�1�^C@����
*���\;;��-Kq~��@TE�&�����0��"�c�@�����p@��JO@EQ���Rf������f}��D)LR��a�ef��/PE�x�"�|FJ���WA*�	�R�:�b�������8�[%
�%z��}��\u4�������(Q�)�b��l~\o#u������X�5�����	�3%F���qvF*�P�����$	����!�_I@E�`�H�����:�p��(��&beX������8�i+�m"������+[#������X���`9!qE1�p6\*�g�)�0|>%�/q�`N����DS�g7&�bOHB�Q9���x@��z��8�3q�h*�"g���j��$��	�skPq=M2������Az"����l�S��xi���}*������gv'IIQS��`�j��T�`q�(���TCj�@�S�E�X>�DP�y*���ZT\O��O�����q P��jT�W2�!qm
*��F�L��e��(f��A���\�W���!��z�E��	,��>�E1S��P���R\��������i��z5���J���U�c�����	�+��\3Vb�S� R�n����@�}��Zg��H��#�2��N��T��9�Lo	���8$T1cv3�}����\���2�e�����Ob�XQ�9ynV�" ��FJ$���I��i$f�<���aE���:i�����"�`R��B! ��K���b�)&=b�>��q����0���������J&�
)�����.W��
�Y8�*�x@J����db�R%����b�SQ�oc����/��Kc_q=% ��J�UbE��\�����%|\Q���-�]�z�G	�b����b(�zI����1M�l�/�b�%����z�|o��x�����TK�8�!���1�(��H��,2��}l���+�1W?��*�+q�\�K)=< �j8q��8L�=��XQ�9�$�J����0��cN���8p���(R*���O�q��8R�����I�V,�f._������XQ�I���W)y�b���f��8RR�r�{���`�B�/�g�a�P+��L0�d����i�
R���&��(rR�|1��%�"��r>���r�O���R%N\EQL�z*��1'�����
�M�S&�������N���U�\0���4��I�]O	�"����?D@EQL��m��O�InQ�UR������EH�F|CQ�H�2v�"��+�m�`��8U����4S���d������{BLL0H���&<����0y�4E�TpLR����
�1�bE���ud�x��R�gV���f-�M�r�b{B�vM�|�x���{�i���:p�Nf���f���)�9"5�<[�(&�����8�
)��[&���%M(�'���<ac �J�E.N
U���q2b5(!��
T������	FjO��BrV	�@��=��&!��b{|�+��	��
q<R"� 0v�������pD�J��W�]OI�
1RJ|Y�t��/�& ��p2�P�H��� ���L1R��-��"�I�U0Ri�����������0R�S9�J�aE1�L��$�IB��7���>��u0��|��1��MBEQ$�D9��4�/������Ex�#�g'Q�E*�6R��p�|���$Q�]Ov�J�#J���D��?��Z������P+���8p1�������(R��XIf��q��)�������M�fH8vr�}�l��8�
���>	�_"4��Ze�$QJ�%�
X�K�d64��g�aM����������W��>V�p�Jjr�f��ga�[b��$�����(ISEQL3lO  Nf�d$��f3%A$�5v=%`���d�W���������$[p!0���I�b�SN���:�p~��u��H���RWIf�j�ZR7��))����Di ��FV��c�-��S�1��������C�S����R�1�W�������y ?R/��H);�����)x@)?8��	����:F8�
S�Y%�����������p~��k��)�S_If�.�M��2+��hH�uX�T+�l��G0p2�4�cO
1&L�p�����R%��dx@��NC%�Mz&��|����iJj-F�0����0��"��A��	f�� [f?O��I^�����:R����H@�o�FJ2��C�q��8'���FjC���XQ$Q��i��d6tB���T��v=M����8|�n����q1��q���4�p>b��4q�|�rq���(r�
T1���d6�:`���<���)���I����y��,��fS%�M�R�
�H�g�]OS�o�S�R�<������>S��������L�?D@�zJ�b{B���<F�XQ��vx�2%�M��_4&8���)ie
R����G�e@P���a�xR�q2��
�Y��V��t�zJ�"�����\����w�\%�M��^>����<��4��p@^��cd\Rc�B@|��e0�q`7x�2/�����b{|�+�12���b�&c����&�9g%��/��z�5l1���d<#@(�eY��d6Y@'�8�f��������A��cdA����(
d�����w|M��=\8$�<�&�Y���*�bF8�3#I#5W����8�-�-��"�2
�����
��!���i�l�H;�Q��g��P%��������}�aE1�]���NfCb�Boc��)c�}8�8��&l2�|%N����;"��XIf�%�d���i�(�Cl��rBaE1#)�FH���lH�R��$�����c���t�����c����,
q>�,U���6����4ELo5���y8����1����b�Y6p���(���,�p}���w��#��H�)�1��AE��0z%4��^��v��A!�O^M�[v�E28e1s��|<�/�����r������y����A87�����\]
g[�d�/������+F�g<�9�����.����O��x6z���yyY�����[�� z�:��!��^_��O���l6���\��>��[���'O��3[>��-g������o�������;����5��T-=X���>n�M��?by�[^f���~^�����k�t�����J�_������t���O�9��sz:+��'�|���t�����s�����,��_Q��G����:��7*��+�<v����^��D
�����D?�6�����;���^��W�����A>����Y��1�x��^����}v���W���p����RW/�G�p�n8�^����[U�����������A�]��w�/����>��E1� ��>�
�����g?�<z�b�y�4�f>����;�U-b���g�v
z�������+�n9��2|��:-�r�:���Q1:+���`N����Kk���x��^R�
x^vv�=����")X��P�m���4���y�^��?��h+v6�-�u��?����h4$!����W�b�'�?���l8�V�rf$����wU�p�e�m��������)7[^N���3��N�%	\����[���i�MK�	�z���������!O���M���oI��c�gOh C������-?����5$Hc�kZ
n���taci�i�2	>�$kR{���xY����������w������)������d���		4_q;4��s=-��O���U�^��%ieZ�UV�<��g��WL�
���,�n�H�� ���\wnA������Tw�A^t��6.nh���W�A��K�?;;a){�=wi{v��!������r�Mo�b�C&��z�)�S~n;G
������P7�jDk�9,�OH���0�+�wE>�R�6a#Z~�
��N�}�'��I������:���}�I^*�Us(���9pC��#x��\�?��
)��H>8�����d�i�y���7���|8�OY�Gv�#����s�B���=�����tBk+�m>���b������A+�U�R�|��L�?���!���/���s��Z�c;��c�x����g��w?>;=8:t���,���t2%��x����������?��;=����;|�����������]���i�����;O�O��5t����.��'4�����Q5�������EN���b\Ls>��G�)�[�?�r���!�U��U�r��?`
����\Q��&0i�q����������4C8;���%{�G�L*��G-�sp��������s��-b"������m�������hrC�L�����W�3���lpkf<��B5v��`�Bn�^�Xrh���|Fw<���u,� �},j�k.j��L���{�/T�����d��_}t#��3�Y�E��^x��.pv��U#h��/���-FT�������>���5�|/b��+�M]��#�g$1y��lj����/_��J����~0����9�����Y����2���@��|^�"��]I�]����<���o��n�SZn���Er��uH������g�n{�}tpxJx2��f}������s�]xx�~��V��cc}�a66x��}X���0t����������v��_�<�e�?�c������i��N����^�����y��{�
�����_;��k��,���0+��m{S�7�7n����.v����*��YYL�E��m�#��s�>������hp��@������X��{���5}������0����s��������[<������5��\�ow��MY�wo�[[�����}�K����z���y9�o�W���|{vYl�[��n�������uN#������t���$����<��5��0I��]��<���9����?s"�SJ~�Ln���8��A�|7�\9~�z�~�����Q��{���b�Dn��9W����������u���;>m�7����&����'�����C~����b:+�;���������z��p��a���J�D��K��v2��w������;}�����^�G���	a��eI�����K�fm��������1�q��������8�
+��K8��F����|v9���'Wy������5�g�`�*wsZ���}��T��������|g���;>�Oh�77�'����R/~��'�:��^]LE�_��G�9�|A������s`7�i>.�+��sg7�B����u���s�;�m�����.�*�����u���}�K_��#�`�b8�|4-q����j�������|P�6���c�r�/��v��d4,g+��Nx��(�I�?�/��/hesO�z:��c[�#p���q����q�:����Z=.�#������o��8q�cz]��77�O���{}{c��|Z������F�]?X�k�%����`Dz��(����{Ad;;� :O37���4��&|��A���_�c6�>�����������]��5�N���z��
i�jm�b4���5g�9�����'�A�vzS����!��������,�k�Lv��M�����4;y��k��=���LxK~u��64�0�6�_���/����^�%���c��f;W?���D�s����*�$9�IQCU�^ZA*�[8F��Z�����8���K��������}�������w��8��~r�4��{��f9����zm�>�r8���o��	5��i�����\��P�;�����
�A��F�.�Q$�0��~���|��e����B	}������c�����c�X+���r��*��cat'���Qc����Of,�
&��!!��L����j�Ao��t��6�@x3v�_�8�v8^�b*���~�|��4��rC"��n�-_�/�O�_�d������v�����q<����E�
[y�e��[�@�+L���.��KpE���KO���u5/=����r���Z�����b��`�	��$s��UK���~��eG�����������5��o�8/~Nz�J��T8�X����h8�"V�!��>6�-���"�|iM�g�iI���=��4�z�'!�����|4,�����LF��|��i������y>*c7�g���M^##���~qV����8�X��-	q�G�x��������y��d�=���$��X��:_^������G��������X�d48���
����:8��Xw76x�6W�o��h�tF��U�v��,���_�8�����^��Rg��oN�^��^���Cv~����������(V 3�?Y�|��������zo#�oF��D�k��M�y�����h7����2���c������X���-3���_������k�a�l:$�Y��B�- ���p��<���N�������zf�������������9���b��|�:L���^�6�o�^
����[��������o��q��~��>��c��p���)���������5��	�<����s����c=���T�y9� �z=�>�l�|D|��j�(_;�����/��\����r��M��e`+(�5B���b�"&Wx���#��G���������|����!��|Y
��������!$��`���@P��W��'��7������m�������4B�}�Q�l���hzDoF����z�^$!H�(�'�_<�z��2��\0bX����7/�^���{vzf��LH�t(2��$ ����2Q�
����w��il<o�`��:�����R���,�\����1� �*
���i�jxq9�*�V���-�Az�R�y�|�������^?�M}=-�!�N^�B?�5�>f���4L��o���m,�����z=����m,�����dVo=�j���}��r��|'�h��N�N��H�C'��[�N���}�m�&k�9G��};??'R�~q�q���������en8�7[�E��?��|���0���������b�v�R�`�`$lF�:�(�p�<9\5e^�?|���s��N'����Bl�s7b$�����}i����G�hgD�H|��]��6cQr�|'�X���n>�+�U��!�lf�{p��N���H�NK��0�L���B:�S�����b���k�a�;��|f\��
��K2����L�c�����%[I��c�t���-#>X�f��(w&�+��B����~w4!UnP6^8�^��fr��.l�[(��8��G��8?��`q����Y����������(?2�0&~���"0-����i�^v��&��{l��zx�&n��@!d���y0��B6�s�����/�C�O�D[NN�-u1#��z������M��!j��D��+��x��h^^6��N���*z��R�q�E
3�.�f��cb�9�A������!��5��7�����tr=�lI�f���}A��M��8�c&kv���O�x&����VJ�nIS;�z��%�:���7�}'|������NQO��U�������JL����c����aP�v�	����$Cb���!��T��� ���ugu�C����(>BK��HL��RZ�6J��$P-��C)����q����������~�4
UX[-hK,���)k��Yc���=-fmN8����
o����@�&+i�n&r,t}���/�j���]�>���X������Bk>�"4#���-��������0�zP
�������<>c
�������J���u��V*��^���S!������=Z���Zm�����w�x�T-5��u��Aq���h�������3~�5f�Kc��\;|%�|1�%*h�M��-�lK�by\���?�������tA��=:f�����������E���i��~/��;��hV���oW����:�EY~o��0;,S_Wm�ke:�U�n��Zu>��QZy��SI����;;~r�%��<�H4yks�.k�U����1������y^s�������i�����A�n�{�Q�k`��U=����L[��Y��������sl��x7Xq^|?��z7��W��0������������^&L���<s��'g�����sGnFO5V�1\������=�:����������Q��1b����D�Y��jtE���l_����yN2/�c9�,a�]!���_W�W�{l�<�+�5����n�j>_�-7e�kVG��Q\r��<G����zn��Y�����D�y���.����{B]!���5g�|��O}@W�}�y6[�6�8��[*��1k�rVA�^�N��;�jwn�~vGY���Q���)�E��S�\�@�u���ldjv�yi^z��)x=����}}	X��~���Cd�X�9T�r�������>mC^��<[~�9q�,�����Q-W�d��t�T|c�����|d��X�b���|�+�Nrka;�7��:�j���n�:�,=��@�3�w�;����$�����y�������=A�7�I����|��$���	�h�������/�n��d����
^\�r?f{������N{���-�������dC���F]���X������c�))$lUw���b�����=5Y�g]�sZZ��A-�s������9��o<nB���x�d2�*uz]�RY:��{:�������OB���A�
B��K��� ����j�[K�}���u�A#��}jw�R���2����p���S��s�
�����0�{����������?�v�:���C������E�����w�{���B/�~�����kdu�s���a."������#������wP;�F�W�}��P�G���.���I��.��~�$_��o�5����?H!j�oK�H���$��oE�V�C��6�Z���$��,��~g}��������#O�n��&�����������ei�T��F�:�	Dk�M�����^z�w�)��{a�,5R^�B�K>�UP�t���*/jC6�%$;x���������^�b�
�
�W��C��F�A�H��^A#�Q�V�xlR7��tZ�2=V����f���j���o��f�>�_�t&-9�N�M��E9�sjE<W�5�kBJ�q,�z�4'q;����gp9���p,�dOc������~���nv�%�y�sKc�H.����O�������`�D����3�{�a�-���n��(������a�%q�x"�M�����������=S{�k����-+�M�r��k�1�������dZ_�[��?���#3�7o��8���L89�W���O��0=��:��8le�KY����H���WC���YK�=���UGuY�Z�mjy�F����j����y�������R��L��!���M�S3����S.-�Tw������Z���1u:X	�Y���)�CG]#-��w�����d���#	�M� ��q�������]o�1�B#@�?L-R���q~E��7�� �������8������|���$f�d�_~;#�J��v��/���?��M�������M�.q~�2��f���M.�^8�C���Z���v,�P��S�m��a���H���an:���<k��p��YT�l?�����e���l�hC�'�[�����+,��W�
�'.w#C�UY���9A����_�|az����n���F'���k=o��>���
�i���`r�u_8o�6��[s�
1o��Ul�������I��Y�0!dh-�����I�*��f���'�)��Y��o��[+��2�'V65<�w�+�����<�=og'��A�����jS���5&���v�{Af!�qN"3����;	t�`�1�������G!��
�B��gb���+�ld��$ ������J��`{o0���1���`��_U�������0t����dJ$G����������D���+����G��p4��n����~V
���D�0�s/��Y�dQ��	$���S�����u���C����S�{�{��v��$J��P���yc>v���aqS����b����n1����Ox���y^�=��y���O�����|��kv�r�j���~q}�E����������w��?�����i�s�zs���g��"=>��N�������"t���h�B�&�B�r�IYW�&��"8�%8'U*.�h��/�L\���{�����>g��"e�)e����W����;��Il6�W9C�g+B�:EH�9BZ��=G{�������)�<������NL����o�bsmS����K_W����i�A�����c�	6�a]Wr5�mJ	]���
Bm��8�9W��A)��uO�_���y=��f�
Jy/Q(��6�Gg7�*�U�S���d��"�S�>+��MF����HEi��5��[�x���)O%��su"��c��8�./'3�d�xR���Q��N���T��$*b"�,�;&�N�J]���>q�qz4�a�d����?��i��c�o8V�~1
�~vx�$}���������V`[��6�]V�����WCIu
+�<'%�(��dk�$;Z�W������k\$+4�����G�#N�b*�Q
���+Q_�'�5�[W���s����j��%���$+����H�����VP�j�');Y��Z��HBD��VE�������uh��D��O�%�CC���sa�6 �S{X���L����y��)�rV�.�t�r2��V�;�mq^�^=�U�ic
W�3>`VAG�'��zw������l�p�u�A[>:ew��$�Q3���N��&���:M2�&�&R�V���.Gg������9�s�������C��!.g���U=�!r���n0o�v�j	.�$Z��V�����RM�qV��d����M�mp���*�u^��cx[��U�RQ;�}&�2��2������z�FO�� �%f�����{����vC���@�9���S�O�6[���
�q�1��+�;�b�%�$��uy|�R��"���6�q^�|�����+�������5��hQ�76�O��x'�z#B�A���%i�R�I#����L��e�v+KCmepXP3����U]n�)7���I��q����=���?�ln(Z��<�/M�������oLf���U���z����ij�qar����[M^�����v��Q�0j���	 ����f����A�6
j5��
05�z^��G���or�JV�=��	+6H`5�J!zPUS$2��-�R��&B�'�!�:��o.'-�\O�/�/	sgB����Z����?|]����hz�,
{���� �>�(H�?;�O�Kt�������bl����cC����e����mGb�M��-����/U��
x����zY��� D����4"�b��e7��OL��I����plI��&K>��eB�/�$h�0����e(w9�bRY�����3b�����m��d$e�J�e�C�'$���������8��q����4�f��s����v5Y"�t2���������d,�!�6���C6���p��bd@��w���"�1[�K�THoTd>��|�,qm�7����8�����:�Vj�XMQn�F�����m�9!����k�p1p��\E�9�]�r�
���p��X#)Z�@dTZ>��E��2G����{�`d����3g�;�vC${�nbh��~��'i� �0c���<9E,v��\����������D������m�@���0U+Q�F���h�_<�;��� w�W�~��'�y�]�=�8��'�,�nq�l�6d�mJ3��4mY�����y�bc��E
a�cb��kzk�:���D������4�%�|&�vYa�������l������S�N����O�e��������*�WT�c��v"{bt.Z;I��8�EB)'���02Y�[:�e��%��������%/n��%Xt,���������k�D+��	U��5���t,����JU�����?Y�Y�����E	OU��J"W/��u+k������2�.Q�K��Wsl�M���s������y�|kPu5������EbZ�F�%�ot�JO����|4������m>������Q�a66l�i��&�
�?8���a]FT�	�f3!�hH�|hr69#hTC���?�������z��/��&a�U1���Q:bD�J���>�%���'��
���@�c	!�
�M&�&�N��5k;��3��U�(��O��N�+�M]�,ukIe���\�Yt���)��Zj�����aYoi[��5��s��ls ��;
��J�_�<QA�%�e��L���DR7q�����Q���WS�}s���r>Lnh`��Ue�����j�*���3��c"����T�Cx!�vl��6j�Z�@�R�W4qM��'m��%���a�m�`W�m\O
�]RBd��DH��^V��������1�Y-�L���b�^}�D8);}���9i��YoT^�w��|rpth^������&8����im���a��pj-J������}88Ov�84����?���MZS� ��9��p��M����J<�s�����
��a�}{�I�*m�3�T���Iy1-N���Q��V���3��q�!���FL�Q/$�����M�h/&�Y.����c/��S[���?[�1�V?�u���T�PK��<6���k�H��R���J��e)���f��FV��</::�y?��l4��^���6?���������]_���y7�����;��1�'�=����]	'�lU���GO��{��k���9�U�Cs�i6����E<�G��d����<�wvS������X�%D��;-�Y�[��-K����5V���+���.vl*=�O`M�e6��L�*N�{|bU��,����Z����S%�[��f2�l�U���yVn���[��15XN���#����C^>���7%3���6v���������l�;��fb�'n��"����NA.G*�����qF���N�|���kB�Z;I�����O/H�\B��V�������G��`>_��~����9���A"����9����}j��& ��C��f�g+:��fvi,J�.j���}X�@�F�xi ���aZq��f��La�O�����������(�*�`�{l��ba/�Xxf�(���#;$����1[N-�ZHn
�Yja�,�������1�6c^�W}o����_4���dk�b�|'Y���^�d����6:�����G��]�}M^jq�0��I%'w��������F�����*5����Vqi��� 9�M�N���7i�,G����5��4(�:j&��\,-q�r%[
�?����)mAfq�>����
�+#+#�Q_r.4C\���g�]�6Cg+S���p�SR��'�:�%���j�����a�R�{X�n-��Q8��5�n�kz04���U2<�?M�w�rv$LKP��S�7������6�>]�M�����������fhz�NY��;�3]���H�^K�Kv?�w�Pf�����v�:>��d��B��g��E���=SY������Dd���j��!Q������V�
��0�c�r����F�6��������wv����Y�)�6�ji����-�#�3I�����buLQn8���r�����|�wL����I�7`�Z��l���Vo�_�aB;�I�;F�~|�_�N������	M����|����X����:B�:6,����i�XJ��2K=�jMPr��0Z�1��%"I�3IQ��V���Lk�f}�*�NE\���3�in)�{Z�����
�M��a�&���N^;0T�`�op������%B6�&��,�!�h�>5ft��;.��z�1����$���Tdn��6�J����B��sb��Y���m	C�D�~���
R>]���xw������B4�m�4�g1���#>b���������2[[G�����*>F�v>	���������-7�������&�;��K���b�������dok���ios!v��q����Unt��>�4������������^'�&L��~�7��
�D�[��I#6�`�Fc��������b�������P�A��.l�~��}������-�R�R���%�cfP����QsB��3����v#D�������&�w`2,��D�����u�0���_�Be0n7����]��qfB��H7���lK���*��s�M?���r�/l��#k[2;_��QL���*�H�4�n�
������v\�F}�[���W��
�a6x��\=���Q�_Z�-K�E����B����zn�'bD��^���l.���z���m���H��T7�>��"�\�F����T.�����S�b��V��3��-w�����V�5�n��g�u����!�}���J>��k�ieI�����3���T����G��Q�
���_N#���
��k�������&��l�Obkf�ae�1�p&<��+��/���Y����>�b����M�2�5��W��~*?�0���P����"��#���/�VV���� �����^�/��_������0�����a�
I���q��xy��n�������-;"Z��������/L����V�#cz���6�g���V�Y ���:Q.w�)P��'ieH�E����ZA������F���k��{>���t���h�F~>���;K��k������h&�d�E+���Q����8�%�lx�bKu������Z���!�����
j�X�lwgU�S���q=Kf���7�����0
>����k�#w�A����n��� ��I����~�,Y`����� �����q�P�����r���r��;>z�<�%�i�K\�rw6�LwF(�>q~<8���}�f����J������	��3����o8l�<o?v���_���.81��e��b�����7�-����
7�&�o�_�1�Y�����B��u3���V�����t���wj����V;���+���n�H���jW���N�n�6�P��?y(�m~w6������f�4����yiNC1���9��mO�����T�.��A���o�[��H�|�W�W���m��-h}0`7g��=�sg��R	t,$�J2fz��o�+UY�0to�=��T�Y������p�<I
r)��lqOt-b����1
�t�
R���8��$������Y�S�T��.�W�)Ex�sj�K����$�q�J�i/�^f����;�����8B7s%�n5�@Du��E�\�R?���G�,?��/\-�f�����X��3�.m�7�����-SjT��A���	�B���x�x���19�3�'�����F��^9�Y	8���I�v������A�<^����HM��:r�#D)�sb��w\E RIO�,�}���xw�6"�hx@d�9�;`C~V,�������fpXa�*�F1c��+�a��.�X��CH:hj���Sd��At����$k����v8�Z�'�,r6�d�QoK?y<a��u��HD +����,
��:r"����F�������%YM�B��������
�a�&����tP�&���]�S�{z��MIL���qo�8��DGJt����s�[:}�C��(��\���l�,)Z_/|,�E��������r������4��h�t�l�#�%� @^!/���O� ���S�J�����N~o��eI\B�j��zC����4�����r��H0Q��\���;�<������4[����-�������N��f���7~F%����s2�ho�z��<5�Td�>�[����i��d�eL�R|KY��1��Y���������1>�i��O�!�sN	�������B�~�'�AqIA��4�b�$��P"F��2�x)��w�Xm|u�g�������4��i����G��V;����?S������B�����r@���YkV[�z���Y2�"y\��'�a���3BI�OI�_Br'KF)��~`[t�����������\����4��zzVX�34��6��Y��n�
O��������uM�IF��j���_�<�����^DO�
����}[�9X��2��:�i���<�����>s�h�>j6�����^������x���p�A�lV��RB��|j�����2�����V�W���'	.O�N���	`������sD� ���E�����/?�\����M��{�����
���'�s �#���;r��~`���L���4������[�O�nl��S��:<V��������������(�����z]�{3�5��g�����Q�*���G���]��U'���I:@S�qE�� ���Gc7��|�B�g<����/u*0&S�x)b�[����>J��������������l������
��y�d�j}�u�����[v#�R���n�������TM��ZG/Dp�I��G���O�m~�I0��6������,���0�k����#8��O��&�%5{Q�T>m@���;	@�!����m&�J����=���9���������e���4	c���e��tWr�������Y�g��1�/K�
+���7�X`��2&�SD�F2y�H@=����E'
�uL	�����	 *����*3a��`��(XFa�?BB|E�Dx�06�l�5/h�������(*��%e_Z����xs�����>�NDFv�}v�'�D.������7�����`��X�#3P�4��s��`�P���}�@���)�P�E�$�i����,���ze��ThE	�c|�����$�h��d���K��q����%��CE`��=�>?����cZ��t����#+M��������|n#��	�+�����DDKf��6z�����G���6�(�U��������@p��A�������n��D�AT)�1�K�!-=QCB�|�$x�9�R�<w���+�-���;l%�X��V�+�0����Ja���� ���{-Ek*v��%9������q��l&��+�^�/���~��[g�-r)J4U��#�uxD0Cqw�B8�+�
�5O����V�|wb�����'1�n�I��'�nz�mn)
/�x<�vM��g^�����j1=c'���*��{���	ccY7��Jm����l�EW[N����xY��K;��Y��eh�
/�_A�{U�y���������v������J�Vv���"��tC��t!�W�bFa�����biwYi�����B,�{�N>\�^�-���5����+r���o�W'�������aW`��������L]�(����u��G��b��(~�A����~�����T�L��?������Z���S�xF���#�3�Rq��L�ix�J*\a�$����z��S�y�0,����Z�<{�J�����+���OZ+�J�� �X+���r���	���^�y`{���a�u�����������g+��E����[R����p4�;��m�/��;%��������l�����A�u;�%M��G)1�O���@�\%�"T[	�qL#����4��B����!�OO�+�v��?Hq��$�zP����&�+�(���H�l8�sn����(	|�]�@���������1K��UM����S�k)J
FZ�u�a�"&�{��e���h�{�����:{[F��������<cu���u�������%�L��7��;)2S����T����x��a��F�Y<�/�&!��!��i��^h��Z�!_�����km��f�c{�*#x����-8\f�p�@��4�K}��E#Bd�F�����:���1���	��4>G��I��r�F �1��1���4�L���b��e��/�����E�����|6��~W�=q�	��8h&���d'pY�]ke��Ln���2J/�{:�N�S$�$��]gW_��|]�H���J\�c������?��Ui�����J0�\=�ph����.���~	�(�u=K�����K���*�p��m<�Cn���B98��"1=%Q��'A���_{��X���G��1x4���8�$q�>4T���y�K��y��hTWq���;+��2�K7e������w�_�������LT��9n�G#I����/Q;[�X�	d����=�5g�{�-������2�I�Y/_xt�x��cG�������a�V��k��\{��N��j��uo���c� h���I�F������H���]��u]��bw�^�9I�a�Sg�R������lNl�u�3&Y�Zo]_G��^.G��(����$s���yN����B�*��f���k-�8�H��S���0n{v^l|t���D2��5�D�6��	-C���8��d��L:$E��:�������O�
����
h��D����m v�R/��@�h��E�,������ve>�A�������8�=T5"���]��L�1��-{�q�Z���;�}7�Y{m0�D�u�17��''#�:kA��DS�K7��&8ot�vTiq6<��p�P�z�i�6�a��`-�b!)��� ��W�^x���lM��q��)�z�y!��	R=��a�$������W4���V�h>�i �E������j����y1����m[��cT���i����6c��B�5"�ut0�MV
��F�G,+��&��lY����(��W���TH������Wk�����������]����]*��z��I��(��ZA��x`�,&�0(�~+=h�1�z�;X�y���3�qz?�q���[���9���'�0����q��!�Z����eBN�SnHS$�O�Cp�)�{>��C2/���|
E
L�?KV�eF���Za�g�I���	|�W�����`4>��AzL���/F\�����oS��_���x��W
Sf�$J_@K�NG�p��j�
Zqe��Z��[�M�s���2�R���Y0$���n_;R���C�I
&ho�=������M	����#�����It%q�Q���x����PzG�8���d��x�UQ��m�,3� �"���03:�0X)�����D�S�ohoR�8s������[������+D@vu��e��v{����W��N�1�=���u������&��fFIdd��i���s��b7$
'�&��9��:���,�}���<�1�a22�?g%5I%��,�prh���
�"��Ami�*��3����*�WQk�
+�����h�m�����Vvn�DIV9j�Jo�U�[�r�UoD�z�[���^���������5���V��O��,��,~�~
y��Ly@Q��5�x����onkpNs�`�D%@��N�
b�zx�=@E%@"B%+�+6p����_ ����)�#]���bd�}���TDY�I����c����l�V���hV���%2����81F3�� �.������F��Qm�^�?�����V�h7��n���n�{��N������[�z�Z���ZT�}�?���&��eiR��<�����\[{C�H�7#Bh�F{r���2�=h��6qf������oR��[X	��%��Wf��Q(�;s��:6q�kn�96��%54�XbG#��������$��d���.��"��l5"���!�G6���7:��4�*$1�7)%�zBJR6�����t������ ��U5EYy�(l���YJ�6���4 �����U���E�k�}]^���?����5����h*�9Y�y�_�LY�O������FDn���;�=���������r �}�z�����\��A���R��ZQ�f�����I�������)]	b:bmm|?��TZ��.s �Y��C[�nR����Q�?���@���`��_�+�F�����-eu�+J�;�6��N����1>�t�a:<�s�������Z4i��N/����M��7��-5�$��b��1��R�:1u^��-#�h���v/���r�_k���+����2�D9�[x������`��\%�_c�����)7����}�i!�TO������Ep�O�x
C�W�;0����o��`f������#�[�/&��zsv~�����������<��F~�5���])�?C��t����x�/m��=�����\+=��.'��W[��v�)�>��;�X�����=�Q�U*�|�� )�����?�z���G2�����Q���GdO�������5�7��Gd����g����:
���(;<�����Cil�*��]k[C9��������(wLxJ����y��-kl+
N�.�2��7�W?�~��|��L�2|�v����a��(���z��Z�����(����r0s���F��
����������wz����,�0�64Ct:�7�_��-������Fw,�k���DlN��J6N�)�BJ��
������`���"5��V
S����>��NG�^0��x),#�&��89���/�N>�t~������X-��*��8��_���Ev�������tD'�Rd*e��4��/�x�D��������W'�?���bDg]��"��&�������$������!��k��7�6��l�����+s�����FO�����-����Jc���W7�.�)�r���^
�/���g��v��xl:?a�+E��MT{g�\0jW|)%o�(w9����y)O��s��� Lc��V���6.��
l1�h%�~��&��/,��J�MX�D���g�`�W�y�������J��������a����ggrf�QL��k�l�oaY��W��c�q�E�9_�
{�Q\s�Q��:r�����D�lW������;��n��&6�w��E�`s�&�u�u������O��������b��v��m�������<����
F`e6w�D�7 $?��������1��V��PU�PE_~��X�
v���`�7���.A���r��!�/���|�(e���W������A�5��v���F)�S��{�0V'8�����h)�d��X�b�V�DJ���:�����V�9��TC�p7/E�X�C���P@�<��=�=xH�|m8/�Jw^NM������������!���SU�P�T���5��5B<4�u�~�M=Xh�z�F���F_B��|���B�L ��d�G���}D2�M�`��gw)�&S�������������*�_��U����^�Y����g48��!�&�q�r����2p�|���}�[i��������w�G���	��W��hWiokU�P�zi�`���0�@��1m�&��WjA:���d1}2FL�nW%� ��t{��L��D#��I�3������������P]���C�b�p�
���w�����4�`�z��.�R5��)�:!�b���h��%������*���/��B�����QPF�)��C3#+0��yh ��!�)5�E�}����g��U����?!�@qoCq�msdU���������
�zu��,��:D8{�"w3����]�$5`3&�am�;��a+p7�aOWp+��6��8>y�����YTH��X��v����6�F��6��T��W������W�(�1�8��8��CQ���QG�����������.��X��r�:���ww��H�|^�h��}��=f5��]�
-+Q�8��nH�^��B��]<�86M�V���Up���y
���a����J6�vI�3"�^}<�mU=%o��o�f>����I�x@y�����Y:�/LW3~��iT��.C��V��E�"�HP�G��VP�C|��|0�
���$����>�<p���p��'J��0�m��3���L�o(�Q�:r��L2����n����1��2�~K
��t�E�]wJ#��1dv����mf���E���	;�eg��rPB�!��\�e����;3�Hq,�����?������������JrS\��I��/OA�R����h:���_/�Oj+lWwj�Du]�����jS��z��+[8������`=^�����>�!-3�P�Y��!+��^(�����_�iH�O��|���`��Y�A�.0No?�$���L����x�����.���c��f�(--G��f�}w v�1���+Xyb�Oof�h�����<0��>J�d��6���M��������#�nt���1("�ypWQk��r�v��~E*~���w��B)�f�����{��)#`�,���9��%���@����Xa��5�j�4Y������
�������J������Gn���sf�������M����:<��Z�"�YE�8���3��*y���QIi�(�jp��y��fdl���0�1�����;�@�r%S�k��"�H��#�C�pj�������[�	������u��e�*�
�	C�������*G4	�e����oB?�aZG��|�����h�������*b���b�BN����1�0����.�4�R
L��'m�(XAR$H�C��T�UZ�*[�f	5��.8 ��-sm��:�#o�����&s-����������`��)��s��q�����!�����
MuYk�����u���.��Y���(���r!#�H�!$�JY
,
!���8�zlP:���G�W�e*"@[�����j&�8�e:�_I^AK���D)�b^�9�f��U�z�*,��^��g9���K�EM�	��}�Im0��)�:���)���D��p�"���$�tl�~�-������+���e���,rx�b'4�1I�&\_Z���>	�}
`g�n�������F��\�@'��1Yd*�Fwr�����������))��UD��Jw�m��$l��(
1�H��A�U��u8����pV
�*$����Q�����mXZ�]����j?��mi����q��aO���a�J<���bp�K�������HF1]�,d)!-�gB]�������rV
~l93;����z�:���P7B�Z���m� ��������]/��.��4�&t�F����D���3�Jruj20C��NO�x����b�����t�y1kW `4���3���/+��^���Aa(�$�!>@��5���ztEQ6�#�Ny�#(�3�$qZ�"�sC��#��R�����n����1�H^'I��K�����������GX�~���K9��"y_��Bx)K�^A$�_��y���l��*��E��yG�*Q�&sK(z��-m��M���)����i^$���h�S&)-a�_����%���*xs���7=�Do~U�����?��%7�J�B�E��^����$d��%�o���U�@��Q~u��H>!<M� ������do�,L��"a��}�:�(�i�G�o���6����6)y8�`8y9���_FD� �1
sDr ���0q��`���5�-�%}@��G����]��]d{"k����gCj�RkR����T�-Y���eZ���/F���X1�4���-�
�d:S�H)E���0B.K�h�7�W�������n<5��R�(C,NC1M%�l�.+k�v����H��[m��j�����-|��|C�x��}6P�\d�&������1y����4�P���B�������zF�Q��|�9A�Q����%!��
���4��6����������:����S}H��0F??l�?������^������!8&�����L�s��/dn�������N`�<���
�sr���#���Gz�?E����v�����o�e:;yU�BB�C����7���|��.B�X!9�RK�R�\&��`��������}f��1���{�<x����#��E6W8����������\"�����2�l�$%�@N��;yOJ�}��d�N����KW�o��C|��U#u�bO������#�&�S����#���3TYt�j�L��OO�}������W���p
�o����-�p�OL�oq)O{��=y(x��~����$���}��lA2%��n0�p	43�`e�����k#\��O���yZw���):��Y.�s_E��a�1�s���%O5�|�;���!w.5��&?�3cw������Oe�|�;�V��z:YPs�Ullc�H�k�X�b��w���kF����Q_r�91�
�A����:>G<=��V7����B�F�g���i��	�LK+Q^x��
���e8�������VX�C����P�}���G�����f7����C8,
��b3��"�;8}�+��O����M��#i���VZK^�x/8�V)g���"����@�F�}���|�p.������)oX�%���?�����<;���~���������q�����9A>��������oe����������T�m���n��Z��H�
>!���<�Q\����g-x0�A�����\������;�M��s��i���t�D��(�������P��L��;�9HG�Eg������M,���`��v�c��jDtL
���g #A����x\���]o���mE_���I�w����R�Jh?��{���1��y����
I�J�2i�����R�u<n�?��(	��>��!��Xvm����o���Y����T��=sf�9���u4��@�l�������i������KnC�K�3�F7���D��8Nf�P����^@k�� FI�(Y]�Y~~��)���I�4s�,c?�"� �#n�;�����~8-B�g0~(�4���&�=�
��^C�(c&��&q�t��4q����K&f�.��(L0�B�����h��	�*���E����"/�I��%p8�G�^q�:����T��m�M4e���}/����J�����J=sv����K���|�!������w0�	�0��3��<A�D^���|���g�0V�X��Vu�c'��h5�/���d�a���B���D#�����S-��M������������
"���x��D�*��
����Xi@��#�� ��nd�6
sc<�nC�jC�7o/0{��lY�	W�ia���Z=�LC�#��� ��LQ��� 
�4>�u�<�8���]e�&�����V��z���������|8K�{��G�T����h 8�����?>�m�1��_IHV���8@h����M�[��+���,�\�A~�B�=�#G0]_fp!���0x^Y�&�&Y#�qI��C:����1������~�����*gg<�a��T�6������O6Mx�����L�$�5��1�YcpL������O�IBMAc�r�����E��`0�����3�+k%�m0:O��N���'����+�c�l
����bB&�_�����hJ�����2�O�8{�]�������R���U�#��uN�q�~��J��!�V��������b.G�b��g}p��c���������!Xr5+H
le�'L/!�a�����G��O]�x3�Ha��8R_�[�%�
�{����|1�d���F����uDB��f����G��@� �T�Vi�I�?V����OG�u�
��cg�Z����K �F�pA��V����H:j�$�f9+Q:�����T@php)��,[�+�
8O�����,�L��:B�W�u��\6p�p;fL���R�Lj$�@|3��������O����K$��|�!'�� u�������2B��v�A9��
�P^N,1��80�F��*��2g��	�p1����{A���j�s��R�*[������
��p�=y����T��K����+b=�E��bi��5P1�DE��6>���������������K�\4
�v�S\�+7���S�It�/MCO�Q���s��B?'C&��|r��b,+�"F�C���b��� �e@����`6��7c�0�>�V��`�A��w�N�C���l�2�����<�
|R�T�V�^{��[�O}�����ii�K6�E��C�;T&�Q wN������������n���RXR�^���Lf�L3B/�\�hs�ba�u������t��JG������YuF.�����p���\z{��ZIy�����
�o!���������sawJ�L ��2A�
�?�K2�b0*��\��
^m5����.����47�,cv���4u����1j������VC��Gq�2�\N1�y�]��:���;��[
�����]�o9�T�)zaY���P�������R��������D����d@a1U��W����Y�+�Q<��/5/&#��b��CV���DV�5���#�+�G�t����fM������t��R���}�C?#&���-C��F�H�/�F�H�8�}���@�>Q>�_���^�����:,QPv����[����)�z�9��HWCP��&X������~'h�Xx��|���������i� a��\y���e���u�Su�l�����w���oj�3z".8�%}�6Q�f���cu���	?F3-�X�\���^��=�Y���T�z��GVK}|��|�"|�s�n��j���N���y	{NVZ�;��!�7���Ra�����$�>���������[���������R���������ns����>\����C��V��H��m�i����B�����G�5�2�jU��	X�SOD�f����wF��������~�����
�N�;�g��z����x���s��^�&0r�"{�u}�����N�T�0�^�d���"����������N(�^�%'�k�=�hMG�������ml����l�^���R ����U�����6d��]wk�v����%n���
�u�:3{�D���q����zI,���x��&��,:t����]R��R	n�0{
���������=�x5(T!Y;l!��� 7Rm��A�Q������V�����:��#�����<�@���u ;hG�\���y�+@JM��8�{�~�{�r����-���U�E���p��C�{����Ln�u�Z����4��HG���F���������T��y�y`�I(M��1�/!4�������|�(	��A\D�x��'a��I�
H�S��m�����.tDg��iD�`s)���"���',~��5�2�U��m���?@�(U�@`����WQ��ddL.�U�0�t+%g5�n�FE�H��2o �\�����;d_<���
��5�\l4�����9�B#E��w��Q���' 3	��zG�9�g^�K����C�-`��q7�T�`����K�"
��^X8�#�5�����6�d<���v�XG=ql0�w���S��GE�run��Q1���f-y
 ��O^�'Wi�q�J��Y���i�>���-�i_��)���L���'���t��d������<�����5_�F	�l9�0������)e����'�p�@B�r���H`�:W�[�Y�Y(W3�C\�k-�Ie$[�N�"�T������9m�����e^��P#���
���������Q��+��3{�?�%��V��<�������W��M:����UF����A��K����������M;�7�(H7%��Q�S{Re�Q��L�"�� �K�����uk������K����R��`�������H��E`�x87��g9���e�>c��}�z�np4�G�1u��g��V?�C�:��*h�&�?������i�=-a�V%W:���z�I�Y���J��E��&�}��*"����C���E�Aa�Q��$q
���u<���/�����X���7�����-��FBw�@@�X����D(�]�<�����w&��p��9���,�7�Z�ZF"+Z���k�z~��|��K}�P��n���Q��1oB�|D�]�4������	w�$h����Ey�@�(R[����v��Lev��v�Lg1�m���o��v�\G_���)iq�=�S���|����`:� 5&h���s����5�(���i�����LHL��^���sF��VD�BK�C��������#
���5�,FB��9�2i�9(�#����,?��W%�T����o{������%��D~�`��4�M���u��B��Z�4�5��=h�!K���%:8�t�O/��F�������GH=&>A����������y�������=�k��������+��]��"�9����wu���1����o�b!-kk2Z�����}�����(@&%������f��&N��=r�������f��JdV"�`�`������On,(Elbz7`�pg���k���ir>�E���A����:`�:�j��W����8.h)�	�c�)�e��>�N�Q%J����G�}d��F_&����9ZNV3�+qDM0� 4]cJf17B�x���|*l�1v��|�ff��{��e�S'Y�&���H�����9�M��F���(E^V]*A����d�bK�w�Amb���D
�B�E7�8#h���������b�m3\��
����fs�G��E	'��x��c%���E����2	�����������R���q�)��V�=�P�Q	2�;�q|v�
�����
��L~�[�{���+���B��W������x�-5��V�[5Mo�Z��Yi����s����_0���v���Aw�~�w����au��u���I��t�X�i��~�	��
3���*���i��B������.�D��r���q��r��(>D�x��\���qh$��	~�--+������i�����L�����|F)��������u��/���zeg��z��}-��Z�J>�$��@GP�3���U��O}�����2G_�������3VLG�������0�E�<*�t��-�E���S�b���AHB��'�Z�F���O1
��� ������7�f|0����L<o��A4(���M�����E=������GiV-�O��]>A��'Qz52�V#�KU��@O��!#�iF�;=~��aa���J<5�W���G�5��x����	-��sEhL�FP�����_.��u��~8���(������� D%	�Lq)�<2r?q%���b�&�������)�@q���b/*����K�(�/���<N.�����R�|���b/@ �)�MrO� ���2A"��W��F��>}E`��c]�|�EX-�$u���-g=�,x��8�����I"��������t�@��M2)�o��j!>�Yb���W
��A��Go=��C;o�`�C��)L!{���1o6��Z�i�b0�P���T�o[��q������Z�^�U���"��~+j���
��o��-Pd�����e�"�;���Y`�H���h
�v�f��`��x7���_��m��%��Rm��J��J�tH��X?bE� .A�w���f��������;(�%�SIR�I�������@���g���m�fr�Ayq��,�1�����������������+��u��������������[�Ta�����trq�Jy����x�G�:8�p��9^��y�b\�[F����9"�b���<����L�W�A���0�[^KY�y�V�a��u��F����u�������f���,BAi�����k�p�`�*����|�n?�����~/���}��K�����&��R_��vD�c~ U��Z�B��z%C
go��W*����f0�	�Y`�5�����nmO���*��y��;��?�)Q��w�R����s�U���s���_�#������A2/��W�3���tB���j��.��J�����������&��Mn!��sd"���>6�"�&����b������0�j���I�YQA�}#��nM*p+99���/��|��N+mK�`k�9���H��&Ya����3�pg-��z��vj$�d&�@���������x~q0��E����M��n�����sN���^�I\\s�����E^��������"v��zu��dP2	o�dSo�Mj0"u"��5��$O���p@����(<��m7�8�������
���$�(�Z:��%XC-�{�	������.���M<-���"�Y${�o���EK�.��T��eI���#�_�3K:,���������������d"h��u���"����\�}��qHXWiC�S�Q�0(��(s~�����G:���6��/���������e#Y�R��Um-����T���j�{J�{��T�����NsOsC��?����}8�90�B_�:����Hj\�`�V�W�������y��|^>/��������y��|^>/��������y��|^>/��������y��|^>/��������y��|�������p
#358Julien Rouhaud
rjuju123@gmail.com
In reply to: Antonin Houska (#357)
Re: POC: Cleaning up orphaned files using undo logs

Hi,

On Thu, Nov 25, 2021 at 10:00 PM Antonin Houska <ah@cybertec.at> wrote:

So it should be ok if the temporary undo is managed and discarded by
individual backends. Patch 0005 of the new series tries to do that.

The cfbot reports that at least the 001 patch doesn't apply anymore:
http://cfbot.cputube.org/patch_36_3228.log

=== applying patch ./undo-20211125/0001-Add-SmgrId-to-smgropen-and-BufferTag.patch
[...]
patching file src/bin/pg_waldump/pg_waldump.c
Hunk #1 succeeded at 480 (offset 17 lines).
Hunk #2 FAILED at 500.
Hunk #3 FAILED at 531.
2 out of 3 hunks FAILED -- saving rejects to file src/bin/pg_waldump/pg_waldump.c.rej

Could you send a rebased version? In the meantime I'll switch the cf
entry to Waiting on Author.

#359孔凡深(云梳)
fanshen.kfs@alibaba-inc.com
In reply to: Antonin Houska (#336)
1 attachment(s)
回复:POC: Cleaning up orphaned files using undo logs

Hi, Antonin. I am more interested in zheap. Recently reviewing the patch you submitted.
When I use pg_undodump-tool to dump the undo page chunk, I found that some chunk header is abnormal.
After reading the relevant codes in 0006-The-initial-implementation-of-the-pg_undodump-tool.patch,
I feel that there is a bug in the function parse_undo_page.
According to my understanding The size in chunk Header includes chunk header + type-specific header + undo record.
If the entire chunk spans pages, also need to add the size of the page header.
But I found Now only the scenario of chunk header spanning pages is considered, and the scenario of type-specific header is not considered.
/*
* The page header size must eventually be subtracted from
* chunk_bytes_left because it's included in the chunk size. However,
* since chunk_bytes_left is unsigned, we do not subtract anything from it
* if it's still zero. This can happen if we're still reading the chunk
* header or the type-specific header. (The underflow should not be a
* problem because the chunk size will eventually be added, but it seems
* ugly and it makes debugging less convenient.)
*/
if (s->chunk_bytes_left > 0)
{
/* Chunk should not end within page header. */
Assert(s->chunk_bytes_left >= SizeOfUndoPageHeaderData);
s->chunk_bytes_left -= SizeOfUndoPageHeaderData;
s->chunk_bytes_to_skip = 0;
}
/* Processing the chunk header?
*/
else if (s->chunk_hdr_bytes_left > 0 ) s->chunk_bytes_to_skip = SizeOfUndoPageHeaderData;
------------------------------------------------
Should this code be fixed as this?When the type-specific header spans the undo page, the page header should be skipped.

else if (s->chunk_hdr_bytes_left > 0 || s->type_hdr_bytes_left > 0)
s->chunk_bytes_to_skip = SizeOfUndoPageHeaderData;

------------------------------------------------------------------
发件人:Antonin Houska <ah@cybertec.at>
发送时间:2022年3月29日(星期二) 17:25
收件人:Dmitry Dolgov <9erthalion6@gmail.com>; pgsql-hackers <pgsql-hackers@postgresql.org>
主 题:Re: POC: Cleaning up orphaned files using undo logs

The cfbot complained that the patch series no longer applies, so I've rebased
it and also tried to make sure that the other flags become green.

One particular problem was that pg_upgrade complained that "live undo data"
remains in the old cluster. I found out that the temporary undo log causes the
problem, so I've adjusted the query in check_for_undo_data() accordingly until
the problem gets fixed properly.

The problem of the temporary undo log is that it's loaded into local buffers
and that backend can exit w/o flushing local buffers to disk, and thus we are
not guaranteed to find enough information when trying to discard the undo log
the backend wrote. I'm thinking about the following solutions:

1. Let the backend manage temporary undo log on its own (even the slot
metadata would stay outside the shared memory, and in particular the
insertion pointer could start from 1 for each session) and remove the
segment files at the same moment the temporary relations are removed.

However, by moving the temporary undo slots away from the shared memory,
computation of oldestFullXidHavingUndo (see the PROC_HDR structure) would
be affected. It might seem that a transaction which only writes undo log
for temporary relations does not need to affect oldestFullXidHavingUndo,
but it needs to be analyzed thoroughly. Since oldestFullXidHavingUndo
prevents transactions to be truncated from the CLOG too early, I wonder if
the following is possible (This scenario is only applicable to the zheap
storage engine [1]https://github.com/EnterpriseDB/zheap/tree/master, which is not included in this patch, but should already
be considered.):

A transaction creates a temporary table, does some (many) changes and then
gets rolled back. The undo records are being applied and it takes some
time. Since XID of the transaction did not affect oldestFullXidHavingUndo,
the XID can disappear from the CLOG due to truncation. However zundo.c in
[1]: https://github.com/EnterpriseDB/zheap/tree/master
execution, so we might have a problem.

Or do I miss something? UndoDiscard() in zheap seems to ignore temporary
undo:

/* We can't process temporary undo logs. */
if (log->meta.persistence == UNDO_TEMP)
continue;

2. Do not load the temporary undo into local buffers. If it's always in the
shared buffers, we should never see incomplete data when trying to discard
undo. In this case, persistence levels UNDOPERSISTENCE_UNLOGGED and
UNDOPERSISTENCE_TEMP could be merged into a single level.

3. Implement the discarding in another way, but I don't have new idea right
now.

Suggestions are welcome.

[1]: https://github.com/EnterpriseDB/zheap/tree/master

--
Antonin Houska
Web: https://www.cybertec-postgresql.com

Attachments:

undo-20210909.tgzapplication/octet-streamDownload
��:a�}�{�F���*�=�7eR��v�Z�mmt�('������1IpP�&���_�@�$);�f�H}VWWWU��^��a��4����_~��F����4z?F�M���0���m�^�-������>�I�[���P��/kG�#��/�o�X\���m����}z�|�V���\��o���������Z�BZ��z������h6:�v3^�fK4�M����u�����������{��[��=�,�2�N[v�?�-x��[Vo��u�h����cg%��h4^��	������e�-������O�������8u����f�f������n]jO�����FOT�������?]
o�?582Z?�J�X)BO(���K�����'��#����[b��b�<�O���7��Fi���0#ho57��7��}gn�����u1>-L�v�p��,l9���P�6<tg�b:��O�>�&�s����"w�,>[�J<�W�w�������?�j��xF����ba�$n�����k"4?9"��gC���(f����>��X�����
"��D�B�X�����_aOS����{s����� p�s��`�g8�~&,o�^q�����g��t>��2`B�c�������e��S������0�ha����`�X���DB�Pn)��#�u�K��W`�����.VsW�W���sh�_/q��/��%�����b0%�&��<��[��8_/}o��
�u�Sz)��p�<:zxx���[�	�6�{����	��9t����������w�{'��b������?�~o���?������������'?xP�����c��0�y���Z�b���p��}wz8�-���`�����U���j���tt�������j�z=]%�����7�0yY��r�a��i����:�Z-e�g�Jy���K���`Y���=����C�o_��u����b��.�QF����J����>2-�����w��g�y��&�'vN�[��g�p�Vq� �?��;���m����uy)s����������n�����c�H�sE�E.��P+����h���U#ekA�|��wC�k��u9
}���9�����^jC��a\-���k�h��#MA6�a\s��}s����.�^/5Z���x3W�V��Hv�8l�\q�� ����~�u-�4��Qz~�U�V���/I�_�]ka9� ������cG&8O�l����u��9���x>�)����;��G������&"���gd��{r�����7�'�'�&��[�,���rG;w9s�v�Z�P�P8����z�2���;	"���=�s{�Xi_s�3�a�*����(\�v�]�*����r[ek"D���xS�ka�^<GUO����i�*�vsNE\�LwiDK�
j�C��Y����W�����B����c�D�;���V���������O��a�z{�6	�[qBIVl�v��><�u��?*�/����[�����v
�0�z���3��Y�!G��kI���^e=��O���
j���s�v��*|�\K�M�X8{W�hc'<�s@8[/N�$�UNh�~A����|r>�^
��&o����<x5q����Z}��(�V���8�8���������y~C5hgoO�
,�/^����_;�����D"|Zy^������_"%�?�����vPb�����������);	��^�cP��1�TB��9�[����d�=��t����OE-%���bWq�[ f]����x-����!<����������J&K��o���������$���~UV�o{�BT~���H��e�T���z���lt2�(��$�C H U{>�+P@�
����.�.���a�.�g�m��h���[PAbW�q:v���L�����N��^��
��.E��JD�jp��?�i�����T�!��h��K���X���u`.,����P���Y��<_A���MT�zf)�]S_��7�x���8��'>�x�~�B�g�5�{��)��u���������}X����lx��#!q,����������^��#����<�R	�p�,]��1�Q�W���t�CZS5�f��F�7���B���<y��re��
�s:�s��48�qHO�����xV��G����?������::h�I�HC?��`Il����`1��c��5���`S+w��=7��'j�6��j�xY�w�i��Jf�O<6���EO��7�x0Q	{�@��>SX�{���8I5�~����}��H�@�w/�o�-)-����g����H�4�����80������r�L��d�T�������'��������G�w[��@��A��i%�Iu��tb2<�]k����������xu�[��R|�����T�����qK���r��V��0��	���o�Ng6�N�p�D����Q>c:�*�-T��2uA���7�gs�.�~ ��f��vf�zN�kc%��o���/���#���������p�����S���CY�%*Rp��&��	+�}���*/W�����`fMa�f��v�]�	+���
Fuh;53�/��4���d���?�WX\=`�'o?�{7�>M��O�O/&p����7��R������MEU1���������hr>|�X�T��ZWT�/	z�B����+� ���_�`�k��=>�P�[�A�j���#�]�PdO;��������5�Ak���%0Ju�L����(�����X�����8H �������y�X�cO���o�f$��EO���<V��5Q��Ag��0j����3)��
�M���B �A��.����L��3<�w�/o
�j<�fbT�����*�04���-������9-|����+���:��_kd���'�yl��D1��6<�����<�^^���`���d�wY?Q7�����/6K�������5��/[N0��$Q�~]r�`�]G��<����"�����F~��P��7����o�Uyv�2�GF���:1�N�L���3s�#
O�I��Z�^�]�>�>����$G�����?�?�#���4J��!�������mTsZ�"�k��`��m=>��t15Y�����B���5�6�z��J�G�o^e{�u�{i�kM`���n�9�q5P�~��,�W��n��^�����r����{�� ���k0j�����#�e���|@ZZ�
�������~�v5z8H��7���?�!���O��$��SP����J��J4���.C�[X�q�@����0�W�X���%F��h�n@�L<�f��Z�6�0/9���'&���?��V��:9���&'���?��D�n;j������I�XN�����R�0;"��o=o^��<����>i"���Mo�F��m��v�H��h��
$G�N_N��,T�<MN��#<R�NNLkp��iS6�?jo����}'\�d�(�h�#P��M�i�M%�:���m�3�[�z��Z�v���6����+06��K�2%��`���M�7�9�;��WO}�~�W�'���wTR���y�L��L��M���7�b���NHAf�������-��@�������L|�z�7RQ�������kR��UC�^��s	�8=����;��������a=�E������k�'�[�v����C�1|���9�t�Rg[�2Z
�����f������H��6�1�nS��8��W�������w��jB:S����w{M�/cb�_0Y8�m�y�`�W>�'�@�.�pu�I�Y����>���R����}
U=��������b�ODz���S]�
4��������P�J���qe���x)����h���.�"�Bd`9m�k�����w����mD�dS���,�h0����?�������S�La������%ug�9�15`iP���]8�Zz5AF�/|�$(����*C�>
�E�m,*��@�p$�����8v��T��AMx�Y��5>^�����N/����R��PG��$��v��`��#�Bt��D\uOrG�X�����p�C|S�x�&&�e�����_�VZ����2����{f���?]7\~g�!�t����#�O*!�.���&��R���~��S6��,%�zj�v�o5�z�����}�tz[`u���N�D�n����}�� �����JV���o�Q���4�VRT��
504=�^��R������&i*ER1WM���P�\8��0<��B���IU�0l��1j�x�{�6URk��vY�U8O4�A��J���������&
)C�v�mM��Q�7��e5{��`{��LR�I[5h�)}H�C�z����5U%+����%�B(9�@B|^���cJT^K�yu�%���4�{�<����k�?���xi��;�Sd����N�����b�b=z}b=�C�4��n�B*���O��$�`3�\Q����F���L�����xl���k�p�H���R�&E8�B#�x"�d�	x-�=S0ce��I6S�������sZ�1�����������5S� @�Y��%�h���Ik�6��[O�~N�R���v�3��60�F���vfN����dZ+�#���������%�	r��k-C�B���9��V�(�a��S �@�Z�T���K�B���Gar[3�%�����,����Q9o�������6�
*�9*������^x7_w�KGj�q���Ew}���r�=uCl��O7CP|��;�3�1��t�2�3�{��(K����<���I�����;�L`D��a�u�6��`-Acy�����Gn <L���|#�P���}�,�B��xt��������xryq�g����=�%'Sr4���[��b>��Hw�;�p��d�]�9���
q��%!`a!�^��
����v<]	�EN��Q���&�h4������7��z1�������b'�&3)�?��\�����e\g����Z��=�N���knV���|���$�6I)��s'��H�b �jpR��jh�5�3����y4N�7Ii����tlZ�/��y%B�^������~�vH���#H�i`5��F����p0��k���)����������j������X�m�{y���h�L����p���,.&�'�"�UF8��3+������������"���=� �
R�������Q�n������^�c`���f�9�T1�'���6�}�'R:x��n�����-W���7�IL���PJ�-M����r ��O���
F#�b�����p����c�������d1_�2X���`&��N����gj|>�2F|�G����R������c|\�lS�u�H��*[
���8���G\~��b��Z����k��p���NgZ�s���-v��X��=�W�2��y������d�����������	!S���S��	Q
��O�-����|�&~����?��y8�"����6j�n,����#�p�LS�C���L����J���H�o,���Ry�&�*��9�����������#���L��=���\
�/8=��Ay�h��,?P^�������N�,�6��_pW�����B����u�>�Q8�-��] ���y�]u�z����������m���a�&�(JO�gg��O��o�F�����ztr �?g�tyU��	���b������,H���F�N(R�k��+�9J��E�	0>W	��2� �C�X��i��[���t�RN�%�py#frY8P9���{A�wy��T-�i�����L��m��
���-D��g�4^�|���9-#��w�'86�d�6���e��r�n�H�:w���R�	�f���&	M:?�gn���������+l�����`�����4A<���)Z���]�C��m�D�8�5IA�A��29�X���h\JK`�
��z����vbo|~�Gu�x�9�	�%�"2FU�+��y9B�l�(xIiM�S��G������v�B8��7E9r�y?SR	g�`K%A2\��$�)|�w�^�;��������-�io�����!Y��H�|G���j0s!��'S����
i���yaB&E�APf�uP���r
3�B����*K�Y��hO�
YW{yo�������%�o>0�$;��[M�^�o��<Kz�������{�T:X����W
������M��m�������B��a���v�����~����n�@��
wO� �Y��q�e�k1
�0����h�TzZ��D;���dcD(��T����Qm�)�h�|P�a0��X,��������w��v���JQ)��@N����M����3y{$�q:7���h�i[�
�>���h�A������cY�HY��S�����0�����bR��A3�g	��RO�&L��`]������p%8ywu*�	$��8(5;�$�U�M�+4����)L,@�]��3e��[n��m��nX��1M���}�a9�������=h��&��i2%���@T����a�"@k�
	�U4���x�[[���a�^��R�������=h;����xj`�|���6��^������������D��N$w:���x2���E��L���u�-��9���T��������\���F��`����r�NXV�5hO
���\���N�f����
)ENY�io��GJ�`�3GL]�Wd��Bq���a�~�8��$���>����{�����Yjl��O@�u������J�Y<���
t�F3�Hg��;e}�g3*%!}
�l�`����������{a�Ek����|�����_�/$����������C��?�����P��������L��
�L�����M�l=��v���t��'�&N8S����Y�2,��t�i���W�O�6���#U�\����Q��r2�����J���N��RI��\[�(�L����#�90?�VP���,LN�pg�WU�i��V�1s�s�q�9H}����%��q��VU�U�H r�Op��<�p�b�s]l����kA���O�*Y�ZFH��h��K�_����������g>R2��x�p�����;gun�M�\��W���>���N6M���n���67�o����������K��{���ot�R���D��E�e{�/�m'��[*�dB
G0�p<M�"�6����l��RnrO�d��c�=������k{�����\���S.U��y����a�I�N�uWg����V=�?<��������v
�r"u2�zM�tq8$p��L����.m��G��$�k>>��p���-���38�`08�D�)��]� ��)��	�|��
�I
��+%R.���vi'r�O7]�E-G��).����������0`-v6��y��9�u�g���mf����G���q��Y�A��PXk��H�2&1���+�pCJ�([A�|��%L�����(�/��"s8vE.Z�X+\ZM��zLY����������>&g��c��?$�^����"��:�L����=����n�3�21�)|E8��k��F��~e��7���9�����.�L�?���}��Z�Z���`���5�����K�;�F�2
�EDA��1�N�C��/�dtP�f��k�pP�Z��Wks����:.j��y�/Q�lf���.�6��~���s�6&��7���|���#�?Y�z����w��j�6jV9,��.�[u��JH	�Ha\��Q�[w����V�B�v:s�y��O�UiwN��$h�����6����w
��/-�	\�^H���7�=��[�U�}��cK��'����}�#�p�o�vS�y���������\*o���l�jR�D`��1�f��/|d�&p��
���$S�����PL���a�����L����*��_�xon�}%n'.�&��}�������92:q�9����-������@@�K��`���v)d}�0����|RP�w�V\��Q4�M���}y���+����$+���u�.���y���C��R����u�C?�&FC����$��$,S>���2t�a�W���T7�.n���3�9c�0U3	�_xo&��'��X��Hhi)����fu/�-^�8�V������H��k�	N	D� �2����R����P�/�������X��u��!C��]�#�5�2�K�K�\;/�o�B0AW��\��H�\���Db����<h��
>���j+���������iq��q$����mp�z�i5�(�>�������g���M�����zt���g3��^��$���Brr`8����V���������k���7�0�<��,Y\T_����
���u4�J�����B*]��4��n����"0| �F2���A(eD��(���
����{�tl"Eh��Ft�
�m��h�"]k�+���cB�p�#Fd5��SE��.-��l�Z1�<
�*�h/BO)��o�'���WeQ��������f��*H;���^���[,E�`��;��t� ��B?���������h�"�|8�����4�l���t�hQG"�b����`M��D:�X1V�|
@��Zqx��\����~����|��=y��i�M��2�������[^�D����N�a��A|�3�K�v+����J`�����v)�K3L������NN=�B��`�6��X��s��+�C�4 [CV����M>Bp9�CDQ��;t������h�'��'|��� 3����y>�7=:t���?�?3x��F�J��l%RmF�mQ�:���*�F�/IX�v�NKL��I��u�K��KaR�$V�,���m�y�(�x��P:U�k� ��Pg5N�L�(L��SJZ�z4��h
�wC�2�"p�2����=��c~qL�?���7�
(�D���C�5M��Y�*v'C�'~J�o�W$�ej�����%���\������Fo�J*�k�k;F�0����iZ��l�!Mn�[���������@
�'p��N��g����u\$��f�������n��<��0]���J{��G���<��^(���;^�9��~xF���:���d��<#�X����=�QZ�~�h�W!>Ib�h���|t�B��<������hx2���;��Tp���2���FaeO�Z+/�������_isS	�`��>%������M���&���FZ������Q��Z5�"�I
!A���(�]�O����eP3Z����/�|�%Ez��/�GP��U�!{{K�`x�{�?��������7�|����Rb[/"� ���`�E
@#>5*���8jZF������%��S�����Q���V���J ��t_��o��H��"��d���(���/^�K��,
�!�����/P���o0��r�vB������G�gS�z��XIj�JWvg�I� /b��u����:�S;��E�R�(>��^��<���U2��iL��k�>����u[���`��V'�*L
�)��v��d����h)�w��m/F�[H����>�1��g����1_� Fhv0�m���y�TL�-P�g+����RP\
/N�)(����I�.1E����3��Bj�����'.5��8Z���)E�V��|��d�SvT5�i�^<�r�����3c2Ys�\�W�D�������r�?�	��x5A�G�L��g����c8F�X;�)*�	3���L�il_���1H�������#�8�%�W�H�$m�^D�I�Dl(�����Qu��R_/	H��;�/$+�0�|�����Aq��@�f��������k�I[b�����s�����3#��_�U��(�6\�q���/��[YxQ8�pI��R��:����u��
�s=���HG?�J�+g�2�		b��;s���Z����v����S���eSH�SGw���C���b�qd@o��M�9G)c��AX����N8+,��IRJ���&��q��o�5��/�k4&�x������x���9*�R�����[�pD�Ed��\�����Y���t��3�w�Y2�s�-�<�Dv&/�����rc��0����AIL����C�G����"��R���Z�1
�\y�>�3i0�����$x�w����}�<	���V�lO[0=�WE���u+q0�i�db����y�\�;�Y���z}`��~��m�D"����5�eXH�lm��Q~�LI�\6�H�T���*\Z�H)�tsL��&q���X�����A*��l\�8�.��H�b����iH*�FK��L{����>��H~�C$�'o&#Xx�G�D$5/^l$c��P���
�����P3�E��RJ��(����5u����7��s����S�~H���wK�.o97XP������
)�RU��P��Z�7�)|��E=��%�f���`�J�\
��
��j�9+rMx���@���"��dLIHML5p�+�r����f3X����U�����Q���K�Y!\ ��'��F�	����Li�s������D�E��w`�B���%��R^�G�e�.*� mi7�1I������
�,E/����%{S ��m���`d��R`��D��XY�����f�H�j�����Cd0�.%�_�e5uxs���xx3:��]9Hi��l��`�����:�=��?�G�
��d�H���VELRZ �\TX���������c�����#J�l-���1��<����tM�`����um�Sv��5�s��S�t�����CIa������#�>9��?K�Q�Y�]��z�>7�9?�I�Y���*9I����F�h����3Z�f�$,Pi���/-�A���r�d	�Y��.g\������s(^X�g�zNZ$�z)�1����N!�a�E���)������o�A�Ks�m�>�H������Q��6j�3E���HS�#�u|��8����	-�!������06�[��::����ZP��g���l���Z��C��3���8r����'%�||"������f��ca�_<f��S�{c�N����_�,os��j���^�L��6�^
��7�^Y@�mZ��(N����^�����P�����<��d����U�p3|+3D[�=�Tj�A�4�E7:v���:�p{�X|�g��|��^0Y���R�u3i���������g��}����#+���[����;K�u�G�%��vd�=������_;���7Q��<�#��nlO����qcrK�o�v7]�q���@n��}��KK����M�:�##~�3�����+S��uZ�30���=kv���/�$Y�Zv��k��eTKF]K���8�%^��!�nR�:hF� d�!��8��o�/��$J����[/�����1��
�]�V7���L1DAFe���Dm��pi����{n'�Q��W����������rZN\���$
�$b���AI!Ja	��[�t'����WhZwl�,��Y�	)=�-��������%%}L�u+�DCsI��z��&���,n��Q\�m]������e%R'�bem*c1My+'��~��AT��+�� �����.��L.NOT���A�8]���/A�@�m\;�������F����5�n ��X������������v�^���{2�;D�p#@��n��*������O�J�����6]�����s���/��o���������6o���d�������kBH�HSL�������sd�i9�@>�"l�
[�����{�m����8����H�$k!yfS�jG�{�$v�n�r��UI��o�I5����n��S���7�O�l�[�\�V
�&)����-!�l~;���>m���7�6E�Sv��!g�\62C��5)�����<CN�*�\;3��j������-��rsD�����?����7XN9VQl��|V�=�,GF7�����P�����,�}����"/^�U��N����|��'�F��]]�^�L.F�������	h�|�i%�����~t�7	`a�����KjNL���a�;p1�_�$_��i�>9XY��N���k�����w20���_��%�N��$�Uv ���|��
z	���4�g0%���[KF�8*�z����m��sS�����-��������H�#�.��.	�-1��^��}>q�F����S~��Zvu
�� $�����qz	����O��!�S����@����8FL��!����s�c�@ y�^&�"q8�6V�0���V��$�k����$Y@�W*:Q�I�U��E?5y��������;��R#Iw���v�wa:���D��2�j)�������x4��������L�wg�����&n;Of�^����,����Q$�����T�.i�7����o�mrr}y5y���������������"���W��E�i�����*[���9g�O���sy�2QQ1XE�'$�X$�wI���A�p�n��f�)�
r�"�'w9^h�A��8�@������y�
-�(�N���p7���s3�A>I����w��=^���R9~�a�I{��J��4"c��o���;�V���J����+�����l�?�UW�/�����SS�!�tT�Te�)<Iy}8�@���7;_0�`w	 ��O�K������4o��JEr�VY��	�<�	E�<�@92��EO�.��%��9H&hJ
�4���6����k������A���T��������GU��d�E���!&�/������+UZ7j�����<��H(�VD�|0�����+i�����7^IAQvG='bL)GP��M�_�Y����tu�~kG�#R~AT���k|k�_��cY�nS!"��]RV���D���/�92{���Qd)x"����,��<���l�l��58EiP��0"���%Z*����Y��t�7�����]K�=mu:}�l��Mk`��Ac6���Eko�m�V�t�������&�H7_D�b
&�������f��������{ZggD����w��\j���t e��S6!v�����<bz`������}}�	:���V�`"�$�UIU�\+
l�����RaGW`z������+AR�A]���3_{%�;7
\z�Fq��Q���|�S;�7J�@���1)V���c��V������<�I�r���R�E�f���e���$��Udy`��F���<W��m���K�E��,&�����j�h�?�
x5�X#�3�1m����;f?������
*;Ww�~���������I�i�iYS��Y�-\����3��#����������P�����PN��`���H��%��F?�}�~?��(���)����b��,VR2�U�t�����}��o�f�(�UQ��V��� �}d���I�e4I9�%	\"�S���,����z�������-M���(�hAf�l|DzNZ����V� ��<�rkl�Q�4iO��8�������g4�� @��,������e�RWDa���'���_R��E�I-�B���KL3�i�L�����i�v�9�7cZ�N1~�edJvN��X?BB<HEQ��9�g�N$�%��0�,;f��YE����TU���4�.k)��|	�P'(C>d����P�����bLi���mh�Pr����j�zO~������L%���u�����������[$G/lt���8����KB���.��CY�����Y_*}�X����z�M��=�&_�"����x��8�y8��Q��@����N+��������A{
[�v����[�-�T���N/�������uh{K�*�b'fH��nI���G�����Zf�i�B���$�{*"�	�?D*��U�n���F����e�\|���o�j��N�c���_�it�����z	W]��8l��	)��Y�J�02>�^E��
1�A����JT��r��&{{"�3�����
�Af��a�SJ�A�b��@&�����Ng)`����Y@���d)Z��R�}��/��O�f&�a� l6��w��hu;���CG+��d���*��5>=�k��Vh��hy�Xa;��+,C������M�?�>ld)�U�e>�S���D����_ �]5qoT���[7�'�/��AE����q��Y7��'��I|�����<���s��o�^5�^��N8�GJ'�����0������J����������0�����m`,:�9(����2x�[��-��ka�O������������rK�`�,�T�\&q�+r��d�e�k�\=���bT�^��r��YW;����Y��f+�=5$�27$���5�|15c�U��s
�L.�/N��e�7�ZN���~t���a�f�����Of��$~������hoh 
�����T�q2Y!v���J��3N������^?E"a�l
�'��8���o���Z
�w�i��e���u���_�����o�j�hv�;YJc�S�l�D��9`��5_��
��%���^I	y���o��j��������s���Z���R$LpV%������Yt����=�}�����#�D5[���de�6�UIv��d���kF<��cu���V!�	c���	���G�JXc�����5�(�,����Yw�i��ZP&�+��
�����H]�Ia6F�+[dqc*�g��1�T*>�/�M�N����
��6�.���6��8�V�1=��5pB�@�'+�\+F*?�\��H����-��MY:����|��7�r��X�T��x5z/Qy$�;�aY�}����K+����^C�f�� ���E�����lw��D%g�*���v���[x��z��m�:��VgH�R��"�4����3
vfH�c�����v����cpaL��s��`����c|�@��:���1q��dV�T32D'Q���y��I���E����H��q��l�_$��<�f����J?�z����
l��8��&���F6N��l��m��kVk���F��[�;F��Ir��/���W�
ao�lz�<����
���Kv�, 7����-��lZ������h��V\5T��U!���L)r{nk�E������<_���aE����Q6��=�e���"��s�x016�����cS��ty�^���������sD����q�e]�������6���V����������=���6�w|��`{����PV��74�m�(ED�{�����(�Sv���-�L�mJn5i��@4);�5l�P�����J���j�,s���d�j4�^/�`Sk��]T��H�����l�&�/��y�fh�fv��sw���
�,t���H��q�|��a/|J�9s?s�|L�����#��]�.F?G�|<�����P��c
���9�Id����sh��2~��3�B
jF�����Pj���0��#=�(���i�	Y���e:�hhC�6s�'��=@,�����U���-���W/}�n�Sp[��:���lZ�XG�B��QI�n�c���Ko1|������o�;m�����:���l$���}Q�?�=�Q�|��Lh5,C������)J����TV$vAM�a��6g{�l
V�ZS�����>�{�PA e$j�>f�O��$��R���������n�L�+Q)���L�w�m�V���k�����A�85_?���cL4��d��g0�i�d��i���s���dn�h�5*��S��3���Z�
\D��969�`s�K0����fZ��bR�<Z��m��FVl���������:?��6��[���V��g��l9��L�atg�mrn�
�)=��a��H��M�o����UD�_Ks���*#v�#��d(�k���,$1C7���I6&O���ob��������I:q�y��#��%�8�n$�|�z���&�|��i�mX�L�"���B��(�PQ��Wic��/i���������A���i�	��:��#��e���7y"$��"S����0���U���^����rA�,V�3Y� ���n�,��*��H��4����m+oB�P��n4�s��*��?pr���CjN�m|����5�}���|�q��I���H69�������A�������Mg�i�����n��t�n����
6��A|������l�_�=v�'�b��P"�q+�d:H�d����p�!
9����A���l�J\�&#�yh���s�� T���Q��MVG���r�.�0�a2O�O�z��F�na����@S�Mq���,��8N��N�a���>=��iuU���n�������
�����a�V�>��{6hY9���Z�b���
;(�m+)���F�G�z���T�i��G�i��I$�vF��r$^�K��rx1�����"-AR9��	�N�.�rV6A��0�	(��l��h-C��L&<�x�2c�rnT;�N"���VO�MG@���`n��3e����#�y��C�*�	Vy�'
��l�9o)?�vA&�)kJYeZT����r^�k�cJ$u(����Q7��������������4G�F�y8���`�Z�d{$��v��)���j��L�@���A;_�����'�k����0���m�^�%��a��E4~�yG��x�C17�g�{���>���{�����J��h�,��3lX��c���i���^���
q$a������K�O4��>6�R���t����>��[��O�#�t�c����>�\����[����A�F�e�����m�����:V�R�t5�9��pd�~��Bb%9�X�|����7^Tfd�C
���q����{�S�--��fyK����Io,��e�oC��1���.�H$�?���g�-G�!�~{��	S
3���4(G��C��l�K&T�[@�-����lx����������WYG-s8�.�p#D��T
�YW�598�����
�.V����wK�A�rtx�p�
u���cc�<J'��e���p��Q"��>�����O�|h��=�������'������`�*�j�m_]<x�5�( ��S�a}�Aoa�|���}�M�e��_��z�*�?��
�	K���L���=�c��Q��z��e��.��X�U�E6]��
 [�g�)1"gR�_/�zh)Hw�W7���E$1z�*��R��9������� �t���;�e[�^�����F��l�m���o�`�I��0G����*�W�@��t})H@,�F��><�E�Z�
f0:�]�O�7��;8�>^�.ni��U��(�|"����}]�]X��}"�t�	�n(�RW���f<���V���}
��G
J�_�#�<D��Z�'�fW�N���i�^���0{J
�/[��/C���������>%�G���^����l6���M}��������&��/.������pi+�cc+RCj{��w�hm��ZM�m�p,l�a�������;h��H
�;M�O������_l���7u��!��EX����(���Q��e/|'�qk48�-���:�FE���e�Dr�B�����=P���p|����W�K& �
 D���$[�?��
2�4��)����������-�
�������q��>b�i�$��HE�
�	�0Fk����]���t��
t8aa�@l����������j q�a�����Q���J!iId�u?=��h.&""m
�>28&`��t��m�aap@�c* 
uuG��!�A��|��0��G��L'B��*���c�8W'�$b��	1�A��:�:3��* �u����M��0���\�.x	��")�	D�L��FYHB+�GO�KQZKX|�_h��m�a����W�]!	E����@McN�l��BaP5�w8��)�u?@)�R}ru�m��.���(A�A�$Z�c�(���P���s�&�2{t�/	�A�~���!	<�z��1�/[�b�d�s�������l"�<8����$;7Y���p���`B�%p� �$�A��9FI
�lu�E�f	��[�����0�Sz��x��9��r9�3��)$���|9�&�~l�ID���M
��)��ft~���7=��x���Pi����0���T,$��?��nh�O�.�i#2�`�-�xKt �������c�$I��/K��'I�����l9G��~�'@�"��y���-[��:i�,E���P�Y����H7+D�q�3�������h�y���
EA3�%�6!HY���]�:�;��V���0��Y<��7�O��$-E���Wn����e�8?�o���7	��[�}}z1�������&�������5�s��������N������T������)�����{Ev}~�Bi��7�Y�H�+���Z���0jC��Dg���;���wC��#���RF��q�M�.O%?.W�2e�L�����K�i���o*�M��@bm��Y5����H�kdFR;�X���\e�Q>	��2�0���}����;�-~e��������������X�O���x���������_�E[������x�~t|�*�����9A����_X�=G�7x����������J�U�	�#f��:r%��|��t��W�-�@��A,�����T���`��S]9abX����3Wp���C��ZS5Qt@�\]U�F<u��0?����o`:b�=����b_�'&�
�m��]Scv��h	���nb�������������:n��q�{��MHv	��s�= 3���.���yx��Q�%�v	U=?�*�
���{K�����@��Qf�j�Zj;�@} ��J��t`���o��e\��+���q��j��gdr�����7�����I��/�T	�����+�d�����	k&)uX57�S5/FwU&*�!�Dq��D4�MQ��@!z�o���$���$!e���%��%�97�����
�#	�H5���F.W�����C�D�J���8�a2�0���(���M����hkB
A�(U��5`�6n���vCb&Q
c{q�	������#�vB���O��-"�����^��&���ke�	�������dG���;yb��{��K}sF^`�@���O`������@q���.�S��K��}p��w�!���a���dP�c�Gt:c�7\��[[wuy�1�����������Mb���,�5M1I��^�pYg�i6fz�p�k;����g��s�.�q�����DdA�7����=p���w��T��>���X������YW��z�7I^�mp����(�IS=�� �X���`������~�h';�N���C����X�!���
�(�FT���!u��>����JQg�
������I�d�	�Te��NE�����TF����k������<e���yz�Rt}�������u~C�`�9L/+r}B3���E�����t#/RRs��3���l��I/���tH��g�Y�%��7�'�T�%�R|�xC�a�*�.B�r�^!����Qb�L�/����h��@)�n�yzu;1�aB&<r&�g����[M��DT�Z���	 ���bxH��3�K��6�"�^���0��3��8��Mk�Q�-���}��C	����*�
E�;1r�,m%���1r+M�Z%���'�S���'���`�3p���]dV���%��]E�z����YC��|�9�O�YdU�Ls[}M$@!���b�T)!U��2Q��B�KjCb����c&w��/��"�O�;�~�kT��gf�c��'N�r�R������G���D}o�S����zB�~�DOqr�z�����s`oW?�������]��G��{d�����6G%�u)r�s=WO�PW���p1c$��J�8X�%�"AcO>�u���,��,4���ZT��$�9���a #`[�6�/�X^G�Z�IQ��*<[������Ph��s�����0�G&|���p�u�S7Pd�\�Z��?Z��j�]�T��r�D��<���0��$��C-4i/��������/n��7*����OA�{��V<amo}{W?H0�g�9����?��^"4�V��zal���I�\<	�i����	�/C�<���p��_~?���v��� �ob�R,jFF��EP�����r:r�&�e���Q��,�9K��Q[C-6�G���K�'DGrY�B�y;}���Q�X@��'p���W�G����;M���3s��;���69#�J������%PQ��[�tu��]dy�\	Q
D:�L�&�l����8&_�v�L�����`R�(�c���Ia���������	%��2�;��[�?|��g��~f���)��<�f���V��$g�0�]����Y�C�
�ns0��W�N���
�(f��c��N,��X>��w��� NyS��+��@l���0�7��[�.}�o���n��=���`�(m��t�Eo9+�i�h3Y���J5��K6����&����e����,�������
�s�9�7l����������m�7����M�X��7�������a�� �i�i9nu��
�$�V�����H��=��F�_J��$�pC~��{����D�����Z1������`z�z�3�u�V��I��u�u�X���q���z	fP�
i�p�el����05M������N��r�
Q�j��4��u��(���4�P1>�o�M��TM�'�*h�C��
��rP�o�X��E�:qzz�w�w(�$6"��H�+����[��~��Wq��z��N���t����j���G��R��(&F���
�'��X�������YmoO�~����)#3�[5������ck��������H����TZ3���T�����l��Z�I�������WuF7(f���^�uZ-�T���lN�1p�������M��}���r��Xs����y�|~�6�d_�I���d�����&�I���*�������z�������%�^Oy6���O1������m�*'���Fd,,��jb:��ax�{���N41<������	B����/�Up���g��_"�_jB�hN�������4�6����]��{q�;R;�k������%�W��0��RD���|^��-����H��G�R�v�&��~�]�[�6��M�$�@�����^�AH���F"�&�yas�����	=�HL{GIGu],z&���<~59���
I�^RH(uL��I���������^��;�F�,	+���j�>{dq��z=��(�g"�=
��wQ�wW�H�k��ww^P�'���H�L�����s��P*Yv����W��hu{�^/�����������G���f8���#������wfF������[�e�]��O��V�	��3)���m((XO�/��@T��?��JEW����(�t�Q'wY���yd�4S9V�h�IF�'�|2���e�����Y;�������v�&$��~�&|�oJ��l$����'�Bb�ao�"�� T�C_���Q�%��C9����������h
R����+I���b�|���7�P�@F����toS��9�)�����\�[�t�E�M6�@��Z@�jFK�I�y��������8o��I��d��!l�9c�,<��U���r��	Y��m��I������t���8�����~��&f����%�-��w��H}H���KF#SE9�=u&#1l����Dxj���?0Z�D�!O ka�f@A#X�M}E�A���: Q�s��}C�P�r&h3C�W�z^�\8������������y'4��</d�[� 3���<���i~��2���y ���Ww����4tB�[g�.0��$���:��u����G��DqtBt�N 7@X���4(wJ��+�N`�+��za���9sM16��������O|0M��C�D|g�`+,��r���eu����K
�����wa�
^�� \q�����p��l�4G�����O>����������6��09���{���oF�}�sk��!����������f]�����P���?���E�J���1	w���	+�&Q���iB���T�+?�,��S4JU/|�Vw@�R����}6�0�_�?��l�c�eG��m������&��8P-B�V�p������jE�@��r&���Z���D�KW�K�������!U�������;�LU�7T��Mu1���xe��f2��lg"q���2�#f��+��N��-�����DN��E���P���� �
�����z"�HU��6F�;������!��t��\�[g�C������gbe���U�H�eg���m��������O�Jb>j������0�V�sW���m5@��a��d�@�KYJ�%$9�m���p���I!Q�,H��`�TrY&��n!fj���A�rD����/'B(�1W9�L7����N��pr5�M�m�_��gdW��-��8�]g�(�=�[
����9��i��N��������X�����]������`b��j���A�Gz�55�z�z�:�?�1�]��P�@���E�uS������N���g��n���M������T5H.<)l��s��O�-]�������.�9������'��9��r�
E�z�*����j�����j�lW� ���t�Dg�<��g��mX�^QT��k0;i��j��.��V&����g"�&fW\�.�2u	1;E�����V�������/�Xc����n�T��Y��[�YR
iC��Z#�����+�����J��Tk9��^I5���g�.����t�fw�j�R��% ��1�1��z��N�,����������jR���aE���!����=�g��N�u���z�v����r��N�w_�f��Y��-����W3DAo�����,PrSn�������C���^a�����eg�����m��JTM���W.3H���^���d��i�lnwE�C��=:�����R��r2Q���~'��at����F�H�oW�Ld�������������T������s-V��\�"O��B�^W�?vj ��l�V ����
K�r��f&b�t>�XA����b�a��1�V#b�Tm�i�'��YR�9MD���fg�h�L��7����N����k�f�1-��N�R�"*A.h���V�[M�kwn�.z��G���Pyr�OE/r@�����soj��"p������M]����.u�}�o!)���c�Ov9
}���`E��B����{�����fB��9�I�{��1%��&�+'A�X|����+�\8g�)��~�FG�>���v�v{�����m�p��E�����������au0�#��:�����>jO��<XY����~>r�8���mY�������%�h�?���V����z{������p�G�s�\���FH&�Cp6j���&�D�����/
;���J.�8 aG������W�9����<�8�V�>���X�����1�A	�Nt��q&��n'����Y���VZj�a�-]�{�%�q���u5���sF�tM5,��x2��9}wz<�9���S�~9d3_u����|���3t�_�O�g�/�QD
�k�z����C	�I*�eQ�JL��/�L�A�V�b��S��2k�{'�,�YT�%��W�����T9�\���0C��K
��n�����^S������K�<�
oFQ0������<3_|���*9�0`��T�v6��+4�gP|��?����t�����T�(Q8<�
�7>i��?���#��3U�Y�/g3`=���E���$������n�?^�^e���[�v���z`�i��b[����r<�|�������h�J��x����Bp�mQ[C+O!1^O�mT��H��
� �KX�M>�'�d�4��O���Jd���G[TV���V����41r|���pr����m����so�-#O�N�10f�i�Y�w����hu����6&9�c'w�8�/=63z���.k����T���K`�>oM����{M�E��wB�����r�+�R�8y����&��E���q��j�qi���5��2(��&���iM':#*����������q���5��;�W�c���m�b��W5�iW[���b�TGP%�uj"�&T=�*
�)~����_2�q]����x�2�LS8H�8����B��t��NODU?�f�%-�[�g[oQ���t������������Y������^��+�zR������E,�71r]?���VV��<�]O��	_]�\M��W$V����������J�_������T����j��C�����bT2s�*���s|w���:(fRr2NxuCF1���+,bhO��[�Zps[��Hn�x��l�-�K)�su=���t��X%���n�h���	Zr�HWC�I�G����%���q���K��PP(�C$�T���We��\�.��
��>
��*�������,(ML��G;�^F���(�������Z0��^����95L<�rK������C���B��W���r
�ZM�j�\���k9-�Q
�C�$Y��"�e���xlj(X��>�~����{ R�Yf��0����>�qpXQ�h�+X������
i�\�������#���.kr��ab*���@b�h^����������C�����O���;�w�v��F�s�D�E�����j����'�D�'�1����_�D<F����h8��OD�C9�����=��Y��l�������Q�|z��s���qM�K��8|��������*}���7<�N(��1�xoT�n�~0�:@ok"��^J�A.�(f�d���1�&�����L����G�JW�gQ�,b��X���DPGK{;�V�G��������&p}��zx>9;=?���~<��!*�F(X��gp8���a$�8u��ap��b�K�����di�������a$I�����HcRskN������Ku7BR������jx=|c4K$�S�����8������uq�v��"`�NzG:*�}��4����@.o��{���n(�|���sY�i��z�|_�)�p����1M��g�P��T`��!����S�����a��~���c�:�u����L�����`p�Y�9Gtph��A�
�\��:�Z��=�}�k�Z.p������A
�"U��������Z)T�;���:|�I4�/�1�e��z�2jA���g#���������mP!<�8>�;�������;��[VRE$�y����S��*����������1c��)�l|!U�Kv#���[	W�+N���Zd��Lvn�����=*_%��?"�%�����.H�0�/a@i�� ��	w����^h5�|o�H�9`n4{�P2Y#%����W���t�,�wM�����]�R��B�������s@k�l���41
�&+t�G����gL>-=�����T�|72�
���<�'Q5����fgr����B��~1x�\���?
��`I�;���gw%�G��w��(������]��@f���^�I�$���L��������\��go���	��'�!i�/������%�?�<1��e�Q��#@#�GL;��u~Dq��Y���9v>����s5����8���T�D��1l2?�J(!�q�L��������n��.�b��~��X��NC������^f+��#�(c(;�Id�S���L�C�sJ����[Xya����G"�FF��g
�|>#���F��;}�lKw-��s[��������{�����':u�~}D��Dj����ZJ���[�h�����C�qE��3\a�61��	r&��j:~I@�:�0~�-���r0���6@I�Rj������1f5%����[�G�}/3����A�}��`�W!^�g�W�9�w�6����,�t��b��/�;wn�D:������3���?bgtv�_w��������|<�0<>���u�}�~?�H\[+��{]���H�U����mc`�����k5��=�Zq%������h��\X���|uU��R������-4U����uX_���4QtT���HF���xD�������������6G1���������:z���(x�������>�~2d��\�e�A�>-��E�C�T��I�bF�����/�A�R�������e<"�E%��*�*y<U�t��V�0�Z]���A�Z/����:S��x%���M+D�R*�[��VD�%�D�F�.�W���p���W�n89�9����a����5^O����Kmt�����I����b0����)�<�����U7�#�������`���P����Zw�x�X�����E;	���i�E�
�j�{����#n��tr�C��nrN�\�������r���xk�r.���
���Q��j���A�Nm�D��<�X	"HL�F3H�������{���F��	�_�f�l����.�]���4�v��SK+���P�(%�]�ow<�/yWu�Y3��te�����qy�	�)oZ9o
�6��]��<�rp�X��"y�����g��t�����h���(�g�G�K�5E#�����h2���J���p ���F�2
M$�:UiM/�5Q�n"���'�����%�%n�Z&EI������UlL��R��kk��g�����_y���K�rn��{�g�H�v����>�2��_�S�����
L/��<l�^��l�6Y16W�q���D�1��I��G���A��Z��k����`k�G����3����XveP`\��g.�y�������s��kH(��A�2)�M�hHK�X�I��$w%
�����W\�x��KqA���v{/,\��A{
lP'���'��G�aBzTl!.,��g�w��@+tXt���A�Q����hi����%���H����!:�?��&wi����[�3���������	o���i#����Zk���5a�\)J��uM�M���u���V]��$����������f������5�~��4G��ON�XP�\��L���%�?�P�F���$���&�es)�/��{��S�~a8���3�xS���h��1W�f'�F*y��&;�F�W�$�0�i��3�g �,L\L��������;T��D	n��a����U��K��c�@����C��^�yJe6���l�`x_33�u�'|rA�s�T-�|`��;'��[1*c��1>�6�I77�d�]{U(�;��R��D�Q��G��� J�C��.;��[�
f��1�����p�Id��5Yr-���uTkX���"�~��>�0������v��s'�C�f���-K��G���lr,�J:���`V0��t.z��"=y"�i��4��&���>����<��i23��r�-D�bi��y�{el�[���}�T�����F7��rp�GTE,�Lx3�k'X���%�#M��^����O���7�12�g��&��S���-�L�S���h�r�1��1��G�%N'R���5!�����Z����1�j�W�8o�����9�dq�?���y0���x�+
�W����"oq���t��yuW��3L'���q�=���Q�V��p�0�!���O�ELj.a��:U:��~������5]��z��^�Q��Y}6��aA$�; �9F
5vz�)|�������DY2�������vx�0��9e�rg+DW���64�������_ ;j�v����%|�(�7���7�^�����`��a������9��*�I��#@�\�H/�TT��3��h�h�e�*���U�@%��&i�X����|���y]G\���a8UY�;�|gx�Vx��I>�8l�>���DV�"\)����kD�'�S�Q�QyQ~����
���a.�D�C�T��x%�%�a���.,������s�O
0]'�5���Y^FGv>Oc�s�^sC�#HP���r)��|	7F����fm�lR(Y�zy>"p�rjn����^|�\5���<=�%:Ci$��F�&��%���;�L�<F�`X_��R!���)*#�Y Zw�Fa ���Fa ��9#���J#��8�Y���lOl!Y�J���>Z�w����D������V�F�����9(�����a��D-��r8�8+��EG4�;u
��9�mD1�H�@��$�p�*n��d�R�JF:
���_+.�*���*
�m^�����R�nN�������X56���=���j+zL7A@Z�g!qFRg��T�r��Xo��0e�1�����!YV�ih���8�����X�jbpc�v��B3�30���V�\rB���eE��<�����i�����h��}�����s������B��q���`O'��@U�������O��E����Z��P;�Q���Z?8�����g����;��P�����?�P��az�]���O�����qB�3\������������p�@���B/���y�r����|G��������|����D%�C�m�T����������2"�P�c�t�����:�s��/��l�Jr��Sa�g���/Ng���Py��F�U,I�Z3:��hyc����������$�k�<(�>Cg����UW}��4|}����7�����Em�zxG��������~{?V����Ha\Z�����J�����f@>��Y���t��P�I�����zC��h�6��*�$�"Y�!�=6���		�>�?��`w�
[��Ny+�r�"kGr���5��������?����E��n������a7�&��G!#U���p���B�[2�.�w�:Z�R��e����.��[b|�u��9��~g�����o��|�������ix�~��8���2���i��RKjuu����l�%�ui���[.VA�#���<���L�D�]:�FI<Y��ibX�4����G�U��
��h�&i��)y@N�4b�AXk�\J�~l(Fdd�Q�9�-��@��	Eh�7���� �I���9����T�x������
h����?H���]������[�k��fW=��:�	�,=��L�V,��c�
s���X�;�&2�/d8�^�1�y2��A�#�8���%3��x�����3���i:�r�j��xkY��nX.���U"
C���������d�<Ng3��6T^!(�*��s����,��6��_�gGL�.�p��E�E6N�Np�+f������v�������w����m�N=q�Q����U���r���Fc��ekkk���L�������hp��������-0{dw��k5`��q&�A�����������6���n)6�\�{���8B
=<u��n���cFQ�N-`[�q>i�}0�K
 B�:?��8�>9Bh���M4VE\r���	��)�S�C]�:/� �w�
�'��	�>r#W��%�C9�<�al��'�(����9v�}�BP*�GV#l���$`���$:3�����~������h�Z[�;�l�F����V'���G�J��'N
W���s
���/��s����
x;���~�{�����<����,�{t��(��/wLVB�g�Ne�}So�Y�^�1mCU�e�/{P����F��&^sR7�rR��oA}�WD#U��y<L�G�^ ����@��.
�����@�-�S����10N�����NO���4���gmmm�J����:=N�Y*=+��W��[/���������YT�#��1����#q� I�pA��~�G,#�%�U��.�d�$��k�
u����`
'�n<��BG�76Ym��e!fy�g�(�:�^T��k�����+A���E�X6^ i���K�����r����vyX�@�	wNtA��N��� r��4C��K�	g>I@2'V;!�n�t��������e6�6��s�"\� I���W�,�����3�����}��I��G�^����1u�?������v�(N���@D��L)��Ky����{�&�e��).-�[V�F(����3K>���}wA�@�|���Du��;���9`���X���B��Y���^�3�,��Lz�5:�9����7H_�����Xo������MA`����N������Ud��!U�,�lt8H��9}��K��z%�O�+J8�S$ABB�����K���2�Zd3q3�
��[�D���)1"�0=��A��;
B�m���m���m������u��`�w�C|;�a��3�c��
1��"�K��+jj<rUF�J��~���b�Qj-��iC����)T<1
��$n�!���5{�-�X�bm��s0p�����K�SHW��X/��\k����ai>N�b
���>������
#fw^c�����L�]�����+�BG��p#%I��Tkk�^�T��H��F��`�&�C9����
9���������a��1�4_���EBi���,]�7!16��6j|���g��r8�{9�1m�����9���]���\V�9q�7F�VL9_)��bkv�=rj�.c����3Kdsc�y�����N^�� �q?���m�Dg����q��A���|zW��B��N�%���k��-�b]����"<j�e����
�Q�1� �h6$�:H]	~���Q�sXf������{�;��jx����7	��4�r���@}��H�E��d@;�R>�n�����[i�7�6�o�7��[���?�����w>�wv;�^��e"!��������yg�bK�k��{�g�����+3��pr�Kf�7t��[���u�+���So���7F�����>�a��a�M����������-���8��~1E�����7�KL��m��IN�����V6!��A'5K�����G�t\�����������Ws���7��DQY�*�`�N��nU���j���=�[�/���8c/���/���T�T���x��k��mq�
�U���:GZ�����G�Y�=��#��J�7v12K�@���Y7�<�f�^���c�����i�o���,Dc�v�������h:6�7h&:�16�EC1#�}�GR�a�[�����I�%��
$�M��*��VwY��7C�&�`���j����M��~����s4�~�1���p��q~�!t���%���S���|x	B�%<hG��c�'�2�������T���h9�In��UI]B��,�����2����\{W��YJ�`-y�m����"8��?�U�9��9��z��Y0�F7�'d���B4�`K �K�k�UE>����������dR���W>�Y��A����P�g��<�-��/^<�{����O������c�+���Z��@���G�I9����C�r��~�^m������8��!���|r�]om,U�xIbt��f�����to8lJ��$�Y"/�%Ka��X��Y����<�V��l��K��M*��[V�@	�|O����dKF�M{��D�������������9���[�fQz.T����Q��/�]<�k$�9�;��`�]�|������Y=�9K��{�!��V�Cs�"���h�����l%xT]�&KK`#���SX��&����4�C*n������t����"_�%����	���������]��|z���k�N�]�i%P/�s�?:���sr�m����2�U�S| >X� 5*
�F�����zq���s+�;��`��mm�����}v�����Y��Rq�B�x�V��1Y(�:��R��irF����k�u���.d����~�hv9%|���d.��(��(5z�����HQ��C�PF������:��KRi)Se:0�%m �
uY)�.�������ci�+C�.�T���
Wq�\P����|�Ui�bV<�����c�;��E����H����W��y����l�}7��������2k@=���/��=G���������U�����������*~��QTF�������/B=�MsSAa������4#�E����k�(z���ep�X��TC+�t���b���H��Fw5����3����=z��3�j�%Y���|�f����P�<h�<���=f
�T�������}�����F[��������j�cCvZ��#F;��4���9�{W���p(]��*sn��P��������b���}�9�M��_m�h���a ���N�`!Pa�2K�Kf�_�F�0F��q����o{`�MQ�E'�H*�}Te�%p�E��,��*�
�'Z��=�r�:���V%yz=�����V�f2+(����/Z�L�X��n�
�y�uX.��Cl���b���6��Q�&���h��F4S,� %3��6�:%jm�n���3�����=������)��������1~M����4J���A��-��Y��M���1�=^.\Ti�/[(�1��z���k��.?�����y�N���$1���Nf�4W��e<c�i5���I�,4P���'~K��B�e��d�a=?���#jX������l0�T#,���3�Z��2�s�^t��h/1���|D]M�@z1��'�Rx�e��X1<���K����5��U�K�l��0B��;�������_Y�+��]��Mlh�����3b���i����~�8y�;�7��D�9��6���U������{����N'���|�.}y��* S����gPR�W	s�(Qd���T���
z������t��Q���i�3Z��qP�R,&�4"������O�,�A����YM�a0���e93�����v����laU�6���hW��+3�?����f^��(R��!1���=�je�x4<�G���'t�U�@��#��T���r�GK�M�@d����N��g�U��g�4+�r����+l�W>�.�`��JQf��Gg�g�.�}}z�(��K_sSy�R��K��+���)
��
�0����o��Zb*��]D���J4]>�jR=��6y��������%�#��8�����j��|�7�zS}O�>����N��x�".<D)g"��M�Z�f�p � #�*;��O!� �6
�_�������^eF����	cf��A��c��a����m��k�3���,
�rP�\#Y6�n�������'�����m"���q��5e����y�W%�m]',��]O�k�'��t��Tp�3�if1�T�8Vs>'�]��4;�,����*W�P>��P<�����'����PF-�U�W��Oj�V'�T�&�J��D[�4$������	PP�jD�_�/��t��2�3!�HX�]���a�=��(���G���t��<2z���5��lI������tI�i!�.�w�X��i���t���h�`K�' �Bx�rI���(���N@9�����lfz�1�~ XfJ�2��C	a,T��D����4O,5u�3*�2��e���[�[���8;��(���N��3�B�v��s����|��
W���G�AGB<����k��-����v�/���=B��{��f�����n������������=E�a�W���T�B����:�to�\k�=f���;��?��h�j��R��W�kF����|�u��e����c�wjQ��%K^���>�U��^�])^�m��X�$���0����)�Y����<�f���?{���$�m���TWt":26�h#�]�f��.U�������4< ����p�d9�)n�'8
��J[�N!�I�f�#u�����4$u��rm�A�@2�T�V��1z,�f�$��ciqd��+T���`�ef�+�-R���������O�S+�U�)��^�g��x=x�t�a��JiI=�p'o���e�<!��U.l�UM+��>,�RF��G=]�I�p�|� ����Q��c"��
�HT�(
�������mJ&ZG�2�,���_��1�,O�����L���H�7�4?P��<k��$~i���GwN�dj���Oj�P��:���@�-!�	�Bp&o�)�w����z����F��!���v���!�u���C_#St��Y�II��\��V%M7g��*J���T�7���V��&U�p�$��YFhZ��r6���)�����G�V��1�m����MobqD�0��[1����>�	
�����(�����i��n����������17/�KU����-����k��,�a�m�CH��Op�/oA�NY�r���!9�����H��=��Q����b�[��c���Jv�1g�<���}F�����O���1�<��~�o��?�7�{/��}�g-���6g�Kg�>�
N����G	�-~F.�7��P1���uvD�}"\Z��P/��������?A���A�������6��I��m����M�v����KA�3����a��a���a{�q�a�:.;��f�j�.�X��]��Mp�W�8�[�`��_��9���w������%
���;������7�l������wO-�;�/����y��ra��Y>�=�gq��m���r��j�������Yd~���o�?�2Y]�>������h}mm{kkqeee��-.//��N�a:�������$Z�G��o�d�r��*7XsQ67]].]����
}`�EQ#�I�{���b��b_3���x��L���i�^��qd��%��	�7'E�al:�c�aU@H��Kn`=�
�^%���'YK%��.��M�6&G�)�����(���K�)�g�a��'=�����$�4�������v���K�9n���f�.���P����Kku�U~|x<�	S�2f��O���=���P�lq�\���#� �����6�����1
�\
�b�[UH�%�-b������r�y��g������h?3�.o�n��O:'����vW��,�������b����1�8V>�WBl�(�af����,��3a�3�������N���!$V���-q��F�w�(EN��4G�6�R�n��3��`�����xN��1�3�K��/�^m��]�mvb����6i����&���<�J�0�[�	"�}�4����E���0�/�<Tl����g��);�z����\cG��Ar.���nm�A��h~9)2fLn	�c'>�=)+�O��O�e����\O�5'�c�����#��x[�D�>����@�*�;�4~>�����k>����M�{������XB���5�j�%�W[��f����$o8�`��!�[-x��UCA����ON�����w���owwj�����sp�i�
~��]+�Iw�W��.��IX�3�M���p��1��o����!��ve�w���
������D�u�Q��NU�67�W�[[��������^�m&�k�R�l�s)I�nm���t���ee���O�������q��8W�z�+�A�.��I����;�s��qrq6=_y;�_��8@���PJ��Vzd�e�����_Kn�q�)%�������X*Y��
�����1T�3
�+,�V�m�����X\?��"rl������1>=7u�����i�=4���g�c*���/�[����Fw�`v'������u��Y����	������V�Y���-��M��OG�������t��y�����~c}#9�������29�'�����Z���\�^o�BZ��e��S���=��^�%��L��Z����7�������G`y���f�]����HT�o���O�uVo�����p 
r�L(�F�7�xn����E��g�97t��A���A\����������[y�LB+K�~�(�����o��w���>>��8����*/���w>�&���mE^���0�{�
SR�=��`�/�O7�������-;tMJE�I ��?��.=�h������x6�9�yM��s���O-1J�ND�(ZRp<W�zN��������,����H*���L��,�e8�j����?�fnbj:�����<��v�D��X>�*fv����A��+%�������=[�t�(Yq|ftss��v���hPY�w�g�����c�U����t���|>�������O��Mm������&ku�{B���q����4x��u�Eb�]j�!���a���afi���$���<��g�"5�e�0u�[Gd7��5��VW����������!�������S
M�I��5�h�Zk��1�	k`������L[��"}~��0����_%�w������]X���������/.���~����w/.O�Q�l��|���3���e=h�w������^�dg��^v�����&���j8�2�O:��_8����
�TGW�o�����=���[��/�nS�)�^}�o������a�E���ll��:����"��xK|ssk����q�(F�F�iQ���J8�T�wY[�@�L3'�2S��=&����T
��0B�M|#����9c(�.��+�
�.2�������h�G�e#��z�����(�b��
c��cN����8�&���[��~��N)��4������`DU���L��c��Qj�CO�.�\N'����o�A���X2V���5��W���N�������qb����]�G��l�p�s���hi���d8�A�O��4�trK�������a���ci�m��b����#�6�[����/�����-V��W������a�w��Q������E�7�mn�
��5R��y��B:�{$��J�d������
���I�r)��-k�<�\�tU���m��s�&TaF�����;?}8�9��W�%'F9G>�����a^���p�U��z��t��o����e/�L����ty���}�o�������b��a����r��7�qlJmD3OY�jE'd��n'I^|�g�y}��f�����4Ba(�l�l��h���s�������t%0�)��(;��1����k(��Fs��'R��(#gv���N�KnP�y��"�U�
������v"�}�^�T'��ev���� JyA}.�%'|�1=c4/M����9��!�)����mo����vx��>�z��I.	�
qu��0�p��k��a�a�a��9-8��\�#��
��tpK�D@Z��4p'$�O?]��^X <2{��_fh%��G���~?5���y�D�(���/'s@�[6��H���'E7����d��&������������i�e�+eI�4������8��9���)%������/�������F���'��������'jm��R�B!�F�e]�F��	�-���)K:r_�6�`����?U!�K;��x����E!�FQH��%zl��E;����'�Se���t��}���)xtA�\U�Pmd�DI��_��N���i-�bK�KK��*����Un���g�:�*DI�.g^jK�����i�9C$U�G+�:�i�.T?]B���>�������#�G�s�������16�%.��w���fV��m�.�&���E��Y��8��2��`��tE\?�B�}����������*����-���������������V������������ +N�j�����N�
�Wv�����#:��}�9�9���b�~`��B�e�����c��d<&�k�]���e���(��*�h,����L��-}������wWK�H?��t��Z�an� 3��)�e����=�����7��Po�'t ���`�a���#R	�@��i�}v�~RXe�Q-������P�S���V�XV�M�����Hny�����sP�}f�h�G�R+�-��T����R,����Kg���d8���r� u���]P��V+������0�����t���b}��}������}zy�c ��QF�A
�I����1W��3b��%�%��W�D:�H����<����94��s���t{��d��v���2��p�+���������^���mTP5Bd��&���\��G6C��y~;�����Q{���e��d)	�����:������L6>�t���b
�/�M�������U/TK�zj�� [�W`���;1f��|x��j�5tG.�B�U�p�9{27\Ex5% �����2XjX���$�x@f���VA����+��*Oh��>I`�
�-Vj�~����G3���aa�����YV�b��o��7����>h�w������d�"����;���=w���g���+//d�.��]*k�(��$�/0�.�j7u�P�@5h��|��l����\~�Y4�T��T���:��	�]��:�Ph���*����dX2g,
��#�D,9���!r���T����L����Ndy��x��bf�����E_,���<B�y���u��C|j���k���W���E�7�/�9�)��x}1�&�7%�i�)��1����+h�	U;�P����7�Uje��Kn V
��QWL��p��x��
��H�22�bDN�+?�k)t�p�igx$=���5�<�B��p��+�������r�5To�C%���)�yw��j/�s�9:�h*CG�br�R(������vr�}�p8Oo�
�m<� k�Ln�^y�����q�!��q:�����'��p��s����`��]��A���d{rh�P�Aa!���?�JY�����ErG(�S?G��hf�3xwNv;�"K�����A24#����%UX�������> �������(d�J�i]���6��`6"#�^SW�
���r�j���5��b������ ~���V��IU���a�v���P=�P@P{�w�>l�/�g8c�������4B4������DVz���v����1���hu�{K�����	f����xb���A��P���^�q�N]�1:�%�4���q�U��~�
5[}��f����_}��A����k�oi�G����{�����Nw������i{�^��_�C����MO]+"-S���V�Z���T�D��Z}X��n�����oX���;�m>�h�����^�����?����Cl��\���n���.-��1X3�I�{�Eb���olo�'[�{#�\
��
�_l��h,In �*���ECm��<��E�\K<3V����t�p����;�T�(�/Z��I��7H����*���|�*bE��^O,e	ngW��
������J�rD��K��A'���Y�����~nq+I�*"�T�	�1��c��fJ�������_���/�_��}�������r�)fE���c��|3����t�\�m1H��.�u�K�v
����L��	������{��=�;��c<J���g�����m����s��OF����j���^��<#�����1��qoP�����m����dF���
��x�z��9��|<!�4�e\��(6�Jw�J�FYR�Q�9���eI��.%_%�������y���G�����X��i���F�-�'v��@�~�9@��
JI�q��^iN-�u��y��/
��U{-7}���1�������;���������t?�?�k��+wEPY\C,�.���#I�S<�fz`�'�����(5=
~��2�<�=�Hp^��g�-�bV�hD�~�H
l��%at�?���w��6�0��.��	��k$����#���=����������#=�����Yq.�*9�azK���}��"^x[�����H�����OIB%|��NK?����PN��'<DQ�&��U��:O�q6B�h�;������6�w���E4����\:��qH����{*����Kh?;Jy�<��E��}B����L��J���z��N���Ei��E`)�2d��{�u��W���e-H!��1���:�t�qtE��#��R��:��������G�~���
����I?!���~�@��:�(���2{�;�������������}^b���V?.x�SZ�EWyJ��1����s4��9d�����v�i�U����ow2���W����;�#63�Z��Jb�%����,v�I*c�����$�b�6�?�
:E�Y�������2�2�y���������A(;�B��`�����l,7.�* ���1������Fm����0r�*��s�?�P��d|d�~n�l�_�����*���Y1d���U5yG������	����
�w������g�3����kEk���MGj#*(ZS�V��%!��8���Q���:Z^N�K�JMG�q�ue�'���M���^�~n��������Rs���k.��ts�����d�3��j�f�x���J`O�h�3�P�����j��C�����4�U
<��pE
�j/��c�Op��
���	7��4PYC[-U�`�����������U����ZF�����u�
h��i
(jI(��1LJ%9�7i'wHS�G��rD�����c���x�3D�h�Z���~��V����j�b����p�_�{�����]�Qt�*u*��YJP�)��l�U���(0%M%y��_	��������%�	x���1�D�f�f?���A`q�Za�k�xBz	%�cY��=������q�z�9��m@�t7�"�����x���^��"��c���agO���;E�$�B}���>��g.��{���X�
�Q��w�g��KE��Aw���3�ft�:����C�68X����x<�"�?CdI�@��jM�o82�4Q�V"��g����'��Q�@9��@��	>�U�B��4�O�X��a�Q�+Jz{�6`�I�x���1��3@"�����m�j��er��^z��!�0F��Y_H�PBmr�$
��H:��gD��j�nm�������������Ic$f�s�����W���Q�-E�@����X��p��'�|��%��^��
����`k�O���V���ds�����>cq�t����Y��4f��y~:��~����v]+^�?��H��C��CB��YC+z{����?�32J�"FwY	k������)����C�0w!�<�0�.0E��2��B����@�M"[Hs=������ ��P
��A�7���}��RwVM4M�,�I;�f�W�7)^�k��o���X�OF�����}U���i�W�:��pBZ�����\����>$���H�������<����Ird+N�l�e�����������tm��~�C��-�"�O�I���@�O����8W:=N�d3HG�!��q����-�iX�[��P�(��N��O�jT�3�A}�J�=PuMW��o���H��_�8�$����
�zG%���%��l�����n�����&�g�~s�ma����!~����
�J����)C�"����lC��b���P��������0�%7��CT%h�L��������2��j��~���;�C������0�$���+%H��gz�����`�yj�TP����M����*��}��*��}�% ��(��[,w�zYb����v�:yt����i�������wNN�c��&l^\��/�����$\��,`���3��M�KDp�pn|`~���ix�d��5��9���&����G�o*�@�����J���
;�n������T:�6�H�6"���A���4��	�
�b�FL�,����j��`S���}�I	�K�?8��0j|wb���1�����
<�y/,�'��_����+���yF�����g�v+��D�a:�yp!xEp#���C\�?��$\�@�k�=+�� ��E]4���F�W�����_��,!P�}������5�Q��x�rU�WF�Fn���A�c�e�[��@m�'6c�sbMoQ���l.���ZQ����������_/(�(zjU��9Gv?{���\��}pxr�����%�3C.g+=�p����0�I.�V%tv���3��,4��������G�#g>����x�W��m��Y���P9�s����u����l����i�� }p��[����d<f=�����-w���.��N8C���������)QV>*p���lq��q����,r]T�oKt���o��{�
Q����}�>�7�����U����Gm�qB�5�i!j��r�V�E���;�R����-��M����g���U:`���<ZC������������*�|��`@�(k�S��5����F��OG��R\��4���=j��s�Wi��:�A:����}���#\����6��d%�O������pH
��x�=]�o&*f������\�7�������=�~Yhu]2^O*l����=e��L������~��Xy_�j,3�sEQ9K	?e�GQ�Y��������Y��\����5D���)it�����������vil=Y����J�a�����P���h:ZAP���mLg�z����+&��>�����X��p7��c�!t`
Lw>0V�%H���Jc���k��0CH�p�V�N�Y�kp�Gf��>�\��,������[��+)������Vk����l8Q��f��5U��PM8O��,j�&�'�0g]�L��U���4���f�x����k}��8	��t�t=W�B��f�������?;K�J�:��CBD��"�������I�dNDK�:�Y�e(�p�<���E
���yb)��(v_����T����5��,K�I�$F�T�=a�������b0y��e�I�!xU��L����1X�za���8=��3R.�s�3"�g��<����:@N��+�/rl��$`~kA������_�������_�7~��]��r0��9PI=�����b��a��7���,�n�j�E(2Q9U��Z�S�q�hx������	��J>���W�
�}��`����Q��+Q2Tr2���`0[�E��2�����6NY��hW2����p����V�%N����o	51��a�GV3�
(oX��t�D�2y�i]�SYIt`�[PxY+"��4�85����������R���Av�����{�� H�_Z]�2?����7�	T,bd���b�|�J�5���s``V��k��-������$�����[{I����!<�F��e�n#o���p�9iu+����)\N��hl�H����p�{x���l�e.����#����M��:����r�!}��*��5����}��A)�2���uW�mn�8�:|+(��������(���~��v��?c"�<�x�3j�	�������q��q���SJ���+�O2t�\O��X����r��^2��"=<�
e����sh(�1QO�NzK,�5��1���h�����;���-��SCW)k^�>s����vdv�mB��F ������~�67��?���	�
�2��8�J���������-W�N�f_Q �(u����U�yD���F��w��������RHCJ�I�;Q��@���JsJrsF��$3�p��gf�\9����:�+Y�+�I��c��F�l4N����!@�
?�!S������)b8�j�C'�������-c��)��obie^��\fS�*���>���*���,%�;���j����N�9�����4?w�;�=��Pe�Z�[4G�X�Mv�y8�2�-|&P���=M;����?'��)���%���^!a��{5kJ����*��� �IL�I{V���1��)�%-�q�+��#8�<�Z$�*g%����%���
	p?����*�hX��u��~j~�R#BM����g�8�Dq[f��K��l�\@.i��)ZnFa(�b�1e�hz������z��G|����*����!@�,
����U#�����`VT=*�!��,�-�����bZ
8�=%C���\���Z�"o��$��|w�����w�=Z^/��U�?'�6�����yr�����e��q��[JB�v�����R>�!���������*�5��'z)"����Q��y�>U������
\����~b�h���cE���d�b����@?��M��eJ���K,�	�*0�����3�Ni���cR�a�!���*V�d��(�Aa'm�H��d�[_I��S���j8��o�C��$���a�2���Ze��Or<�������D|�jT#&�/HH+-�Z}�
y��'�e� ?�I5}����[�������M@�*��
��c��i���U5�������:���DH�+]��<_����30�U��a�E{���S_k�
.��.h���}���y�F��<��Fj��!�/o�j�R_i�R>�:��m�?B��,�G]E������:`�[��_XY������=#7��N��m���f*���D��y�����F�U��*:�!s�n�b���
!p�K��������q*���I��C�o������TXd��>�������:O�	}IG~2U#o����K��2T{+�j`Ns�*�����P%�Y'��u�J9
�������8��c�8R*�nz5
p����Q��|}:��O�a�N�A)��II�|��hu
]�!@~P;���?��f��!0��uo����`�o7�\"�� �6��r��1�!�Bo��'���WU���V{kuIV��?�?��0S7�UAL�a:g�y���z��7�l�i�YL��>g��FI��L�a�qn�lS9U�����@���O�����6��\�!��X��|����9}��v�4�x��"�J���l���V�D�T�l;�T`���1jUJ��p�i��zY�������8�$��j��)<����l����nf�������
�MT���':P+L2�1��d@�|����"~�D-��%V�D97�3"�n�j�~��<��Ou>���9�tK�P �cJ� =x�\y��)��#|;�����,�7K�B����O�'Qz5��5A�k��&*����ev���wz��ac-������gVx�C�U��t��$��_�y�&��y����7bU�?��o�Z����>������L����l:��T�7���W�:|����%��������rK[2Sv���#�=��T`LJ���(GY&��L/������������@�v%L���������^���Q" n�S.����u|����0Nl��c��{�����9�_�� ����F^F�P�X{���Wh0XQ$����AvZ1I�VxQ�}cB�7��>��s�l��������I��8VV��`�u�
,��P�Y��=T���M��K��g�}�����/a��r�����������q�&��>~���]��#z�$�_����<4�B������X��0�y��M��n~)��_r��j@p���q�M��}��R�pt���u����i]Y'�'h
���*���AQs�MS���O9��@���7p���d���A��96�WY?=�m���*c�J������1nSDs2�I�Z8&[PjV��5x��r��+�>���q�����j����O������x�
��*��	�A|�q���_�4\���Q���9��6�`�����5��#|�J.�
�o^)�/���jE��SV���M��^�q�1�	q�ew�_�)�7��G�5�oG�M���U��c���[�TP8L�GD�N�"��,!^sV�����M��s���:�S�>����#�}rsi����p<wl.�0p���#-�����,��v��sw)���
������O����tZI�Yd����o���������_�#������tH�Y0o#Y9L���%M��q�%���U82�5���L��:<�qh����|��	��;��9���k�w��u���������1��������{J�E��e��I��X����*�5�Z$1��U� ��Q��w��Qw�k����H,���N����1�oy+�|%�a���t�Q���=*9k��? �J����Y�/���G��1��U��iO	E�9��J�M�����s
��p�:"'������-[������S��R1
/������\��A�a�]C�m	��	�k(�+���;Y���@����/� /�L��$',�E�X����\19�kt�w��O*S���Q6H{����$5V��Q��:�����V��C��?�}��q�#2Ka����C}�I�p>�~��Vd����h��3�H(A�����t)�1.0BL�Tga�NN^t2Q���zt�7��k�[JF6�����9;��k���Sc��\�6����G��?��$e��lf��@�8_���y�8v"����;����(�I�+m�&
"�/,���_U���c��1)��� /�������}"�[/:�X�	E\�$��?��m6���n���S <������;�\e<����q�C�������A��E�&U�h��n�
-�*���\Da�����e%�"H�N�-u&��%�o�`kuH6*�Z����������|�%nS��;?g{��iJ�#n{Z��|��'i���%jG�����/GL�q�,8���!n2;�]�P}����	#<#��)��Bxk�Q���S�qF���b;�%��1�"�7T�pp\>BB�2I��!����?��+ikv�l�p�R��G��$��9��Q~
���po��������u�x�����Cr�k��)Lh��E4:��]�c�0�>�4N�d�(W��KMs�����_o��tB#f:�y��Z\<��G�(��O�����Sp�[�Q���M>�z�
z�������j��qZ��r�J��!x�cS����5��'�T��M��xv�^���������]:����o��3��K�jd�<4��+��'�������N�#�&n{[Du}�t��k�W
|^�f=4Z��0�\�,/6�y��9�wQ�$R-9ht��q5�0�tdl<���4���Fr|����)����k�C�����,Y��v0�E����(�[�&������oW����7!j}��f�(v���w��T^z���L���3u�w�-�����T�g�Z#�
�����3%�
���X�6�_�>A�Xf=�^����]T)����X��x���d��������i��PQ���LAG�QC�2	�d	��
{i~�n���Z<�t��a�dt+C�,E���1������1����v�]���;X����t�(OK/��V�����V�w�RT#�L�n~_�|�g(���Z�^�dw�x���X�`$�V��x)"_�#zc��Z�g������r$?�H��B4�2��J]D�y������]�T?���f�tvR��dp��=�Y�
'W�H|�/%�w�t(��e����[.	����"�:W��E�[J�n�i�xSaJ�������b��C�����X����`�Q"�q�7&�
��B�T�H����[�/H/NY��YwA	+��7�ZQR(l�{���`��%0d��K�:\U�E�cx�(�.��d�5n������e�J�jg���nL��$��
�}�=���8�H�H�c��s��=�L=��g&OU����P�?z&>�����nJ��^2s�	7���E
���r�0E?�T��g��G�y��UK��3�Js1�����+?����g�������"�2�bX�;�p��}x���_�W����c�7�P+T���*��*������P������u3���;�5Q�A���G�B���iN�a�~��ti��Opd��WU�����Sz�*Jq���3
H��r�H-.���lLj�^��|����*=ff=Y��e�����U��/f���<�B�_��E�Z��k6�!��Da^a�D
�F�n���N.�g��~��!�S����U��-�1�kUZ�7�s�W� )������N8����#��N�P)+&�����
�������jy'��)R�6dr-,����x�heM<",v�T�F��]{NJ	��ior�H>���j�6g�����-�]�����D�/����b�}+���y9=-y<������|w����'w?'
�����$�����n��N*���P���a<�/��xc���Y<��1bP����e6�\�J��L]�^�#9G�3
�����|�O��b������X�<U������Nnz�h�[s+����TPV�������;������Gpb��b�HIp�����F����MH3�{{���+UZ�a�F� MGV��M�)���i���B�?K����w��q�z�������q�B<�
#5�*�
R��b�Y����e,T�M��=�~��y�&��u������f0��Z�8��E�3j��R�������r��oz����q�0����C������7���!.���
���L.
'L��6E����jD%�I�t������C�o��G�F���Y�p���4A��,P��L�D"�B����0�X?���\*)�(���J�5��L���>���� ��*�nAL��t�1*_��J�nA��3T�a�v�}���(�t��(��B9�8! *Rc���k�]�7���6q*��1�l��`[M�$I������b���$2�t�8q��0�n�)�r�m=#�He��?����(W��.�����J�H�����NG��*����&m�.���������)&�E�S���F����2�YQEn� ,�X{�����P����n����$�6�~b�
������f]'���\��Q*4��4�cA�	��
J=i��/�������W����UH���]%�f�O����(�SKF���'Y)�SY������
�����cdi��AJR��U3�p���#C\uSR0���-.x�#>�e�7��%�������w���k	(�w�O�d{w�1�?f�Y&���wC��~�?�_��?�%=����'v[n�,�~�����c���0	���g���%������y;��bL���t[����3N^]"�(�f�g
�P�����B��D���K�O�`X���E����U"l"��qe[�6�4����Y��/��y���`:���l��'�0����=��E���� ��[�gG�/���&��:�8�B��^H�N�h�P��-NRNt��>����N.z8��!���K�p��qb�t�;��H�(���3@����X�.6[�n3�S�X}/��'��c��w�����M=�Y��	���XrE�tK� <��<l3���v������[��
/3A+��-Eiw@�*����Hc��@����4������_e&�`eG,<~=��?�M�<�����,��~��E��&�U��~(CL��.���Su���
����F����NU$�
�c��"�%�h�]C���A �B�&n�����S��Qi XMPO�$�<XLn�UU:(aX���<���JuB}.�^�P����VP�~U��v��j���[4�`�h��l�N��P���B|�_7�2q�7??G|��5A�����[�B�������Nk�F�3���m!t-��Ym|(��G�Y���Y�4����o�����m���D	���A"e��"��5������DA���������|�����i.�2�D�C�#��zzR����(��8��';���o���-�t�'��J�������r�cp�
�.�
"E#��-��k-Y�Ipy[|&�\�hF����J+cQ�xCV�i
����+�9'�2v�],F����� E��T��T����|����U�����R���I�9as V�O����o\ Gd>�w���e�T�G��/%���%�b��at��q<h��p=�UG�{�0��[���[��K?8�c��$���k��<��~�e��/��2�$}.�5�^�=@{$�@�����	�iJ<���:�NV�����d�#'� x'�;��N<# �$����PU�����R�=Ij������x�)�����O)����,��6drA�����3�1��:"�#�Qg��)@�"��f�
�zB7�2��\Cvn��������S�7P���T��|�rj�D@tZ��Nq�t]�+�v��P���T
�V�\S�
�8�cK������%.V���	SO�"���~J|������]{8Wxj���\�"H��Q-����{%U����������6���6ak�~��m�s�"dVi:�<(6��m|f���(Tf�q��dBU�#��
���A�V8�D��h�/��k��}���YNa��!�&/c���x~�'�v��Q��dk�~M����6�!�W��)}���6��`o��*		�c!)A����{�	]n���T���MFr4��v�>��n[��wf$��������v!����lDn������sb����7U�s�IB�b�Ra	wti���r�h�%*-ay��SL����F~�L�[
������'���I���DT�����s�c��8<]�h��_^.y�o�O���@�G��0�yT��N��bZUD(��ce�z��a~��$BL�������/T���	\�q:��[�������l���:��5�@����&�S�2�	ep���=�N�;v6������F�8�����J�B?�{�N�\���*��y:������z��CI����8�����'�U�f	h�K�G�MIc��S�(����ud��d��h	+��ZY��I��\�Z�_i�*Wl�]���Yl,~�H�������
�b�����'�j���{�*!B�
&6�������d��O��a��V��<���`~�8O�ewh����(�/F&�]�6����v��#B�3S�!"F����.��0�1|^�����Vkk�z*�����=�v�|d��t�
�G^���%����Jxx���+j���~E����� �<T�td���4&����9QN��-�!fq��)� ������d�CK|���8nE���-�(���#�mq�����*�z0���q��q�PZNG�J5.&��Qn����}c��7�G(M)OM��=�>���)�,�8�
B��2��'1�P�B���h$�9t&3��������N�f�f����G7��>
�I�~�	��!�kEOZO(".��9���I|#6�y�������.��GG��O�/_�dW�����'&����G�w?���q��vAX]T�1����!����G�sg��K�o;*�&�Khi��I��zKK($Dv�s��+��"�e��9�L>�v��^���J�}�����x��#!J��$d�/����m��Cpy&} yp�����v����i��v�*���}���O���i~��%�����<pL��cI��+4{������z99�%��*�x1��2�A	�
A[������'���<��4�+�^h����m"��KT�y��2�:��'�dE7�M����^d�h��J*�!��q��M|e��u����z���=��"r��Q��h��zX��
Q=*��i@=����;�9%$�W��8D� ^L.�Q,�g�j�aQ3��{G��]��7	���L�^���x�G���8��L���
�3l	��+<�k���Qt�B=�p�Zi�6cn|�_kO*�BP��	�l��n�Q�9�>W����� s>�L�+��9���V%������Q��~��i��t��~��hg�H���pE�|�EN6W0�:H�G�g����dmP�
o}�0��sz)�p���@&�����B��U>��u�#g>��92{�]"�"E!a�;�:&d�@��;�:�_$�ua���|f���������w��lz�]���U����X��Ty�L,��7l�>�y�S�RT"�y�~i�Lh.�J��zc6].��Q`C"���+��d���=�.Ecv�^M�&Q[�r��J��z���*j�^�K$��K���5���'Z<=���'�Y�q��`���^�>��H��^w�����1����B7?6�>zX�w�+��Z�$��_����R
v�6���z�_����:��M'����jB1�$���S�%@�0��6��R��ST{���ZOmS
�_������b~E��/�b�r"�����ZsV?�:��#�:�=�����y[N�����<QK-Ly�Q"����s)�-��5|�J��O8��X���j
�{K�������P���T�JiP:����u�v�w>�O��]�����`�+�6�*��������{1��<����.�4�q���EBb�L��l�v\�{�\�]Fn�����N���vv�=�a��1	��?�w��=���6���sw�"1�P�D{���i*3g+�M{=V���5�H��sr&�C����d%��q�o��.���1K��^���6DI����`:����l���;P�0�2��9"D�%8/���c~������i�W��Wjt���?GF&��2b�"���-H���>�T�+�T5���������$���~�e�TAU���+0��x����J#��A�����o�]Fl#��sZi�LE_qsp���#�'&-�_�"]��h��4XP�����olQd�MZJW���1)�B����h��Wd�|�L%8K,^5GH&��rM63A�[E: �����B�:DU�����mq6�kuR2�KV�^�x��R@���/dI��oAp���1w�0��VL�g	X���-r�{JL�c�2$j5�?)wG@P0��f��-dEk>:�|���;�t����YS)��}�������j)����"�$1$������Y���:�M��fWL��R�)��_D����t��SEq�Z���;���-��p����k�\�����i��9O�7Ul�,B@���t�p������;�"`��V���-W��"�g~E.�vgA�v�_��������k�p#������i6�c�KHI%ce6)��U?��p�������j�����y���	��h�p�3�Z<AOr�BEmH"�����Q������G)�+��'�,!d��%^'N���Lx-x����������,���q��2������}�M�g�n���5S�}%*H �f�}�"�-���vX(
�L3�9�e�\��(�^r_��� � �p�V}�w2��/oS)��JJ�]8k���Z�=!�QRjq�tL^���pd��X�\�G�	<��o�gC�{��G�;[aq���o���@y��,noW�F�
J�'T�,���
$��My����vf�Dd�	XF���q�<��,��o��%RF"������B��W������2);�zUh�r-����/_8����4�oH��:Z��oi�
n|�e�16�H�h j�f@��c�mU9-��4���E�X�t���{�gy�b��+��$���\�� �q����P��Kd�e:.��pg�a��I�Fw����E�x-�eT�����)�������0�������L*�7��A���c�|�5��Rh�#����U�	�l�,%���TW�J���N���PbX�����8c��5N�������5�`���|(�,8���.������u�3A��0��P�k�nu���Y��w,�����?��a
���7��L�8������i�$�r��3���5H��l��������a�N"�5�M��g��E����<#����d�������(��v|�=�
T�z��"$����4��sB<�n���'��@"�C:(cJ���b�#����a���GIK<�#�Z�G�<�5u*��2�d�f����:���9�ayh���v�IY]P�^�sC��o�=��~1�j�
M5�-��R���A1U%�?		�:*�2
���[����GO�+O^�O	c����Fp����h!pr�*{��R8��j��`}wb1M�o��`�5�}�����������u��V8��>��F�e+o�]1/�.5}@>�|��%��0���)?��7�Op����F-}�?�.�.~��+=�;S;����L��30TD=��
t5Gg����ZjEA#N�yM��p;5[�ZN���Q`���x������N8��CX��#>���N���������E�/gA%���o^ A9�P��YJ\b�
�(s����!�&n�������_��;X@�����@�S��f�j9���8U@"����C���R9�k2�� ���
�k"F8D~�W
�6U�xpW('9�  ��H[%��\���x<
5��ZN��\�q����.�`�U���-�Bu��LQX��Lo��8�o�����������-��_�?�������#���z���g$y�gU�\�E=��,��3������K���%EU.��� ��G���*��^����g����p��SzX<�K�]<���|@���k���a�[:w��T��O����_t�,I#,(�F(�9qT�0�O{�����9�rey���	�������{��I��>�����Ydv�'[�T�,i��fJJY�����YJl��r�xI<t���l#���������D������S���k��I��Z%�E��I���%c����'{?8�c���av�����p?F���)��gS�Q")�9H�!��}�S����:`-\�������Yf��@�O `����j���'�J��JZ���=�Z\lO��0Z]�q�������i����s���O���7Rb�DG�F���>�*?�Iw�p�$z�H����U��g�S�A����Q����{t���	5B��/<���^�����7��?R�����O�5�I?"�]�W��v��8���&7��F���?-���(�]Mte5�J�J.�T{,��-s��'9u_��9{	�^Y�E\<����9����?�G�|G~nE�p6�O>z��9�t�F��������S�Oi���/�r�����c����_A�@�]��M�(�q����b'}B����������G*'����{�\�����<
�
�O����SK������:t)��H��ZDkt��&���\��MnG�|��!���.]m�ha�[�cY�8�O�r��mh����C�+���%D/��q4���I�R�-a��NH"���������4�n�P���N��I��4k��u{R��.�.��)>2j����X^�w�N��8�-�Hq��7��v��I_,x�I��U(E�`;�����n�\#��`��?����
���H�os���N�_>Y����[��H�@�������/���W,4�3�5��w��ox��d@�hH���	F/f�/:�&�[2��Z��L8��1vJCp�p�p��C}B�j1��j�I�/>K�h����?�����\L����!�@��������o���d��o��Y6���uzf���(��,~~L�b�:�D��W��%B{�C3?K��jM
~jL}m���u�����%�:8������������f+*nH=�
�B�t�a�&g�nr�u���O
��/��w�2�?��_�aF?h{�r�b4��Y��/~�AQ�F�����V8�^�����y��{9�u��N67��I�O;#}�=��{I<�t�QF�>eB,�����S"5K����d�_�_�Y���F[<�+`��+�c{l\]A�M$U]��
�C�@�W0C�{����5>��K!�~-D�*����o��w��w�7�Vw��j�CT�]S�-�� ��"i�p~yC��*w-QZZ)pUu�~t�7Rl��E)���0�@��H�	H��q/=����X�.��s�P�v2�0���
"	@H�#Ha"�� �M����������?����k[�����{l3e���`_k�-���.\X�}N��$
s�!��<b�J�Y%�%rh�P��~�Y����|���\���-M
18�8�A����jE���!s��_�m�����F���V�����������O�F�7*H��7�
&j��b��6E��A��6l���s�Z*��~�<|i��������/?t.rSY��Q�+QR�6���E[�'�r�*�RH��*����Lz�Y$�q���U]����X�;�[fHY��K��X~55|�|}��|tF\�Z��[~�_m�`1��J�Pq=���������c�e�����mx�i�����[bW�M���+ ���+�#���r3��j�L��i['F�6�U�� 9��
�����T ��`�WWI����[�gO��,s���)2��q?">����JKv�(�a��bw�����`G����J���wLy����!���A^���(�)�P]f�#.CC=��8��_.WO��k{��S�
0���QA��A����]#��
�d\��"'��g����F�����7s�*�5#�r�6D���fl3�=��]mbA�U�T��&/g���L1�;D\��V�%����(Xh�������|*�'aJ�
�}5�\�ua�	�;������YT_tv�8 Mj��7+R�Kn�Q�!�?�����������{�7�p}�oN�]����
d��@c{wh�UEg�*������Xtc��]�04�1#�����+�3_\�i����5�a]N7���}�"��6<u�k���K�����0�j��^"��o�h^l���������\�k�J���O�sW��}M�b*�(H�0H[���ir�>�	���|qm�0�B�M2D���"���B;jm&)�`�Z�w��D�����o�h�%j��dV��t� �7���T��z�5p[����~y=ag��)�����g����?������._!����6�����>��3����l�x���L���?�#��Yt�}b������|��lL�������K<l�z�^V���5�q���T���Um�RW�g(+�M,_R�T��K�FYuP�5'�*�]C����c`u��ApW��U�7���VN���{I�����t�w{D�}G��I�R�'@w���FI�g
����R��O�����nM��kvz�5dv��M�L~Itk��A'��z�8��~�?6��������on������>���H���l&3�,gYvT�e�$o�7O����
	p��v����y�>�h����;�w��b�h4������;��g?��s$���r�`���8���������jq���5��$Gq���O�v����;;|���h���,������8m�^E����������~�*�"��������q�����	&�6R��1�$�[�@������������S�67��1�{�q!��	�>�[�3�����Y�S�����6�n�($���qU ���������p�����}�_^&��W`y\W��lxLE�-� �mg�\��h�v�b� ��'O����
�0|b�����WO���/�7�����I��1�H6������_��l?>I�v����?%���������kS_����9:��m�9��� �������|������WUv������������*��=}quW�q���w�Q3�>.���(�F�/��k�u���u�����b<]�M0���;��jH8z�X���z�,O�	�'�N~B5�3��][L���y��� [4*�YK��HC�'p�&�~���?�DTS|���E���F������_��]g��?`G���
�w��-'#k�Qu����[5��S*�	����
�O��5r��s��9�^������e��a�#����T)�&Q��I:����>l*A�4�F�����k@�\�/iu��s���B�QIz{� �|��.8�w���hF� ������x��=�z�b?
X��Z�������*9�d���Z�g9�V/�]d�3�F<.�(������X����F+-w?r��c���+��r�Q#��0�J��.x�i
��B��?3N|�(� ��(���I���4VZ.�I!f,k05���*������oK#���a��:��1�%y%���y����#"��a~���&�\�C�pi��<�������y�#&"ix�u�*2��=��Km�}��K#�&��8{pvvtz>B������'W��(�tD��9[tj[�y�G�����[g�C�O����?����ZS�k����v5ip�����Y;�?`�y��G�������X�?t�#�A������i���}�P�4������-��l�?Xx���8��:w,�!'Y�<������f��r�6�(��PA��/��m�e���K���
��{� ��6.��b��Y�A��1C�S?���U���i����x\.��Er����,T	vh��%KqF=<��L�$=]qt�>O���
w�k��S�\��u^����m�E�Z�8����%�/B)[Id O��xu���[|��o��V�>�5����8l��79������H+�Gl_��JBn� d�����1��ZC��hA���L�p�aaF0_k����b�3��16�g�����t�v�|�g��G0��F��b@{��"?�'+[��V������5Vt;i~w�zG�Xu8n���s��rt�\}:si���,!��vb8��������9�/��G9>6	��I�%�=��I�j��?���%�,*~l����o��d|< �<�qo�*K=��c�F>�����;��D�M����Y����L���"������=�������b���������/Do���3��������_
����>����������H�����|�������	���~X��K)h��:=��IP����������+/NCP��*�2���o���7����iQ�<�������^Q���>A'R��hh	��],.�Bj�z?-����`�*���f��7����+��4�$��~�?*���/DT3<��y9��W�:��2|����E��xeT�	��T���ODhP{�#d��$������{+/���3��c�����8�������-N_p)��C��f|m���v�fF[��.%����(�*�T����Z;���6�I��[���b��R��L6��f����\J��0ZD�Y!�!�t���9tsC`D�B�K����������7�b��w����F|z���r`gC�>�v34�t����������$�m�2lr���tg���+����E�����s�f�m�a�����g6�]7W�]��f��������h�w�$���D8��L���E���*
��5�����nL��lygSk�{7u�S�<�8�;�x��tG��Ky��k�
Jl��<��Q��C{T��j
��+��h�z���v�>E�T��i�/���Y�1�j���O�/�����.�-8�������������}Q���%a���m9~K����Q	�S�������v��I�� ���z��=�.����`7�N,#���8�������ws��>p�qG8�@M���t�I����{����N�|d�Q��o���-2+X�V�� ���]
Kt��U�;jA�~Jj�� wn��=��'H�����=�����A�k}�Ioe�75Z7��rw�/����$�s�7H}����*�;�����M�8>K��O��'��S��O����;�*$o^$�oN�NO��1H��Ba.hFrVU^�������<��#�Jh�V"yq��kH
1�v����Gb:
s�1�<D��f�����
`�VJ��������*'�q�5�8'�B����^��~q�hl|})]��v[���u<E�G��7��sd`?7}|���]U<% ��C���Y�U�����~�cYQ���G�iTC�����8��5�()��QQ4;D�J'L��_�\�����������ZA�wQ����xc��t�	��*1&�����������.7���zG[+,���o��N��!�-J��U��Bz$���(#����n��}��`�����-�I��[7C�5*Z�R�e���v��p��q�ms�C���"!�
��2���q�M��(��S����6d�����~y�)v�@�Q�he�/��OF/���x�������������7��^y?c��?�jo��8m�8�����;���9�/�3�(�����ni	!��G?�����;Id���M�$�,Pb�d]%'��&X��`�iN���'u3�j$��a��nr�l������$�uP�����K��.�"w"�&"u���n�i�*���+�vn�[��r
���I�r�2�haJB�+��bA������0d��Q�4�1�
s
��m��<;��,��F�B$"�s���]YXl���`�0@�����R&bu���,���0oy����C�a�U��>��;x_���R|��q����������I�A/��8M�|�Q���#�G8��
6�!D�v�Hq!�f*V
��n��d���ZUw�
c0j<��,2��������X"b���������n��f����5D�a���/�NG���H��2BZ���#�"5���M����3��RZ�F��������5l�f� 	]6J><L�!��@vc�~��
d�DC�|�)����?JL���c�	m�_
Q�;���
��Q�H
P�����Ii��6"����p�,�&-<�'�u��Gfd��6c�S�PB:n���>��	�
g��Bl����<�^��O�|���������[SD�2�f���2�O�rd�Z
���H��k���>��D�#z��v'��\UF41��NCAg������F���C�p	����[���\^]������+�A�y���Gr#�DBIb-��9����X���dS(+	�@�-h'�Mc8/��8���h�D>H��U�u�]���H�0�v�vt�	0���v\//l+����K����0���G����3�G������k��@���M$?fq-IC�
�b���D>��7�62��'��]w����$?6O�*��������Z�$y��oir�T���U�#A����_�I�L�^	a#������p��6�/�p%�l+���������������>o��m�����t-���Je�Pc+�$&
-��n�����o��DC�X���{}����tJ�i��0�?N�(�q�p��T�eA���j���8�T�Z$C&�^x�x��%����O��$b�
�Y2�'�)F9P����[����N�tQj=C:Re��i�0h�.�����J�����:C�i�>���!g�b�@+XA�1���&~�6z�
��T���G�M��5��G���1�j��K��1�>{� �>���a�$�(���`��"�W��Uq�����Y�H��\�/�~s�O�zU�+�����r���.%�+�g9E�U���b��G���md��rA%.0M����q/F���!x�0?����u�����RT���R�N%2`Pr��i��o�,��L�[a���#�P��0y|����Z�J��cE�"�0����q6��N�6��x�dI9)��g�tc�Q�����R��"��s|���O��	 Y�Z��	��� ��.��:������������=���g��I(�0�k�?��hZ�iBDw�Fu�7t��^j���qT������3��(������u?�y-{F|��%�7��bNQ�����I��&�I8mS{���XJ���,��,V���S��	��1�h$���N�[�c'�p�%�'�:}|]�
��3����x���f�c�!���<{+�@���^��t�Y��bhgx��?}�E���b�Q�n�������U}a�o?�P;	~v7���<��%�	_�<;yk�2sV+�~:����{jRD�U6��}��
6�~_��!.o�����E(��*�<����F�R����r,���]��N�z�-��]���z����
���E���a����s��#�1����R�����'�+��������S���.@O4�2>���4n����Uz�A�����:�Nk�'��sl���1�W�<��!Du?�I�-=xv�6������N�Y����
�.�p�"QS��EWe�����A�3����r����k�.���F�.�65������+G�����:�o�y�����L�/�/�Oa�p����cP��F�\�Z�U��A�2)2�L���D�&CiVM�����TB��1�f�6@�E���K�m��7��Ua�����]������$���Q|	t�Jrd��s�@�x��v�2���TBu0Qh�&UP��E��x������N��v����2�AFi�X��i����iD���#e"JcE��z�(�fn��M.��
X�7���F+���ix� �;R���������
�����[,�C���U�;��|	W�����Dz���6��)�
�
����`tWyQ��b@�c�+�����r2���������s	������
��f4D6��e��rou���,�
���-}�Lcli�XlHI@�}�%�3����fi:����\�n��KJ�$L�Mz[C�;�>�_���W"k��`l�u��{h�6��
z��>Ar��� ���eY�_x���Flko�|�
�o9�>R�2��y���_X��Q��=���y���>���+o?�O�,�#
>)�h/h`7��^H
Nk;������BzOaSl�Y3��@���ya����w�a2�L����a����f��`&f�N5n3� ��B<|�w�i+X�ki3{���0b%��Z��0��o����:�����I)�
�����]f{{.;�o�
�e���O�[P�[��	������>��n��^��(����"���.�t�c���3������4n���q��k��8�������+h������������3��et��(�+���e�`V���8���`�Gv��y7��0�,�H��O�_
=`��#�Y�
���hF��f��L�^�ES2�B �"�U�l��7���J�j�T�<=�/
������h��vz�n����Rw���==��cz��Nd
�a��?������������tB�f�uEDT(c|�F�0jv�wf� ���{6�*����[��Q����&�e����1RB�"�v��o~��QrDN(���d���q��)�b6TRi��4�a-�c
L�-�{���������f��g�7'��=�J����tOR��������8��8������1��o�������;+�Z���|khl@��$ �-�H��L]`��~}�Z|d�=������s�^Yz�Z;�&tv���H��!<Q���K��d�����,T�Hn��!�b
����m�#7
PK�����&�.�������R��3���HD�F��AMn�a�"��
����R�����b�����������'v.��J���~{��:����;��@�s��l�_m���%��*�xH�B�U��������E�\�P���s��0�oa�����]��M�����tQz��������H�����B��z��E���ex�	%L�m�.w5R�������j;d�kP���=�n�a���GL�A��M&���$�V�����.�+�A�E���r�3?���/���'�WB�t�\�L��RlmL�����|����.�+����������81����4�r�@W����m�����#QL��#��p8����-�������/��qkJ�XZ_P��U��@���������m-��E�g���B��(7P�XC� ���7��i���T8���I51;jj:te��!V�Q���FS���M�����(�(J�A�{`ei��G��X�����^9�@��m~R����]<�W%�.��&���V���OQ���-�N����4��9�[B�,�����
�����h��-
���F�� R�s���/��>������p{;]��P����ZG9\U,�}�04�gF8�~IB���{&D�����OM�����4�����M��,Q�9�.���;Q^xh�������i����s�]��8��
�F��ltZ��������=������q����CH����P�.3r\�q�AW�Se+y�V9l���.�+ ���*J�#�&i�R�E<�����i�f[w��0%	#�4)iz����G`����^4������imB����^������d��z�IP����7t$���������\q�v��W�~�E�8����f5�Hq�R��fF�F������7%&8����"4H|
��Yz�nD4R#�w�,�5�N�."�''�>��=+��~����!~_�.�������9�FvJ�B�����@!��
���� �������Y�^���N�6��0�����
������`4����;�
��kR����O������G/(v�tV�}xd�W��nw�$Q��qx�
eF5=wu4hrO�o ED"�����R�"����[I2-R\:�U���(qf�N���G#��SvB��������t3u�����j�m�����N��:G��I�����M����+��7�.<�I�_�
5}��������&D�h���=������^5ey���J������q_�����LtH�_2h���z{C��UZ@N=�*`Z�����l�D�A�����B"2�ZZU)y��5��(��8���0�BNb��}���Rboy��WQfG��z�"����������a@P���
��*���l��zf{�^�M_��.������]��R'�%^����N��w�A=��
�x*�7�~����`wl�����c�D�=����`��B�
@��g>�[B�K�<��2��������v�;��^�(��)
��r
�g���?�C��h
#^.��0��? )V$��-�ui��T���Q�?�����<�3I��h7���=�iiv��=u;4X)����RpW��,���%��
�>���q�5=��`�^#(�U�P��)�����2�W���j�<��#l�����.�-�0N����?�������=������z�g�1��\�������7#&���	�Q�8��]�x_c���j��A��[%�����bIx�^I;�,���q,�!�t!�������M|;Ivz���0b����D�t�j<\�������[�:e��b(s=vBn�������E6���a4��f��	���!FV[���
p!
+3K��v��8OF,6���p�����:7���_K�� J�=[&�e;/�.X��G�+��p��+�{M��f����\��6����6``m���\�WI���~|V2���F�����"S)�������dY�I�ks��\�&o��A��4�
0��rYR
+ �Z���i9�B(A=��e�����=k/,����]�t�*������"�h�-��y�^V�nY�z@@7�w��*/�f����$gX��+�@4�Y5��>�`"�U�CT��,���=��f�A����Hn�o�z������1�~�������_����jJ�:���@����U�����N�V�����&����CP4�4�7L'��s�Fh�s������N"Z�
kT���)�T�<�"(���:�x8����G�)�a�Z8?a)��#����R��'C)�FC���W/�xR%����.H�g{f	1E8h�y*�I������H3�#����%�����������_C���'J��#�X��<\�] 9�0,��=���n�����7��f�2}��S�����&�3�����.6Q���_��1�9-�b�XA����	�����1)�l.�-;&)t�5�7�A4��u&a���pz�T+]�������S�2���;R�����J�R�D��4����/J�����%<����h�8R(�PZ��pXS^�c��W�����e����-�}S<�%O���mT2#�x������z(*�=��L�8Ld-���4�x=����}}a��������y!;���N	~p���!eI@�C���L����L8��-��{�����?w��>4@Z`�h6���^���8<��eE�=�������o��z��'��M,Fq����������9�a����U�������Q���dF4�!
*_�]�0���V�<��ZW.���U���0G>�\GgA�I��T�P���g�<#E����a"cY���~�b*��IMm�4WT<6WYj=�_���cv=&H����'F�h�T���I�a>�<1BG�#4��q���k����c�����`���
b�UuM��~]����Eu���*�����>���Vj�kw1#�<�a����d�����^o�8�tF*;���3�`���u9��[L���;�wF��/_B�������!������)�D�g������������!Ef��/�mQ��
�������G�cU������=�n��s+��Krr!w$��12doZj���
L�����r��y=*��o�Hj���S�	�o��1�@�k�K\��O�q�����*LP
�T�;�k(����gvt��^/�������-3�Y33a��G5�=Jon�$��K�^�����j^{���F{��5����Fo��6~��.�^C�\.�:k�5K'���>�m���!ue�4H�R���J�\�@��-r�8$�h{�L������&nEo%,!eY ��7<it�k!~�>����~T;�a��rSe�%�,�4���Ik�cR4�!����M�!2��:��h�����2��S�#�;���������{^R���sv��*��&������x����_?�� �q��y��5�������(�D���z5(8h*���7�����I�.��S�!�����	u�����g^^�~rz����������������������@����D!<3�{������3/�q(���>#������7��������	F��������h���OZ�w�K��O�^����p|8:>9>�7Z��H����Hv�����f���j�k��U�no���6��??z�v���/_=
�z���o6������m�z#�b��c�2��������Q�;<s?a�M�T�lf��,�rQB�2����Z��-h�3����fG&f�5Z�nhV���l`����rp���������P��mb���r���`�-�)+\z��=C�S�wV��^Fc�����OAY3O'�st�������_��u�J�~:�������9��Qw?~s)i�����4�����P�t�����ca������F8�_�m67�������:3Gu|}^��9��%�=�f�
���3<��V������ �[����g�<�U}�I���?����6;ro�z����S
oRl^�\���;���-��+��5L���^�����8_����������C�n����bR�Ip�����.D�����A���CCT'���l��r�(!��ynZ�,z��V-s�����"`$eq��O��l�`J ��T��E��fV���Fjj���h\��#�sI7�j"�>����,\������(��@(�C�y.���0I�{^��+s�4�u
��(.��v�H(����VTO��r���x4�Ez52o��&<������Uu<��j�g���=[c�<���F���,�Cz����p>�OZ(���[������Ry#�B�7LP�=�����E��^.�[C%��k����Q���[RI(�`��
xM�)�Q��Bk����0e=Q:����[����I�Y��^>��/���zz~����_�;;���uob�p`�)�"����GU��g�c+���a���7��B[���������:���������fk��z������G�&tM6<4�e�d�$b$�@�q�R����~�Ht(���:��_�B����j�l;�U��#0���"��M	�j��+:��G���
`��w������V�%����E�l�O~���S��b���,<�&��y��%*��R�����1�j�����{�I������E��[����v��fm-Xi�W�%qK3s��h���&p�&������!�����H#���?lT���ort/�F{�hr�!�$�7�/D9���s2G�l�X�C��v���b;a�Q��e;.]��n?i)���q_=1��rQ}�k�	 c�e�6�����u]�s�&�QD�,
�jyM���~�M����"�|��S6oj$�)����������g����/#H����UJ���r
��#�~.�������I�p0|g4_����
]�}��"��*�S�@�W�J�WfS�N�g�<���l�����y����z��t������CqC��S����������~e���G���G�g�g��!���e�)��w���C���b� +r�#����f���>��.�����y����
����w�,[�;n9Y����j�,&����IZV�/��7%�-w`�#���5���XO,��S�� ���K{���t�z����r��Xb�����5�`���Wu��>X�G?�z��|������i�.����Pf�k�~�,).����K_�t�b��~@A���aK��QJci�{O:l��*5"�"�X�
?;VRIj_ke�"M���w��k���B���7T����
�u��j�����Z-��&[�I��t������-?������r������'}���d�`��o�(������
���2���T�/Y�A�qA��F63[^_�z-!Om@�#���0����1D�����Hg^d�,xa|�&��-n}�����?��"p��-���G'P]����i�&����*�1y9<YC��D��-�!d�	��
�L��i(U
���d��0N��N�"��G���z :��V��5gf��VU�9�@�a��,a�@$/Lc�5%!<�r�(�DW�������0I��K%�hKg<��k�^���&UGB�}���b����t�OB�MJ���2	a�8
Yz?�ZzY�Mb���	����?p��
#�t�����[3���nj?"!�n�^��k7��^��
������K<�Z���C��b;�
,L��%�R9{6�9�Tj�[�I����MP�w�
����>������]u�^��]-d���T�D/����w�X@�;�J���8�e"�p��z���!.�~@d#��^�Y^��-��%P�*�Xns�%�8V�-2J�K�xK�=�T8w��@�$q,dL��G���n����K�U��8� ��D��a����������t�A���gc,��
�C������~���l���c!��m18����+�gj��=5��l�������m]��
P#�� >�N�8����L�7X�!b&�$Z]-�*5���~�@Ijb~9���I���nHb�AE���G��7!���+��+�%��oI)��92{�@�R��)��N#�M����B�'.�\�;�ou1�E�0��U�;��uv�QP����������2��/S������
�:�����9����n���*!�����~S��� ����F �A�+I����L�4�W���l(�����w�����bW�b���!D�q]/Ig=]�DT�v�������`�k������]s�P��t$41`�.��l�C+z3�>'fM���"�6�i�!]�`C��1�+o
��s7���<)W��W>������jEB�1���U�E�<�y��t�"�=���P����������@B��������O -�M�0�e���O��NY�%D`����[f�l�ll�*
������K@�r��N���~�L�|����6^�/<�g��d���d���$s��uLm��|MsIqT�N�"��7}���8�a����EVP*��m$�:�%����v���1��
FeD��N�#n�Q�-�#��HO�����)�0��c'Y:��8�7]�|�_o;:o�����t{�gQ����{�].��"�$�R8��x�_j����sC���x�i���
|=�(���N�����s��.�^��{^�}����Oi��wf
�c�qJ�"
VFC+'�
�����e>�p�����,>���9��`Q+s��L�����pu���;S��-���eDm%+}���C��oN1��R�
PPD]�=c�+��Z�
�*�
���5�Q=r����g���
G���7T��G�rD�*�NZ�k��6 .�!�{{�v�"��d8S���)/
���r�|WS)P��|�CP��M?��b7|����o&[�����%$a^jD��_���H9�����&�IUkh9����k��]�x�P��J���N)�^C�,��\2���%5j
�GygH��Q�0�F6Wl}]�x����\�Y=�x�d�/S��1�KZ���%ga�C���,� M�@'7'�mM������������<dh���v|]��������!b�E��l���_���F;�� ����pC��	[G��d��#"�
a�R�I���� 7�N�� ��9?R�����]���ls(Hp)U��I�8���@��-����RK�m��a�������[�@�Jx�\����3I�x6��m���?Z����M�E����z
*��o���Cg(�g�6��%�Y�[��8��������g~�
����M��M�d#IT����O�
C4h%
�?���u��v
��kg��^CU��6���*����F/��=�j	�)����-��������x��U�����X�"*0�$'���Co�#20J��e���T*F�q�.������b #�-d��ia�lIw)Zp1����G����
�[tD�Sz�2�w7w��k��H�!�+��N��p���<�mrP@������=����V�[����2���C	)0�;b����W�$�S�!"OPF1�;b��S�89��
^��VSs}'(3��#��@	dl�~l��������`������H�;����dz�3P4����9|���%��/I������L@A���4�.����,�,0'����Xlg�:/�6\\xB�G1���x|%���%X�����WE����\N�Z�A�
o��	�R5-��P&��E����9�QpMz����������b� ���b��������|��2�������>.5n����	�����L`!������G��X/gX����&%�����L�W �!��9�{��:BN�_FV����:X1k0Y?��5c'_/K���2��g�{��T
g�8}Z&�uG����m�T��W����%��^�;p�f�
�+�6q���(��Eg�
%o�1�+`+�����YP��3������aH��?���vn/<Fj��%�xP�DkWX8�8�,1�oT.��;[�Kt�\@��+ �p! IF��I���lT��m��g�dp���ho��HF��cc
�P����3��U�3t8�i���� 4�c�\(Rc��Cs9��P[l@�1�RC~9"�F	��&u��gS�����}�
��r���B�E�W>�N�s�/��������l��~�����Q�Z��E� "^����G�cl5���k���E�u��lP����jh�������Ns�:��{�Y�������P�"�$�P����T�����C(
v�������pf� ��4g�q�?!��:��X��C7}|5�~}���*ZSyc�W�i��V�7���wsoI�J�|�� ���A#oI�W���}���?�=x�������WIzo������?���0i�}JJ�'#S��4qp�)��d|�-f44CY���(�'���
�0By�jm���������r����q;��h�M[H~����q8��t�^�;/p��U�h�c�#+�,����uP����b�h1A,�Q�X� ��l6�|��6���xt��Z�C/����#4w�\��$!6^"�Ua���+P�<�BO�	���A��{#U)iI�����T�j�t
���'�� U=`1t��:,(��5���=kS 
�&��g��,�7B�����u���^��5k����������"R$�K�uv����|��Z��.5��Bl�%x������4�R�l��ru�58cy�7�KG�oD�f�����O���0�bJ�i5#��tN��"��5<w��W�.���I����0�#���0yA@��c8zy����@��q�}��N���wq����v�*�MU��9��+�/��
:�^��<�j���4��P���
#�7�2����o���L����&}�"�i�	7�n@��hP5=#��y6�E�H�	"��P�P�3��@�T�XR��#��+�_��
�������2����24"�c�`��D/>�����_�7L��tuz1�zO���-U�w"��V��Q�v*�{�����V���P_R*T�l�{��}���L�i��V-�b�]dC���E�F����_e=��	���o�b�_f��G��a���5A�
 K�3�h�6�����q
�I	:�FtQ���uY�{"�m�Q�

cg![���U�S��{�V�L�������7�X����z*4��ZQ�����b���7���p
�������9����>�����]�;N��7J�g E�hg�f�I�����y��G��H�h��K�h�N:q�K�3�-q(g�zm�-�yMZ��p,�m����'��._������M�������G}�
{Gp��R-k�`��L���rK��z������������~R^�~�'�{�P����������|��m��{������_�}���2/w\K�����*���AC�����!j�5��Z�C�=���BhHr������=�������	�:_��7_���	7�=����@��,�����n�N�/���6���u�������7�az=N��V�_[y�W����������y��qn���P�	s� Vi�~{�����a
m��x��������������%?������[^���[�x'���ZD�������7��4��d�c)�q�99�	q�������9P'{^ea{���u��O��;���Xm�l���p_��I���H8����x4.���;d�vH;A�e]��;�Z���Oa�H�{Z[��Ex��0�f���"�j[3�_H-��a�H�`�.Xw#���9M��}�E@�y��P��4�����|���7�GW���

�V��6���~�_�������]@C�����b�]�*���$��@}����:�R����G/^���-&6�i�=p�('�)����+O�S��@��!�����aK>���������{1����������|T@Lc�����������P6&���Vy�V�#���P7m������J�����)�$9VY�������@Y��ns���R"�:| H	�P����C�|�����?�|��H�,����y$����]jN�92�1�b��n4<GO7e��j_K`!�g���5�sp__����.NzSm�G5���	��~P] ��R(i-�4���o%{:�G+���`��(�:e8)��	���dF��|#����BY�AC���iM3tz"��/:����zD�IxGK��A����67V�
�`Rf��5� �r�h�hEWuJ�]T**���}=z]�����B���lV�Ri��O�GY9�?,���I��-Z�� �
��b��V�:�[,���_�p_dI.���o���<�����"-�Hb����nd�E.��4�c��4�/	�$$�^1�����~�:��U����3��&��6�C���������y�3��o��W�0�"������<����#����%�����]D8SX�[>��!���G|���E��B�'���	i��O?z��VP�����=Q	���0���Ga2���
���R��c��8|z��Z����sSF.8C�pIt!����T�`_ ���u1��(��8�\��!0��pk���k"�4���=B)�IF��

���J�C�5�����"Idb*���4#�:a��W���*�zQ	��
���[@ ���%m��IN�7l ���'�DH��������c<�O��^a	��qnh���5:8y�1���h�H.T�U��3��
o�s7�$?Y���3�i�p?�1,��D6��=!J���O���e��8���^����Gq���Hb�\������� �N�h��$\�Z�q���@��(DH��4#���e���zZ�Mwu�����g��_['K~m�&Y�m�%c���$_���	G�:/�#���VtX/pQ�Y�2�$_����.�-�����������?�N����y p�''o�_�1�$3<��\}3@�+�Us���'���?�PQ	�}����	 ��H(�5�V��a�~,�_e�������*�6���Rt���U\n�ND�����9����r��F����VGO,�yG�B�i�P�q�,��^��r��f��^�2���]������#�)�HB&<�A"�
P�C�vL����tL�����h��$dT��i��u%���G����'����XT0^2�w�����V$��l����b�`4J6c!d�%�_P����]��E8�8A��8�Q�\�����+E�Dd���	F�����<���f���|��%
`������9�T�{
?��3�9������F��V����!���LE�Xk�n�(�����kMb�9D�k����������r|Y]��W��;��H����J�0�N�D�B�"HD��2����Q2��]{c�/4�F��z��>D/����@�M�cz�yWdO�I�i�%,+s�m��a�u�6���x�U����*��:���7��i����i��+MSs���8��5�l&�.FH%,5����c4����M22��8l�p����Y_x��LI���rW{���2qe$8BCM	{���i��[
��8R>C��>�/ ���UY?�]��/������(o5�r�+������)�r'������X�Oct��������y�J��������(O'����W�������������0���6c��l�����:�6[[ ^-�L��e�(oki���1��������[%F�-EA�H�"-;���)4��/�����k����q�y������^�����X�5��5���*�O+����^����-�>���K���e_���V�@ ���Ik2�A��J)����T�Y,vs�**�.��y��A�6f��(��A5[��������������B�+�	��]2�������p��!&��9���y�?�!��� �6kp�q��)��x+�ZI#|�^I4�����B#������ZA�Ck�\��T�;������Z��D����;�D�r;u�N����*@�D
�����K�TgV�&��p(�(���.�|�l'�8�s������B��N�H��'�B���h7�������k8*���9$�p��NA������~����u~|��GK�M���Ta�QS��L	�y YJ����y12�&u=�Qg+I~�rat&�w�i7���&� +%nP��h<K*!R����C6^Rp�TH ���R�:*�������W%P��d$�:�w�-+8Q$e�]/_����f�#hg+����'�$����
�q[N�=�����-��?o��G��� �/2�
�`�����`��lb���l��p�|�{#M/m�r1�������'��F�hFu�hr�����
�o<p�������Wg' �	�:~9�n�#5��H  ��>��@���������8P���}������\��!:��B�S��Y@��.��Uf�x���4����8�K�
�+�����j�yf�P|ka_�Z���~4)��2$i�1)s�\���z��@�+�7��3�U�|�=
��Aj�f?M�������j�a�H���a��w���R��4�"�xD�43��(�G��=2���� ����UG�8u$+bU��}�UFZ �y%6B.��+���<�[v	V���(_���N�l�0 I����������pY\����8����J���*&an��O��r�&�1�D�p{�OE�J���K�$4"�; ��r�8>z<�����!����jsM��>~ms�������'c�-s������QO���9��e��w�\o6@��7���;Z�������Sv���c4��i_���=�j��rA
N�2�-������7�@fk1�sd�s�!���������'��F�sq�(����.|�+��s�lz<��9�����p&�e/��@'�k�i-���J2�3��x��!4�)��jN2�����������r��'^k�@U��������^��X����s���j3��
���� b�6�l~_��t���^��0��tm���BH�X9��
B��g�^ss3g�a�Yp�Kj#�`w�j�����'m�DJo
b�~&d8�3�
$�ccRf��e����Om�9;U�C�G?
���~������!%�Hb�>$������9`~@�;��5>�8Zx_�
��y�.5[��4{�M]Y��WP�e����K~by���20��Y���1	��'a/�y�5�}^�A�AN�D��cHCb��Y�1%�
��1��J�8��e����+�+}��	����C�B��u9����T�h%��B
N�����FXT�MrB@D�HH.���@��to�-��l>j
��x���D����P�$��!iJ�:�5��{��S���,���jV����J�n�Z�\�w���V�M�����s�;���������WG'����f*�d�S��V��l����``nk�mS�Q�j���V��I`����X��Pk2�8I����z�
�����"J���f�6�aQ�>eO���
�O��I�*���(��zY��k^�����V����H�$Eg����Z����.�FM�f�l�����,�:0�R�Z���" ��~ZW�~SP761����9���gX��__<�����M��p�=Rk�
M��^��q:GD#�N}��_�jfB��R��o�����v�7�7���|����@��,<Y���:��T1�#��tp��!�}
X� M.����
�Jk{B�.��-��f��b@�R�,��}9��R���*�_��BW4����*�>��P"��,��d&��{R\�N��7ms��EX��UT�Y�L��KN<��r��+nR��ReI��g^^�_��m�(DH��y�K~%�C�;B���g���*���������� �l��s��#`h9WL�DZU�
P�������>��r�C�
C���������z,��A1��e��(l��D��6����}�� *�~���0�9<jxY���|�����c����������������I����=�>���Z�����TN�w��3�y�q��A��b���B�c�"��N�GV���P��Id�Vi��g�����"�\a�Y]����`�	���O��!�+R5���%��BW��:� ^H�b�H���9 �=�A��-���\l��T9��Qy��e1�aV6,<���	�/!C�s�F�0���
�nC���\H�+��t2�-����(��,��j����g�-6k(�Dq�|�N�[`�A����z�}0lo�8��B0B$P�L������|$��/��y20����o��52��eie���D�F4riy��P{��S���3]_���E�������?�SL��I��� �E>>)ge��=H��Y��GB�@�d���o����oD����
��#��M��2��������x#D:��K���9����[�{o����N�+��AC���4T?���b|1u��!���'\J����a4[$�-������;-������,���Cn%o	�)2���� TvV�7��0@>:3`D�aLb�L����l�:%���d[R-��5'"�tu��5��G�����As�(�j���{Y��������������@����
�E�ol����B:���"����+�
M��+��7�P�_r��9�ByE�J�v��S+�����Gr��!�\<7��4����"��Q�x�:�E�0OW.'�X^"��
p���C����������1�
E��
#'~���@2�g�l���-�:I%W�e�2;T���Xw�Q�
d#��Qm��b)��{��@0r���7�9��e'�H��7
G�
X�9���	�2�2�-
z�M_�v�`�]���`K0�U������/xhc�z�p���ac�hB.Jl���R�+�}���hjW���Ctf�maF�����u����D���������N�.)�,���`������:�"��
���rU^�J����������H{,q5I�Y/�]@F�xq��n���'7��������n��S������H��0��	:,i}M�2K���cj#\��Od�0C�I�o�s��f:,�o��OF/���x��u��9����������U5}prtr����'��Rv�#�!�RD����(gw���u�n�Q>1�����E{4����4�	�*���f�U��������/?�8	n/�w�|�����S�B��ppz�����*������I�L�6O���H�6Gr��V����z\\S�+zcU�s*�P��
�8LG�)���!O1T��(dp��!��"G���6��F��o����9c��t��`��9�"��e'0��k&�M�P6��/��������������Sf�Y���NO��<�&������lR���=�����<����@���Q���k8�l���Q�mL��s �o��3.��m���9G�s{��A�r+W���U|������,
�^�_m.5iPp�� �j;��7t6���)V��]��-�mR��(�� 0H���t�;&+[�-<(B��p��������(e���>NYq6�3`?�Q�nU�_����b����B���������Mv7������g@l^�^����O�9>�

����-8
��u����RvA�d�f���Z��Hrh�h_(Q�@��|�f��K�$�<+�	�R0|8Wi}=���B]�M�d���XM��I�|]�L����F�
�_����c��cFD��g�5��.
u��M��3�4_0=�� ��i_������!j����:�� ����uo��Z��\!���h��(\p�}���=�<���U���Z��(�)�0���4����\��Tr����Fh��?�.���<L��K�/\�V<����Z�f�WVi����-cl����M��{��vz�z��i��``�Y�f��-��b2�"������t���`�N)��z����c�_1=k�����z��]���6qJ��\���my�V3�!�5D�>�A`:+0k\�X7xGb����h]�����g���%H^������5�`E��21�`8��)�t%�l>��;���6��G`��F:C��p�����f�N80T�m`�=�@q����8
�����[i�i�#��2Wl�4E��.e��<�����ibO=�l��:,g�%��Y^��/'8<|0�uUd�z��b�
k[Z���8�!�dBu���b���������PLj���c�Y�)��}3���3�����&�M���,��|��2�F�����x�q'BHC���p�7���_qX�=p:D��<k���������>�_|�w��}Cpr�e�A$`>��Qz�u��Js#'6*mooS.&����}��~.��>~�slJ��I`��������O)L�������(�M�{;�#?e?��5����a��fN�H@����Ce�q��nZ$��j�:R1��>*��*����%���(s3;6k�N5#�g��}U7.i�MLvd�@�0l�.�E�i��c!���"�#��(��_����&Z�^��tV_��7j�'y�k3,���'���6���Xk���(�O���"n%�?��7��PT����������`�����Vn���[RUR����� Ed��?������C-)r�Hz	^S��r�Z����!B-4��2;a��+<U��\2��"�d��������8l��imEl��z�eW���p�����F?��f�	�v�v��9R��H�����&��?$Ftp
q) ��}�-���}�z}F�N�wQR�"g/����������_�V���>&�q���2����o��&�]�������8�l/���+��r�FA�w���
�E�����H��]r��?B��6��^i}�,0�����YV]eb7�(�@f���k?�.�3tvT,�[�tb�Im$�'A
g���l #Q�u_�������b��Q�%uy�	RFE�m����<��6 va������r|/���������s%C���A���G�
��Y
GA�RP}:�O��F����+?`��C1]����y��>h��X��77G8M1�p ������p~�5�%���-[:�f��G`���H��UK�~�==zq�3m+��������������H��j�����SM�?O]y�~8�����0�-�$}��?�}w�
�K�����P���
���AXn2�&��_���8>��r��-'Y�`�_<v��s�@������j�"pm��7�\J����)���<�>�dGM�EI��p��������u���~>~�����y�RN�$.�������V'P�<��A�GF:��!��h[6���
��l����1:/���,`�re#�5C#O����+�m\���t�TC��rZ�����\��	?&������� o�v
����e�	�usH��f;	�&��,$���'z��IsQ39�	[0�~%Y���"(.��\`8���9m��CB�������<������O
r�n�[����K/��]�D��3���A����C��!�M�h�I���
I6��p\kD�nc��5���|Y=HD�������W6��=uq84YL���C��A�q3�P<>��3�y-t�Y���H �1�i1��O�!K�e�F)���D	��I��uFw*]���������
����`J�R��'Xp���_�<H�$E�������������^#0������P�Q��
� e��T���Cn�j
��E�>"��}.�>�S:5z������|k�9SRyp�c��?kZ�V���H�{T"1�����P�>s��x�
�6�8L�2����Z���v��0������{x[��������w�#�V�h
S������Q1a��k8�rUX��>�v�X��������:G&	���>d��w�������!�,PNpZN�Q6	J�? ���C��[�o���O�������J�
��V�6���x%�>oc#�r:���$�2a�������
��ssWc������*0�:���
���m��":,?~���e�i��%�e�ben+4+_���a�����m����*��|�qY�u��/��x�]��Iq�W��p� o��V���D�>��e/l�B,V�XL��JE��j[~AO�P����8]�(��i�}?k;��8"�"��������n��5��F��g�U�6��w"yb,gC��Ev[�7��O3��RROk? P�@�"9s��
<'\C�b-+���A�M�����@-8u��\pj�wp2!: E��|�@��R08^�P�s�6��@J���0�U�������,P��n�P�G�=�+�-g���;����d�%��a�'yh�����GF�X%A��m�����ay�6������F��Bd��;
��{uu��#(�AE��la%�a�/2z8��zme�@���;��#���V�MD��)���DEO������z/ �C�w@�I�B���Eh��A]�?��!�R����hQ���6��u#��P���h03,��ga�}��).���Xg��(��_���(�GC��}�9���0�3(��PK�<{O[������S�99St���W�B��CR�wO���i�����Xrx]���p��)c���	�:@+��W	HP�����������f3��r*m��6`�U0W'�|��Rs����6���j�:`|#�w���*�d�b&��
�I1[�C1��%�M�;�mmozW�[���z�T-q�*|y�-k�_1v;��5&�����GVY_�>���z7�%&��<D��WW
Z����,
w�	H@��r"��H-�/���6��.,��EU��}�-�������B��U�`��5\� ��W�G�Al�(�z�����K��T���}P^^>d�bKp��&(���JE�)��|�~m���H+�lTd�H����cK�w3a�]��TT#��S��!�(&�M^��I�C%�2�����%(���!/(^O�c�������>.���|��h
qa��L:�:-��4����fe�/U����q��E��=�s��4R�(RG�a�;�?��������'^�R��k�M
Y%�R��u�Y9�/sW@�����>5x\�I���^i[,<�Esyt}�,���g�}`=%�m����pOu�6V���*G��@�I���`������|�wk��~p)=rg��r
%���L�54ce�.�dl���o�������a��
��������D$���Xi�@�ai	8i��`�)�,_1!�`-r���q0����d��f�b��#g�M��Y�G-W2~�c�E���u(�m��T����!]��� k��:0W����h�'��jx�`H���"��_:|O8��$�`S���,�`����E�E����T������R����8n.���ZHE^��F\%��-�&0��t������p)1��Vnqp�z��Yn���$�B�����T�l�
�rB����+���4���!x��I�[[^X
R��s7��I�!��W-;L~���m����/����u?@w.?������H�����No��������P�
 |Fc�z�����r6����-P/ND��������%��D!c������B����e�^�t0�!����k} ��S�����	�:��m/pE3�C�B�U=d>J����j�C��xo�!��Yu's&��::U�u
��}�,��@Bxi�M���<oX�-��\�4�V<�K8��A$�(
z�������	�%9�6|@1T�3�q
)���I;U���J��S�R�������hj+b\�|$I��<UC1��W������4�T96��2�2"�5����=.�V�D������rg���~(o �~s�\�[x�6Whd�9��Iz��I'�&��,�FI�c�$8�=s;"z{��4���u��#Z0�+�`��q15��Ls�ug=�k@^��t/��������l6��f�v��NzV������U�=�9��&���#W�D"�����
40oN�����"U�J��8ge�R*Q(�����������E(�P%���NCNh3���m�vI���W�D�������#�����y�T}^:�0�J��(�t���A���i�0���*`� #��;�u_����3
y�Q�3Lv��\����&�h� �0��*q�X� V�z}��M/7��9L1�l��x�"�;�����E�{=�l������zO���mB%��)�����	6B~�K��{�\�s������r�C�����#�v�u�@}��MLqwx���J�
s��s���"�.Q���Oen9��-U�6@`F��Yn0��Y�\|�l���4�J4
;W3�� .��-����n���8�C�lP0�9�p+���2�i2� �&�g���rf��U�r
�WJRb\i�C����Q������[��3[�M�5�������J����q��$��2Z)|������
da��@�M��5dV��^��c�M>�<���	32e���D����_D/=0���d$Wa��u�V�p(�)VPz�bz!���T��F�w��hv�F�����C�y��5[���>J=B�|����Y�2�47�]�����S����Inx�������?�6�	��1t���+<[�����8��S�
0`]����)�}������ ��'�;;_�~3�L�_�n@R��I��qa�������~��M���$[�����$�+�����?{���6z	��6gu�{���l� ���v��K�uNJ���^����g5�`T�V��������oN��N�G���������s����s�(�8��1�cE� ��r&S)�=!��!���m��&��}���fb����Qg�;MH��#�u]�!����{o��D�2�����M��i��m��3�J�8�fG�(B6q�'pod����`X��#5V�l��_�n���n�~q��Y���B� ������K�$r���#"�
���1���1��t�5�9C��8��t�H�\,�M�B�B>���0f�t�DF3����
q�<h;�a��&d"������U��B	.���15�</)��b}������0r��y~tvtz|�����^�|��?O���p����/.����>�fjhGI���@�A;����r;�B[��3~D`y��Y��?�'����cHO�|���c�Q]����T�y�3�n.�C�%�����Mw?%]
�g�%6z}p�#E[o�k���G��Nm���;N�x��0i�c�(�?�t��)����M�'T���������g����N�;'K�Y���k����Qg�]�AK$1��4�\ b��=NR�����JU��������>������>�{7���M��#t\�u�DX6����v�g��n���_�wO����a�	�n��VH.3:~�y`6�k��`�^��H��\����u������_&���)Za��s��e���q�}�E���/�.�S��uw�n�~����8h��
�����#Y��E��1��G�8��VORq�h_,�u�,�]d�>Z�--����N��5ZW����5�������;i��CJ���W�������]R�f��������&yd���,�VV�k������H��.@A�2VK��8GjA4��g��`6i/��C.�J)�`�����b�lq]N���1��@�����Aa��1��L)c#�{�0��25�Y�U������d�n��U^��$�(#�ndi�zK�0���:�H1t����La�����k���
t�O��tNp;E��[�mQ��=�����Y�/�`�����0�������D�FXZ'��)�-Z�+�s�+enD+�Sp�}��_����.�i�U����az�48\���[�z���f��K���%�f�
v�"M��I���7?���_U��h�V_d�d�*P,$�R><�:��#%-�Y�%8��:��BV����k�P�1������a�Bs�\�y1���g�v��&+1�;��S+K�p������7	k,����mv��~�JL�f���$��"_i{�Z���y<���!�����i� ��	�C���������8L�V�T�h��4 (��
���-+��C��W�Z��l��o������7C�-ig��*y9-/�i��,���_V�r�������ud��"G��I�0���<�<]iN�/������n`�b��1��?��$�-��&�F�O�����/E:
��S�����+mNR�.�t��� �e2���wX�Y
����+
-Z��5K�8��[���3s����F��_,���0*(F�:QkTZ�GT����&�W��:� FE���jB�@2�����b�d�� �R^�Q�F�h�lH�ly�
���������);�=��=���:�j�b���a\�z������a53���f�X9m����P��E�+�r
��lM�l��c@��X^�3���'	t�:��U=���oes�B2��872i@L���Z�������R?�/(���0��yG�������:����z���Y�w����������Ztx�����b��gl������9��`
L�6#N���W�{��}L�47�k8[^��$&&����W/|o��a��S�89�w��+5f�t~8y�����`�g����	��&�����������;�����7�G���6�w����?{�\����E0{
�}h���5S�LO��Gl
��C�,� 9Qb�|��� �cO��I�	 `$��]#�v\�(;����^���W�K�]�S���w�����`���r��5�#f��i:�����>C�N�2�Io����`�E�`�����N�)�,��V��@ �TNW�4�a:W���o�!��y�0�z����P�K�-�>�ubB��8I����K�9z����(j_��G��������0|�Cs�z��d9��O�����sI�U� ��@!"7��-�@)� Q����C3�Q�SC�+�Q5)a���R'������q`��6��x������L�h��	�����������{��fH���\��E&!���	��"�%k�C���6e���snrlc���E?����T;��(I�{
6?/v�����D.�3DKlp_p_v&J�n�50����B��*�A��%�mv��)�������w0�Q����@[h����=KB���k�`*���a{�Az�j�g��[��/#���$#sDGk��m�!��p�>_r�v�����������Gq�m8��yR�O�EFh�J#"�X�0��R��R!����ez��Z���krJQZ��8G1�Zf��E,���J�A�l������*��T*�_��oI���/�*>�%��y�Zm�V��
-$�
.t�����@V4d�K��\�d7��n
�v������f�9��&�����������@�t�"_(��!�t��%@<�
�h���@���������%�H��N�Un�A����i�4��25��|��\����H�;���SV����(!,��5uL&h�5��J�2��U���yf���o2<u�*N��
�Rx�VY��k�3K?�Yw�'[$��V��Z29�����A��a�����`������>�P�9���:���!nJ�P22:�_���%��k��q[.��������8C��na���$5���tN(�i��G��qc4<��"������W�ob���je��CT��g�dd������{&�?%I�_g��"�P��q5�q/7�����K"��U5�An"�=OA��k�d�$�� ��)��\[��J�`y^�pU��>]��@lf�F�>GM��nXr�|uvBb&T	����������]�,�+j��2��K�*v�:pm=yL2BI���+��~ZF��9EIz,�5:�O�*�J8������6�O �$��(��{�w�%��#����69#]�@K!���T���a~iEur)MF�!���������k�o�;K��W������t$��<��t�p�:)e�b��!�-f�U�j�3����-����ok���5'&y���5�%�����.H�Py6�/���VVzq|dG��`�R01��9�1dA{|�l�����M���D�b���M���eu*���_tv'�HFe�$m,��k����i"��)��f��ab3Jqi��J���%e���������`��+�LfP�mv8���r����K�S���S��n��W`����4:2H�~2���H8_�4�������4 �,�bd�� �z+%N��|8]��&Et@�%2��h������jH�s��4�Wy��5���������Y'W�#+�8
-�O�J�$����5H��zFK�]WQz��L�L)�[g}�X)�[��De �K����,�f~�:WW����<�{���c`p`�0���D��������U�*�* �$e������m������@t���(��V+�\Z�e�hI�����$@	�$��eM&��v-�a!)��w����S@��������_��9?��gC����6����!���&�8��C�O���c�G&�����IL�}��2�����vj%'��)x�@X��]SmM��^P����P���U��s=@3�(Rl������gb5�	���J"a���Hyh�L�U���X���CP�1*>5
�n7����c?�Hn��vq�9��RYZU�����mD�_���l��/����@|d�t��J����t6�����\L���>������;��]4��6AEIc�� ���.��N�B���'~N�tx���5��-]=V��%��U�|f�g��i ��ZQ�(R�E����aj�bH�{
q
<�v	;H��P=���_�~���������8o��K�6`�"��E>��Do)bS,i�X.��G&��9�=�V�k��T:!E
��^��cd<�;�.��G����/�t�9�4h
��o�
aO^	!��M�J���%�(<Y_��y0���������I8}�L}������.������]��F|F H(9\������1�MJ��������A�B5���i2|����M�����qI�������H��.��6IV�� �r�h@��O�w������
��k2��t~���R������	�������� �Z��,:A�b�)���b�����z��L&�8a��p��O��]������`P`Ax���Uu]E�t%0�+�&f��LF��/��\k����� .+�R�j(�L���/�M��
����cb1Lr���g�Ql��^�t:��M����F����u��6����������z5Z�c*��MEs���j�t�oSk���VB����EKq�7/%D��2��eX�k����v�'�Xh�o����_���<(h�Ia�1�T$�Sp}��{� )?b�=�#!x�:���������QNc���t��������l��u�RC<`��I V�'���B�pq�p�!�y�r���P���2�)^�������p2�4�^P Ed����M����33��Hl��L[�!l��K]�v
��
r7O?d���&���b3u���T7�s�4�����������#	2�O��:��������u�M7��0vB�OA�IQ��M�����������#.�������J���l65��`014��2-�h#&����\oh��`;�`���!�6�Y!��2��o�(
����<~�f{��DB�v���lW������<�h�o@9���|�R�g,*	�C[��~��aLK�N��B���l&x
m_-(��[@�
3������.-D��a|y��O�%�5���K���(���
�y*�!������+�B���'K0z�H�e0o)}P��h��r/W2��p�D�gC�f|�Fh�5��%D]�xcRA=��RmI���)L5��Q�$4t2X�d9e3 ��	b=�_��)i�<���PH-�����F ^������'�J.�E\���:z����yD,J����?y�5�lx�d�(V��\9���� ��[��%2�JH*zSN�9b���
�sA��
�g:,]G}��V������(�&�Q��J�h3��Ax����y5n �j�g�&�|��S���Gd\�����#A��;��$�z��au��dz�QqN��H:02�>	�E����T��*$��X�@f��Ry)�����a,���7G0�D�����(�L�*	|��Z���e��2�����Hw�� y-_CEK�R������A5���'Z	����M��Z�#q���][&������2.#�Q
T�'�2�9��@��GDz_�d�����iO�s"���)�NM�4���(�eF@�3������>c&>�-8� 	�D����d�#�f���eF�f�@�rS�%�qDtB��86��E/�[g������{=�����C<�+;�(6K����P�������L�ux�R��jwdNy���$�rV�&V!��.�T"��j�bcK��F�G��y���������C��m�s��uR�:�4���������{�9R`m�
��^�[E���o���n������J&�+������O�l�b��H ��ml�!yF�������%���k����3&e��]� ��Y���HZ�����;s������4�$���r��.�A��C��T����1��!!*���+U�1@v(k�	"�)��(���!4�&Y
�T���,��p������Ey��Q���	��������Itik%��4G���/}g���Tv�bt��i��:����������7��g��=�E2�f���'Y��K�M�-������6��'3J�6f)B���+�h�F�~R%gx��o�I-]���b�:S>.�cd������b"���5f��S��2�9#�N�R�dC�������?��ig+sA�����*AQ	d�����1�XH������Q���q���������!�����m�(�������~^dR)}����S!iO���#���`�)Pj�x���vL�VA�{�b��S�j�j�g�tP��`^�<
M4Li��=mY6"�'N��;��P�q
R���UB�iP�0�u(-�]���y�����s��ze^w<�Iks��S���w��9�)@5�&T��\�ay��8����|��d9����O�_��@�h*e������������Fp�V�l����JG/�K	���C�.���V���d�;1�x�G���|�	��0|+C�S��&�=�x�^�x�Z�>�������������]�Ve^)���o�T�������g=&�A��D�o`�	���BU�sw����le��m��`�s���k�Z�{���2��X��#xb�k�YSa�c�n�L�L�R�Wh(�Y.n&�fl%|=�lYO����P�1����i��>W�(xWY������0?H���T�C��K��������L��z�%����'���iK��1L��Q���j������Q�;Y������9S8��<8�����������}y��\CJ��j"����\9��h��.z�����\v�N9�
Q��X>�C�!�W���(C�G��T�r��A{!<fXh����8F��r-T��c8&M����$V���An���:���M����y�%ECt@DK�v_�z�?�s������|���J^!�K���@�����ntr���oR�5����Q}��Kr��a��r��CgG�xc������H�\��9����E��[s�yYx�������R���G>x�#�����f�y�1�t(��3�G&����J$�/�C���X�b�<P$���KDf�|?�|8�|����A���#�����(?Sc:���q���p�!�:T�'*��|v!������u��N&Ua
T�������u���7�Q���l��]��wo�}$�|>y�
�!��KZQ�}������^MK���<��#���<���Jr��E3w�++��
�
�k��f�P�����d����'7��[��t����N����n�;��!�2�c<��.�������H-��4Qt�D)q��o+Ol])�P������1n�t�s���S`Nb�����������,���~����h�������l�#��*������t��aT�����K����bd�1���R.��mR��t�P&d�~E_����C!�� v_-��d�\N�rV���/g�VaZ���<ZH�
�F[x�$Y�]-E����\�<�q����fh���B�
��B���(������+�{7���KY��{�IT�����r}r���,������5jy���{<��N���c�m��E�ew�������&�:��F��P6t1�G9
�Rz��PL��&EI>���@l�(N���P�:p'�#.���~�$b!�"q�LHw.h�Y,���El�t�Xw�0��7���\���:5�J���-���$mq�}�;�V����2�Gb��*�c�	����;�!o��}0��^R�iw�e��8�GB��Y<��,�U���!!�����3N�{�����1����3,�W�Yzn�5���5�ES�%3����O�&���h�e�T~�L���������<#_������'���W���9r���q���Q,`
��J������4�'����'��7B�M����\fn3��C�z!�����[f����	 U�#8�.��A���Q�K�7�z�`,���|m4����=<��#����!]�-gf��V*�b(����Lp�Tc�0x�,Wl�`"������#J���}B�������a���8��V��X�B�&.�o�k�,�0
�QX]K�K�%�twQ`}����X�����V%=��$S8ci��=�m��cE��XQ|M�=�:�#PA����9�/�~���v������l�\�;�~�o�S�k4'B=�{���[S��.�6��&�IrsNH\�b�R�������$�T�6�=S������@I�5�r��(���3�����N�H�������Y�+��&��C�D|��������z8_�<��%cU��*+\1�RM�w����+rA�
��%ipz�R����Q��2����N���o�q�m����C���<L4�r�I��G�u�h����Zm�6\�d<>�6�x2U��|2����N�)�A>��B�95�n)������yHK��&�/4zW������`8���/�����M�{!]�7��N�{��w�c���_T�*
P���g0�R�������
���xh���bzY5�����U����������Xat��(�����)(��q��N&����I�v������������QT�Bl��'�S���a�,���+����Y�4:��"�o�[�4,6��CP���G���e������*����],��D����h<o�!@*A�������=���x�h����!n�(���gp���2��
 ��<�#���}�t��y��1���x"��Mo�v	���m�#�P��C�������b�(�d�S�z���X"P��#���o��:/���#y\���#��tc��c�"e}�6m�odTcE�;$�8!��W=b��$E�b�V���6C��'������:����5y�B0�?�8g��`��(Q�!����!n��G
��f�4���]b�s2�R�eQ(�#��3�[�T����t���(k��-#Y����nv#�$]��������D��w��{wJ�@~1��������7����ep��O�wZ���{g^�!(^`(8<9��H�9�����b-H���Z��u�/\M|�YPC]�{,��; I7���$�t~�F��%]Up�Q�t��f����7���}Q�T��b�����V|���
�C����f����	�j������8�$2"55�s�U,��F��?��v����vaL���}X��;���~��=�<px8�w�����;�Q����(��-;��h��1�)?���Q��[�(	!��*_"wVW�%"���%���8>������s?;������Z��c����!u��Fk�:iN&��Z�=����Z�9�����B�{M�����W\�%t8l�B��A������m��^Z	����;��J7�o�qg�t[/���9�0s8=g�a����}���h�/���61����A��k���>�16+��!�Ew
��{�]������9�n�������ji���hTC��I!��'L8��/�,����+���M ��[������3�!��n�B�
Y�7}��\���2��������AsT���#�x�p�/7���g��39���:���;�������B�F��_���7�f/�C������m�����\����^/H�m��1�mM��;m�����~h�
�.��{�t�����) ��~n�����Rh�b-��n�Dx�����8����������[=��>����^Y�]�M��c���KH���g�HA��:���2���&�V�i�I���������)VE���G���X��Zm\����7�+����tq��i���/���
bH�)�f<���8�����o���������,NzBZG3(������e0������� �8�<������
���#��S����P� �	h�)b�������������Y�[�C�ky�h�d�u��]��#��H��M���"�!Y��q������V������b��X�3���%h����C<�)�V��F�����Z5az�Eam�qCJ����z/)���I�>q�7G~�>:�����YC],�=:���#��u��V���6�����"YA��Ge*�o�b�t�����!`���I���Jp�i(���r����M
��}5�����BM�K~�~��y���1k��KV�%�� 496O&'.\���'G�
�O��5��hSd���VFM��y3�
^`�����R������������wxI�o����7�����.N�pz��$j�.���L
0��a����������{�5
��`����L���l�Q����!��������r�A��QD>3�!�E�o��������,@6�>1�p�Q��E��4?�E��U��"W�Z2���c�CX�QH>3��5�Y�6^?��= |�.!S5s�b�GvrxD������*z�5��e�����qD��'�:R�)y�
7.96u��h@�p8�S!q/������������8L�$��9\��yr�VB3���}�Wd6~��dx&x�Ym��]��%8�`�~r������q0�����Y����n�zM����P�P���rY����v�����v]��Vky�����kLPR�y���p9�_(j	��^�f�����BEqi��"� j�.��?]2�v�
E��t�=^N	�S��	�)x.<�<����#G���"�T���b-
u���*�s(����2N:�W%�|B���BES����va�����l����&��j�<%��������!d&�{������}�M�+L#�dTu@�(�	�T��}�0W��1�L��bz���c�q��q�i���6����97�)��C�[4��\��r���}X�w�t"����B��
�L,����U�+���5�7�E4/���T�������n�)�!����R}c���J�"�b���P�I+3��O��9��K�\ ��4��q{��F�5#������w��/O�`�w��?C`M���(�����!�*p�(��Bt%����n����2&l��"B)��F�T`�����l��}}~��F[Y���n J����b��2e��J����F�QG����*FT�{�1��
�����D��� �1@5���4{	��_R
I���"����/����l��`�?�"������v���`����/��(��5x�*j�������)��������gb\�[��4�E�.S���Z������\c�*�gaUtS"�-'�g�4��LF���K����	�g��f�w(A���kxj���QU����<�U�!O�!�s[��?�=����uU��e�<!w��~}4���mB���d�)6S=�������H
���j����`�p��U)s��
tA������9�w�������:}�S���D�"U�/|R�O��}�5�%�����7?����*#|��([��-�o6�9
��@���<$=c�3B����2#I�VX_G��cg|�5�����p1!��y�,g����S����������W��$�:dIB|^��m���v��0����"4���O>����;��|(��s^g�<������\�b���[.�}N>�7p�[C�#�t;5����)���ye���|�.�*�G�#����.�^��L)�L��>���B?�X�!&�95SGq��;^��������:C�):��h��(V����]������F��y2����u}p��[� ��s�&����A~���
���S�bF�R��j�]�WqC��i~�`=��j����Q��������v;�J������E��rJ���+(�H�\��
���rmf>-9������r�`��]��HMV�C��L��q�e`P���*o��e��:����8t~�\�9`�a��>�HP3������BI1^� �VF\�#
��:�������e�����2�v���nf�Z���o�����"��7
>o����^��m.������)	�����(#�����BD[9g
g�Nj���S6����j��������_Z��qN�dA��u���+�,�_kIn�1��=$
�^���s7��hpb�L��c\	a8�U'4�]�n����c+���Z t�?�@�V�,XC�����Ti��8���50�L����x�h����x�h�O�_�����U���P����[�S�(�"�u�AZ��P�!��S4�B)g>��sP����������oh���Xz�:��x��i#�\�k$D"��M���y�~�Q8����!#�e�"��1#����Y@�&�+�X�Sy�,wL���o.������d��t��:rv����6����������1��dz��D�n;t�:�8���Uh��C'.�^�e��?�m^f
�����tx�38��Q�yH�~�	�^!��5u#���W�C~K���,n�0��MO��mf��S�(�-D��r��p{q��e�D��>�v���� ���{S[�{��4X��������]_w����9	��^:u�����C@����4���
d�% ������[��w�3RT_
{
�kS��.����i7V�:)N j������K�H�&�����G�5S�+N������yD1$$g��r�L`*�vE3c�����+i��
f�xL�b"�dR
�7H�
�Q�p�n#��p���T�t�Jw���r�3&�#��W����F�i
W��%���Y��������=F<��E�YjJ��dr\
�0��,3s	�������"�X�6[�����^L����DS���n��l&�K"1	����%0�$����7y6�K�k�������tv�-G�h����?i����X(~�G���T	4�1��Iu�e��S��d�B�4���u~�Z�c�N�J���B]5�aD	��p�m�osV�N�Px���H�������U������]��s_"�k��9N���!� �@&�!OH�����(��\��,k���b����O��2���y�C���p:k��;7)w>�t8�l0��6
ff#P�1�7�)���s�_�S �d��a������$��`�+��0_���	Q�Z*����4L���\H]q�E��-�
�G��������j,���B����H\�?@������Xb�����
�A�?�_h�3�Wy�M��}taZ�%�V� oQh,��#P��y���l�����X7��G�m`��������.��j�/"�Y)���C�[',���/K�z��S�H/�|�0ny-�;����46/�Qr�cb@��#�{X�N ���P%b6��/1�8=���c��Cc���rK��������u�� ������`�l%���9�4��-k%�*y�����)����;����0X[$�3�",r���~px��
,��8��0J`@�L��5�,� �O��a�77���@s����lRp���l!�C��1�n�q���&#r���N��`������tA�)�*����S�2��f&
���6Z�w�I�?��ZK5%�q��z6��Xvh�����	JZc,�?(>���|�TB|=F�}?�� G�2�L�NK!�������F7N���6�L�RcJ������=�����u		��6}o��?��j�����q�u��A��Q�
wz�b-5k�Q���L��L������U��-� ���I^3���<�eg@	�6����E(�P�`��,~�|���]��|��z��1T��YqP�F��x����th��hr��/�ejt'��<�X��m��,o���q���>��`�,�`�t
�_QRb�N����dttT���ON&'�IcE����`Ui���c\�OC����X8�������{�<T^�q����}��w�KsB�����]�o�B]`mR�H�"�4��O�go;g?��t�� Cyy��A(�)�
�7���	Us���-��������
Ku����PQ��
�	��$�9e�V�7��*D��'T�����`W����������+��U��{����7��2A�|��\<f�4����G&���k$8�#:�(��22�x4>�?<j���a�1����U��u��t9�c�P���(�������%�P�CX�a#+��-�	�f%�k=N���7��?f	H?-!_�*IF*�|�D�g:�-���-V���������&��S����"(�<l������;����cP�Wx@E�"Y��e=o#�i��9�
�L�s��0���&�s��`��vx�M}��13Q:}EPQ�R���1��t]��I�#�������X�SVc���������4����&�;d���������~�	r��nr_��0��U�w���t
�['+�2�974��`�!�T�����.8L���.r�����E�3�����T"L2AfK6:���Z����B��5h�L��.A;f����tD�1�h�I(�0L
�x`�uR�����CJ�5��f��:����V�6�NF��o�	%SM��5���" �#���JBk�s��S�Q,��� �{�����@�A8]�$���j��9����)cP4�4���/������*z�}������YB������N�	2�$�;	����~�{6�2	�J,D
�(����Hlq���s~Z�}0��8��KH�����
K��������w��HJ���U���
mPe��to���j�OP.��l��������5����5v6
��t��O�����x�z����cf�7gm�n�d��bR��!���q~� @&/����_b���~2�	V|�f�*��
�y}P���w�m�Yn���^�
��;�~gP��K6��<CB����T@>�-	�)*z����0���xw��[�������/G=������W^X��t?�)#�%2%$%�/�t6�	�~��7��4A��!X���wHsZoo;=�K. &p��:�\�F0�d��P�9f
��"9�*$1������oL�h�<�����
d���
ej� �r�Rq���FS���p����\��i#��xV�2������woc�$���I�����+[;����6����H�Rg�pJ���v����B�]��\3,(�������$	�h��o����9��5�:���� !X��h#a��o��N5�7e�A��{H��qjw�Wc�X�5���$t�y�JyE��~rx<>���m���x���
�����X1UU�(k�<}�����������P�[��g�M��,�o5���3�����e.x���+TxG�N���,���&%Je�����=�a���A3�������j�{�l}Pu�������dE�t�����,G�+�.!}����l�M�oqW������v�9$~�a�a�����H�n
!4	�����qz���La���<��2`��@����c/�%z//
|O��>%��p��K��e�ZcJ����o$+k�n������BaZ����q%��q����8���e��:��!�_�����z�j�[�yF�U�]��S%�lPK���������'< �y�v��&�Vk�q����D-���-��?��]9`to�lp��D�v���B��u�1A�.�/K	����\�-���5���LB���Y"<,x��������|�I��C����J��fI>N5�����1��v_��XE��b�,EO�`�������;OS���eO	1JCf������Rd~�#�%<�F�->�s*��j��xd�3�/U����1�V������
���O$���$
CQ'M�1������6��>V@[�"���Pf���=�]�;�8��i������	oL��(	xYI���>p0?{��E��j��T���z�m�4�"��h)kD�=X�D�b�|�M�T� �
����Uq"4dn��l������*^���/��^ ����� �c�-�7����}����8t�z�p��u��h5��a�r�S��S��EU�q�Ew���J�n�9zA����!���,���'��H�|<��8�����;�D^����&HJoFT9��kPC��j
��%�H�D��u�������tF~��������5�q�5>���Zm���|�`��9mMm���*�&�<��?0��9z#��<	+$,�K�]�2�9�3��*O�7����G����=
�B�\~�"������R�� �#��a���S������5H�r&�Q�p�sb�qA�?�'�6��S*��4�A�^� �o�+�&e��7�Fd������:����S>lay�7�UI6� U8}d��#�$�'�+�z	�� 4�8q]&U+�Mr'N�g��R�J�	nZ��=��7������`y�h�����L�LF��}\&
U�g�g��cEo�t:U�I9!.(;����k��yZ��������h����2-�Xq�3�8�Mr��3C61�f��3	U���j��Z�fQ}�S�s�(7���om�������jC��T9�gx`l�d�o}u��PF=HZ��5�i�Oq���~��'^V"���<	�����s�����{�jJs.������?�����W c+pA�/�4W
�o9���d��h�[�'��G�A�mMj��>9>h���L-� S�������'7�N���%��������������>\���y�s5�����_�~�]�7����e�}��3>�-�d����-���e���/�Z,�2u)'��'%2�;�J���*b2�_<�S�w������A����Q����'����;.f���2lWP�]&����uN�4���8��������:K�ggT�g�������pa�6����[\h}���%�I|"�XC�s����<����|�;���
����N��L>\�\���1�N;'�wf!L�C'}!28������wc
+~���.���M��2r����o����1�R���y����m������o���!2��Nu��+�>��Xb��!���Q^DN����nq� �1�����x;��_�Sh��3�1�UFV�/�0h�v�a?�+���e���E�Rj6�P�4\����V��-����(ZcC�(
H�y�_*�Mu�z�1S5<�F�X��5(�>���0'�
	�*D�BP��3���G�w�: �����'L�yk���7��a;�M���S0�%���!c@�q���V�d�4�`1������#��������a ���U`k`�B������y�y�N���?�>���:X���8WS�_�,=`�b�R��K$�o��x�e�J�_;���M����c�5d�%���`&h]��T�5M�$���������}�m�~�~'�4+�}Won;����>���o�������iG��p�d�i���L�}j�L8/�$} CD�����k�����o~0pD���FCX+-CHT����b�iq�^e���xn�g�N{ z'���G�o�A��V3t��"���CNg���D
R������;�-���(i�&l�8D����y�@	mO,�
�j
V��h�f�����y����w9�t�����8�}�h8������ ��(��{4��h�p�<z+�BG���q���zF�M�gEw\o����~���&�'�f�u8.0������f��)W����{����*(�ib�`�YfP~&U�K>J��%w~�u��H�$�xDF�"���b�������;�?�Av�*%�<Vq�[����������|�|_�"�A�Bq��qv����gP���w5��J�s@<��"NX��b��3����p�}�~8�\q�i����q���>WPK�2%2������|�V"�'���X:"���;���5b�����~���^$�����;��N'����y��7Gn^���J
I# e0~�Y�x�!�O�&���>�Cry(����$r�	�����S�od��x�N�D
����X���ECer�<�\�� BVw:B�J�l��;T�������2�a���[4}����ni%����*|7'GEohF"<�� 	q2��@�z� A���u���>�p�I�y��DGp���
z6�!l:�����*||uJ�3���-L��+Ut��77�O��55JF�BZ�i�Z�Q���+Nw��CiS6kIH>YS�(���I[O�t��'�N�G�H�'���G�����WC��`��K�"�t�	P�
�#��CQ,N����D�#}�4L%P�0gBa�&V��1:$g�~�DE�2:96�����/R�*�6���
Yn/��Xh 1���G�X�6
~hP4j�j��Mo�s�Yh�W�f���FyK�F�^��G�F����A��m������Z���{wyC�R�If_Q��������J1.��D���(���	�������;���=z��Ge��"*��0�O����z��?^.|������������\v���m��M��
uh��c��F�1h~�����������9��s�&�J,z?^H2���O�P��,��x��/�i:��`I�������B�z���}@c���=��#����kY����a-�v���1����=)H������C���P���������4:�@?�4��T��G�����zK(����~����x��fUU��Y�0��CV�����
����d
�����O{��ap�[K�����P����(F8��=��E��O�bL�)� }�������E��� _���	���W��?F�R���/���"%��	�����xWT^=��A]���T����b�1K�������u����}�]��+dea������}pj�=�Xa������<{�LB6�d��L�:�V��16*�����;��c����)��� fBT���������L����>t�{FW�X�@'������Q������4��7�v�1��n`S/���yOss�\&C��]~pZ���/�[�_����	P�)����\��y�t�.�GMv�L,[��.!H#R�qUh��4�C�e 5!����i]2������u�=*VJ0yd�b�u:.�u����@_�'Y@>����\��o �����%5P��{(E�����q2��:�`�ON���O���T�����������y��e�@�����sNI���1���v��w��^|uy�(�w)����/�O��{d5S�)���K�����B��3��)�?�&�,���:l���}��Vk6Z����������pR�RX@���-�{����F#p��!3�1�7����D�2]�
����B�����!�q�y��S6X�:��uT1�O"��Z�K��[?:9�Oj��hr|���j>������
�
� �!�N,�TC����d���G��Op]~j~��N�{bK���s��%h/Tn��-���
�#���5��=:�
`��Q���v�5O<�q,Mz��J�cv}#_8�2�����>Ix��O�>0�O)�F	�g���#��u����a�5�.i������/�1�M�������#��<���W�{��7���<���J
�L ;�1Y��!
@<�p������mg�L��Ul;l�A����{
K9�rX&�U��,�'����e����IYH�%�}�L�'t�������wpR�'�N���n��]8�pX7����y6�u�On8�y+��wp!��re�)�q�h���=�O���+�"r	j��`�����8b@��"�oyqvCQ��cGL�m���x��4X�L����dtn/ ����o_X���W*B
��!�x8���g@��J�o�l��8�Ygw*�*\���"�Y�����������������A_<}�!��1���T��e��{�|��w��-G/��������^��������5��m�C�&��YL������Mo ���l�?���]��nK&q���%I����s4�'�#{��i/\B��"����u��W[�����P. ��}JOD�5�C�!!B���8=��������7N��U��t.��H�������E��h���N��\6a�$�'j��a2���+��pZ��@�	����n��B&������(1�9����m�xeP$a(��{F� ���6d8c>[8�-GPs�~b$&�%�^2S&6G�������$:��!	�.�fGU�eS�60#�/_�lM�a��(T������'��T.e1�L!��JJ�.��9���3WAt��+�)�~��?�������&���4������>*0��J|�����������\��#��s�.���zDNt�����HO09�����t�x���/�(��v�f{F�m$��5*U�e���	'��������c�Qv�D������6������	q;�W`�[�2����v�scG��3�9�������������8������Z�3�-�*��~�������~2����`���:��S�/�r�A?�?�}h~6��M&JO�b#&� H*�����u�����E���$R�#z0���"<�����Q������ZD��'���r�U�z��p�O����3
�im}�!�\c�|i�h�8����L��|0{�LyYA����*�|����}E�^�zkjC���cK�GTX�GVY{����Df�36=*}�J�M-#���s�"'*�U��w�����U
oZ������e	<G�NL�sa��B���������	�R=9uvv����������1&-79��d�f��R�)���n���'9H8��j��:���D,4?�Xy
������R��v�;����k��#]��]J^���
�g"��f���"W������h���2t�	��Dj0�s��������	���32�GP��
�2g�����e,��f�J��H,�Gp�l@�X��B-���skH�]�:�����#�e3`�-�
�F,�Q���4��:zP��G#��y����	���y�-�����zB�b�<�g���o��YJa�9v>�@2Fcf��||�%a`:�H^��*p��5x.`�����suktw���3D�A,�p=/F_�ce�R�8>�!=��J1�[����l�.`rD)�K-[���t���M�Y�hd���������g+��7�p�=������x�����T��G�_�*�D�@`R�{��-��s�D,�X]�#���&���c��4�0fbf��`%�&��[���y�����
���_�Q� �R��D�v�tY9��.�UYSc��^��;�NYm9����F�#����IhqsUt�+���m�A���pI�����U��|��Nj��3X�iz������76�Eb�e8��G�\��aolb.�������b�E
HjU4���?}$R~Z�s��'�8�>����$�hQ��~K��jpfD�ZPG�
{�"�����Z��Y ����cyfSET+���c���b��[�auR�f�N���q��v�K���"��K��S��T����PE��wy��`��<�'B�E!�p�h�tS��roU,��T=T�2j�R�r��P�[x�o��c|
Lu�7������������&H����(AS�1��Z����]b������{fb�/>T��j��x�4����;���g�
�����i�6��P�*���w����%8���C�-�^B�r���v�Nu+�h@�g�_L_G�7[.��:	$���oT�w���i���UyY�yQ��
������:i����bu'%���J���pJ�f��
C)S�*����*�d�#(�N���]0���.X����p�<&;D-�)�=F���J��#�y���S-������*#��	e��dz�Q�	Y��N�i���L�*�k�Z5�~L�W�cn��aaLU���U��7��Q���X}�+j���9���I������1�e�B2`��z��Z��`��P�q��=0�,28;N�'J=���f)�'��$�v���
(��Yz���T��@�mCi3T#�KF�Z<D�)���&�4��fJu�
5fC��Nb�2��$�>lj%���)��&�O+��=��Z)������e��Mk4�������	���hf�j�1,5�[r
��ON�<�o��E:��*�r�;cN��+�)����SN�h\I�hTZ�K�)�uHB�B3^�aI����;���IG��)��h�i�Q�R�����<k:P�v;]�T����8Z������:�S���7a7]�w�or.���"�'�������C���)�o���F��Mn�*s����q3���E��e��	S.����v�5�unlozCI��m��nSla�Q��l�a�%v#�����z���T�|�*���c��
s�_�4��9��BV<���%�q(.��}oP��T������� �4�����y!Q��U��'�1�+�JXz�<�E���1��t|C�Z�:���}�g���s���D�S.g
��+�>�0x*Pq���}-�R��H����N�a����7Q���U����B��d7�����uS�VI��L�[&����I���7��o�K��;E��xCi�l���{��>��u�xt�������7�^1�\N=��2�(p���'���%���'� ~�c��Q�M�����t�R�!z@�a���.�C���|��p���r.��9�����b�����8��D1�"��I}2i�Z^}|��jco4x��y�j)$�i�hQ@�B^�#S���A<�����"E7*g�P-P�!b�����e����x������\:����e��pJ����N��/DI�,�B%��%w��h~^n[�9�bov��"�5� �XY�d�U����}��sv'������������h����5����M������w����r����o���:�����)Y7��G���C
�X����B}����������|[&����C��?T��|"B���/�!��/�����.�h��/;\�^��sd�<��Hcq����?^�]��z�/2�/h�����N��I�������������pQ[�pac�j��>9���,<��u���2�B�YE�
�s����x����"<�+�CS2��~��:�T�Q���� X�]�a��1A�}J�F5T
k���S?�&�@��|)�'�u�L[�XWP5�Tqv�7Jf;� &&�1���K[��cB��;�sH'�{�?�	s�9�E�>�}��_�w��WD�����q�I��N���}���� �'@t��X���<s��P�Z,�MV9N5Bm�"����9��JtC"*@��l������^p#����K���w���#	��S�ZI�,/�d�����!*!�Y�������rl����$eM~�^��z�����������w�?�W�Owy���h�P(�l���vx�n�r���|De�_�%V���@o�E�"_��zc�����/M����
�B/�t��C:��
��-��g�7��J2J0mqf���t����k)�]~��d���~���u��CE��������BS�
O����':������9��44�(�[���M�<H�S���lY>�4��#��:�pC�5�$�h���/���sl�X9��G��d	R��������<��u��Nmt�0�L\�+wt��]��	nr=R��,R�?F�n��8V6�.�t����}on����o_���s�m3|���S�5���AU��
����Si�7���xM.��j�|F���T�WU/S~���RM�N�=B^<lqt��$..�"%�vM}��]3�46[v\l��8�N���^��4����?j�j��������?���2`�=9-��p�+�M2����$�
�/���3�$���C�OYn1�Q+3N.�MF�$U-�%�����h��i����jC��~#�U?w���5����\�Ee^�1��F��L�=���r�IL���������j~c2�o�''9���uh��O`����FQ�#�����{-���������9@9�1ut�s'��
��g8?�g�������B���3�@���C��U�O��&�����u0��N���������cR\�J&�2(���!��?����GX��(�����+������:��S�W���!R����6��(�*�R�$�T��_a��b��>P�F�K�Z��U�O�~nZ5���>�@Q-��Z�I���M�=ST8#*�5��
��P���RU����_��~S��{���/��gLcu�'��;������M[�zw9��������Q.��7������^���}K-����;�~�����?��cc����_�U�
���U��N�9�3��io;��&5��w�MQ=dor�������c�q����u�\�Q�Q��*~=�u�E_/bh�r���*���v���1�3)��Y�����<&7��1����'�	~�	^e+��^S���� �Z�U
�t���=�D���5����6�,Tk�B5�,���E�A��h��e��w~b>�]����U2���,~��
���{�#~�p������+��������������y�_�x��)��l[se_e+>������>�sD!$ ���p�H���f����\n�^�����z�|�g��yD16�R�e��g���
_t�;�.ox������dA������oB���S������U�7�K����4�Qv��%�Y����`�w��Z������7�����&���R\��Uc���3T����d����������?C�)n�k�@�[�J�Pq#�V�h�X���T����2�j���>�X_M�Z����V�`���b��~�Tuk����^+8��T����w�d�=W�w���6�������Z�m��:�gp.2����r�9��w29��F�����I��]k����t4�6�����H%�����!hu�V<b������!��?
F{^R=uO/�"n����LT�����;�(a�(2�]�|�E��q>��o����)�����\��0z��<����p[6#D�;O�SS�&��w|B�@����g�#��8o(��n�v1F�� ��B������yC�R2l��w�!u���Vv���q�\�)#��!lp������:��-`���-8��%���%
[���/�Y�md@�O��r~�rJ^2�������dh�|7G�
������&�K����	$���R��m��[���{��p-�||�%���<�B�sA�O-x�c����kt���	�����S���iO1��&����� �y_�cHX%�D��A5�r��s�SBc��E%~����Y~��#��O��Z���x�f����_F=��(C.��QH�P�g��t^�[n�s�-�������Q�)'iQ�3��SZ�����Z�G(����l'[��rj3������ ��b5���:*}����1=�?=>�R�t���j����s�H���S�}����rv�wpt4(��tBo��x�Pa����m
���oz��W��_���_g$����?���pc���)�D��bT��mS�1��qrr��$T�5�W!0��������2����(Wu��x\���k���I�M(�RV��F����h��[�9�c�Bf�A������`y%`�H���(I��s��]Q�*�Mih�=>9t�A��	 0Ym���C���>��������Gv8c�w*#��:�Bbw����-`������|S&�F��B���)B�����fpV���3<�}��s�)Q�`�R��=�	�vc�AL��hVu�<'��x�7V�_�9�H���>z��x=����}qy�x�6{��SE,7(�v�`l�{��!C	��}��(�	%�L`[c�G��Bh<���.%�	��M�3�O����@7&p,�=]�������#��`��N����w���;�|�#����?�eEHs1�H[��\!/,�y��G���;�o�����~������n���d�j�� C�%C}PT#c��Cw��[��<SA��	�Z������U���8���t�g�Hg��}0�w H
�� �J�O���),u���e�\�T��V����g���� ^*�;��h�Z�l$Y&]Eyi�!je�VSq�~X��y�d�]��:�i��^n�w
6o,o����������pc�6���$�t��:C���}����w5����������x��{�P�/�����N��TN��B\M(��e�V��,;�'_��u���_��tx��r�Z�U�R���1��)�B�zP�P����L����Q��m�N��m��q�(���q�X����0� ��!�GS��U����h������*��*a
5$�R������a����F��R	���}�����2�,�B� �.%E���@&���$ K&b_6R
B-o��:/�[��H��OK�� �]��=�`���#5�	���C��$\�
�Bf���'i^df,T����zWc���������EM�IfN��E
j&�`���,[��@���1/�0	����FKTl�I
sTq�:
x�@�Db�ZX��;����^u\���rP
6����\��6��U3�����zJ1�K��^��6?�[��_��ee ���"UD��6������`�()`s�r-j��E,T����qvP0���I�����.��ee����K����������+�;���<LL+����.�rB���������S�>b��X�K�����I)$R �;�8��4O���@?�V1��b%<���Kha�����+p����!O�����B�I��������R��H�j����F�������Ul�w.����:�0 �����{�V�����6��p�T�3LI��p�Rk���U�&����S����8�
�#�2�B�bnU��(.H�k��f)����U���N��K�exA}~@��9n��e��	�B�'����8�=�_trV�9kS�%�`���5��@U�2��� �� 42v���
�G�o!�^�0mr���5� 6s}f��������%=��_������7 u��7hT�Fr'�C��d�����%����S����4���3�V4����d�N5�O���X�b�@e��X��b>�p
�� ���!�b���@#8�*���/����eh4Q���( $%J��W�,>�R��@���A�~dZ�_���*Kx��x1���N����x,��'����BZ��z��d�Y^�(t�gaLb`��}��m�\h�~5'�6R�E��;�E�
c_��/l���
�lx��}�hv��$V_��{S�[�h�>)9�!�e���E��ai�N�[��������YlFx��t�	7�%.�P)*/�y��dg�?�W�6k���Z�U�F��%P	8�����J>P���\������AX,Q�_�/
P!A��LV��C���/��_�����x6��	�G�N ���v�~��t��^v��W����b���8������QL~-���?���5�a�j�I���>*��67dx��U��}���jw�A�������������G`51HI�6�1����\V���k&���0��WM�A�*��9��j�5��s��t!S�\�B��I*DVI
J����3{��x+v_|Q�U��Y�
�~<��oUI��hN:�+��S��y3+ ��������V%[��	�z<�������V��,��S��>������$	�B�k�xhMR�u��J�����
����J-��q4/�]���~�`�UN���,��{x��������e~j~�����Wh<�d�)���E�*����/����$I�I'�����6.��(m �m�v#J�x�|��H4;'�iZ�		W�m�K����u�(�+��x!E@��-/�`Z!���%a���
SH��Ye�2\��~��t2�Nd��/��&u4�Z@��%�{�"���
K"o;���i����0g���wG�R����F���t����J/=_DnT��d������o7E�*���=O��2;[��9���Q�:����+��k�������'1�2}'��a�f3�XcL�/C����B��Vg�'�I�{F����`78���|�

yQ�vr����a�m��9�F������4����Q�.?���$x�<��� *�YBX�h�TI>MV��>+�	�=���S����n�gLu)�����G�t������fZ�B
<i�n"�q�z���B:&W����c�b�T����%)��m����[N��5\2��	�m�[�=�
�ELr����t��KRW'L]�JN�v!��y���M���y���]��&�.�R~�Y
�9���fj�B�B��;��S��}'�"b��l�i�K�q��U}��$��F0�T�������.�����M���
����iJ<�.����������
�o��g��M�R/n���G���*����E��h��C����H���}���n���u�c��0Vm�5�)87$@rV	>��7b�X��SU�7�Bu��T�x ��U.��ih��4�<���&V_0qU�?W������s���x�TS���C<�/������1iT��S�b0���sZh�	��H.�P����	�_������4��r��%�����K�3���bU�	�b%����|R���V������8��>H� ��j�8�a]�X.9�i@y&:q�$B�	��#
C�,��rR��x��2+�j+S_�"�J�H�p�cHLf�I4�o���ae
8�����1���J��	�rQ�������%���� ��������������q
1-h(��E������zs:p�Y�
����X@�G�3~�FF<Vjq�P�'�)�u*�H���B�N,w�l�����T�c���+�Uva4��s�C����r���,H`/F
g�����k�:z\������<a0^qg���o��:b&�F(��Q�F���������w�:]fTpX�������S��Pn&��U`����u\�O��9��*���M�?$4�u[	���
Lg%>�y��}�!�2&y���)���X�/9+��/�9An��^�6ej�f��D�����GoO�=c�&�?�B^$}�W�qq�T��$����l�f�./���y�jP*�(��.�3�'{d�K�JIS8�l|�-�����:�{� ��\��r��\�y�P���2K�)�t�M��jhd��/@�.�M��0���,�{m.�.ME����N��P�&�����&��������/�H��]h�CZY��Vvz���y^0��S�~'�N����)-KW��P�;o�������w.;W���p���N��E���*Fs�.�sk=�X{���U��������vG~z����F�������<���g7�t`�<������8����^w������E�����e����7�7$����z�l��VI��������iV0��Z� �������tU6`k}�e�M�Q�qw9O�D�:X�#�4L���Pl��j��Sq�M������7-�<o�����u�j��}=��Xl�����4)���y9;UnL%�
>
�[�J*��c7Ja��O< � ����4	�-:u�(�#����m6���>�C}��:���FLfmb�f�ARKaU@�m��V[;i����/� ���>{����i)�b�w�5��|����2�K������l��Vx�n���>�x_LA��?�~��e	h8-�A
��k	u�V��M���]>-��sZ���e��T2{dB����1�q.W:��[�8�:X���3��TAs~�B�y�s��e���U^o
�<�w@DZc+(R>.�^���0y�����e
h�d(#s�#��j2��Y������&gH�TU�L)9�O-�f�P��,G�s���T�������oD�,��QV�L%�We069������"�d���M�_tb����y�6�'��m��jJ��N|�^/��)8�<������,��-�����<��jD4:��(�0y:������:3��@��/�I5U�L�`�4����{��q�4#����=	�*JV�9z����
#x���8w>�!��<9w�p�0|ob��@&nuu��W�8�P��(�����z�����L���I���x9��Y������ ���M$�Cm����3�R�@,��vz~�k��3�IN<�X�j(EH�o��>��/�����+(��\�����;�rH��L����6Oo�����0��S��L�2G�n���4�#�1�`�Zv�l�����V5������w�|(�2���P���ui�W@��!��c���ad(7�3���zA��n���;�2��2����3����YT��B����T�.��������e�E��T!�q��������$�����w��0��3�W-)���l����}�qxO��n�/{�p�E=�U$��3A�:J)��
R����o���]���&�yV���e!����d3����r�wZ���KfT
�V�K�S��T1����H�������8Rg�����t�<�����A�e�����F�)�9`VGy�.c�()5�q}^����`�e���E���& &d=@��VHP ���~��s�����o)�����\B0��2N���d=�������z��W,Y(���PW}�U5_����G����SN���X�E	9�oLuH~*q���&��z�!��q����G����4[���7����4�G�r�-��TX��U��H��VT�����WF���C��6$�[��1�����%�.txCm���z��)�'5'�F�Jw%��N:*�Z4t��6�z��@���dB���?	]&�?�xO7�� ���]z�:Y���4t�9���+������T�������W|=��:�]�4"��$�E����)@\�ue��/[QV�W'7f<�o�f����(R[r�����>HT ��0'�
��d�����
�A��-&����J�IW��
���M4�2_��b��M�����`?����~����kJ�@S��HB�g�aW����,���KC�w��/_*�Z�0�+FD�RyX��)m�xcn#�`%����kM�=0���O�����9�	�������`$�$%3D;�$��$��R�����%�zk.��W8���>3DJ��(�{p>L�5Y����+����gTx�P�q,��;�U_C����:�)�*�-���Sw!�7z}�.��U�W����I�����TZ2�%���@��0�[9S�s
�������~��.�5
2�J0�
G��{�Q�&�
���3�?o��� �o�"�*��B�^�?�_��������_���s��,���aE3KK����(��&s�i�M�k���I�����~�4�MR���dw��Iv�W(���w��	���(�~FGY}��t��v.�r�P{JF�whm����e2.q+=�����@0z�T����\8���;-5�f���.6�kw���L�gf�[��q2��uO�*�+��aL���t�Vt%����/-�m-�w&�x,��/�����4�7sn������������y��6� ���V�9�������G)v������a��6Qr�y�C���DnGW�;�=�N��,"�+�b��K�d`s}Yb=w�ghW
����u6����4���)�{$U��"��w]�";���/���������� �Dk����_S���L�3J��e�\_����^Z�����E%
�y�,u
j�W�S�����4���r��u��a��~Wm���D#o0��>2/�2Pu����R��K�5�����5.�'����E?{���*���f���]�*�HZ�����t�?��	ay�^����a#z��ML��+�?���\Q\,���T��]�&�^�TY5�*��<_�����0���=��c�����b�J?~T]>�>��7!I����~TG�Q������c��bh\���l�Y���$��t����A�Z:���^���"�%��V�����?=.���$�k������(�qz������#�[3��&y�a������Px|��X�M�������Hb����p�y<D��8����3��|�Y����� ��� f|&gT��-��x=��{[��t%���^�����_+a��|6:���0Y�����������/Ik,}\@�����^?:Pt�W*�X>8fX�d[Z���Y�l]@��kt��BH�v��/V�,��O�I9JJ�M���N�`F�.U]6�{9���Gc��Y-��8P
P5cP#C�$�����-3���n���=�f3)�lz��$�hSy�U4K��(?O�
��q5~g�,*�_�����������:�wOn����!g1�C����QR,�������C�]%����Q��Un���;//T����U�]"���"`h�
I �X���k�)�%vR��V�#<�W1�.����s�e�]�0H���s����^^��7/�D��#�`H'���G0&^x�F��U� � �P:�����+'��#����K^��<p��1�W��$.n�{#�l'��]��J��S�Nk�]��*K+����:�����������(:-r0ft,��{oai�;�������>x|	����Eu!v-��^pP���}9~*#m�N�B�z>�~���H����Ty]�s(����K���'|� N��8��,m���(����F,��&<�����x '�������c���Qo���������d���]t`������t��jg9���nJ'��O}�`2ZA&��@ev���?���s�`-��Dx������szv{}��y�������]��5aF��&�-- �����@	����%���x�/��5#M{�k'*/&SI�Q��i�d����Fp|vS���Q]MT-����=�������1�V��A�x�;*���
f����4$��T��B��8R��)��7����xwvw�g8�����%jM���Z=�0�aM��th��G���s6��krRv���H{�/�������'��y����s�1�n��K�����:k��X���)>cx��������{J['~a�E,�:�f2bt�^��4��A�DH�Xc��0��%X��2�1�P.���OJ�W�:s�q�({�'���qz����6�H�}�r�u����,���^����B+i�g�����j�L(�"���t4uPg:�4�J_9��<�zHN�k}��)7UQ��~c�j�G���p��+�o$�T����%c�Wq7r5���p���>�.���H������j��C���#qs�J��DD9�h�H��b�#��TL�G�=�D+M�4���:�;���
(�N*�*�v����H�{D%I����v�'��7��i�,�@�����R��`m�d��;����(���R���hb�|���z��p8G�0��H�%r�	�1�1�����v����(L����.���C���#A���w���������A�`�����H1-�������
'�]�b�@k���_�I�s��Y������}���Q�������=�%��t��g�dx��M�]]��������j�����W=�.c� �f��\��I�axd��r�?��'B|�����L��lV�W#�4 
y���IK�6Xc"r�[Q��p#���:�!��>�cd�#�cZ�`����������`EmV���T��T�-�e���&2!�gZ�(*
8���,�A�
1�&�$���\���y�j&GaA��n����5����x��y�y<����,�q�Q�1�����M�(d��F����:�Q���&�� -T�"gy���C�����%��;���
�d|@C���{�'bE�ML<C����@��X������.�����(�y��6����uP|�b=���I��c	�13��E����/-`%��"��������<�	g��mM:c�31mW����B�IIv���B��p����*yCx�<���N��u/Z�63z����}��G�4����-L�pV����{4�w��|���
k%�2Q�R%��W ���M����aQ����8��&[�F��&G�c���W{(�c�p�����J/R,��-����gn�B������{;�	�pH�d��k@a�<S������������H��:H/<���,7Ff��f5�\��C�
N���M2Gl���V�������"f"^�0����Q+R��Y0�����]��l�5�9���Mg�KcP��{Sv��5���C��y�V�B��u�sY��!���<o�:�0�C�T\#��;��"�'�4?�X1��LQ��s0��{Y�sY/��`����,�d2�!���q���te��':�j��&En��A!"Ka��DS��F�V�2����p�h�Z��hl����~��F��b�\�7��KN���it��BA��o�PHg������<�4�����{�%�ML���rk_-��-��J��cK{3V�n+��w����j�v$
����cU�yn�R-k�������A�B�/L�H��xs*�'
7��7��=��������"	���gA�'�]��+#��;�!�7V�k�#�/`��!�U��:{�J�8H�������G��C0����t��T��qZ)�zu}����<��O&�H���������7.���Qg�����F���3�:$���jE9���t�je�7�L��6hC�x��a���O�|�
��|z|3-b�Z���^�1���'�ypwD��9b�6������-�q[��c0��B��e<���kgR�1�!OW���Q�����m�;}=�E��6e
�}�ua>��W�E*��G�A;��+z�L��Ik����M�1:������W�a���j�jk�+�����g�
���5HW�7� ��%�;d���\���|�j��
�fn���}[��$o����$�]�BAQ)��x�#�JAB&�-���K!�V9��EGP������1H�?����b���L�
ZI@�P��#kUgLQ�W�]#Mk�9B-b�,(���O��j���	���f�$;��K�d<�q��K������K����RZ�t9��q���~2����'�!�������+~��y��/�{���e��b�D�}�6_����u�
�
�F���M���Q��v��{fP[}����:kd1�"f,1D�-M����%2'\o���)",���>�J��+�/��K���6w�Iu��/�`�4�b����!`��CB�YOb�#bv��Yg�.8�4��0�K����"�H�Z����'�a��0Q�U�X�hp9��bL>h^F�y����T�����{y���_��
u�
^���U�V,}��{��xM�I��O$Bb�_��s4j���f�Y�y�a�[�	I�#	I7�&�H[�F�d�������*Ib���oR��f����XCRrkHpq�=\m!�lm�^�I5d�]eN��[a���^�	^i$�;�Q mW����`PX��t�D
�tt� �y72&��R_M�I��F�o��R������[���S_7�v�k�[�b�o7�����l����J�6��Z�
-b�P��o�P)�i<���p����7�o{<9��wn:d���fA�P�A�X�zZ ���
>b%���6X����H�C#
�K��]*3d���7(�R�j�Gjj�(b����:d��O��(~
�����r��-��q�Kja�M�B���a�)�r�����f�U+K�J{�(���Fla(�d��������&�xi����j�}W���Tv ��e-��Y�	�z~gk��aT"����tI�}�d]To����n���.~�B�L��+hP��Wo��^ _~�.by��.��qq��*�N�*/��H���Vw��%�=i0&�n���zr��v�))dK���Q�V�b�^.��{y����E)=	\�h�/7Zu���B8'�Q�h�/�f�����a��=������r������7z�������L�E��y�U��D������
~V��`�Z�V��������%�,�)��W�1�:��s�^��v�U�s�eu�����Q]�}{���U����s�;�X�E���5+�A��Q����C��G�G.q(�p2�<�C�)K#�&�~�L�	L-e�+x�rc�=�I�����<���A�0�t�����\�=���W{$������tX�����"����<F���x�5>R���
K56,��a�,��ES�Or��:G�cKvS��E?��3�	���4��4�^?j�������^�5=Cs��,{+��zA#���!]^^~�;���7' �h|����q$M'�b{�V���J��m����j���59�w&G"�E+U�U��m��^��)c����������B�GF���S!1K��2�0��go�[D��$"ka���st������{��SX������p�B\�h��"_��w�9�!%[��
9����t�CF���a�������7��V
(.�a��2x�R�Uk�����7����������!�W8P�2��{��p���Px��?�9��a�6n@�I/,�<�f���L��G���vd	
�N�Dl^��R�>0HA�{
h��:]Ba����7�G�S�|-��F�z�����9j��
�9hdw|�LzRIH�k��>bZ�x���N7��{�tB��}@������;�'�����7�7���|��U��svvr��\]�\'^�0Lv3W@�����N�7(uG�$DM������8�����~��M��`�]
@X;Rs���x��X=Y�jcX����b�_o��~����8��������4Q
��j�t�� c�bE�5��OL6O
��r!�:�K��,@X2x��+5���@���0�`���?p�$;�~�$�5�B�+�\��8��t��)���P���"���~KE�nN��3a�q����3�=�R���r�����s��*\�� ��F]S��4O�^�1Bj0<�F��(n��S�'��:ib��N<7g�~)S����V��5��Q�?��F���\� �=$N�FW0����I��w�ki��o������L���������L(� ��86�����='�����g����kg��g�����������=���o0��7��	�pp�	
"��������a�����]c����h��crx�
C`7������� �s�����V���p�S����&T(�v������`��n:z�w��u^��k�ntR�����4�����v��m��y�^j�v4��#��/��+���Yn��LdosR7��\�{�D�C*�b���e��� ���w.��`+<�<??���t��7��g7��_�}�=�Y�������5n��8�\�'4��m���1���(��P��F�]n����FR�B,+�z�w���-��J$Jx�]'�_�W���������(8&�����+K&����b��3�_�W4�w\���ho��BO�/���m�kR�~���t�RW�&99L����������J�T�bG{���}��L.p������>���D��cv��a�Z�����s����t��:|w'�x!��'��fs�/����R�Jd�Wf�,�]B�����@��-p�Wt���:�������tJ�mP"����$���@�����X��!v��������,��'R�%Bf�p����,�;1���}���W�(#dx��%��S��NP�?��3b�P]�s��i���1
�����J�0����$�w�r��9~�����,z/����b�����Do8�;#����y�_��[.��p�n�>Xo���9�z�v����~�5l��A+[��)��O�-i�������tzw{��%]���.��D~&o�9��7�=�����������r�
�s�4C�pS]��x�o�iG�O]�>]5���5U�sT-Mtm������:���H�6�-g����7��pMzv��3��?��M}���J����[�bs�U�������Ah�G�HQC����O�r�sG�8r�tn�����������wy���������������:G����D�������q������r\\�0��9�L�8�<����~zu}rv�9^���M����qQ)=\����pX�����h�Z�V����
���5	HY\&�C�p��PZ���^���b�_L�1R����fqk��e�����>���Ws_��9m�}���79����m:]	�����p��f�b��*�;A�ZI�V��=��A���1�?�z�J�V�����=l
Jo-��e�I,�J	�!~��I���������pN�|��z���%��h��_�F+|�>�9�)k���{�����W����KY�d2\�t�%/3����g����o=��s�=�@�������TN��������������{s��� g%���8/���ry��2��?_�Z�>N�MR��k��J^uPi�0�
�a��v\}��1������6�)|��"Y���O��+���	�
� ���
���?�ko�`��<���
��tA)�^_�;R���O�OT.������<�	Q��?�\�������i�/_���y��0Xz=�(�~��U��jM�G���x}x�2�9�..�:���I�M�:��2?�����0iU"�hiU26hZ�B;�J#�P�����`ge
r�]�~6��+���v3��9�t�ut�k7#�N���+���j�oCf,!E�����i���mx|Z��3bx1��Em�$��L��J�Z.�������$�+����~�T����hQ�_	�Q���pd81~�5��(���������O�+�J�JS�k�r�Vn4+UU�T������?������i���|C������[�j���Q�1��j�~�:�{�~�����zD���=6GuLU���rS�J����c��d^��tLA�~,�O���w����>�x8x��A�_���e^��2�*7^�j�K ����vw��O�_��\un���Rm�\�IW&.�Q���������*�D5S��I0��'6��#0^�`fbtlA�r:�|B�{v��Nrp��V�_;e����)�n�S�(�v_o��Cg���]N@�X|�{�5~8��&[;�$���L �Z�i���6�v��r��������U�X��"DN<�~K+LF.��6$����7���X�5�\�Z$�h�'�X�V����\��p�=q��.�)��BR
BG����xz���6C�� �*��Mt�m9���oG-����.�v���.��@������V�6�k���%7%���G��5>���Q�����&Q>�h���$�K����&�/�5-<����r1�RT��GCp}�g�G4��"�"�V|duo�hB
�e|���D�S��2�#�#��TKbE<z��&x��d�����f$�	b���A�#�
��ft�|�y*�"�d�WG�D�D��dv��sE���L/�F!&B�TE�`��!�p2�����3��h{tQ��h.0�q�5���i�A�jbETr�]�XsvSl�f!�J  _h��M9�������Y���8�W�<A�*4$���5\w����U���p�8?�,8��s���l9�/���bM �aT�6�2Sz<�����Cri�����������v��F+1�k��&O���rq�S�X.��C�A����N�����=P��-���N����~��'�m2 ������C�
4�w�pP(�����=w6f�`Tt.��V{+s�b.gQ�U���E��V�U)�=w6�}�dOV.�l��Q���g&�E����f����?���_-�������4���_�%;Wos��M\>��B��r������9�x��xs����d���&x ���\��}�u�\�d.V��z�y�x��s�(W���_�#���\e���D��lV9�?z�{�
1H��6����YQ1�bM��X6��3J���b��{��Hd�\����f2W����f�����H�P#�sU��JM����f�a���=�55�'>?(�n��m^��z��w5l�5�,��
���o���k�a�X-�~��76��T�$�+�!�1�9�"C
�!�O\C���d��_<��?F�G�}x�J"����@�g&}Hg����=Wr�0�3���:��&�yN��%Ze�q��k����
�"��Bh�_����;��NV��T
|C��Yr�H~$�������F������s�v�]������u��us�aF������������Hg��z�N���v2V'����
&��^������K�C�6S������br$J��"�9�a��C�z�/]���_~��U?��[*���0��r�pR��A2��.N>�\���].B�s����b����r��h���Q{8�4J5�b����V8��WVC���$Kx�v<��r�9��Sm�;���'4q�<le]���2.��o�62�$�'gW'�������9q�w.��\���f�!��)c�i��(�����<���pI8�����%_* �.���L�q6�c��S�<�#@�K:.]�9��k��I��c�j�Y����=wm.�#����� i��c%3�T�P���[~���=�V�7��W.;\	�Q[��&%y.��yv���!�.��r�J�\4E� 9�����)]x�}W����
4��u��N$\o?�������#�'v.�X�0�+a"�|'$�X����a���%��[�&�Nh�N��FrAhs��H�&�Uo"���+@��`���Q���j}����V�x��U#m���	�����3���C��z�<�������gtk����8�Pk���%�-������B��C8/�F��1��=5��U�p�I����e9���-o9�
���BtSH��D�GW[`��D��^�9�v6C�����.s�#�0X���<���e��3�%����K���'��#�������#�20��	LD����E}��1�&����3m����`���i�cV��
����Xl�~����q`5��������^-�"}�d�& �y3�Y�(z�$�+���)a��tJ�&�,�lwH����%Y;������	A.���t����P�xE�����'��8� +J�t�b-��+�\��\b���u�B�Oq��2�^�O��r�|�F�����)���C��"g�� ��R�������1�%3�-�Vh�#�2O�t%��]^'/������(Z��so?�+bz���n�U���H�D��3s1�q,���U�����1m���R�����	/q����%��x��w�Heoo�I�����C�j�������G�z���<?6���n0��u�#�B�0H��'��{��Gx�D��^
l6^�-�\c"s{��`8,
G���]��x������l�	��'=g���r�s�#�9 ���`�I���L��)>�g�e�W
P�sTg�t�`�4�z���Qk��H���{IG>��K�Y���rU8^P��t���o�i]w��\���uR������n���7������E�Q+���,�N[�E������z/c�_�;W�B��<D�D��;������B+9U8F��I��zH%�f����<��>�|`�1}���:��}
EX_�RX�n�����3����(���:��;�xtv�=��	-s[+
-��z�Vg^9�6���9zR'�����C�r.�X���]��}���x���X�R������NQ����shK%{����X������lq��%^+h�P�Lc��������?E�+�,F�	�g�1�������I��Kb`��Ri�n�Q��j�=��;��W�������i���:�^9���9L�bq_����?9;�.�Iy�G^�|��v����`�L������/�w�?7���=nv��70�#T�Z�Y��Lt�MJ����Z�NB�XK@m�����-�m	
�=L�'y7��y�&W"J��`ywOVO��
�e=��=Ra@�q��L�r���@?-8� ��
�*�"R�&�;A6��3n(�)���%bV4�4���B9������x�x�2 pC9L��1��onQ�~��2���;�K_�<4m��������m�QY����7�FA�Ym���@��x�W�<��F9���Y�R��z=�P�y�����'�������^�������z�'��7w\��z�0��3|�Q�)�>��H�'��Ad����w>��\�#���u��vO���:������aC�����heP�@%���3�������n,��������������w�:���]z)4$���K���@�=LKi��F6#*��Yj��
����R��x��������������{{�r�����n��`~D�� ^�pWZ.Vq�
�����A59�-��=2�g�$�e�.4��=�-2<GkV�Dwu����{��!jNa�&}���0������	2r-��K\
��S�x{[�*�������G�[������q�b��r�5zZ.��tp@���-Zm�����M.��8|����M7]������������f�(�uB����6r���i��s}�����s�q[e�W��|h�i1
2� ��~z<���P,'_����P��uR�Z�~�zQ8|��pT��
K����}��� ��F�"�S���mC?'������7E�W��������-�0 {�b�.�I�����l��e!6���x�@sUNL��������N'�+���I��RO[[�B6��)�^�pR[�u��-��@@W�
U_�������=��������.x��l�f�����t��g��"�`k)SJ��i~x����3<����zz�(�vh�i��xS�U�>k'���*(��P��P��J"CH�/�	�_?�-D�z��w((�L��P"���q^�����p��������`vOP�x&E
�~�1�r���@��
����pv���d�o&��w��UYu&$��l��-��m�I�~����(���M�|)�j�6'OLW���'z:��4]�M��
��u�u������p�������l_�d��7}�\>�__��?�_4���z��-�XzD<6�a'��W#jg1�;�
1iv����?�>�d�����}���z<>	;A�M���dD�0[.��}���$�������%WZ=C��������E�&�[���W��sz<O&c��s�]��U��w�4�N��X������N�_H���;��4�M�iDkLG�,Z�w�h&����Q�������GuJNJAp���;����I��W�#�>�x�����-E�-y����#/������
it��,�T�O����|ok���'#$����_���4��1#>����IA��|������dIa��;h&�`�$>�sw���>m��g�3���uOn�/�[ry+?N>���./zW������{;��D�^����������B�^��������*l���}�LW������%R4�v�Y��*�b�����W86��Z{�LM�?U���R��:.��bK�z��s�����h��LnZ�YO��VC�o��z,Cvt��$�����F$�'�3���$��:T��~��dz6G�Ty�u��b��(G�Q�T}�8�`
�������#�w�/7��d6w%SI7�9����Vw����vB����_��TK����iC�����!�l����S>C����V�J�M�p��vbH�^wu���F��w?�S��xh�Q�Y��-�����(2�eS��}�=������m�W�g1�I�Q�(8����$��}��
�)���[V�_j����CF���\�o�J�}��Bs�Y)W�h)�'�6��Y@��c��AH!d������X,X8�>�N����1��I�Q�]�����<\Xt-���#�p):wj�y��PD
�/QC0`#q2b!G[8�
�|���;:h=.�����t�<���~�*O��"U�aCko���@�W��1&�Y�ZA��DM��}��r4�X�\Xs�pd���7Ywtw�v>�-�I���$�R�e��i�.{8�)�vR���FK���2S�2�&����K���?��|��\��\,�*�
Y�W*��W����<V"M#����-��Y�9�o�Z���?J*��/�A-��q�@v&�<T���,��a���T�+�		M�"@|""�%��o�
Eq�����$"�����MX��;c������E�E�;!�QV[8G�!��y2�,��<���lN:rS5&��`����r��t{J�a�XE��gJ3T9!��@>���-�5)��	����t�sH-	-��k*Y�@ZMa'>-gL5����*+�dS�6� 'pW��������/j��j��w��@�e�9��pq�I��&������2���IP�4D_e�`3��;��kf�'n��}������g��MJ�F�&+H��h��/o��n����t��F�~�[[�L�(:���������'�h�`�
�&4,��������m�����VC��9�Z�8�G5��4�<�Z?���\__^[>����$�$��n6k��-��Y*[0M��@s��/�+�s�v����X����_��!@�%EJ��D
�.$F�z�1T6�Z���P��MF��*�[q�(�:f�5��aBk�L�`4v���A���Q|���!V���!���#�����v�%����#���R��7�����3�u�����d�OR��Y��^I��:T	�ERD�4����2��-�S�����}��j5��M9$J7`������������Y���d�g1
���p������$G���������|�i�������k��D#d
�Q�]�HO���G�bjM��z�!>�Y}I\j{p��w���*=��2���GZ{{<��T1f�\��*�nB��/n@�A�QY�dd/��;������h����m�%����w�lY��U<N����:C����|�N>�2k8�2^�b���&���7{�w�^����;q���=A����7p��1C}���>J'��v�T�p�L ����q�Sk���>��}(�$��z`z�����>����0N��Jp�O�P7D��<��(�b�!�����.���Mln�����L�~�g��q�v;�]f{�||�oP7R��bB�zd��ncF�d��y�|C�1������f����A]��O�Bdu������{���%c"dT	|��A�=�
��J����D�C(w���<d	���p��D�>O��mk�V�D���t
3�@�sls�]�l9��P�i*1
�S���3}���;����U�����B���c���^����{�������8k��Y����z&H!���;���i%�c�L_��������W��NC<�>j�/���6I�sl���|�a#�� ��W���Y ���x��3�����~��!���:a5y��p�������6�k���
H��)dq �%goJ�	��i���� m��-%��
��Vh�Af�
�5��G��$/���)���Q����>��tB|�1�GP@�������a
�;�����N�s���"b3)�o��s��i/���q�r���[|^]��A��q��r�>Xo�m���t���l[����0h�A6-�n-a%�#��4����|�
;Y5���4J�f%�r�,�$��G�c�;�=u{�*^$J:���~i�����*��~�������@[a9*Y�v��c[�*&w$9h>K���&k��+c���+n`~��,�|�BH���#��$6��c��$�)'��|6J����
������I�&��]L0*"KK;�1�z����b�
��rt������[��#��H2q{3���$j�@�ey������B��l;d?�pXK��&S�z�/^�h�g�R^��$zZ�Qc�r������0a��n4< ��'6pO��|x6���`9�x�<�������Zo�$z�yy��=!����}����J����UB����!%0�7��_���?��������7���z�Ok�����l��������	���bt�&�9�(��a1�cB�)j>�e�No���D��Ua�"$���6$��f�rI�=X~����\�r�W�G�"�,Ob1��
r��z���*<\��'��d����j���[j��2�r����c�2����,,�J�"�W<�&���:;���k�ab�u������l����gy����;N�kL0�
1y#�UTM�p�s�X�c�+���T��I�S�/r�
��m�Y�@�q����]�+G��(���Ws�J`="�m�2��{���?����7��)P��#�x�����Bd~J��1Yg:���x�i�R��s[��=�o�����z�������8�x�"���2��DA����J��)��Ce�
1:�
@#���Lf������m��[	�k��u�l�-�
������������!���q1�h�]
�Uv���y����[�J�����f�>q��Gi
����n��%�B�a����V�,�"��9.��GZ�������A�H���&57E%����[���Q��o�6OQo1�����:��y\l_d��?����.����k96�C��+�g�q�>#��z�7p�C�X�K [!F�oc�^ScX5'��d�QNt�@����`����"�Ffg&��"�$�;��z�3r���~�/`����3]����(��Z��5NI�y$����t�=Z���C��Lztb�!���:!h�DO���*�om-�"V�q�zly��9!D����� ,����d.�����$k������J�m\e%����X\N&s��<�����FdM;�#��r�1m��%����h5�+��n(u�����^����a��G+���.���H��Os[���y��N�����l��HVb�*$�D�I��	$�p$e�e�,�����p�����.i���)���/�����%��Hg>4NS&��`���(L	na����"r�rv7MR�k�E%Rcs�����1�E�h���������	Yr�������|��}��*C����{r������[wxz�5���X�z�_g<������nyQ��������/�U ���
4Q{8m�X�����\�/4��3`z�h� ��_�$;�1�{���������z�^����tO�O;g����u?��^��������D+>n�{F�x�*�!�w��{W�w'���:��s��p��������V�,����'�i��"/y����lg��%����[�c�f��2&���	*9��k���+���d���u={�#�j�x�r���pR\��N���wM���m����4�l~Zq|��B�H�bfa�Um5�b�k5+�js�jm6-b�g����:��5�����kE��D�QUs��]��N6�]n��Y���*����1(>���Q&]N*�����6��j>�7�2��X?*w�~�P#�JC��g�V�4�?zN}�i>CL)1�bq�0Z^
�/N����=+��*"y4������V�B��i��7�1�x�ia��m�v>�M�|��(0^�BH)K���u�]#�d,[��H�����������Wn�]��b#�r�N��BL�^`�5�������o��i����3�f�d��bl���]��J?��O��z��#�3���UC$xa�<���A��<��@����<���@b�PkC��Zm�[����,
���t����v��88�0�Bd����8W<;�E�p����'^�u�u����)�o3�<gT�����_�^/��ZP��k�b�����^�Vmf/�i:�5;����0,8}���
,��&�J0>�G�G)
oQ������xJ�iMF� D��c�`����I\`��$����pK�R�
��ua�����>�9:�A��-2���&�:�g+_�>�������A�Y+h��?���K�:*~�N;3���lOF=l�_>��Lt�OsQ�f^ D��rrUV�qo��MB��[J���0�|%�b�������"
8;="D*�X�YB6��e/�����id����:��v�XmT��v��8��i�w�<,�N�Ie�r��Q�=q�I��(-�q�����B��K��aM���C��{�����,�%�E�7�x�����C[�x�W�'�r�k��!1�R�d9X��dE��;�0E�2�k���:(���76�U�Y�V�E,%!�c�S���"Ny��������"��_��z�
G)�P*��Tg��2�!G�c0��%>|����v�>q��3��
51�����O��u�����,�n�����/���A>�&X�q���	�����W��z&��"��������^��`����7j4��l�sQJs�+9h���>H�A<�hS3.x�x���K��\�q}�W=�D��Hq��^
Qu�F��3t0HTN^�%
�9�����	����h���m�*������#�*�m��:t������r����Z-��({$WQL������$^d-�������E����9�*:��y�9��S����9�}�>�\�����	�z��7��F�l�7���Mt����D�hz?�&,N�SD=y ;+,'o�,�t4Fon:���o�l���*��k�)�
*��W)yp)�Q����[�*�Yl�N-z���Z�:I���9����v�(0�+����-���E�a��r��x�@�.���*%zh��Gp���-� ��I���{x�O�@��������{m���M���
A�x����"��=��^��j<�E@�5v�&,�S&�(/C%�����;�J
��������p��O��r�$l���WSru�I���u�9[�*�?���7	�&$�R
z��{q���BY���iW�6�������$S��]��:�V��x���Hl���������{F���	LW����vi���	1��^��
��
��Ec+/�[�&�n(��s!r���?�RM��������l�����4�s���z�����z%��p��y�&m��g�l�	��Q3�}C6)�e��� . ��V���{4�����e����	�;7��9��J"Ul`z_� �}�����������k����v��jy.�b����Ajb�&����&��jI�|�&c�	�}/�s&A����~2������p��5��b���)��,y��������.������`|��y����C�����&l^	ro�Zs%� �����c��H�����d�Y����GobO��;���z�r����bqP*
�5��2�7�V�m3�YA�eC���n��z� �P��NR<	�����"c��y���Pt=0o�wY�E;��H>��wa�������%�I$����da�g� �Pd5_on��g�++DW ��n��y��8���M�@�D�����/���o��&U�o�Vf�4���K�XXS��%�������!pk
�&>#�����wx�^��,��WI?.}5de�r�����Vm��B
�*�^�N�r���������z4y��z��|�|��5���H
�6�{Y���o�|����$C��`��c�-Vk�K�Je���� ���x�������?�O����V�E�����P���s-zni73|����N��"�Ch����^�#.�;kf��`MD�Q2A�"�j^����
6�����A?��&.��?�po�?���|��p�Of�!
d�m9�i#QzU4�#W���t+3��::.?!�>�� ]a<��nw����lr���C�JX���B��[�B�^��aF���//_�P�Js�\@��[k����&,j	 E����������#%���'9�Usu{��q�"����t@����N<+��V4DN�����im����|(&���K���eu��8�4��{����Ln���?Q���"A�(y���DpM����kC��-t����4�J��(��:~�����9��pqr����&���IP�����T��m��q@r7�w;�y�[�G��������%~�}?,����n��Y�x�G���?x_:���"����"�hx��l���|�� ��8��XD{}��c��]ZM��k�*\�����YW�Q�Z/������o��v�~��(N�^�����_����W95��R+�m�������l����M�Fu�����:������*�~*���s�9>���4%���/Y�|����*��[(�q�`4a ���hb�A6�V;�c�EA��X|�cf,}�@��E6i��r�C=�h#7
�nWZX�[��j&�;�b�?I���i�
��1X�'
U��
"v������T�)PMBV�,n�#$	�&LJ��Z�29� ������A�T�C=��9{{zv��a<�����Q��Q�E�k��&�����)r�9��k>��?%�w�����O��t�a\�	����� �
�lf��c`Z^ru]w�U��x��\~x��-�?�)��I�T�H�yl?�>�&�&}�'$Gig��>�^��N8d:%HA7�/�]��d}'1�+�c�a��?N��@;�`����p3��|S��w^�	�mw%��9�=��g������Jd�f�U�y�����=o��?�z�����t��Eo�y���^�������>g��{���������������p�ya�
�Y]����^���$�����[��C���Z����������=o�]5���o���7]z����2�5��-��uzM����8��>�AqW��?v��������U��&��{DcG����1���s~���>c���E���-��fu�m\Rp=�Xr7��G%�W,j��W��U�8��������ZS�'J��%� ����i@yN�
���dy�U�����+��x���Yn�F�^���d}y7�����b�8u�nC�������r��R-���;d_-?�D\<��#8gC]�c�Tk�N���i�R<��5M1.����9�{�9��6��m����|���VD�?X������Y�������f��D"����Y�x�F�Xl�[�������k�eN�dB�J�(N|T��0Q_��X�k�0���xX�`;B�9��pH�?jDD��
c2M#���`��Ly'����2��7�_O����Z%�'�������E2�77-*
�t%�
a	RH�;
��-�7�@�\0.��D��Qc-��#�jf"�Na��e`��xs3��P�4/��A�?����p�����al_7���T,o�����n���^�;��]���t�^���E�@F���?�xS_3��P/!k��
���P����jO��3q8]cm�9�%�(`�vw��Y]R�i��i���Q� $G�}�s7�S����j�N�C�kc�#��L��g�6�#5�`M{�R_��� t�*���O�����v&,�A��ybe4���u�^���~eT�W[ �x��_����������J[ip��t�
����bT�B��
��'�Z<��<I������y���N%}��N�q��x��Bf�l������>�v�!����:�N�j��"w�1�L�p��$��D����j�r�5���&H��~���g���T�5���.�.*��r��GFg�v����j���+��W��������/��F`B1g��A�g�Y��<���\ ��dt3e��e�}����M��(e�1����f�@6m���\Ei�D�u k2�\p@Q1�U��a68��CF;0�U���u�r{�����E���<x��
A�Ei%�T[R�q�P�
���������=>|�jB�h��<���$F��a~I�7��P�p��&��_0<;<ev�� ����^@���&������$����<�X�p��g����X��-s��Q��;<�J�A~��bY�o$k��?�m����7�����0���A��g?@k�!H�0O�����%�_��{"p-��r��������h�`�v��ea�en�O�G]bt�LRh���f$���}0'��'l�	v�n���3W�
ZTn��J#��Z�Uy��\�r����fI���m����t��;��r=����x�I�5&�
��nhX��U�OQ�j�:P��g�
��T�`|B�a�32��NX����{��gW��dM%�����FT��b��5���j��_�\���5���P���B����I�H�����@�7����B�W�Q.���rc��� ��v	�(U'�.�(W�����Q���5��4I��f��XdBF�E1Wt|��i�s/ut�e�q� F�3��A ��B���x����m����N�������<�Pk�6�>�o7h�x.6Z�Ud�(�*nb�"�;��f<�Dp=�J�������9^w���U�6����L����wu��	�.�=l
J�qXnUZ~c�;��VJ��#�`�h�������x�,�����p�x'����Kt��>9:9�S������y[H����a��_��o��8�)�t}�����+�j�X�����.t��=�v��_*�������������F�EP�8#������B0-P���]�3�(�*�,7�e����
�j���F����T�5K�S�H��U���O��sc����C�pU����`�FM���p�����F��
Fu��o��}u+}���rS�J�������d^��tLagy,�O���w�����?_�����a�D����~�Wm����������,:%����P���_�:7G�U��_���H�*�J���{�J�q��a������#s�����^���u����ss���p�������&�0p�������)c�C4s,[_*�Pg��#5r��m{��j3h ���!A��
c��!��ns�+�;"���F�eH$�Q�{�������%�3<k�)�7��F`���4%C���F#�����2�E��k��-�(�����-�������%��__^��A,����q���
��x@����)�,?�]���~�,��,x�T���<@O6��VK���������6*��E�Z�/����>���p�a��s��K�g���y�{�_N�_�/��U���t����w{ga���=�_����7���_8\��������Ci~Hu�o���Ke�&C(_ =f�U����	re�����93���b-(';���k���%*�U�",�
q�r�-mI��:��,��k�qU�����q�(Z�<�w�.z�T5q;���kHr�����N<G5�#�=;��V�;V�b�-��L��9x�b�a^U*M�@h�{���&�a�Aaw����
�5�^���dw'��+���i4J�V��y��7j��v���j�L�egj�$��&]`������s`�M�P�q�.����`�
�`n��=bt_�Mj�4A�xb����n-�[%�No6��W��x�T����m����}n%�.�S��7�<t������D��D�G������>]�U�,�[���-
?7�r���!�^�zFs�����I��c�C9��.��xb~���z3-�W_&�]��vq?eX�{o��:��lH����m��rD���|�����l���D�����Tc��`�h?�g4A�j�����������l�tS�W�:�#�&bU�����h�3=��i����8�S}^������wz�����?rU4����O��+w���Z"���LV���c�@���,��vi�Y�<��z;�e���ak{N���^��0@>e"��s�3���-�)��.pg5�b�|Y���������k���$������~��N���{���h����������.��.Q�]�aC��-�$~�3��aj�y�3F�_�����pT\��E�0�:f!Z|�������������1�����fI���*�F_��k�~�J�H�4���~}�~kL����G�v�7�!Z�8�8�K:��q��-�`�n�c��y�/(�d!�>��
�??����u����#�����Q�X��r�6l��V�yu���+�>��&��o|����
���9����NE�b7y1������[�|���c�������)��\9XGn��f2Q4n���@�����x�h��O����?���h�� �%��,�F�@9���]��]�(���i�m��V���S�3	�����x��o���#���C�
�_�ju{���0����Y6�������y������W��@�����l5*x
���Y[1I�taRi����#y��u���Lg����]K5]��'����7[����G��
���7�=�~ON�7��eg������(��3���� ��fc9.fy���`r��F��{/4�I
�;�~5�"s�H���I2�5�h�;V@��$���q��6�^����CA���+lNn�I-�-%�)-��J������E\8��=*�uTe�S:�W�l����,f,-��z�=���� �Jc`�.U��(�0��MD�a ���#xp�4B~|��g��pK���^\"C4u��Z�{�z)_E�_[�-������g��sw��MG�V�?�Z9����"m�������o����%��/���JQ�*��Q��n�&�~�dW�����V0�nX�J�����R�_~���k��E"��	��������x��QTj�����X�GY�
�]�q��K��b�I��vj\��L��x3R�#@w����X�6;�t�(�rb�Y�8|���I��W1��D�@(9��{:�r=b�A2�|�����q^3���!a"!���
o������yF�D$�Z1��8kdx��t�����\�M�C��$�D��e��E-n��K�!�m�'9"k��Ca���kz9�E��s��}'p�bO��E&IB��),��g����wfi��z��a��T���
��qSQ6���B'��kI���*��X)�}�,�zX�x��O��N���������|?�\�\��/�w��<
/��!NQe����H�\/��}Un0�b���W�5VVXY�i!�@��d������s��,7f�T��.'��!PkdA^��S
@���YB��48��[0k���$�z�]h�b��\�`X�L���p�O�\�$��A�H�S�Qw��JRL�).<-���R	���@�qN2/�v�MY4�b^2���<}���S�C1�������4]���.���t�o ��M�{B^n:�/EIE_Mf!�x��9@F�$�e����(�(e����l+�i<�F������$����j�e��a�D������X���:��cw��
��������E���kz�w����c�q��qR/I����r�B&d���v��B �,g�vo��g~�V��H6���E�(U�����G���-?�W����Jo}��a��.�������'�����7����-�� ��"*���$�������D^�|�!�Z����b�k���n���>�	��'����ig�e[�N���Ig��s_��P�g	)�[}��s���t�Q�hjHa7�J���66�b�8u�g�k(��������tL&`�O��<B���`V�<_&�[�F4�I]r��'�'���m!����mh�k�O�^��+z0v����6~;��1�>0y�	l��Ce�6�f����`��;�Us�\��������������U�1p�U���(�vL����h$+�La�tFr�k��������s(&��|b�k�^����'�����b��M���f��E�x�U!���+��3�������lp��z<I<��bJg�������X��s}�?ybB���G��OHp��Q�+�$,�9�D�{2�<|f��W�tK�6>a��c��5���j�s���-��Z����W�n����v��Ti��~�5��~����Z�Q���V�e���+�*���O���k�����,�P%~K���M�{���</i��V����E:;�K�������YTk�*�jc�(����+�����/):��/�4d���h{��x�9Hx6��\%�!�E�+�����PUE>J2�g���
��"ga-o!�K��:�W�i���&3�0�.����N�,&�

�PHTl/��V�7�a�����	�~��|�c���2�Z��G�Ln��%�{}#X�5+�Jm�����z��hUv��t2�Q�4�
������?���B�J��oo��
�.� O�UQ^��]�}`@^�`a����������vP��zL��2A��0T9D����8���
���X�cK1�U�a/���)X���cGtxP����md>nu������#�Y_)o���������Dc3����5�
���^($��������-���9�^	����BQ��$�
�,�����MK���"��b�RmU�u���R�����47��H�=3��L��
1��u!�nn���g�����2��Q��E�(�B�����*�(�AEg�`��>z��&;\����a�`�b(�:�hF��NX����>�jc��D�5z��@E9��Y
di����pt�<n!�&���~Vw�S��4b�K�o�>����.K�6�n����"I]��D��[)�����&� ����~� #���oo�����zU�D� Tj�k�r�������_�B��+Al�Z$	I����u9'��M�2T�0/UM�oEX��S�V�K��z�u���R���q�Ug
THl��Z	���,�����,&)(Z��/7�{R'i@3q4�}=��6�2FG�
�&r��W��0����td�eO���F�W^����>����4�z���O��_
��Q�3za</@�,�A7n���.�lK�/�����_��Pn�K�J��^�6����G�������6;�������F��_-��`X�6����T���?4�����+U�+��gq�"���>�����Yv���z)��q�w�/�%Hm�-���N��� 2)����,��@!��?���^)�<�3��I���<��b�����9��q�S2��.���}�_�E�x�������a$rX�-O�F�����"�=�m~��vVMM��X4��x���X�II�<��	��5�Z��<��4�����Q-�d@���Q�r���za�F���!�G�������8A�]wy;�\K����$�@����������Q�8C�-7�q����gfq1�z@����c�m����\��W�~y�(o�F
E�=� ��gH�k�K�����^>�����<jqSaO�L��
����S3����s���/\�Z�{��r\K����l�L78Qg�A/W���l����3_y�����9c����	������X��i���W|$n��h���|�L�������w��p�EC�^��]���Qrs{}�����q���se8k�a{j+9�fx�DV�$�+eVL���.�+?NA��&[�@�Y�H�H�E8�7���	����HX>S���=d'&�[��U��|:�F:�-J'&Wd5�>�C��xwrzG�;�9�uO�Q�����P@o&��z!+����-�]�,�w1� �����j��\���B����#�G���
5�t��%lV�r���1��DD�;7���#VGA%n��M�����N��${��3�t,6���,�<�����g���{�|�H�F�����3�������=��N=��[�g=�G���V�z�)���V��P9'�4	�c��NQ���|MG9��W;��Z���2��^�]��\��#3������<��M��
�{wS�����*q����|�u�g�������P�rV/��ZqW���Z��5\{JOm����C:]h���C��oCT�Og����Y]A����a�����WR�h"�y��%�sM�,������_����]��k��`�o�$`Q��
�A�T�m;��hPD��C����c4�����O�u����P���T;��
=�x@^^����o�e�
#\����Q��Q���@������`����ZA,
�J�7������	jGX���0G���"�����{�p��>�5[�#z�f.�%8�(�g�SL��S��*om	Ds��D�q�����0���,�"�	�4��}8��M�A#�f.F���{"��y�]6������}>�?[�k��#�=jr��-�-k�b�!X�S��`�����0���$3(E�����e���K�
@���|�8�B�J�������Fl��6��h������T�#UQ���ag���P�U�@l�5�������X�{4>���Tr�{�.U�6�]�$�{$g'��{W�?Z�������-re��)�2����
�d�*6(���z�s���8�B���JF���#�L�A#F|���H����`t��@�J��0B��-��a����)#�`>��p�-eu�uZ=�5M�[d"B��kCZ�-e:�L��\��6d��
m��Ev���B�:^]OCDky��{B�E�g��E��.��TQ�>�y��C���{�_N&�o����a�>�b�9Z��!
;�x�8OQT:��5i���.�el2�����-��_��$xH^s�5�F�4���Q
^v�qO;`9���e�j����C �'�g;�/�%,O�h.x���p����(���*�.�"4����������"�x&�	'���F�K1{�a[���
:�CC�d�O�4�}k�p�6/�(�E=�^2��@���� ����_�����q��0��%Z�.L�JMD�������bH���L�	`
�'�Kf����3}��3�@�����m�Zl��pwa���
������g��Sg@����������zh��Wgz]8�k�*��fI��h3f&kF�i*���b"�v.a���z���~�_���7�DD �4����Q���_��T��9Yj�1{
���u^����#��(�B����m�y\�3�l��-����x�Z�U����H��C��Kt��������]���q3����SrQ������fUs���&�G/����d��Ig�
[�K1qC�g�Jx�z�x���}j�8�����b���(����I��\���/��}��(Th>B����k���vhU��$[[�"�,��y�:�v�p|7�(����)�m����y{��/|�%?E��M@X��h)�"!��.@���}��� �qt?�[[o� E;��Fq$r������Z~%��(�)�>=������������O���'.�T�*p���������_Jg�����������+rV�?����aA��S�]�Nv2C�6��h������-J7�;J+	W5� ��0m������m@g���q�"	���^�������>���-|���bk�����y>V�hK&�	j�����u�����_�a�-B�����������`�Rx�,�� ]+������7���?��"���'t[����Z	�3����=g4B�f�.�Q�M��,�"X��S����5r��4�r�
PO7P�tRr��!��NH\��]v����v�hD����
/>�>;\������Q$]�U����bf��5A}�u83����&�
�aU�,��"���������{��u4����a8���XE�}����s������U��j���0��A�����I7�����8amV�����j����|=�0�\������V'���_.�8G������V���|]�^H���<+��b�x�!�s6��*����"d'�k�^�]bd��Ket������FR)�P�`1{�;7Yl�y����Jw�s�W�s���f�5$-xS9�`\FZ1k5�&�}�g���0���3��r���A�����b��/?;��d��HI�:l=$R�z���Ov�cu�QiT���*��	&9���9�(NO���1--����vDL"�����r�J@ob�3���DG���Hw�k�Ym�
7J-����:��=��z
�����^�6�C�B��:���/)SMG�8"�g1\`�GC�%�o�x�����t�[
�����<��A*�$Sh$�K1��g�1�����M1>@	��������)���GH�n��Q��������Vm-�Hs|C.K89Y�Rg����[\!���S��//����7N����d���j��&���a�v���b~P��`0��+v~%$��a0Z�n�"r��@�52l{�	Tp������7�]j[�.��B����-���f�[��w���Y�4�"�@��sVdW�
Q���P���"}�l����[�����>qPp�jl���SA�>�����H�#.�
k���s�g�=���u��K���a�S�w�
*���;�4����U�����r�\uRy$�y��b�O����`,:�t���(9�O�~�>%�)�x���k����H��G�b����ecr`���.MF��1<ax����V��5���6�;��6��5�;sJ?���	I:i�	��dG
IHZw�uR�3�_�G���~~����Q�1P��&`��!Di[KJ~��=(?E�O��F�G��m^�=��8s&�Qx�����J�G����-�$7�/���l|S�����aR��>W�{�T�6��������&����7-cv��x1�8���WzQ{x/�Q�����{Z�)�B�i���a��k`�cg���Zq���������
[>�27���j_5��[G�w���k���"�=9��g�51d{��S��r���8�-l�}�����K�~��>�=�H���^]9��L���e�;Y������%��K\-f�8Jn]�H�'<�����V�u���%��
���Npk��������}��di��U����P(��}�t�3�<u��M���|���mc���F��$�	&�� ���<b�zEE.���
Qo�6����9Lb7k!��&r�Q�����������=����������6�����r)�����c0�	
�.���Di�@[|7I����p��M�9��j"�����+NK���,nf�*e[5v��+n�v+��3���)3��M����o��V�M����Q����������i%L���o�����n=I���������p�5��<����@	��P�8������	][��K�����N����EZ��7��V����n�&6��g�$�����/����a\+����eh��s�3��
�����?�8����������O)o�1L�g���3���|{=e���\@LM�k�Iym�q���,XM\���������������2&��"�N��?C�[M�r����)�����M�x�f%����`���}�;��Nu�RV�����7�R�Tu�%0�9b���0�X��o	4�[�yw\[Y����	���@�2�9������g��T_�H��,\�bo����������������~��VH��nA��xQ��V	x��L`�-�y�T�~Z?���������,����0�jJ?�#dS��3��V\W��c����j>RG�u
��f��oo�g����#���JE*�j\�Z�,�n���m��+����_s]��-3 �+W����>�!|H=��I����j�7�]/\4O�M��/�k�N#�:���AMe0S�N���>������{��[��F5���#���V�H�V.Wk�pgo������~�#���^�k�+U����w�"~YWkU��Q��~t?�zOf�[T�����1H���z
��N�����3�i�_��bS��}���a�:�A������i[�����mNAB�R��|�
uL�x2������CE^jz/g:����_.���J
�_���6�w{����Q�L���������M��~Roc�f��J�J;���>�"�T
�<��l_��b������)��f�I��{\�\~N��tL��9�H���Z�&;lm%��?��|sz���^�T�`1�W���F��cqD����.�5���
P�������}���aHp����-~S���j�S�nU[���O���Z�.��Hczb �X��/T���__@��r���8�#�����/�rz��?������kxZ������{�z��_�o�YX�)h�����T�5�34u��|���
���
����8�4�[W(�]6/��?��C�n���s0�W�C�_�:$]���f����`Xq ������[���Nn�����W����O������=�U��LHD%�D�p9�e�W��I[l��:����La����3���h��P5����
Y��n�j��Z-�Y������V�w	�'`!�5��P��Ju1�*+�~r�����U�M�����N��V�m��;-��a�UF�M�n~�H��Y������Q��=�]�NS�C$��#SO��v�'�?�f2������9d`GbQp�Y�	{<��|-�m"k�8�v�5^���d��n�w�zc]�%0�@��e�������(�/&��������#�(�V�/
��
�dE��|�4pO���6V�����Fp(Q�Q��g�B�HLFQ]��p3?����0�n�5�b3���>�N<�`���BsN4?V����}8�"c�UNk�l����q^�e����)���v��������������D.��\�M,Q��������1+4���=��w���_F
F�~��D&�k�\V'GWGp�I%Y��2�����,{�~.��X�\#���U�T��m#�KU���B�������E;2_k �f��� �R��C%3)������6���0O�I�����)V�7�_��\^�W�g��&N<�4�����U��\�O�YJ%���!aTP��w���o������!*k.��ay<�~U���6�:��������k�-%2-*����=:&"�
HZP�)����"���l��%Sh�
�,�#�&Ce:p�M�#|$��p��o�9��z�oeO���!�����F�p��q���*�������Y����p9�����:A�S��[��
���q�F	Z3U:�z^�R��W,V�|PI�PDa|��Q��K�����|��9���a���q�p\�!"� =�4��z��ty����_�
�{6��
dn�������
i�������Q��������R=���yp�l��HZF-t�i����o*���aB�h����u'�����<�W���t6P����9�I��^5N)�2�c���~vVhd	36����}����Ywc�V�R	�cQ
����/8���RY�.�c(pr���n!�1���G�����������&��WH��3'��E��+^����i��xt�81kC����������5����b��AT��G���M��������EO�v8�@��P����3:�����,����]��hgic6a�������/��.0��i�K��R��a8���!�����k��7(�h���8���wl��OsG�/<�7iL2Q(fl���a�Y�]���M�H���9��\u6�T����$�9�L�����h$f����t���������D�]$��L�H\�T$�c�q���S�\�n��B�|��!��������66Z
�"����C+�	]�w����E�f�Y.5�Wn��Pz�^��+7�@���3�Pz�1	?���7i	�HHC��p���8���^��]�soI�3�-h?HD���ZcN�z�CS�����������%s,D�;��@����������Wz���,��c]?�������e��;��,��c��w�!�,�,#��q���n����F�J�q��9m=^�x��7P��E�(QK��H4�����8��-����6i�
T��G���H��Z��0%������W�
��Br��a�m��2�~%`�Bs�~�+�H�Ato�T|6,\I4?:>	kH�������7=���q��4��T`�����*,V���3�x��'��}4BW\ |�.�G��oTd9}�����Bb���	�J��:8�Q��~e�s`�����b:M���lpaX�r���t�.���^�@��uB5.�I������@r�u
���*I���	fT���I8�ToJ���J8Y]0���P����*�u�hOo�^���aAM+��F�7�������x�l�9�a����O	��T`#�U���J3��P�f����5�(a:��%�N��M�T�5�c��[b��T��}�6��������g*��93�
$\O3�\7����C
rj�T��&�s(.�K����K������&	��=�xD6���j���?���z��cYI�-��-��A��B�r���I�1�*h:�\cKo���9�OM�����@���o�i�7kN�=��_h�4� ����
t`�����	~��j�����)gh��a��3�-&\u+^C�s���3��]�?��
d���:R��0_?S.T����7,*u��!b���!�
�����[���;E��P8�LW>���e)���q��s���6�5'��fZDl��Nr�������(o0n[^���2��59'�=�	�~FC���z���V.�5�f��VT��:���i�-�
u�%fc>2��1���d���n�T��z�x�\.��G^9=�`��P��O�����t�����KU�^,�_���+��6���viP^���q4�&�F�eq������F����It�6�����t���Y��l��|,�tF��?��x!�d�~Kt����E*�%Mj,�+{tE��,��e��� '�1[���"�
��,��1��Xs�����[�,���Pi���D�>.�\����9>X5C��\`���%	?����B'��� ��
��� �_9���-a
������o�@�}l��wz-*���:�4&�[�	�F��OO�b�@���X<���"�%bT�/�0`=�V�'������o/���w��rzQ��z9���`�h�����n�N�[�����$eX 
����������|���,��P��O����vuwC5��v��g'�7�zSmP�
�>W���F�	��s�,C'txE�+<B�v��!��B;3��q� �0H�����}>E��*����O�:��"����|%|�T����_����{���M�[�e���>���6�Rf%I�~��z��{�����2%V����S�0���;P�N���~<zrP���/�q��K���0Ri�N�cY�#5�����L��k6k52��r���:z
�"�F{�V��c8�E��a];���ZK��d�.7��� �gy��3�p^tS�����vS
A]vNu>����S�KK��+ �[~�����O���Y;D	�^F�����/S�8B*-��B����^}k��2��`������c�m�a�>9��.��m9��D#�cp�Y"V��{F�%�94{�����uW����/�`�i'$��|��E��k�B�6���1��H�m�[�F���@���)N��n�l~.ra��B��8Y�k��S?���)(
��rS]���$��r\�zg�������/���u�������_����'�h���l���n��QJ��������Q_����r%LZ��6"���j��%"�R�*�����&��J����d�����.S�8����>"����c���|��hS�[��B������Xo��-,�CL�2T���O������#��������Q4*�d��>Q�SP��6\H#L$���+�[	JB��os�;�t�'�{��#��[�#���gS�3��U/���L>"}K'��:6�t���S K��8�B���b8E0�������@p&��A$q�z�9��~u�A�����a��fR����lN��K�&cppA�=�����!��������j�6���,�pkC��0��x9�{?�^�NZ���w���������dZr�]���L������F�RW�J����~;�q��Sw8���l.��&�����i�Q���L�>���?���=H����3@G�|l�+Q����6�u������f=i�9��v�E���|~�{,�8���U�=�+M||�cc��r�hSM�rI�W�)������n]�cJ@}0k�eq���o\�[������p�
��'��n�o������t�����Nj��:{��~��S����4��tV$�����d\&��0�� �L1t�O^ ���`I�����8G�C\�mSK���!L�{�����oI�F�H<.�����"i��������A������.����Xeg�@�^H�T�}��g������	��,�'�%qLD�����:�7�V�fU������`;x���$V������$�b���4v�il�a�^�������,e�1����|�`�����Bh,��]|�}��z���-kk�ge���8�o��qo�gg���q���,F��Y����"y���L
�b��$�6�{*���_������N����V�^�S�'�d�_[
��A���FH�� e0�3|�]��L���%��P%�/���cN���\d>`�G{��~oq����`0���dM�[u���j�MyP����t�{��2�z/��w���Pzes�j`{rXc	��4^�'�:����: )��Cx����S����C�)������IU���&"�P�+��O���q+GC''��6e����W:����\�r4g|-�in�X-Z���W��)P:�=�*'���i9��19N���]w�*�l{��+�."��
$[��P�d���y���m�������j�6mg�BgO�(S��W�;&�������!rS��V^I��OR�����j��������#��
�K���w;*m���(@� Ffz��"76���fgNmb
���$�z4����.A� l������.F������#5~��Wn�� �@��!B>S�c������]����t#��!�4w�EzxOY$]���1mJ���3���t&�%w��Z@�p�L�9��@TS��v���LA�� �T��Y��Fc�z`�
�q���c���!T$�Jx/R�4��:P�DRC]&_����(>�X�V
D�����}�+���s(�����xAfxn� �4[���1�l�]��R�]��x*8�4}�p��M0u���aP�(1�Y���������%\_o!�0�S'Zr1����A$�M#��.�����N`������\��b1ni���&X�O��4�'�K
���nM���Z"��jA�U�`%�����l�������C�K��]��7�X��<����~b��=���:_I\����Tr�0�xn�q���Jc��w�t�\�0�LX�q@)a����I��;��}KSZT�H��~�ON��25��)�����p��;��C�a�~�(�l6^^�:�W����(�	�'����8�Z="h���w��EM�S�������R�cI�	9���t����~	.�JN�
z�:D�3���W,2�OZ�|�T$#W�����������7����n�d���c
:y��_��\����t��I����	5��-�;����6
��f�1���}L[`�>z�2O��d�B\MG���y�d)�����V���j_?��q�v��P����	7<�bS�75�w�z=�'�6���vD��U�g�
�>w�)f�z�:0�pO�a�,tG^jzU@�">����N�F��p����N���A/�F��:�=�&w�:���A��!���x�����.�HIG���2�c�lO�D��;��9"s/�	����W�e|7��L���&��z$y��~��3����A��4`g����?f-
9��af6I���1�	�}l��t|l�+�(�7Ql��("y����z�������%�ta�s�}�
������1�Gb��9z���o��O�Pa����
�J&Edt�������b��]�~ ����;Wn����F��t�0��O����z��N1�p#�B��sU�k(@W
}Q1�z�h�j��j�8>��<�Ms��������m�tX���������J���|3�1����	J��Sz.�"�B��4e^��[�K-B���l<����~��[�w���E���N���t{db���HG��eh�Z��;D�R��.�������-�Z�?��FM���M��{�.g0�[�������&�J�@6�0r4WX��1�3��e*X\Ie��}3���03e������PJ�!q���]���y��1U��A��4Up?�1��KRa���^*��b����2�(
����p=�9)P5E!����B2�Dva4����&�Y�Etc���rV]���W�*�LV�����6��	[[���'�����X}������MVg�:gI��0�N�B%����)�g����"n`2
f
��QV����|f�g�^�Xr�J���&�h �����L,f����:��Ce-.����q�~s���F��Q.f�kNa_
[M~�&=�_>�i�y�F"�s*��z�-��L���nf��+f���RfI������5q~�`3��2~'�_���s�o�B�-��
����N��dS��L������p`=$��yo�"������b~��VO����'�S���}��ud��J���r�~��~Epo	_�������.%��/�5��4��B���Q�������0{�Nx�gWa04��`���*�"2�����?�����9���b���� �s`� I�b���'��&�*nb��Iw���:d��XJD�Y�_WM~k�5Q�"T��E�i����I���qJ��-���U'Y�~��H����)����~{�q��������Y��������*��HS�d�|26�>�zr~�&��\L����
5Wd��X�	-���{��P����?����->���y�5$m��[HS�UE}���o&I�9v�He5�k�j$���)������S���h{t�-i���uE]���^�C�����c�� ���kC��"X~�8�9�X�%#��G��	����k������lE��V�J���1+��|!m'X�&N<zI�!�yDw���j!����b�����l���0F�V��	C��\�O�v�����)Xr�94�E�o<�����;����@�<�<�'���4	�s�t���fv.�oeP��E�j4d�9e1F���U���_��/��=���t9���r�����#\��X�QV>�}!t�"f3�K�;|mA���$ml1�ib�Z�#py6T ^����5�R�1��$��x(fAn�?���4q
��Q�����+Hb���Uh��v�����F'�,u]�\����0W6aq������kZ�����?���b���	����#��7m`~�X���K�!0XOkH{�\=V���B�u{�9p�WX}�}�I\+�U�ka�k�{UJ�Q�^'���"��K�+������%~��n��>�����:
<�z�����J�\�>��d�
��Y0g��~��Gr�OE�rg|�����k�S/�e�k)n�,��G4�lp��)$0�>��J:K`!��|<~��R���K�V�ak7��H�<8��6����$��l��M�9n*��"���#��I�7eY������G��4�/�JLhY�K����z���h;�v{���#�,��Y��Uy@��\�b��I�7E���sY�,��,���h����7�`���t��%����
��X����~�����n;?��\�F���:��{IR�#ch,������cky�����g"'�!��c�t�u
-�:��`����������>q�;����v}�8����)6E�@�t��^-kC\+�z��s�	rw5���(��hw�}�`�5����������nE��D�9���7Jpt0:�Ate|�#uKU$z��sgz�C-��5EC�T�	�#��q����<I���o��$_�������r$-�r?t ����A�����>���V�$n�tT�V��I�Na#9'�U��H�(��y�)��Z�C,�|F'�
�i%��:�_���g	6&"��zE2��[bM�@S��I4\%6�i�:.0�B���^�"�za����O���c��'���kR�������q�aL<���X�Z�7���GLF�u���x�b��k��y�{zr&?�R����qj_��yL����Y��u�Z������7��*�������S1��N&���R�������;�K�fy�1�6T+
~g>��"�h�[-�e��������� �n��0�A�
��C�	fe��N��O��3+�>r]#Ng�!�����kn��e?���Q����O�j}���14l�If��M�Y&=go�c]�y3�1��
]{C�:J���cx���eA�mi�6o����H���9E2�.d���������������%�E���-�=���SB �8���tY�v�2��m��"��W�b8O����5~#��R���)����{��\�i�cl�]o�	�.@[�fP��7h���j8�����1K���);k�����i�.�g8�_h>�3��d��r�Yk�Y�(B��^�9���[&�E&"��G��p�#�����MP�S���;�����`tq�6"DLf>��b�����B�`;��cO������5(��������`D��\oS�V��f\W|�
$�4��?z�C��E?e�����>��.	�x������E%�������k�F��r���:����h��z,bKv�m���T���_cXC:���j]	������-:���ZM�	t�T�����h�S��& �D��Giq�d6I&�Au'�5uo����w���=b���� }��>���;7�K���h���T���OrMu�������&�������M)!�����v��no�i�����]�&������V
	�Z��n�U���F8 t��������o��c��O�Q9�:�M��8��:�o>n��]�%����N���X������O#zl��Q����k�)��`���Y���Sz�*��������n"����G���ydf���#S
3PXi��Rf���o�u~��4
!~6%�y�]�nr��SS]:5x�z������S��KNH���� 9�Z)�z��C$C�5W����"��!��8�����
���L��9���)�I��'Z��p����ti!�."���>���eR��8(���}�XAT�g�"��Y�B=z������^������i��\��k��s�p������k/;-�C�y�
������@��KWQ���'���@��i�RdU�������A������o������YtM�3]6�#Nb�|_����]�+M��`�N��r<e4e��N��#������n�V�U��b����q'���p<!�����|���m9�k���"�F�����cW���hx��W�w�������}y�l��.�h�q9�ArrA�-�A6�����]�����l+��D������Ju��������}��Ga����C���aX9-o�h��UJ�zV �Yp4�Q���,����{*�j:���&��.�|D�8��.`�5$P/f�s��`'/�)�c3@s&F#�9�0�����`��:(-Xc��:����X7�4W4%�I?����Zs[r�1w�����������M�.�Q2Y�qL���e{C�d~��c�����<���qR1z�5��^����1�������G2��K�-���E2�L�,OS~������95��/�cT��!��'��W�"���{�3pqd��I���}��H�tR�tA���������n����l��D$�����fKc��0�LH�W���t�!�8��
Y�0$��E~K�o������N��Pv@|�wd7�1"�Z����p���%H�#���'�IN^K���`>��}v����/��G���lN���^�r���s ���kR=c�����Pc��>������9%�0��X��1b�>������4����!px6uV)%���A��N��_.���v�T����jYmV`^Ve�Z�v�X��ou��J.;kG��I)��?T�~Z?�2��n#���p�JJ�R^�������y�����YJi|����f]�r)uE��1�)U���_T���<�����`M����3`��Ym�����|^]4�������P:��C�������(�`3;]����-k3�F�n���q�{	�O�2����q���s�rv�������O��N��"+�L�?����6��7���V'�����]��G�*����MEC�kOuu��F��;R�*iglyl��yRo���sy����Q&����A�%��,*���l�-�(��Q����t=���J�{X�����@��'!��i�=�h~����,��j'6�U�Y����4���D������iVF��,����Qy���:j���Y�QyM�����4A��L��wp,���[�ru}������G�xIT9�<�T*{�wQXA�D���K�q���������s^2����`��Y�F���V�v����g��[���v���]���m�Jm{���_���L�6�J�p��_���`������TV��a����;;����Ae?����`k�����Z�����3��������J���j���X�u4���������.����=\6fa��^���2~u3/��	OUw_lo����B�Zo�;�z�~�<�:�^U��W�~U��Xr�E�T���Ti�����}��"t\wDp�U�	8	�(�qP8�Q��q��]�]�>j�1V��8��yR.���^)�����<:������L"<J*�Hpeu�����^�n:���V��az���okp;S��������Y���cI�8V�$�&T�!��3�S�LLx�q[<�u�x�K�JTxQM��|��N��0��d�ft~��u�f�^�J�_"Z���#3p��JG���x����s�(Vs>wn����G�8Y�?���R�B��i0��[����BUU�8�XV�n��t�3�G���Z���q��K��)��k8��b��)���*����XW%)}�U1���L�%_�->��DL4��W���(W�Um��z�0�'���P�k�Y���)	�V����%��v�{�m��Vk�`��M�Z�F'	�
�����fU9m�m�����dG*��(}�6�����+�������������xy��SJ�������u��d�@������F~�=�I�>�P�����9J�}�g�WVZoz&oz�6��8�Hm}B�{z��}���}��3�7�o��m�.�&	��U���Y���+��K��:�D�K{���l�4��������[g����t������G����q����%��1��e8T�	J��"r����7�����d��s/�����a��:�M�d�k����
��s���"�M�����0us,��V�OsY@a���e�WO"�����Q���_����F��
�
Z9��lm��v�����+[Q��
gP��T�������f�`�uSBNE��Gu�/k����l�/��D+���A��
��^�R�U���[K~��LV+G��-Rm	�OMl�&��(�YxFr�E4����S,
�6G�q�{��Xb����b�s�VRV7����T�������j�S����W�1s�SK�Joo��Z����&���%F����o����XH�������y|#����EL��:$�d����5��ND������(�a�V2W(]��,/#����w�����r���J�����*�u%�#���>G�w����nfs8)\"F�R�v>�
�� �5wp��h2�
��P��i6%�qDO�e�<��I_��C�_|K`�$���r���M��������1\��������������5lvcs��@�Q0AV�W'��avoxb�K���z�����f^���8�b%�rn���{���k���8�3�<�������A�3������v�}>
����n��^)dL�7��}l�r�Tao7-($&�J��T>�J�'D������x6f�!���41"�E��C[-�I�I0��B��E�sGr�����}6��IWR���<�m������������.N������zK��l�.��~���)��K`���8�Ys)&i�$�:r����x<]��j����
�;�/���W���?������,���D�{M#�{�,�d:�8���	#�3���@O-C�r�syqkN)m��:��k�>$�+�T�`
��p��y
���4N�|��;���#���r�	X�HP{��xQD�+FG&$����xc�����au�B\���y=��}"����R�5����o�Z$0��h8�`�[�zh��s�{�������T��@�	�8#�x���&95u�\�(�y��0�=A8��n��@�����J���]xT���
;{[�ju����#�������q~�~s���}���4��V�8��](������h4�h�� �H���
���$*%��fx�U�'����WV�2������f]up3{v���`DP>���\��y$!�8{�s�8��t�Q4����+��zs�rM3+��"\�w��ftH�%�����I�4���7���1�}���`:G�oH5�.�H�*�oM�!{����M��.�j�[!ipeX**wW^�����)�l�fD�0��}oOIDE�//��y-��	�>>�����e�[6j���t��3:Dr�rC��4�Htb����p�M14�!�����)��^��d�'�&5E�by���2F=�{������O���x���-���H������5�����4(�2f�`iM�����>l���'������m{������������z[�j]5�_q�SV�t�zi+���K���-�/L�:��~�����}*���8�/��=�X
���1�B�@
i�($��hp�j�31�k��pz���8r~W_�����N�M�}��z��=L�U*<����If��m�Kx�3�����%��1�);30�9����e"Q���_����I�	E��YQ��c:�$c�l�6p����$������Y�h~�d���n�����������"�����>���e�-�,c��ZH�i!N-�����-G�w�A���xt�1���9 p����3I1���j���K���N�I�G����9�c�D��
r�����"���O�w���m�K�R�I��;K���"g��k���dj������L$���6��"LH.���d6TSB|tc�z���C����"��X�s���ZV�`�@>[iz-H�
��^x�H�_0N�g~+�e�"��n��%�O�$3�vRZ�J��Kn��.V�QaY�
���Z�_�� 
gN3m�>_c��t��PX4e!�4���T-=g����~�pg%�B�������e��qt�n�O��.O��g��e
W������F�I��8?��Q��p��;�ZAa���w�����XC��/K\�02O����>����=����F��Z)sW�i�
�����V.�����1�o���A��z�.RJ�H����D����D�t�(�<��c�l;�(D�d�]6��N���(uH�����]����\B����B����T�o]}�����z@G���#D���^�H�2���!��k� ���V�n%�}c�Ha@0��x*�@������;R
 m0B�4W��R{�ZQ����l��h���i+�����y��m:�(:T+j����@���`�R��'�y���c���bn 
�u+����B�C����
��)\2���G:bG4P�y���t��Sh�p�/)��k�������C*!��s�� 0�]=
iGk������=���/W�]~.o��^M
!B=�6_��}!Q�XB�(��F`H1�x���Ld���S��y���3�e�:��I��Av������^���o���� �uG2Ah�E��8�^cvmQ�&����"����x�7��Q��G4C�@��������7�$% ���5����*���	A`Qw0�R����IY����S��
��9Z���Y���G����h��t���s*�������qU_��
����
��������rxx�e�'���'a�P/�� ����	�w������������\�cXw
�����5���e������j��c��!�����>����f���,�����}9���d�1F�����M�"�1�`}3�x$�a8QleB>���=:\����Mr���\����v��Wk��:y��Q�|��!�V$g��v0�u�����5������H�zN��F�u�<�XA|��6jX�XY5���K�K���e��GL��H��2|��a(�-3�^�j�>����]�L�_^��=w�hJZ0#��<��?k3���MY�mYz��dA�R[y�z������BpU+���W����$��b�P��X6�B�0#���)k�8/��E}f����bmK����1���Ik��n1�/��uA��]e����}B�����w����4'��yJ^E�%4�c�/�����l����A���y�V�3�3&W[�*���0�{��~JP�6�7�xeF��<1�F��7�|��i���j
�E	����gz����0L�����_���*#��T��!�K��aAR��.(2/�5����'���1�T��������S���a�Q�J���|���x2{1},w#��G7j����g�`#7�bh������Z�"���^�)���_@���g+�Z;i4��c>�!�	��>
{��Mu}������:�? 3!�yf�L��\4N�&�#�9��+8�n�#��V�([����=v��v�yQx��Aq���jgh�������.8n4K}]�t�|��� r��}��r������1�4����������|d��UeR|db~0\;���`DT�!�<w����s�#�Qwi������,�h���v��dxD���R�y�<||V�q�8B�E�z]K��%}�q����t-4ti)h���!�G�e���6��6e�p��N��$j����52�RX�'�)<�<+f�'�"�������	��$2 �f?z`���`v����9��X������S����1`�@��Cx�G{4��������qB�__��6����]�3ZQ�i�����G;�P6���H���}$~`�!}W0�q�:t��R�9|�@=�I����
�I��*:��@�	`m�c��a���y.��_}�+,��~��9�y>O�t��
�}=�@���$��1LR�21>=�8�������4��'m-H�uY��������G���7���z��U_����/,^��J_X���/_���U/����/Y�Bz^��V�������Y�gAJr�{���������������>�����fJ��{���������N#}G0*���@S|{k�,q��i�Rjl��%Y������H���|{;�
��mzf6
"W�e����zG�=v�PE�c���U��=f7a����,��Vw��j���a�����_>�B�(��f��c��h�>�?����8~4	�{���j��GY����C;�q�p�/������vJ��UGd9�i����C�h��a�z��$6:k�"cY�����$��P����As��N:tT��G�n��l�����^���
�O���[��gQ^\;�?�,��3^x�	/<�|������N�a�y
S��u�v76��f�
FU�������*ob��>�1R�hm�
HbKL Oe���t�%�	
���><d�R���Z0I-Oo+��X��[��*�V�C=�	2�yW���_^�����
���4�,�"�!iw�v��U�n�l}�b[��xY�
e��1�l���i6����LF�LLZ�&(����x���~�w��^;���e�jx��BT�8�4UX������D4/�[�`��sj��C�:o���%��!t��k����$FtrP�+��N�w��*��Y(��IR�����R��X�����R���AX�srB'�-�&�EJ
�R�4�����!�(:��Q���!���\3���-�]�� �;oBo�JjO�g|��4]�*�������y����F"MA4�E�($>tn�{z�	o0O��
{i���`���[�2�qn���`I��D�����IN�������3X��x��D�Ae�����Y���lh�����*���Vl��4�~O��eMC>�����C�%j�zau�'�����o0
��O�^�FXu������xM-�O�����=j�H��B�������m����~���/���a�$�KN�S�5#���	JI7��Qr��vH�8MX�A;-��:��z���[�!������f����i���I����~::��
1�L�!���@%�5�P�yx0�]r�c��t,R�X�gct�`|����K�D����Ajb�8�(�u+������4?7������*�V2�&n��~J�l4������"����V<��y��.�n�����3UT���e��j����p���gG���+���������l2��W{����v�$��T����X��Q�
�4���:����M���}��)c�%\����ESQI��]i2��q�H���tSi
	<��hl�@��e��/Y���Lf$:m�G�����T�=+�5B*�u���fd����ai��oD��yz�
��CU��I�/q8�7p����t��i$s�� =��F@��>n|���/�K�����t��DVn�U�_�P����UQu���Ep�"���
��!������
IB0J��^�M�u�3����^���H���ZmFA���h	����������(����#�fL�}�=�D=��
QOm�Z��z����M�2x���]
��e���D>��6����?�4��VG"���y����~���7��u(BX&��,�����g��:�"G�1'��#w�X
�{�7��G��)�{01
�D8��{>�X7�������0��.H����%*{|sGrq�����P��b�UD/Z���v�)��u���by�W"
�4��$�'��!�����*�.��"b�jF
��ev�\� �wgr_��1[(����&��bI��m3.������|����F�=�v����Z�\���:i���9�����}S������
`���C���H�"��m�	����oI�L!����E��������(���A����
:z^���1<������,�~���U���� �w�n���v���]O2gx���+��c���h�r"<}�c�i��G3_�d�^�j<:���h�~���R1��Y��w�k�����4���	����5Ah�����^��f��y��v��v���*6��S���6��BJ�x.:�,��D��)���Xa��9e�^��.��6�@mj.�q��[�_C g�N���Egh� dK��wn����������Kn$��`MN��m0���nL~Y��L&� {Ng����{����FxU��)�w����N���EL�y/���qR�����/U�U������Y����s�]x���@4���4%�|�8��`	�;WN
\�9�����=�E��`����%���s�����$����Bi��-z���mt���!����eI��q0@�G��tk�E��O:�����D���������I�M���J��W���Ab=���`j^K��k|K|����"V?-%��qY�G��>���^��t���c����b�"���4�&LX!���}��_�w���nmwo�\>8��j��n%[�YP�j\E
���Z�Jq�D�����Rc�1�C��V�T��@a}
�W+����NP��^D��X�Ve�8U&c)�G��OI
*�Z%�S��D�O<<�;�P�}�?�'P�	��1e�w�:��E_e��j�����4���k_w���Z��wO����Z	mL�0
���\����&�-��Ln�5�d6E�K���9���}G��lW��B�v�_�KG�^	���@P��~6��F�<���gq������v,�k��W�O��?��
�Ae������+��`���{uo�`g���{� ����������@�
�R1U� i@B���WP"J���5�P%a��k�fA�/���(D�7w7�h�;��]�q�A��#)�&��3�7�����R���K�|Tt�O��@g�(���3����T������~��\�A@���	c:#w`�Xz�)z����L���S�M6ZJM6j����3��s^��9�������Km�}�����A/^	|TSX��'gu��}l�\.�Jj�%�q!~���mN���i��a�+xb[����/��J�j�b?�1n+�]���V��]����:�������l:vz��q���x���J2��j�]WX��|����/���1��R�c*J'�/�	��_^S��<�O���&�+�m�?4q��$��}d����N��l~�����uV�N���l������j�g�F��'S���!��g�I�W�i����������Uk�g��oK�oE����[
.����vDp;Y�0��+�s�D�f�L?3;�����������A�`����I.Izv9���&`��=6n����vK�T��_	�%p����xO�Q�f<�yO�������Q�<��E�����m�v���x�!����������R�q����=��
z�����T���������Z����������z�3�F�/��z������O�c�u����V�Yo���M��~Roc�c=J,l�����'��&��������%{��N���p�o�����D�������w�z��E����7��PxrM����}K�x	��nB`�_�L�<���S������y���Ia��+�^��'m�{\�*��[�[*O����a	��P����D�-q����(��D^J�"C�H�� �l�����c�_�r��T�������h���(W��j�`�;nb70O*�"��;��=?�M:"G�`�^ {�]S���o���w�|����> $��[FRfl[�A�:�XE����U��k����@e���`J�i'DZD����_'z�f���~?�W�`��Ej�X�n$��F�-B}�����=�������.@h9Tv�4�Q��E!��l��m0�ck��-�"2��$�
���e���pv��!*PVu��~�"�w�����B������{�J���y��X���]�i����e��G���4-L`r�[���z���8;j�E�P�:.~��p
U��/��2��	�)=\/�
�q��7�T�������gy�����zK�6��pV;�	b��p3
�|
S�nH:��\����as�3��H��"������9T��������k7l2����d��������<nW.Z��b[W
�#~D��>,3��'��I���f�(�!�85�������%�Ei���Iv����"�,�VA�kvC�����P]��V�I�;(3d&��z�M��,6��$'�p����O���@��S�K�F9�����x���O�5�_���>�Lq�
��L:�1���l��e3�A�eyQ���1�*��N
�7X�?�s]�d���[3m�q�Z5Z����{O��`p�P���/Le���_�+�i����h��
V��"'���2f0�c]c}P�wrNrDwx-b����s?7����,����9r�����osh%6��#*������3�z��Np|PRHz���j���q_�(��b���
nN���6?m�f�����8����;=v7�%S=e��(�l�wkD:��{����������i����2"N�y#��^�V;�
����3���T��Si���<G�hE��R�����K+�\�?������B�����_� -������� 6���TzLW@�(������j���5�iGf���&��NR�������w�=
�{J	�?2��2�����o���o/�=�=�����K��!Gz������._�P���Zw�[�}�����k�������%�
z�c"'���WQw:�P��&v1��E�%&�q��8�^�%���)��bYc�����D�YV���9G��w�d���h��z�<o��#���C��j6���G-�)f�����kxS���}w����Wh�����l_�TOm�p0��=���#,��2���7�c�]8)BN��������G���w���~5�����_��5Bo�����-��%�v)����FIS��Q��*+��0v�I�����q|tE|;����U=�T=w#7�hP/�Q��� �q�.%=8+c�e*#��O���������:�~KS�����I-��"�o��$�-t�y\<V�eq��E%��f��X��l0���k'����6��di����4�V8S��a��O#e��m[x����s6��3t�����m������:Z�Z�n���
��'^���2'[���e�����	��3��
X���(����Z��������=`(a�G�bm
,�����&�$��������Q��I�Rg��77��v��,��0���h	�!�n���h�X���8E�<�^H�z��o[2g!\n�5��x����&�����|��r�r��j���<
z�
��>�I�eY0a�"�����K1��S Hs�N@/�!��=^����;s���$����j<y���x���L"�&�u7�k��8�{/b�	�M��]5�i��8�w��T�0fS�=�{�Qe���S��g��)%�h���#j���\�39�@��x��4x��JD�����:A��:/����$W��h��4
�*�
���=E�q��Z�)�Y����X��'�
�|!hW��`����{n��0pl���2>����S��������>�a����G����K"W��5Iv�$���e~�'���:����������
�}�x�xK!��#���O'i���#(����M��7�
�z���w�qtO��s��(�$���nd&���>�du����H`��e�p$�)h�)��`����B�y�}����%��a83�'&� =B��$�����5�N��L���"�_*r�A��/..����s����������&�~��sv��
-���~����`*�D��9��X:fySM��i��������6f�k���~~�1�[�����	U
�w����F����}�a�3������o	Q-�[�!��H-���Fs�;�z����ZX��q��j�Z	������y�+l3���Nm�p�s��q�T3�dC�^��7��E)�Z�4h�MxK��)L��x9���2�7�c2X��A�l�Y�3�Rvg��b��n\,.6I�(c��{�Ke9�����\-oK!�X{��{_n5������I�G��9{�&�i�Dp	���4��k
W���*F�+U|`���1T
��L/T�#5��em\��`G�)�J-�W��JR{��J��1WCK�����y`�������D���`�s��F���)FC�����X*��A\��3EP����,��gl�y^s|#�+C�,Q�3��yo���	���	K�����> �c�]]�}1
��bQ=�6I)��/�����8[�|gv����!����X�����G�{��7�p��W�q/,3�������?n�J!$�r9��aSh.
���������G"��W���y]W�+�Kz��)�XJ�l<�a���@�� ��Y2 T�h�@�4���*0��R��n�[w0��� ���@ ��|h2���n���M�Q|po����Z�h���'I�]����b*�P�!fz��1��FwD�=m�%
y����9�IG����a���}{����z����T���[-��3��=TP�7�9���S���^�@��w��+C��dV������9����y�%�x��S^���U�C��p�Y8�tNlD��@�&�C\@/�83K�=}5�<ci�_*���+
?"�����Y�F,��g�����������^���NG�J�������aN�?!\��m�[|�Y8�H�Eu�S���Q��8�4w!_��"�gy"R��a����l�����I����cZ���i�����rr0t)������6��e����U���Y��O�j�OfB��I�`��d2l�8�2���
V���e������V�q�^��fw�(9mZ��F��-E1 �"G�����n32����#;h|w��p�Ye^{�H��z	��b�����j�>b�S)��[��_������Ed��1�������,$	�!S��,-��h*�4�<z���i�����(D�@I��t*0o����iD�W��	SX��A�>O
�
U���������_9�����-�s��uD&�n�_��l������g�����G4���VNLX:��CV
��`Rr�.�J��yA^
i���S���;G	_(	��@U�V^�Yx��U��W���Nmw��W	���+M;v_��n
����e����	_k���"��r^p�?�������c0�lb���$���������,{�O���&��+�-�"
�!����jD&��?-��Vv�*q�'��?�O������;���n�����lw;��N��;�l�������~����t@�O�*���R��T��~E��U���e���"���6i��(�pl����������Z�O��I��T�o�����a�,n����	��~�����m���jw��]9�&<x����y��!vpB�=A�L������w�D��V���I
:�/�:-�=�,����:N38�}�)��s(���9hc�)�R���rU?j_�t�|,/VE�z] ��-����{NM���B
#��17b�1�����,��'Ze�G����K��,�c�@�v������Z��"Z��~��z��X����+��:E�R�-�5�j������X���6�Lx��\4�[��r~
8�K��p(������:������q�}N?6N�o����L�8�Oc�;������I~�h#o������*N�h�o^�2�����K���N'�����Y���l�'����-7��/Q��~<bf{|��o-��2�&���D���C����#�(����5����z�,|��MN<+��������0�����R�@��N���k\�����q���8w�>�����������S���-2�2�C48�.�j#���9���l��������
���D)��pC&��[�|��~���7X���M���5!��D��X*�TQ��c�@�k������X+CS����h3:�&UP?5�?i
e��W0Z�����YT�e��M�K��q�ma�6��N�s^Y����Y�����]�\4�w����Wx��%W���K�&m�B}��L�E����Vq� ���
i"�;0	��rFq�9������s�rGA%��$[���� ���^������+�C�y�I�=�����:j�|��'�$�Y��@O*�,^�+�C|F�5�H����k������Q��C[�o~>��j��r-O�f�(��=qX���T�pa���su������D�`������������0/lA��	����&��90��lz��
���"Mu��l�2W R�u`�i����bE���QDs��<�q���G�X��tn�l�W��~����5i��37PD�
)�f�S����lp��cfY
���-	��~#�t2�oec����_.���1��������A{$`C���|�G���O�i����w,��K��2��#;��+��i��q��x��9�/��X�]c4����X��j�k��#����}`�hm�q��Z�)�N>C�h{�G���,�8>�`��������M�Z��{�r�,�w\mp�������-��z�|pAh��_����A3X:����\��^��Ov����h��=G���{�:1er�z�7o��Hq<so��$'�_������H��Fms6�������;��3�X��G:��:����ZA���G������XA�����Y�������������������w���n��TA����V;�J�V��V��A�v`y���_������w�R�������$M�����4b,���1�K��y'� �E�@��/����C'�~<R{Np�����]Lg�Y����l&�{�d�?���=ty3
aE����F���n�9���L�����t�~U\�X��TS��u������M��wWCR':�m�il�X{<��T��$�^Fl�n.�W���j�����Y��1@�i��xVV������)&����Qj �=��(��&c3�������]����i����Y�z0\���A�������|����w�i#����7��v��9�d����p)��v�B�LC��,���.� L���j���Rp.��a;B��O4�m����xM��G�v��k�l[;�,�`{�tY\(�oV�"�A<�c������_\E��H�X��2�]H��,���>O#��	b`�o��������1�r��_��l�l,M����U���K�2@��5��J��j]#���������
���������(��3C�F�T4�b���H��n�>+��;���$L�{��B����LG�v��;����6)}��|��,u��[�����~��������IK�����5O����6����#b����m8�'�h�y �L��S�{;��#�W�Q���t�8S������^g�������;z@�n�=��_u�������v��U3���'��Y�%���A��Q6��5��W�������?m������x�R������"��/;i��������G�myv^�z��ay�3�������y�S���)���YbC����`�Bm ��6������Wv5��2�����O�����^qE���9���7��1gI�N�9��|�a4�)�����	�b�������iVP�_0���X���r�q���,u��et�n����mu����je�����Kyp��L+G�&�}��}S|I�L�$�"��p@f�����:���%%����g�iS@�(fx�H��"��-f�kqf������S�V�`�`U�����������O�DE�EP�2�������wq��v��QO8�XN�}x��$^���/��~Q�����q�`��f9u�mkU\�����~���d}��< ��
((�����b�_e}�@�Y��h/��v�iux$��$>���r�b!c0��4L�a��(}�g�(���LT:��������hM���g$|�����P��!�(
f�l��e(�'V)���U��$�l���O	N��n�?�C*�i����|'9�6�{;To�A��E%�$�R�1pD��1HU9�KQ�?��`��S��x�h�����)�e���g�md,T".@�[T�i����T�E��,�<�CXM����XQ��E=�XZ�qP��7r�l�]�M���y[I�?���WQ�9�����K������z�����S�J�m!1*�����k�EDf:>nC�gv�;n��*3232�����U�D�N�$�Z8<��fUz������u�s��DEN�n4q�\�R�2��`����{y���y6��O��~��Ln~&�����l��i����aO������(.���m4��h����:X|(z�Y��n���l�G,�6�?�GMf�J��}�A����8��52���c!z��p�b�K���WW��e=EV�PW����s�z�n��?����d���O9��3����M����W�5K�)c�3Jb$�C���y=��cf��&������>v�y4�,��������b���w���#�0�����>�
���J��n���yYU��j�J���B��{6�����6~�=+g
_�E�~��F�W&�&�_���������Z>��9�L���Z@l;��=��&T��m�\����d�+��r"��N���o����g1��b��*��px����k�����W�+~������r����f��B57�8��^��H�7�1����fL�"54�����dG���S�U����
�k�O�7M�����_����&sn(� ��4*;���Of�����ck�����������]�MBI�$16�IG��L�����4I��l�����}� =�����J<�{6��Uh����c��y���6;\�0�����^���I'-ty���^K��`��:gr
�PS��TT���vT/�Yk�����_"�����F�.��a�3�/�\��.8 3�#���v��%�W��:�_0���O�X.��]!��p����}���
���qh���rx��|�sU^���3������/���g�k0��c��<���.2+�/������e��������g�J^�N<9�W��������ok����� ��yI�Q�n+�����-���r��J���_�����L�6,�M��/�@h"~/�Y����u�M32���GR��c�c��&y-<��@�#�rm���]u�c���[pm����j~�f�Y�y�+���b5����KS��`�1�r���K�����h	w���_	�
��6�\N�������k�H��Jq%Q���R�*F������L�GsJ]��aQ�qX��m;K[���/�VOmw�[�"������hiYU"lR��Z:���;��%M���Y��jyu���S�d?2M����>�W��n���������/OjGT�'�/3��wJ������w�;�P������M,p��;��a
[-_�5~�L�
���8����K�V�
QH��E|����YV��G��W�l�~����w�#�4��#�18���9]?q�Q�K�o��Z+��7�\m��n�V��5���B�k�iy�D:��F����Z�e���%�L��� ���c^���h�o�\[
j���wo�;�oe��It���l��T�+�
������������JhiY<������~�]���y�-�%�M�M���r��uSJ�=������kQ���2.�:kW�_��R��*�������&��Y��v�j[hJsL�{�����?V�)P���V;���A`V��8��{����j�,�����"����pJ"xQ��h��i����B#|kG������?������ZW���+��s����$m��VO�pp������ZX��+w{�AN�����*f�/��t��$���::�l����B�����7������
�-��S|��:EN~�U�
�[)e�L�&��eO.L�}J���,Vg-�4������f��F����]_.e���WuXo
1[Ic��-����ym�d�m��
��,v/v���?�r&i?�4��"u��Q�����V��p
[��*(�����K��,�G����^-�V/Z2���Z�\�M���Dii�\a`��n2=!��k��fo\����8?:8xyz~r�����Ek{�	�j
}s0����Zc17���%����H_���f�LnK|�q���3B����Vr���0_����6L\co��?:|A�����$3���`?�x���|�Uig[������r2h;�Vm%��V�6ZDv��1^1�������~Y�~uV��L�����c�b>�����{�qn����=Z����_�#����>��E`k�j�[w�V���=?�T��O��������.��v��#�U�����,g�yo����������u���$U�����z���j��]V3�*M�C�T��(��wp��\�w��z��
����K�{�v�����)]����F��f��s�M%w^1)W�����Vz���89�7>���$%������~RW��[�J��Ru�4%P�����p&bQ�����R�.����m,TL1������{{V���c������|(� �z��8=�/��y�|��;�r{;7���l��h����'T�q~���m>k�H�X�)�]sh����>W7�������k��������y��>�}��e����e�c�f�<�&�94	=���}���D`}]������(�E
S�G�����UK������K���Q%Q?M{�u�6�J>��������A��������M�[�|�mh�K��!��c���p�t �wr��������#}���WG'�/�����x{�qK��',���#���p������O��|D<�_;)h �K����r���;-��nX���4`k
������l���2��*�b�����^?��
�O���A��&P3-:@��������w����[�d_��<8}���������n�*��;O��~�Gm~��H�@%9��i5�6!�:X?���j���0���K���^�$i�v������^���R^�Jh���N�����w�v�P�-;�������L������~�����j���(�r]n'���",|�O"��xi�x�$�����y_����z������k����w+y��DTY���kS����]���n~��}��]���X;�&�Iw��k��;���
�h�Q��"-����O���q#��RP?a'w�����*#�%q�{]��I?��xw���g����vV+Z�<����
��5�eV�:����0��1g��af����F��f��;�Fa�	�m��/���i�O��*i'��sL�y8�R��l"?�f\x qs�U��{�7@{�����m��e���_U�pU���]A����6dk�[Rs_��~sj��O����&����T��W��������z����1h�-���R������tZ�2=V�����Rw���='g�l�������l�u�3kI�[wn���������aFi���~���]��e��0��/0��i
�:�G\}�����/����B�����T��m��/��R���!Z���v^I����U���t2������u.��`�d���P.��I���h8�L���F��y�o>����{@�>p����`��9���r4HK2X��$�^Tcm�'
1�]B��w7W~��b�&�Y���Z��'�W�N�e�sg=�kN���a9��z�v�.>k�?��9��=sW�l����9Gc�
�z:e�s�
Y���\�!u�L
9��0-Y���e1n��UG���B�?e���X7�/�\j%�1�����xYMXj����ok>�1�bF����6
|��tll����R���J�4
��~��"��Z���$����1��.|pg�2�u��*��xj��3�^~W��5|���MB�	�Z��c<�weDI�!�c8�}�}��,y`Pp�+����
+��t�%q����n�'�0D�5����V�����'�%a��H��?�X��Oh-��o������_$l��������g����k��ag����[xE/�+\��3�t���1:��;L����|C���i�=3FQ��������l��rGx�Q����8�0���Y�H���	Np����!��M�����>X��6����w�O��G�����U!F��yuZ���C�$����u��.U�S
~=v�C�����9�m3*69m6��zZ�N��c_���	`�S&�1pmqY7�2�������l��qY7�2��U�p���>�G�>L��x���8�.������}$�zy��}����vA.��F��W|�����s�9�
��n�P���o[`UR8R6�;����n�J���}��������an������{��~��>v�dT�#������h�S��5����p������n/�� i$����tI`5�,��Z%�s�.�Vm�|8����0;d�8o�7�Ohy�*v�&��R<����)�}d\����������#����w�������E����rj��������U���+�<u������aZ�4���A�89�?;���|)1�&��Nqd]e����	��1���xsd.S9=8sN��O�_��Hj������3g���|������������YD�����~l1�������F��'���a������q�L�aY�Sb�[���?5���9��f�������/�:�����o��������#f�s�xz�<����"��ue���
����|��!�+�}�M��*�Q4q��b[�U�,(:�?�rN1���3-E>r��	��a3��?��t�Ca�h�����S��+��L}��e�����_x�$�G���0�|_��Y9��'G�G��.�x����K�#�(���-���G�D0���C�Q������i��~��3b�k7$�>�|wF/�}M����XjN��>9�-�Y�'�I�O� �ik����������bx�V�(�(b	��,bl&����>:q��~����?||0��<1����@D}F�C����8��.q�q�����Y�C�O�O�Qu����N�y���H��b�,v��?}�����&�����/:�^��D��nd�,��Y���!{��������X������e��'�\��R�K)U�>�!}:�y
�>�s�����g�,�*��g���j�4�3���s<�y<5��<q�h���K�{ �%�?gN������DL�������������
�t����B��/��w�?�xH�~���92R���G��<������RE�cLi{p ���,���#z����b�e��o����	Mu�}�����9;v~��4�_S�y�cl�
�y���6��?I,�����h���'���|4�

�W�+K����ea���W`.
������i�J�����m��|���0�G3)���W���]���
~eA<R��1�e����A��|����#����<���5�~M�����m�G3��Q~x�}<���R���<�E���?������G�]���@}w�g�&<�i�x��]��]��]�7w�Rl')K!R��Nw(�wpw�������-.��:�-��
����8��u������n9� 7c`��>�@j�5�"��X&Z���P����>}_�>_�+K�'B��g��g(�BA�%��6��#�P[�H#�(T���J��F�q�%q�>�@��D#�T��i� ��J��F��2N�U�������>b`
B��d���(t���Y����)C�2��8T�/Q��K�m����P�(}f
Q���������x(����+r��q���d~��)������c��D!j?U�?U���"<}��u����??V��BE�"
�(S�+D$��h�6��%]��COQ?B_�+4AJ�Q���~���ce=�D!j��x��L�"W���������E�2�(R&!����e��TY�(S;�2�5�6��C���H�q�l�8Q� W~��DS?�^I|���U����H�*%
KMR�n�La��f(��>�
QwS��A�&��L5��\E,g��H,��,T�:��mO���P��<Y����f���*����X�c}[�3����)VDYS�KF�&j�n����qq�YE"��O�w�a��6I���D��������H�M/�_�������l� �&�����
�r�C���B����>I*+��h���<�'R�6��k�#5#)�����$��������^��^�aW�G���X����[2\�%KR��S�^��^*�L��!�~x�����i���
Qg��A�
�	|���w}
j�*Q�x(>�H������=�P�=e�|^m�]L&��c�}������}?���C��Y�a�e��A��*��Ce=��7_3}b�x(Q��ma6�G	dC*KF�#�X\����gXD��"\�w����X��������u��~��~�`��'��}e������b��Y��:p}����`E4p6 �"�&��$�G*�g�)D��C�C1�l�������Aa*B�������
C1Slb��p�Q��J@]b�4�P{������3�C�x	v��l��	�b��T��i�u��A��A�aP@��L�����*t�m�F��C7��G�)�")�x�B/�D�.V?���\	}��
Q����P�PI������Ib�m�1��P9_	#_FJ�����2�8T&!N��K\e��h!Lbe�SW!�T1I'TH3U_��W�C))S�?#����\E� ��}�YM�)��E������W��#?�t��6��(P�(P��H��Q`q�	tQ��'�u��:�,��X1���*�
����<J��D�o��Ce��b(F��U�H��Y�p�b���h�*�1Q&D��L������bU=�}(&���B��3P��� �D�����{!�H�OM�%t0��#%�)����8�0Q���
m2e����S�^����8M����xM��
Qg)�W1��`�H����k�K�������S�|�������h�"I��JB����!V?�0��6�����dF(���A���J'�85�P�DX�H��O2���LL�I��I��4��nSW���������z�z�������b(���,���i�a2I��+@%�4��:%s%V�4�Lc��&��'�3�4UBOS(f�������R3�|%sc��I�� �BO3�[��`q�����>�4A��3R��P��,TBOi3`��4�#�C1�c��d��g�D�8SEA#��U��h��i��aC�s]���]���9��-#�p{���N@L����sl(0�&	Y28>������C��8��#c51��\�P$`ML�x�%����f�83lJsl$j���O���@��.O��}�'p��n���zW�
��A@l(z^�Bq<���.T?�V���|k6&j/������!���yJ|3�~p8�+�����)D�{�^��(?	���${5'-�%���	�������U�>��H�K�T�>2�1BJ����#�a�G�X�f�4�����GL` =%��q����P�����&P���/���1>�����0�t���G@l(zA��CA��<>��}�n�����u�c�-GIa�!VB%�����D���0���^{������P1	�b�.Lq>Se=�+�a�C-H�G��#���^��m��CQ�������q�)3L�Q��Q�
E������0��6R��CO��(�������P�b����?���������I
,tH�1F(����jb����F<^k�P��Cq���8����0Q��O �b(&d����<�D�������
�M��S��|L�|���C1!=7a���������������?&�D���VE��1���Q����M=�����L�$���������������A��	��H?���*I����*�b�Exf.�oB@e�S��ad=�2Rj00������ ���f�WC��q2���bEt0���8������e���b�J��e�
t����cH��V?�
E�
#���n���0U����|7���L Q�n�����bC��X�������@�������1Q���Ca��I��>V�1B16�|�\0B	=%���D�<7�$�����I@\��d`��O�8}%������|$bC����|?���3B�P|�B�����q���W������"3����
��C��6 vX������u�|AbC����� ��l�B���>�$Q�CO	�5����6����3T�����naR�y6q�)W�Q�1��% ��|�RJ�����W�/��	>-5^2R��bG���#�~(�6R���\%��&>I+�#� �P��Q�
E����&�p1?�U���J�+����4G���O���T��X)fC[���*�q<)�8���M1Qs$J�
E?q�6�p1?Q����W���~�����7! 6}�Z��%J1fx=�,
#������c��+�g�aC�Tl�^P���$J��[�G�����)����i��	�F�b6~��������<z����D������������aU��,���~���S����2\,`_=������J1�����n���CO7�!Qn����b(n�����l��,<}�_	�Z2f���������b����Y��
W���G@;���OB��q��P����������J����M�sl*�e���I����X������J1��(&����3b�4������W�Ic�[�i#*��b�0�������g����$����%T��0�l<�p>/M������3& 62j���W���r�8?;3zJ@��F��D6����&�n�b6ADk�����F@zD�"?������P"%~(�]\����[��@��rT*&�8����V�����q�	L�q����]5������[9�>�@3	����$��lH4�X�$�o�5����-��:I����b�d8^3H�b6A��XO`s��8�4HCli�����q�m@&��,E��R(�\zJr�����cY�
Eb�a���Y�`�#�q=���2N�?D���K�@����' .f���p��n����M�COC7��>.s�4�`}��G��(�lB�u���u���S><��M��#�PO�p�
)��%��Wx()��z=
}��C_����P}�#��R�&���l��H����������O��|�
@(�e!�����:����\_�u("%�#��1����(����J1�0P��C�5Pq���D����LHl(�d�C�.����:L��S\���8���+|�r�
�g�aC����yC��Mi���p�1=
�$�D�X� ��
���xhz1��D�
�����0q�)�VoB�0��s�q��?D�����*�3�p��0�q�)�N|=���PIm����yF(Q�!�s��$���hc�x�0�����bC��
\#L�b6a���d*��B-���
&�4��c���
��4;<����!"l�����w�ix=i$��3�z�����,���������%���1/��\7��:Zxafs%�g����H�+^�Y
E1��P��j���@��9!oa �J������PB��@m���3�
B)C@,�%5��4% �����P�3PY2���
���v�1P!jkd����@��=�y2����.���@m�X7g �����-	B$r��T�Z��m-��V���e�@hS[��v,�m�\-�@huG\L^*���>b����G����@m���%r�
����(r��1b�
�W����F@M����x�"����1r���^Nb�(�g���
�7���L@�A& �=3P�F�!��Gn���{�	���\*@��>K��S������SC��D��+����
q��d1>�" >�"������,�K�0B	>�# >�# >%d�S����P�W��P�sT�%R����������b�&Ha������>��sq�����Y<�)~��S# � �f(f8Z���`��B�p�������DeD\ZO�H*�������m���#^".w�RJq��G�D��8�(����Wx(>�}" ��" ���<%������)�a�qe�i�R�Qp��>CyG@�P��Pz�8A����+@�H@��#I�Wt+t��M��O.��l���G��z��r3J�)��".�����8^���^�c�#/�Q�|����L@�P�R]q�P�#�	�c�	��g���#�U��UE-Z��8�>���Y%������=�O@@��i�)��+KFb/��d\�jP��~������Sop��f!���D�!�%q0�����|I���j�"'���D8�)��X<}1��b�VY��oE�����b(�	�6�|�����b�Oqn���!��SE��3�P���A��C���(P��#�b�p*�3pqff�ab�<E���01BJj�ab������B@�i��K����qfFH���o��T�d1�?gNG\W#�l���5p�J)1�|�85�P�|v�L�(HC�#�q���lR�����}���/Hq}���#�C1P�)�a�BWp ���
]�z��D�)�p�B%u���K/�q]
����<�W��a�5�������q�x�B\'���[8�pm�H��q�#���Lj�b��5�uo��Ae�\k'�:�d)��C@\����H��B��r�f�R$q,���s")��T�H�.�iVx=�<n�W�H��,���[fx��P$���,��g�T(�x��)�SGQ�+�P1�W��D���/���\��F�������r3Je��e������:VJ�s�l���Z�P��z���#�V��:#}(pEH�Z�T�����$��~�U�R�m2�c��#\�3�c%��D61��#% �d�Ji�H�'�b��J����me=3%^3V��G4XOH\�PL<\
�����x�>D��D��K@\/���b�#��$���	���WT���I�P���&1��L@\9�����U�p��(Iq�l���TBO���&��~������8{���+�GZir�J�i�������ISR�T�m0J5�m�T���~FJ�i�5G �����H"oAb��8�R$��]TBO�����1{�D>�P������d�U��wSP$�0Qg�MQ(�b��;HP�k�C�,�w�P	=�"|�	_X��}+�b���B@��3K�
0T�,UBO��WC@�����>�]�:F�z�<���F����U�8���H����)%Pq�!�-���@��9�-��N���
s�&���1IHhb�f��L��[���]]l��%�\|?��$��
�����f��!�����w�q�6��9�#b�#�"l(�NGV-�# ���`RRJ�����+�
��S\z��}d��c^�@����R?$�[2�P����3���p���L�P���1���Rn����LTnF�}�4�xF�P����o������Kc_	=% �/5��V	�
E�/�����/q��e	���%��COc�(�)�!Zjl(��r[/Y28>>f���M�^����PR|�1��b6(Wc��rUK�8�!��cbQX����Ed�+D���6c��N_�����rL.W�a��[�	������	�
��kI���8)3|#{��[ B������!B�r�|��t
�vGJ~�?���������C�P��Q�
���mhz��R�(fOnV	h�#�T.�u����,WH����=��c%���	��,; =��P!�8���TE.���/���S��8Q�g���S����:VnF�W1�=�������K|��&J�)L�I��M�k��
z��*u�	�`��H��$���DA&�D�"�b(�n��6U�'���(�*��	�COI�	�$��#���i�,��b�Hg�}��~�&8�4N�$>���!�C1c/��&q�)��32j1���'	$��2�P$��,��jh�|���X�?�D	^�P��o�,��b���l��I\N�@�OH����/q5u�m�wZ@�%���g���$<�Y�?D@zJr��DM@O������$ .fCf���I��cM�����&O���bC�/'�&I����
���A*�RzJ�����Bz6	�@��=\�&!��b|�+��	_7�
q>R"7�A`�B���!��<�������b�����o��^]@���b6L���vX%A�CO	�b�"�/HK�
Eb��`���$���'q�)Ba���r8����b"��!P)f���
��.��l���t�I/�8�7	C�4eJ�L�B<}\�
"$u ���bG>��N��T�m�����v�"o�D1=M8 #�*�G�bC1�2E~���L1{����$V��N���t�~B��V�P��C�R�&���+2���c�7�*D��p(���q1�jo�$��}��TL0Qk7�$�ru]B��U�D)fC��x=��k���x����P��z5I�cC1a�7��&�a�x���% =M�8�D�*���4U�4���b6IFz(l6S
D�Z��S���I��r��	!�?D@\�&����	��O���r�&L�Y������
E�G����J1�T��%u�����]@�K�F�zo��`C�v=�����:u9�q�J�)�|�;�:�|\�/��r����j�lXB�#�b���r�
�>.��J����wPOH}���6��l�*�*�e\
�}<	~��k�|w:FH�m0��b6�����p� �2L��F�|+J�cC���x.f����� ��y�3Q�3PnFI�R2�P����P)f��^��8�z��Y��:q=�4�����rPD�a��+3(�9�
t�q�)g@a����T0l(����R�&��g�l�4�	U;�H5�����Rl(���U�4vq1�!�[*������w1B!��r�3��`|�&�8�fPg�q�i��X�M�r��f(r�
41���b6l:`����P�M��lAL�d�`�I�<^O�Da��R�&e-�e$�3���)�7L��rS�<������>S\�������L�?D@zJ�bB���z�4���U���eJ1�4��hLp=)��S�����
����A}f�������l27�na2[q}02�q�)����G@|=F�*wX	�|��U��d���c�
"�)����y!�#�+%00��+��/|
&��7R�e8�4�]���|�z������������|�Y	�1>��������nI�A6��� ���,�p1�,��\_3BzJLS!� ��cdA�
����
d�R���w|L��=T\8%�<�&������*�bF8�3#M5���/Lq�[R[p�E&�4@�����
�P�f		U�L+f�E���b\�8���&	mN��~V����Y����,�p1R~+MG�����7a�1�+q�
������J1�,��'��`��CO�D�b�7���i	�5BJ.fC���%�O���fd;`�N}|=�P�����R��
Y�X���R��V=�X��e.�#�<l(f\e7`����^�����q�8}D���3�zd�P�{�5Wh��~����?�� ��4�egZ�&��S3��������M.��S�/�~w����s��pn��K�7���:�d�+������-F�g<�9������.����O��x6�uz���,��b^��� z�{8[([T�/��i�8�M���
�}���m����������>��-g������o��[����8����5��7��-=����>��:�?az�G�^�-T?�}�����s����g��/e�����C�������l���������4����Nk����w��������v�+j������\g�wG��t���NR������XA������������������������\�?����g�����a���'o����	��1���xs�H�g��u��hy��`���!��hi��Uu���0�}*�/��q�G�v�����,`���DSE������.�����p���������Q5���V����j�W&�5h|d��G88;;���{�����=BuZ���u>�G�bt^�.�����{��6��~��I�k��������~�Ei�"��R;L��il{{�~����;;Q'v����:����o�J4�U��"��n1������Eo6��K�J93R��cy��*w��������y���s���-/'�Q��t�'��.����=ij��p�����t=jv��vNZ�������xF�������6s��g����<n�2�	������ilBM+��������P�A�f']����R�e1t�����p���[��yIhJ�=���C3���zB
�����w�����d���h�FsK���� V�<��g��WL�
��(�����J�� ���Xw� ������Pw��:�X������q�^M�EE��J����nX�A�&}!�=Z�=fa����d�v\g���A�t���hB/<�����!���'��Ff�x��c���	)�=V���C����gV��!jbB�������m�O���7��?��:Q'������K%�j6��<�Nh�w��;�U�-���r�7�����s*n�J��0��^��!M��h>e+����1�����-�M~��D���|1���
y���s���|4���>b��o���^��h��=��Z����/���Y�������sr��������^�9�|X��t:���qrp������~B���_���h^�����y}��h�tc�����v��/'�����������p��2�E+���;8z)Ux��^�,�p��\�b��.w�>*��@�4�t�y����SL�O6}<������9X?	<���9�<�Y���'G�G�����������L��}Zg2Q�>i���S�TM������3nm��\>}~�|+?��:$�F��
�3	��o��}
^����>��D5~��`�Bn�A�:Y�i��bF��������G ��������u�ev3�sJv�^����*[��?}����/�?fIRVx��_u����`���Y �_|��Dm	��lE�\���k��^3��6������!�|���4&/x�E
Q����Yi\:����};����Rz�0���Yf���X�&���HfUReUh�������B|��`J���7�H.��	����M&��������GgD'��h�w����;������,k�<��ga��_���o`i�'�n6�=��j��q�=����}�3������K���j'?��D�
�_c}>�Mx��B��0+�s��o���'�cf���oo�t���
��n�������Fkq^�aQnz	D�r.6��]�W8
�~${��y���wV�����d�����?e���x�����=}Gd���6�C�&?\h��a��(�n��z��/��o��.��p�=.T��m^{;�Y�3��.�y�����;�����^���������K���%�����'��;^�yq���8��W�������O�3'^8%T�;Fr�����w�������gqJ���,��A����"�fn�{n��Y;o&c���v��q�����O_np3����lB���~2/�������v�������o6^�`��y���&<�����s7u�]jk�t�������?{���y{^���P��3&�A�����������&�������1�q�����&��8w��LH8��A����|v9�2����t������D�sh��{y����o��
�9^��W���l���T�5��'4������no�?������3��������?c�9w|A��
�E�9���4���|�����i!a���Gl��9���2��p���x�T^��������>��/n��|�{1�
I?�����G�E5��y_���h�/Zu����{����k��	w2���O`'<l�h'�����4�9�����Nz�$��\g{/���.^�S��[��x����Hg���|���3������Er�������;[���
��k|�7�A���*]�/�o�&3 ����6G���^�
����h�fn��W3iP+M�z��2������lr}��G�>�_;��{��������+[�x����h��G��s���/ON�0�|�t�$9C�_�Y��sY�����;��S-Yv���6v�<yRe��������mx�m�����_����^%��c5�f�W?���L}�*,��=SU\���^�F	LU�zi%��l���hg������/y��_OK�)���������_�s����a�����v9��_���6��9�?fsP_����Sj����?4��������A����~w�S�`��(RK������v�p��1H]sn��>����H�9J��r��M��fN9��KI���6����Br��1I����g3����q���FV&}[�K5���iA6�T�H!�;'o^�H;��v1���<|��6�����L����o����������`�����j��^O����^>]����k���T`KX�
�>a�K���S�������!g~]
�K��������0������Q1���[��|IY�����n��_�9P�x/j���E>�`�w�[�����]��=;� 	V�}���
� �g���c��"5����u!���%�z��l^i��2B�q����h�_~�i�;����������g1-��A>*�7�g���m�#��l�}u^%�����:����M)q�'O
���b�_�M�<�n2��~�`R�f��M�/��i�}�{���7��dz�b2��;��{��������>���S��Mwk�'�`w�����I��F��3bc�����f�����Z�����dxw�����8?;xwv��-����F�����g�������X�� d�l����13����zo���Xm����!�6b������h7��p��e^�''�o���X�g����/��Y�hy����(q6���MPo���f8�]�M6�&��cqO�D�0�������w����lv�����0�6�I���L�[�W�����t++tx�5����w����v�[v�z7�.�b��?Y?�����&�6���<>|)��my[�&%���^O.�����OF%�/����Z?��N��d��(����\���I��l���F$<�XJ]���nYx��{��N���u�<����FI��
6{���'7CVH���d�!��0-�w���O|:�o8������������4B�}�Y�����hx�oF�Nqu=�
�T�%J�����W�������m���E���!�����W���_���p!��?��s#Hy�v:&�����Z�n25��'��,Q����Q[�y�����9	]����]ea5c5M]
/.g����3�)��d��*u1����7�Onh�L���,��i��l��&���9�1�O��:�|+�������g���f����tz#���Y��,��684��n��V��NI��������0�Nv������2x���M�p��_�v>�i��q����=������[���Rthu���6��-��M�������
3�s�J���a����������hj��tp�r�>�
�$8��.�w��Z�~�HQU1�������Ox���q��x1�d�m�����N^�<�Y�|�GR���c�����Z9k�����;-�j��3-�;6i�N��CtO������-����'��	�c.�?�b�g��������%{IS�c�|���=m#�X�f��(w&�+�������~w4!S�_o6�8+^��f	r���*l�S(��8��[�59?��`i������������l��4)?2�0.~S��b0-��l��i����&��{l��zx�
� �<�!d����rR	���Z�u� ���z���{�����]�?\��6}^�)�9�m=!�
]�$��yyY��3��<���������1f5�xL�����NNHP���2���N����RG
��������t(�%#^�q�J������Y�pa�B��-���LrCA
�4�l��8�v��6�K�u<l6�}'r���������0a����3�'K&1-�����?m��!}ZefX��O��KH��
g�R�������5�f�k����4�����ZJ�#���V;����|,g�_1�?�^�>>=h���
j�m������e-���k�;����
�)g�9_X�
�6���d&���@N��o������VM������������J�+������i�������=}����d�A���{��{����-��7��0��b�;��mmT�H�,����,���)�4�?}������������Zjhe�@��b���h�������R~�9��K���\;|$��n��#&heM��/�,k�b}\���?���_�����{|�@��������?9�~�M���%����L@3�?|�2	�����/�|�{[����3�u�hC��ff���c�P�]�U��'kO�V^��TE�zE7�F���������������Y���|��|bN�	�cO����<��u!������G�.m�����*��@%�z�k?���~�����'�������=��l�����~��lpM_�C��3'��zB6R��{�1�����o�
��?[�����j��S�oy���+vX�7�!i������OOW��)1b��������]����3�V�>2s;���s@:O�c%�La�]a��-_W&�W�{j�<���5�������|�[n�x���8�y��*�v-�c�f��I��fE���2���{';\j�������]a�������z���{"��|�|l�f��~l����p��Y�k�)v���]�(��������n7?#N��S�\]@�uY��,djV�ym^z��+x;����}s	X��~��}@l�D��Tdr�����W�Gd>oC^��X~�5qy/�����1-W�d����L|c�������|d�X�b���|�'��.rka;�w��&�j���vZ�a��w��������jKg��|?�vw�nJd���u������=!�R��H1mG����%)�����r�ix�OD~~���v��&���xr�����i��'[�&�����wL-���]n=���������Ow��`�������s2H���,�9�b������=�YD�x��)�9��g��V�9z�G�}��������h2�1����*^�^�"�5A�]n���xi�x�$$�?���k�Da;+���V�u���jm��?��.?h4]"�vL�^[���V�5��n��yj�b�����y��~y7�vw{A?��^����8O�wdy����;r�wu���^���M/�q���v�kdm�u1pX����==�C��g <nj��=����%��q?x�(��O��:���I��.��q��_��o�7����?� j�ok�H��7��wM+�a��6�Z���$�h/����g=����h����O�vBg��f�q��������6C��I�jO�Db5]�&�CVf�O=��V��D��2[��6�i��%�����\���
z��?�![����������jyoh2������Uyaqh=0h���d��E=���J�SS�����������u���[�UG|���S��N�#��~�HZz����rf���x�V?jF���*x,�z�4'y;����{p9�h��Xz����K{q��]RC�0K�8K�5��;�[r�E��~������3f��x~�����+hm��,x�cL����W\NF|��
,I��3��hrw�� m��������v��=�X�oz��`\[��u�[8���i}n�S���gO����u^LY�pq�/�y�Dcaz���������/d6RQ�����lH���<o���|���.Wk�bM-��R��qJ�����^�h<(����~����48� >9>:��%���������R�p1�N�+I�����O{J���^#-��{���?��� ���t�EY��u-���������Q ��.2���q~E��7�3� �������������|z{^��}����OF$�F���<�U0���9��:��|z��R@\�`����os}��r:��������#>Vzj����)7����z�]�i�����<l*����N=1��
��snji.X�S���6�{2������~�5Y�������*2+��������y�0<���Fw?��u��y��Z�����k�[��{���QL�1�����`zk�Q!�-��J��7>��;�xC��l�&L�Z�Jr�mknR���r3fZz����,o�o��[3+��2�gk�ljzr��W,sO�������Ho���H����mj
w_��� d�N/�,d8�Ie`�zZ\|'��l>�$3Q_��}���"B����I����)K�J4]a��?�����R������wly�b�����WU4���� m��r6��Qk���_�8����/Q$����K���/�~���������l�����^?	� ����������K��J��Jg�*�K2�qK�N9�����]�nlH�p��H�!��|��u����M������1*�rR��E�{��9��������x9,{�����;���u�|o�0�����������w�K���'�����?�������������������Ezrv�"�~��?�������������d
a�
)YW�&��$"8F$8�U)����_\X���o��`������E>�$J��y�+�.�UCu���lK��r�/�V*��u���#j�����
G{�k����7S>��y���3���,��}�Q��������k1���������Pm��W�n��&�Db�����R�ac[���|�!� ��v�������*h��K9O�{�t��F���i}7�*)�V�P$tm��8�n(��Vw�:�OS�zh.��r�YYll3������k�;�x���)%��st"��c�8�./'3�d�xri-Y���3H���������d���;�+uM��b�yz���7d6Cc����{�Q�s��M����/b���]^;)_�>A�,�6�b��Jb��������X��ly�jH�T��r���q�P��o��l
N����~%m�zKnu���d���b�!�S��_r�:��B+�f��/F��6s$�K�D��`����p.�r�RM4�!qB)����j,�?#X��
IW-� e%k$k��9��4d!$iui��W�6��
m���\��i2��vj��D�����m��D\J�I��.��i!{�(�bH[�)'�zo�I��"���k����J3mJ��Dy��YT������^���0W�	S6e8�8��-o����_K$��Y\N'��K�JkH��?Q�c��E�uM{���E�=�|j6����9-F�r�u��fH�O>Ue�r�})���%��}S�$�^/z�V��$�{r�2���������9��S��|�y5�5�u,��2\�F������k�q�0������spA.=J�0=�mk��,�����
[���zdKU,�?��n�&�6����'�$��/�.9%�������m�#���������;/�4�^��o��$}�����E�����}Bw�ah�1rNBdB-�����I��nzl�����i��4�^�5�;m6\�eGv�A��!�y_�W�6��tlm(���<�/����m�2
>A0�e�V�:����&������Mn�sv���[�O�r?5�� e��3-I %L�Z3�INuM u���`��&��T��.��������7�|%3�Z��U$�B�RE�E���pKz�\pN!}��KI�7��{�����D�3aF��w���j��W_Wy��/��^6��Q;�1D�3I������d��%��_�.��k(?6���VO��n5l��7	���Js�T1��qm��e�[��0�
���yiT��6s+n&�����(6����������m�|*�#����_ZM��a�
+��p�rr����
�_���]�
+�v�~�����R�F�.Hj0�+��I��q����,����k���;��(j�D\�t4�}-6$i��)��X�)�6���C6��AO$�e12
 Q�2�J��X����J+�7*2�NI�f���]��l�\�c\�u�*5z,aAC����l2��n�nNDfq]Pu
.&����x6�� ��gN�����[k4E���J������4\��0_yO� �V9?s6��o�D�'�&�V����{��
��0&�h���]�jqi+exJ�,��v{Ke+�IS��-�t;�j� ��(;�a�������*r��q���yV��0�MZS�3}}z����7�o����4#��YS�*�������c1,Z����x�[3��uo�&�}��f��@�h����eE���W���ck^wo����^�?0��V�2�X�����*�WT�c��u"kbl.�;)����"��h��0����-���`��k�oq����������*:��_���pfJ��7A2����������yA;����y�����r����LG��l���'��|�%��'[�}��%��F{=���r���N��WKlMS��sc�����y�|kHu���d`d�"3�`���7�ge�U\�}>��|��/6�����hN�S��H����41
h�zF���hD�Q�mA��LX�#R9���M��j(��s��sc40W5��5o;�Rkk^}^�o�c&���T*Z���Y��Z~���(��d}1��`�do����]��c>s:�eKB���E�T����U�T��T�Z����%����R~�����U��������$������p�u��+�'&H�$��t����4�H�&N����eY�{5���7I*/�����[l*����V{�����/3���G�*�i�c��Q��"�������k.f?i�M-��x�8~�(�m��k�zjX��"�7;�O�������X���H�����0+9+����K�qA�r���l���|�uG�uo��'���G�+m+�YY�c	i��6�[��[������a,,x������l��C(^�F�Y�M��&�u����N}��%R��x.�d����23�&����xU�:W��T����bZ���kc�����#-e��+�hC[����b��^H��Y�5�ZL��\�k�#�_P����U�:�3�6?�w���T�P���I�����51�Xq��H��`����EM��~�+�Km�[^�M�
���f���G������z0q��k�:��F`��V&@l���?��$���+�:+�� [@��"z����s0��=s15���|hv=�f2�6`>�gr�l����u�r�}g7��9\�Q9
�UY"D�������M����4��X^�u��	���-�b�������Ygc=��Y��qON���T��6k��b�Bt���L��-;��%W2��-��y��1�+IYpp����r��G����T�2���s��7����-}'6��H��$��[d�{V�)��He��d��7�(U6�i��O|�rMDP[�#���;���2����~�H��U�]bw�����������Lz�9�W\0H�@�46=�l4Ax1��WY���h����l���`Z�u�B�����a�v��P��
^B�ub���U�so�lW
�r�pZ�s�K��������
�����0{���3GD��������bJ.��9��kY!�)Le�DYo��o���)�vc^qT}o.3����d���� U�;�.k�@Z_\��������7
�:��f/��cG����3���Y�gzi�*�d�IU���rN��4U�*IN{S����\��&M���M�v�F���Z[��������[�do��
����,DK[�D����{a�E�de"6�K�������t������T��&\����Y�.zI;�OXM�W�x1�^*g���2�2
�)�f��i����r�*�g��)�Ns�)�R������t2�p��-dOW��fQ������m���#���^:SvA����B��="A��������4�Y�+�,�]���������6���y�*h�Tf��0�k4Y+9�����m�v�k����5���Zt�z�*����������"u�^i���������-[#�3E�����u�Qn����r����:|�:&�L������^-UA:$�����{����@����1��^��������iEa�fe1l��/3�6G��T��u:��=��R:��Y���GkB����2�Y�-`K�YH�	e��)]~i���1��gQ�g*"LB��NsJ���-�,oXm��7�ivv�:���[��h��
{"dAnr�������?�Qc�����R{l���*��X�m�hE������d�\�~L��h�������)��r�O���������k��A�mn�)D@��vM��p�{:�-�� �\���{L��Eq6���������p���\*�	���rc�@Jl�Ijv����$��-�?�����L�{[/�MNk����������
��i����1�*G��DO{]��(�K�yk�%2��X�D��FmX��F���T���%�i��"�Z}^i���jw�M����p��[�SA�O�Q)�kc���1�~��U��9�Y%��u��q�b��8��"��I����K�F1�Df8r*b�6L��u�W�P�L�
�4�q�!I��0`��
�� ����Se�y�����on��M=yb]`Kn�KR:���1Q�������Z3����+���z��y��bzR���y�o�����/�x��jQ,��s��oj�<���	�bdpVx6_���zy��i�R�H�lR7�>��"�\�F����U.��������+���7faG�,�C+�)j���w�l�F�9U@ ���)�|F��L����.^v��8	g�������'O$��r�=2*(��$&8N�i���uz�7��IJ{������@#��`c��LdH�g�_6�c��P�Y�;8���	�)��eTNM�d��T~i+������L��4�5U�y�+`���5����� ���������,J�At{�{y���0��2w�n�y?�����N�P����rM@K��xY���������*}d\7��V�lb�7*>K
��� ���e
TW���2$�"�^�x���gi�6[�mm�Ri�O�*6]yq5�#���]����a�T���_� h�huZ�����������=�FT-������.a%o���<���>2�������pZ�z5nF����i
a�/�5��b�s,K�py�T�����$	��t�~��o)�6al}��A������x:/����S��}������9�=��{�	��3r�|������{���������8�����7��3��L���pn�y�~��_?�����Z�4�@�<������0�ol[�W/�
7�~�o�_�l~��7���W{�c�nF�^��9_]O�����,����|�y��|_�U��=��v�������i���`C����|���^�����f��!��O�����b����z\;\a8��&UNJ�4�|��kh2QvD�,&
j�
P����F�j��XE���k`�L.;����� �����������:�5m,@^��9����K�i/�v�3�P���������X��o/�/c����f�Qt��P�'�9K����O�6���4��E����7������ �C�*K������a)!	K0����de��R��lN��f����j���5�|%���gOJH���s������L�vL�����,�O5)� ,�_���t�zx�cQbly���O�h#2F�oL�0���7p|O,v!���(Oniz����A����a�@��S%�/�M�~���%�G|3`����?&H�<[���cl�H�4Z���p�j�WU��7�l�3+@��W{5��Y�en���-*]�	��(�����f��z3�>�3��D���i���
m�nT0Ka��LdQt��osV,O��o�w
���e��,��+��VzKt�N����N��%.o�Wo��7����E9���Ua�������Uc-Y���e%������~�us�w��ED�Y�����-7�FC[~E���z��_JDU[��$]7��u�_��f1?'U��*�8�W=�y�����x�(�{q$^(��c��|��7���!��/�;��J���=�n5�TZ��9��4����ea���L�������kA��Ck������S����1I{B�	ii���Ks��������|���s�z�4�������K,����������o���=&�^���O�?�\��3X}�sX��f���������-�{���~�����7��G2��E:wN�T�^���k-��yl�s�������[�~pB����,Z�1��a&�r���;���<�Z�J�8��\_�e��x�~�F)Y�S��?�&J�DAe����Li��~q�f�I�����^�!�\�u
f�
f��6��6a��3����'�vz;�}n�&'�f���=m��+����VZ�O{u'�Y��n��d��3�O�,��!V�F�q�T����wM[{/�q������dA~
���i�f�?������[����rh�5D����/��k.����s�oKI�����d�������'�����h��/��o�6lj�'�o�8D�����s(�L�K��F�����{A�x_��b������n�{��$<��:��I��gV�e���������c�����5��b2�_��q6z��_��	I��E��lU������a�'�������Wb�L�}k�����o��v��������$[Kv�x�50��C��N����-��"[B������S��M]<��A�!��l^����S�5Rzt��q���[�����Wd���S����spR�jM6<\L��:�(��tO�S��EP��[}o�����������S:A�}���'�����l����HA�E��'��"X�[�Oe��d�;�/���I�C�_�n�	��k�!�������}�z ;�7g'�,?�<�I�	�N���K��q�����3M��
cN3j�O_�����&2�:!�b�����&����-Y��%8Op��iGN���T���km`���������������4c�=��� �"�+�y0-Pi�y��������|��W�g���.
QEe]�	0A��7����=����m������=�f� ��������yEP���0�s�v�%�`v��!��#"v�*��!����Q�x�`�G�\�����k���/��A',�������G3(S�����������{����Ta�	���.��C��R=A���f�-��X^eWy�@x���B����v�f-h���}�����������7�Q�\����N~���i��\\/�"�����1]2�������Z�
-i�I�������13q^]C�s������(���EZ�Y�<�b�E�Yh����������m|-�s����%#�s��������~C0��`e�����?�k��T\h�RT+�D������W;�������7D�<kykG�!u'��S��;��W�I���kW>��4��.������A�m�9�d:`%����-���6-���u7�"J-U�x��u������~���"��^/��K� ����W��^��Mh�&�}��
�{q9�<�Fg����e~zuy�	��,(�j�D�J}�v��������e�I��/Kj��`���|��_w?���-&��t��c��.�����{�c�z�gCzFU���69h&)�����W��g�|�H3�������1������LY"�<J�����\s8�- #���[c��yZ6�*s����c�������`|u:HFJ��,���`	7_t����Y��n�D�9�vI2��;�UD��'=$`x�?��k<�,�G�D��T����?����"���r'.�>�y������4�=�n�7������������H6����&5%f���/G�d����o,V��~�� oq
+��uP �Q`�/��

�w���hG�l4ItIU��kkB��ANR�����=��o������v�����%���?���~@�t�&�|�<����~;-S�t�XN��5$������|�gT��	�n-�~��]L�'��m��}2����K��RtRF>���mw�G����I~��O������F))���iQ���m,<���2o�CPv6�f&��F(�g�� � oQ`���p",����:V&_�^k�/\,�Q�"�AS*4�����_.��i>#6j������h��!�J|�#�o2@�-�VMz]�w����{�������-`{<�$�������U���r���0t��UQ�r7_-����y��{h ��Y��( p��#3�cO�����������UZ@��5���c�k�sI���A<�����y:�������/�]����>aW8�uV�������G�^��0j,��T�uk�m� Y�2�y�Xf�����4E	��������s�,xVR�AD���r�p�"�����1{��E�f'�)9<���j��#�Ot�����8����iw���	$EJ����������Ol$��t��?	��X�7���/)�L�I
=��*G]��W��I�M3�%���r{��h_������o���R���c=����T�����?��^/�??��sq�o>{�R��������[>u�i������������?�F��)������'�w�������-g��
�y��?�.���]��e~����"�v08�fC�Ee$:,z��}�8��V��T�;�j�5�hy<>{).����:�U���_������U���xC���D��(�B	�%���Sp��������k�Lg�a�e��jU@g_\Qx������3tvQ��_�=
�}@oWV�96:����D����9�,z5���{���0���������?��w���~�Vi�o���>x��|P�8�a"p"�:�c��^%�{,���nG�)�r�qL���<�K���<�\"[F���E�S��Z���^x��UbM��n��9��:�)��Q54��9���
A9��
4��_fO��������
@������7o\y��4�� ��E64��2�����}��L�g�l!��7�!�#<�}�U!�����3Ze>��-�xEs�#/I4��mj��h>���\����du�]� �O'��(w�q�tjU���(#Q���cSI�P,E�T�i+=N�Qo�7D�y.@�%��$�p��q@�������}-T�5H���zH��8TX��+�y�9�n��x���L��S�w{U����ez�����������#J{��ab��)&����guF&`Q��u�P���e*���r��\k��R
C,)r�}R��Pq���6��1$&����7����"\8��l6'�m�n�QH��w	@.�Q�f��!���K)��k.����nT���`����U�Q���s�^(7�'��0�i����*<�0�|����CQ>{�fO�����Z�|&Jeo�_�����s���!�G4�b���d��x��
�B-�V�X��v�+d�rS{T%��w���o��p2����������0L!���#�l���\m�B
%0$��G����vX�i�CTJ�7e��c1L�-�CC�?���T�)����U�B�6\k�+���v������p��VX5��D�D����	x���6��MmjS����6��MmjS����6��Mm�+�����[�
#360Antonin Houska
ah@cybertec.at
In reply to: 孔凡深(云梳) (#359)
Re: 回复:POC: Cleaning up orphaned files using undo logs

孔凡深(云梳) <fanshen.kfs@alibaba-inc.com> wrote:

Hi, Antonin. I am more interested in zheap. Recently reviewing the patch you submitted.
When I use pg_undodump-tool to dump the undo page chunk, I found that some chunk header is abnormal.
After reading the relevant codes in 0006-The-initial-implementation-of-the-pg_undodump-tool.patch,
I feel that there is a bug in the function parse_undo_page.

Thanks, I'll take a look if the project happens to continue. Currently it
seems that another approach is more likely to be taken:

/messages/by-id/CA+Tgmoa_VNzG4ZouZyQQ9h=oRiy=ZQV5+xHQXxMWmep4Ygg8Dg@mail.gmail.com

--
Antonin Houska
Web: https://www.cybertec-postgresql.com